From: Tomek Mrugalski Date: Thu, 13 Apr 2017 13:09:12 +0000 (+0200) Subject: [5213] set-config renamed to config-set, config-reload implemented X-Git-Tag: trac5187_base~2^2~1^2~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=59b3f2e97dcd6e27a2e33b0a10ffc83c0f7243e9;p=thirdparty%2Fkea.git [5213] set-config renamed to config-set, config-reload implemented - removed obsolete kea_controller.cc (all code moved to ctrl_dhcp4_srv.cc) --- diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am index 7c29dd033a..cbb52631da 100644 --- a/src/bin/dhcp4/Makefile.am +++ b/src/bin/dhcp4/Makefile.am @@ -66,7 +66,6 @@ libdhcp4_la_SOURCES += dhcp4to6_ipc.cc dhcp4to6_ipc.h libdhcp4_la_SOURCES += dhcp4_lexer.ll location.hh position.hh stack.hh libdhcp4_la_SOURCES += dhcp4_parser.cc dhcp4_parser.h libdhcp4_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h -libdhcp4_la_SOURCES += kea_controller.cc libdhcp4_la_SOURCES += simple_parser4.cc simple_parser4.h nodist_libdhcp4_la_SOURCES = dhcp4_messages.h dhcp4_messages.cc diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index f19aba2a83..548103d41d 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -7,14 +7,15 @@ #include #include #include +#include #include #include #include -#include +#include #include #include #include -#include +#include #include #include @@ -24,11 +25,141 @@ using namespace isc::config; using namespace isc::stats; using namespace std; +namespace { + +/// @brief Signals handler for DHCPv4 server. +/// +/// This signal handler handles the following signals received by the DHCPv4 +/// server process: +/// - SIGHUP - triggers server's dynamic reconfiguration. +/// - SIGTERM - triggers server's shut down. +/// - SIGINT - triggers server's shut down. +/// +/// @param signo Signal number received. +void signalHandler(int signo) { + // SIGHUP signals a request to reconfigure the server. + if (signo == SIGHUP) { + isc::dhcp::ControlledDhcpv4Srv::processCommand("config-reload", ConstElementPtr()); + } else if ((signo == SIGTERM) || (signo == SIGINT)) { + isc::dhcp::ControlledDhcpv4Srv::processCommand("shutdown", ConstElementPtr()); + } +} + +} + namespace isc { namespace dhcp { ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL; +void +ControlledDhcpv4Srv::init(const std::string& file_name) { + // Configure the server using JSON file. + ConstElementPtr result = loadConfigFile(file_name); + int rcode; + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); + if (rcode != 0) { + string reason = comment ? comment->stringValue() : + "no details available"; + isc_throw(isc::BadValue, reason); + } + + // We don't need to call openActiveSockets() or startD2() as these + // methods are called in processConfig() which is called by + // processCommand("config-set", ...) + + // Set signal handlers. When the SIGHUP is received by the process + // the server reconfiguration will be triggered. When SIGTERM or + // SIGINT will be received, the server will start shutting down. + signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM)); + // Set the pointer to the handler function. + signal_handler_ = signalHandler; +} + +void ControlledDhcpv4Srv::cleanup() { + // Nothing to do here. No need to disconnect from anything. +} + +/// @brief Configure DHCPv4 server using the configuration file specified. +/// +/// This function is used to both configure the DHCP server on its startup +/// and dynamically reconfigure the server when SIGHUP signal is received. +/// +/// It fetches DHCPv4 server's configuration from the 'Dhcp4' section of +/// the JSON configuration file. +/// +/// @param file_name Configuration file location. +/// @return status of the command +ConstElementPtr +ControlledDhcpv4Srv::loadConfigFile(const std::string& file_name) { + // This is a configuration backend implementation that reads the + // configuration from a JSON file. + + isc::data::ConstElementPtr json; + isc::data::ConstElementPtr dhcp4; + isc::data::ConstElementPtr logger; + isc::data::ConstElementPtr result; + + // Basic sanity check: file name must not be empty. + try { + if (file_name.empty()) { + // Basic sanity check: file name must not be empty. + isc_throw(isc::BadValue, "JSON configuration file not specified." + " Please use -c command line option."); + } + + // Read contents of the file and parse it as JSON + Parser4Context parser; + json = parser.parseFile(file_name, Parser4Context::PARSER_DHCP4); + if (!json) { + isc_throw(isc::BadValue, "no configuration found"); + } + + // Let's do sanity check before we call json->get() which + // works only for map. + if (json->getType() != isc::data::Element::map) { + isc_throw(isc::BadValue, "Configuration file is expected to be " + "a map, i.e., start with { and end with } and contain " + "at least an entry called 'Dhcp4' that itself is a map. " + << file_name + << " is a valid JSON, but its top element is not a map." + " Did you forget to add { } around your configuration?"); + } + + // Use parsed JSON structures to configure the server + result = ControlledDhcpv4Srv::processCommand("config-set", 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-set\", json)"); + } + + // Now check is the returned result is successful (rcode=0) or not + // (see @ref isc::config::parseAnswer). + int rcode; + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); + if (rcode != 0) { + string reason = comment ? comment->stringValue() : + "no details available"; + isc_throw(isc::BadValue, reason); + } + } catch (const std::exception& ex) { + // If configuration failed at any stage, we drop the staging + // configuration and continue to use the previous one. + CfgMgr::instance().rollback(); + + LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL) + .arg(file_name).arg(ex.what()); + isc_throw(isc::BadValue, "configuration error using file '" + << file_name << "': " << ex.what()); + } + + return (result); +} + + ConstElementPtr ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) { if (ControlledDhcpv4Srv::getInstance()) { @@ -63,9 +194,23 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) { ConstElementPtr ControlledDhcpv4Srv::commandConfigReloadHandler(const string&, - ConstElementPtr args) { - // Use set-config as it handles logging and server config - return (commandSetConfigHandler("set-config", args)); + ConstElementPtr /*args*/) { + + // Get configuration file name. + std::string file = ControlledDhcpv4Srv::getInstance()->getConfigFile(); + try { + LOG_INFO(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION).arg(file); + return (loadConfigFile(file)); + } catch (const std::exception& ex) { + // Log the unsuccessful reconfiguration. The reason for failure + // should be already logged. Don't rethrow an exception so as + // the server keeps working. + LOG_ERROR(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION_FAIL) + .arg(file); + return (createAnswer(CONTROL_RESULT_ERROR, + "Failed to reload configuration from file " + + file)); + } } ConstElementPtr @@ -315,7 +460,7 @@ ControlledDhcpv4Srv::processCommand(const string& command, } else if (command == "config-reload") { return (srv->commandConfigReloadHandler(command, args)); - } else if (command == "set-config") { + } else if (command == "config-set") { return (srv->commandSetConfigHandler(command, args)); } else if (command == "config-get") { @@ -491,7 +636,11 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) CommandMgr::instance().registerCommand("config-get", boost::bind(&ControlledDhcpv4Srv::commandConfigGetHandler, this, _1, _2)); - /// @todo: register config-reload (see CtrlDhcpv4Srv::commandConfigReloadHandler) + CommandMgr::instance().registerCommand("config-reload", + boost::bind(&ControlledDhcpv4Srv::commandConfigReloadHandler, this, _1, _2)); + + CommandMgr::instance().registerCommand("config-set", + boost::bind(&ControlledDhcpv4Srv::commandSetConfigHandler, this, _1, _2)); CommandMgr::instance().registerCommand("config-test", boost::bind(&ControlledDhcpv4Srv::commandConfigTestHandler, this, _1, _2)); @@ -505,9 +654,6 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/) CommandMgr::instance().registerCommand("leases-reclaim", boost::bind(&ControlledDhcpv4Srv::commandLeasesReclaimHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("set-config", - boost::bind(&ControlledDhcpv4Srv::commandSetConfigHandler, this, _1, _2)); - CommandMgr::instance().registerCommand("shutdown", boost::bind(&ControlledDhcpv4Srv::commandShutdownHandler, this, _1, _2)); @@ -555,11 +701,12 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { // Deregister any registered commands (please keep in alphabetic order) CommandMgr::instance().deregisterCommand("build-report"); CommandMgr::instance().deregisterCommand("config-get"); + CommandMgr::instance().deregisterCommand("config-reload"); CommandMgr::instance().deregisterCommand("config-test"); CommandMgr::instance().deregisterCommand("config-write"); CommandMgr::instance().deregisterCommand("leases-reclaim"); CommandMgr::instance().deregisterCommand("libreload"); - CommandMgr::instance().deregisterCommand("set-config"); + CommandMgr::instance().deregisterCommand("config-set"); CommandMgr::instance().deregisterCommand("shutdown"); CommandMgr::instance().deregisterCommand("statistic-get"); CommandMgr::instance().deregisterCommand("statistic-get-all"); diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 8df6045d60..4fc38365e8 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -34,15 +34,25 @@ public: /// @brief Initializes the server. /// - /// Depending on the configuration backend, it establishes msgq session, - /// reads the JSON file from disk or may perform any other setup - /// operation. For specific details, see actual implementation in - /// *_backend.cc + /// It reads the JSON file from disk or may perform any other setup + /// operation. In particular, it also install signal handlers. /// - /// This method may throw if initialization fails. Exception types may be - /// specific to used configuration backend. + /// This method may throw if initialization fails. void init(const std::string& config_file); + /// @brief loads specific config file + /// + /// This utility method is called whenever we know a filename of the config + /// and need to load it. It calls config-set command once the content of + /// the file has been loaded and verified to be a sane JSON configuration. + /// config-set handler will process the config file (load it as current + /// configuration). + /// + /// @param file_name name of the file to be loaded + /// @return status of the file loading and outcome of config-set + isc::data::ConstElementPtr + loadConfigFile(const std::string& file_name); + /// @brief Performs cleanup, immediately before termination /// /// This method performs final clean up, just before the Dhcpv4Srv object diff --git a/src/bin/dhcp4/kea_controller.cc b/src/bin/dhcp4/kea_controller.cc deleted file mode 100644 index 43499f91a6..0000000000 --- a/src/bin/dhcp4/kea_controller.cc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (C) 2014-2017 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 -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include - -#include -#include -#include -#include -#include -#include - -#include - -using namespace isc::asiolink; -using namespace isc::dhcp; -using namespace std; - -namespace { - -/// @brief Configure DHCPv4 server using the configuration file specified. -/// -/// This function is used to both configure the DHCP server on its startup -/// and dynamically reconfigure the server when SIGHUP signal is received. -/// -/// It fetches DHCPv6 server's configuration from the 'Dhcp4' section of -/// the JSON configuration file. -/// -/// @param file_name Configuration file location. -void configure(const std::string& file_name) { - // This is a configuration backend implementation that reads the - // configuration from a JSON file. - - isc::data::ConstElementPtr json; - isc::data::ConstElementPtr dhcp4; - isc::data::ConstElementPtr logger; - isc::data::ConstElementPtr result; - - // Basic sanity check: file name must not be empty. - try { - if (file_name.empty()) { - // Basic sanity check: file name must not be empty. - isc_throw(isc::BadValue, "JSON configuration file not specified." - " Please use -c command line option."); - } - - // Read contents of the file and parse it as JSON - Parser4Context parser; - json = parser.parseFile(file_name, Parser4Context::PARSER_DHCP4); - if (!json) { - isc_throw(isc::BadValue, "no configuration found"); - } - - // Let's do sanity check before we call json->get() which - // works only for map. - if (json->getType() != isc::data::Element::map) { - isc_throw(isc::BadValue, "Configuration file is expected to be " - "a map, i.e., start with { and end with } and contain " - "at least an entry called 'Dhcp4' that itself is a map. " - << file_name - << " is a valid JSON, but its top element is not a map." - " Did you forget to add { } around your configuration?"); - } - - // Use parsed JSON structures to configure the server - 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(\"set-config\", json)"); - } - - // Now check is the returned result is successful (rcode=0) or not - // (see @ref isc::config::parseAnswer). - int rcode; - isc::data::ConstElementPtr comment = - isc::config::parseAnswer(rcode, result); - if (rcode != 0) { - string reason = comment ? comment->stringValue() : - "no details available"; - isc_throw(isc::BadValue, reason); - } - } catch (const std::exception& ex) { - // If configuration failed at any stage, we drop the staging - // configuration and continue to use the previous one. - CfgMgr::instance().rollback(); - - LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL) - .arg(file_name).arg(ex.what()); - isc_throw(isc::BadValue, "configuration error using file '" - << file_name << "': " << ex.what()); - } -} - -/// @brief Signals handler for DHCPv4 server. -/// -/// This signal handler handles the following signals received by the DHCPv4 -/// server process: -/// - SIGHUP - triggers server's dynamic reconfiguration. -/// - SIGTERM - triggers server's shut down. -/// - SIGINT - triggers server's shut down. -/// -/// @param signo Signal number received. -void signalHandler(int signo) { - // SIGHUP signals a request to reconfigure the server. - if (signo == SIGHUP) { - // Get configuration file name. - std::string file = ControlledDhcpv4Srv::getInstance()->getConfigFile(); - try { - LOG_INFO(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION).arg(file); - configure(file); - } catch (const std::exception& ex) { - // Log the unsuccessful reconfiguration. The reason for failure - // should be already logged. Don't rethrow an exception so as - // the server keeps working. - LOG_ERROR(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION_FAIL) - .arg(file); - } - } else if ((signo == SIGTERM) || (signo == SIGINT)) { - isc::data::ElementPtr params(new isc::data::MapElement()); - ControlledDhcpv4Srv::processCommand("shutdown", params); - } -} - -} - -namespace isc { -namespace dhcp { - -void -ControlledDhcpv4Srv::init(const std::string& file_name) { - // Configure the server using JSON file. - configure(file_name); - - // We don't need to call openActiveSockets() or startD2() as these - // methods are called in processConfig() which is called by - // processCommand("set-config", ...) - - // Set signal handlers. When the SIGHUP is received by the process - // the server reconfiguration will be triggered. When SIGTERM or - // SIGINT will be received, the server will start shutting down. - signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM)); - // Set the pointer to the handler function. - signal_handler_ = signalHandler; -} - -void ControlledDhcpv4Srv::cleanup() { - // Nothing to do here. No need to disconnect from anything. -} - -}; -}; diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index a8e1b31431..d7aaac94c3 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -383,10 +383,10 @@ TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) { EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos); EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos); EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos); + EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos); EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos); EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos); EXPECT_TRUE(command_list.find("\"libreload\"") != string::npos); - EXPECT_TRUE(command_list.find("\"set-config\"") != 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); @@ -594,13 +594,13 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { response); } -// Check that the "set-config" command will replace current configuration -TEST_F(CtrlChannelDhcpv4SrvTest, set_config) { +// Check that the "config-set" command will replace current configuration +TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { createUnixChannelServer(); // Define strings to permutate the config arguments // (Note the line feeds makes errors easy to find) - string set_config_txt = "{ \"command\": \"set-config\" \n"; + string set_config_txt = "{ \"command\": \"config-set\" \n"; string args_txt = " \"arguments\": { \n"; string dhcp4_cfg_txt = " \"Dhcp4\": { \n" @@ -665,7 +665,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, set_config) { << logger_txt << "}}"; - // Send the set-config command + // Send the config-set command std::string response; sendUnixCommand(os.str(), response); @@ -691,7 +691,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, set_config) { << "}\n" // close dhcp4 "}}"; - // Send the set-config command + // Send the config-set command sendUnixCommand(os.str(), response); // Should fail with a syntax error @@ -720,7 +720,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, set_config) { // Verify the control channel socket exists. ASSERT_TRUE(fileExists(socket_path_)); - // Send the set-config command. + // Send the config-set command. sendUnixCommand(os.str(), response); // Verify the control channel socket no longer exists. @@ -752,11 +752,12 @@ TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) { // We expect the server to report at least the following commands: checkListCommands(rsp, "build-report"); checkListCommands(rsp, "config-get"); + checkListCommands(rsp, "config-reload"); + checkListCommands(rsp, "config-set"); checkListCommands(rsp, "config-write"); checkListCommands(rsp, "list-commands"); checkListCommands(rsp, "leases-reclaim"); checkListCommands(rsp, "libreload"); - checkListCommands(rsp, "set-config"); checkListCommands(rsp, "shutdown"); checkListCommands(rsp, "statistic-get"); checkListCommands(rsp, "statistic-get-all"); @@ -797,7 +798,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configTest) { // Define strings to permutate the config arguments // (Note the line feeds makes errors easy to find) - string set_config_txt = "{ \"command\": \"set-config\" \n"; + string set_config_txt = "{ \"command\": \"config-set\" \n"; string config_test_txt = "{ \"command\": \"config-test\" \n"; string args_txt = " \"arguments\": { \n"; string dhcp4_cfg_txt = @@ -863,7 +864,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configTest) { << logger_txt << "}}"; - // Send the set-config command + // Send the config-set command std::string response; sendUnixCommand(os.str(), response);