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
--- /dev/null
+// 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 <ha_state_machine_control.h>
+
+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
--- /dev/null
+// 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 <ha_config.h>
+#include <set>
+
+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<int> visited_states_;
+};
+
+} // end of namespace isc::ha
+} // end of namespace isc
+
+#endif
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
--- /dev/null
+// 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 <ha_state_machine_control.h>
+#include <ha_test.h>
+#include <gtest/gtest.h>
+
+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());
+}
+
+}