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
--- /dev/null
+{
+ "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"
+ ]
+}
.. _cb-sharing:
-.. _command-server-tag-get:
-
Configuration Sharing and Server Tags
-------------------------------------
"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
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
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) {
} 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);
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));
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");
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.
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);
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) {
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");
/// @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\": {"
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
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.
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) {
} 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:"
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));
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");
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.
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);
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) {
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");
/// @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\": {"
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
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.