]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3106] Network state change per HA service
authorMarcin Siodelski <marcin@isc.org>
Fri, 13 Oct 2023 16:18:58 +0000 (18:18 +0200)
committerMarcin Siodelski <marcin@isc.org>
Wed, 29 Nov 2023 19:58:55 +0000 (20:58 +0100)
19 files changed:
doc/sphinx/arm/ctrl-channel.rst
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/hooks/dhcp/high_availability/command_creator.cc
src/hooks/dhcp/high_availability/command_creator.h
src/hooks/dhcp/high_availability/ha_impl.cc
src/hooks/dhcp/high_availability/ha_impl.h
src/hooks/dhcp/high_availability/ha_service.cc
src/hooks/dhcp/high_availability/ha_service.h
src/hooks/dhcp/high_availability/libloadtests/close_unittests.cc
src/hooks/dhcp/high_availability/tests/command_creator_unittest.cc
src/hooks/dhcp/high_availability/tests/ha_impl_unittest.cc
src/hooks/dhcp/high_availability/tests/ha_mt_unittest.cc
src/hooks/dhcp/high_availability/tests/ha_service_unittest.cc
src/lib/dhcpsrv/network_state.cc
src/lib/dhcpsrv/network_state.h
src/lib/dhcpsrv/tests/network_state_unittest.cc

index d1d1318431d932a23bb01ffc45eae50550af6434..66c8772d52497325b182c38037165ba163f469af 100644 (file)
@@ -715,11 +715,11 @@ Since Kea 1.9.4, there is an additional ``origin`` parameter that specifies the
 command source. A server administrator should typically omit this parameter
 because the default value "user" indicates that the administrator sent the
 command. This command can also be sent by the partner server running HA hooks
-library. In that case, the partner server sets the parameter to "ha-partner".
-This value is reserved for the communication between HA partners and should not
-be specified in the administrator's commands, as it may interfere with
-HA operation. The administrator should either omit this parameter or set it to
-"user".
+library. In that case, the partner server sets the parameter to a unique
+integer identifier of a HA service. The integer values are reserved for the
+communication between HA partners and should not be specified in the
+administrator's commands, as it may interfere with HA operation. The
+administrator should either omit this parameter or set it to "user".
 
 ::
 
@@ -743,10 +743,11 @@ Since Kea 1.9.4, there is an additional ``origin`` parameter that specifies the
 command source. A server administrator should typically omit this parameter
 because the default value "user" indicates that the administrator sent the
 command. This command can also be sent by the partner server running the HA hook
-library. In that case, the partner server sets the parameter to "ha-partner".
-This value is reserved for the communication between HA partners and should not
-be specified in the administrator's commands, as it may interfere with
-HA operation. The administrator should either omit this parameter or set it to
+library. In that case, the partner server sets the parameter to a unique
+integer identifier of a HA service. The integer values are reserved for the
+communication between HA partners and should not be specified in the
+administrator's commands, as it may interfere with HA operation. The
+administrator should either omit this parameter or set it to
 "user".
 
 ::
index e5a5e8821703cdbef900408f60bf716fe3f77598..972a3436a34fbf77f952dc8a12d6494f7f0b6707 100644 (file)
@@ -520,7 +520,7 @@ ControlledDhcpv4Srv::commandDhcpDisableHandler(const std::string&,
 
     // If the args map does not contain 'origin' parameter, the default type
     // will be used (user command).
-    NetworkState::Origin type = NetworkState::Origin::USER_COMMAND;
+    auto type = NetworkState::USER_COMMAND;
 
     // Parse arguments to see if the 'max-period' or 'origin' parameters have
     // been specified.
@@ -548,14 +548,11 @@ ControlledDhcpv4Srv::commandDhcpDisableHandler(const std::string&,
             ConstElementPtr origin_element = args->get("origin");
             // The 'origin' parameter is optional.
             if (origin_element) {
-                // It must be a string, if specified.
-                if (origin_element->getType() != Element::string) {
-                    message << "'origin' argument must be a string";
-
-                } else {
+                switch (origin_element->getType()) {
+                case Element::string:
                     origin = origin_element->stringValue();
                     if (origin == "ha-partner") {
-                        type = NetworkState::Origin::HA_COMMAND;
+                        type = NetworkState::HA_REMOTE_COMMAND;
                     } else if (origin != "user") {
                         if (origin.empty()) {
                             origin = "(empty string)";
@@ -563,6 +560,13 @@ ControlledDhcpv4Srv::commandDhcpDisableHandler(const std::string&,
                         message << "invalid value used for 'origin' parameter: "
                                 << origin;
                     }
+                    break;
+                case Element::integer:
+                    type = origin_element->intValue();
+                    break;
+                default:
+                    // It must be a string or a number, if specified.
+                    message << "'origin' argument must be a string or a number";
                 }
             }
         }
@@ -598,7 +602,7 @@ ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&,
 
     // If the args map does not contain 'origin' parameter, the default type
     // will be used (user command).
-    NetworkState::Origin type = NetworkState::Origin::USER_COMMAND;
+    auto type = NetworkState::USER_COMMAND;
 
     // Parse arguments to see if the 'origin' parameter has been specified.
     if (args) {
@@ -610,14 +614,11 @@ ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&,
             ConstElementPtr origin_element = args->get("origin");
             // The 'origin' parameter is optional.
             if (origin_element) {
-                // It must be a string, if specified.
-                if (origin_element->getType() != Element::string) {
-                    message << "'origin' argument must be a string";
-
-                } else {
+                switch (origin_element->getType()) {
+                case Element::string:
                     origin = origin_element->stringValue();
                     if (origin == "ha-partner") {
-                        type = NetworkState::Origin::HA_COMMAND;
+                        type = NetworkState::HA_REMOTE_COMMAND;
                     } else if (origin != "user") {
                         if (origin.empty()) {
                             origin = "(empty string)";
@@ -625,6 +626,13 @@ ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&,
                         message << "invalid value used for 'origin' parameter: "
                                 << origin;
                     }
+                    break;
+                case Element::integer:
+                    type = origin_element->intValue();
+                    break;
+                default:
+                    // It must be a string or a number, if specified.
+                    message << "'origin' argument must be a string or a number";
                 }
             }
         }
@@ -948,7 +956,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         cfg_db->setAppendedParameters(params);
         cfg_db->createManagers();
         // Reset counters related to connections as all managers have been recreated.
-        srv->getNetworkState()->reset(NetworkState::Origin::DB_CONNECTION);
+        srv->getNetworkState()->reset(NetworkState::DB_CONNECTION);
 
     } catch (const std::exception& ex) {
         err << "Unable to open database: " << ex.what();
@@ -1337,7 +1345,7 @@ ControlledDhcpv4Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
     // Disable service until the connection is recovered.
     if (db_reconnect_ctl->retriesLeft() == db_reconnect_ctl->maxRetries() &&
         db_reconnect_ctl->alterServiceState()) {
-        network_state_->disableService(NetworkState::Origin::DB_CONNECTION);
+        network_state_->disableService(NetworkState::DB_CONNECTION);
     }
 
     LOG_INFO(dhcp4_logger, DHCP4_DB_RECONNECT_LOST_CONNECTION);
@@ -1368,7 +1376,7 @@ ControlledDhcpv4Srv::dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
 
     // Enable service after the connection is recovered.
     if (db_reconnect_ctl->alterServiceState()) {
-        network_state_->enableService(NetworkState::Origin::DB_CONNECTION);
+        network_state_->enableService(NetworkState::DB_CONNECTION);
     }
 
     LOG_INFO(dhcp4_logger, DHCP4_DB_RECONNECT_SUCCEEDED);
index 963cfd12b24d40f297344cfbe8acc3fed1014e32..2046ec92fdf664fb243ca63b8e2bbc7d9382954b 100644 (file)
@@ -1765,7 +1765,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisable) {
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->enableService(NetworkState::Origin::USER_COMMAND);
+    server_->network_state_->enableService(NetworkState::USER_COMMAND);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
@@ -1785,7 +1785,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisable) {
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->enableService(NetworkState::Origin::USER_COMMAND);
+    server_->network_state_->enableService(NetworkState::USER_COMMAND);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
@@ -1805,7 +1805,27 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisable) {
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->enableService(NetworkState::Origin::HA_COMMAND);
+    server_->network_state_->enableService(NetworkState::HA_REMOTE_COMMAND);
+
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin\": 1102"
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    server_->network_state_->enableService(NetworkState::HA_REMOTE_COMMAND+2);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
@@ -1894,7 +1914,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnable) {
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->disableService(NetworkState::Origin::USER_COMMAND);
+    server_->network_state_->disableService(NetworkState::USER_COMMAND);
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
@@ -1914,7 +1934,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnable) {
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->disableService(NetworkState::Origin::HA_COMMAND);
+    server_->network_state_->disableService(NetworkState::HA_REMOTE_COMMAND);
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
@@ -1933,6 +1953,26 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnable) {
     EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+
+    server_->network_state_->disableService(NetworkState::HA_REMOTE_COMMAND+1);
+
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin\": 1101"
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
 
 /// Verify that concurrent connections over the control channel can be
index 433ae2e3bcc1fa977012ad06356603b2bde6c320..3e99d48fa5a513873a406863a3d595c9a2897ccb 100644 (file)
@@ -522,7 +522,7 @@ ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
 
     // If the args map does not contain 'origin' parameter, the default type
     // will be used (user command).
-    NetworkState::Origin type = NetworkState::Origin::USER_COMMAND;
+    auto type = NetworkState::USER_COMMAND;
 
     // Parse arguments to see if the 'max-period' or 'origin' parameters have
     // been specified.
@@ -550,14 +550,11 @@ ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
             ConstElementPtr origin_element = args->get("origin");
             // The 'origin' parameter is optional.
             if (origin_element) {
-                // It must be a string, if specified.
-                if (origin_element->getType() != Element::string) {
-                    message << "'origin' argument must be a string";
-
-                } else {
+                switch (origin_element->getType()) {
+                case Element::string:
                     origin = origin_element->stringValue();
                     if (origin == "ha-partner") {
-                        type = NetworkState::Origin::HA_COMMAND;
+                        type = NetworkState::HA_REMOTE_COMMAND;
                     } else if (origin != "user") {
                         if (origin.empty()) {
                             origin = "(empty string)";
@@ -565,6 +562,13 @@ ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
                         message << "invalid value used for 'origin' parameter: "
                                 << origin;
                     }
+                    break;
+                case Element::integer:
+                    type = origin_element->intValue();
+                    break;
+                default:
+                    // It must be a string or a number, if specified.
+                    message << "'origin' argument must be a string or a number";
                 }
             }
         }
@@ -600,7 +604,7 @@ ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
 
     // If the args map does not contain 'origin' parameter, the default type
     // will be used (user command).
-    NetworkState::Origin type = NetworkState::Origin::USER_COMMAND;
+    auto type = NetworkState::USER_COMMAND;
 
     // Parse arguments to see if the 'origin' parameter has been specified.
     if (args) {
@@ -612,14 +616,11 @@ ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
             ConstElementPtr origin_element = args->get("origin");
             // The 'origin' parameter is optional.
             if (origin_element) {
-                // It must be a string, if specified.
-                if (origin_element->getType() != Element::string) {
-                    message << "'origin' argument must be a string";
-
-                } else {
+                switch (origin_element->getType()) {
+                case Element::string:
                     origin = origin_element->stringValue();
                     if (origin == "ha-partner") {
-                        type = NetworkState::Origin::HA_COMMAND;
+                        type = NetworkState::HA_REMOTE_COMMAND;
                     } else if (origin != "user") {
                         if (origin.empty()) {
                             origin = "(empty string)";
@@ -627,6 +628,13 @@ ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
                         message << "invalid value used for 'origin' parameter: "
                                 << origin;
                     }
+                    break;
+                case Element::integer:
+                    type = origin_element->intValue();
+                    break;
+                default:
+                    // It must be a string or a number, if specified.
+                    message << "'origin' argument must be a string or a number";
                 }
             }
         }
@@ -953,7 +961,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         cfg_db->setAppendedParameters(params);
         cfg_db->createManagers();
         // Reset counters related to connections as all managers have been recreated.
-        srv->getNetworkState()->reset(NetworkState::Origin::DB_CONNECTION);
+        srv->getNetworkState()->reset(NetworkState::DB_CONNECTION);
     } catch (const std::exception& ex) {
         err << "Unable to open database: " << ex.what();
         return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
@@ -1356,7 +1364,7 @@ ControlledDhcpv6Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
     // Disable service until the connection is recovered.
     if (db_reconnect_ctl->retriesLeft() == db_reconnect_ctl->maxRetries() &&
         db_reconnect_ctl->alterServiceState()) {
-        network_state_->disableService(NetworkState::Origin::DB_CONNECTION);
+        network_state_->disableService(NetworkState::DB_CONNECTION);
     }
 
     LOG_INFO(dhcp6_logger, DHCP6_DB_RECONNECT_LOST_CONNECTION);
@@ -1387,7 +1395,7 @@ ControlledDhcpv6Srv::dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
 
     // Enable service after the connection is recovered.
     if (db_reconnect_ctl->alterServiceState()) {
-        network_state_->enableService(NetworkState::Origin::DB_CONNECTION);
+        network_state_->enableService(NetworkState::DB_CONNECTION);
     }
 
     LOG_INFO(dhcp6_logger, DHCP6_DB_RECONNECT_SUCCEEDED);
index 5baa867c7be9606659ed727c5fae53e574c63669..d467a249ffe8c3f4be2228bde51d34315ed233a6 100644 (file)
@@ -1796,7 +1796,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisable) {
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->enableService(NetworkState::Origin::USER_COMMAND);
+    server_->network_state_->enableService(NetworkState::USER_COMMAND);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
@@ -1816,7 +1816,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisable) {
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->enableService(NetworkState::Origin::USER_COMMAND);
+    server_->network_state_->enableService(NetworkState::USER_COMMAND);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
@@ -1836,7 +1836,27 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisable) {
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->enableService(NetworkState::Origin::HA_COMMAND);
+    server_->network_state_->enableService(NetworkState::HA_REMOTE_COMMAND);
+
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin\": 1101"
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    server_->network_state_->enableService(NetworkState::HA_REMOTE_COMMAND+1);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
@@ -1925,7 +1945,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpEnable) {
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->disableService(NetworkState::Origin::USER_COMMAND);
+    server_->network_state_->disableService(NetworkState::USER_COMMAND);
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
@@ -1945,7 +1965,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpEnable) {
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 
-    server_->network_state_->disableService(NetworkState::Origin::HA_COMMAND);
+    server_->network_state_->disableService(NetworkState::HA_REMOTE_COMMAND);
 
     EXPECT_FALSE(server_->network_state_->isServiceEnabled());
 
@@ -1964,6 +1984,26 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpEnable) {
     EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
 
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+
+    server_->network_state_->disableService(NetworkState::HA_REMOTE_COMMAND+2);
+
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin\": 1102"
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
 
 /// Verify that concurrent connections over the control channel can be
index 75c4e41a7a32af708e82d6eece4a8af9c9f8e8d3..447bdc40696a85595e05a183c6fa10c50401c5a0 100644 (file)
@@ -38,11 +38,12 @@ unordered_set<string> CommandCreator::ha_commands6_ = {
 };
 
 ConstElementPtr
-CommandCreator::createDHCPDisable(const unsigned int max_period,
+CommandCreator::createDHCPDisable(const unsigned int origin,
+                                  const unsigned int max_period,
                                   const HAServerType& server_type) {
     ElementPtr args;
     args = Element::createMap();
-    args->set("origin", Element::create("ha-partner"));
+    args->set("origin", Element::create(origin));
     // max-period is optional. A value of 0 means that it is not specified.
     if (max_period > 0) {
         args->set("max-period", Element::create(static_cast<long int>(max_period)));
@@ -53,10 +54,11 @@ CommandCreator::createDHCPDisable(const unsigned int max_period,
 }
 
 ConstElementPtr
-CommandCreator::createDHCPEnable(const HAServerType& server_type) {
+CommandCreator::createDHCPEnable(const unsigned int origin,
+                                 const HAServerType& server_type) {
     ElementPtr args;
     args = Element::createMap();
-    args->set("origin", Element::create("ha-partner"));
+    args->set("origin", Element::create(origin));
     ConstElementPtr command = config::createCommand("dhcp-enable", args);
     insertService(command, server_type);
     return (command);
index 276f405df519bb12ca1d1395563ddbe077531a0f..a9ec33b72aea423cbcbd6f0401acd094c03e5369 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2023 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
@@ -24,22 +24,28 @@ public:
 
     /// @brief Creates dhcp-disable command for DHCP server.
     ///
+    /// @param origin A numeric value of the origin created from the
+    /// @c HAService identifier.
     /// @param max_period The max-period time the service can stay disabled
     /// before automatically transitioning to enabled state.
     /// @param server_type type of the DHCP server, i.e. v4 or v6.
     ///
     /// @return Pointer to the JSON representation of the command.
     static data::ConstElementPtr
-    createDHCPDisable(const unsigned int max_period,
+    createDHCPDisable(const unsigned int origin,
+                      const unsigned int max_period,
                       const HAServerType& server_type);
 
     /// @brief Creates dhcp-enable command for DHCP server.
     ///
+    /// @param origin A numeric value of the origin created from the
+    /// @c HAService identifier.
     /// @param server_type type of the DHCP server, i.e. v4 or v6.
     ///
     /// @return Pointer to the JSON representation of the command.
     static data::ConstElementPtr
-    createDHCPEnable(const HAServerType& server_type);
+    createDHCPEnable(const unsigned int origin,
+                     const HAServerType& server_type);
 
     /// @brief Creates ha-reset command.
     ///
index 9eee6f02504662dd1854fb2395252bd5bd06cf55..410772fb5fb19f39a6ba23346635675f9b32a86e 100644 (file)
@@ -28,7 +28,7 @@ namespace isc {
 namespace ha {
 
 HAImpl::HAImpl()
-    : config_() {
+    : config_(), services_(new HAServiceMapper()) {
 }
 
 void
@@ -40,19 +40,34 @@ void
 HAImpl::startService(const IOServicePtr& io_service,
                      const NetworkStatePtr& network_state,
                      const HAServerType& server_type) {
-    // Create the HA service and crank up the state machine.
-    service_ = boost::make_shared<HAService>(io_service, network_state,
-                                             config_->get(), server_type);
+    auto configs = config_->getAll();
+    for (auto id = 0; id < configs.size(); ++id) {
+        // Create the HA service and crank up the state machine.
+        auto service = boost::make_shared<HAService>(id, io_service, network_state,
+                                                     configs[id], server_type);
+        for (auto peer_config : configs[id]->getAllServersConfig()) {
+            services_->map(peer_config.first, service);
+        }
+
+        /// @todo Running multiple HA services concurrently is not yet implemented.
+        /// The work is in progress. This break should be removed shortly, when we
+        /// finish up adding support for running multiple services.
+        break;
+    }
     // Schedule a start of the services. This ensures we begin after
     // the dust has settled and Kea MT mode has been firmly established.
-    io_service->post([&]() { service_->startClientAndListener(); } );
+    io_service->post([&]() {
+        for (auto service : services_->getAll()) {
+            service->startClientAndListener();
+        }
+    });
 }
 
 HAImpl::~HAImpl() {
-    if (service_) {
+    for (auto service : services_->getAll()) {
         // Shut down the services explicitly, we need finer control
         // than relying on destruction order.
-        service_->stopClientAndListener();
+        service->stopClientAndListener();
     }
 }
 
@@ -97,7 +112,7 @@ HAImpl::buffer4Receive(hooks::CalloutHandle& callout_handle) {
     }
 
     // Check if we should process this query. If not, drop it.
-    if (!service_->inScope(query4)) {
+    if (!services_->get()->inScope(query4)) {
         LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_BUFFER4_RECEIVE_NOT_FOR_US)
             .arg(query4->getLabel());
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
@@ -153,7 +168,7 @@ HAImpl::leases4Committed(CalloutHandle& callout_handle) {
     // servers. In those cases we simply return without parking the DHCP query.
     // The response will be sent to the client immediately.
     try {
-        if (service_->asyncSendLeaseUpdates(query4, leases4, deleted_leases4, parking_lot) == 0) {
+        if (services_->get()->asyncSendLeaseUpdates(query4, leases4, deleted_leases4, parking_lot) == 0) {
             // Dereference the parked packet.  This releases our stake in it.
             parking_lot->dereference(query4);
             return;
@@ -211,7 +226,7 @@ HAImpl::buffer6Receive(hooks::CalloutHandle& callout_handle) {
     }
 
     // Check if we should process this query. If not, drop it.
-    if (!service_->inScope(query6)) {
+    if (!services_->get()->inScope(query6)) {
         LOG_DEBUG(ha_logger, DBGLVL_TRACE_BASIC, HA_BUFFER6_RECEIVE_NOT_FOR_US)
             .arg(query6->getLabel());
         callout_handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
@@ -267,7 +282,7 @@ HAImpl::leases6Committed(CalloutHandle& callout_handle) {
     // servers. In those cases we simply return without parking the DHCP query.
     // The response will be sent to the client immediately.
     try {
-        if (service_->asyncSendLeaseUpdates(query6, leases6, deleted_leases6, parking_lot) == 0) {
+        if (services_->get()->asyncSendLeaseUpdates(query6, leases6, deleted_leases6, parking_lot) == 0) {
             // Dereference the parked packet.  This releases our stake in it.
             parking_lot->dereference(query6);
             return;
@@ -309,7 +324,7 @@ HAImpl::commandProcessed(hooks::CalloutHandle& callout_handle) {
         /// our sole relationship in a list.
         auto ha_relationships = Element::createList();
         auto ha_relationship = Element::createMap();
-        ConstElementPtr ha_servers = service_->processStatusGet();
+        ConstElementPtr ha_servers = services_->get()->processStatusGet();
         ha_relationship->set("ha-servers", ha_servers);
         ha_relationship->set("ha-mode", Element::create(HAConfig::HAModeToString(config_->get()->getHAMode())));
         ha_relationships->add(ha_relationship);
@@ -319,7 +334,7 @@ HAImpl::commandProcessed(hooks::CalloutHandle& callout_handle) {
 
 void
 HAImpl::heartbeatHandler(CalloutHandle& callout_handle) {
-    ConstElementPtr response = service_->processHeartbeat();
+    ConstElementPtr response = services_->get()->processHeartbeat();
     callout_handle.setArgument("response", response);
 }
 
@@ -380,8 +395,8 @@ HAImpl::synchronizeHandler(hooks::CalloutHandle& callout_handle) {
     }
 
     // Command parsing was successful, so let's process the command.
-    ConstElementPtr response = service_->processSynchronize(server_name->stringValue(),
-                                                            max_period_value);
+    ConstElementPtr response = services_->get()->processSynchronize(server_name->stringValue(),
+                                                                    max_period_value);
     callout_handle.setArgument("response", response);
 }
 
@@ -438,13 +453,13 @@ HAImpl::scopesHandler(hooks::CalloutHandle& callout_handle) {
     }
 
     // Command parsing was successful, so let's process the command.
-    ConstElementPtr response = service_->processScopes(scopes_vector);
+    ConstElementPtr response = services_->get()->processScopes(scopes_vector);
     callout_handle.setArgument("response", response);
 }
 
 void
 HAImpl::continueHandler(hooks::CalloutHandle& callout_handle) {
-    ConstElementPtr response = service_->processContinue();
+    ConstElementPtr response = services_->get()->processContinue();
     callout_handle.setArgument("response", response);
 }
 
@@ -467,31 +482,31 @@ HAImpl::maintenanceNotifyHandler(hooks::CalloutHandle& callout_handle) {
         isc_throw(BadValue, "'cancel' must be a boolean in the 'ha-maintenance-notify' command");
     }
 
-    ConstElementPtr response = service_->processMaintenanceNotify(cancel_op->boolValue());
+    ConstElementPtr response = services_->get()->processMaintenanceNotify(cancel_op->boolValue());
     callout_handle.setArgument("response", response);
 }
 
 void
 HAImpl::maintenanceStartHandler(hooks::CalloutHandle& callout_handle) {
-    ConstElementPtr response = service_->processMaintenanceStart();
+    ConstElementPtr response = services_->get()->processMaintenanceStart();
     callout_handle.setArgument("response", response);
 }
 
 void
 HAImpl::maintenanceCancelHandler(hooks::CalloutHandle& callout_handle) {
-    ConstElementPtr response = service_->processMaintenanceCancel();
+    ConstElementPtr response = services_->get()->processMaintenanceCancel();
     callout_handle.setArgument("response", response);
 }
 
 void
 HAImpl::haResetHandler(hooks::CalloutHandle& callout_handle) {
-    ConstElementPtr response = service_->processHAReset();
+    ConstElementPtr response = services_->get()->processHAReset();
     callout_handle.setArgument("response", response);
 }
 
 void
 HAImpl::syncCompleteNotifyHandler(hooks::CalloutHandle& callout_handle) {
-    ConstElementPtr response = service_->processSyncCompleteNotify();
+    ConstElementPtr response = services_->get()->processSyncCompleteNotify();
     callout_handle.setArgument("response", response);
 }
 
index b5e96ec49a06fdb93ffddf8d5432a747773370ee..cb5bcf7882d1f39605fd1e71057150dbfce36edf 100644 (file)
@@ -183,8 +183,8 @@ protected:
     /// @brief Holds parsed configuration.
     HAConfigMapperPtr config_;
 
-    /// @brief Pointer to the high availability service (state machine).
-    HAServicePtr service_;
+    /// @brief Pointer to the high availability services (state machines).
+    HAServiceMapperPtr services_;
 
 };
 
index e6abe0dc19f615ea693e7a633ddc447fc89bc1b7..9dc44a37b54bff11c60d2d90bd762cbbb71ebbcb 100644 (file)
@@ -71,9 +71,10 @@ const int HAService::HA_MAINTENANCE_CANCEL_EVT;
 const int HAService::HA_CONTROL_RESULT_MAINTENANCE_NOT_ALLOWED;
 const int HAService::HA_SYNCED_PARTNER_UNAVAILABLE_EVT;
 
-HAService::HAService(const IOServicePtr& io_service, const NetworkStatePtr& network_state,
-                     const HAConfigPtr& config, const HAServerType& server_type)
-    : io_service_(io_service), network_state_(network_state), config_(config),
+HAService::HAService(const unsigned int id, const IOServicePtr& io_service,
+                     const NetworkStatePtr& network_state, const HAConfigPtr& config,
+                     const HAServerType& server_type)
+    : id_(id), io_service_(io_service), network_state_(network_state), config_(config),
       server_type_(server_type), client_(), listener_(), communication_state_(),
       query_filter_(config), mutex_(), pending_requests_(),
       lease_update_backlog_(config->getDelayedUpdatesLimit()),
@@ -86,7 +87,7 @@ HAService::HAService(const IOServicePtr& io_service, const NetworkStatePtr& netw
         communication_state_.reset(new CommunicationState6(io_service_, config));
     }
 
-    network_state_->reset(NetworkState::Origin::HA_COMMAND);
+    network_state_->reset(NetworkState::HA_LOCAL_COMMAND+id_);
 
     startModel(HA_WAITING_ST);
 
@@ -144,7 +145,7 @@ HAService::~HAService() {
     // Stop client and/or listener.
     stopClientAndListener();
 
-    network_state_->reset(NetworkState::Origin::HA_COMMAND);
+    network_state_->reset(NetworkState::HA_LOCAL_COMMAND+id_);
 }
 
 void
@@ -1066,7 +1067,7 @@ HAService::adjustNetworkState() {
         LOG_INFO(ha_logger, HA_LOCAL_DHCP_DISABLE)
             .arg(config_->getThisServerName())
             .arg(current_state_name);
-        network_state_->disableService(NetworkState::Origin::HA_COMMAND);
+        network_state_->disableService(NetworkState::HA_LOCAL_COMMAND+id_);
 
     } else if (should_enable && !network_state_->isServiceEnabled()) {
         std::string current_state_name = getStateLabel(getCurrState());
@@ -1074,7 +1075,7 @@ HAService::adjustNetworkState() {
         LOG_INFO(ha_logger, HA_LOCAL_DHCP_ENABLE)
             .arg(config_->getThisServerName())
             .arg(current_state_name);
-        network_state_->enableService(NetworkState::Origin::HA_COMMAND);
+        network_state_->enableService(NetworkState::HA_LOCAL_COMMAND+id_);
     }
 }
 
@@ -1854,7 +1855,8 @@ HAService::asyncDisableDHCPService(HttpClient& http_client,
          HostHttpHeader(remote_config->getUrl().getStrippedHostname()));
 
     remote_config->addBasicAuthHttpHeader(request);
-    request->setBodyAsJson(CommandCreator::createDHCPDisable(max_period,
+    request->setBodyAsJson(CommandCreator::createDHCPDisable(NetworkState::HA_REMOTE_COMMAND+id_,
+                                                             max_period,
                                                              server_type_));
     request->finalize();
 
@@ -1932,7 +1934,8 @@ HAService::asyncEnableDHCPService(HttpClient& http_client,
         (HttpRequest::Method::HTTP_POST, "/", HttpVersion::HTTP_11(),
          HostHttpHeader(remote_config->getUrl().getStrippedHostname()));
     remote_config->addBasicAuthHttpHeader(request);
-    request->setBodyAsJson(CommandCreator::createDHCPEnable(server_type_));
+    request->setBodyAsJson(CommandCreator::createDHCPEnable(NetworkState::HA_REMOTE_COMMAND+id_,
+                                                            server_type_));
     request->finalize();
 
     // Response object should also be created because the HTTP client needs
@@ -2000,12 +2003,12 @@ HAService::asyncEnableDHCPService(HttpClient& http_client,
 
 void
 HAService::localDisableDHCPService() {
-    network_state_->disableService(NetworkState::Origin::HA_COMMAND);
+    network_state_->disableService(NetworkState::HA_LOCAL_COMMAND+id_);
 }
 
 void
 HAService::localEnableDHCPService() {
-    network_state_->enableService(NetworkState::Origin::HA_COMMAND);
+    network_state_->enableService(NetworkState::HA_LOCAL_COMMAND+id_);
 }
 
 void
index 80ecf7760cea11e25baf0219b31d1d5713293129..fa6c5d49eb0f989b6680b20424a18192d23942ba 100644 (file)
 namespace isc {
 namespace ha {
 
+class HAService;
+
+/// @brief Type of an object mapping @c HAService to relationships.
+typedef HARelationshipMapper<HAService> HAServiceMapper;
+
+/// @brief Pointer to an object mapping @c HAService to relationships.
+typedef boost::shared_ptr<HAServiceMapper> HAServiceMapperPtr;
+
 /// @brief High availability service.
 ///
 /// This class derives from the @c util::StateModel and implements a
@@ -98,12 +106,14 @@ public:
     /// state model in waiting state.  Creates and starts the client and the
     /// listener (if one).
     ///
+    /// @param id Unique @c HAService id.
     /// @param io_service Pointer to the IO service used by the DHCP server.
     /// @param config Parsed HA hook library configuration.
     /// @param network_state Object holding state of the DHCP service
     /// (enabled/disabled).
     /// @param server_type Server type, i.e. DHCPv4 or DHCPv6 server.
-    HAService(const asiolink::IOServicePtr& io_service,
+    HAService(const unsigned int id,
+              const asiolink::IOServicePtr& io_service,
               const dhcp::NetworkStatePtr& network_state,
               const HAConfigPtr& config,
               const HAServerType& server_type = HAServerType::DHCPv4);
@@ -1153,6 +1163,9 @@ protected:
     /// @param tcp_native_fd socket descriptor to register
     void clientCloseHandler(int tcp_native_fd);
 
+    /// @brief Unique service id.
+    unsigned int id_;
+
     /// @brief Pointer to the IO service object shared between this hooks
     /// library and the DHCP server.
     asiolink::IOServicePtr io_service_;
index 1c91e1c95937d0c41d8709447622697ab03374ed..36df83e94d0fef2cdc420a3d54cdbbc2ea4b364d 100644 (file)
@@ -373,7 +373,7 @@ TEST_F(CloseHATest, close4) {
 
     // Check that the disabled state is reset on dhcp4_srv_configured.
     ASSERT_TRUE(network_state->isServiceEnabled());
-    network_state->disableService(NetworkState::Origin::HA_COMMAND);
+    network_state->disableService(NetworkState::HA_LOCAL_COMMAND);
     ASSERT_FALSE(network_state->isServiceEnabled());
 
     // Start HA service.
@@ -508,7 +508,7 @@ TEST_F(CloseHATest, close4Backup) {
     // It is first reset by the constructor and then adjusted by running the
     // state model.
     ASSERT_TRUE(network_state->isServiceEnabled());
-    network_state->disableService(NetworkState::Origin::HA_COMMAND);
+    network_state->disableService(NetworkState::HA_LOCAL_COMMAND);
     ASSERT_FALSE(network_state->isServiceEnabled());
 
     // Start HA service.
@@ -612,7 +612,7 @@ TEST_F(CloseHATest, close6) {
 
     // Check that the disabled state is reset on dhcp6_srv_configured.
     ASSERT_TRUE(network_state->isServiceEnabled());
-    network_state->disableService(NetworkState::Origin::HA_COMMAND);
+    network_state->disableService(NetworkState::HA_LOCAL_COMMAND);
     ASSERT_FALSE(network_state->isServiceEnabled());
 
     // Start HA service.
@@ -747,7 +747,7 @@ TEST_F(CloseHATest, close6Backup) {
     // It is first reset by the constructor and then adjusted by running the
     // state model.
     ASSERT_TRUE(network_state->isServiceEnabled());
-    network_state->disableService(NetworkState::Origin::HA_COMMAND);
+    network_state->disableService(NetworkState::HA_LOCAL_COMMAND);
     ASSERT_FALSE(network_state->isServiceEnabled());
 
     // Start HA service.
index 8966b4cb9c7f5b61601f6dbf21efc14db0d86d72..d2bf2f4afe6106968384133f63449bd16c7be42f 100644 (file)
@@ -14,6 +14,7 @@
 #include <exceptions/exceptions.h>
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/lease.h>
+#include <dhcpsrv/network_state.h>
 #include <boost/pointer_cast.hpp>
 #include <gtest/gtest.h>
 #include <vector>
@@ -150,7 +151,8 @@ testCommandBasics(const ConstElementPtr& command,
 // This test verifies that the dhcp-disable command is correct.
 TEST(CommandCreatorTest, createDHCPDisable4) {
     // Create command with max-period value set to 20.
-    ConstElementPtr command = CommandCreator::createDHCPDisable(20, HAServerType::DHCPv4);
+    ConstElementPtr command = CommandCreator::createDHCPDisable(NetworkState::HA_REMOTE_COMMAND+1, 20,
+                                                                HAServerType::DHCPv4);
     ConstElementPtr arguments;
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-disable", "dhcp4",
                                               arguments));
@@ -161,28 +163,30 @@ TEST(CommandCreatorTest, createDHCPDisable4) {
     EXPECT_EQ(20, max_period->intValue());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ("ha-partner", origin->stringValue());
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin->intValue());
 
     // Repeat the test but this time the max-period is not specified.
-    command = CommandCreator::createDHCPDisable(0, HAServerType::DHCPv4);
+    command = CommandCreator::createDHCPDisable(NetworkState::HA_REMOTE_COMMAND+1, 0,
+                                                HAServerType::DHCPv4);
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-disable", "dhcp4",
                                               arguments));
     ASSERT_EQ(1, arguments->size());
     origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ("ha-partner", origin->stringValue());
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin->intValue());
 }
 
 // This test verifies that the dhcp-enable command is correct.
 TEST(CommandCreatorTest, createDHCPEnable4) {
     ConstElementPtr arguments;
-    ConstElementPtr command = CommandCreator::createDHCPEnable(HAServerType::DHCPv4);
+    ConstElementPtr command = CommandCreator::createDHCPEnable(NetworkState::HA_REMOTE_COMMAND+1,
+                                                               HAServerType::DHCPv4);
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-enable", "dhcp4",
                                               arguments));
     ASSERT_EQ(1, arguments->size());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ("ha-partner", origin->stringValue());
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin->intValue());
 }
 
 // This test verifies that the ha-reset command sent to DHCPv4 server is correct.
@@ -283,7 +287,8 @@ TEST(CommandCreatorTest, createLease4GetPageZeroLimit) {
 // correct.
 TEST(CommandCreatorTest, createDHCPDisable6) {
     // Create command with max-period value set to 20.
-    ConstElementPtr command = CommandCreator::createDHCPDisable(20, HAServerType::DHCPv6);
+    ConstElementPtr command = CommandCreator::createDHCPDisable(NetworkState::HA_REMOTE_COMMAND+2, 20,
+                                                                HAServerType::DHCPv6);
     ConstElementPtr arguments;
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-disable", "dhcp6",
                                               arguments));
@@ -294,29 +299,29 @@ TEST(CommandCreatorTest, createDHCPDisable6) {
     EXPECT_EQ(20, max_period->intValue());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ("ha-partner", origin->stringValue());
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin->intValue());
 
     // Repeat the test but this time the max-period is not specified.
-    command = CommandCreator::createDHCPDisable(0, HAServerType::DHCPv6);
+    command = CommandCreator::createDHCPDisable(NetworkState::HA_REMOTE_COMMAND+2, 0, HAServerType::DHCPv6);
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-disable", "dhcp6",
                                               arguments));
     ASSERT_EQ(1, arguments->size());
     origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ("ha-partner", origin->stringValue());
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin->intValue());
 }
 
 // This test verifies that the dhcp-enable command (DHCPv6 case) is
 // correct.
 TEST(CommandCreatorTest, createDHCPEnable6) {
     ConstElementPtr arguments;
-    ConstElementPtr command = CommandCreator::createDHCPEnable(HAServerType::DHCPv6);
+    ConstElementPtr command = CommandCreator::createDHCPEnable(NetworkState::HA_REMOTE_COMMAND+2, HAServerType::DHCPv6);
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-enable", "dhcp6",
                                               arguments));
     ASSERT_EQ(1, arguments->size());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ("ha-partner", origin->stringValue());
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin->intValue());
 }
 
 // This test verifies that the ha-reset command sent to DHCPv6 server is correct.
index cb6e71522af50c3048e81f1f4c5371ecba24f11a..c2b5d5f0d1a2184deca66e7b5c34d291476478ad 100644 (file)
@@ -61,7 +61,7 @@ class TestHAImpl : public HAImpl {
 public:
 
     using HAImpl::config_;
-    using HAImpl::service_;
+    using HAImpl::services_;
 };
 
 /// @brief Test fixture class for @c HAImpl.
@@ -115,8 +115,8 @@ TEST_F(HAImplTest, startService) {
 
     // Make sure that the HA service has been created for the requested
     // server type.
-    ASSERT_TRUE(ha_impl.service_);
-    EXPECT_EQ(HAServerType::DHCPv4, ha_impl.service_->getServerType());
+    ASSERT_TRUE(ha_impl.services_);
+    EXPECT_EQ(HAServerType::DHCPv4, ha_impl.services_->get()->getServerType());
 }
 
 // Tests that HAService object is created for DHCPv6 service.
@@ -134,8 +134,8 @@ TEST_F(HAImplTest, startService6) {
 
     // Make sure that the HA service has been created for the requested
     // server type.
-    ASSERT_TRUE(ha_impl.service_);
-    EXPECT_EQ(HAServerType::DHCPv6, ha_impl.service_->getServerType());
+    ASSERT_TRUE(ha_impl.services_);
+    EXPECT_EQ(HAServerType::DHCPv6, ha_impl.services_->get()->getServerType());
 }
 
 // Tests for buffer4_receive callout implementation.
@@ -155,7 +155,7 @@ TEST_F(HAImplTest, buffer4Receive) {
 
     // Initially the HA service is in the waiting state and serves no scopes.
     // We need to explicitly enable the scope to be served.
-    ha_impl.service_->serveDefaultScopes();
+    ha_impl.services_->get()->serveDefaultScopes();
 
     // Create callout handle to be used for passing arguments to the
     // callout.
@@ -263,7 +263,7 @@ TEST_F(HAImplTest, buffer6Receive) {
 
     // Initially the HA service is in the waiting state and serves no scopes.
     // We need to explicitly enable the scope to be served.
-    ha_impl.service_->serveDefaultScopes();
+    ha_impl.services_->get()->serveDefaultScopes();
 
     // Create callout handle to be used for passing arguments to the
     // callout.
index f6076a61c7d55c986c710e36042ce7ac4fcbd089..2749f92556211759cdb0970fecc481332bc3862f 100644 (file)
@@ -36,16 +36,18 @@ public:
 
     /// @brief Constructor.
     ///
+    /// @param id Unique service id.
     /// @param io_service Pointer to the IO service used by the DHCP server.
     /// @param network_state Object holding state of the DHCP service
     /// (enabled/disabled).
     /// @param config Parsed HA hook library configuration.
     /// @param server_type Server type, i.e. DHCPv4 or DHCPv6 server.
-    TestHAService(const IOServicePtr& io_service,
+    TestHAService(const unsigned int id,
+                  const IOServicePtr& io_service,
                   const NetworkStatePtr& network_state,
                   const HAConfigPtr& config,
                   const HAServerType& server_type = HAServerType::DHCPv4)
-        : HAService(io_service, network_state, config, server_type) {
+        : HAService(id, io_service, network_state, config, server_type) {
     }
 
     /// @brief Test version of the @c HAService::runModel.
@@ -192,7 +194,7 @@ TEST_F(HAMtServiceTest, multiThreadingBasics) {
 
     // Instantiate the service.
     TestHAServicePtr service;
-    ASSERT_NO_THROW_LOG(service.reset(new TestHAService(io_service_, network_state_,
+    ASSERT_NO_THROW_LOG(service.reset(new TestHAService(1, io_service_, network_state_,
                                                         ha_config->get())));
     // Multi-threading should be enabled.
     ASSERT_TRUE(ha_config->get()->getEnableMultiThreading());
@@ -312,7 +314,7 @@ TEST_F(HAMtServiceTest, multiThreadingTls) {
 
     // Instantiate the service.
     TestHAServicePtr service;
-    ASSERT_NO_THROW_LOG(service.reset(new TestHAService(io_service_, network_state_,
+    ASSERT_NO_THROW_LOG(service.reset(new TestHAService(1, io_service_, network_state_,
                                                         ha_config->get())));
     // Multi-threading should be enabled.
     ASSERT_TRUE(ha_config->get()->getEnableMultiThreading());
@@ -505,7 +507,7 @@ TEST_F(HAMtServiceTest, multiThreadingConfigStartup) {
 
         // Instantiate the service.
         TestHAServicePtr service;
-        ASSERT_NO_THROW_LOG(service.reset(new TestHAService(io_service_, network_state_,
+        ASSERT_NO_THROW_LOG(service.reset(new TestHAService(1, io_service_, network_state_,
                                                             ha_config->get())));
         ASSERT_NO_THROW_LOG(service->startClientAndListener());
 
index 866f30a2973008fd469d0b462a7a329b534a5d4b..5280aa945a9025ebbe0294915b1bcd3629f2cc2a 100644 (file)
@@ -135,16 +135,18 @@ public:
 
     /// @brief Constructor.
     ///
+    /// @parma id Unique service identifier.
     /// @param io_service Pointer to the IO service used by the DHCP server.
     /// @param network_state Object holding state of the DHCP service
     /// (enabled/disabled).
     /// @param config Parsed HA hook library configuration.
     /// @param server_type Server type, i.e. DHCPv4 or DHCPv6 server.
-    TestHAService(const IOServicePtr& io_service,
+    TestHAService(const unsigned int id,
+                  const IOServicePtr& io_service,
                   const NetworkStatePtr& network_state,
                   const HAConfigPtr& config,
                   const HAServerType& server_type = HAServerType::DHCPv4)
-        : HAService(io_service, network_state, config, server_type) {
+        : HAService(id, io_service, network_state, config, server_type) {
     }
 
     /// @brief Test version of the @c HAService::runModel.
@@ -650,7 +652,7 @@ public:
                          const HAServerType& server_type = HAServerType::DHCPv4) {
 
         ASSERT_FALSE(config->getEnableMultiThreading());
-        ASSERT_NO_THROW_LOG(service_.reset(new TestHAService(io_service_, state,
+        ASSERT_NO_THROW_LOG(service_.reset(new TestHAService(1, io_service_, state,
                                            config, server_type)));
         ASSERT_TRUE(service_->client_);
         ASSERT_FALSE(service_->client_->getThreadIOService());
@@ -1054,7 +1056,7 @@ public:
         state->modifyPokeTime(-30);
 
         // Create the service and replace the default communication state object.
-        TestHAService service(io_service_, network_state_, config_storage);
+        TestHAService service(1, io_service_, network_state_, config_storage);
         service.communication_state_ = state;
 
         EXPECT_FALSE(state->isPoked());
@@ -2058,7 +2060,7 @@ public:
             listener3_->start();
         });
 
-        HAService service(io_service_, network_state_, config_storage);
+        HAService service(1, io_service_, network_state_, config_storage);
 
         // The tested function is synchronous, so we need to run server side IO service
         // in bakckground to not block the main thread.
@@ -2107,7 +2109,7 @@ public:
             listener3_->start();
         });
 
-        HAService service(io_service_, network_state_, config_storage,
+        HAService service(1, io_service_, network_state_, config_storage,
                           HAServerType::DHCPv6);
 
         // The tested function is synchronous, so we need to run server side IO service
@@ -2176,7 +2178,7 @@ TEST_F(HAServiceTest, loadBalancingScopeSelection) {
     // Create HA configuration for load balancing.
     HAConfigPtr config_storage = createValidConfiguration();
     // ... and HA service using this configuration.
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
     service.verboseTransition(HA_LOAD_BALANCING_ST);
     service.runModel(HAService::NOP_EVT);
 
@@ -2218,7 +2220,7 @@ TEST_F(HAServiceTest, hotStandbyScopeSelectionThisPrimary) {
     config_storage->getPeerConfig("server2")->setRole("standby");
 
     // ... and HA service using this configuration.
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
     service.verboseTransition(HA_HOT_STANDBY_ST);
     service.runModel(HAService::NOP_EVT);
 
@@ -2268,7 +2270,7 @@ TEST_F(HAServiceTest, hotStandbyScopeSelectionThisStandby) {
     config_storage->setThisServerName("server2");
 
     // ... and HA service using this configuration.
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Check the reported info about servers.
     ConstElementPtr ha_servers = service.processStatusGet();
@@ -2738,7 +2740,7 @@ TEST_F(HAServiceTest, processHeartbeat) {
     HAConfigMapperPtr config_storage;
     ASSERT_NO_THROW(config_storage = HAConfigParser::parse(Element::fromJSON(config_text)));
 
-    TestHAService service(io_service_,  network_state_, config_storage->get());
+    TestHAService service(1, io_service_,  network_state_, config_storage->get());
     service.query_filter_.serveDefaultScopes();
 
     int unsent_updates = 6;
@@ -2812,7 +2814,7 @@ TEST_F(HAServiceTest, recurringHeartbeatDelay) {
     NakedCommunicationState4Ptr state(new NakedCommunicationState4(io_service_,
                                                                    config_storage));
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
     service.communication_state_ = state;
 
     // Let's explicitly transition the state machine to the load balancing state
@@ -2985,7 +2987,7 @@ TEST_F(HAServiceTest, asyncSyncLeases4) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
     // Setting the heartbeat delay to 0 disables the recurring heartbeat.
     // We just want to synchronize leases and not send the heartbeat.
     config_storage->setHeartbeatDelay(0);
@@ -3114,7 +3116,7 @@ TEST_F(HAServiceTest, asyncSyncLeases4Authorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
     // Setting the heartbeat delay to 0 disables the recurring heartbeat.
     // We just want to synchronize leases and not send the heartbeat.
     config_storage->setHeartbeatDelay(0);
@@ -3202,7 +3204,7 @@ TEST_F(HAServiceTest, asyncSyncLeases4WrongAnswer) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Start fetching leases asynchronously.
     ASSERT_NO_THROW(service.asyncSyncLeases());
@@ -3220,7 +3222,7 @@ TEST_F(HAServiceTest, asyncSyncLeases4ServerOffline) {
     // We just want to synchronize leases and not send the heartbeat.
     config_storage->setHeartbeatDelay(0);
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Start fetching leases asynchronously.
     ASSERT_NO_THROW(service.asyncSyncLeases());
@@ -3257,7 +3259,7 @@ TEST_F(HAServiceTest, asyncSyncLeases4ServerUnauthorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Start fetching leases asynchronously.
     ASSERT_NO_THROW(service.asyncSyncLeases());
@@ -3326,7 +3328,7 @@ TEST_F(HAServiceTest, asyncSyncLeases6) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage,
+    TestHAService service(1, io_service_, network_state_, config_storage,
                           HAServerType::DHCPv6);
     // Setting the heartbeat delay to 0 disables the recurring heartbeat.
     // We just want to synchronize leases and not send the heartbeat.
@@ -3456,7 +3458,7 @@ TEST_F(HAServiceTest, asyncSyncLeases6Authorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage,
+    TestHAService service(1, io_service_, network_state_, config_storage,
                           HAServerType::DHCPv6);
     // Setting the heartbeat delay to 0 disables the recurring heartbeat.
     // We just want to synchronize leases and not send the heartbeat.
@@ -3545,7 +3547,7 @@ TEST_F(HAServiceTest, asyncSyncLeases6WrongAnswer) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage,
+    TestHAService service(1, io_service_, network_state_, config_storage,
                           HAServerType::DHCPv6);
 
     // Start fetching leases asynchronously.
@@ -3564,7 +3566,7 @@ TEST_F(HAServiceTest, asyncSyncLeases6ServerOffline) {
     // We just want to synchronize leases and not send the heartbeat.
     config_storage->setHeartbeatDelay(0);
 
-    TestHAService service(io_service_, network_state_, config_storage,
+    TestHAService service(1, io_service_, network_state_, config_storage,
                           HAServerType::DHCPv6);
 
     // Start fetching leases asynchronously.
@@ -3602,7 +3604,7 @@ TEST_F(HAServiceTest, asyncSyncLeases6Unauthorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage,
+    TestHAService service(1, io_service_, network_state_, config_storage,
                           HAServerType::DHCPv6);
 
     // Start fetching leases asynchronously.
@@ -4022,7 +4024,7 @@ TEST_F(HAServiceTest, asyncDisableDHCPService4) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-disable command with max-period of 10 seconds.
     // When the transaction is finished, the IO service gets stopped.
@@ -4068,7 +4070,7 @@ TEST_F(HAServiceTest, asyncDisableDHCPService4Authorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-disable command with max-period of 10 seconds.
     // When the transaction is finished, the IO service gets stopped.
@@ -4097,7 +4099,7 @@ TEST_F(HAServiceTest, asyncDisableDHCPService4ServerOffline) {
     // Create HA configuration.
     HAConfigPtr config_storage = createValidConfiguration();
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-disable command with max-period of 10 seconds.
     // When the transaction is finished, the IO service gets stopped.
@@ -4132,7 +4134,7 @@ TEST_F(HAServiceTest, asyncDisableDHCPService4ControlResultError) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-disable command with max-period of 10 seconds.
     // When the transaction is finished, the IO service gets stopped.
@@ -4166,7 +4168,7 @@ TEST_F(HAServiceTest, asyncDisableDHCPService4ControlResultUnauthorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-disable command with max-period of 10 seconds.
     // When the transaction is finished, the IO service gets stopped.
@@ -4196,7 +4198,7 @@ TEST_F(HAServiceTest, asyncEnableDHCPService4) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-enable command. When the transaction is finished,
     // the IO service gets stopped.
@@ -4241,7 +4243,7 @@ TEST_F(HAServiceTest, asyncEnableDHCPService4Authorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-enable command. When the transaction is finished,
     // the IO service gets stopped.
@@ -4269,7 +4271,7 @@ TEST_F(HAServiceTest, asyncEnableDHCPService4ServerOffline) {
     // Create HA configuration.
     HAConfigPtr config_storage = createValidConfiguration();
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-enable command. When the transaction is finished,
     // the IO service gets stopped.
@@ -4304,7 +4306,7 @@ TEST_F(HAServiceTest, asyncEnableDHCPService4ControlResultError) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-enable command. When the transaction is finished,
     // the IO service gets stopped.
@@ -4338,7 +4340,7 @@ TEST_F(HAServiceTest, asyncEnableDHCPService4ControlResultUnauthorized) {
         listener3_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Send dhcp-enable command. When the transaction is finished,
     // the IO service gets stopped.
@@ -4361,7 +4363,7 @@ TEST_F(HAServiceTest, processScopes) {
     HAConfigPtr config_storage = createValidConfiguration();
 
     // Create HA service using this configuration.
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Enable "server1" and "server2" scopes.
     ConstElementPtr rsp;
@@ -4409,7 +4411,7 @@ TEST_F(HAServiceTest, processContinue) {
     ASSERT_NO_THROW(config_storage->getStateMachineConfig()->
                     getStateConfig(HA_WAITING_ST)->setPausing("always"));
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Pause the state machine.
     EXPECT_NO_THROW(service.transition(HA_WAITING_ST, HAService::NOP_EVT));
@@ -4462,7 +4464,7 @@ TEST_F(HAServiceTest, processMaintenanceNotify) {
         HA_HOT_STANDBY_ST,
     };
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Test transition from the states for which it is allowed.
     for (auto state : valid_states) {
@@ -4528,7 +4530,7 @@ TEST_F(HAServiceTest, processMaintenanceStartSuccess) {
         listener2_->start();
     });
 
-    HAService service(io_service_, network_state_, config_storage);
+    HAService service(1, io_service_, network_state_, config_storage);
 
     // The tested function is synchronous, so we need to run server side IO service
     // in background to not block the main thread.
@@ -4580,7 +4582,7 @@ TEST_F(HAServiceTest, processMaintenanceStartSuccessAuthorized) {
         listener2_->start();
     });
 
-    HAService service(io_service_, network_state_, config_storage);
+    HAService service(1, io_service_, network_state_, config_storage);
 
     // The tested function is synchronous, so we need to run server side IO service
     // in background to not block the main thread.
@@ -4623,7 +4625,7 @@ TEST_F(HAServiceTest, processMaintenanceStartPartnerDown) {
         listener_->start();
     });
 
-    HAService service(io_service_, network_state_, config_storage);
+    HAService service(1, io_service_, network_state_, config_storage);
 
     // The tested function is synchronous, so we need to run server side IO service
     // in background to not block the main thread.
@@ -4667,7 +4669,7 @@ TEST_F(HAServiceTest, processMaintenanceStartPartnerError) {
         listener2_->start();
     });
 
-    HAService service(io_service_, network_state_, config_storage);
+    HAService service(1, io_service_, network_state_, config_storage);
 
     // The tested function is synchronous, so we need to run server side IO service
     // in background to not block the main thread.
@@ -4709,7 +4711,7 @@ TEST_F(HAServiceTest, processMaintenanceStartPartnerUnauthorized) {
         listener2_->start();
     });
 
-    HAService service(io_service_, network_state_, config_storage);
+    HAService service(1, io_service_, network_state_, config_storage);
 
     // The tested function is synchronous, so we need to run server side IO service
     // in background to not block the main thread.
@@ -4752,7 +4754,7 @@ TEST_F(HAServiceTest, processMaintenanceStartNotAllowed) {
         listener2_->start();
     });
 
-    HAService service(io_service_, network_state_, config_storage);
+    HAService service(1, io_service_, network_state_, config_storage);
 
     // The tested function is synchronous, so we need to run server side IO service
     // in background to not block the main thread.
@@ -4794,7 +4796,7 @@ TEST_F(HAServiceTest, processMaintenanceCancelSuccess) {
         listener2_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     ASSERT_NO_THROW(service.verboseTransition(HA_PARTNER_IN_MAINTENANCE_ST));
 
@@ -4845,7 +4847,7 @@ TEST_F(HAServiceTest, processMaintenanceCancelSuccessAuthorized) {
         listener2_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     ASSERT_NO_THROW(service.verboseTransition(HA_PARTNER_IN_MAINTENANCE_ST));
 
@@ -4888,7 +4890,7 @@ TEST_F(HAServiceTest, processMaintenanceCancelPartnerError) {
         listener2_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     ASSERT_NO_THROW(service.verboseTransition(HA_PARTNER_IN_MAINTENANCE_ST));
 
@@ -4934,7 +4936,7 @@ TEST_F(HAServiceTest, processMaintenanceCancelPartnerUnauthorized) {
         listener2_->start();
     });
 
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     ASSERT_NO_THROW(service.verboseTransition(HA_PARTNER_IN_MAINTENANCE_ST));
 
@@ -4966,7 +4968,7 @@ TEST_F(HAServiceTest, processMaintenanceCancelPartnerUnauthorized) {
 // This test verifies that the ha-reset command is processed successfully.
 TEST_F(HAServiceTest, processHAReset) {
     HAConfigPtr config_storage = createValidConfiguration();
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Transition the server to the load-balancing state.
     EXPECT_NO_THROW(service.transition(HA_LOAD_BALANCING_ST, HAService::NOP_EVT));
@@ -4991,7 +4993,7 @@ TEST_F(HAServiceTest, processHAReset) {
 // the server is already in the waiting state.
 TEST_F(HAServiceTest, processHAResetWaiting) {
     HAConfigPtr config_storage = createValidConfiguration();
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Transition the server to the waiting state.
     EXPECT_NO_THROW(service.transition(HA_WAITING_ST, HAService::NOP_EVT));
@@ -5018,14 +5020,14 @@ TEST_F(HAServiceTest, processHAResetWaiting) {
 // state and enables the service when it is in another state.
 TEST_F(HAServiceTest, processSyncCompleteNotify) {
     HAConfigPtr config_storage = createValidConfiguration();
-    TestHAService service(io_service_, network_state_, config_storage);
+    TestHAService service(1, io_service_, network_state_, config_storage);
 
     // Transition the server to the partner-down state.
     EXPECT_NO_THROW(service.transition(HA_PARTNER_DOWN_ST, HAService::NOP_EVT));
 
     // Simulate disabling the DHCP service for synchronization.
     if (service.network_state_->isServiceEnabled()) {
-        EXPECT_NO_THROW(service.network_state_->disableService(NetworkState::Origin::HA_COMMAND));
+        EXPECT_NO_THROW(service.network_state_->disableService(NetworkState::HA_LOCAL_COMMAND+1));
     }
 
     ConstElementPtr rsp;
@@ -5054,7 +5056,7 @@ TEST_F(HAServiceTest, processSyncCompleteNotify) {
     EXPECT_NO_THROW(service.transition(HA_LOAD_BALANCING_ST, HAService::NOP_EVT));
 
     // Disable the service again.
-    EXPECT_NO_THROW(service.network_state_->disableService(NetworkState::Origin::HA_COMMAND));
+    EXPECT_NO_THROW(service.network_state_->disableService(NetworkState::HA_LOCAL_COMMAND+1));
 
     EXPECT_NO_THROW(rsp = service.processSyncCompleteNotify());
 
@@ -5537,12 +5539,12 @@ public:
         // state.
         if (dhcp_enabled) {
             if (service_->network_state_->isServiceEnabled()) {
-                service_->network_state_->disableService(NetworkState::Origin::HA_COMMAND);
+                service_->network_state_->disableService(NetworkState::HA_LOCAL_COMMAND+1);
             }
 
         } else {
             if (!service_->network_state_->isServiceEnabled()) {
-                service_->network_state_->enableService(NetworkState::Origin::HA_COMMAND);
+                service_->network_state_->enableService(NetworkState::HA_LOCAL_COMMAND+1);
             }
         }
 
@@ -8011,7 +8013,7 @@ TEST_F(HAServiceStateMachineTest, clearRejectedLeaseUpdatesPassiveBackup) {
 // Verifies that the server doesn't terminate when the partner is unavailable.
 TEST_F(HAServiceStateMachineTest, doNotTerminateWhenPartnerUnavailable) {
     HAConfigPtr config_storage = createValidConfiguration();
-    ASSERT_NO_THROW_LOG(service_.reset(new TestHAService(io_service_, network_state_,
+    ASSERT_NO_THROW_LOG(service_.reset(new TestHAService(1, io_service_, network_state_,
                                                          config_storage)));
     NakedCommunicationState4Ptr state(new NakedCommunicationState4(io_service_,
                                                                    config_storage));
index 14e4fe7efb2431561edda911494cb635752e57b3..7dd77311bd87ca61a3faf275497df427dfec93a8 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2023 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
 #include <util/multi_threading_mgr.h>
 #include <boost/enable_shared_from_this.hpp>
 #include <functional>
+#include <sstream>
 #include <string>
+#include <unordered_set>
 
 using namespace isc::util;
 
-namespace {
-
-/// @brief Name of the timer used by the @c NetworkState class.
-const std::string NETWORK_STATE_TIMER_NAME_USER_CMD = "network-state-timer-user-cmd";
-const std::string NETWORK_STATE_TIMER_NAME_HA_CMD = "network-state-timer-ha-cmd";
-
-} // end of anonymous namespace
-
 namespace isc {
 namespace dhcp {
 
@@ -35,14 +29,15 @@ public:
     NetworkStateImpl(const NetworkState::ServerType& server_type)
         : server_type_(server_type), globally_disabled_(false),
           disabled_subnets_(), disabled_networks_(),
-          timer_mgr_(TimerMgr::instance()), disabled_by_user_command_(false),
-          disabled_by_ha_command_(false), disabled_by_db_connection_(0) {
+          timer_mgr_(TimerMgr::instance()), disabled_by_origin_(),
+          disabled_by_db_connection_(0) {
     }
 
     /// @brief Destructor.
     ~NetworkStateImpl() {
-        destroyTimer(NetworkState::Origin::USER_COMMAND);
-        destroyTimer(NetworkState::Origin::HA_COMMAND);
+        for (auto origin : disabled_by_origin_) {
+            destroyTimer(origin);
+        }
     }
 
     /// @brief Sets appropriate disabled or enabled DHCP service state for the
@@ -57,49 +52,27 @@ public:
     ///
     /// @param disable The value of the flag used to perform the transition.
     /// @param origin The origin of the state transition.
-    void setDisableService(const bool disable,
-                           const NetworkState::Origin& origin) {
+    void setDisableService(const bool disable, unsigned int origin) {
         if (disable) {
             // Disable the service for any flag.
             globally_disabled_ = true;
-            switch (origin) {
-            case NetworkState::Origin::USER_COMMAND:
-                disabled_by_user_command_ = true;
-                break;
-            case NetworkState::Origin::HA_COMMAND:
-                disabled_by_ha_command_ = true;
-                break;
-            case NetworkState::Origin::DB_CONNECTION:
+            if (origin == NetworkState::DB_CONNECTION) {
                 ++disabled_by_db_connection_;
-                break;
-            default:
-                isc_throw(NotImplemented, "origin value not handled when "
-                          "disabling the network state");
-                break;
+            } else {
+                disabled_by_origin_.insert(origin);
             }
         } else {
-            switch (origin) {
-            case NetworkState::Origin::USER_COMMAND:
-                disabled_by_user_command_ = false;
-                break;
-            case NetworkState::Origin::HA_COMMAND:
-                disabled_by_ha_command_ = false;
-                break;
-            case NetworkState::Origin::DB_CONNECTION:
+            if (origin == NetworkState::DB_CONNECTION) {
                 // Never go below 0 (using unsigned type).
                 // This should never happen anyway.
                 if (disabled_by_db_connection_) {
                     --disabled_by_db_connection_;
                 }
-                break;
-            default:
-                isc_throw(NotImplemented, "origin value not handled when "
-                          "enabling the network state");
-                break;
+            } else {
+                disabled_by_origin_.erase(origin);
             }
             // Enable the service only if all flags have been cleared.
-            if (!disabled_by_user_command_ && !disabled_by_ha_command_ &&
-                disabled_by_db_connection_ == 0) {
+            if (disabled_by_origin_.empty() && disabled_by_db_connection_ == 0) {
                 globally_disabled_ = false;
             }
         }
@@ -110,25 +83,14 @@ public:
     /// @note The dhcp service will remain disabled until all flags are cleared.
     ///
     /// @param origin The origin of the state transition.
-    void reset(const NetworkState::Origin& origin) {
-        switch (origin) {
-        case NetworkState::Origin::USER_COMMAND:
-            disabled_by_user_command_ = false;
-            break;
-        case NetworkState::Origin::HA_COMMAND:
-            disabled_by_ha_command_ = false;
-            break;
-        case NetworkState::Origin::DB_CONNECTION:
+    void reset(unsigned int origin) {
+        if (origin == NetworkState::DB_CONNECTION) {
             disabled_by_db_connection_ = 0;
-            break;
-        default:
-            isc_throw(NotImplemented, "origin value not handled when "
-                      "resetting the network state");
-            break;
+        } else {
+            disabled_by_origin_.erase(origin);
         }
         // Enable the service only if all flags have been cleared.
-        if (!disabled_by_user_command_ && !disabled_by_ha_command_ &&
-            disabled_by_db_connection_ == 0) {
+        if (disabled_by_origin_.empty() && disabled_by_db_connection_ == 0) {
             globally_disabled_ = false;
         }
     }
@@ -138,7 +100,7 @@ public:
     /// If delayed enabling DHCP service has been scheduled, it cancels it.
     ///
     /// @param origin The origin of the state transition.
-    void enableAll(const NetworkState::Origin& origin) {
+    void enableAll(unsigned int origin) {
         setDisableService(false, origin);
 
         /// @todo Enable service for all subnets and networks here.
@@ -155,25 +117,12 @@ public:
     /// @param seconds Number of seconds to elapse before the @c enableAll is
     /// called.
     /// @param origin The origin of the state transition.
-    void createTimer(const unsigned int seconds,
-                     const NetworkState::Origin& origin) {
+    void createTimer(const unsigned int seconds, unsigned int origin) {
         destroyTimer(origin);
-        std::string timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
-        switch (origin) {
-        case NetworkState::Origin::USER_COMMAND:
-            timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
-            break;
-        case NetworkState::Origin::HA_COMMAND:
-            timer_name = NETWORK_STATE_TIMER_NAME_HA_CMD;
-            break;
-        case NetworkState::Origin::DB_CONNECTION:
+        if (origin == NetworkState::DB_CONNECTION) {
             isc_throw(BadValue, "DB connection does not support delayed enable");
-            break;
-        default:
-            isc_throw(NotImplemented, "origin value not handled when creating "
-                      "a timer for delayed enable");
-            break;
         }
+        auto timer_name = getTimerName(origin);
         timer_mgr_->registerTimer(timer_name,
                                   std::bind(&NetworkStateImpl::enableAll,
                                             shared_from_this(), origin),
@@ -185,27 +134,26 @@ public:
     /// @brief Destroys a timer if present.
     ///
     /// @param origin The origin of the state transition.
-    void destroyTimer(const NetworkState::Origin& origin) {
-        std::string timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
-        switch (origin) {
-        case NetworkState::Origin::USER_COMMAND:
-            timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
-            break;
-        case NetworkState::Origin::HA_COMMAND:
-            timer_name = NETWORK_STATE_TIMER_NAME_HA_CMD;
-            break;
-        case NetworkState::Origin::DB_CONNECTION:
+    void destroyTimer(unsigned int origin) {
+        if (origin == NetworkState::DB_CONNECTION) {
             return;
-        default:
-            isc_throw(NotImplemented, "origin value not handled when creating "
-                      "a timer for delayed enable");
-            break;
         }
+        auto timer_name = getTimerName(origin);
         if (timer_mgr_->isTimerRegistered(timer_name)) {
             timer_mgr_->unregisterTimer(timer_name);
         }
     }
 
+    /// @brief Creates a unique timer name from the origin.
+    ///
+    /// @param origin The origin of the state transition.
+    /// @return Unique timer name for the origin.
+    std::string getTimerName(unsigned int origin) const {
+        std::ostringstream timer_name;
+        timer_name << "network-state-timer-" << origin;
+        return (timer_name.str());
+    }
+
     /// @brief Server type.
     NetworkState::ServerType server_type_;
 
@@ -224,13 +172,8 @@ public:
     /// destroyed before an instance of this class is destroyed.
     TimerMgrPtr timer_mgr_;
 
-    /// @brief Flag which indicates the state has been disabled by an user
-    /// command.
-    bool disabled_by_user_command_;
-
-    /// @brief Flag which indicates the state has been disabled by the HA
-    /// command.
-    bool disabled_by_ha_command_;
+    /// @brief A set of requests to disable the service by origin.
+    std::unordered_set<unsigned int> disabled_by_origin_;
 
     /// @brief Flag which indicates the state has been disabled by a DB
     /// connection loss.
@@ -242,7 +185,7 @@ NetworkState::NetworkState(const NetworkState::ServerType& server_type)
 }
 
 void
-NetworkState::disableService(const Origin& origin) {
+NetworkState::disableService(unsigned int origin) {
     if (MultiThreadingMgr::instance().getMode()) {
         std::lock_guard<std::mutex> lk(*mutex_);
         impl_->setDisableService(true, origin);
@@ -252,7 +195,7 @@ NetworkState::disableService(const Origin& origin) {
 }
 
 void
-NetworkState::enableService(const Origin& origin) {
+NetworkState::enableService(unsigned int origin) {
     if (MultiThreadingMgr::instance().getMode()) {
         std::lock_guard<std::mutex> lk(*mutex_);
         impl_->setDisableService(false, origin);
@@ -262,7 +205,7 @@ NetworkState::enableService(const Origin& origin) {
 }
 
 void
-NetworkState::reset(const NetworkState::Origin& origin) {
+NetworkState::reset(unsigned int origin) {
     if (MultiThreadingMgr::instance().getMode()) {
         std::lock_guard<std::mutex> lk(*mutex_);
         impl_->reset(origin);
@@ -272,7 +215,7 @@ NetworkState::reset(const NetworkState::Origin& origin) {
 }
 
 void
-NetworkState::enableAll(const NetworkState::Origin& origin) {
+NetworkState::enableAll(unsigned int origin) {
     if (MultiThreadingMgr::instance().getMode()) {
         std::lock_guard<std::mutex> lk(*mutex_);
         impl_->enableAll(origin);
@@ -282,8 +225,7 @@ NetworkState::enableAll(const NetworkState::Origin& origin) {
 }
 
 void
-NetworkState::delayedEnableAll(const unsigned int seconds,
-                               const NetworkState::Origin& origin) {
+NetworkState::delayedEnableAll(const unsigned int seconds, unsigned int origin) {
     if (MultiThreadingMgr::instance().getMode()) {
         std::lock_guard<std::mutex> lk(*mutex_);
         impl_->createTimer(seconds, origin);
@@ -304,8 +246,12 @@ NetworkState::isServiceEnabled() const {
 
 bool
 NetworkState::isDelayedEnableAll() const {
-    return (TimerMgr::instance()->isTimerRegistered(NETWORK_STATE_TIMER_NAME_USER_CMD) ||
-            TimerMgr::instance()->isTimerRegistered(NETWORK_STATE_TIMER_NAME_HA_CMD));
+    for (auto origin : impl_->disabled_by_origin_) {
+        if (TimerMgr::instance()->isTimerRegistered(impl_->getTimerName(origin))) {
+            return (true);
+        }
+    }
+    return (false);
 }
 
 void
index def52e682c3367204e3fadf0d319d9711cb8b622..b3d203ff084008e1cde5dc869f07ce18a68ae484 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2023 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
@@ -20,53 +20,54 @@ namespace dhcp {
 
 class NetworkStateImpl;
 
-/// @brief Holds information about DHCP service enabling status.
+/// @brief Controls the DHCP service enabling status.
 ///
-/// When the DHCP server receives a command to disable DHCP service entirely
-/// or for specific networks, this has to be recorded to allow for re-enabling
-/// DHCP service for these networks as a result of receiving a command from
-/// the administrator or when the timeout for re-enabling the service occurs.
-/// Currently there are two types of command originating either from user or
-/// HA internal mechanism.
-/// The global state can also be altered by the DB recovery mechanism which
-/// disables the service on connection loss and re-enables it after the
-/// connection is restored. Because the server supports recovery for multiple
-/// connections, this is implemented using an internal counter.
-/// Combining all the origins of the alteration of the network state, the
-/// behavior is:
-/// a) the network state is disabled if any of the originators explicitly set
-///    the disabled flag.
-/// b) the network state is restored only if all originators explicitly clear
-///    the disabled flag.
-/// In the future, it will be possible to specify "disabled" parameter for
-/// a subnet (or network) in the configuration file to indicate that this subnet
-/// should be excluded from the service. When a command is subsequently sent to
-/// temporarily disable a service for some other subnets for a specified amount
-/// of time, only these subnets should be re-enabled when the time elapses. This
-/// class fulfills this requirement by recording the subnets disabled with a command
-/// and re-enabling them when required. The subnets specified as "disabled" in
-/// the configuration file should remain disabled until explicitly enabled with a
-/// control command.
+/// Sometimes, a DHCP server must pause responding to the DHCP queries.
+/// Typical cases include a database connection loss when the server tries
+/// to reconnect and various cases related to the High Availability operation.
+/// It is also possible to explicitly turn the DHCP service on and off via the
+/// control channel. This class receives calls from different origins to
+/// disable and re-enable the DHCP service.
 ///
-/// This class also allows for disabling the DHCP service globally. In this case
-/// the server drops all received packets.
+/// The general rule is that the DHCP service must be disabled when the class
+/// receives at least one request to disable the service from any origin. The
+/// service must be re-enabled when all requestors previously disabling the
+/// service re-enabled it. This class also allows for specifying a timeout value
+/// for each request, after which the service gets re-enabled automatically. It
+/// is particularly useful in HA when there is no guarantee that the HA partner
+/// will be able to re-enable the service because it may experience an unexpected
+/// outage. In that case, the "max-period" parameter must accompany the "dhcp-disable"
+/// command to ensure that the service will eventually be re-enabled.
+
+/// The HA hook library may include several independent relationships. Each
+/// relationship is treated as a separate origin by this class. If one relationship
+/// disables the DHCP service, the service must remain disabled even when any other
+/// relationship requests enabling it. The service is re-enabled after all
+/// relationships requested re-enabling it (e.g., they all finished synchronizing
+/// the lease database).
 ///
-/// The "dhcp-disable" and "dhcp-enable" commands are used for globally disabling
-/// and enabling the DHCP service. The "dhcp-disable-scopes" and "dhcp-enable-scopes"
-/// commands are used to disable and enable DHCP service for subnets and networks.
-/// In case of the "dhcp-disable" and "dhcp-disable-scopes" commands, it is possible
-/// to specify "max-period" parameter which provides a timeout, after which the
-/// settings are reverted (service is re-enabled globally and/or for specific
-/// scopes).
+/// The HA service instances must have unique identifiers they use to specify the
+/// origin. For example, an @c HAService with the identifier of 1 should request
+/// disabling the local service like this:
 ///
-/// Disabling DHCP service with a timeout is useful to guard against issues when
-/// the controlling client dies after disabling the DHCP service on the server,
-/// e.g. failover peers may instruct each other to disable the DHCP service while
-/// database synchronization takes place. If the peer subsequently dies, the
-/// surviving server must re-enable DHCP on its own.
+/// @code
+///     NetworkState state;
+///     state.disableService(NetworkState::HA_LOCAL_COMMAND + 1);
+/// @endcode
 ///
-/// @todo This class currently supports only the case of globally disabling
-/// the DHCP service. Disabling per network/subnet will be added later.
+/// The DHCP state can also be altered by the database recovery mechanism, which
+/// disables the service on connection loss and re-enables it after the connection
+/// is restored. Unlike in HA, this is implemented using an internal counter. In
+/// this case, there is one origin for all database connections. The requests for
+/// the @c NetworkState::DB_CONNECTION are counted, and the DHCP service is
+/// re-enabled when the counter reaches 0.
+///
+/// @todo We should consider migrating the database recovery to the same mechanism
+///  we use for the HA. The reference counting works because the database connection
+/// classes ensure that for each request to disable the DHCP service, there is a
+/// corresponding request to enable the service. It prevents the situation that the
+/// service remains disabled because there were more requests to disable than to
+/// enable the service. It is hard to ensure the same consistency for the HA.
 class NetworkState {
 public:
 
@@ -81,15 +82,29 @@ public:
     /// The enumeration indicates the originator of the state transition of the
     /// network state: either user command, HA internal command or DB connection
     /// recovery mechanism.
-    enum class Origin {
-        /// @brief The network state is being altered by a user command.
-        USER_COMMAND,
-        /// @brief The network state is being altered by a HA internal command.
-        HA_COMMAND,
-        /// @brief The network state is being altered by the DB connection
-        /// recovery mechanics.
-        DB_CONNECTION
-    };
+
+    /// @brief The network state is being altered by a user command.
+    ///
+    /// Specify unique origins for different commands by adding a number to this
+    /// constant.
+    static const unsigned int USER_COMMAND = 1;
+
+    /// @brief The network state is being altered by an HA internal command.
+    ///
+    /// Specify HA service-specific origins by adding a unique local service
+    /// identifier to this constant.
+    static const unsigned int HA_LOCAL_COMMAND = 1000;
+
+    /// @brief The network state is being altered by a "dhcp-disable" or "dhcp-enable"
+    /// command sent by a HA partner.
+    ///
+    /// Specify HA service-specific origins by adding a unique remote service
+    /// identifier to this constant.
+    static const unsigned int HA_REMOTE_COMMAND = 1100;
+
+    /// @brief The network state is being altered by the DB connection
+    /// recovery mechanics.
+    static const unsigned int DB_CONNECTION = 2000;
 
     /// @brief Type of the container holding collection of subnet identifiers.
     typedef std::set<SubnetID> Subnets;
@@ -107,7 +122,7 @@ public:
     /// disabled until all flags are cleared.
     ///
     /// @param origin The origin of the state transition.
-    void disableService(const NetworkState::Origin& origin);
+    void disableService(unsigned int origin);
 
     /// @brief Enable the DHCP service state for respective transition origin.
     ///
@@ -116,7 +131,7 @@ public:
     /// disabled until all flags are cleared.
     ///
     /// @param origin The origin of the state transition.
-    void enableService(const NetworkState::Origin& origin);
+    void enableService(unsigned int origin);
 
     /// @brief Reset internal counters.
     ///
@@ -124,13 +139,13 @@ public:
     /// been reconfigured or all the connections have been restored.
     ///
     /// @param type The origin for which the state flags need to be reset.
-    void reset(const NetworkState::Origin& type);
+    void reset(unsigned int type);
 
     /// @brief Enables DHCP service globally and for scopes which have been
     /// disabled as a result of control command.
     ///
     /// @param origin The origin of the state transition.
-    void enableAll(const NetworkState::Origin& origin);
+    void enableAll(unsigned int origin);
 
     /// @brief Schedules enabling DHCP service in the future.
     ///
@@ -138,7 +153,7 @@ public:
     /// unless @c enableAll is enabled before that time.
     /// @param origin The origin of the state transition.
     void delayedEnableAll(const unsigned int seconds,
-                          const NetworkState::Origin& origin);
+                          unsigned int origin);
 
     /// @brief Checks if the DHCP service is globally enabled.
     ///
index 7edf4581ae9e1630d204032f9fc0ddf31cd8c261..8c3b8708a958b4a6de93d71bb175e0bc992bc953 100644 (file)
@@ -174,19 +174,19 @@ NetworkStateTest::disableEnableService4UsingUserCommandOriginTest() {
     NetworkState state(NetworkState::DHCPv4);
 
     // Test that enable/disable using 'user command' origin works
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test that using 'user command' origin does not use internal counter
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -197,19 +197,19 @@ NetworkStateTest::disableEnableService4UsingHACommandOriginTest() {
     NetworkState state(NetworkState::DHCPv4);
 
     // Test that enable/disable using 'HA command' origin works
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test that using 'HA command' origin does not use internal counter
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -220,23 +220,23 @@ NetworkStateTest::disableEnableService4UsingDBConnectionOriginTest() {
     NetworkState state(NetworkState::DHCPv4);
 
     // Test that enable/disable using 'DB connection' origin works
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test that using 'DB connection' origin uses internal counter
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -253,25 +253,25 @@ NetworkStateTest::disableEnableService4UsingMultipleOriginsTest() {
     NetworkState state(NetworkState::DHCPv4);
 
     // Test that a combination properly affects the state
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -282,19 +282,19 @@ NetworkStateTest::disableEnableService6UsingUserCommandOriginTest() {
     NetworkState state(NetworkState::DHCPv6);
 
     // Test that enable/disable using 'user command' origin works
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test that using 'user command' origin does not use internal counter
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -305,19 +305,19 @@ NetworkStateTest::disableEnableService6UsingHACommandOriginTest() {
     NetworkState state(NetworkState::DHCPv6);
 
     // Test that enable/disable using 'HA command' origin works
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test that using 'HA command' origin does not use internal counter
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -328,23 +328,23 @@ NetworkStateTest::disableEnableService6UsingDBConnectionOriginTest() {
     NetworkState state(NetworkState::DHCPv6);
 
     // Test that enable/disable using 'DB connection' origin works
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test that using 'DB connection' origin uses internal counter
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -361,25 +361,25 @@ NetworkStateTest::disableEnableService6UsingMultipleOriginsTest() {
     NetworkState state(NetworkState::DHCPv6);
 
     // Test that a combination properly affects the state
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -398,31 +398,31 @@ NetworkStateTest::resetUsingUserCommandOriginTest() {
     NetworkState state(NetworkState::DHCPv4);
 
     // Test User COMMAND + HA COMMAND + DB CONNECTION origins
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.reset(NetworkState::Origin::USER_COMMAND);
+    state.reset(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test User COMMAND origin only
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.reset(NetworkState::Origin::USER_COMMAND);
+    state.reset(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -441,31 +441,31 @@ NetworkStateTest::resetUsingHACommandOriginTest() {
     NetworkState state(NetworkState::DHCPv4);
 
     // Test HA COMMAND + User COMMAND + DB CONNECTION origins
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.reset(NetworkState::Origin::HA_COMMAND);
+    state.reset(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::DB_CONNECTION);
+    state.enableService(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test HA COMMAND origin only
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.reset(NetworkState::Origin::HA_COMMAND);
+    state.reset(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -484,31 +484,31 @@ NetworkStateTest::resetUsingDBConnectionOriginTest() {
     NetworkState state(NetworkState::DHCPv4);
 
     // Test DB CONNECTION + User COMMAND + HA COMMAND origins
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.reset(NetworkState::Origin::DB_CONNECTION);
+    state.reset(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::USER_COMMAND);
+    state.enableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableService(NetworkState::Origin::HA_COMMAND);
+    state.enableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
 
     // Test DB CONNECTION origin only
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.reset(NetworkState::Origin::DB_CONNECTION);
+    state.reset(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -517,17 +517,17 @@ NetworkStateTest::resetUsingDBConnectionOriginTest() {
 void
 NetworkStateTest::enableAllTest() {
     NetworkState state(NetworkState::DHCPv4);
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableAll(NetworkState::Origin::USER_COMMAND);
+    state.enableAll(NetworkState::USER_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableAll(NetworkState::Origin::HA_COMMAND);
+    state.enableAll(NetworkState::HA_LOCAL_COMMAND);
     EXPECT_TRUE(state.isServiceEnabled());
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
+    state.disableService(NetworkState::DB_CONNECTION);
     EXPECT_FALSE(state.isServiceEnabled());
-    state.enableAll(NetworkState::Origin::DB_CONNECTION);
+    state.enableAll(NetworkState::DB_CONNECTION);
     EXPECT_TRUE(state.isServiceEnabled());
 }
 
@@ -537,24 +537,24 @@ void
 NetworkStateTest::delayedEnableAllTest() {
     NetworkState state(NetworkState::DHCPv4);
     // Disable the service and then schedule enabling it in 1 second.
-    state.disableService(NetworkState::Origin::USER_COMMAND);
-    state.delayedEnableAll(1, NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
+    state.delayedEnableAll(1, NetworkState::USER_COMMAND);
     // Initially the service should be still disabled.
     EXPECT_FALSE(state.isServiceEnabled());
     // After running IO service for 2 seconds, the service should be enabled.
     runIOService(2000);
     EXPECT_TRUE(state.isServiceEnabled());
     // Disable the service and then schedule enabling it in 1 second.
-    state.disableService(NetworkState::Origin::HA_COMMAND);
-    state.delayedEnableAll(1, NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
+    state.delayedEnableAll(1, NetworkState::HA_LOCAL_COMMAND);
     // Initially the service should be still disabled.
     EXPECT_FALSE(state.isServiceEnabled());
     // After running IO service for 2 seconds, the service should be enabled.
     runIOService(2000);
     EXPECT_TRUE(state.isServiceEnabled());
     // Disable the service and then schedule enabling it in 1 second.
-    state.disableService(NetworkState::Origin::DB_CONNECTION);
-    EXPECT_THROW(state.delayedEnableAll(1, NetworkState::Origin::DB_CONNECTION), BadValue);
+    state.disableService(NetworkState::DB_CONNECTION);
+    EXPECT_THROW(state.delayedEnableAll(1, NetworkState::DB_CONNECTION), BadValue);
 }
 
 // This test verifies that explicitly enabling the service cancels the timer
@@ -563,12 +563,12 @@ void
 NetworkStateTest::earlyEnableAllTest() {
     NetworkState state(NetworkState::DHCPv4);
     // Disable the service.
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     EXPECT_FALSE(state.isServiceEnabled());
     // Schedule enabling the service in 2 seconds.
-    state.delayedEnableAll(2, NetworkState::Origin::USER_COMMAND);
+    state.delayedEnableAll(2, NetworkState::USER_COMMAND);
     // Explicitly enable the service.
-    state.enableAll(NetworkState::Origin::USER_COMMAND);
+    state.enableAll(NetworkState::USER_COMMAND);
     // The timer should be now canceled and the service should be enabled.
     EXPECT_FALSE(state.isDelayedEnableAll());
     EXPECT_TRUE(state.isServiceEnabled());
@@ -580,12 +580,12 @@ void
 NetworkStateTest::multipleDelayedEnableAllTest() {
     NetworkState state(NetworkState::DHCPv4);
     // Disable the service and then schedule enabling it in 5 second.
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     // Schedule the first timer for 5 seconds.
-    state.delayedEnableAll(5, NetworkState::Origin::USER_COMMAND);
+    state.delayedEnableAll(5, NetworkState::USER_COMMAND);
     // When calling it the second time the old timer should be destroyed and
     // the timeout should be set to 2 seconds.
-    state.delayedEnableAll(2, NetworkState::Origin::USER_COMMAND);
+    state.delayedEnableAll(2, NetworkState::USER_COMMAND);
     // Initially the service should be still disabled.
     EXPECT_FALSE(state.isServiceEnabled());
     // After running IO service for 3 seconds, the service should be enabled.
@@ -602,14 +602,14 @@ void
 NetworkStateTest::multipleDifferentOriginsDelayedEnableAllTest() {
     NetworkState state(NetworkState::DHCPv4);
     // Disable the service and then schedule enabling it in 5 second.
-    state.disableService(NetworkState::Origin::HA_COMMAND);
+    state.disableService(NetworkState::HA_LOCAL_COMMAND);
     // Disable the service and then schedule enabling it in 2 second.
-    state.disableService(NetworkState::Origin::USER_COMMAND);
+    state.disableService(NetworkState::USER_COMMAND);
     // Schedule the first timer for 5 seconds.
-    state.delayedEnableAll(5, NetworkState::Origin::HA_COMMAND);
+    state.delayedEnableAll(5, NetworkState::HA_LOCAL_COMMAND);
     // When calling it the second time the old timer should not be destroyed and
     // the new timeout should be set to 2 seconds.
-    state.delayedEnableAll(2, NetworkState::Origin::USER_COMMAND);
+    state.delayedEnableAll(2, NetworkState::USER_COMMAND);
     // Initially the service should be still disabled.
     EXPECT_FALSE(state.isServiceEnabled());
     // After running IO service for 3 seconds, the service should not be enabled.