From: Marcin Siodelski Date: Tue, 10 Jul 2018 14:39:45 +0000 (+0200) Subject: [5674] Implement state machine control for HA. X-Git-Tag: ha_phase2~54^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=247f42ab8ce6b46d8ea8c3f4af3b084dd0ee9705;p=thirdparty%2Fkea.git [5674] Implement state machine control for HA. --- diff --git a/src/hooks/dhcp/high_availability/Makefile.am b/src/hooks/dhcp/high_availability/Makefile.am index 4f7cec02b5..1065e65fd6 100644 --- a/src/hooks/dhcp/high_availability/Makefile.am +++ b/src/hooks/dhcp/high_availability/Makefile.am @@ -35,6 +35,7 @@ libha_la_SOURCES += ha_log.cc ha_log.h libha_la_SOURCES += ha_server_type.h libha_la_SOURCES += ha_service.cc ha_service.h libha_la_SOURCES += ha_service_states.cc ha_service_states.h +libha_la_SOURCES += ha_state_machine_control.cc ha_state_machine_control.h libha_la_SOURCES += query_filter.cc query_filter.h libha_la_SOURCES += version.cc diff --git a/src/hooks/dhcp/high_availability/ha_state_machine_control.cc b/src/hooks/dhcp/high_availability/ha_state_machine_control.cc new file mode 100644 index 0000000000..31692a9853 --- /dev/null +++ b/src/hooks/dhcp/high_availability/ha_state_machine_control.cc @@ -0,0 +1,42 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +namespace isc { +namespace ha { + +HAStateMachineControl::HAStateMachineControl(const HAConfigPtr& config) + : config_(config), paused_(false), visited_states_() { +} + +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); + + // Pause if we should always pause in this state or we should pause once + // and this is the first time we visit this state. + bool first_visit = (visited_states_.count(state) == 0); + + // If this is the first time we're in this state, record it. + if (first_visit) { + visited_states_.insert(state); + } + + // Only pause the state machine if it is not paused already. + if (!amPaused()) { + if ((state_config->getPausing() == HAConfig::StateConfig::PAUSE_ALWAYS) || + ((state_config->getPausing() == HAConfig::StateConfig::PAUSE_ONCE) && + first_visit)) { + paused_ = true; + } + } +} + +} // end of namespace isc::ha +} // end of namespace isc diff --git a/src/hooks/dhcp/high_availability/ha_state_machine_control.h b/src/hooks/dhcp/high_availability/ha_state_machine_control.h new file mode 100644 index 0000000000..463f6b35bc --- /dev/null +++ b/src/hooks/dhcp/high_availability/ha_state_machine_control.h @@ -0,0 +1,79 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef HA_STATE_MACHINE_CONTROL_H +#define HA_STATE_MACHINE_CONTROL_H + +#include +#include + +namespace isc { +namespace ha { + +/// @brief Hold runtime information about HA state machine. +/// +/// Currently, the only available runtime information is whether the +/// state machine is paused and if it should paused in certain states +/// upon next transition to these states. Note that the server may be +/// configured to only pause the state machine upon the first transition +/// to the certain states. +/// +/// The @c HAService calls @c notify upon transition to the next state. +/// This class determines whether the state machine should be paused +/// in this state based on the provided configuration and the history +/// of already visited states. The @c amPaused method should be later +/// called in the state handlers to check if the state machine is +/// paused or running. In the former case, it should not transition to +/// any other state until the state machine is unpaused. +class HAStateMachineControl { +public: + + /// @brief Constructor. + /// + /// @param config HA hooks library configuration. + explicit HAStateMachineControl(const HAConfigPtr& config); + + /// @brief Receives notification from the HA state handlers about + /// the current state. + /// + /// This method pauses sets a flag to pause the HA state machine if + /// it should be paused in the given state. It also records that + /// state as "visited", so as it is possible to determine whether + /// the state machine should be paused the next time it transitions + /// to this state. + /// + /// @param state current state of the state machine. + /// @throw BadValue if the state is not recognized. + void notify(int state); + + /// @brief Informs whether the state machine is paused. + /// + /// @return true if the state machine is paused, false otherwise. + bool amPaused() const { + return (paused_); + } + + /// @brief Clears flag indicating that the state machine is paused. + void unpause() { + paused_ = false; + } + +private: + + /// @brief HA configuration + HAConfigPtr config_; + + /// @brief Boolean flag indicating if the state machine is paused. + bool paused_; + + /// @brief Keeps track of visited states. + std::set visited_states_; +}; + +} // end of namespace isc::ha +} // end of namespace isc + +#endif diff --git a/src/hooks/dhcp/high_availability/tests/Makefile.am b/src/hooks/dhcp/high_availability/tests/Makefile.am index 40296c828e..fad8b1b615 100644 --- a/src/hooks/dhcp/high_availability/tests/Makefile.am +++ b/src/hooks/dhcp/high_availability/tests/Makefile.am @@ -29,6 +29,7 @@ ha_unittests_SOURCES += communication_state_unittest.cc ha_unittests_SOURCES += ha_config_unittest.cc ha_unittests_SOURCES += ha_impl_unittest.cc ha_unittests_SOURCES += ha_service_unittest.cc +ha_unittests_SOURCES += ha_state_machine_control_unittest.cc ha_unittests_SOURCES += ha_test.cc ha_test.h ha_unittests_SOURCES += query_filter_unittest.cc ha_unittests_SOURCES += run_unittests.cc 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 new file mode 100644 index 0000000000..5d4b56f14b --- /dev/null +++ b/src/hooks/dhcp/high_availability/tests/ha_state_machine_control_unittest.cc @@ -0,0 +1,70 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include + +using namespace isc::ha; +using namespace isc::ha::test; + +namespace { + +class HAStateMachineControlTest : public HATest { +public: + + /// @brief Constructor. + HAStateMachineControlTest() + : HATest() { + } + +}; + +// This test verifies that pausing HA state machine works as expected. +TEST_F(HAStateMachineControlTest, pause) { + HAConfigPtr config = createValidConfiguration(); + + // 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"); + + // Initially we shouldn't be paused. + HAStateMachineControl control(config); + EXPECT_FALSE(control.amPaused()); + + // Should not pause in load-balancing state. + EXPECT_NO_THROW(control.notify(HA_LOAD_BALANCING_ST)); + EXPECT_FALSE(control.amPaused()); + + // Should always pause in waiting state. + EXPECT_NO_THROW(control.notify(HA_WAITING_ST)); + EXPECT_TRUE(control.amPaused()); + + control.unpause(); + EXPECT_FALSE(control.amPaused()); + + // Should pause once in the ready state. + EXPECT_NO_THROW(control.notify(HA_READY_ST)); + EXPECT_TRUE(control.amPaused()); + + control.unpause(); + EXPECT_FALSE(control.amPaused()); + + // Do not pause the second time in the ready state. + EXPECT_NO_THROW(control.notify(HA_READY_ST)); + EXPECT_FALSE(control.amPaused()); + + // Never pause in the load-balancing state. + EXPECT_NO_THROW(control.notify(HA_LOAD_BALANCING_ST)); + EXPECT_FALSE(control.amPaused()); + + // Always pause in the waiting state. + EXPECT_NO_THROW(control.notify(HA_WAITING_ST)); + EXPECT_TRUE(control.amPaused()); +} + +}