From: Thomas Markwalder Date: Fri, 9 Dec 2016 21:08:24 +0000 (-0500) Subject: [5046] Implement set-config command in kea-dhcp4 X-Git-Tag: trac5030_base~1^2~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=44d38ce10ac4f0ed22fcf329a9ddd6a1f2d0099c;p=thirdparty%2Fkea.git [5046] Implement set-config command in kea-dhcp4 Mirror the changes made in kea-dhcp6. --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 4cd019a315..3ac909d914 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -65,6 +65,51 @@ ControlledDhcpv4Srv::commandConfigReloadHandler(const string&, return (processConfig(args)); } +ConstElementPtr +ControlledDhcpv4Srv::commandSetConfigHandler(const string&, + ConstElementPtr args) { + const int status_code = 1; // 1 indicates an error + ConstElementPtr dhcp4; + string message; + + // We are starting the configuration process so we should remove any + // staging configuration that has been created during previous + // configuration attempts. + CfgMgr::instance().rollback(); + + // Command arguments are expected to be: + // { "Dhcp4": { ... }, "Logging": { ... } } + // The Logging component is technically optional, but very recommended. + if (!args) { + message = "Missing mandatory 'arguments' parameter."; + } else { + dhcp4 = args->get("Dhcp4"); + if (!dhcp4) { + message = "Missing mandatory 'Dhcp4' parameter."; + } else if (dhcp4->getType() != Element::map) { + message = "'Dhcp4' parameter expected to be a map."; + } + } + + if (!message.empty()) { + // Something is amiss with arguments, return a failure response. + ConstElementPtr result = isc::config::createAnswer(status_code, + message); + return (result); + } + + // Logging is a sibling element and must be be parsed explicitly. + // The call to configureLogger parses the given Logging element if + // not null, into the staging config. Note this DOES alter the + // current loggers, they remain in effect until we apply the + // logging config. + Daemon::configureLogger(args->get("Logging"), + CfgMgr::instance().getStagingCfg()); + + // Now we configure the server proper. + return (processConfig(dhcp4)); +} + ConstElementPtr ControlledDhcpv4Srv::commandLeasesReclaimHandler(const string&, ConstElementPtr args) { @@ -116,6 +161,9 @@ ControlledDhcpv4Srv::processCommand(const string& command, } else if (command == "config-reload") { return (srv->commandConfigReloadHandler(command, args)); + } else if (command == "set-config") { + return (srv->commandSetConfigHandler(command, args)); + } else if (command == "leases-reclaim") { return (srv->commandLeasesReclaimHandler(command, args)); } @@ -235,7 +283,13 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { } } + // Configuration was parsed successfully, apply the new logger + // configuration to log4cplus. It is done before commit in case + // something goes wrong. + CfgMgr::instance().getStagingCfg()->applyLoggingCfg(); + // Use new configuration. + CfgMgr::instance().commit(); return (answer); } @@ -253,6 +307,10 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) boost::bind(&ControlledDhcpv4Srv::commandShutdownHandler, this, _1, _2)); /// @todo: register config-reload (see CtrlDhcpv4Srv::commandConfigReloadHandler) + + CommandMgr::instance().registerCommand("set-config", + boost::bind(&ControlledDhcpv4Srv::commandSetConfigHandler, this, _1, _2)); + /// @todo: register libreload (see CtrlDhcpv4Srv::commandLibReloadHandler) CommandMgr::instance().registerCommand("leases-reclaim", @@ -297,6 +355,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { // Deregister any registered commands CommandMgr::instance().deregisterCommand("shutdown"); + CommandMgr::instance().deregisterCommand("set-config"); CommandMgr::instance().deregisterCommand("leases-reclaim"); CommandMgr::instance().deregisterCommand("statistic-get"); CommandMgr::instance().deregisterCommand("statistic-reset"); diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 57883e5fd8..6ef362cd41 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -143,6 +143,19 @@ private: commandConfigReloadHandler(const std::string& command, isc::data::ConstElementPtr args); + /// @brief handler for processing 'set-config' command + /// + /// This handler processes set-config command, which processes + /// configuration specified in args parameter. + /// @param command (parameter ignored) + /// @param args configuration to be processed. Expected format: + /// map containing Dhcp4 map that contains DHCPv4 server configuration. + /// May also contain Logging map that specifies logging configuration. + /// + /// @return status of the command + isc::data::ConstElementPtr + commandSetConfigHandler(const std::string& command, + isc::data::ConstElementPtr args); /// @brief Handler for processing 'leases-reclaim' command /// diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 52b554972b..4b50286056 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -501,6 +501,47 @@ void setGlobalParameters4() { } } +/// @brief Initialize the command channel based on the staging configuration +/// +/// Only close the current channel, if the new channel configuration is +/// different. This avoids disconnecting a client and hence not sending them +/// a command result, unless they specifically alter the channel configuration. +/// In that case the user simply has to accept they'll be disconnected. +/// +void configureCommandChannel() { + // Get new socket configuration. + ConstElementPtr sock_cfg = + CfgMgr::instance().getStagingCfg()->getControlSocketInfo(); + + // Get current socket configuration. + ConstElementPtr current_sock_cfg = + CfgMgr::instance().getCurrentCfg()->getControlSocketInfo(); + + // Determine if the socket configuration has changed. It has if + // both old and new configuration is specified but respective + // data elements are't equal. + bool sock_changed = (sock_cfg && current_sock_cfg && + !sock_cfg->equals(*current_sock_cfg)); + + // If the previous or new socket configuration doesn't exist or + // the new configuration differs from the old configuration we + // close the exisitng socket and open a new socket as appropriate. + // Note that closing an existing socket means the clien will not + // receive the configuration result. + if (!sock_cfg || !current_sock_cfg || sock_changed) { + // Close the existing socket (if any). + isc::config::CommandMgr::instance().closeCommandSocket(); + + if (sock_cfg) { + // This will create a control socket and install the external + // socket in IfaceMgr. That socket will be monitored when + // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and + // callback in CommandMgr will be called, if necessary. + isc::config::CommandMgr::instance().openCommandSocket(sock_cfg); + } + } +} + isc::data::ConstElementPtr configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) { if (!config_set) { @@ -632,25 +673,8 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) { subnet_parser->build(subnet_config->second); } - // Get command socket configuration from the config file. - // This code expects the following structure: - // { - // "socket-type": "unix", - // "socket-name": "/tmp/kea4.sock" - // } - ConstElementPtr sock_cfg = - CfgMgr::instance().getStagingCfg()->getControlSocketInfo(); - - // Close existing socket (if any). - isc::config::CommandMgr::instance().closeCommandSocket(); - if (sock_cfg) { - // This will create a control socket and will install external socket - // in IfaceMgr. That socket will be monitored when Dhcp4Srv::receivePacket() - // calls IfaceMgr::receive4() and callback in CommandMgr will be called, - // if necessary. If there were previously open command socket, it will - // be closed. - isc::config::CommandMgr::instance().openCommandSocket(sock_cfg); - } + // Setup the command channel. + configureCommandChannel(); // the leases database parser is the last to be run. std::map::const_iterator leases_config = diff --git a/src/bin/dhcp4/kea_controller.cc b/src/bin/dhcp4/kea_controller.cc index 102691cc3b..d79d9d037f 100644 --- a/src/bin/dhcp4/kea_controller.cc +++ b/src/bin/dhcp4/kea_controller.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -33,11 +33,6 @@ void configure(const std::string& file_name) { // This is a configuration backend implementation that reads the // configuration from a JSON file. - // We are starting the configuration process so we should remove any - // staging configuration that has been created during previous - // configuration attempts. - CfgMgr::instance().rollback(); - isc::data::ConstElementPtr json; isc::data::ConstElementPtr dhcp4; isc::data::ConstElementPtr logger; @@ -68,26 +63,14 @@ void configure(const std::string& file_name) { " Did you forget to add { } around your configuration?"); } - // If there's no logging element, we'll just pass NULL pointer, - // which will be handled by configureLogger(). - Daemon::configureLogger(json->get("Logging"), - CfgMgr::instance().getStagingCfg()); - - // Get Dhcp4 component from the config - dhcp4 = json->get("Dhcp4"); - if (!dhcp4) { - isc_throw(isc::BadValue, "no mandatory 'Dhcp4' entry in" - " the configuration"); - } - // Use parsed JSON structures to configure the server - result = ControlledDhcpv4Srv::processCommand("config-reload", dhcp4); + result = ControlledDhcpv4Srv::processCommand("set-config", json); if (!result) { // Undetermined status of the configuration. This should never // happen, but as the configureDhcp4Server returns a pointer, it is // theoretically possible that it will return NULL. isc_throw(isc::BadValue, "undefined result of " - "processCommand(\"config-reload\", dhcp4)"); + "processCommand(\"set-config\", json)"); } // Now check is the returned result is successful (rcode=0) or not @@ -100,17 +83,6 @@ void configure(const std::string& file_name) { "no details available"; isc_throw(isc::BadValue, reason); } - - // If configuration was parsed successfully, apply the new logger - // configuration to log4cplus. It is done before commit in case - // something goes wrong. - CfgMgr::instance().getStagingCfg()->applyLoggingCfg(); - - // Use new configuration. - /// @todo: This commit should be moved to - /// CtrlDhcp4Srv::commandConfigReloadHandler. - CfgMgr::instance().commit(); - } catch (const std::exception& ex) { // If configuration failed at any stage, we drop the staging // configuration and continue to use the previous one. diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 77bcb09035..1338a91c11 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -121,6 +122,13 @@ public: ConstElementPtr answer = server_->processConfig(config); ASSERT_TRUE(answer); +#if 0 + // If the configuration doesn't contain logging config, processConfig() + // will revert the logging to default (stdout). We call initLogger() + // to restore unit test logging. + isc::log::initLogger(); +#endif + int status = 0; ConstElementPtr txt = isc::config::parseAnswer(status, answer); // This should succeed. If not, print the error message. diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index 7800554ce1..3126447cfb 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace std; @@ -47,6 +48,9 @@ BaseServerTest::~BaseServerTest() { // Revert to original data directory. CfgMgr::instance().setDataDir(original_datadir_); + + // Revert to unit test logging, in case the test reconfigured it. + isc::log::initLogger(); } Dhcpv4SrvTest::Dhcpv4SrvTest() diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index b190c55dd0..2eed5762bd 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,8 @@ public: // Create fixed server id. server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, asiolink::IOAddress("192.0.3.1"))); + // Revert to unit test logging + isc::log::initLogger(); } /// @brief Returns fixed server identifier assigned to the naked server @@ -167,6 +170,8 @@ public: } virtual ~NakedDhcpv4Srv() { + // Revert to unit test logging + isc::log::initLogger(); } /// @brief Dummy server identifier option used by various tests.