Kea 2.0.3
ha_config.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2022 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
10#include <asiolink/io_error.h>
11#include <asiolink/crypto_tls.h>
12#include <dhcpsrv/cfgmgr.h>
16#include <util/strutil.h>
17#include <ha_log.h>
18#include <ha_config.h>
19#include <ha_service_states.h>
20#include <sstream>
21
22using namespace isc::asiolink;
23using namespace isc::http;
24using namespace isc::util;
25using namespace isc::dhcp;
26
27namespace isc {
28namespace ha {
29
31 : tls_context_(), name_(), url_(""), trust_anchor_(), cert_file_(),
32 key_file_(), role_(STANDBY), auto_failover_(false), basic_auth_() {
33}
34
35void
36HAConfig::PeerConfig::setName(const std::string& name) {
37 // We want to make sure that someone didn't provide a name that consists of
38 // a single space, tab etc.
39 const std::string& s = util::str::trim(name);
40 if (s.empty()) {
41 isc_throw(BadValue, "peer name must not be empty");
42 }
43 name_ = s;
44}
45
46void
47HAConfig::PeerConfig::setRole(const std::string& role) {
48 role_ = stringToRole(role);
49}
50
51std::string
53 std::ostringstream label;
54 label << getName() << " (" << getUrl().toText() << ")";
55 return (label.str());
56}
57
59HAConfig::PeerConfig::stringToRole(const std::string& role) {
60 if (role == "primary") {
62
63 } else if (role == "secondary") {
65
66 } else if (role == "standby") {
68
69 } else if (role == "backup") {
71
72 }
73
74 // Invalid value specified.
75 isc_throw(BadValue, "unsupported value '" << role << "' for role parameter");
76}
77
78std::string
80 switch (role) {
82 return ("primary");
84 return ("secondary");
86 return ("standby");
88 return ("backup");
89 default:
90 ;
91 }
92 return ("");
93}
94
95void
97 const BasicHttpAuthPtr& auth = getBasicAuth();
98 if (!request || !auth) {
99 return;
100 }
101 request->context()->headers_.push_back(BasicAuthHttpHeaderContext(*auth));
102}
103
105 : state_(state), pausing_(STATE_PAUSE_NEVER) {
106}
107
108void
109HAConfig::StateConfig::setPausing(const std::string& pausing) {
110 pausing_ = stringToPausing(pausing);
111}
112
114HAConfig::StateConfig::stringToPausing(const std::string& pausing) {
115 if (pausing == "always") {
116 return (STATE_PAUSE_ALWAYS);
117
118 } else if (pausing == "never") {
119 return (STATE_PAUSE_NEVER);
120
121 } else if (pausing == "once") {
122 return (STATE_PAUSE_ONCE);
123 }
124
125 isc_throw(BadValue, "unsupported value " << pausing << " of 'pause' parameter");
126}
127
128std::string
130 switch (pausing) {
132 return ("always");
133
135 return ("never");
136
137 case STATE_PAUSE_ONCE:
138 return ("once");
139
140 default:
141 ;
142 }
143
144 isc_throw(BadValue, "unsupported pause enumeration " << static_cast<int>(pausing));
145}
146
149 // Return config for the state if it exists already.
150 auto state_config = states_.find(state);
151 if (state_config != states_.end()) {
152 return (state_config->second);
153 }
154
155 // Create config for the state and store its pointer.
156 StateConfigPtr new_state_config(new StateConfig(state));
157 states_[state] = new_state_config;
158
159 return (new_state_config);
160}
161
164 sync_leases_(true), sync_timeout_(60000), sync_page_limit_(10000),
171}
172
174HAConfig::selectNextPeerConfig(const std::string& name) {
175 // Check if there is a configuration for this server name already. We can't
176 // have two servers with the same name.
177 if (peers_.count(name) > 0) {
178 isc_throw(BadValue, "peer with name '" << name << "' already specified");
179 }
180
181 // Name appears to be unique, so let's add it.
182 PeerConfigPtr cfg(new PeerConfig());
183 cfg->setName(name);
184 peers_[name] = cfg;
185
186 // Return this to the caller so as the caller can set parsed configuration
187 // for this peer.
188 return (cfg);
189}
190
191void
192HAConfig::setThisServerName(const std::string& this_server_name) {
193 // Avoid names consisting of spaces, tabs etc.
194 std::string s = util::str::trim(this_server_name);
195 if (s.empty()) {
196 isc_throw(BadValue, "'this-server-name' value must not be empty");
197 }
198
200}
201
202
203void
204HAConfig::setHAMode(const std::string& ha_mode) {
205 ha_mode_ = stringToHAMode(ha_mode);
206}
207
209HAConfig::stringToHAMode(const std::string& ha_mode) {
210 if (ha_mode == "load-balancing") {
211 return (LOAD_BALANCING);
212
213 } else if (ha_mode == "hot-standby") {
214 return (HOT_STANDBY);
215
216 } else if (ha_mode == "passive-backup") {
217 return (PASSIVE_BACKUP);
218 }
219
220 isc_throw(BadValue, "unsupported value '" << ha_mode << "' for mode parameter");
221}
222
223std::string
225 switch (ha_mode) {
226 case LOAD_BALANCING:
227 return ("load-balancing");
228 case HOT_STANDBY:
229 return ("hot-standby");
230 case PASSIVE_BACKUP:
231 return ("passive-backup");
232 default:
233 ;
234 }
235 return ("");
236}
237
239HAConfig::getPeerConfig(const std::string& name) const {
240 auto peer = peers_.find(name);
241 if (peer == peers_.end()) {
242 isc_throw(InvalidOperation, "no configuration specified for server " << name);
243 }
244
245 return (peer->second);
246}
247
251 for (auto peer = servers.begin(); peer != servers.end(); ++peer) {
252 if (peer->second->getRole() != HAConfig::PeerConfig::BACKUP) {
253 return (peer->second);
254 }
255 }
256
257 isc_throw(InvalidOperation, "no failover partner server found for this"
258 " server " << getThisServerName());
259}
260
264}
265
269 copy.erase(getThisServerName());
270 return (copy);
271}
272
273void
275 // Peers configurations must be provided.
276 if (peers_.count(getThisServerName()) == 0) {
277 isc_throw(HAConfigValidationError, "no peer configuration specified for the '"
278 << getThisServerName() << "'");
279 }
280
281 // Gather all the roles and see how many occurrences of each role we get.
282 std::map<PeerConfig::Role, unsigned> peers_cnt;
283 for (auto p = peers_.begin(); p != peers_.end(); ++p) {
284 if (!p->second->getUrl().isValid()) {
285 isc_throw(HAConfigValidationError, "invalid URL: "
286 << p->second->getUrl().getErrorMessage()
287 << " for server " << p->second->getName());
288 }
289
290 // The hostname must be an address, not a name.
291 IOAddress addr("::");
292 try {
293 addr = IOAddress(p->second->getUrl().getStrippedHostname());
294 } catch (const IOError& ex) {
296 << p->second->getUrl().toText()
297 << "': " << ex.what()
298 << " for server " << p->second->getName());
299 }
300
301 // Check TLS setup.
302 Optional<std::string> ca = p->second->getTrustAnchor();
303 Optional<std::string> cert = p->second->getCertFile();
304 Optional<std::string> key = p->second->getKeyFile();
305 // When not configured get the value from the global level.
306 if (ca.unspecified()) {
307 ca = trust_anchor_;
308 }
309 if (cert.unspecified()) {
310 cert = cert_file_;
311 }
312 if (key.unspecified()) {
313 key = key_file_;
314 }
315 bool have_ca = (!ca.unspecified() && !ca.get().empty());
316 bool have_cert = (!cert.unspecified() && !cert.get().empty());
317 bool have_key = (!key.unspecified() && !key.get().empty());
318 bool use_tls = (have_ca || have_cert || have_key);
319 if (use_tls) {
320 try {
321 // TLS is used: all 3 parameters are required.
322 if (!have_ca) {
323 isc_throw(HAConfigValidationError, "trust-anchor parameter"
324 << " is missing or empty: all or none of"
325 << " TLS parameters must be set");
326 }
327 if (!have_cert) {
328 isc_throw(HAConfigValidationError, "cert-file parameter"
329 << " is missing or empty: all or none of"
330 << " TLS parameters must be set");
331 }
332 if (!have_key) {
333 isc_throw(HAConfigValidationError, "key-file parameter"
334 << " is missing or empty: all or none of"
335 << " TLS parameters must be set");
336 }
337 TlsContext::configure(p->second->tls_context_,
339 ca.get(),
340 cert.get(),
341 key.get());
342 } catch (const isc::Exception& ex) {
343 isc_throw(HAConfigValidationError, "bad TLS config for server "
344 << p->second->getName() << ": " << ex.what());
345 }
346 } else {
347 // Refuse HTTPS scheme when TLS is not enabled.
348 if (p->second->getUrl().getScheme() == Url::HTTPS) {
350 << p->second->getUrl().toText()
351 << "': https scheme is not supported"
352 << " for server " << p->second->getName()
353 << " where TLS is disabled");
354 }
355 }
356
357 ++peers_cnt[p->second->getRole()];
358 }
359
360 // Only one primary server allowed.
361 if (peers_cnt.count(PeerConfig::PRIMARY) && (peers_cnt[PeerConfig::PRIMARY] > 1)) {
362 isc_throw(HAConfigValidationError, "multiple primary servers specified");
363 }
364
365 // Only one secondary server allowed.
366 if (peers_cnt.count(PeerConfig::SECONDARY) && (peers_cnt[PeerConfig::SECONDARY] > 1)) {
367 isc_throw(HAConfigValidationError, "multiple secondary servers specified");
368 }
369
370 // Only one standby server allowed.
371 if (peers_cnt.count(PeerConfig::STANDBY) && (peers_cnt[PeerConfig::STANDBY] > 1)) {
372 isc_throw(HAConfigValidationError, "multiple standby servers specified");
373 }
374
375 if (ha_mode_ == LOAD_BALANCING) {
376 // Standby servers not allowed in load balancing configuration.
377 if (peers_cnt.count(PeerConfig::STANDBY) > 0) {
378 isc_throw(HAConfigValidationError, "standby servers not allowed in the load "
379 "balancing configuration");
380 }
381
382 // Require one secondary server in the load balancing configuration.
383 if (peers_cnt.count(PeerConfig::SECONDARY) == 0) {
384 isc_throw(HAConfigValidationError, "secondary server required in the load"
385 " balancing configuration");
386 }
387
388 // Require one primary server in the load balancing configuration.
389 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
390 isc_throw(HAConfigValidationError, "primary server required in the load"
391 " balancing configuration");
392 }
393
394 // In the load-balancing mode the wait-backup-ack must be false.
395 if (wait_backup_ack_) {
396 isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
397 " load balancing configuration");
398 }
399
400 } else if (ha_mode_ == HOT_STANDBY) {
401 // Secondary servers not allowed in the hot standby configuration.
402 if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
403 isc_throw(HAConfigValidationError, "secondary servers not allowed in the hot"
404 " standby configuration");
405 }
406
407 // Require one standby server in the hot standby configuration.
408 if (peers_cnt.count(PeerConfig::STANDBY) == 0) {
409 isc_throw(HAConfigValidationError, "standby server required in the hot"
410 " standby configuration");
411 }
412
413 // Require one primary server in the hot standby configuration.
414 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
415 isc_throw(HAConfigValidationError, "primary server required in the hot"
416 " standby configuration");
417 }
418
419 // In the hot-standby mode the wait-backup-ack must be false.
420 if (wait_backup_ack_) {
421 isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
422 " hot standby configuration");
423 }
424
425 // The server must not transition to communication-recovery state in
426 // hot-standby mode.
427 if (delayed_updates_limit_ > 0) {
428 isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
429 " the hot standby configuration");
430 }
431
432 } else if (ha_mode_ == PASSIVE_BACKUP) {
433 if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
434 isc_throw(HAConfigValidationError, "secondary servers not allowed in the"
435 " passive backup configuration");
436 }
437
438 if (peers_cnt.count(PeerConfig::STANDBY) > 0) {
439 isc_throw(HAConfigValidationError, "standby servers not allowed in the"
440 " passive backup configuration");
441 }
442
443 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
444 isc_throw(HAConfigValidationError, "primary server required in the"
445 " passive backup configuration");
446 }
447
448 // The server must not transition to communication-recovery state in
449 // passive-backup mode.
450 if (delayed_updates_limit_ > 0) {
451 isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
452 " the passive backup configuration");
453 }
454 }
455
457 // We get it from staging because applying the DHCP multi-threading configuration
458 // occurs after library loading during the (re)configuration process.
459 auto mcfg = CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading();
460 bool dhcp_mt_enabled = false;
461 uint32_t dhcp_threads = 0;
462 uint32_t dummy_queue_size = 0;
463 CfgMultiThreading::extract(mcfg, dhcp_mt_enabled, dhcp_threads, dummy_queue_size);
464
465 if (!dhcp_mt_enabled) {
466 // HA+MT requires DHCP multi-threading.
469 return;
470 }
471
472 // When DHCP threads is configured as zero, we should auto-detect.
473 if (!dhcp_threads) {
474 dhcp_threads = MultiThreadingMgr::detectThreadCount();
475 // If machine says it cannot support threads.
476 if (!dhcp_threads) {
479 return;
480 }
481 }
482
483 // If http_listener_threads_ is 0, then we use the same number of
484 // threads as DHCP does.
485 if (http_listener_threads_ == 0) {
486 http_listener_threads_ = dhcp_threads;
487 }
488
489 // If http_client_threads_ is 0, then we use the same number of
490 // threads as DHCP does.
491 if (http_client_threads_ == 0) {
492 http_client_threads_ = dhcp_threads;
493 }
494 }
495}
496
497} // end of namespace isc::ha
498} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
Exception thrown when configuration validation fails.
Definition: ha_config.h:26
HA peer configuration.
Definition: ha_config.h:53
std::string getLogLabel() const
Returns a string identifying a server used in logging.
Definition: ha_config.cc:52
void addBasicAuthHttpHeader(http::PostHttpRequestJsonPtr request) const
Adds a basic HTTP authentication header to a request when credentials are specified.
Definition: ha_config.cc:96
Role
Server's role in the High Availability setup.
Definition: ha_config.h:70
void setRole(const std::string &role)
Sets servers role.
Definition: ha_config.cc:47
static std::string roleToString(const HAConfig::PeerConfig::Role &role)
Returns role name.
Definition: ha_config.cc:79
static Role stringToRole(const std::string &role)
Decodes role provided as a string.
Definition: ha_config.cc:59
void setName(const std::string &name)
Sets server name.
Definition: ha_config.cc:36
Configuration specific to a single HA state.
Definition: ha_config.h:235
static util::StatePausing stringToPausing(const std::string &pausing)
Converts pausing mode from the textual form.
Definition: ha_config.cc:114
void setPausing(const std::string &pausing)
Sets pausing mode for the given state.
Definition: ha_config.cc:109
StateConfig(const int state)
Constructor.
Definition: ha_config.cc:104
static std::string pausingToString(const util::StatePausing &pausing)
Returns pausing mode in the textual form.
Definition: ha_config.cc:129
State machine configuration information.
Definition: ha_config.h:287
StateConfigPtr getStateConfig(const int state)
Returns pointer to the state specific configuration.
Definition: ha_config.cc:148
uint32_t max_response_delay_
Max delay in response to heartbeats.
Definition: ha_config.h:744
uint32_t http_listener_threads_
Number of HTTP listener threads.
Definition: ha_config.h:750
uint32_t sync_page_limit_
Page size limit while synchronizing leases.
Definition: ha_config.h:739
std::string getThisServerName() const
Returns name of this server.
Definition: ha_config.h:330
bool http_dedicated_listener_
Enable use of own HTTP listener.
Definition: ha_config.h:749
HAMode
Mode of operation.
Definition: ha_config.h:42
void validate()
Validates configuration.
Definition: ha_config.cc:274
uint32_t delayed_updates_limit_
Maximum number of lease updates held for later send in communication-recovery.
Definition: ha_config.h:741
PeerConfigPtr getThisServerConfig() const
Returns configuration of this server.
Definition: ha_config.cc:262
std::map< std::string, PeerConfigPtr > PeerConfigMap
Map of the servers' configurations.
Definition: ha_config.h:232
void setHAMode(const std::string &ha_mode)
Sets new mode of operation.
Definition: ha_config.cc:204
HAMode ha_mode_
Mode of operation.
Definition: ha_config.h:735
bool send_lease_updates_
Send lease updates to partner?
Definition: ha_config.h:736
uint32_t max_unacked_clients_
Maximum number of unacked clients.
Definition: ha_config.h:746
PeerConfigMap peers_
Map of peers' configurations.
Definition: ha_config.h:755
uint32_t max_ack_delay_
Maximum DHCP message ack delay.
Definition: ha_config.h:745
util::Optional< std::string > cert_file_
Certificate file.
Definition: ha_config.h:753
void setThisServerName(const std::string &this_server_name)
Sets name of this server.
Definition: ha_config.cc:192
PeerConfigMap getOtherServersConfig() const
Returns configuration of other servers.
Definition: ha_config.cc:267
PeerConfigPtr getFailoverPeerConfig() const
Returns configuration of the partner which takes part in failover.
Definition: ha_config.cc:249
PeerConfigPtr getPeerConfig(const std::string &name) const
Returns configuration of the specified server.
Definition: ha_config.cc:239
util::Optional< std::string > key_file_
Private key file.
Definition: ha_config.h:754
PeerConfigPtr selectNextPeerConfig(const std::string &name)
Creates and returns pointer to the new peer's configuration.
Definition: ha_config.cc:174
bool sync_leases_
Synchronize databases on startup?
Definition: ha_config.h:737
bool wait_backup_ack_
Wait for lease update ack from backup?
Definition: ha_config.h:747
StateMachineConfigPtr state_machine_
State machine configuration.
Definition: ha_config.h:756
HAConfig()
Constructor.
Definition: ha_config.cc:162
util::Optional< std::string > trust_anchor_
Trust anchor.
Definition: ha_config.h:752
static HAMode stringToHAMode(const std::string &ha_mode)
Decodes HA mode provided as string.
Definition: ha_config.cc:209
uint32_t http_client_threads_
Number of HTTP client threads.
Definition: ha_config.h:751
uint32_t sync_timeout_
Timeout for syncing lease database (ms)
Definition: ha_config.h:738
boost::shared_ptr< StateConfig > StateConfigPtr
Pointer to the state configuration.
Definition: ha_config.h:280
bool enable_multi_threading_
Enable multi-threading.
Definition: ha_config.h:748
uint32_t heartbeat_delay_
Heartbeat delay in milliseconds.
Definition: ha_config.h:743
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition: ha_config.cc:224
std::string this_server_name_
This server name.
Definition: ha_config.h:734
boost::shared_ptr< PeerConfig > PeerConfigPtr
Pointer to the server's configuration.
Definition: ha_config.h:229
T get() const
Retrieves the encapsulated value.
Definition: optional.h:112
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:121
TLS API.
const Name & name_
Definition: dns/message.cc:693
#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
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1152
const isc::log::MessageID HA_CONFIG_DHCP_MT_DISABLED
Definition: ha_messages.h:28
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
const isc::log::MessageID HA_CONFIG_SYSTEM_MT_UNSUPPORTED
Definition: ha_messages.h:34
boost::shared_ptr< BasicHttpAuth > BasicHttpAuthPtr
Type of pointers to basic HTTP authentication objects.
Definition: basic_auth.h:70
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
Definition: edns.h:19
StatePausing
State machine pausing modes.
Definition: state_model.h:45
@ STATE_PAUSE_ALWAYS
Definition: state_model.h:46
@ STATE_PAUSE_ONCE
Definition: state_model.h:48
@ STATE_PAUSE_NEVER
Definition: state_model.h:47
Defines the logger used by the top-level component of kea-lfc.
Represents basic HTTP authentication header.
Definition: basic_auth.h:73