]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[904-add-ability-to-force-a-cb-update-remotely] Added server-update command
authorFrancis Dupont <fdupont@isc.org>
Sun, 6 Oct 2019 23:51:06 +0000 (01:51 +0200)
committerFrancis Dupont <fdupont@isc.org>
Mon, 28 Oct 2019 09:33:38 +0000 (10:33 +0100)
13 files changed:
doc/sphinx/api-files.txt
doc/sphinx/api/server-update.json [new file with mode: 0644]
doc/sphinx/arm/config-backend.rst
doc/sphinx/arm/ctrl-channel.rst
doc/sphinx/arm/dhcp4-srv.rst
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/ctrl_dhcp4_srv.h
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/kea_controller_unittest.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/ctrl_dhcp6_srv.h
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/bin/dhcp6/tests/kea_controller_unittest.cc

index 248bea740321240fa117a5915214769822819677..51346dd3e77445bba08639c585c9ca80bb43a3fd 100644 (file)
@@ -129,6 +129,7 @@ api/reservation-get-all.json
 api/reservation-get-by-hostname.json
 api/reservation-get-page.json
 api/server-tag-get.json
+api/server-update.json
 api/shutdown.json
 api/statistic-get-all.json
 api/statistic-get.json
diff --git a/doc/sphinx/api/server-update.json b/doc/sphinx/api/server-update.json
new file mode 100644 (file)
index 0000000..a0eeb42
--- /dev/null
@@ -0,0 +1,30 @@
+{
+    "avail": "1.7.1",
+    "brief": [
+        "This command forces an immediate update of the server using Config Backends.",
+        "This command does not take any parameters."
+    ],
+    "cmd-syntax": [
+        "{",
+        "    \"command\": \"server-update\"",
+        "}"
+    ],
+    "description": "See <xref linkend=\"command-server-update\"/>",
+    "name": "server-update",
+    "resp-comment": [
+        "When no Config Backends are configured this command returns empty (3); ",
+        "If an error occurs error (1) is returned with the error details; ",
+        "otherwise success (0) is returned."
+    ],
+    "resp-syntax": [
+        "{",
+        "    \"result\": 0,",
+        "    \"text\": \"Server update successful.\"",
+        "}"
+    ],
+
+    "support": [
+        "kea-dhcp4",
+        "kea-dhcp6"
+    ]
+}
index f88c1074874ac7293b17f4c1127ceae9e3d31814..71e5bd7acbf5c63e037dc244f1413d6ee9fcdf75 100644 (file)
@@ -159,8 +159,6 @@ the list of supported configuration parameters, can be found in
 
 .. _cb-sharing:
 
-.. _command-server-tag-get:
-
 Configuration Sharing and Server Tags
 -------------------------------------
 
index 3650964bebbb8a43ffeeb553c9bf051129301e4f..3b839f51f5fe1c1660e0a852c0a085fe76133d65 100644 (file)
@@ -538,6 +538,23 @@ The ``dhcp-enable`` command globally enables the DHCP service.
        "command": "dhcp-enable"
    }
 
+.. _command-server-tag-get:
+
+The server-tag-get Command:
+---------------------------
+
+The ``server-tag-get`` command returns the configured server tag of
+the DHCPv4 or DHCPv6 server (:ref:`cb-sharing` explains the server tag concept)
+
+.. _command-server-update:
+
+The server-update Command:
+--------------------------
+
+The ``server-update`` command triggers the polling of Config Backends
+(which should be configured for this command to do something)
+explained in :ref:`dhcp4-cb-json`.
+
 .. _command-version-get:
 
 The version-get Command
index 3e6d8647df3ea666e60cfc2b56780a1ae3f6ca6e..cb7613271b007360ffc4a0dccab2e66a61b9b60b 100644 (file)
@@ -5891,6 +5891,10 @@ server's performance, because the server needs to make at least one query
 to the database to discover the pending configuration updates. The
 default value of the ``config-fetch-wait-time`` is 30 seconds.
 
+To trigger an immediate polling the ``server-update`` command is
+available for both DHCPv4 and DHCPv6 servers since the 1.7.1 Kea
+release.
+
 Finally, in the configuration example above, two hooks libraries are
 loaded. The first, ``libdhcp_mysql_cb.so``, is the implementation of
 the Configuration Backend for MySQL. It must be always present when the
index 4c8d33ab808e6ad6dc473d607447b35335d633be..5c24fe64d8ae413ba20979e05daa9b0dc024b979 100644 (file)
@@ -544,6 +544,34 @@ ControlledDhcpv4Srv::commandServerTagGetHandler(const std::string&,
     return (createAnswer(CONTROL_RESULT_SUCCESS, response));
 }
 
+ConstElementPtr
+ControlledDhcpv4Srv::commandServerUpdateHandler(const std::string&,
+                                                      ConstElementPtr) {
+    auto ctl_info = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo();
+    if (!ctl_info) {
+        return (createAnswer(CONTROL_RESULT_EMPTY, "No config backend."));
+    }
+
+    // Reschedule the periodic CB fetch.
+    if (TimerMgr::instance()->isTimerRegistered("Dhcp4CBFetchTimer")) {
+        TimerMgr::instance()->cancel("Dhcp4CBFetchTimer");
+        TimerMgr::instance()->setup("Dhcp4CBFetchTimer");
+    }
+
+    // Code from cbFetchUpdates.
+    try {
+        auto srv_cfg = CfgMgr::instance().getStagingCfg();
+        auto mode = CBControlDHCPv4::FetchMode::FETCH_UPDATE;
+        server_->getCBControl()->databaseConfigFetch(srv_cfg, mode);
+    } catch (const std::exception& ex) {
+        LOG_ERROR(dhcp4_logger, DHCP4_CB_FETCH_UPDATES_FAIL)
+            .arg(ex.what());
+        return (createAnswer(CONTROL_RESULT_ERROR,
+                             "Server update failed: " + string(ex.what())));
+    }
+    return (createAnswer(CONTROL_RESULT_SUCCESS, "Server update successful."));
+}
+
 ConstElementPtr
 ControlledDhcpv4Srv::processCommand(const string& command,
                                     ConstElementPtr args) {
@@ -601,6 +629,9 @@ ControlledDhcpv4Srv::processCommand(const string& command,
         } else if (command == "server-tag-get") {
             return (srv->commandServerTagGetHandler(command, args));
 
+        } else if (command == "server-update") {
+            return (srv->commandServerUpdateHandler(command, args));
+
         }
         ConstElementPtr answer = isc::config::createAnswer(1,
                                  "Unrecognized command:" + command);
@@ -838,6 +869,9 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_P
     CommandMgr::instance().registerCommand("server-tag-get",
         boost::bind(&ControlledDhcpv4Srv::commandServerTagGetHandler, this, _1, _2));
 
+    CommandMgr::instance().registerCommand("server-update",
+        boost::bind(&ControlledDhcpv4Srv::commandServerUpdateHandler, this, _1, _2));
+
     CommandMgr::instance().registerCommand("shutdown",
         boost::bind(&ControlledDhcpv4Srv::commandShutdownHandler, this, _1, _2));
 
@@ -898,14 +932,15 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
         CommandMgr::instance().deregisterCommand("build-report");
         CommandMgr::instance().deregisterCommand("config-get");
         CommandMgr::instance().deregisterCommand("config-reload");
+        CommandMgr::instance().deregisterCommand("config-set");
         CommandMgr::instance().deregisterCommand("config-test");
         CommandMgr::instance().deregisterCommand("config-write");
-        CommandMgr::instance().deregisterCommand("leases-reclaim");
-        CommandMgr::instance().deregisterCommand("libreload");
-        CommandMgr::instance().deregisterCommand("config-set");
         CommandMgr::instance().deregisterCommand("dhcp-disable");
         CommandMgr::instance().deregisterCommand("dhcp-enable");
+        CommandMgr::instance().deregisterCommand("leases-reclaim");
+        CommandMgr::instance().deregisterCommand("libreload");
         CommandMgr::instance().deregisterCommand("server-tag-get");
+        CommandMgr::instance().deregisterCommand("server-update");
         CommandMgr::instance().deregisterCommand("shutdown");
         CommandMgr::instance().deregisterCommand("statistic-get");
         CommandMgr::instance().deregisterCommand("statistic-get-all");
index ae8095c2a7d23b8ee7dc70498a9db56fb4314619..39a2feed059ade16e95e1973e7931cb4eb368533 100644 (file)
@@ -304,6 +304,19 @@ private:
     commandServerTagGetHandler(const std::string& command,
                                isc::data::ConstElementPtr args);
 
+    /// @brief handler for server-update command
+    ///
+    /// This method handles the server-update command, which updates
+    /// the server configuration from the Config Backends immediately.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args (ignored)
+    ///
+    /// @return status of the command/
+    isc::data::ConstElementPtr
+    commandServerUpdateHandler(const std::string& command,
+                               isc::data::ConstElementPtr args);
+
     /// @brief Reclaims expired IPv4 leases and reschedules timer.
     ///
     /// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases4.
index 7aa0f28a3f1eed3c5637c990eebb68bdf900b728..1452017f948a685acb9b3ccca7ee24616b82f232 100644 (file)
@@ -485,6 +485,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) {
     EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"server-tag-get\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"server-update\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos);
@@ -1025,6 +1026,19 @@ TEST_F(CtrlChannelDhcpv4SrvTest, serverTagGet) {
     expected = "{ \"arguments\": { \"server-tag\": \"foobar\" }, \"result\": 0 }";
 }
 
+// This test verifies that the DHCP server handles server-update command
+TEST_F(CtrlChannelDhcpv4SrvTest, serverUpdate) {
+    createUnixChannelServer();
+
+    std::string response;
+    std::string expected;
+
+    // Send the server-update command. Note there is no configured backed.
+    sendUnixCommand("{ \"command\": \"server-update\" }", response);
+    expected = "{ \"result\": 3, \"text\": \"No config backend.\" }";
+    EXPECT_EQ(expected, response);
+}
+
 // This test verifies that the DHCP server immediately reclaims expired
 // leases on leases-reclaim command
 TEST_F(CtrlChannelDhcpv4SrvTest, controlLeasesReclaim) {
@@ -1145,6 +1159,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) {
     checkListCommands(rsp, "libreload");
     checkListCommands(rsp, "version-get");
     checkListCommands(rsp, "server-tag-get");
+    checkListCommands(rsp, "server-update");
     checkListCommands(rsp, "shutdown");
     checkListCommands(rsp, "statistic-get");
     checkListCommands(rsp, "statistic-get-all");
index 724af352128465104d4b8bad60f5ba77e391e88d..bbd0eaf23a56e4cacf2c30214305cc6f612d29a4 100644 (file)
@@ -217,7 +217,8 @@ public:
     /// @brief This test verifies that the timer used to fetch the configuration
     /// updates from the database works as expected.
     void testConfigBackendTimer(const int config_wait_fetch_time,
-                                const bool throw_during_fetch = false) {
+                                const bool throw_during_fetch = false,
+                                const bool call_command = false) {
         std::ostringstream config;
         config <<
             "{ \"Dhcp4\": {"
@@ -261,7 +262,40 @@ public:
         EXPECT_EQ(1, cb_control->getDatabaseConfigFetchCalls());
 
 
-        if ((config_wait_fetch_time > 0) && (!throw_during_fetch)) {
+        if (call_command) {
+            // The case where there is no backend is tested in the
+            // controlled server tests so we have only to verify
+            // that the command calls the database config fetch.
+
+            // Count the startup.
+            EXPECT_EQ(cb_control->getDatabaseConfigFetchCalls(), 1);
+
+            ConstElementPtr result =
+                ControlledDhcpv4Srv::processCommand("server-update",
+                                                    ConstElementPtr());
+            EXPECT_EQ(cb_control->getDatabaseConfigFetchCalls(), 2);
+            std::string expected;
+
+            if (throw_during_fetch) {
+                expected = "{ \"result\": 1, \"text\": ";
+                expected += "\"Server update failed: ";
+                expected += "testing if exceptions are corectly handled\" }";
+            } else {
+                expected = "{ \"result\": 0, \"text\": ";
+                expected += "\"Server update successful.\" }";
+            }
+            EXPECT_EQ(expected, result->str());
+
+            // No good way to check the rescheduling...
+            ASSERT_NO_THROW(runTimersWithTimeout(srv->getIOService(), 15));
+
+            if (config_wait_fetch_time > 0) {
+                EXPECT_GE(cb_control->getDatabaseConfigFetchCalls(), 12);
+            } else {
+                EXPECT_EQ(cb_control->getDatabaseConfigFetchCalls(), 2);
+            }
+
+        } else if ((config_wait_fetch_time > 0) && (!throw_during_fetch)) {
             // If we're configured to run the timer, we expect that it was
             // invoked at least 3 times. This is sufficient to verify that
             // the timer was scheduled and that the timer continued to run
@@ -857,6 +891,24 @@ TEST_F(JSONFileBackendTest, configBackendTimerWithThrow) {
     testConfigBackendTimer(1, true);
 }
 
+// This test verifies that the server will be updated by the server-update
+// command.
+TEST_F(JSONFileBackendTest, configBackendCommand) {
+    testConfigBackendTimer(0, false, true);
+}
+
+// This test verifies that the server will be updated by the server-update
+// command even when updates fail.
+TEST_F(JSONFileBackendTest, configBackendCommandWithThrow) {
+    testConfigBackendTimer(0, true, true);
+}
+
+// This test verifies that the server will be updated by the server-update
+// command and the timer rescheduled.
+TEST_F(JSONFileBackendTest, configBackendCommandWithTimer) {
+    testConfigBackendTimer(1, false, true);
+}
+
 // Starting tests which require MySQL backend availability. Those tests
 // will not be executed if Kea has been compiled without the
 // --with-mysql.
index 755619e1581801b0119dbc93c2e2aa7aa9d8c29b..c84e71ba6d667ee5444660461059dee641d251fd 100644 (file)
@@ -544,6 +544,34 @@ ControlledDhcpv6Srv::commandServerTagGetHandler(const std::string&,
     return (createAnswer(CONTROL_RESULT_SUCCESS, response));
 }
 
+ConstElementPtr
+ControlledDhcpv6Srv::commandServerUpdateHandler(const std::string&,
+                                                      ConstElementPtr) {
+    auto ctl_info = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo();
+    if (!ctl_info) {
+        return (createAnswer(CONTROL_RESULT_EMPTY, "No config backend."));
+    }
+
+    // Reschedule the periodic CB fetch.
+    if (TimerMgr::instance()->isTimerRegistered("Dhcp6CBFetchTimer")) {
+        TimerMgr::instance()->cancel("Dhcp6CBFetchTimer");
+        TimerMgr::instance()->setup("Dhcp6CBFetchTimer");
+    }
+
+    // Code from cbFetchUpdates.
+    try {
+        auto srv_cfg = CfgMgr::instance().getStagingCfg();
+        auto mode = CBControlDHCPv6::FetchMode::FETCH_UPDATE;
+        server_->getCBControl()->databaseConfigFetch(srv_cfg, mode);
+    } catch (const std::exception& ex) {
+        LOG_ERROR(dhcp6_logger, DHCP6_CB_FETCH_UPDATES_FAIL)
+            .arg(ex.what());
+        return (createAnswer(CONTROL_RESULT_ERROR,
+                             "Server update failed: " + string(ex.what())));
+    }
+    return (createAnswer(CONTROL_RESULT_SUCCESS, "Server update successful."));
+}
+
 isc::data::ConstElementPtr
 ControlledDhcpv6Srv::processCommand(const std::string& command,
                                     isc::data::ConstElementPtr args) {
@@ -601,6 +629,9 @@ ControlledDhcpv6Srv::processCommand(const std::string& command,
         } else if (command == "server-tag-get") {
             return (srv->commandServerTagGetHandler(command, args));
 
+        } else if (command == "server-update") {
+            return (srv->commandServerUpdateHandler(command, args));
+
         }
 
         return (isc::config::createAnswer(1, "Unrecognized command:"
@@ -851,6 +882,9 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
     CommandMgr::instance().registerCommand("server-tag-get",
         boost::bind(&ControlledDhcpv6Srv::commandServerTagGetHandler, this, _1, _2));
 
+    CommandMgr::instance().registerCommand("server-update",
+        boost::bind(&ControlledDhcpv6Srv::commandServerUpdateHandler, this, _1, _2));
+
     CommandMgr::instance().registerCommand("libreload",
         boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2));
 
@@ -925,6 +959,7 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
         CommandMgr::instance().deregisterCommand("leases-reclaim");
         CommandMgr::instance().deregisterCommand("libreload");
         CommandMgr::instance().deregisterCommand("server-tag-get");
+        CommandMgr::instance().deregisterCommand("server-update");
         CommandMgr::instance().deregisterCommand("shutdown");
         CommandMgr::instance().deregisterCommand("statistic-get");
         CommandMgr::instance().deregisterCommand("statistic-get-all");
index 83a2d29ab42fe08b9cefb335d8b27abe1bb4a697..0be001c30e384cc2e817c74480dfde3ffe6a0093 100644 (file)
@@ -303,6 +303,19 @@ private:
     commandServerTagGetHandler(const std::string& command,
                                isc::data::ConstElementPtr args);
 
+    /// @brief handler for server-update command
+    ///
+    /// This method handles the server-update command, which updates
+    /// the server configuration from the Config Backends immediately.
+    ///
+    /// @param command (parameter ignored)
+    /// @param args (ignored)
+    ///
+    /// @return status of the command/
+    isc::data::ConstElementPtr
+    commandServerUpdateHandler(const std::string& command,
+                               isc::data::ConstElementPtr args);
+
     /// @brief Reclaims expired IPv6 leases and reschedules timer.
     ///
     /// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases6.
index 41de6ff27c4a0e0f0729a0cf40a822b284db8492..06fcbba27a817bad3f9d00e9794c98253d7ebc93 100644 (file)
@@ -504,6 +504,7 @@ TEST_F(CtrlDhcpv6SrvTest, commandsRegistration) {
     EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"server-tag-get\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"server-update\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"shutdown\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"statistic-get\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"statistic-get-all\"") != string::npos);
@@ -937,6 +938,19 @@ TEST_F(CtrlChannelDhcpv6SrvTest, serverTagGet) {
     expected = "{ \"arguments\": { \"server-tag\": \"foobar\" }, \"result\": 0 }";
 }
 
+// This test verifies that the DHCP server handles server-update command
+TEST_F(CtrlChannelDhcpv6SrvTest, serverUpdate) {
+    createUnixChannelServer();
+
+    std::string response;
+    std::string expected;
+
+    // Send the server-update command. Note there is no configured backed.
+    sendUnixCommand("{ \"command\": \"server-update\" }", response);
+    expected = "{ \"result\": 3, \"text\": \"No config backend.\" }";
+    EXPECT_EQ(expected, response);
+}
+
 // This test verifies that the DHCP server immediately reclaims expired
 // leases on leases-reclaim command
 TEST_F(CtrlChannelDhcpv6SrvTest, controlLeasesReclaim) {
@@ -1176,6 +1190,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, listCommands) {
     checkListCommands(rsp, "libreload");
     checkListCommands(rsp, "version-get");
     checkListCommands(rsp, "server-tag-get");
+    checkListCommands(rsp, "server-update");
     checkListCommands(rsp, "shutdown");
     checkListCommands(rsp, "statistic-get");
     checkListCommands(rsp, "statistic-get-all");
index b9d67494606bd723e9560c2932472bdfbabfe093..6eebbe68abdd61bb34ccf31839a2d35195da0289 100644 (file)
@@ -203,7 +203,8 @@ public:
     /// @brief This test verifies that the timer used to fetch the configuration
     /// updates from the database works as expected.
     void testConfigBackendTimer(const int config_wait_fetch_time,
-                                const bool throw_during_fetch = false) {
+                                const bool throw_during_fetch = false,
+                                const bool call_command = false) {
         std::ostringstream config;
         config <<
             "{ \"Dhcp6\": {"
@@ -247,7 +248,40 @@ public:
         EXPECT_EQ(1, cb_control->getDatabaseConfigFetchCalls());
 
 
-        if ((config_wait_fetch_time > 0) && (!throw_during_fetch)) {
+        if (call_command) {
+            // The case where there is no backend is tested in the
+            // controlled server tests so we have only to verify
+            // that the command calls the database config fetch.
+
+            // Count the startup.
+            EXPECT_EQ(cb_control->getDatabaseConfigFetchCalls(), 1);
+
+            ConstElementPtr result =
+                ControlledDhcpv6Srv::processCommand("server-update",
+                                                    ConstElementPtr());
+            EXPECT_EQ(cb_control->getDatabaseConfigFetchCalls(), 2);
+            std::string expected;
+
+            if (throw_during_fetch) {
+                expected = "{ \"result\": 1, \"text\": ";
+                expected += "\"Server update failed: ";
+                expected += "testing if exceptions are corectly handled\" }";
+            } else {
+                expected = "{ \"result\": 0, \"text\": ";
+                expected += "\"Server update successful.\" }";
+            }
+            EXPECT_EQ(expected, result->str());
+
+            // No good way to check the rescheduling...
+            ASSERT_NO_THROW(runTimersWithTimeout(srv->getIOService(), 15));
+
+            if (config_wait_fetch_time > 0) {
+                EXPECT_GE(cb_control->getDatabaseConfigFetchCalls(), 12);
+            } else {
+                EXPECT_EQ(cb_control->getDatabaseConfigFetchCalls(), 2);
+            }
+
+        } else if ((config_wait_fetch_time > 0) && (!throw_during_fetch)) {
             // If we're configured to run the timer, we expect that it was
             // invoked at least 3 times. This is sufficient to verify that
             // the timer was scheduled and that the timer continued to run
@@ -844,6 +878,24 @@ TEST_F(JSONFileBackendTest, configBackendTimerWithThrow) {
     testConfigBackendTimer(1, true);
 }
 
+// This test verifies that the server will be updated by the server-update
+// command.
+TEST_F(JSONFileBackendTest, configBackendCommand) {
+    testConfigBackendTimer(0, false, true);
+}
+
+// This test verifies that the server will be updated by the server-update
+// command even when updates fail.
+TEST_F(JSONFileBackendTest, configBackendCommandWithThrow) {
+    testConfigBackendTimer(0, true, true);
+}
+
+// This test verifies that the server will be updated by the server-update
+// command and the timer rescheduled.
+TEST_F(JSONFileBackendTest, configBackendCommandWithTimer) {
+    testConfigBackendTimer(1, false, true);
+}
+
 // Starting tests which require MySQL backend availability. Those tests
 // will not be executed if Kea has been compiled without the
 // --with-mysql.