Kea 2.0.1
ha_config_parser.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <ha_config_parser.h>
10#include <ha_log.h>
11#include <ha_service_states.h>
13#include <limits>
14#include <set>
15
16using namespace isc::data;
17using namespace isc::http;
18
19namespace {
20
22const SimpleDefaults HA_CONFIG_LB_DEFAULTS = {
23 { "delayed-updates-limit", Element::integer, "100" },
24};
25
27const SimpleDefaults HA_CONFIG_DEFAULTS = {
28 { "delayed-updates-limit", Element::integer, "0" },
29 { "heartbeat-delay", Element::integer, "10000" },
30 { "max-ack-delay", Element::integer, "10000" },
31 { "max-response-delay", Element::integer, "60000" },
32 { "max-unacked-clients", Element::integer, "10" },
33 { "send-lease-updates", Element::boolean, "true" },
34 { "sync-leases", Element::boolean, "true" },
35 { "sync-timeout", Element::integer, "60000" },
36 { "sync-page-limit", Element::integer, "10000" },
37 { "wait-backup-ack", Element::boolean, "false" }
38};
39
41const SimpleDefaults HA_CONFIG_MT_DEFAULTS = {
42 { "enable-multi-threading", Element::boolean, "false" },
43 { "http-client-threads", Element::integer, "0" },
44 { "http-dedicated-listener", Element::boolean, "false" },
45 { "http-listener-threads", Element::integer, "0" }
46};
47
49const SimpleDefaults HA_CONFIG_PEER_DEFAULTS = {
50 { "auto-failover", Element::boolean, "true" }
51};
52
54const SimpleDefaults HA_CONFIG_STATE_DEFAULTS = {
55 { "pause", Element::string, "never" }
56};
57
58} // end of anonymous namespace
59
60namespace isc {
61namespace ha {
62
63void
64HAConfigParser::parse(const HAConfigPtr& config_storage,
65 const ConstElementPtr& config) {
66 try {
67 // This may cause different types of exceptions. We catch them here
68 // and throw unified exception type.
69 parseInternal(config_storage, config);
70 logConfigStatus(config_storage);
71
72 } catch (const ConfigError& ex) {
73 throw;
74
75 } catch (const std::exception& ex) {
77 }
78}
79
80void
81HAConfigParser::parseInternal(const HAConfigPtr& config_storage,
82 const ConstElementPtr& config) {
83 // Config must be provided.
84 if (!config) {
85 isc_throw(ConfigError, "HA configuration must not be null");
86 }
87
88 // Config must be a list. Each contains one relationship between servers in the
89 // HA configuration. Currently we support only one relationship.
90 if (config->getType() != Element::list) {
91 isc_throw(ConfigError, "HA configuration must be a list");
92 }
93
94 const auto& config_vec = config->listValue();
95 if (config_vec.size() != 1) {
96 isc_throw(ConfigError, "invalid number of configurations in the HA configuration"
97 " list. Expected exactly one configuration");
98 }
99
100 // Get the HA configuration.
101 ElementPtr c = config_vec[0];
102
103 // Get 'mode'. That's the first thing to gather because the defaults we
104 // apply to the configuration depend on the mode.
105 config_storage->setHAMode(getString(c, "mode"));
106
107 // Set load-balancing specific defaults.
108 if (config_storage->getHAMode() == HAConfig::LOAD_BALANCING) {
109 setDefaults(c, HA_CONFIG_LB_DEFAULTS);
110 }
111 // Set general defaults.
112 setDefaults(c, HA_CONFIG_DEFAULTS);
113
114 // HA configuration must be a map.
115 if (c->getType() != Element::map) {
116 isc_throw(ConfigError, "expected list of maps in the HA configuration");
117 }
118
119 // It must contain peers section.
120 if (!c->contains("peers")) {
121 isc_throw(ConfigError, "'peers' parameter missing in HA configuration");
122 }
123
124 // Peers configuration must be a list of maps.
125 ConstElementPtr peers = c->get("peers");
126 if (peers->getType() != Element::list) {
127 isc_throw(ConfigError, "'peers' parameter must be a list");
128 }
129
130 // State machine configuration must be a map.
131 ConstElementPtr state_machine = c->get("state-machine");
132 ConstElementPtr states_list;
133 if (state_machine) {
134 if (state_machine->getType() != Element::map) {
135 isc_throw(ConfigError, "'state-machine' parameter must be a map");
136 }
137
138 states_list = state_machine->get("states");
139 if (states_list && (states_list->getType() != Element::list)) {
140 isc_throw(ConfigError, "'states' parameter must be a list");
141 }
142 }
143
144 // We have made major sanity checks, so let's try to gather some values.
145
146 // Get 'this-server-name'.
147 config_storage->setThisServerName(getString(c, "this-server-name"));
148
149 // Get 'send-lease-updates'.
150 config_storage->setSendLeaseUpdates(getBoolean(c, "send-lease-updates"));
151
152 // Get 'sync-leases'.
153 config_storage->setSyncLeases(getBoolean(c, "sync-leases"));
154
155 // Get 'sync-timeout'.
156 uint32_t sync_timeout = getAndValidateInteger<uint32_t>(c, "sync-timeout");
157 config_storage->setSyncTimeout(sync_timeout);
158
159 // Get 'sync-page-limit'.
160 uint32_t sync_page_limit = getAndValidateInteger<uint32_t>(c, "sync-page-limit");
161 config_storage->setSyncPageLimit(sync_page_limit);
162
163 // Get 'delayed-updates-limit'.
164 uint32_t delayed_updates_limit = getAndValidateInteger<uint32_t>(c, "delayed-updates-limit");
165 config_storage->setDelayedUpdatesLimit(delayed_updates_limit);
166
167 // Get 'heartbeat-delay'.
168 uint16_t heartbeat_delay = getAndValidateInteger<uint16_t>(c, "heartbeat-delay");
169 config_storage->setHeartbeatDelay(heartbeat_delay);
170
171 // Get 'max-response-delay'.
172 uint16_t max_response_delay = getAndValidateInteger<uint16_t>(c, "max-response-delay");
173 config_storage->setMaxResponseDelay(max_response_delay);
174
175 // Get 'max-ack-delay'.
176 uint16_t max_ack_delay = getAndValidateInteger<uint16_t>(c, "max-ack-delay");
177 config_storage->setMaxAckDelay(max_ack_delay);
178
179 // Get 'max-unacked-clients'.
180 uint32_t max_unacked_clients = getAndValidateInteger<uint32_t>(c, "max-unacked-clients");
181 config_storage->setMaxUnackedClients(max_unacked_clients);
182
183 // Get 'wait-backup-ack'.
184 config_storage->setWaitBackupAck(getBoolean(c, "wait-backup-ack"));
185
186 // Get multi-threading map.
187 ElementPtr mt_config = boost::const_pointer_cast<Element>(c->get("multi-threading"));
188 if (!mt_config) {
189 // Not there, make an empty one.
190 mt_config = Element::createMap();
191 c->set("multi-threading", mt_config);
192 } else if (mt_config->getType() != Element::map) {
193 isc_throw(ConfigError, "multi-threading configuration must be a map");
194 }
195
196 // Backfill the MT defaults.
197 setDefaults(mt_config, HA_CONFIG_MT_DEFAULTS);
198
199 // Get 'enable-multi-threading'.
200 config_storage->setEnableMultiThreading(getBoolean(mt_config, "enable-multi-threading"));
201
202 // Get 'http-dedicated-listener'.
203 config_storage->setHttpDedicatedListener(getBoolean(mt_config, "http-dedicated-listener"));
204
205 // Get 'http-listener-threads'.
206 uint32_t threads = getAndValidateInteger<uint32_t>(mt_config, "http-listener-threads");
207 config_storage->setHttpListenerThreads(threads);
208
209 // Get 'http-client-threads'.
210 threads = getAndValidateInteger<uint32_t>(mt_config, "http-client-threads");
211 config_storage->setHttpClientThreads(threads);
212
213 // Get optional 'trust-anchor'.
214 ConstElementPtr ca = c->get("trust-anchor");
215 if (ca) {
216 config_storage->setTrustAnchor(getString(c, ("trust-anchor")));
217 }
218
219 // Get optional 'cert-file'.
220 ConstElementPtr cert = c->get("cert-file");
221 if (cert) {
222 config_storage->setCertFile(getString(c, ("cert-file")));
223 }
224
225 // Get optional 'key-file'.
226 ConstElementPtr key = c->get("key-file");
227 if (key) {
228 config_storage->setKeyFile(getString(c, ("key-file")));
229 }
230
231 // Peers configuration parsing.
232 const auto& peers_vec = peers->listValue();
233
234 // Go over configuration of each peer.
235 for (auto p = peers_vec.begin(); p != peers_vec.end(); ++p) {
236
237 // Peer configuration is held in a map.
238 if ((*p)->getType() != Element::map) {
239 isc_throw(ConfigError, "peer configuration must be a map");
240 }
241
242 setDefaults(*p, HA_CONFIG_PEER_DEFAULTS);
243
244 // Server name.
245 auto cfg = config_storage->selectNextPeerConfig(getString(*p, "name"));
246
247 // URL.
248 cfg->setUrl(Url(getString((*p), "url")));
249
250 // Optional trust anchor.
251 if ((*p)->contains("trust-anchor")) {
252 cfg->setTrustAnchor(getString(*p, ("trust-anchor")));
253 }
254
255 // Optional certificate file.
256 if ((*p)->contains("cert-file")) {
257 cfg->setCertFile(getString(*p, ("cert-file")));
258 }
259
260 // Optional private key file.
261 if ((*p)->contains("key-file")) {
262 cfg->setKeyFile(getString(*p, ("key-file")));
263 }
264
265 // Role.
266 cfg->setRole(getString(*p, "role"));
267
268 // Auto failover configuration.
269 cfg->setAutoFailover(getBoolean(*p, "auto-failover"));
270
271 // Basic HTTP authentication password.
272 std::string password;
273 if ((*p)->contains("basic-auth-password")) {
274 password = getString((*p), "basic-auth-password");
275 }
276
277 // Basic HTTP authentication user.
278 if ((*p)->contains("basic-auth-user")) {
279 std::string user = getString((*p), "basic-auth-user");
280 BasicHttpAuthPtr& auth = cfg->getBasicAuth();
281 try {
282 if (!user.empty()) {
283 // Validate the user id value.
284 auth.reset(new BasicHttpAuth(user, password));
285 }
286 } catch (const std::exception& ex) {
287 isc_throw(dhcp::DhcpConfigError, ex.what() << " in peer '"
288 << cfg->getName() << "'");
289 }
290 }
291 }
292
293 // Per state configuration is optional.
294 if (states_list) {
295 const auto& states_vec = states_list->listValue();
296
297 std::set<int> configured_states;
298
299 // Go over per state configurations.
300 for (auto s = states_vec.begin(); s != states_vec.end(); ++s) {
301
302 // State configuration is held in map.
303 if ((*s)->getType() != Element::map) {
304 isc_throw(ConfigError, "state configuration must be a map");
305 }
306
307 setDefaults(*s, HA_CONFIG_STATE_DEFAULTS);
308
309 // Get state name and set per state configuration.
310 std::string state_name = getString(*s, "state");
311
312 int state = stringToState(state_name);
313 // Check that this configuration doesn't duplicate existing configuration.
314 if (configured_states.count(state) > 0) {
315 isc_throw(ConfigError, "duplicated configuration for the '"
316 << state_name << "' state");
317 }
318 configured_states.insert(state);
319
320 config_storage->getStateMachineConfig()->
321 getStateConfig(state)->setPausing(getString(*s, "pause"));
322 }
323 }
324
325 // We have gone over the entire configuration and stored it in the configuration
326 // storage. However, we need to still validate it to detect errors like:
327 // duplicate secondary/primary servers, no configuration for this server etc.
328 config_storage->validate();
329}
330
331template<typename T>
332T HAConfigParser::getAndValidateInteger(const ConstElementPtr& config,
333 const std::string& parameter_name) const {
334 int64_t value = getInteger(config, parameter_name);
335 if (value < 0) {
336 isc_throw(ConfigError, "'" << parameter_name << "' must not be negative");
337
338 } else if (value > std::numeric_limits<T>::max()) {
339 isc_throw(ConfigError, "'" << parameter_name << "' must not be greater than "
340 << +std::numeric_limits<T>::max());
341 }
342
343 return (static_cast<T>(value));
344}
345
346void
347HAConfigParser::logConfigStatus(const HAConfigPtr& config_storage) const {
349
350 // If lease updates are disabled, we want to make sure that the user
351 // realizes that and that he has configured some other mechanism to
352 // populate leases.
353 if (!config_storage->amSendingLeaseUpdates()) {
355 }
356
357 // Same as above but for lease database synchronization.
358 if (!config_storage->amSyncingLeases()) {
360 }
361
362 // Unusual configuration.
363 if (config_storage->amSendingLeaseUpdates() !=
364 config_storage->amSyncingLeases()) {
366 .arg(config_storage->amSendingLeaseUpdates() ? "true" : "false")
367 .arg(config_storage->amSyncingLeases() ? "true" : "false");
368 }
369
370 // With this setting the server will not take ownership of the partner's
371 // scope in case of partner's failure. This setting is OK if the
372 // administrator desires to have more control over scopes selection.
373 // The administrator will need to send ha-scopes command to instruct
374 // the server to take ownership of the scope. In some cases he may
375 // also need to send dhcp-enable command to enable DHCP service
376 // (specifically hot-standby mode for standby server).
377 if (!config_storage->getThisServerConfig()->isAutoFailover()) {
379 .arg(config_storage->getThisServerName());
380 }
381}
382
383} // end of namespace ha
384} // end of namespace isc
An exception that is thrown if an error occurs while configuring any server.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
static std::string getString(isc::data::ConstElementPtr scope, const std::string &name)
Returns a string parameter from a scope.
static bool getBoolean(isc::data::ConstElementPtr scope, const std::string &name)
Returns a boolean parameter from a scope.
static int64_t getInteger(isc::data::ConstElementPtr scope, const std::string &name)
Returns an integer parameter from a scope.
static size_t setDefaults(isc::data::ElementPtr scope, const SimpleDefaults &default_values)
Sets the default values.
void parse(const HAConfigPtr &config_storage, const data::ConstElementPtr &config)
Parses HA configuration.
Represents a basic HTTP authentication.
Definition: basic_auth.h:21
Represents an URL.
Definition: url.h:20
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
std::vector< SimpleDefault > SimpleDefaults
This specifies all default values in a given scope (e.g. a subnet).
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
const isc::log::MessageID HA_CONFIGURATION_SUCCESSFUL
Definition: ha_messages.h:26
const isc::log::MessageID HA_CONFIG_AUTO_FAILOVER_DISABLED
Definition: ha_messages.h:27
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:760
const isc::log::MessageID HA_CONFIG_LEASE_UPDATES_AND_SYNCING_DIFFER
Definition: ha_messages.h:31
const isc::log::MessageID HA_CONFIG_LEASE_UPDATES_DISABLED
Definition: ha_messages.h:32
const isc::log::MessageID HA_CONFIG_LEASE_SYNCING_DISABLED
Definition: ha_messages.h:29
int stringToState(const std::string &state_name)
Returns state for a given name.
boost::shared_ptr< BasicHttpAuth > BasicHttpAuthPtr
Type of pointers to basic HTTP authentication objects.
Definition: basic_auth.h:70
Defines the logger used by the top-level component of kea-lfc.