From: Marcin Siodelski Date: Wed, 29 Apr 2020 14:22:59 +0000 (+0200) Subject: [#999] Enable passive-backup configuration X-Git-Tag: Kea-1.7.8~120 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=05229c8671cd48477701c7b6cb19a8882c2ca697;p=thirdparty%2Fkea.git [#999] Enable passive-backup configuration --- diff --git a/src/hooks/dhcp/high_availability/ha_config.cc b/src/hooks/dhcp/high_availability/ha_config.cc index c340564924..6721040abe 100644 --- a/src/hooks/dhcp/high_availability/ha_config.cc +++ b/src/hooks/dhcp/high_availability/ha_config.cc @@ -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 diff --git a/src/hooks/dhcp/high_availability/ha_config.h b/src/hooks/dhcp/high_availability/ha_config.h index d5d7ce6e1a..83b9891d2c 100644 --- a/src/hooks/dhcp/high_availability/ha_config.h +++ b/src/hooks/dhcp/high_availability/ha_config.h @@ -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. diff --git a/src/hooks/dhcp/high_availability/ha_service.cc b/src/hooks/dhcp/high_availability/ha_service.cc index b3a4931ce2..69f9d5114b 100644 --- a/src/hooks/dhcp/high_availability/ha_service.cc +++ b/src/hooks/dhcp/high_availability/ha_service.cc @@ -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 diff --git a/src/hooks/dhcp/high_availability/ha_service.h b/src/hooks/dhcp/high_availability/ha_service.h index 3fb2685d5c..d6430a532f 100644 --- a/src/hooks/dhcp/high_availability/ha_service.h +++ b/src/hooks/dhcp/high_availability/ha_service.h @@ -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 diff --git a/src/hooks/dhcp/high_availability/ha_service_states.cc b/src/hooks/dhcp/high_availability/ha_service_states.cc index b22f9a81ee..be4f5884fb 100644 --- a/src/hooks/dhcp/high_availability/ha_service_states.cc +++ b/src/hooks/dhcp/high_availability/ha_service_states.cc @@ -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); diff --git a/src/hooks/dhcp/high_availability/ha_service_states.h b/src/hooks/dhcp/high_availability/ha_service_states.h index c87797b3b5..2c6323daa1 100644 --- a/src/hooks/dhcp/high_availability/ha_service_states.h +++ b/src/hooks/dhcp/high_availability/ha_service_states.h @@ -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. diff --git a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc index 0fcb37c48a..2fe508f3ed 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/ha_config_unittest.cc @@ -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.