Kea 2.7.6
ha_config.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2024 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>
14#include <dhcpsrv/network.h>
17#include <util/str.h>
18#include <ha_log.h>
19#include <ha_config.h>
20#include <ha_service_states.h>
21#include <sstream>
22
23using namespace isc::asiolink;
24using namespace isc::data;
25using namespace isc::dhcp;
26using namespace isc::http;
27using namespace isc::util;
28using namespace isc::dhcp;
29
30namespace isc {
31namespace ha {
32
34 : tls_context_(), name_(), url_(""), trust_anchor_(), cert_file_(),
35 key_file_(), role_(STANDBY), auto_failover_(false), basic_auth_() {
36}
37
38void
39HAConfig::PeerConfig::setName(const std::string& name) {
40 // We want to make sure that someone didn't provide a name that consists of
41 // a single space, tab etc.
42 const std::string& s = util::str::trim(name);
43 if (s.empty()) {
44 isc_throw(BadValue, "peer name must not be empty");
45 }
46 name_ = s;
47}
48
49void
50HAConfig::PeerConfig::setRole(const std::string& role) {
51 role_ = stringToRole(role);
52}
53
54std::string
56 std::ostringstream label;
57 label << getName() << " (" << getUrl().toText() << ")";
58 return (label.str());
59}
60
62HAConfig::PeerConfig::stringToRole(const std::string& role) {
63 if (role == "primary") {
65
66 } else if (role == "secondary") {
68
69 } else if (role == "standby") {
71
72 } else if (role == "backup") {
74
75 }
76
77 // Invalid value specified.
78 isc_throw(BadValue, "unsupported value '" << role << "' for role parameter");
79}
80
81std::string
83 switch (role) {
85 return ("primary");
87 return ("secondary");
89 return ("standby");
91 return ("backup");
92 default:
93 ;
94 }
95 return ("");
96}
97
98void
100 const BasicHttpAuthPtr& auth = getBasicAuth();
101 if (!request || !auth) {
102 return;
103 }
104 request->context()->headers_.push_back(BasicAuthHttpHeaderContext(*auth));
105}
106
108 : state_(state), pausing_(STATE_PAUSE_NEVER) {
109}
110
111void
112HAConfig::StateConfig::setPausing(const std::string& pausing) {
113 pausing_ = stringToPausing(pausing);
114}
115
117HAConfig::StateConfig::stringToPausing(const std::string& pausing) {
118 if (pausing == "always") {
119 return (STATE_PAUSE_ALWAYS);
120
121 } else if (pausing == "never") {
122 return (STATE_PAUSE_NEVER);
123
124 } else if (pausing == "once") {
125 return (STATE_PAUSE_ONCE);
126 }
127
128 isc_throw(BadValue, "unsupported value " << pausing << " of 'pause' parameter");
129}
130
131std::string
133 switch (pausing) {
135 return ("always");
136
138 return ("never");
139
140 case STATE_PAUSE_ONCE:
141 return ("once");
142
143 default:
144 ;
145 }
146
147 isc_throw(BadValue, "unsupported pause enumeration " << static_cast<int>(pausing));
148}
149
152 // Return config for the state if it exists already.
153 auto state_config = states_.find(state);
154 if (state_config != states_.end()) {
155 return (state_config->second);
156 }
157
158 // Create config for the state and store its pointer.
159 StateConfigPtr new_state_config(new StateConfig(state));
160 states_[state] = new_state_config;
161
162 return (new_state_config);
163}
164
176
179 return (boost::make_shared<HAConfig>());
180}
181
183HAConfig::selectNextPeerConfig(const std::string& name) {
184 // Check if there is a configuration for this server name already. We can't
185 // have two servers with the same name.
186 if (peers_.count(name) > 0) {
187 isc_throw(BadValue, "peer with name '" << name << "' already specified");
188 }
189
190 // Name appears to be unique, so let's add it.
191 PeerConfigPtr cfg(new PeerConfig());
192 cfg->setName(name);
193 peers_[name] = cfg;
194
195 // Return this to the caller so as the caller can set parsed configuration
196 // for this peer.
197 return (cfg);
198}
199
200void
201HAConfig::setThisServerName(const std::string& this_server_name) {
202 // Avoid names consisting of spaces, tabs etc.
203 std::string s = util::str::trim(this_server_name);
204 if (s.empty()) {
205 isc_throw(BadValue, "'this-server-name' value must not be empty");
206 }
207
209}
210
211
212void
213HAConfig::setHAMode(const std::string& ha_mode) {
214 ha_mode_ = stringToHAMode(ha_mode);
215}
216
218HAConfig::stringToHAMode(const std::string& ha_mode) {
219 if (ha_mode == "load-balancing") {
220 return (LOAD_BALANCING);
221
222 } else if (ha_mode == "hot-standby") {
223 return (HOT_STANDBY);
224
225 } else if (ha_mode == "passive-backup") {
226 return (PASSIVE_BACKUP);
227 }
228
229 isc_throw(BadValue, "unsupported value '" << ha_mode << "' for mode parameter");
230}
231
232std::string
234 switch (ha_mode) {
235 case LOAD_BALANCING:
236 return ("load-balancing");
237 case HOT_STANDBY:
238 return ("hot-standby");
239 case PASSIVE_BACKUP:
240 return ("passive-backup");
241 default:
242 ;
243 }
244 return ("");
245}
246
248HAConfig::getPeerConfig(const std::string& name) const {
249 auto peer = peers_.find(name);
250 if (peer == peers_.end()) {
251 isc_throw(InvalidOperation, "no configuration specified for server " << name);
252 }
253
254 return (peer->second);
255}
256
260 for (auto const& peer : servers) {
261 if (peer.second->getRole() != HAConfig::PeerConfig::BACKUP) {
262 return (peer.second);
263 }
264 }
265
266 isc_throw(InvalidOperation, "no failover partner server found for this"
267 " server " << getThisServerName());
268}
269
274
278 copy.erase(getThisServerName());
279 return (copy);
280}
281
282void
284 // Peers configurations must be provided.
285 if (peers_.count(getThisServerName()) == 0) {
286 isc_throw(HAConfigValidationError, "no peer configuration specified for the '"
287 << getThisServerName() << "'");
288 }
289
290 // Gather all the roles and see how many occurrences of each role we get.
291 std::map<PeerConfig::Role, unsigned> peers_cnt;
292 for (auto const& p : peers_) {
293 if (!p.second->getUrl().isValid()) {
294 isc_throw(HAConfigValidationError, "invalid URL: "
295 << p.second->getUrl().getErrorMessage()
296 << " for server " << p.second->getName());
297 }
298
299 // The hostname must be an address, not a name.
300 IOAddress addr("::");
301 try {
302 addr = IOAddress(p.second->getUrl().getStrippedHostname());
303 } catch (const IOError& ex) {
305 << p.second->getUrl().toText()
306 << "': " << ex.what()
307 << " for server " << p.second->getName());
308 }
309
310 // Check TLS setup.
311 Optional<std::string> ca = p.second->getTrustAnchor();
312 Optional<std::string> cert = p.second->getCertFile();
313 Optional<std::string> key = p.second->getKeyFile();
314 // When not configured get the value from the global level.
315 if (ca.unspecified()) {
316 ca = trust_anchor_;
317 }
318 if (cert.unspecified()) {
319 cert = cert_file_;
320 }
321 if (key.unspecified()) {
322 key = key_file_;
323 }
324 bool have_ca = (!ca.unspecified() && !ca.get().empty());
325 bool have_cert = (!cert.unspecified() && !cert.get().empty());
326 bool have_key = (!key.unspecified() && !key.get().empty());
327 bool use_tls = (have_ca || have_cert || have_key);
328 if (use_tls) {
329 try {
330 // TLS is used: all 3 parameters are required.
331 if (!have_ca) {
332 isc_throw(HAConfigValidationError, "trust-anchor parameter"
333 << " is missing or empty: all or none of"
334 << " TLS parameters must be set");
335 }
336 if (!have_cert) {
337 isc_throw(HAConfigValidationError, "cert-file parameter"
338 << " is missing or empty: all or none of"
339 << " TLS parameters must be set");
340 }
341 if (!have_key) {
342 isc_throw(HAConfigValidationError, "key-file parameter"
343 << " is missing or empty: all or none of"
344 << " TLS parameters must be set");
345 }
346 TlsRole tls_role = TlsRole::CLIENT;
347 bool cert_required = true;
348 // The peer entry for myself will be used for the server side.
349 if (p.second->getName() == getThisServerName()) {
350 tls_role = TlsRole::SERVER;
351 cert_required = getRequireClientCerts();
352 }
353 TlsContext::configure(p.second->tls_context_,
354 tls_role,
355 ca.get(),
356 cert.get(),
357 key.get(),
358 cert_required);
359 } catch (const isc::Exception& ex) {
360 isc_throw(HAConfigValidationError, "bad TLS config for server "
361 << p.second->getName() << ": " << ex.what());
362 }
363 } else {
364 // Refuse HTTPS scheme when TLS is not enabled.
365 if (p.second->getUrl().getScheme() == Url::HTTPS) {
367 << p.second->getUrl().toText()
368 << "': https scheme is not supported"
369 << " for server " << p.second->getName()
370 << " where TLS is disabled");
371 }
372 }
373
374 ++peers_cnt[p.second->getRole()];
375 }
376
377 // Only one primary server allowed.
378 if (peers_cnt.count(PeerConfig::PRIMARY) && (peers_cnt[PeerConfig::PRIMARY] > 1)) {
379 isc_throw(HAConfigValidationError, "multiple primary servers specified");
380 }
381
382 // Only one secondary server allowed.
383 if (peers_cnt.count(PeerConfig::SECONDARY) && (peers_cnt[PeerConfig::SECONDARY] > 1)) {
384 isc_throw(HAConfigValidationError, "multiple secondary servers specified");
385 }
386
387 // Only one standby server allowed.
388 if (peers_cnt.count(PeerConfig::STANDBY) && (peers_cnt[PeerConfig::STANDBY] > 1)) {
389 isc_throw(HAConfigValidationError, "multiple standby servers specified");
390 }
391
392 if (ha_mode_ == LOAD_BALANCING) {
393 // Standby servers not allowed in load balancing configuration.
394 if (peers_cnt.count(PeerConfig::STANDBY) > 0) {
395 isc_throw(HAConfigValidationError, "standby servers not allowed in the load "
396 "balancing configuration");
397 }
398
399 // Require one secondary server in the load balancing configuration.
400 if (peers_cnt.count(PeerConfig::SECONDARY) == 0) {
401 isc_throw(HAConfigValidationError, "secondary server required in the load"
402 " balancing configuration");
403 }
404
405 // Require one primary server in the load balancing configuration.
406 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
407 isc_throw(HAConfigValidationError, "primary server required in the load"
408 " balancing configuration");
409 }
410
411 // In the load-balancing mode the wait-backup-ack must be false.
412 if (wait_backup_ack_) {
413 isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
414 " load balancing configuration");
415 }
416
417 } else if (ha_mode_ == HOT_STANDBY) {
418 // Secondary servers not allowed in the hot standby configuration.
419 if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
420 isc_throw(HAConfigValidationError, "secondary servers not allowed in the hot"
421 " standby configuration");
422 }
423
424 // Require one standby server in the hot standby configuration.
425 if (peers_cnt.count(PeerConfig::STANDBY) == 0) {
426 isc_throw(HAConfigValidationError, "standby server required in the hot"
427 " standby configuration");
428 }
429
430 // Require one primary server in the hot standby configuration.
431 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
432 isc_throw(HAConfigValidationError, "primary server required in the hot"
433 " standby configuration");
434 }
435
436 // In the hot-standby mode the wait-backup-ack must be false.
437 if (wait_backup_ack_) {
438 isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
439 " hot standby configuration");
440 }
441
442 // The server must not transition to communication-recovery state in
443 // hot-standby mode.
444 if (delayed_updates_limit_ > 0) {
445 isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
446 " the hot standby configuration");
447 }
448
449 } else if (ha_mode_ == PASSIVE_BACKUP) {
450 if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
451 isc_throw(HAConfigValidationError, "secondary servers not allowed in the"
452 " passive backup configuration");
453 }
454
455 if (peers_cnt.count(PeerConfig::STANDBY) > 0) {
456 isc_throw(HAConfigValidationError, "standby servers not allowed in the"
457 " passive backup configuration");
458 }
459
460 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
461 isc_throw(HAConfigValidationError, "primary server required in the"
462 " passive backup configuration");
463 }
464
465 // The server must not transition to communication-recovery state in
466 // passive-backup mode.
467 if (delayed_updates_limit_ > 0) {
468 isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
469 " the passive backup configuration");
470 }
471 }
472
473 // We get it from staging because applying the DHCP multi-threading configuration
474 // occurs after library loading during the (re)configuration process.
475 auto mcfg = CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading();
476 bool dhcp_mt_enabled = false;
477 uint32_t dhcp_threads = 0;
478 uint32_t dummy_queue_size = 0;
479 CfgMultiThreading::extract(mcfg, dhcp_mt_enabled, dhcp_threads, dummy_queue_size);
480
482 if (!dhcp_mt_enabled) {
483 // HA+MT requires DHCP multi-threading.
485 .arg(getThisServerName());
487 return;
488 }
489
490 // When DHCP threads is configured as zero, we should auto-detect.
491 if (!dhcp_threads) {
493 // If machine says it cannot support threads.
494 if (!dhcp_threads) {
496 .arg(getThisServerName());
498 return;
499 }
500 }
501
502 // If http_listener_threads_ is 0, then we use the same number of
503 // threads as DHCP does.
504 if (http_listener_threads_ == 0) {
505 http_listener_threads_ = dhcp_threads;
506 }
507
508 // If http_client_threads_ is 0, then we use the same number of
509 // threads as DHCP does.
510 if (http_client_threads_ == 0) {
511 http_client_threads_ = dhcp_threads;
512 }
513 } else {
514 if (dhcp_mt_enabled) {
515 // DHCP multi-threading requires HA+MT for optimal performance.
517 .arg(getThisServerName());
518 return;
519 }
520 }
521}
522
523std::string
525 const std::string parameter_name = "ha-server-name";
526 auto context = subnet->getContext();
527 if (!context || (context->getType() != Element::map) || !context->contains(parameter_name)) {
528 NetworkPtr shared_network;
529 subnet->getSharedNetwork(shared_network);
530 if (shared_network) {
531 context = shared_network->getContext();
532 }
533 }
534 if (context && (context->getType() == Element::map) && context->contains(parameter_name)) {
535 auto server_name_element = context->get(parameter_name);
536 if ((server_name_element->getType() != Element::string) || server_name_element->stringValue().empty()) {
537 isc_throw(BadValue, "'" << parameter_name << "' must be a non-empty string");
538 }
539 return (server_name_element->stringValue());
540 }
541 return ("");
542}
543
544} // end of namespace isc::ha
545} // 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.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:28
SrvConfigPtr getStagingCfg()
Returns a pointer to the staging configuration.
Definition cfgmgr.cc:120
static void extract(data::ConstElementPtr value, bool &enabled, uint32_t &thread_count, uint32_t &queue_size)
Extract multi-threading parameters from a given configuration.
Exception thrown when configuration validation fails.
Definition ha_config.h:28
HA peer configuration.
Definition ha_config.h:66
std::string getLogLabel() const
Returns a string identifying a server used in logging.
Definition ha_config.cc:55
void addBasicAuthHttpHeader(http::PostHttpRequestJsonPtr request) const
Adds a basic HTTP authentication header to a request when credentials are specified.
Definition ha_config.cc:99
Role
Server's role in the High Availability setup.
Definition ha_config.h:83
void setRole(const std::string &role)
Sets servers role.
Definition ha_config.cc:50
static std::string roleToString(const HAConfig::PeerConfig::Role &role)
Returns role name.
Definition ha_config.cc:82
static Role stringToRole(const std::string &role)
Decodes role provided as a string.
Definition ha_config.cc:62
void setName(const std::string &name)
Sets server name.
Definition ha_config.cc:39
Configuration specific to a single HA state.
Definition ha_config.h:248
static util::StatePausing stringToPausing(const std::string &pausing)
Converts pausing mode from the textual form.
Definition ha_config.cc:117
void setPausing(const std::string &pausing)
Sets pausing mode for the given state.
Definition ha_config.cc:112
StateConfig(const int state)
Constructor.
Definition ha_config.cc:107
static std::string pausingToString(const util::StatePausing &pausing)
Returns pausing mode in the textual form.
Definition ha_config.cc:132
State machine configuration information.
Definition ha_config.h:300
StateConfigPtr getStateConfig(const int state)
Returns pointer to the state specific configuration.
Definition ha_config.cc:151
boost::shared_ptr< StateConfig > StateConfigPtr
Pointer to the state configuration.
Definition ha_config.h:293
uint32_t max_response_delay_
Max delay in response to heartbeats.
Definition ha_config.h:829
uint32_t http_listener_threads_
Number of HTTP listener threads.
Definition ha_config.h:836
static HAConfigPtr create()
Instantiates a HAConfig.
Definition ha_config.cc:178
uint32_t sync_page_limit_
Page size limit while synchronizing leases.
Definition ha_config.h:824
std::string getThisServerName() const
Returns name of this server.
Definition ha_config.h:346
bool require_client_certs_
Require client certs flag.
Definition ha_config.h:841
bool http_dedicated_listener_
Enable use of own HTTP listener.
Definition ha_config.h:835
HAMode
Mode of operation.
Definition ha_config.h:55
std::map< std::string, PeerConfigPtr > PeerConfigMap
Map of the servers' configurations.
Definition ha_config.h:245
void validate()
Validates configuration.
Definition ha_config.cc:283
uint32_t delayed_updates_limit_
Maximum number of lease updates held for later send in communication-recovery.
Definition ha_config.h:826
PeerConfigPtr getThisServerConfig() const
Returns configuration of this server.
Definition ha_config.cc:271
bool getRequireClientCerts() const
Returns require-client-certs.
Definition ha_config.h:721
uint32_t max_rejected_lease_updates_
Limit of rejected lease updates before termination.
Definition ha_config.h:832
void setHAMode(const std::string &ha_mode)
Sets new mode of operation.
Definition ha_config.cc:213
HAMode ha_mode_
Mode of operation.
Definition ha_config.h:820
bool send_lease_updates_
Send lease updates to partner?
Definition ha_config.h:821
uint32_t max_unacked_clients_
Maximum number of unacked clients.
Definition ha_config.h:831
PeerConfigMap peers_
Map of peers' configurations.
Definition ha_config.h:843
uint32_t max_ack_delay_
Maximum DHCP message ack delay.
Definition ha_config.h:830
util::Optional< std::string > cert_file_
Certificate file.
Definition ha_config.h:839
bool restrict_commands_
Restrict commands to HA flag.
Definition ha_config.h:842
void setThisServerName(const std::string &this_server_name)
Sets name of this server.
Definition ha_config.cc:201
PeerConfigMap getOtherServersConfig() const
Returns configuration of other servers.
Definition ha_config.cc:276
PeerConfigPtr getFailoverPeerConfig() const
Returns configuration of the partner which takes part in failover.
Definition ha_config.cc:258
PeerConfigPtr getPeerConfig(const std::string &name) const
Returns configuration of the specified server.
Definition ha_config.cc:248
util::Optional< std::string > key_file_
Private key file.
Definition ha_config.h:840
PeerConfigPtr selectNextPeerConfig(const std::string &name)
Creates and returns pointer to the new peer's configuration.
Definition ha_config.cc:183
bool sync_leases_
Synchronize databases on startup?
Definition ha_config.h:822
bool wait_backup_ack_
Wait for lease update ack from backup?
Definition ha_config.h:833
StateMachineConfigPtr state_machine_
State machine configuration.
Definition ha_config.h:844
HAConfig()
Constructor.
Definition ha_config.cc:165
util::Optional< std::string > trust_anchor_
Trust anchor.
Definition ha_config.h:838
static HAMode stringToHAMode(const std::string &ha_mode)
Decodes HA mode provided as string.
Definition ha_config.cc:218
uint32_t http_client_threads_
Number of HTTP client threads.
Definition ha_config.h:837
uint32_t sync_timeout_
Timeout for syncing lease database (ms)
Definition ha_config.h:823
boost::shared_ptr< PeerConfig > PeerConfigPtr
Pointer to the server's configuration.
Definition ha_config.h:242
bool enable_multi_threading_
Enable multi-threading.
Definition ha_config.h:834
uint32_t heartbeat_delay_
Heartbeat delay in milliseconds.
Definition ha_config.h:828
static std::string getSubnetServerName(const dhcp::ConstSubnetPtr &subnet)
Convenience function extracting a value of the ha-server-name parameter from a subnet context.
Definition ha_config.cc:524
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition ha_config.cc:233
std::string this_server_name_
This server name.
Definition ha_config.h:819
static uint32_t detectThreadCount()
The system current detected hardware concurrency thread count.
A template representing an optional value.
Definition optional.h:36
T get() const
Retrieves the encapsulated value.
Definition optional.h:114
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition optional.h:136
TLS API.
const Name & name_
#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
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition data.cc:1420
boost::shared_ptr< const Subnet > ConstSubnetPtr
A generic pointer to either const Subnet4 or const Subnet6 object.
Definition subnet.h:452
boost::shared_ptr< Network > NetworkPtr
Pointer to the Network object.
Definition network.h:73
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_DHCP_MT_DISABLED_AND_KEA_MT_ENABLED
Definition ha_messages.h:29
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition ha_config.h:37
const isc::log::MessageID HA_CONFIG_SYSTEM_MT_UNSUPPORTED
Definition ha_messages.h:35
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 &input)
Trim leading and trailing spaces.
Definition str.cc:32
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