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>
13#include <limits>
14#include <set>
15
16using namespace isc::data;
17using namespace isc::http;
18
19namespace {
20
22const 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
34const SimpleDefaults HA_CONFIG_PEER_DEFAULTS = {
35 { "auto-failover", Element::boolean, "true" }
36};
37
39const SimpleDefaults HA_CONFIG_STATE_DEFAULTS = {
40 { "pause", Element::string, "never" }
41};
42
43} // end of anonymous namespace
44
45namespace isc {
46namespace ha {
47
48void
49HAConfigParser::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) {
62 }
63}
64
65void
66HAConfigParser::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
230template<typename T>
231T 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
245void
246HAConfigParser::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
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 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:23
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:22
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
boost::shared_ptr< HAConfig > HAConfigPtr
Pointer to the High Availability configuration structure.
Definition: ha_config.h:509
int stringToState(const std::string &state_name)
Returns state for a given name.
Defines the logger used by the top-level component of kea-dhcp-ddns.