Commit 9fe0906d authored by Francis Dupont's avatar Francis Dupont

[904-add-ability-to-force-a-cb-update-remotely] Added server-update command

parent 4fe28306
......@@ -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
......
{
"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"
]
}
......@@ -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
-------------------------------------
......
......@@ -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
......
......@@ -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
......
......@@ -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");
......
......@@ -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.
......
......@@ -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");
......
......@@ -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.
......
......@@ -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");
......
......@@ -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.
......
......@@ -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");
......
......@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment