diff options
author | Marcin Siodelski <marcin@isc.org> | 2018-07-18 12:28:42 +0200 |
---|---|---|
committer | Marcin Siodelski <marcin@isc.org> | 2018-07-18 12:28:42 +0200 |
commit | a5fab941c35ed179ad2343ce5f1da9574ab49e23 (patch) | |
tree | fafc9b998d62339218cd373b471967432b349c20 /src/hooks | |
parent | [5674] Pause in terminated and backup state. (diff) | |
download | kea-a5fab941c35ed179ad2343ce5f1da9574ab49e23.tar.xz kea-a5fab941c35ed179ad2343ce5f1da9574ab49e23.zip |
[5674] Added extra level of hierarchy in state machine configuration.
Per review comments.
Diffstat (limited to 'src/hooks')
7 files changed, 222 insertions, 127 deletions
diff --git a/src/hooks/dhcp/high_availability/ha_config.cc b/src/hooks/dhcp/high_availability/ha_config.cc index 93239a8bd9..ca1d6076d3 100644 --- a/src/hooks/dhcp/high_availability/ha_config.cc +++ b/src/hooks/dhcp/high_availability/ha_config.cc @@ -10,21 +10,6 @@ #include <ha_service_states.h> #include <sstream> -namespace { - -/// @brief Creates default state configuration. -/// -/// @param [out] state_map map of state configurations into which the -/// newly created configuration should be inserted. -/// @param state state for which new configuration is to be created. -void -createStateConfig(isc::ha::HAConfig::StateConfigMap& state_map, const int state) { - isc::ha::HAConfig::StateConfigPtr cfg(new isc::ha::HAConfig::StateConfig(state)); - state_map[state] = cfg; -} - -} // end of anonymous namespace - namespace isc { namespace ha { @@ -135,21 +120,26 @@ HAConfig::StateConfig::pausingToString(const HAConfig::StateConfig::Pausing& pau isc_throw(BadValue, "unsupported pause enumeration " << static_cast<int>(pausing)); } +HAConfig::StateConfigPtr +HAConfig::StateMachineConfig::getStateConfig(const int state) { + // Return config for the state if it exists already. + auto state_config = states_.find(state); + if (state_config != states_.end()) { + return (state_config->second); + } + + // Create config for the state and store its pointer. + StateConfigPtr new_state_config(new StateConfig(state)); + states_[state] = new_state_config; + + return (new_state_config); +} + HAConfig::HAConfig() : this_server_name_(), ha_mode_(HOT_STANDBY), send_lease_updates_(true), sync_leases_(true), sync_timeout_(60000), heartbeat_delay_(10000), max_response_delay_(60000), max_ack_delay_(10000), max_unacked_clients_(10), - peers_(), state_machine_() { - - // Create default state configurations. - createStateConfig(state_machine_, HA_BACKUP_ST); - createStateConfig(state_machine_, HA_HOT_STANDBY_ST); - createStateConfig(state_machine_, HA_LOAD_BALANCING_ST); - createStateConfig(state_machine_, HA_PARTNER_DOWN_ST); - createStateConfig(state_machine_, HA_READY_ST); - createStateConfig(state_machine_, HA_SYNCING_ST); - createStateConfig(state_machine_, HA_TERMINATED_ST); - createStateConfig(state_machine_, HA_WAITING_ST); + peers_(), state_machine_(new StateMachineConfig()) { } HAConfig::PeerConfigPtr @@ -247,17 +237,6 @@ HAConfig::getOtherServersConfig() const { return (copy); } -HAConfig::StateConfigPtr -HAConfig::getStateConfig(const int state) const { - auto state_config = state_machine_.find(state); - if (state_config == state_machine_.end()) { - isc_throw(BadValue, "no state machine configuration found for the " - << "state identifier " << state); - } - - return (state_config->second); -} - void HAConfig::validate() const { // Peers configurations must be provided. diff --git a/src/hooks/dhcp/high_availability/ha_config.h b/src/hooks/dhcp/high_availability/ha_config.h index f7203b2135..5a36c1dde0 100644 --- a/src/hooks/dhcp/high_availability/ha_config.h +++ b/src/hooks/dhcp/high_availability/ha_config.h @@ -221,8 +221,37 @@ public: /// @brief Pointer to the state configuration. typedef boost::shared_ptr<StateConfig> StateConfigPtr; - /// @brief Map of configuration for supported states. - typedef std::map<int, StateConfigPtr> StateConfigMap; + /// @brief State machine configuration information. + /// + /// Currently it merely contains a collection of states specific + /// configurations. In the future it may also contain global + /// state machine configuration parameters. + class StateMachineConfig { + public: + + /// @brief Constructor. + StateMachineConfig() + : states_() { + } + + /// @brief Returns pointer to the state specific configuration. + /// + /// If requested configuration doesn't exist yet, it is created. + /// + /// @param state identifier of the state for which configuration + /// object should be returned. + /// + /// @return Pointer to the state configuration. + StateConfigPtr getStateConfig(const int state); + + private: + + /// @brief Map of configuration for supported states. + std::map<int, StateConfigPtr> states_; + }; + + /// @brief Pointer to a state machine configuration. + typedef boost::shared_ptr<StateMachineConfig> StateMachineConfigPtr; /// @brief Constructor. HAConfig(); @@ -444,19 +473,10 @@ public: return (peers_); } - /// @brief Returns HA state configuration by state identifier. - /// - /// @param state identifier of the state for which configuration should - /// be returned. - /// - /// @return Pointer to the state configuration. - /// @throw BadValue if there is no configuration found for the given state. - StateConfigPtr getStateConfig(const int state) const; - /// @brief Returns state machine configuration. /// - /// @return Map of pointers to the configuration of all states. - StateConfigMap getStateMachineConfig() const { + /// @return Pointer to the state machine configuration. + StateMachineConfigPtr getStateMachineConfig() const { return (state_machine_); } @@ -465,17 +485,17 @@ public: /// @throw HAConfigValidationError if configuration is invalid. void validate() const; - std::string this_server_name_; ///< This server name. - HAMode ha_mode_; ///< Mode of operation. - bool send_lease_updates_; ///< Send lease updates to partner? - bool sync_leases_; ///< Synchronize databases on startup? - uint32_t sync_timeout_; ///< Timeout for syncing lease database (ms) - uint32_t heartbeat_delay_; ///< Heartbeat delay in milliseconds. - uint32_t max_response_delay_; ///< Max delay in response to heartbeats. - uint32_t max_ack_delay_; ///< Maximum DHCP message ack delay. - uint32_t max_unacked_clients_; ///< Maximum number of unacked clients. - PeerConfigMap peers_; ///< Map of peers' configurations. - StateConfigMap state_machine_; ///< Map of per states configurations. + std::string this_server_name_; ///< This server name. + HAMode ha_mode_; ///< Mode of operation. + bool send_lease_updates_; ///< Send lease updates to partner? + bool sync_leases_; ///< Synchronize databases on startup? + uint32_t sync_timeout_; ///< Timeout for syncing lease database (ms) + uint32_t heartbeat_delay_; ///< Heartbeat delay in milliseconds. + uint32_t max_response_delay_; ///< Max delay in response to heartbeats. + uint32_t max_ack_delay_; ///< Maximum DHCP message ack delay. + uint32_t max_unacked_clients_; ///< Maximum number of unacked clients. + PeerConfigMap peers_; ///< Map of peers' configurations. + StateMachineConfigPtr state_machine_; ///< State machine configuration. }; /// @brief Pointer to the High Availability configuration structure. diff --git a/src/hooks/dhcp/high_availability/ha_config_parser.cc b/src/hooks/dhcp/high_availability/ha_config_parser.cc index 3da6af9a7e..b7e41dccb8 100644 --- a/src/hooks/dhcp/high_availability/ha_config_parser.cc +++ b/src/hooks/dhcp/high_availability/ha_config_parser.cc @@ -103,12 +103,21 @@ HAConfigParser::parseInternal(const HAConfigPtr& config_storage, isc_throw(ConfigError, "'peers' parameter must be a list"); } - // State machine configuration must be a list of maps. + // State machine configuration must be a map. ConstElementPtr state_machine = c->get("state-machine"); - if (state_machine && state_machine->getType() != Element::list) { - isc_throw(ConfigError, "'state-machine' parameter must be a list"); + ConstElementPtr states_list; + if (state_machine) { + if (state_machine->getType() != Element::map) { + isc_throw(ConfigError, "'state-machine' parameter must be a map"); + } + + states_list = state_machine->get("states"); + if (states_list && (states_list->getType() != Element::list)) { + isc_throw(ConfigError, "'states' parameter must be a list"); + } } + // We have made major sanity checks, so let's try to gather some values. // Get 'this-server-name'. @@ -176,13 +185,13 @@ HAConfigParser::parseInternal(const HAConfigPtr& config_storage, } // Per state configuration is optional. - if (state_machine) { - const auto& state_machine_vec = state_machine->listValue(); + if (states_list) { + const auto& states_vec = states_list->listValue(); std::set<int> configured_states; // Go over per state configurations. - for (auto s = state_machine_vec.begin(); s != state_machine_vec.end(); ++s) { + for (auto s = states_vec.begin(); s != states_vec.end(); ++s) { // State configuration is held in map. if ((*s)->getType() != Element::map) { @@ -202,7 +211,8 @@ HAConfigParser::parseInternal(const HAConfigPtr& config_storage, } configured_states.insert(state); - config_storage->getStateConfig(state)->setPausing(getString(*s, "pause")); + config_storage->getStateMachineConfig()-> + getStateConfig(state)->setPausing(getString(*s, "pause")); } } diff --git a/src/hooks/dhcp/high_availability/ha_state_machine_control.cc b/src/hooks/dhcp/high_availability/ha_state_machine_control.cc index 8033991a86..6c9f95a431 100644 --- a/src/hooks/dhcp/high_availability/ha_state_machine_control.cc +++ b/src/hooks/dhcp/high_availability/ha_state_machine_control.cc @@ -20,7 +20,8 @@ void HAStateMachineControl::notify(const int state) { // Always get the configuration to verify that the state identifier is // recognized. - HAConfig::StateConfigPtr state_config = config_->getStateConfig(state); + HAConfig::StateConfigPtr state_config = config_->getStateMachineConfig()-> + getStateConfig(state); // Pause if we should always pause in this state or we should pause once // and this is the first time we visit this state. diff --git a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc index 6a732f3198..ecb51e66e9 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc @@ -89,20 +89,22 @@ TEST_F(HAConfigTest, configureLoadBalancing) { " \"auto-failover\": false" " }" " ]," - " \"state-machine\": [" - " {" - " \"state\": \"waiting\"," - " \"pause\": \"once\"" - " }," - " {" - " \"state\": \"ready\"," - " \"pause\": \"always\"" - " }," - " {" - " \"state\": \"partner-down\"," - " \"pause\": \"never\"" - " }" - " ]" + " \"state-machine\": {" + " \"states\": [" + " {" + " \"state\": \"waiting\"," + " \"pause\": \"once\"" + " }," + " {" + " \"state\": \"ready\"," + " \"pause\": \"always\"" + " }," + " {" + " \"state\": \"partner-down\"," + " \"pause\": \"never\"" + " }" + " ]" + " }" " }" "]"; @@ -142,34 +144,41 @@ TEST_F(HAConfigTest, configureLoadBalancing) { EXPECT_EQ(HAConfig::PeerConfig::BACKUP, cfg->getRole()); EXPECT_FALSE(cfg->isAutoFailover()); - // Verify that per-state configuration is correct. + // Verify that per-state configuration is correct.x HAConfig::StateConfigPtr state_cfg; - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_BACKUP_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_BACKUP_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_LOAD_BALANCING_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_LOAD_BALANCING_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_PARTNER_DOWN_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_PARTNER_DOWN_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_READY_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_READY_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_ALWAYS, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_SYNCING_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_SYNCING_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_TERMINATED_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_TERMINATED_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_WAITING_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_WAITING_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_ONCE, state_cfg->getPausing()); } @@ -237,31 +246,38 @@ TEST_F(HAConfigTest, configureHotStandby) { EXPECT_FALSE(cfg->isAutoFailover()); HAConfig::StateConfigPtr state_cfg; - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_BACKUP_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_BACKUP_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_HOT_STANDBY_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_HOT_STANDBY_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_PARTNER_DOWN_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_PARTNER_DOWN_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_READY_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_READY_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_SYNCING_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_SYNCING_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_TERMINATED_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_TERMINATED_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); - ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateConfig(HA_WAITING_ST)); + ASSERT_NO_THROW(state_cfg = impl->getConfig()->getStateMachineConfig()-> + getStateConfig(HA_WAITING_ST)); ASSERT_TRUE(state_cfg); EXPECT_EQ(HAConfig::StateConfig::PAUSE_NEVER, state_cfg->getPausing()); } @@ -757,8 +773,8 @@ TEST_F(HAConfigTest, hotStandbySecondary) { "secondary servers not allowed in the hot standby configuration"); } -// State name must be recognized. -TEST_F(HAConfigTest, invalidStateName) { +// state-machine parameter must be a map. +TEST_F(HAConfigTest, invalidStateMachine) { testInvalidConfig( "[" " {" @@ -786,11 +802,11 @@ TEST_F(HAConfigTest, invalidStateName) { " ]" " }" "]", - "unknown state foo"); + "'state-machine' parameter must be a map"); } -// Pause value must be recognized. -TEST_F(HAConfigTest, invalidPauseValue) { +// states within state-machine must be a list. +TEST_F(HAConfigTest, invalidStatesList) { testInvalidConfig( "[" " {" @@ -810,19 +826,17 @@ TEST_F(HAConfigTest, invalidPauseValue) { " \"auto-failover\": true" " }" " ]," - " \"state-machine\": [" - " {" - " \"state\": \"waiting\"," - " \"pause\": \"foo\"" + " \"state-machine\": {" + " \"states\": {" " }" - " ]" + " }" " }" "]", - "unsupported value foo of 'pause' parameter"); + "'states' parameter must be a list"); } -// Must not specify configuration for the same state twice. -TEST_F(HAConfigTest, duplicatedStates) { +// State name must be recognized. +TEST_F(HAConfigTest, invalidStateName) { testInvalidConfig( "[" " {" @@ -842,20 +856,90 @@ TEST_F(HAConfigTest, duplicatedStates) { " \"auto-failover\": true" " }" " ]," - " \"state-machine\": [" + " \"state-machine\": {" + " \"states\": [" + " {" + " \"state\": \"foo\"," + " \"pause\": \"always\"" + " }" + " ]" + " }" + " }" + "]", + "unknown state foo"); +} + +// Pause value must be recognized. +TEST_F(HAConfigTest, invalidPauseValue) { + testInvalidConfig( + "[" + " {" + " \"this-server-name\": \"server1\"," + " \"mode\": \"hot-standby\"," + " \"peers\": [" " {" - " \"state\": \"waiting\"," - " \"pause\": \"always\"" + " \"name\": \"server1\"," + " \"url\": \"http://127.0.0.1:8080/\"," + " \"role\": \"primary\"," + " \"auto-failover\": false" " }," " {" - " \"state\": \"ready\"," - " \"pause\": \"always\"" + " \"name\": \"server2\"," + " \"url\": \"http://127.0.0.1:8081/\"," + " \"role\": \"standby\"," + " \"auto-failover\": true" + " }" + " ]," + " \"state-machine\": {" + " \"states\": [" + " {" + " \"state\": \"waiting\"," + " \"pause\": \"foo\"" + " }" + " ]" + " }" + " }" + "]", + "unsupported value foo of 'pause' parameter"); +} + +// Must not specify configuration for the same state twice. +TEST_F(HAConfigTest, duplicatedStates) { + testInvalidConfig( + "[" + " {" + " \"this-server-name\": \"server1\"," + " \"mode\": \"hot-standby\"," + " \"peers\": [" + " {" + " \"name\": \"server1\"," + " \"url\": \"http://127.0.0.1:8080/\"," + " \"role\": \"primary\"," + " \"auto-failover\": false" " }," " {" - " \"state\": \"waiting\"," - " \"pause\": \"always\"" + " \"name\": \"server2\"," + " \"url\": \"http://127.0.0.1:8081/\"," + " \"role\": \"standby\"," + " \"auto-failover\": true" " }" - " ]" + " ]," + " \"state-machine\": {" + " \"states\": [" + " {" + " \"state\": \"waiting\"," + " \"pause\": \"always\"" + " }," + " {" + " \"state\": \"ready\"," + " \"pause\": \"always\"" + " }," + " {" + " \"state\": \"waiting\"," + " \"pause\": \"always\"" + " }" + " ]" + " }" " }" "]", "duplicated configuration for the 'waiting' state"); diff --git a/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc index 1fcfaa630b..12b7bbd525 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc @@ -3553,12 +3553,13 @@ TEST_F(HAServiceStateMachineTest, stateTransitionsLoadBalancingPause) { partner_->startup(); HAConfigPtr valid_config = createValidConfiguration(); - auto state_configs = valid_config->getStateMachineConfig(); + auto state_machine = valid_config->getStateMachineConfig(); - // Set state machine pausing in all states. - for (auto cfg = state_configs.begin(); cfg != state_configs.end(); ++cfg) { - cfg->second->setPausing("always"); - } + // Set state machine pausing in various states. + state_machine->getStateConfig(HA_LOAD_BALANCING_ST)->setPausing("always"); + state_machine->getStateConfig(HA_PARTNER_DOWN_ST)->setPausing("always"); + state_machine->getStateConfig(HA_READY_ST)->setPausing("always"); + state_machine->getStateConfig(HA_WAITING_ST)->setPausing("always"); startService(valid_config); @@ -3619,7 +3620,7 @@ TEST_F(HAServiceStateMachineTest, syncingTransitionsLoadBalancingPause) { HAConfigPtr valid_config = createValidConfiguration(); // Pause state machine in syncing state. - auto state_config = valid_config->getStateConfig(HA_SYNCING_ST); + auto state_config = valid_config->getStateMachineConfig()->getStateConfig(HA_SYNCING_ST); state_config->setPausing("always"); startService(valid_config); diff --git a/src/hooks/dhcp/high_availability/tests/ha_state_machine_control_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_state_machine_control_unittest.cc index 5d4b56f14b..64d85386f6 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_state_machine_control_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_state_machine_control_unittest.cc @@ -29,8 +29,8 @@ TEST_F(HAStateMachineControlTest, pause) { // Always pause in the waiting state and pause on first transition to // the ready state. Do not pause for other states. - config->getStateConfig(HA_WAITING_ST)->setPausing("always"); - config->getStateConfig(HA_READY_ST)->setPausing("once"); + config->getStateMachineConfig()->getStateConfig(HA_WAITING_ST)->setPausing("always"); + config->getStateMachineConfig()->getStateConfig(HA_READY_ST)->setPausing("once"); // Initially we shouldn't be paused. HAStateMachineControl control(config); |