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
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
}
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 =
// Let's create a new object that will hold the merged configuration.
boost::shared_ptr<MapElement> 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).
}
// 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<string> 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).
// 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();
}
- /// 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();
}
}
-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,
isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
}
-};
-};
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
--- /dev/null
+// 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 <config.h>
+#include <cc/data.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
+#include <dhcp6/dhcp6_log.h>
+#include <hooks/hooks_manager.h>
+#include <dhcp6/json_config_parser.h>
+
+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<string> 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
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.
///
/// @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
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
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());
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);
}
// 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();
-}
-
};
};
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
#include <config.h>
#include <config/ccsession.h>
-#include <dhcp/dhcp6.h>
+#include <dhcpsrv/cfgmgr.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <hooks/hooks_manager.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
-#include <fstream>
-#include <iostream>
-#include <sstream>
-
-#include <arpa/inet.h>
-#include <unistd.h>
-
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;
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());
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<ControlledDhcpv6Srv> 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));
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
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<ControlledDhcpv6Srv> 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
};
-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) {
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\": [ \"*\" ],"
}
// 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"