]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5674] Implemented HA pause for ordinary states.
authorMarcin Siodelski <marcin@isc.org>
Tue, 10 Jul 2018 15:44:49 +0000 (17:44 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 10 Jul 2018 15:44:49 +0000 (17:44 +0200)
Currently it excludes terminated and syncing states.

src/hooks/dhcp/high_availability/ha_service.cc
src/hooks/dhcp/high_availability/ha_service.h
src/hooks/dhcp/high_availability/ha_state_machine_control.h
src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc

index e55ec2cffa8b0e627fde57801ac4e490766a2db2..cb18d91230102195ca5e88da217a4cb0678e4587 100644 (file)
@@ -45,7 +45,7 @@ HAService::HAService(const IOServicePtr& io_service, const NetworkStatePtr& netw
                      const HAConfigPtr& config, const HAServerType& server_type)
     : io_service_(io_service), network_state_(network_state), config_(config),
       server_type_(server_type), client_(*io_service), communication_state_(),
-      query_filter_(config), pending_requests_() {
+      query_filter_(config), state_machine_control_(config), pending_requests_() {
 
     if (server_type == HAServerType::DHCPv4) {
         communication_state_.reset(new CommunicationState4(io_service_, config));
@@ -130,10 +130,16 @@ HAService::normalStateHandler() {
     if (doOnEntry()) {
         query_filter_.serveDefaultScopes();
         adjustNetworkState();
+        state_machine_control_.notify(getCurrState());
     }
 
     scheduleHeartbeat();
 
+    if (state_machine_control_.amPaused()) {
+        postNextEvent(NOP_EVT);
+        return;
+    }
+
     // Check if the clock skew is still acceptable. If not, transition to
     // the terminated state.
     if (shouldTerminate()) {
@@ -180,10 +186,17 @@ HAService::partnerDownStateHandler() {
             query_filter_.serveDefaultScopes();
         }
         adjustNetworkState();
+
+        state_machine_control_.notify(getCurrState());
     }
 
     scheduleHeartbeat();
 
+    if (state_machine_control_.amPaused()) {
+        postNextEvent(NOP_EVT);
+        return;
+    }
+
     // Check if the clock skew is still acceptable. If not, transition to
     // the terminated state.
     if (shouldTerminate()) {
@@ -220,10 +233,16 @@ HAService::readyStateHandler() {
     if (doOnEntry()) {
         query_filter_.serveNoScopes();
         adjustNetworkState();
+        state_machine_control_.notify(getCurrState());
     }
 
     scheduleHeartbeat();
 
+    if (state_machine_control_.amPaused()) {
+        postNextEvent(NOP_EVT);
+        return;
+    }
+
     // Check if the clock skew is still acceptable. If not, transition to
     // the terminated state.
     if (shouldTerminate()) {
@@ -361,6 +380,17 @@ HAService::waitingStateHandler() {
     if (doOnEntry()) {
         query_filter_.serveNoScopes();
         adjustNetworkState();
+        state_machine_control_.notify(getCurrState());
+    }
+
+    // Only schedule the heartbeat for non-backup servers.
+    if (config_->getThisServerConfig()->getRole() != HAConfig::PeerConfig::BACKUP) {
+        scheduleHeartbeat();
+    }
+
+    if (state_machine_control_.amPaused()) {
+        postNextEvent(NOP_EVT);
+        return;
     }
 
     // Backup server must remain in its own state.
@@ -369,8 +399,6 @@ HAService::waitingStateHandler() {
         return;
     }
 
-    scheduleHeartbeat();
-
     // Check if the clock skew is still acceptable. If not, transition to
     // the terminated state.
     if (shouldTerminate()) {
@@ -477,6 +505,11 @@ HAService::verboseTransition(const unsigned state) {
     }
 }
 
+void
+HAService::unpause() {
+    state_machine_control_.unpause();
+}
+
 void
 HAService::serveDefaultScopes() {
     query_filter_.serveDefaultScopes();
index bff04ca0063cce8c4bdb7266168d188099452f1a..e1485ec38bf4ad0a8d7be4af66c7e58b32a8ae5f 100644 (file)
@@ -10,6 +10,7 @@
 #include <communication_state.h>
 #include <ha_config.h>
 #include <ha_server_type.h>
+#include <ha_state_machine_control.h>
 #include <query_filter.h>
 #include <asiolink/io_service.h>
 #include <cc/data.h>
@@ -246,6 +247,13 @@ protected:
 
 public:
 
+    /// @brief Un-pauses the state machine.
+    ///
+    /// This is method is invoked when the server receives the 'ha-continue'
+    /// command which instructs the server to unblock the HA state machine
+    /// so as it may transition to a next state.
+    void unpause();
+
     /// @brief Instructs the HA service to serve default scopes.
     ///
     /// This method is mostly useful for unit testing. The scopes need to be
@@ -635,6 +643,9 @@ protected:
     /// @brief Selects queries to be processed/dropped.
     QueryFilter query_filter_;
 
+    /// @brief Controls state machine pausing and unpausing.
+    HAStateMachineControl state_machine_control_;
+
     /// @brief Map holding a number of scheduled requests for a given packet.
     ///
     /// A single callout may send multiple requests at the same time, e.g.
index 463f6b35bce923458ec93402937538cd83a0246b..887c70124be4cf2d20e5890628096cccd37a1d72 100644 (file)
@@ -8,6 +8,7 @@
 #define HA_STATE_MACHINE_CONTROL_H
 
 #include <ha_config.h>
+#include <boost/shared_ptr.hpp>
 #include <set>
 
 namespace isc {
@@ -73,6 +74,9 @@ private:
     std::set<int> visited_states_;
 };
 
+/// @brief Shared pointer to the @c HAStateMachineControl.
+typedef boost::shared_ptr<HAStateMachineControl> HAStateMachineControlPtr;
+
 } // end of namespace isc::ha
 } // end of namespace isc
 
index 572f71754fbe0a8ac3344f8f7caae635f0b35c52..8151ec6bbff0152d92255940ac997761099d638a 100644 (file)
@@ -3547,6 +3547,71 @@ TEST_F(HAServiceStateMachineTest, syncingTransitionsLoadBalancing) {
     EXPECT_EQ(HAService::HA_SYNCING_SUCCEEDED_EVT, service_->getLastEvent());
 }
 
+// This test verifies that the HA state machine can be paused in certain states
+// when the server is operating in load balancing mode.
+TEST_F(HAServiceStateMachineTest, stateTransitionsLoadBalancingPause) {
+    partner_->startup();
+
+    HAConfigPtr valid_config = createValidConfiguration();
+    auto state_configs = valid_config->getStateMachineConfig();
+
+    for (auto cfg = state_configs.begin(); cfg != state_configs.end(); ++cfg) {
+        cfg->second->setPausing("always");
+    }
+
+    startService(valid_config);
+
+    {
+        SCOPED_TRACE("LOAD BALANCING state transitions");
+
+        testTransition(MyState(HA_LOAD_BALANCING_ST), PartnerState(HA_TERMINATED_ST),
+                       FinalState(HA_LOAD_BALANCING_ST));
+
+        service_->unpause();
+
+        testTransition(MyState(HA_LOAD_BALANCING_ST), PartnerState(HA_TERMINATED_ST),
+                       FinalState(HA_TERMINATED_ST));
+    }
+
+    {
+        SCOPED_TRACE("PARTNER DOWN state transitions");
+
+        testTransition(MyState(HA_PARTNER_DOWN_ST), PartnerState(HA_LOAD_BALANCING_ST),
+                       FinalState(HA_PARTNER_DOWN_ST));
+
+        service_->unpause();
+
+        testTransition(MyState(HA_PARTNER_DOWN_ST), PartnerState(HA_LOAD_BALANCING_ST),
+                       FinalState(HA_WAITING_ST));
+    }
+
+
+    {
+        SCOPED_TRACE("READY state transitions");
+
+        testTransition(MyState(HA_READY_ST), PartnerState(HA_LOAD_BALANCING_ST),
+                       FinalState(HA_READY_ST));
+
+        service_->unpause();
+
+        testTransition(MyState(HA_READY_ST), PartnerState(HA_LOAD_BALANCING_ST),
+                       FinalState(HA_LOAD_BALANCING_ST));
+    }
+
+
+    {
+        SCOPED_TRACE("WAITING state transitions");
+
+        testTransition(MyState(HA_WAITING_ST), PartnerState(HA_LOAD_BALANCING_ST),
+                       FinalState(HA_WAITING_ST));
+
+        service_->unpause();
+
+        testTransition(MyState(HA_WAITING_ST), PartnerState(HA_LOAD_BALANCING_ST),
+                       FinalState(HA_SYNCING_ST));
+    }
+}
+
 // This test verifies that the server takes ownership of the given scopes
 // and whether the DHCP service is disabled or enabled in certain states.
 TEST_F(HAServiceStateMachineTest, scopesServingLoadBalancing) {