]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#999] Enable passive-backup configuration
authorMarcin Siodelski <marcin@isc.org>
Wed, 29 Apr 2020 14:22:59 +0000 (16:22 +0200)
committerMarcin Siodelski <marcin@isc.org>
Fri, 8 May 2020 17:29:09 +0000 (19:29 +0200)
src/hooks/dhcp/high_availability/ha_config.cc
src/hooks/dhcp/high_availability/ha_config.h
src/hooks/dhcp/high_availability/ha_service.cc
src/hooks/dhcp/high_availability/ha_service.h
src/hooks/dhcp/high_availability/ha_service_states.cc
src/hooks/dhcp/high_availability/ha_service_states.h
src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc

index c3405649246a0b2399df96a1c25a693cf1dafbef..6721040abecffd59f51a03f31de61cb8a223525a 100644 (file)
@@ -187,6 +187,9 @@ HAConfig::stringToHAMode(const std::string& ha_mode) {
 
     } else if (ha_mode == "hot-standby") {
         return (HOT_STANDBY);
+
+    } else if (ha_mode == "passive-backup") {
+        return (PASSIVE_BACKUP);
     }
 
     isc_throw(BadValue, "unsupported value '" << ha_mode << "' for mode parameter");
@@ -199,6 +202,8 @@ HAConfig::HAModeToString(const HAMode& ha_mode) {
         return ("load-balancing");
     case HOT_STANDBY:
         return ("hot-standby");
+    case PASSIVE_BACKUP:
+        return ("passive-backup");
     default:
         ;
     }
@@ -297,11 +302,10 @@ HAConfig::validate() const {
         // In the load-balancing mode the wait-backup-ack must be false.
         if (wait_backup_ack_) {
             isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
-                      " 'load-balancing' mode");
+                      " load balancing configuration");
         }
-    }
 
-    if (ha_mode_ == HOT_STANDBY) {
+    } else if (ha_mode_ == HOT_STANDBY) {
         // Secondary servers not allowed in the hot standby configuration.
         if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
             isc_throw(HAConfigValidationError, "secondary servers not allowed in the hot"
@@ -323,9 +327,26 @@ HAConfig::validate() const {
         // In the hot-standby mode the wait-backup-ack must be false.
         if (wait_backup_ack_) {
             isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
-                      " 'hot-standby' mode");
+                      " hot standby configuration");
+        }
+
+    } else if (ha_mode_ == PASSIVE_BACKUP) {
+        if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
+            isc_throw(HAConfigValidationError, "secondary servers not allowed in the"
+                      " passive backup configuration");
+        }
+
+        if (peers_cnt.count(PeerConfig::STANDBY) > 0) {
+            isc_throw(HAConfigValidationError, "standby servers not allowed in the"
+                      " passive backup configuration");
+        }
+
+        if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
+            isc_throw(HAConfigValidationError, "primary server required in the"
+                      " passive backup configuration");
         }
     }
+
 }
 
 } // end of namespace isc::ha
index d5d7ce6e1aca4ddaf3a7892dc6fa28001939f1d2..83b9891d2cbf05d15f6459a2b4bbeb2618e1b972 100644 (file)
@@ -32,11 +32,13 @@ public:
     /// @brief Mode of operation.
     ///
     /// Currently supported modes are:
-    /// - load balancing
-    /// - hot standby
+    /// - load-balancing
+    /// - hot-standby
+    /// - passive-backup
     enum HAMode {
         LOAD_BALANCING,
         HOT_STANDBY,
+        PASSIVE_BACKUP
     };
 
     /// @brief HA peer configuration.
@@ -50,9 +52,9 @@ public:
         /// @brief Server's role in the High Availability setup.
         ///
         /// The following roles are supported:
-        /// - primary - server taking part in load balancing or hot standby setup,
-        ///   taking leadership over other servers. There must be exactly one primary
-        ///   server.
+        /// - primary - server taking part in load balancing, hot standby or
+        ///   passive-backup setup, taking leadership over other servers.
+        ///   There must be exactly one primary server.
         /// - secondary - server taking part in the load balancing setup. It is a slave
         ///   server to primary. There must be exactly one secondary server in the
         ///   load balancing setup.
index b3a4931ce2f7662650255f1014edd4a520a1aafa..69f9d5114b9fcdebcafcb3e442a7e80331fc6a4d 100644 (file)
@@ -121,6 +121,10 @@ HAService::defineStates() {
                 boost::bind(&HAService::partnerInMaintenanceStateHandler, this),
                 config_->getStateMachineConfig()->getStateConfig(HA_PARTNER_IN_MAINTENANCE_ST)->getPausing());
 
+    defineState(HA_PASSIVE_BACKUP_ST, stateToString(HA_PASSIVE_BACKUP_ST),
+                boost::bind(&HAService::passiveBackupStateHandler, this),
+                config_->getStateMachineConfig()->getStateConfig(HA_PASSIVE_BACKUP_ST)->getPausing());
+
     defineState(HA_READY_ST, stateToString(HA_READY_ST),
                 boost::bind(&HAService::readyStateHandler, this),
                 config_->getStateMachineConfig()->getStateConfig(HA_READY_ST)->getPausing());
@@ -343,6 +347,10 @@ HAService::partnerInMaintenanceStateHandler() {
     }
 }
 
+void
+HAService::passiveBackupStateHandler() {
+}
+
 void
 HAService::readyStateHandler() {
     // If we are transitioning from another state, we have to define new
index 3fb2685d5cbc8a8bfe30c063528c60592fd804db..d6430a532f4feb878f9c86adc4e41684e300ae92 100644 (file)
@@ -211,6 +211,15 @@ public:
     /// offline partner.
     void partnerInMaintenanceStateHandler();
 
+    /// @brief Handler for "passive-backup" state.
+    ///
+    /// This handler is invoked for the server entering the "passive-backup"
+    /// state. The primary server enters this state in the "passive-backup"
+    /// mode of operation in which there is one server responding to the
+    /// DHCP queries and zero, one or more backup servers which receive
+    /// lease updates from this server.
+    void passiveBackupStateHandler();
+
     /// @brief Handler for "ready" state.
     ///
     /// This a handler invoked for the server which finished synchronizing
index b22f9a81ee13c4ef25f06064bdf0aef383a1c2d9..be4f5884fb3972d6a09818cef3721a3fdce5a711 100644 (file)
@@ -23,6 +23,8 @@ std::string stateToString(int state) {
         return ("partner-down");
     case HA_PARTNER_IN_MAINTENANCE_ST:
         return ("partner-in-maintenance");
+    case HA_PASSIVE_BACKUP_ST:
+        return ("passive-backup");
     case HA_READY_ST:
         return ("ready");
     case HA_SYNCING_ST:
@@ -59,6 +61,9 @@ int stringToState(const std::string& state_name) {
     } else if (state_name == "partner-in-maintenance") {
         return (HA_PARTNER_IN_MAINTENANCE_ST);
 
+    } else if (state_name == "passive-backup") {
+        return (HA_PASSIVE_BACKUP_ST);
+
     } else if (state_name == "ready") {
         return (HA_READY_ST);
 
index c87797b3b507dbc42a7f36d39fca5efd271438bc..2c6323daa1104d0b1fb4e2a1424fbc98a662a39e 100644 (file)
@@ -31,17 +31,20 @@ const int HA_PARTNER_DOWN_ST = util::StateModel::SM_DERIVED_STATE_MIN + 5;
 /// Partner in-maintenance state.
 const int HA_PARTNER_IN_MAINTENANCE_ST = util::StateModel::SM_DERIVED_STATE_MIN + 6;
 
+/// In passive-backup state with a single active server and backup servers.
+const int HA_PASSIVE_BACKUP_ST = util::StateModel::SM_DERIVED_STATE_MIN + 7;
+
 /// Server ready state, i.e. synchronized database, can enable DHCP service.
-const int HA_READY_ST = util::StateModel::SM_DERIVED_STATE_MIN + 7;
+const int HA_READY_ST = util::StateModel::SM_DERIVED_STATE_MIN + 8;
 
 /// Synchronizing database state.
-const int HA_SYNCING_ST = util::StateModel::SM_DERIVED_STATE_MIN + 8;
+const int HA_SYNCING_ST = util::StateModel::SM_DERIVED_STATE_MIN + 9;
 
 /// HA service terminated state.
-const int HA_TERMINATED_ST = util::StateModel::SM_DERIVED_STATE_MIN + 9;
+const int HA_TERMINATED_ST = util::StateModel::SM_DERIVED_STATE_MIN + 10;
 
 /// Server waiting state, i.e. waiting for another server to be ready.
-const int HA_WAITING_ST = util::StateModel::SM_DERIVED_STATE_MIN + 10;
+const int HA_WAITING_ST = util::StateModel::SM_DERIVED_STATE_MIN + 11;
 
 /// Special state indicating that this server is unable to communicate with
 /// the partner.
index 0fcb37c48a4a05447d3d1b7b59b870baa57b29f3..2fe508f3edc3656db2a7ee68e5c09a4009163aef 100644 (file)
@@ -189,7 +189,7 @@ TEST_F(HAConfigTest, configureLoadBalancing) {
     EXPECT_EQ(STATE_PAUSE_ONCE, state_cfg->getPausing());
 }
 
-// Verifies that load balancing configuration is parsed correctly.
+// Verifies that hot standby configuration is parsed correctly.
 TEST_F(HAConfigTest, configureHotStandby) {
     const std::string ha_config =
         "["
@@ -290,6 +290,60 @@ TEST_F(HAConfigTest, configureHotStandby) {
     EXPECT_EQ(STATE_PAUSE_NEVER, state_cfg->getPausing());
 }
 
+// Verifies that passive-backup configuration is parsed correctly.
+TEST_F(HAConfigTest, configurePassiveBackup) {
+    const std::string ha_config =
+        "["
+        "    {"
+        "        \"this-server-name\": \"server1\","
+        "        \"mode\": \"passive-backup\","
+        "        \"wait-backup-ack\": true,"
+        "        \"peers\": ["
+        "            {"
+        "                \"name\": \"server1\","
+        "                \"url\": \"http://127.0.0.1:8080/\","
+        "                \"role\": \"primary\""
+        "            },"
+        "            {"
+        "                \"name\": \"server2\","
+        "                \"url\": \"http://127.0.0.1:8081/\","
+        "                \"role\": \"backup\""
+        "            },"
+        "            {"
+        "                \"name\": \"server3\","
+        "                \"url\": \"http://127.0.0.1:8082/\","
+        "                \"role\": \"backup\""
+        "            }"
+        "        ]"
+        "    }"
+        "]";
+
+    HAImplPtr impl(new HAImpl());
+    ASSERT_NO_THROW(impl->configure(Element::fromJSON(ha_config)));
+    EXPECT_EQ("server1", impl->getConfig()->getThisServerName());
+    EXPECT_EQ(HAConfig::PASSIVE_BACKUP, impl->getConfig()->getHAMode());
+    EXPECT_TRUE(impl->getConfig()->amSendingLeaseUpdates());
+    EXPECT_TRUE(impl->getConfig()->amWaitingBackupAck());
+
+    HAConfig::PeerConfigPtr cfg = impl->getConfig()->getThisServerConfig();
+    ASSERT_TRUE(cfg);
+    EXPECT_EQ("server1", cfg->getName());
+    EXPECT_EQ("http://127.0.0.1:8080/", cfg->getUrl().toText());
+    EXPECT_EQ(HAConfig::PeerConfig::PRIMARY, cfg->getRole());
+
+    cfg = impl->getConfig()->getPeerConfig("server2");
+    ASSERT_TRUE(cfg);
+    EXPECT_EQ("server2", cfg->getName());
+    EXPECT_EQ("http://127.0.0.1:8081/", cfg->getUrl().toText());
+    EXPECT_EQ(HAConfig::PeerConfig::BACKUP, cfg->getRole());
+
+    cfg = impl->getConfig()->getPeerConfig("server3");
+    ASSERT_TRUE(cfg);
+    EXPECT_EQ("server3", cfg->getName());
+    EXPECT_EQ("http://127.0.0.1:8082/", cfg->getUrl().toText());
+    EXPECT_EQ(HAConfig::PeerConfig::BACKUP, cfg->getRole());
+}
+
 // This server name must not be empty.
 TEST_F(HAConfigTest, emptyServerName) {
     testInvalidConfig(
@@ -978,9 +1032,82 @@ TEST_F(HAConfigTest, waitBackupAckWithActiveServers) {
         "        ]"
         "    }"
         "]",
-        "'wait-backup-ack' must be set to false in the 'hot-standby' mode");
+        "'wait-backup-ack' must be set to false in the hot standby configuration");
 }
 
+// Test that secondary server is not allowed in the passive-backup mode.
+TEST_F(HAConfigTest, passiveBackupSecondaryServer) {
+    testInvalidConfig(
+        "["
+        "    {"
+        "        \"this-server-name\": \"server1\","
+        "        \"mode\": \"passive-backup\","
+        "        \"peers\": ["
+        "            {"
+        "                \"name\": \"server1\","
+        "                \"url\": \"http://127.0.0.1:8080/\","
+        "                \"role\": \"primary\""
+        "            },"
+        "            {"
+        "                \"name\": \"server2\","
+        "                \"url\": \"http://127.0.0.1:8081/\","
+        "                \"role\": \"secondary\""
+        "            }"
+        "        ]"
+        "    }"
+        "]",
+        "secondary servers not allowed in the passive backup configuration");
+}
+
+// Test that standby server is not allowed in the passive-backup mode.
+TEST_F(HAConfigTest, passiveBackupStandbyServer) {
+    testInvalidConfig(
+        "["
+        "    {"
+        "        \"this-server-name\": \"server1\","
+        "        \"mode\": \"passive-backup\","
+        "        \"peers\": ["
+        "            {"
+        "                \"name\": \"server1\","
+        "                \"url\": \"http://127.0.0.1:8080/\","
+        "                \"role\": \"primary\""
+        "            },"
+        "            {"
+        "                \"name\": \"server2\","
+        "                \"url\": \"http://127.0.0.1:8081/\","
+        "                \"role\": \"standby\""
+        "            }"
+        "        ]"
+        "    }"
+        "]",
+        "standby servers not allowed in the passive backup configuration");
+}
+
+// Test that primary server is required in the passive-backup mode.
+TEST_F(HAConfigTest, passiveBackupNoPrimary) {
+    testInvalidConfig(
+        "["
+        "    {"
+        "        \"this-server-name\": \"server1\","
+        "        \"mode\": \"passive-backup\","
+        "        \"peers\": ["
+        "            {"
+        "                \"name\": \"server1\","
+        "                \"url\": \"http://127.0.0.1:8080/\","
+        "                \"role\": \"backup\""
+        "            },"
+        "            {"
+        "                \"name\": \"server2\","
+        "                \"url\": \"http://127.0.0.1:8081/\","
+        "                \"role\": \"backup\""
+        "            }"
+        "        ]"
+        "    }"
+        "]",
+        "primary server required in the passive backup configuration");
+}
+
+
 // Test that conversion of the role names works correctly.
 TEST_F(HAConfigTest, stringToRole) {
     EXPECT_EQ(HAConfig::PeerConfig::PRIMARY,
@@ -1011,12 +1138,14 @@ TEST_F(HAConfigTest, roleToString) {
 TEST_F(HAConfigTest, stringToHAMode) {
     EXPECT_EQ(HAConfig::LOAD_BALANCING, HAConfig::stringToHAMode("load-balancing"));
     EXPECT_EQ(HAConfig::HOT_STANDBY, HAConfig::stringToHAMode("hot-standby"));
+    EXPECT_EQ(HAConfig::PASSIVE_BACKUP, HAConfig::stringToHAMode("passive-backup"));
 }
 
 // Test that HA mode name is generated correctly.
 TEST_F(HAConfigTest, HAModeToString) {
     EXPECT_EQ("load-balancing", HAConfig::HAModeToString(HAConfig::LOAD_BALANCING));
     EXPECT_EQ("hot-standby", HAConfig::HAModeToString(HAConfig::HOT_STANDBY));
+    EXPECT_EQ("passive-backup", HAConfig::HAModeToString(HAConfig::PASSIVE_BACKUP));
 }
 
 // Test that conversion of the 'pause' value works correctly.