]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3344] Use origin-id instead of origin
authorMarcin Siodelski <marcin@isc.org>
Wed, 24 Apr 2024 19:04:18 +0000 (21:04 +0200)
committerMarcin Siodelski <marcin@isc.org>
Fri, 26 Apr 2024 11:38:59 +0000 (13:38 +0200)
The HA partners now send the commands with both origin-id and origin to
provide backward compatibility between different Kea versions.

14 files changed:
ChangeLog
doc/sphinx/arm/ctrl-channel.rst
doc/sphinx/arm/hooks-ha.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_service.cc
src/hooks/dhcp/high_availability/ha_service.h
src/hooks/dhcp/high_availability/tests/command_creator_unittest.cc
src/hooks/dhcp/high_availability/tests/ha_impl_unittest.cc

index ad94b206a75effad95a73d18230bcae1d4221e7f..7585b8f7da57ae40e4dcbad85987ba01195c5561 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2225.  [func]          marcin
+       Ensure backward compatibility of High Availability
+       between Kea 2.5.8+ and earlier versions. It introduces
+       a new origin-id argument to the dhcp-enable, dhcp-disable
+       and ha-sync-complete-notify commands. It is ignored by the
+       earlier Kea versions. The origin argument is sent in addition
+       to the origin-id and has the format recognizable by the old
+       Kea versions.
+       (Gitlab #3344)
+
 2224.  [func]          andrei
        Printing the version now mentions if premium is included and
        shows the git commit hash for the premium source code repository
index b6a5cd849c7a9cdb4b934d5dfb50484edf7963af..d4d92e363bf72c7d9694654cd00b20865330ba9d 100644 (file)
@@ -721,12 +721,17 @@ 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".
 
+Introduction of ``origin-id`` deprecates the use of the ``origin`` parameter
+in the messages exchanged between the Kea HA partners. The ``origin-id`` parameter
+must not be used in messages sent by the user.
+
 ::
 
    {
        "command": "dhcp-disable",
        "arguments": {
            "max-period": 20,
+           "origin-id": 2002,
            "origin": "user"
        }
    }
@@ -750,6 +755,10 @@ administrator's commands, as it may interfere with HA operation. The
 administrator should either omit this parameter or set it to
 "user".
 
+Introduction of ``origin-id`` deprecates the use of the ``origin`` parameter
+in the messages exchanged between the Kea HA partners. The ``origin-id`` parameter
+must not be used in messages sent by the user.
+
 ::
 
    {
index 96c4bb5fe3db82bf14e828943767b4b365159e22..866599105dd684f0e4b0ef7c36fd38eca52f11b5 100644 (file)
@@ -2366,7 +2366,7 @@ responding to clients.
        "command": "ha-sync-complete-notify",
        "service": [ "dhcp4" ],
        "arguments": {
-           "origin": 2000,
+           "origin-id": 2000,
            "server-name": "server2"
        }
    }
@@ -2375,9 +2375,11 @@ The optional ``server-name`` parameter specifies a name of one of the partners b
 to the HA relationship this command pertains to. This parameter can be omitted if the
 server receiving this command has only one HA relationship in the configuration.
 
-The ``origin`` parameter is used to select the HA service for which the receiving server should
+The ``origin-id`` parameter is used to select the HA service for which the receiving server should
 enable the DHCP service when it receives this notification. This is the same origin the
 sending server used previously to disable the DHCP service before synchronization.
+The ``origin-id`` parameter deprecates the ``origin`` parameter used in some earlier
+Kea versions.
 
 It elicits the response:
 
index aecc2c6d7c498d3e80a3591492f3d6dea5ac49e1..ef4b7de88bba6ef1c378e45610d0c340d1c1ebb1 100644 (file)
@@ -559,9 +559,22 @@ ControlledDhcpv4Srv::commandDhcpDisableHandler(const std::string&,
                     }
                 }
             }
+            // 'origin-id' replaces the older parameter 'origin' since Kea 2.6.0
+            // stable release. However, the 'origin' is kept for backward compatibility
+            // with Kea versions before 2.6.0. It is common to receive both parameters
+            // because HA hook library sends both in case the partner server hasn't been
+            // upgraded to the new version. The 'origin-id' takes precedence over the
+            // 'origin'.
+            ConstElementPtr origin_id_element = args->get("origin-id");
             ConstElementPtr origin_element = args->get("origin");
-            // The 'origin' parameter is optional.
-            if (origin_element) {
+            // The 'origin-id' and 'origin' arguments are optional.
+            if (origin_id_element) {
+                if (origin_id_element->getType() == Element::integer) {
+                    type = origin_id_element->intValue();
+                } else {
+                    message << "'origin-id' argument must be a number";
+                }
+            } else if (origin_element) {
                 switch (origin_element->getType()) {
                 case Element::string:
                     origin = origin_element->stringValue();
@@ -625,9 +638,22 @@ ControlledDhcpv4Srv::commandDhcpEnableHandler(const std::string&,
             message << "arguments for the 'dhcp-enable' command must be a map";
 
         } else {
+            // 'origin-id' replaces the older parameter 'origin' since Kea 2.6.0
+            // stable release. However, the 'origin' is kept for backward compatibility
+            // with Kea versions before 2.6.0. It is common to receive both parameters
+            // because HA hook library sends both in case the partner server hasn't been
+            // upgraded to the new version. The 'origin-id' takes precedence over the
+            // 'origin'.
+            ConstElementPtr origin_id_element = args->get("origin-id");
             ConstElementPtr origin_element = args->get("origin");
-            // The 'origin' parameter is optional.
-            if (origin_element) {
+            // The 'origin-id' and 'origin' arguments are optional.
+            if (origin_id_element) {
+                if (origin_id_element->getType() == Element::integer) {
+                    type = origin_id_element->intValue();
+                } else {
+                    message << "'origin-id' argument must be a number";
+                }
+            } else if (origin_element) {
                 switch (origin_element->getType()) {
                 case Element::string:
                     origin = origin_element->stringValue();
index 04a567baf97034400a5dcd6bc4e9bd5dad969d9d..a0be312596ed823ca230110cb64ff47ce97c11a1 100644 (file)
@@ -1724,6 +1724,32 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisableBadParam) {
     EXPECT_EQ("{ \"result\": 1, \"text\": \"'max-period' must be positive "
               "integer\" }", response);
 
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"foo\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
+
     sendUnixCommand("{"
                     "    \"command\": \"dhcp-disable\","
                     "    \"arguments\": {"
@@ -1836,6 +1862,39 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisable) {
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
 
+// This test verifies if it is possible to disable DHCP service using
+// the origin-id.
+TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisableOriginId) {
+    createUnixChannelServer();
+    std::string response;
+
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": 2002,"
+                    "        \"origin\": \"user\""
+                    "    }"
+                    "}", response);
+
+    ConstElementPtr rsp;
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    int status;
+    ConstElementPtr 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());
+}
+
 // This test verifies that it is possible to disable DHCP service for a short
 // period of time, after which the service is automatically enabled.
 TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisableTemporarily) {
@@ -1871,6 +1930,33 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpDisableTemporarily) {
 TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnableBadParam) {
     createUnixChannelServer();
     std::string response;
+    ConstElementPtr rsp;
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"foo\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
 
     sendUnixCommand("{"
                     "    \"command\": \"dhcp-enable\","
@@ -1878,7 +1964,6 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnableBadParam) {
                     "        \"origin\": \"\""
                     "    }"
                     "}", response);
-    ConstElementPtr rsp;
 
     // The response should be a valid JSON.
     EXPECT_NO_THROW(rsp = Element::fromJSON(response));
@@ -1981,6 +2066,61 @@ TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnable) {
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
 
+// This test verifies if it is possible to enable DHCP service using
+// the origin-id.
+TEST_F(CtrlChannelDhcpv4SrvTest, dhcpEnableOriginId) {
+    createUnixChannelServer();
+    std::string response;
+
+    ConstElementPtr rsp;
+
+    int status;
+
+    // Disable the service using two distinct origins.
+    server_->network_state_->disableService(NetworkState::HA_REMOTE_COMMAND+1);
+    server_->network_state_->disableService(NetworkState::USER_COMMAND);
+
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    // Enable the service for the 'origin-id' of 2001. The 'origin' should
+    // be ignored.
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": 2001,"
+                    "        \"origin\": \"user\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    ConstElementPtr cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    // The service should still be disabled.
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    // Enable the service for the user command.
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": 1"
+                    "    }"
+                    "}", 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);
+
+    // The service should be enabled.
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+}
+
 /// Verify that concurrent connections over the control channel can be
 ///  established.
 /// @todo Future Kea 1.3 tickets will modify the behavior of the CommandMgr
index b8314843d8a23088707ac9b416cb165bfc3ccd5a..f9d89a1cee1c806b041a1668ff5d5d21764146ff 100644 (file)
@@ -561,9 +561,22 @@ ControlledDhcpv6Srv::commandDhcpDisableHandler(const std::string&,
                     }
                 }
             }
+            // 'origin-id' replaces the older parameter 'origin' since Kea 2.6.0
+            // stable release. However, the 'origin' is kept for backward compatibility
+            // with Kea versions before 2.6.0. It is common to receive both parameters
+            // because HA hook library sends both in case the partner server hasn't been
+            // upgraded to the new version. The 'origin-id' takes precedence over the
+            // 'origin'.
+            ConstElementPtr origin_id_element = args->get("origin-id");
             ConstElementPtr origin_element = args->get("origin");
-            // The 'origin' parameter is optional.
-            if (origin_element) {
+            // The 'origin-id' and 'origin' arguments are optional.
+            if (origin_id_element) {
+                if (origin_id_element->getType() == Element::integer) {
+                    type = origin_id_element->intValue();
+                } else {
+                    message << "'origin-id' argument must be a number";
+                }
+            } else if (origin_element) {
                 switch (origin_element->getType()) {
                 case Element::string:
                     origin = origin_element->stringValue();
@@ -627,9 +640,22 @@ ControlledDhcpv6Srv::commandDhcpEnableHandler(const std::string&,
             message << "arguments for the 'dhcp-enable' command must be a map";
 
         } else {
+            // 'origin-id' replaces the older parameter 'origin' since Kea 2.6.0
+            // stable release. However, the 'origin' is kept for backward compatibility
+            // with Kea versions before 2.6.0. It is common to receive both parameters
+            // because HA hook library sends both in case the partner server hasn't been
+            // upgraded to the new version. The 'origin-id' takes precedence over the
+            // 'origin'.
+            ConstElementPtr origin_id_element = args->get("origin-id");
             ConstElementPtr origin_element = args->get("origin");
-            // The 'origin' parameter is optional.
-            if (origin_element) {
+            // The 'origin-id' and 'origin' arguments are optional.
+            if (origin_id_element) {
+                if (origin_id_element->getType() == Element::integer) {
+                    type = origin_id_element->intValue();
+                } else {
+                    message << "'origin-id' argument must be a number";
+                }
+            } else if (origin_element) {
                 switch (origin_element->getType()) {
                 case Element::string:
                     origin = origin_element->stringValue();
index 1150b1f577d2442d530cc94bcf93dc953df0e234..8f2dc86942873e59dfc509b9d2630028679b78ac 100644 (file)
@@ -1762,6 +1762,32 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisableBadParam) {
     EXPECT_EQ("{ \"result\": 1, \"text\": \"'max-period' must be positive "
               "integer\" }", response);
 
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"foo\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
+
     sendUnixCommand("{"
                     "    \"command\": \"dhcp-disable\","
                     "    \"arguments\": {"
@@ -1874,6 +1900,39 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisable) {
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
 
+// This test verifies if it is possible to disable DHCP service using
+// the origin-id.
+TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisableOriginId) {
+    createUnixChannelServer();
+    std::string response;
+
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-disable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": 2002,"
+                    "        \"origin\": \"user\""
+                    "    }"
+                    "}", response);
+
+    ConstElementPtr rsp;
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    int status;
+    ConstElementPtr 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());
+}
+
 // This test verifies that it is possible to disable DHCP service for a short
 // period of time, after which the service is automatically enabled.
 TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisableTemporarily) {
@@ -1909,6 +1968,33 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpDisableTemporarily) {
 TEST_F(CtrlChannelDhcpv6SrvTest, dhcpEnableBadParam) {
     createUnixChannelServer();
     std::string response;
+    ConstElementPtr rsp;
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
+
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": \"foo\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    EXPECT_EQ("{ \"result\": 1, \"text\": \"'origin-id' argument must be a number\" }", response);
 
     sendUnixCommand("{"
                     "    \"command\": \"dhcp-enable\","
@@ -1916,7 +2002,6 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpEnableBadParam) {
                     "        \"origin\": \"\""
                     "    }"
                     "}", response);
-    ConstElementPtr rsp;
 
     // The response should be a valid JSON.
     EXPECT_NO_THROW(rsp = Element::fromJSON(response));
@@ -2019,6 +2104,61 @@ TEST_F(CtrlChannelDhcpv6SrvTest, dhcpEnable) {
     EXPECT_TRUE(server_->network_state_->isServiceEnabled());
 }
 
+// This test verifies if it is possible to enable DHCP service using
+// the origin-id.
+TEST_F(CtrlChannelDhcpv6SrvTest, dhcpEnableOriginId) {
+    createUnixChannelServer();
+    std::string response;
+
+    ConstElementPtr rsp;
+
+    int status;
+
+    // Disable the service using two distinct origins.
+    server_->network_state_->disableService(NetworkState::HA_REMOTE_COMMAND+1);
+    server_->network_state_->disableService(NetworkState::USER_COMMAND);
+
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    // Enable the service for the 'origin-id' of 2001. The 'origin' should
+    // be ignored.
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": 2001,"
+                    "        \"origin\": \"user\""
+                    "    }"
+                    "}", response);
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    ConstElementPtr cfg = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    // The service should still be disabled.
+    EXPECT_FALSE(server_->network_state_->isServiceEnabled());
+
+    // Enable the service for the user command.
+    sendUnixCommand("{"
+                    "    \"command\": \"dhcp-enable\","
+                    "    \"arguments\": {"
+                    "        \"origin-id\": 1"
+                    "    }"
+                    "}", 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);
+
+    // The service should be enabled.
+    EXPECT_TRUE(server_->network_state_->isServiceEnabled());
+}
+
 /// Verify that concurrent connections over the control channel can be
 ///  established.
 /// @todo Future Kea 1.3 tickets will modify the behavior of the CommandMgr
index 467a3415b0feab75fd7be8e4bd802e04f47b0a80..6f737641410a47c3e75df7ba27282a6d3f1531bd 100644 (file)
@@ -38,12 +38,14 @@ unordered_set<string> CommandCreator::ha_commands6_ = {
 };
 
 ConstElementPtr
-CommandCreator::createDHCPDisable(const unsigned int origin,
+CommandCreator::createDHCPDisable(const unsigned int origin_id,
                                   const unsigned int max_period,
                                   const HAServerType& server_type) {
     ElementPtr args;
     args = Element::createMap();
-    args->set("origin", Element::create(origin));
+    args->set("origin-id", Element::create(origin_id));
+    // Add for backward compatibility with Kea 2.4.0 and earlier.
+    args->set("origin", Element::create("ha-partner"));
     // 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)));
@@ -54,11 +56,13 @@ CommandCreator::createDHCPDisable(const unsigned int origin,
 }
 
 ConstElementPtr
-CommandCreator::createDHCPEnable(const unsigned int origin,
+CommandCreator::createDHCPEnable(const unsigned int origin_id,
                                  const HAServerType& server_type) {
     ElementPtr args;
     args = Element::createMap();
-    args->set("origin", Element::create(origin));
+    args->set("origin-id", Element::create(origin_id));
+    // Add for backward compatibility with Kea 2.4.0 and earlier.
+    args->set("origin", Element::create("ha-partner"));
     ConstElementPtr command = config::createCommand("dhcp-enable", args);
     insertService(command, server_type);
     return (command);
@@ -257,12 +261,14 @@ CommandCreator::createMaintenanceNotify(const std::string& server_name,
 }
 
 ConstElementPtr
-CommandCreator::createSyncCompleteNotify(const unsigned int origin,
+CommandCreator::createSyncCompleteNotify(const unsigned int origin_id,
                                          const std::string& server_name,
                                          const HAServerType& server_type) {
     auto args = Element::createMap();
     args->set("server-name", Element::create(server_name));
-    args->set("origin", Element::create(origin));
+    args->set("origin-id", Element::create(origin_id));
+    // Add for backward compatibility with Kea 2.4.0 and earlier.
+    args->set("origin", Element::create("ha-partner"));
     auto command = config::createCommand("ha-sync-complete-notify", args);
     insertService(command, server_type);
     return (command);
index 829e81fd15afcf3fa9ff062e52dfb85a6704f2ac..78ee81320bbcc13806f9b8e89a5b38f60c9ca8a0 100644 (file)
@@ -24,7 +24,7 @@ public:
 
     /// @brief Creates dhcp-disable command for DHCP server.
     ///
-    /// @param origin A numeric value of the origin created from the
+    /// @param origin_id 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.
@@ -32,19 +32,19 @@ public:
     ///
     /// @return Pointer to the JSON representation of the command.
     static data::ConstElementPtr
-    createDHCPDisable(const unsigned int origin,
+    createDHCPDisable(const unsigned int origin_id,
                       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
+    /// @param origin_id 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 unsigned int origin,
+    createDHCPEnable(const unsigned int origin_id,
                      const HAServerType& server_type);
 
     /// @brief Creates ha-reset command.
@@ -185,14 +185,14 @@ public:
 
     /// @brief Creates ha-sync-complete-notify command.
     ///
-    /// @param origin a numeric value of the origin created from the
+    /// @param origin_id a numeric value of the origin created from the
     /// @c HAService identifier to enable the DHCP service.
     /// @param server_name name of the server sending the command allowing
     /// for associating the command with the relationship.
     /// @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
-    createSyncCompleteNotify(const unsigned int origin,
+    createSyncCompleteNotify(const unsigned int origin_id,
                              const std::string& server_name,
                              const HAServerType& server_type);
 
index 127ceb9545c37ca0f5f5e1d785f86b3a0188811b..d8ea4ee910591ac2e14c449724c623e10b57c759 100644 (file)
@@ -859,15 +859,24 @@ HAImpl::syncCompleteNotifyHandler(hooks::CalloutHandle& callout_handle) {
     static_cast<void>(parseCommand(args, command));
 
     HAServicePtr service;
-    auto origin_value = NetworkState::HA_REMOTE_COMMAND+1;
+    auto origin_id_value = NetworkState::HA_REMOTE_COMMAND+1;
     try {
         if (args) {
+            auto origin_id = args->get("origin-id");
             auto origin = args->get("origin");
-            if (origin) {
+            // The origin-id is a new parameter replacing the origin. However, some versions
+            // of Kea may still send the origin parameter instead.
+            if (origin_id) {
+                if (origin_id->getType() != Element::integer) {
+                    isc_throw(BadValue, "'origin-id' must be an integer in the 'ha-sync-complete-notify' command");
+                }
+                origin_id_value = origin_id->intValue();
+
+            } else if (origin) {
                 if (origin->getType() != Element::integer) {
                     isc_throw(BadValue, "'origin' must be an integer in the 'ha-sync-complete-notify' command");
                 }
-                origin_value = origin->intValue();
+                origin_id_value = origin->intValue();
             }
         }
 
@@ -881,7 +890,7 @@ HAImpl::syncCompleteNotifyHandler(hooks::CalloutHandle& callout_handle) {
         return;
     }
 
-    ConstElementPtr response = service->processSyncCompleteNotify(origin_value);
+    ConstElementPtr response = service->processSyncCompleteNotify(origin_id_value);
     callout_handle.setArgument("response", response);
 }
 
index 997ae1311a3cd53d58993ca36456dc65737f2b83..ebd3e531fe485e44e51125ed0f277389290fea15 100644 (file)
@@ -3076,7 +3076,7 @@ HAService::asyncSyncCompleteNotify(HttpClient& http_client,
 }
 
 ConstElementPtr
-HAService::processSyncCompleteNotify(const unsigned int origin) {
+HAService::processSyncCompleteNotify(const unsigned int origin_id) {
     if (getCurrState() == HA_PARTNER_DOWN_ST) {
         sync_complete_notified_ = true;
         // We're in the partner-down state and the partner notified us
@@ -3091,7 +3091,7 @@ HAService::processSyncCompleteNotify(const unsigned int origin) {
     // Release the network state lock for the remote origin because we have
     // acquired the local network state lock above (partner-down state), or
     // we don't need the lock (other states).
-    network_state_->enableService(origin);
+    network_state_->enableService(origin_id);
     return (createAnswer(CONTROL_RESULT_SUCCESS,
                          "Server successfully notified about the synchronization completion."));
 }
index a6de2a622739c2b153360935112fbe987ddd6039..0b6bc3a086a6000542e1bd6d0d3f9e1cb72fbe36 100644 (file)
@@ -1121,10 +1121,10 @@ public:
     /// In this state, the server will first have to check connectivity with
     /// the partner and transition to a state in which it will send lease updates.
     ///
-    /// @param origin a numeric value of the origin created from the
+    /// @param origin_id a numeric value of the origin created from the
     /// @c HAService identifier to enable the DHCP service.
     /// @return Pointer to the response to the ha-sync-complete-notify command.
-    data::ConstElementPtr processSyncCompleteNotify(const unsigned int origin);
+    data::ConstElementPtr processSyncCompleteNotify(const unsigned int origin_id);
 
     /// @brief Start the client and(or) listener instances.
     ///
index 46d76cce4451678670656166449fbd29ab7015ce..99f935fc4beb7cd8ad764c6981894eec3872886e 100644 (file)
@@ -156,24 +156,30 @@ TEST(CommandCreatorTest, createDHCPDisable4) {
     ConstElementPtr arguments;
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-disable", "dhcp4",
                                               arguments));
-    ASSERT_EQ(2, arguments->size());
+    ASSERT_EQ(3, arguments->size());
     ConstElementPtr max_period = arguments->get("max-period");
     ASSERT_TRUE(max_period);
     ASSERT_EQ(Element::integer, max_period->getType());
     EXPECT_EQ(20, max_period->intValue());
+    ConstElementPtr origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin_id->intValue());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin->intValue());
+    ASSERT_EQ("ha-partner", origin->stringValue());
 
     // Repeat the test but this time the max-period is not specified.
     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());
+    ASSERT_EQ(2, arguments->size());
+    origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin_id->intValue());
     origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin->intValue());
+    ASSERT_EQ("ha-partner", origin->stringValue());
 }
 
 // This test verifies that the dhcp-enable command is correct.
@@ -183,10 +189,13 @@ TEST(CommandCreatorTest, createDHCPEnable4) {
                                                                HAServerType::DHCPv4);
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-enable", "dhcp4",
                                               arguments));
-    ASSERT_EQ(1, arguments->size());
+    ASSERT_EQ(2, arguments->size());
+    ConstElementPtr origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin_id->intValue());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+1, origin->intValue());
+    ASSERT_EQ("ha-partner", origin->stringValue());
 }
 
 // This test verifies that the ha-reset command sent to DHCPv4 server is correct.
@@ -302,23 +311,29 @@ TEST(CommandCreatorTest, createDHCPDisable6) {
     ConstElementPtr arguments;
     ASSERT_NO_FATAL_FAILURE(testCommandBasics(command, "dhcp-disable", "dhcp6",
                                               arguments));
-    ASSERT_EQ(2, arguments->size());
+    ASSERT_EQ(3, arguments->size());
     ConstElementPtr max_period = arguments->get("max-period");
     ASSERT_TRUE(max_period);
     ASSERT_EQ(Element::integer, max_period->getType());
     EXPECT_EQ(20, max_period->intValue());
+    ConstElementPtr origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin_id->intValue());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin->intValue());
+    ASSERT_EQ("ha-partner", origin->stringValue());
 
     // Repeat the test but this time the max-period is not specified.
     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());
+    ASSERT_EQ(2, arguments->size());
+    origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin_id->intValue());
     origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin->intValue());
+    ASSERT_EQ("ha-partner", origin->stringValue());
 }
 
 // This test verifies that the dhcp-enable command (DHCPv6 case) is
@@ -328,10 +343,13 @@ TEST(CommandCreatorTest, createDHCPEnable6) {
     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());
+    ASSERT_EQ(2, arguments->size());
+    ConstElementPtr origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin_id->intValue());
     ConstElementPtr origin = arguments->get("origin");
     ASSERT_TRUE(origin);
-    ASSERT_EQ(NetworkState::HA_REMOTE_COMMAND+2, origin->intValue());
+    ASSERT_EQ("ha-partner", origin->stringValue());
 }
 
 // This test verifies that the ha-reset command sent to DHCPv6 server is correct.
@@ -542,10 +560,10 @@ TEST(CommandCreatorTest, createSyncCompleteNotify4) {
     ASSERT_TRUE(server_name);
     ASSERT_EQ(Element::string, server_name->getType());
     EXPECT_EQ("server1", server_name->stringValue());
-    auto origin = arguments->get("origin");
-    ASSERT_TRUE(origin);
-    EXPECT_EQ(Element::integer, origin->getType());
-    EXPECT_EQ(1, origin->intValue());
+    auto origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    EXPECT_EQ(Element::integer, origin_id->getType());
+    EXPECT_EQ(1, origin_id->intValue());
 }
 
 // This test verifies that the ha-sync-complete-notify command sent to a
@@ -558,10 +576,10 @@ TEST(CommandCreatorTest, createSyncCompleteNotify6) {
     ASSERT_TRUE(server_name);
     ASSERT_EQ(Element::string, server_name->getType());
     EXPECT_EQ("server1", server_name->stringValue());
-    auto origin = arguments->get("origin");
-    ASSERT_TRUE(origin);
-    EXPECT_EQ(Element::integer, origin->getType());
-    EXPECT_EQ(1, origin->intValue());
+    auto origin_id = arguments->get("origin-id");
+    ASSERT_TRUE(origin_id);
+    EXPECT_EQ(Element::integer, origin_id->getType());
+    EXPECT_EQ(1, origin_id->intValue());
 }
 
 }
index 302e1d2c873b3afdfb320be258a7a7229eaed427..1508b5cc72067c28373379b9e3538629a12cc82f 100644 (file)
@@ -2304,9 +2304,10 @@ TEST_F(HAImplTest, haHeartbeatBadServerName) {
     checkAnswer(response, CONTROL_RESULT_ERROR, "server5 matches no configured 'server-name'");
 }
 
-// Test ha-sync-complete-notify command handler with a specified server name.
-TEST_F(HAImplTest, haSyncCompleteNotify) {
-    ha_impl_.reset(new HAImpl());
+// Test ha-sync-complete-notify command handler with a specified server name
+// and origin-id.
+TEST_F(HAImplTest, haSyncCompleteNotifyOriginId) {
+    ha_impl_.reset(new TestHAImpl());
     ha_impl_->setIOService(io_service_);
     ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
 
@@ -2319,7 +2320,8 @@ TEST_F(HAImplTest, haSyncCompleteNotify) {
         "{"
         "    \"command\": \"ha-sync-complete-notify\","
         "    \"arguments\": {"
-        "        \"server-name\": \"server1\""
+        "        \"server-name\": \"server1\","
+        "        \"origin-id\": 2002"
         "    }"
         "}"
     );
@@ -2327,6 +2329,10 @@ TEST_F(HAImplTest, haSyncCompleteNotify) {
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
     callout_handle->setArgument("command", command);
 
+    // Disable the DHCP service for origin of 2002. Running the command
+    // handler should unlock it.
+    network_state->disableService(NetworkState::HA_REMOTE_COMMAND + 2);
+
     ASSERT_NO_THROW(ha_impl_->syncCompleteNotifyHandler(*callout_handle));
 
     ConstElementPtr response;
@@ -2335,6 +2341,57 @@ TEST_F(HAImplTest, haSyncCompleteNotify) {
 
     checkAnswer(response, CONTROL_RESULT_SUCCESS,
                 "Server successfully notified about the synchronization completion.");
+
+    // The command handler should keep the service disabled under the HA_LOCAL_COMMAND.
+    // Let's try to enable it and see if it unlocks the DHCP service.
+    EXPECT_FALSE(network_state->isServiceEnabled());
+    network_state->enableService(NetworkState::HA_LOCAL_COMMAND);
+    EXPECT_TRUE(network_state->isServiceEnabled());
+}
+
+// Test ha-sync-complete-notify command handler with a specified server name
+// and origin.
+TEST_F(HAImplTest, haSyncCompleteNotifyOrigin) {
+    ha_impl_.reset(new TestHAImpl());
+    ha_impl_->setIOService(io_service_);
+    ASSERT_NO_THROW(ha_impl_->configure(createValidJsonConfiguration()));
+
+    // Starting the service is required prior to running any callouts.
+    NetworkStatePtr network_state(new NetworkState(NetworkState::DHCPv4));
+    ASSERT_NO_THROW(ha_impl_->startServices(network_state,
+                                            HAServerType::DHCPv4));
+
+    ConstElementPtr command = Element::fromJSON(
+        "{"
+        "    \"command\": \"ha-sync-complete-notify\","
+        "    \"arguments\": {"
+        "        \"server-name\": \"server1\","
+        "        \"origin\": 2002"
+        "    }"
+        "}"
+    );
+
+    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+    callout_handle->setArgument("command", command);
+
+    // Disable the DHCP service for origin of 2002. Running the command
+    // handler should unlock it.
+    network_state->disableService(NetworkState::HA_REMOTE_COMMAND + 2);
+
+    ASSERT_NO_THROW(ha_impl_->syncCompleteNotifyHandler(*callout_handle));
+
+    ConstElementPtr response;
+    callout_handle->getArgument("response", response);
+    ASSERT_TRUE(response);
+
+    checkAnswer(response, CONTROL_RESULT_SUCCESS,
+                "Server successfully notified about the synchronization completion.");
+
+    // The command handler should keep the service disabled under the HA_LOCAL_COMMAND.
+    // Let's try to enable it and see if it unlocks the DHCP service.
+    EXPECT_FALSE(network_state->isServiceEnabled());
+    network_state->enableService(NetworkState::HA_LOCAL_COMMAND);
+    EXPECT_TRUE(network_state->isServiceEnabled());
 }
 
 // Test ha-sync-complete-notify command handler without a specified server name.