From: Tomek Mrugalski Date: Tue, 6 May 2014 19:07:14 +0000 (+0200) Subject: [3400] Changes after review: X-Git-Tag: trac3434_base~32^2~21 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b4d53d935f4eb4fe8ae42b98118b398ba652a1e8;p=thirdparty%2Fkea.git [3400] Changes after review: - config-reload command added - config and command handlers cleaned up - files renamed - Majority of the backend code is now common - Added unit-test for config-reload command --- diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am index 1db81831ff..920ef80af1 100644 --- a/src/bin/dhcp6/Makefile.am +++ b/src/bin/dhcp6/Makefile.am @@ -54,14 +54,15 @@ pkglibexec_PROGRAMS = b10-dhcp6 b10_dhcp6_SOURCES = main.cc b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h -b10_dhcp6_SOURCES += ctrl_dhcp6_srv.h +b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h +b10_dhcp6_SOURCES += json_config_parser.cc json_config_parser.h if CONFIG_BACKEND_BIND10 -b10_dhcp6_SOURCES += json_config_parser.cc json_config_parser.h ctrl_bind10_dhcp6_srv.cc +b10_dhcp6_SOURCES += bundy_backend.cc endif if CONFIG_BACKEND_JSON -b10_dhcp6_SOURCES += json_config_parser.cc json_config_parser.h ctrl_json_dhcp6_srv.cc +b10_dhcp6_SOURCES += jsonfile_backend.cc endif nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc diff --git a/src/bin/dhcp6/ctrl_bind10_dhcp6_srv.cc b/src/bin/dhcp6/bundy_backend.cc similarity index 59% rename from src/bin/dhcp6/ctrl_bind10_dhcp6_srv.cc rename to src/bin/dhcp6/bundy_backend.cc index 0a0f5c629a..5dacb85924 100644 --- a/src/bin/dhcp6/ctrl_bind10_dhcp6_srv.cc +++ b/src/bin/dhcp6/bundy_backend.cc @@ -47,10 +47,33 @@ using namespace std; namespace isc { namespace dhcp { -ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL; - +/// @brief Helper session object that represents raw connection to msgq. +isc::cc::Session* cc_session_ = NULL; + +/// @brief Session that receives configuration and commands +isc::config::ModuleCCSession* config_session_ = NULL; + +/// @brief A dummy configuration handler that always returns success. +/// +/// This configuration handler does not perform configuration +/// parsing and always returns success. A dummy handler should +/// be installed using \ref isc::config::ModuleCCSession ctor +/// to get the initial configuration. This initial configuration +/// comprises values for only those elements that were modified +/// the previous session. The \ref dhcp6ConfigHandler can't be +/// used to parse the initial configuration because it needs the +/// full configuration to satisfy dependencies between the +/// various configuration values. Installing the dummy handler +/// that guarantees to return success causes initial configuration +/// to be stored for the session being created and that it can +/// be later accessed with +/// \ref isc::config::ConfigData::getFullConfig(). +/// +/// @param new_config new configuration. +/// +/// @return success configuration status. ConstElementPtr -ControlledDhcpv6Srv::dhcp6StubConfigHandler(ConstElementPtr) { +dhcp6StubConfigHandler(ConstElementPtr) { // This configuration handler is intended to be used only // when the initial configuration comes in. To receive this // configuration a pointer to this handler must be passed @@ -65,9 +88,9 @@ ControlledDhcpv6Srv::dhcp6StubConfigHandler(ConstElementPtr) { } ConstElementPtr -ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { +bundyConfigHandler(ConstElementPtr new_config) { - if (!server_ || !server_->config_session_) { + if (!ControlledDhcpv6Srv::getInstance() || !config_session_) { // That should never happen as we install config_handler // after we instantiate the server. ConstElementPtr answer = @@ -91,7 +114,7 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { // Let's create a new object that will hold the merged configuration. boost::shared_ptr merged_config(new MapElement()); // Let's get the existing configuration. - ConstElementPtr full_config = server_->config_session_->getFullConfig(); + ConstElementPtr full_config = config_session_->getFullConfig(); // The full_config and merged_config should be always non-NULL // but to provide some level of exception safety we check that they // really are (in case we go out of memory). @@ -105,92 +128,14 @@ ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) { } // Configure the server. - ConstElementPtr answer = configureDhcp6Server(*server_, merged_config); - - // Check that configuration was successful. If not, do not reopen sockets. - int rcode = 0; - parseAnswer(rcode, answer); - if (rcode != 0) { - return (answer); - } - - // Server will start DDNS communications if its enabled. - try { - server_->startD2(); - } catch (const std::exception& ex) { - std::ostringstream err; - err << "error starting DHCP_DDNS client " - " after server reconfiguration: " << ex.what(); - return (isc::config::createAnswer(1, err.str())); - } - // Configuration may change active interfaces. Therefore, we have to reopen - // sockets according to new configuration. This operation is not exception - // safe and we really don't want to emit exceptions to the callback caller. - // Instead, catch an exception and create appropriate answer. - try { - server_->openActiveSockets(server_->getPort()); - } catch (const std::exception& ex) { - std::ostringstream err; - err << "failed to open sockets after server reconfiguration: " << ex.what(); - answer = isc::config::createAnswer(1, err.str()); - } - return (answer); + return (ControlledDhcpv6Srv::processConfig(merged_config)); } -ConstElementPtr -ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr args) { - LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED) - .arg(command).arg(args->str()); - - if (command == "shutdown") { - if (ControlledDhcpv6Srv::server_) { - ControlledDhcpv6Srv::server_->shutdown(); - } else { - LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING); - ConstElementPtr answer = isc::config::createAnswer(1, - "Shutdown failure."); - return (answer); - } - ConstElementPtr answer = isc::config::createAnswer(0, - "Shutting down."); - return (answer); - - } else if (command == "libreload") { - // TODO delete any stored CalloutHandles referring to the old libraries - // Get list of currently loaded libraries and reload them. - vector loaded = HooksManager::getLibraryNames(); - bool status = HooksManager::loadLibraries(loaded); - if (!status) { - LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL); - ConstElementPtr answer = isc::config::createAnswer(1, - "Failed to reload hooks libraries."); - return (answer); - } - ConstElementPtr answer = isc::config::createAnswer(0, - "Hooks libraries successfully reloaded."); - return (answer); - } - - ConstElementPtr answer = isc::config::createAnswer(1, - "Unrecognized command."); - - return (answer); -} - -void ControlledDhcpv6Srv::sessionReader(void) { - // Process one asio event. If there are more events, iface_mgr will call - // this callback more than once. - if (server_) { - server_->io_service_.run_one(); - } -} - - bool ControlledDhcpv6Srv::init(const std::string& /* config_file*/) { - // This is BIND10 configuration backed. It established control session - // that is used to connect to BIND10 framework. + // This is Bundy configuration backed. It established control session + // that is used to connect to Bundy framework. // // Creates session that will be used to receive commands and updated // configuration from cfgmgr (or indirectly from user via bindctl). @@ -217,18 +162,18 @@ ControlledDhcpv6Srv::init(const std::string& /* config_file*/) { // been lost. config_session_ = new ModuleCCSession(specfile, *cc_session_, dhcp6StubConfigHandler, - dhcp6CommandHandler, false); + processCommand, false); config_session_->start(); // The constructor already pulled the configuration that had // been created in the previous session thanks to the dummy // handler. We can switch to the handler that will be // parsing future changes to the configuration. - config_session_->setConfigHandler(dhcp6ConfigHandler); + config_session_->setConfigHandler(bundyConfigHandler); try { // Pull the full configuration out from the session. - configureDhcp6Server(*this, config_session_->getFullConfig()); + processConfig(config_session_->getFullConfig()); // Server will start DDNS communications if its enabled. server_->startD2(); @@ -242,7 +187,7 @@ ControlledDhcpv6Srv::init(const std::string& /* config_file*/) { } - /// Integrate the asynchronous I/O model of BIND 10 configuration + /// Integrate the asynchronous I/O model of Bundy configuration /// control with the "select" model of the DHCP server. This is /// fully explained in \ref dhcpv6Session. int ctrl_socket = cc_session_->getSocketDesc(); @@ -271,34 +216,6 @@ void ControlledDhcpv6Srv::cleanup() { } } -ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) - : Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) { - server_ = this; // remember this instance for use in callback -} - -void ControlledDhcpv6Srv::shutdown() { - io_service_.stop(); // Stop ASIO transmissions - Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure. -} - -ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { - cleanup(); - - server_ = NULL; // forget this instance. There should be no callback anymore - // at this stage anyway. -} - -isc::data::ConstElementPtr -ControlledDhcpv6Srv::execDhcpv6ServerCommand(const std::string& command_id, - isc::data::ConstElementPtr args) { - try { - return (dhcp6CommandHandler(command_id, args)); - } catch (const Exception& ex) { - ConstElementPtr answer = isc::config::createAnswer(1, ex.what()); - return (answer); - } -} - void Daemon::loggerInit(const char* log_name, bool verbose, bool stand_alone) { isc::log::initLogger(log_name, @@ -306,5 +223,5 @@ Daemon::loggerInit(const char* log_name, bool verbose, bool stand_alone) { isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone); } -}; -}; +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc new file mode 100644 index 0000000000..3408e3be50 --- /dev/null +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -0,0 +1,183 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include +#include + +using namespace isc::data; +using namespace isc::hooks; +using namespace std; + +namespace isc { +namespace dhcp { + +ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL; + +ConstElementPtr +ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr) { + if (ControlledDhcpv6Srv::server_) { + ControlledDhcpv6Srv::server_->shutdown(); + } else { + LOG_WARN(dhcp6_logger, DHCP6_NOT_RUNNING); + ConstElementPtr answer = isc::config::createAnswer(1, "Shutdown failure."); + return (answer); + } + ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down."); + return (answer); +} + +ConstElementPtr +ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) { + // TODO delete any stored CalloutHandles referring to the old libraries + // Get list of currently loaded libraries and reload them. + vector loaded = HooksManager::getLibraryNames(); + bool status = HooksManager::loadLibraries(loaded); + if (!status) { + LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL); + ConstElementPtr answer = isc::config::createAnswer(1, + "Failed to reload hooks libraries."); + return (answer); + } + ConstElementPtr answer = isc::config::createAnswer(0, + "Hooks libraries successfully reloaded."); + return (answer); +} + +ConstElementPtr +ControlledDhcpv6Srv::commandConfigReloadHandler(const string&, ConstElementPtr args) { + + return (processConfig(args)); +} + +isc::data::ConstElementPtr +ControlledDhcpv6Srv::processCommand(const std::string& command, + isc::data::ConstElementPtr args) { + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_COMMAND_RECEIVED) + .arg(command).arg(args->str()); + + ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); + + if (!srv) { + ConstElementPtr no_srv = isc::config::createAnswer(1, + "Server object not initialized, can't process command '" + + command + "'."); + return (no_srv); + } + + try { + if (command == "shutdown") { + return (srv->commandShutdownHandler(command, args)); + + } else if (command == "libreload") { + return (srv->commandLibReloadHandler(command, args)); + + } else if (command == "config-reload") { + return (srv->commandConfigReloadHandler(command, args)); + } + + return (isc::config::createAnswer(1, "Unrecognized command:" + + command)); + + } catch (const Exception& ex) { + return (isc::config::createAnswer(1, "Error while processing command '" + + command + "':" + ex.what())); + } +} + + +isc::data::ConstElementPtr +ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { + ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance(); + + if (!srv) { + ConstElementPtr no_srv = isc::config::createAnswer(1, + "Server object not initialized, can't process config."); + return (no_srv); + } + + ConstElementPtr answer = configureDhcp6Server(*srv, config); + + // Check that configuration was successful. If not, do not reopen sockets + // and don't bother with DDNS stuff. + try { + int rcode = 0; + isc::config::parseAnswer(rcode, answer); + if (rcode != 0) { + return (answer); + } + } catch (const std::exception& ex) { + return (isc::config::createAnswer(1, "Failed to process configuration:" + + string(ex.what()))); + } + + // Server will start DDNS communications if its enabled. + try { + srv->startD2(); + } catch (const std::exception& ex) { + std::ostringstream err; + err << "error starting DHCP_DDNS client " + " after server reconfiguration: " << ex.what(); + return (isc::config::createAnswer(1, err.str())); + } + + // Configuration may change active interfaces. Therefore, we have to reopen + // sockets according to new configuration. This operation is not exception + // safe and we really don't want to emit exceptions to the callback caller. + // Instead, catch an exception and create appropriate answer. + try { + srv->openActiveSockets(srv->getPort()); + } catch (const std::exception& ex) { + std::ostringstream err; + err << "failed to open sockets after server reconfiguration: " << ex.what(); + answer = isc::config::createAnswer(1, err.str()); + } + + return (answer); +} + +ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) + : Dhcpv6Srv(port) { + if (server_) { + isc_throw(InvalidOperation, + "There is another Dhcpv6Srv instance already."); + } + server_ = this; // remember this instance for use in callback +} + +void ControlledDhcpv6Srv::shutdown() { + io_service_.stop(); // Stop ASIO transmissions + Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure. +} + +ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { + cleanup(); + + server_ = NULL; // forget this instance. There should be no callback anymore + // at this stage anyway. +} + +void ControlledDhcpv6Srv::sessionReader(void) { + // Process one asio event. If there are more events, iface_mgr will call + // this callback more than once. + if (server_) { + server_->io_service_.run_one(); + } +} + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index 2cc09f82f8..632007a041 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -44,48 +44,53 @@ public: ControlledDhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT); /// @brief Destructor. - ~ControlledDhcpv6Srv(); + virtual ~ControlledDhcpv6Srv(); /// @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 - /// ctrl_*_dhcp6_srv.cc + /// *_backend.cc /// /// @return true if initialization was successful, false if it failed bool init(const std::string& config_file); - /// @brief Terminates existing msgq session. + /// @brief Performs cleanup, immediately before termination /// - /// This method terminates existing session with msgq. After calling + /// This method performs final clean up, just before the Dhcpv6Srv object + /// is destroyed. The actual behavior is backend dependent. For Bundy + /// backend, it terminates existing session with msgq. After calling /// it, no further messages over msgq (commands or configuration updates) - /// may be received. + /// may be received. For JSON backend, it is no-op. /// - /// It is ok to call this method when session is disconnected already. + /// For specific details, see actual implementation in *_backend.cc void cleanup(); /// @brief Initiates shutdown procedure for the whole DHCPv6 server. void shutdown(); - /// @brief Session callback, processes received commands. + /// @brief command processor + /// + /// This method is uniform for all config backends. It processes received + /// command (as a string + JSON arguments). Internally, it's just a + /// wrapper that calls process*Command() methods and catches exceptions + /// in them. + /// + /// @note It never throws. /// /// @param command Text represenation of the command (e.g. "shutdown") /// @param args Optional parameters /// /// @return status of the command static isc::data::ConstElementPtr - execDhcpv6ServerCommand(const std::string& command, - isc::data::ConstElementPtr args); + processCommand(const std::string& command, isc::data::ConstElementPtr args); -protected: - /// @brief Static pointer to the sole instance of the DHCP server. + /// @brief configuration processor /// - /// This is required for config and command handlers to gain access to - /// the server - static ControlledDhcpv6Srv* server_; - - /// @brief A callback for handling incoming configuration updates. + /// This is a callback for handling incoming configuration updates. + /// This method should be called by all configuration backends when the + /// server is starting up or when configuration has changed. /// /// As pointer to this method is used a callback in ASIO used in /// ModuleCCSession, it has to be static. @@ -94,53 +99,71 @@ protected: /// /// @return status of the config update static isc::data::ConstElementPtr - dhcp6ConfigHandler(isc::data::ConstElementPtr new_config); - - /// @brief A dummy configuration handler that always returns success. - /// - /// This configuration handler does not perform configuration - /// parsing and always returns success. A dummy handler should - /// be installed using \ref isc::config::ModuleCCSession ctor - /// to get the initial configuration. This initial configuration - /// comprises values for only those elements that were modified - /// the previous session. The \ref dhcp6ConfigHandler can't be - /// used to parse the initial configuration because it needs the - /// full configuration to satisfy dependencies between the - /// various configuration values. Installing the dummy handler - /// that guarantees to return success causes initial configuration - /// to be stored for the session being created and that it can - /// be later accessed with - /// \ref isc::config::ConfigData::getFullConfig(). - /// - /// @param new_config new configuration. - /// - /// @return success configuration status. - static isc::data::ConstElementPtr - dhcp6StubConfigHandler(isc::data::ConstElementPtr new_config); + processConfig(isc::data::ConstElementPtr new_config); - /// @brief A callback for handling incoming commands. + /// @brief returns pointer to the sole instance of Dhcpv6Srv /// - /// @param command textual representation of the command - /// @param args parameters of the command + /// @note may return NULL, if called before server is spawned + static ControlledDhcpv6Srv* getInstance() { + return (server_); + } + +protected: + /// @brief Static pointer to the sole instance of the DHCP server. /// - /// @return status of the processed command - static isc::data::ConstElementPtr - dhcp6CommandHandler(const std::string& command, isc::data::ConstElementPtr args); + /// This is required for config and command handlers to gain access to + /// the server. Some of them need to be static methods. + static ControlledDhcpv6Srv* server_; - /// @brief Callback that will be called from iface_mgr when command/config arrives. + /// @brief Callback that will be called from iface_mgr when data + /// is received over control socket. /// /// This static callback method is called from IfaceMgr::receive6() method, - /// when there is a new command or configuration sent over msgq. + /// when there is a new command or configuration sent over control socket + /// (that was sent from msgq if backend is Bundy, or some yet unspecified + /// sender if the backend is JSON file). static void sessionReader(void); /// @brief IOService object, used for all ASIO operations. isc::asiolink::IOService io_service_; - /// @brief Helper session object that represents raw connection to msgq. - isc::cc::Session* cc_session_; + /// @brief handler for processing 'shutdown' command + /// + /// This handler processes shutdown command, which initializes shutdown + /// procedure. + /// @param command (parameter ignored) + /// @param args (parameter ignored) + /// + /// @return status of the command + isc::data::ConstElementPtr + commandShutdownHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for processing 'libreload' command + /// + /// This handler processes libreload command, which unloads all hook + /// libraries and reloads them. + /// + /// @param command (parameter ignored) + /// @param args (parameter ignored) + /// + /// @return status of the command + isc::data::ConstElementPtr + commandLibReloadHandler(const std::string& command, + isc::data::ConstElementPtr args); - /// @brief Session that receives configuration and commands - isc::config::ModuleCCSession* config_session_; + /// @brief handler for processing 'config-reload' command + /// + /// This handler processes config-reload command, which processes + /// configuration specified in args parameter. + /// + /// @param command (parameter ignored) + /// @param args configuration to be processed + /// + /// @return status of the command + isc::data::ConstElementPtr + commandConfigReloadHandler(const std::string& command, + isc::data::ConstElementPtr args); }; }; // namespace isc::dhcp diff --git a/src/bin/dhcp6/ctrl_json_dhcp6_srv.cc b/src/bin/dhcp6/jsonfile_backend.cc similarity index 75% rename from src/bin/dhcp6/ctrl_json_dhcp6_srv.cc rename to src/bin/dhcp6/jsonfile_backend.cc index 6defe99911..9689431791 100644 --- a/src/bin/dhcp6/ctrl_json_dhcp6_srv.cc +++ b/src/bin/dhcp6/jsonfile_backend.cc @@ -42,13 +42,6 @@ using namespace std; namespace isc { namespace dhcp { -// Need to provide dummy reader. JSON-file backend does not use any control -// readers for now. Eventually, we may consider having a socket (named socket?) -// that other processes (like IPAM) could write to, triggering specific actions. -// For now, it's a no-op method. -void ControlledDhcpv6Srv::sessionReader(void) { -} - bool ControlledDhcpv6Srv::init(const std::string& file_name) { // This is a configuration backend implementation that reads the @@ -70,7 +63,7 @@ ControlledDhcpv6Srv::init(const std::string& file_name) { json = Element::fromJSON(config); // Use parsed JSON structures to configure the server - result = configureDhcp6Server(*this, json); + result = processCommand("config-reload", json); } catch (const std::exception& ex) { LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what()); @@ -101,12 +94,9 @@ ControlledDhcpv6Srv::init(const std::string& file_name) { return (false); } - // Configuration may disable or enable interfaces so we have to - // reopen sockets according to new configuration. - openActiveSockets(getPort()); - - // Server will start DDNS communications if its enabled. - this->startD2(); + // We don't need to call openActiveSockets() or startD2() as these + // methods are called in processConfig() which is called by + // processCommand("reload-config", ...) return (true); } @@ -115,23 +105,5 @@ void ControlledDhcpv6Srv::cleanup() { // Nothing to do here. No need to disconnect from anything. } -ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port) - : Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) { -} - -void ControlledDhcpv6Srv::shutdown() { - - // Stop ASIO transmissions. Even though we didn't use it for - // configuration reading, there may be on-going transmissions - // with D2. - io_service_.stop(); - - Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure. -} - -ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { - cleanup(); -} - }; }; diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index f437bbfb09..d4a5bc0ec3 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -79,23 +79,26 @@ dhcp6_unittests_SOURCES += d2_unittest.cc d2_unittest.h dhcp6_unittests_SOURCES += marker_file.cc dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc +dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.h ../ctrl_dhcp6_srv.cc +dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += wireshark.cc dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h dhcp6_unittests_SOURCES += rebind_unittest.cc +dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h +dhcp6_unittests_SOURCES += config_parser_unittest.cc if CONFIG_BACKEND_BIND10 -dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h ../ctrl_bind10_dhcp6_srv.cc -dhcp6_unittests_SOURCES += ctrl_bind10_dhcp6_srv_unittest.cc -dhcp6_unittests_SOURCES += config_parser_unittest.cc +# For Bundy backend, we only need to run the usual tests. There are no +# Bundy-specific tests. +dhcp6_unittests_SOURCES += ../bundy_backend.cc +dhcp6_unittests_SOURCES += bundy_backend_unittest.cc endif if CONFIG_BACKEND_JSON -dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h ../ctrl_json_dhcp6_srv.cc -dhcp6_unittests_SOURCES += ctrl_json_dhcp6_srv_unittest.cc -dhcp6_unittests_SOURCES += config_parser_unittest.cc +dhcp6_unittests_SOURCES += ../jsonfile_backend.cc +dhcp6_unittests_SOURCES += jsonfile_backend_unittest.cc endif - nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc nodist_dhcp6_unittests_SOURCES += marker_file.h test_libraries.h diff --git a/src/bin/dhcp6/tests/ctrl_bind10_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc similarity index 63% rename from src/bin/dhcp6/tests/ctrl_bind10_dhcp6_srv_unittest.cc rename to src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index 6dfb9811f9..bd0b88f987 100644 --- a/src/bin/dhcp6/tests/ctrl_bind10_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include @@ -25,17 +25,7 @@ #include #include -#include -#include -#include - -#include -#include - using namespace std; -using namespace isc; -using namespace isc::asiolink; -using namespace isc::config; using namespace isc::data; using namespace isc::dhcp; using namespace isc::dhcp::test; @@ -85,13 +75,13 @@ TEST_F(CtrlDhcpv6SrvTest, commands) { int rcode = -1; // Case 1: send bogus command - ConstElementPtr result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("blah", params); - ConstElementPtr comment = parseAnswer(rcode, result); + ConstElementPtr result = ControlledDhcpv6Srv::processCommand("blah", params); + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); EXPECT_EQ(1, rcode); // expect failure (no such command as blah) // Case 2: send shutdown command without any parameters - result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params); - comment = parseAnswer(rcode, result); + result = ControlledDhcpv6Srv::processCommand("shutdown", params); + comment = isc::config::parseAnswer(rcode, result); EXPECT_EQ(0, rcode); // expect success const pid_t pid(getpid()); @@ -99,14 +89,21 @@ TEST_F(CtrlDhcpv6SrvTest, commands) { params->set("pid", x); // Case 3: send shutdown command with 1 parameter: pid - result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params); - comment = parseAnswer(rcode, result); + result = ControlledDhcpv6Srv::processCommand("shutdown", params); + comment = isc::config::parseAnswer(rcode, result); EXPECT_EQ(0, rcode); // Expect success } // Check that the "libreload" command will reload libraries - TEST_F(CtrlDhcpv6SrvTest, libreload) { + + // Sending commands for processing now requires a server that can process + // them. + boost::scoped_ptr srv; + ASSERT_NO_THROW( + srv.reset(new ControlledDhcpv6Srv(0)) + ); + // Ensure no marker files to start with. ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE)); ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE)); @@ -137,8 +134,8 @@ TEST_F(CtrlDhcpv6SrvTest, libreload) { int rcode = -1; ConstElementPtr result = - ControlledDhcpv6Srv::execDhcpv6ServerCommand("libreload", params); - ConstElementPtr comment = parseAnswer(rcode, result); + ControlledDhcpv6Srv::processCommand("libreload", params); + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); EXPECT_EQ(0, rcode); // Expect success // Check that the libraries have unloaded and reloaded. The libraries are @@ -148,4 +145,58 @@ TEST_F(CtrlDhcpv6SrvTest, libreload) { EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "1212")); } +// Check that the "configReload" command will reload libraries +TEST_F(CtrlDhcpv6SrvTest, configReload) { + + // Sending commands for processing now requires a server that can process + // them. + boost::scoped_ptr srv; + ASSERT_NO_THROW( + srv.reset(new ControlledDhcpv6Srv(0)) + ); + + // Now execute the "libreload" command. This should cause the libraries + // to unload and to reload. + + // Use empty parameters list + // Prepare configuration file. + string config_txt = "{ \"interfaces\": [ \"*\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::/80\" ]," + " \"subnet\": \"2001:db8:1::/64\" " + " }," + " {" + " \"pool\": [ \"2001:db8:2::/80\" ]," + " \"subnet\": \"2001:db8:2::/64\", " + " \"id\": 0" + " }," + " {" + " \"pool\": [ \"2001:db8:3::/80\" ]," + " \"subnet\": \"2001:db8:3::/64\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr config = Element::fromJSON(config_txt); + + // Make sure there are no subnets configured. + CfgMgr::instance().deleteSubnets6(); + + // Now send the command + int rcode = -1; + ConstElementPtr result = + ControlledDhcpv6Srv::processCommand("config-reload", config); + ConstElementPtr comment = isc::config::parseAnswer(rcode, result); + EXPECT_EQ(0, rcode); // Expect success + + // Check that the config was indeed applied. + const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6(); + EXPECT_EQ(3, subnets->size()); + + // Clean up after the test. + CfgMgr::instance().deleteSubnets6(); +} + } // End of anonymous namespace diff --git a/src/bin/dhcp6/tests/ctrl_json_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/jsonfile_backend_unittest.cc similarity index 95% rename from src/bin/dhcp6/tests/ctrl_json_dhcp6_srv_unittest.cc rename to src/bin/dhcp6/tests/jsonfile_backend_unittest.cc index 29343ca7b4..3dfbf25fbd 100644 --- a/src/bin/dhcp6/tests/ctrl_json_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/jsonfile_backend_unittest.cc @@ -46,12 +46,12 @@ public: }; -class ControlledJSONDhcpv6SrvTest : public ::testing::Test { +class JSONFileBackendTest : public ::testing::Test { public: - ControlledJSONDhcpv6SrvTest() { + JSONFileBackendTest() { } - ~ControlledJSONDhcpv6SrvTest() { + ~JSONFileBackendTest() { }; void writeFile(const std::string& file_name, const std::string& content) { @@ -66,10 +66,10 @@ public: static const char* TEST_FILE; }; -const char* ControlledJSONDhcpv6SrvTest::TEST_FILE = "test-config.json"; +const char* JSONFileBackendTest::TEST_FILE = "test-config.json"; // This test checks if configuration can be read from a JSON file. -TEST_F(ControlledJSONDhcpv6SrvTest, jsonFile) { +TEST_F(JSONFileBackendTest, jsonFile) { // Prepare configuration file. string config = "{ \"interfaces\": [ \"*\" ]," @@ -141,7 +141,7 @@ TEST_F(ControlledJSONDhcpv6SrvTest, jsonFile) { } // This test checks if configuration can be read from a JSON file. -TEST_F(ControlledJSONDhcpv6SrvTest, comments) { +TEST_F(JSONFileBackendTest, comments) { string config_hash_comments = "# This is a comment. It should be \n" "#ignored. Real config starts in line below\n"