Browse Source

[5046] Implement set-config command in kea-dhcp4

    Mirror the changes made in kea-dhcp6.
Thomas Markwalder 8 years ago
parent
commit
44d38ce10a

+ 59 - 0
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -66,6 +66,51 @@ ControlledDhcpv4Srv::commandConfigReloadHandler(const string&,
 }
 
 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) {
     int status_code = 1;
@@ -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");

+ 13 - 0
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
     ///

+ 43 - 19
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<std::string, ConstElementPtr>::const_iterator leases_config =

+ 3 - 31
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.

+ 8 - 0
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc

@@ -14,6 +14,7 @@
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <hooks/hooks_manager.h>
+#include <log/logger_support.h>
 #include <stats/stats_mgr.h>
 #include <testutils/unix_control_client.h>
 
@@ -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.

+ 4 - 0
src/bin/dhcp4/tests/dhcp4_test_utils.cc

@@ -24,6 +24,7 @@
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <log/logger_support.h>
 #include <stats/stats_mgr.h>
 
 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()

+ 5 - 0
src/bin/dhcp4/tests/dhcp4_test_utils.h

@@ -21,6 +21,7 @@
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcp4/dhcp4_srv.h>
+#include <log/logger_support.h>
 #include <asiolink/io_address.h>
 #include <cc/command_interpreter.h>
 #include <list>
@@ -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.