Kea  1.5.0
ha_config_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2018 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>
12 #include <cc/dhcp_config_error.h>
13 #include <limits>
14 #include <set>
15 
16 using namespace isc::data;
17 using namespace isc::http;
18 
19 namespace {
20 
22 const SimpleDefaults HA_CONFIG_DEFAULTS = {
23  { "send-lease-updates", Element::boolean, "true" },
24  { "sync-leases", Element::boolean, "true" },
25  { "sync-timeout", Element::integer, "60000" },
26  { "sync-page-limit", Element::integer, "10000" },
27  { "heartbeat-delay", Element::integer, "10000" },
28  { "max-response-delay", Element::integer, "60000" },
29  { "max-ack-delay", Element::integer, "10000" },
30  { "max-unacked-clients", Element::integer, "10" }
31 };
32 
34 const SimpleDefaults HA_CONFIG_PEER_DEFAULTS = {
35  { "auto-failover", Element::boolean, "true" }
36 };
37 
39 const SimpleDefaults HA_CONFIG_STATE_DEFAULTS = {
40  { "pause", Element::string, "never" }
41 };
42 
43 } // end of anonymous namespace
44 
45 namespace isc {
46 namespace ha {
47 
48 void
49 HAConfigParser::parse(const HAConfigPtr& config_storage,
50  const ConstElementPtr& config) {
51  try {
52  // This may cause different types of exceptions. We catch them here
53  // and throw unified exception type.
54  parseInternal(config_storage, config);
55  logConfigStatus(config_storage);
56 
57  } catch (const ConfigError& ex) {
58  throw;
59 
60  } catch (const std::exception& ex) {
61  isc_throw(ConfigError, ex.what());
62  }
63 }
64 
65 void
66 HAConfigParser::parseInternal(const HAConfigPtr& config_storage,
67  const ConstElementPtr& config) {
68  // Config must be provided.
69  if (!config) {
70  isc_throw(ConfigError, "HA configuration must not be null");
71  }
72 
73  // Config must be a list. Each contains one relationship between servers in the
74  // HA configuration. Currently we support only one relationship.
75  if (config->getType() != Element::list) {
76  isc_throw(ConfigError, "HA configuration must be a list");
77  }
78 
79  const auto& config_vec = config->listValue();
80  if (config_vec.size() != 1) {
81  isc_throw(ConfigError, "invalid number of configurations in the HA configuration"
82  " list. Expected exactly one configuration");
83  }
84 
85  // Get the HA configuration.
86  ElementPtr c = config_vec[0];
87 
88  // Set default values.
89  setDefaults(c, HA_CONFIG_DEFAULTS);
90 
91  // HA configuration must be a map.
92  if (c->getType() != Element::map) {
93  isc_throw(ConfigError, "expected list of maps in the HA configuration");
94  }
95 
96  // It must contains peers section.
97  if (!c->contains("peers")) {
98  isc_throw(ConfigError, "'peers' parameter missing in HA configuration");
99  }
100 
101  // Peers configuration must be a list of maps.
102  ConstElementPtr peers = c->get("peers");
103  if (peers->getType() != Element::list) {
104  isc_throw(ConfigError, "'peers' parameter must be a list");
105  }
106 
107  // State machine configuration must be a map.
108  ConstElementPtr state_machine = c->get("state-machine");
109  ConstElementPtr states_list;
110  if (state_machine) {
111  if (state_machine->getType() != Element::map) {
112  isc_throw(ConfigError, "'state-machine' parameter must be a map");
113  }
114 
115  states_list = state_machine->get("states");
116  if (states_list && (states_list->getType() != Element::list)) {
117  isc_throw(ConfigError, "'states' parameter must be a list");
118  }
119  }
120 
121 
122  // We have made major sanity checks, so let's try to gather some values.
123 
124  // Get 'this-server-name'.
125  config_storage->setThisServerName(getString(c, "this-server-name"));
126 
127  // Get 'mode'.
128  config_storage->setHAMode(getString(c, "mode"));
129 
130  // Get 'send-lease-updates'.
131  config_storage->setSendLeaseUpdates(getBoolean(c, "send-lease-updates"));
132 
133  // Get 'sync-leases'.
134  config_storage->setSyncLeases(getBoolean(c, "sync-leases"));
135 
136  // Get 'sync-timeout'.
137  uint32_t sync_timeout = getAndValidateInteger<uint32_t>(c, "sync-timeout");
138  config_storage->setSyncTimeout(sync_timeout);
139 
140  // Get 'sync-page-limit'.
141  uint32_t sync_page_limit = getAndValidateInteger<uint32_t>(c, "sync-page-limit");
142  config_storage->setSyncPageLimit(sync_page_limit);
143 
144  // Get 'heartbeat-delay'.
145  uint16_t heartbeat_delay = getAndValidateInteger<uint16_t>(c, "heartbeat-delay");
146  config_storage->setHeartbeatDelay(heartbeat_delay);
147 
148  // Get 'max-response-delay'.
149  uint16_t max_response_delay = getAndValidateInteger<uint16_t>(c, "max-response-delay");
150  config_storage->setMaxResponseDelay(max_response_delay);
151 
152  // Get 'max-ack-delay'.
153  uint16_t max_ack_delay = getAndValidateInteger<uint16_t>(c, "max-ack-delay");
154  config_storage->setMaxAckDelay(max_ack_delay);
155 
156  // Get 'max-unacked-clients'.
157  uint32_t max_unacked_clients = getAndValidateInteger<uint32_t>(c, "max-unacked-clients");
158  config_storage->setMaxUnackedClients(max_unacked_clients);
159 
160  // Peers configuration parsing.
161  const auto& peers_vec = peers->listValue();
162 
163  // There must be at least two peers specified.
164  if (peers_vec.size() < 2) {
165  isc_throw(ConfigError, "peers configuration requires at least two peers"
166  " to be specified");
167  }
168 
169  // Go over configuration of each peer.
170  for (auto p = peers_vec.begin(); p != peers_vec.end(); ++p) {
171 
172  // Peer configuration is held in a map.
173  if ((*p)->getType() != Element::map) {
174  isc_throw(ConfigError, "peer configuration must be a map");
175  }
176 
177  setDefaults(*p, HA_CONFIG_PEER_DEFAULTS);
178 
179  // Server name.
180  auto cfg = config_storage->selectNextPeerConfig(getString(*p, "name"));
181 
182  // URL.
183  cfg->setUrl(Url(getString((*p), "url")));
184 
185  // Role.
186  cfg->setRole(getString(*p, "role"));
187 
188  // Auto failover configuration.
189  cfg->setAutoFailover(getBoolean(*p, "auto-failover"));
190  }
191 
192  // Per state configuration is optional.
193  if (states_list) {
194  const auto& states_vec = states_list->listValue();
195 
196  std::set<int> configured_states;
197 
198  // Go over per state configurations.
199  for (auto s = states_vec.begin(); s != states_vec.end(); ++s) {
200 
201  // State configuration is held in map.
202  if ((*s)->getType() != Element::map) {
203  isc_throw(ConfigError, "state configuration must be a map");
204  }
205 
206  setDefaults(*s, HA_CONFIG_STATE_DEFAULTS);
207 
208  // Get state name and set per state configuration.
209  std::string state_name = getString(*s, "state");
210 
211  int state = stringToState(state_name);
212  // Check that this configuration doesn't duplicate existing configuration.
213  if (configured_states.count(state) > 0) {
214  isc_throw(ConfigError, "duplicated configuration for the '"
215  << state_name << "' state");
216  }
217  configured_states.insert(state);
218 
219  config_storage->getStateMachineConfig()->
220  getStateConfig(state)->setPausing(getString(*s, "pause"));
221  }
222  }
223 
224  // We have gone over the entire configuration and stored it in the configuration
225  // storage. However, we need to still validate it to detect errors like:
226  // duplicate secondary/primary servers, no configuration for this server etc.
227  config_storage->validate();
228 }
229 
230 template<typename T>
231 T HAConfigParser::getAndValidateInteger(const ConstElementPtr& config,
232  const std::string& parameter_name) const {
233  int64_t value = getInteger(config, parameter_name);
234  if (value < 0) {
235  isc_throw(ConfigError, "'" << parameter_name << "' must not be negative");
236 
237  } else if (value > std::numeric_limits<T>::max()) {
238  isc_throw(ConfigError, "'" << parameter_name << "' must not be greater than "
239  << std::numeric_limits<T>::max());
240  }
241 
242  return (static_cast<T>(value));
243 }
244 
245 void
246 HAConfigParser::logConfigStatus(const HAConfigPtr& config_storage) const {
247  LOG_INFO(ha_logger, HA_CONFIGURATION_SUCCESSFUL);
248 
249  // If lease updates are disabled, we want to make sure that the user
250  // realizes that and that he has configured some other mechanism to
251  // populate leases.
252  if (!config_storage->amSendingLeaseUpdates()) {
253  LOG_WARN(ha_logger, HA_CONFIG_LEASE_UPDATES_DISABLED);
254  }
255 
256  // Same as above but for lease database synchronization.
257  if (!config_storage->amSyncingLeases()) {
258  LOG_WARN(ha_logger, HA_CONFIG_LEASE_SYNCING_DISABLED);
259  }
260 
261  // Unusual configuration.
262  if (config_storage->amSendingLeaseUpdates() !=
263  config_storage->amSyncingLeases()) {
264  LOG_WARN(ha_logger, HA_CONFIG_LEASE_UPDATES_AND_SYNCING_DIFFER)
265  .arg(config_storage->amSendingLeaseUpdates() ? "true" : "false")
266  .arg(config_storage->amSyncingLeases() ? "true" : "false");
267  }
268 
269  // With this setting the server will not take ownership of the partner's
270  // scope in case of partner's failure. This setting is ok if the
271  // administrator desires to have more control over scopes selection.
272  // The administrator will need to send ha-scopes command to instruct
273  // the server to take ownership of the scope. In some cases he may
274  // also need to send dhcp-enable command to enable DHCP service
275  // (specifically hot-standby mode for standby server).
276  if (!config_storage->getThisServerConfig()->isAutoFailover()) {
277  LOG_WARN(ha_logger, HA_CONFIG_AUTO_FAILOVER_DISABLED)
278  .arg(config_storage->getThisServerName());
279  }
280 }
281 
282 } // end of namespace ha
283 } // end of namespace isc
isc::ConfigError
An exception that is thrown if an error occurs while configuring any server.
Definition: dhcp_config_error.h:43
ha_log.h
isc::ha::stringToState
int stringToState(const std::string &state_name)
Returns state for a given name.
Definition: ha_service_states.cc:39
dhcp_config_error.h
isc::http::Url
Represents an URL.
Definition: url.h:20
isc::data::Element::list
@ list
Definition: data.h:151
isc::data::SimpleDefaults
std::vector< SimpleDefault > SimpleDefaults
This specifies all default values in a given scope (e.g. a subnet)
Definition: lib/cc/simple_parser.h:31
isc::data
Definition: cfg_to_element.h:25
isc::ha::ha_logger
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
ha_config_parser.h
LOG_WARN
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
isc::data::Element::boolean
@ boolean
Definition: data.h:151
isc::data::Element::string
@ string
Definition: data.h:151
isc::ha::HAConfigPtr
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:509
isc::data::Element::map
@ map
Definition: data.h:151
isc::data::Element::integer
@ integer
Definition: data.h:151
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
isc::http
Definition: client.cc:745
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
ha_service_states.h
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20