Commit a5fab941 authored by Marcin Siodelski's avatar Marcin Siodelski

[5674] Added extra level of hierarchy in state machine configuration.

Per review comments.
parent c0d33f7c
......@@ -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.
......
......@@ -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.
......
......@@ -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"));
}
}
......
......@@ -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.
......
......@@ -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");
......
......@@ -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);
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment