Kea  1.9.9-git
ha_config.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 <asiolink/io_address.h>
10 #include <asiolink/io_error.h>
11 #include <asiolink/crypto_tls.h>
12 #include <dhcpsrv/cfgmgr.h>
14 #include <exceptions/exceptions.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 
22 using namespace isc::asiolink;
23 using namespace isc::http;
24 using namespace isc::util;
25 using namespace isc::dhcp;
26 
27 namespace isc {
28 namespace ha {
29 
30 HAConfig::PeerConfig::PeerConfig()
31  : tls_context_(), name_(), url_(""), trust_anchor_(), cert_file_(),
32  key_file_(), role_(STANDBY), auto_failover_(false), basic_auth_() {
33 }
34 
35 void
36 HAConfig::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 
46 void
47 HAConfig::PeerConfig::setRole(const std::string& role) {
48  role_ = stringToRole(role);
49 }
50 
51 std::string
53  std::ostringstream label;
54  label << getName() << " (" << getUrl().toText() << ")";
55  return (label.str());
56 }
57 
59 HAConfig::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 
78 std::string
80  switch (role) {
82  return ("primary");
84  return ("secondary");
86  return ("standby");
88  return ("backup");
89  default:
90  ;
91  }
92  return ("");
93 }
94 
95 void
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 
108 void
109 HAConfig::StateConfig::setPausing(const std::string& pausing) {
110  pausing_ = stringToPausing(pausing);
111 }
112 
114 HAConfig::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 
128 std::string
130  switch (pausing) {
131  case STATE_PAUSE_ALWAYS:
132  return ("always");
133 
134  case STATE_PAUSE_NEVER:
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 
174 HAConfig::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 
191 void
192 HAConfig::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 
199  this_server_name_ = s;
200 }
201 
202 
203 void
204 HAConfig::setHAMode(const std::string& ha_mode) {
205  ha_mode_ = stringToHAMode(ha_mode);
206 }
207 
209 HAConfig::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 
223 std::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 
239 HAConfig::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 
263  return (getPeerConfig(getThisServerName()));
264 }
265 
269  copy.erase(getThisServerName());
270  return (copy);
271 }
272 
273 void
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().getHostname());
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.
468  enable_multi_threading_ = false;
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) {
478  enable_multi_threading_ = false;
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
uint32_t max_unacked_clients_
Maximum number of unacked clients.
Definition: ha_config.h:746
Configuration specific to a single HA state.
Definition: ha_config.h:235
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:121
StatePausing
State machine pausing modes.
Definition: state_model.h:45
PeerConfigPtr getFailoverPeerConfig() const
Returns configuration of the partner which takes part in failover.
Definition: ha_config.cc:249
std::string getThisServerName() const
Returns name of this server.
Definition: ha_config.h:330
PeerConfigMap peers_
Map of peers' configurations.
Definition: ha_config.h:755
void setThisServerName(const std::string &this_server_name)
Sets name of this server.
Definition: ha_config.cc:192
void setName(const std::string &name)
Sets server name.
Definition: ha_config.cc:36
State machine configuration information.
Definition: ha_config.h:287
void setHAMode(const std::string &ha_mode)
Sets new mode of operation.
Definition: ha_config.cc:204
bool enable_multi_threading_
Enable multi-threading.
Definition: ha_config.h:748
void setPausing(const std::string &pausing)
Sets pausing mode for the given state.
Definition: ha_config.cc:109
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
std::map< std::string, PeerConfigPtr > PeerConfigMap
Map of the servers' configurations.
Definition: ha_config.h:232
static Role stringToRole(const std::string &role)
Decodes role provided as a string.
Definition: ha_config.cc:59
std::string this_server_name_
This server name.
Definition: ha_config.h:734
util::Optional< std::string > key_file_
Private key file.
Definition: ha_config.h:754
StateConfigPtr getStateConfig(const int state)
Returns pointer to the state specific configuration.
Definition: ha_config.cc:148
StateConfig(const int state)
Constructor.
Definition: ha_config.cc:104
util::Optional< std::string > cert_file_
Certificate file.
Definition: ha_config.h:753
HAMode
Mode of operation.
Definition: ha_config.h:42
uint32_t max_response_delay_
Max delay in response to heartbeats.
Definition: ha_config.h:744
Represents basic HTTP authentication header.
Definition: basic_auth.h:73
bool sync_leases_
Synchronize databases on startup?
Definition: ha_config.h:737
bool http_dedicated_listener_
Enable use of own HTTP listener.
Definition: ha_config.h:749
bool wait_backup_ack_
Wait for lease update ack from backup?
Definition: ha_config.h:747
util::Optional< std::string > trust_anchor_
Trust anchor.
Definition: ha_config.h:752
StateMachineConfigPtr state_machine_
State machine configuration.
Definition: ha_config.h:756
uint32_t sync_page_limit_
Page size limit while synchronizing leases.
Definition: ha_config.h:739
PeerConfigPtr getPeerConfig(const std::string &name) const
Returns configuration of the specified server.
Definition: ha_config.cc:239
uint32_t heartbeat_delay_
Heartbeat delay in milliseconds.
Definition: ha_config.h:743
uint32_t max_ack_delay_
Maximum DHCP message ack delay.
Definition: ha_config.h:745
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Exception thrown when configuration validation fails.
Definition: ha_config.h:26
Definition: edns.h:19
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
uint32_t sync_timeout_
Timeout for syncing lease database (ms)
Definition: ha_config.h:738
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1097
PeerConfigMap getOtherServersConfig() const
Returns configuration of other servers.
Definition: ha_config.cc:267
HAConfig()
Constructor.
Definition: ha_config.cc:162
void addBasicAuthHttpHeader(http::PostHttpRequestJsonPtr request) const
Adds a basic HTTP authentication header to a request when credentials are specified.
Definition: ha_config.cc:96
boost::shared_ptr< StateConfig > StateConfigPtr
Pointer to the state configuration.
Definition: ha_config.h:280
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
bool send_lease_updates_
Send lease updates to partner?
Definition: ha_config.h:736
const isc::log::MessageID HA_CONFIG_DHCP_MT_DISABLED
Definition: ha_messages.h:28
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-dhcp-ddns.
T get() const
Retrieves the encapsulated value.
Definition: optional.h:112
std::string getLogLabel() const
Returns a string identifying a server used in logging.
Definition: ha_config.cc:52
TLS API.
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition: ha_config.cc:224
const Name & name_
Definition: dns/message.cc:693
void setRole(const std::string &role)
Sets servers role.
Definition: ha_config.cc:47
A generic exception that is thrown if a function is called in a prohibited way.
boost::shared_ptr< BasicHttpAuth > BasicHttpAuthPtr
Type of pointers to basic HTTP authentication objects.
Definition: basic_auth.h:70
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
Role
Server's role in the High Availability setup.
Definition: ha_config.h:70
static std::string pausingToString(const util::StatePausing &pausing)
Returns pausing mode in the textual form.
Definition: ha_config.cc:129
uint32_t delayed_updates_limit_
Maximum number of lease updates held for later send in communication-recovery.
Definition: ha_config.h:741
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
static std::string roleToString(const HAConfig::PeerConfig::Role &role)
Returns role name.
Definition: ha_config.cc:79
PeerConfigPtr selectNextPeerConfig(const std::string &name)
Creates and returns pointer to the new peer's configuration.
Definition: ha_config.cc:174
static util::StatePausing stringToPausing(const std::string &pausing)
Converts pausing mode from the textual form.
Definition: ha_config.cc:114
uint32_t http_listener_threads_
Number of HTTP listener threads.
Definition: ha_config.h:750
void validate()
Validates configuration.
Definition: ha_config.cc:274
const isc::log::MessageID HA_CONFIG_SYSTEM_MT_UNSUPPORTED
Definition: ha_messages.h:34
uint32_t http_client_threads_
Number of HTTP client threads.
Definition: ha_config.h:751
static HAMode stringToHAMode(const std::string &ha_mode)
Decodes HA mode provided as string.
Definition: ha_config.cc:209
PeerConfigPtr getThisServerConfig() const
Returns configuration of this server.
Definition: ha_config.cc:262
HAMode ha_mode_
Mode of operation.
Definition: ha_config.h:735
boost::shared_ptr< PeerConfig > PeerConfigPtr
Pointer to the server's configuration.
Definition: ha_config.h:229
HA peer configuration.
Definition: ha_config.h:53