]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5674] Implement state machine control for HA.
authorMarcin Siodelski <marcin@isc.org>
Tue, 10 Jul 2018 14:39:45 +0000 (16:39 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 10 Jul 2018 14:39:45 +0000 (16:39 +0200)
src/hooks/dhcp/high_availability/Makefile.am
src/hooks/dhcp/high_availability/ha_state_machine_control.cc [new file with mode: 0644]
src/hooks/dhcp/high_availability/ha_state_machine_control.h [new file with mode: 0644]
src/hooks/dhcp/high_availability/tests/Makefile.am
src/hooks/dhcp/high_availability/tests/ha_state_machine_control_unittest.cc [new file with mode: 0644]

index 4f7cec02b561e03e59f7c4abdd0075a2f0546a5b..1065e65fd611fe756246e0f9b3a72150627be9ae 100644 (file)
@@ -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 (file)
index 0000000..31692a9
--- /dev/null
@@ -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 <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
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 (file)
index 0000000..463f6b3
--- /dev/null
@@ -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 <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
index 40296c828e424259deaf677f7ca4edf337979aea..fad8b1b61583a8a6476f9681e85ffeb3613ff880 100644 (file)
@@ -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 (file)
index 0000000..5d4b56f
--- /dev/null
@@ -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 <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());
+}
+
+}