mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 05:27:55 +00:00
[master] Merge branch 'trac3399' [Kea4 reads config from JSON file]
Conflicts: ChangeLog src/bin/dhcp6/kea_controller.cc src/lib/dhcpsrv/daemon.h
This commit is contained in:
commit
83816e00c5
@ -1,3 +1,10 @@
|
||||
788. [func] tomek
|
||||
DHCPv4 server: New parameter added to configure.ac: --with-kea-config.
|
||||
It allows selecting configuration backend and accepts one of two
|
||||
values: BUNDY, which uses Bundy (former BIND10) framework as Kea
|
||||
0.8 did, or JSON, which reads configuration from a JSON file.
|
||||
(Trac #3399, git 6e4dd3ae58c091ba0fd64c87fa8d7c268210f99b)
|
||||
|
||||
787. [func] marcin
|
||||
DHCPv6 server: Implemented dynamic reconfiguration of the server,
|
||||
triggered when the SIGHUP signal is received by the server's
|
||||
@ -31,7 +38,7 @@
|
||||
(Trac #3268, git bd60252e679f19b062f61926647f661ab169f21c)
|
||||
|
||||
783. [func]* tomek
|
||||
b10-dhcp6: New parameter added to configure: --with-kea-config.
|
||||
DHCPv6 server: New parameter added to configure: --with-kea-config.
|
||||
It allows selecting configuration backend and accepts one of two
|
||||
values: BUNDY, which uses Bundy (former BIND10 framework as Kea
|
||||
0.8 did, or JSON, which reads configuration from a JSON file.
|
||||
|
@ -55,6 +55,7 @@
|
||||
* - @subpage dhcpv4OptionsParse
|
||||
* - @subpage dhcpv4DDNSIntegration
|
||||
* - @subpage dhcpv4Classifier
|
||||
* - @subpage dhcpv4ConfigBackend
|
||||
* - @subpage dhcpv4Other
|
||||
* - @subpage dhcp6
|
||||
* - @subpage dhcpv6Session
|
||||
|
38
doc/examples/kea4/several-subnets.json
Normal file
38
doc/examples/kea4/several-subnets.json
Normal file
@ -0,0 +1,38 @@
|
||||
# This is an example configuration file for DHCPv4 server in Kea.
|
||||
# It's a basic scenario with three IPv4 subnets configured. In each
|
||||
# subnet, there's a smaller pool of dynamic addresses.
|
||||
|
||||
{ "Dhcp4":
|
||||
|
||||
{
|
||||
# Kea is told to listen on eth0 interface only.
|
||||
"interfaces": [ "eth0" ],
|
||||
|
||||
# We need to specify lease type. As of May 2014, three backends are supported:
|
||||
# memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
|
||||
# any prior set up.
|
||||
"lease-database": {
|
||||
"type": "memfile"
|
||||
},
|
||||
|
||||
# Addresses will be assigned with the valid lifetimes being 4000.
|
||||
# Client is told to start renewing after 1000 seconds. If the server
|
||||
# does not repond after 2000 seconds since the lease was granted, client
|
||||
# is supposed to start REBIND procedure (emergency renewal that allows
|
||||
# switching to a different server).
|
||||
"valid-lifetime": 4000,
|
||||
"renew-timer": 1000,
|
||||
"rebind-timer": 2000,
|
||||
|
||||
# The following list defines subnets. Each subnet consists of at
|
||||
# least subnet and pool entries.
|
||||
"subnet4": [
|
||||
{ "pool": [ "192.0.2.1 - 192.0.2.200" ],
|
||||
"subnet": "192.0.2.0/24" },
|
||||
{ "pool": [ "192.0.3.100 - 192.0.3.200" ],
|
||||
"subnet": "192.0.3.0/24" },
|
||||
{ "pool": [ "192.0.4.1 - 192.0.4.254" ],
|
||||
"subnet": "192.0.4.0/24" } ]
|
||||
}
|
||||
|
||||
}
|
34
doc/examples/kea4/single-subnet.json
Normal file
34
doc/examples/kea4/single-subnet.json
Normal file
@ -0,0 +1,34 @@
|
||||
# This is an example configuration file for the DHCPv4 server in Kea.
|
||||
# It is a basic scenario with one IPv4 subnet configured. The subnet
|
||||
# contains a single pool of dynamically allocated addresses.
|
||||
|
||||
{ "Dhcp4":
|
||||
|
||||
{
|
||||
# Kea is told to listen on eth0 interface only.
|
||||
"interfaces": [ "eth0" ],
|
||||
|
||||
# We need to specify lease type. As of May 2014, three backends are supported:
|
||||
# memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
|
||||
# any prior set up.
|
||||
"lease-database": {
|
||||
"type": "memfile"
|
||||
},
|
||||
|
||||
# Addresses will be assigned with valid lifetimes being 4000. Client
|
||||
# is told to start renewing after 1000 seconds. If the server does not respond
|
||||
# after 2000 seconds since the lease was granted, client is supposed
|
||||
# to start REBIND procedure (emergency renewal that allows switching
|
||||
# to a different server).
|
||||
"valid-lifetime": 4000,
|
||||
"renew-timer": 1000,
|
||||
"rebind-timer": 2000,
|
||||
|
||||
# The following list defines subnets. We have only one subnet
|
||||
# here.
|
||||
"subnet4": [
|
||||
{ "pool": [ "192.0.2.1 - 192.0.2.200" ],
|
||||
"subnet": "192.0.2.0/24" } ]
|
||||
}
|
||||
|
||||
}
|
@ -51,10 +51,18 @@ pkglibexec_PROGRAMS = b10-dhcp4
|
||||
|
||||
b10_dhcp4_SOURCES = main.cc
|
||||
b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
|
||||
b10_dhcp4_SOURCES += config_parser.cc config_parser.h
|
||||
b10_dhcp4_SOURCES += json_config_parser.cc json_config_parser.h
|
||||
b10_dhcp4_SOURCES += dhcp4_log.cc dhcp4_log.h
|
||||
b10_dhcp4_SOURCES += dhcp4_srv.cc dhcp4_srv.h
|
||||
|
||||
if CONFIG_BACKEND_BUNDY
|
||||
b10_dhcp4_SOURCES += bundy_controller.cc
|
||||
endif
|
||||
|
||||
if CONFIG_BACKEND_JSON
|
||||
b10_dhcp4_SOURCES += kea_controller.cc
|
||||
endif
|
||||
|
||||
nodist_b10_dhcp4_SOURCES = dhcp4_messages.h dhcp4_messages.cc
|
||||
EXTRA_DIST += dhcp4_messages.mes
|
||||
|
||||
|
219
src/bin/dhcp4/bundy_controller.cc
Normal file
219
src/bin/dhcp4/bundy_controller.cc
Normal file
@ -0,0 +1,219 @@
|
||||
// Copyright (C) 2012-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 <asiolink/asiolink.h>
|
||||
#include <cc/data.h>
|
||||
#include <cc/session.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <dhcp4/spec_config.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcpsrv/dhcp_config_parser.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <hooks/hooks_manager.h>
|
||||
#include <util/buffer.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::cc;
|
||||
using namespace isc::config;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::hooks;
|
||||
using namespace isc::log;
|
||||
using namespace isc::util;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @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 dhcp4ConfigHandler 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
|
||||
dhcp4StubConfigHandler(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
|
||||
// using ModuleCCSession constructor. This constructor will
|
||||
// invoke the handler and will store the configuration for
|
||||
// the configuration session when the handler returns success.
|
||||
// Since this configuration is partial we just pretend to
|
||||
// parse it and always return success. The function that
|
||||
// initiates the session must get the configuration on its
|
||||
// own using getFullConfig.
|
||||
return (isc::config::createAnswer(0, "Configuration accepted."));
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
bundyConfigHandler(ConstElementPtr new_config) {
|
||||
if (!ControlledDhcpv4Srv::getInstance() || !config_session_) {
|
||||
// That should never happen as we install config_handler
|
||||
// after we instantiate the server.
|
||||
ConstElementPtr answer =
|
||||
isc::config::createAnswer(1, "Configuration rejected,"
|
||||
" server is during startup/shutdown phase.");
|
||||
return (answer);
|
||||
}
|
||||
|
||||
// The configuration passed to this handler function is partial.
|
||||
// In other words, it just includes the values being modified.
|
||||
// In the same time, there are dependencies between various
|
||||
// DHCP configuration parsers. For example: the option value can
|
||||
// be set if the definition of this option is set. If someone removes
|
||||
// an existing option definition then the partial configuration that
|
||||
// removes that definition is triggered while a relevant option value
|
||||
// may remain configured. This eventually results in the DHCP server
|
||||
// configuration being in the inconsistent state.
|
||||
// In order to work around this problem we need to merge the new
|
||||
// configuration with the existing (full) configuration.
|
||||
|
||||
// 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 = 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).
|
||||
if (full_config && merged_config) {
|
||||
merged_config->setValue(full_config->mapValue());
|
||||
|
||||
// Merge an existing and new configuration.
|
||||
isc::data::merge(merged_config, new_config);
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
|
||||
.arg(full_config->str());
|
||||
}
|
||||
|
||||
// Configure the server.
|
||||
return (ControlledDhcpv4Srv::processConfig(merged_config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ControlledDhcpv4Srv::init(const std::string& /*config_file*/) {
|
||||
|
||||
string specfile;
|
||||
if (getenv("B10_FROM_BUILD")) {
|
||||
specfile = string(getenv("B10_FROM_BUILD")) +
|
||||
"/src/bin/dhcp4/dhcp4.spec";
|
||||
} else {
|
||||
specfile = string(DHCP4_SPECFILE_LOCATION);
|
||||
}
|
||||
|
||||
/// @todo: Check if session is not established already. Throw, if it is.
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTING)
|
||||
.arg(specfile);
|
||||
cc_session_ = new Session(io_service_.get_io_service());
|
||||
// Create a session with the dummy configuration handler.
|
||||
// Dumy configuration handler is internally invoked by the
|
||||
// constructor and on success the constructor updates
|
||||
// the current session with the configuration that had been
|
||||
// committed in the previous session. If we did not install
|
||||
// the dummy handler, the previous configuration would have
|
||||
// been lost.
|
||||
config_session_ = new ModuleCCSession(specfile, *cc_session_,
|
||||
dhcp4StubConfigHandler,
|
||||
processCommand, false);
|
||||
config_session_->start();
|
||||
|
||||
// We initially create ModuleCCSession() without configHandler, as
|
||||
// the session module is too eager to send partial configuration.
|
||||
// We want to get the full configuration, so we explicitly call
|
||||
// getFullConfig() and then pass it to our configHandler.
|
||||
config_session_->setConfigHandler(bundyConfigHandler);
|
||||
|
||||
try {
|
||||
configureDhcp4Server(*this, config_session_->getFullConfig());
|
||||
|
||||
// Server will start DDNS communications if its enabled.
|
||||
server_->startD2();
|
||||
|
||||
// Configuration may disable or enable interfaces so we have to
|
||||
// reopen sockets according to new configuration.
|
||||
openActiveSockets(getPort(), useBroadcast());
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
|
||||
|
||||
}
|
||||
|
||||
/// Integrate the asynchronous I/O model of BIND 10 configuration
|
||||
/// control with the "select" model of the DHCP server. This is
|
||||
/// fully explained in \ref dhcpv4Session.
|
||||
int ctrl_socket = cc_session_->getSocketDesc();
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
|
||||
.arg(ctrl_socket);
|
||||
IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
|
||||
}
|
||||
|
||||
|
||||
void ControlledDhcpv4Srv::cleanup() {
|
||||
if (config_session_) {
|
||||
delete config_session_;
|
||||
config_session_ = NULL;
|
||||
}
|
||||
if (cc_session_) {
|
||||
|
||||
int ctrl_socket = cc_session_->getSocketDesc();
|
||||
cc_session_->disconnect();
|
||||
|
||||
IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
|
||||
delete cc_session_;
|
||||
cc_session_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Daemon::loggerInit(const char* log_name, bool verbose) {
|
||||
isc::log::initLogger(log_name,
|
||||
(verbose ? isc::log::DEBUG : isc::log::INFO),
|
||||
isc::log::MAX_DEBUG_LEVEL, NULL, true);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
// 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
|
||||
@ -13,36 +13,14 @@
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <cc/data.h>
|
||||
#include <cc/session.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <dhcp4/spec_config.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcpsrv/dhcp_config_parser.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <hooks/hooks_manager.h>
|
||||
#include <util/buffer.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::cc;
|
||||
using namespace isc::config;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::hooks;
|
||||
using namespace isc::log;
|
||||
using namespace isc::util;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
@ -51,217 +29,141 @@ namespace dhcp {
|
||||
ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::dhcp4StubConfigHandler(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
|
||||
// using ModuleCCSession constructor. This constructor will
|
||||
// invoke the handler and will store the configuration for
|
||||
// the configuration session when the handler returns success.
|
||||
// Since this configuration is partial we just pretend to
|
||||
// parse it and always return success. The function that
|
||||
// initiates the session must get the configuration on its
|
||||
// own using getFullConfig.
|
||||
return (isc::config::createAnswer(0, "Configuration accepted."));
|
||||
ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) {
|
||||
if (ControlledDhcpv4Srv::getInstance()) {
|
||||
ControlledDhcpv4Srv::getInstance()->shutdown();
|
||||
} else {
|
||||
LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
|
||||
ConstElementPtr answer = isc::config::createAnswer(1,
|
||||
"Shutdown failure.");
|
||||
return (answer);
|
||||
}
|
||||
ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down.");
|
||||
return (answer);
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
|
||||
if (!server_ || !server_->config_session_) {
|
||||
// That should never happen as we install config_handler
|
||||
// after we instantiate the server.
|
||||
ConstElementPtr answer =
|
||||
isc::config::createAnswer(1, "Configuration rejected,"
|
||||
" server is during startup/shutdown phase.");
|
||||
ControlledDhcpv4Srv::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(dhcp4_logger, DHCP4_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);
|
||||
}
|
||||
|
||||
// The configuration passed to this handler function is partial.
|
||||
// In other words, it just includes the values being modified.
|
||||
// In the same time, there are dependencies between various
|
||||
// DHCP configuration parsers. For example: the option value can
|
||||
// be set if the definition of this option is set. If someone removes
|
||||
// an existing option definition then the partial configuration that
|
||||
// removes that definition is triggered while a relevant option value
|
||||
// may remain configured. This eventually results in the DHCP server
|
||||
// configuration being in the inconsistent state.
|
||||
// In order to work around this problem we need to merge the new
|
||||
// configuration with the existing (full) configuration.
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::commandConfigReloadHandler(const string&,
|
||||
ConstElementPtr args) {
|
||||
return (processConfig(args));
|
||||
}
|
||||
|
||||
// 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();
|
||||
// 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).
|
||||
if (full_config && merged_config) {
|
||||
merged_config->setValue(full_config->mapValue());
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::processCommand(const string& command,
|
||||
ConstElementPtr args) {
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
|
||||
.arg(command).arg(args->str());
|
||||
|
||||
// Merge an existing and new configuration.
|
||||
isc::data::merge(merged_config, new_config);
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
|
||||
.arg(full_config->str());
|
||||
ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
|
||||
|
||||
if (!srv) {
|
||||
ConstElementPtr no_srv = isc::config::createAnswer(1,
|
||||
"Server object not initialized, so can't process command '" +
|
||||
command + "', arguments: '" + args->str() + "'.");
|
||||
return (no_srv);
|
||||
}
|
||||
|
||||
// Configure the server.
|
||||
ConstElementPtr answer = configureDhcp4Server(*server_, merged_config);
|
||||
try {
|
||||
if (command == "shutdown") {
|
||||
return (srv->commandShutdownHandler(command, args));
|
||||
|
||||
// Check that configuration was successful. If not, do not reopen sockets.
|
||||
int rcode = 0;
|
||||
parseAnswer(rcode, answer);
|
||||
if (rcode != 0) {
|
||||
} else if (command == "libreload") {
|
||||
return (srv->commandLibReloadHandler(command, args));
|
||||
|
||||
} else if (command == "config-reload") {
|
||||
return (srv->commandConfigReloadHandler(command, args));
|
||||
|
||||
}
|
||||
ConstElementPtr answer = isc::config::createAnswer(1,
|
||||
"Unrecognized command:" + command);
|
||||
return (answer);
|
||||
} catch (const Exception& ex) {
|
||||
return (isc::config::createAnswer(1, "Error while processing command '"
|
||||
+ command + "':" + ex.what() +
|
||||
", params: '" + args->str() + "'"));
|
||||
}
|
||||
}
|
||||
|
||||
isc::data::ConstElementPtr
|
||||
ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
|
||||
.arg(config->str());
|
||||
|
||||
ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
|
||||
|
||||
// Single stream instance used in all error clauses
|
||||
std::ostringstream err;
|
||||
|
||||
if (!srv) {
|
||||
err << "Server object not initialized, can't process config.";
|
||||
return (isc::config::createAnswer(1, err.str()));
|
||||
}
|
||||
|
||||
ConstElementPtr answer = configureDhcp4Server(*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) {
|
||||
err << "Failed to process configuration:" << ex.what();
|
||||
return (isc::config::createAnswer(1, err.str()));
|
||||
}
|
||||
|
||||
// Server will start DDNS communications if its enabled.
|
||||
try {
|
||||
server_->startD2();
|
||||
srv->startD2();
|
||||
} catch (const std::exception& ex) {
|
||||
std::ostringstream err;
|
||||
err << "error starting DHCP_DDNS client "
|
||||
" after server reconfiguration: " << ex.what();
|
||||
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.
|
||||
// safe and we really don't want to emit exceptions to whoever called this
|
||||
// method. Instead, catch an exception and create appropriate answer.
|
||||
try {
|
||||
server_->openActiveSockets(server_->getPort(), server_->useBroadcast());
|
||||
srv->openActiveSockets(srv->getPort(), getInstance()->useBroadcast());
|
||||
} catch (std::exception& ex) {
|
||||
std::ostringstream err;
|
||||
err << "failed to open sockets after server reconfiguration: " << ex.what();
|
||||
err << "failed to open sockets after server reconfiguration: "
|
||||
<< ex.what();
|
||||
answer = isc::config::createAnswer(1, err.str());
|
||||
}
|
||||
return (answer);
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::dhcp4CommandHandler(const string& command, ConstElementPtr args) {
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
|
||||
.arg(command).arg(args->str());
|
||||
|
||||
if (command == "shutdown") {
|
||||
if (ControlledDhcpv4Srv::server_) {
|
||||
ControlledDhcpv4Srv::server_->shutdown();
|
||||
} else {
|
||||
LOG_WARN(dhcp4_logger, DHCP4_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(dhcp4_logger, DHCP4_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 ControlledDhcpv4Srv::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();
|
||||
}
|
||||
}
|
||||
|
||||
void ControlledDhcpv4Srv::establishSession() {
|
||||
|
||||
string specfile;
|
||||
if (getenv("B10_FROM_BUILD")) {
|
||||
specfile = string(getenv("B10_FROM_BUILD")) +
|
||||
"/src/bin/dhcp4/dhcp4.spec";
|
||||
} else {
|
||||
specfile = string(DHCP4_SPECFILE_LOCATION);
|
||||
}
|
||||
|
||||
/// @todo: Check if session is not established already. Throw, if it is.
|
||||
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTING)
|
||||
.arg(specfile);
|
||||
cc_session_ = new Session(io_service_.get_io_service());
|
||||
// Create a session with the dummy configuration handler.
|
||||
// Dumy configuration handler is internally invoked by the
|
||||
// constructor and on success the constructor updates
|
||||
// the current session with the configuration that had been
|
||||
// committed in the previous session. If we did not install
|
||||
// the dummy handler, the previous configuration would have
|
||||
// been lost.
|
||||
config_session_ = new ModuleCCSession(specfile, *cc_session_,
|
||||
dhcp4StubConfigHandler,
|
||||
dhcp4CommandHandler, false);
|
||||
config_session_->start();
|
||||
|
||||
// We initially create ModuleCCSession() without configHandler, as
|
||||
// the session module is too eager to send partial configuration.
|
||||
// We want to get the full configuration, so we explicitly call
|
||||
// getFullConfig() and then pass it to our configHandler.
|
||||
config_session_->setConfigHandler(dhcp4ConfigHandler);
|
||||
|
||||
try {
|
||||
configureDhcp4Server(*this, config_session_->getFullConfig());
|
||||
|
||||
// Server will start DDNS communications if its enabled.
|
||||
server_->startD2();
|
||||
|
||||
// Configuration may disable or enable interfaces so we have to
|
||||
// reopen sockets according to new configuration.
|
||||
openActiveSockets(getPort(), useBroadcast());
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
|
||||
|
||||
}
|
||||
|
||||
/// Integrate the asynchronous I/O model of BIND 10 configuration
|
||||
/// control with the "select" model of the DHCP server. This is
|
||||
/// fully explained in \ref dhcpv4Session.
|
||||
int ctrl_socket = cc_session_->getSocketDesc();
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
|
||||
.arg(ctrl_socket);
|
||||
IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
|
||||
}
|
||||
|
||||
void ControlledDhcpv4Srv::disconnectSession() {
|
||||
if (config_session_) {
|
||||
delete config_session_;
|
||||
config_session_ = NULL;
|
||||
}
|
||||
if (cc_session_) {
|
||||
|
||||
int ctrl_socket = cc_session_->getSocketDesc();
|
||||
cc_session_->disconnect();
|
||||
|
||||
IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
|
||||
delete cc_session_;
|
||||
cc_session_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
|
||||
:Dhcpv4Srv(port), cc_session_(NULL), config_session_(NULL) {
|
||||
server_ = this; // remember this instance for use in callback
|
||||
:Dhcpv4Srv(port) {
|
||||
if (getInstance()) {
|
||||
isc_throw(InvalidOperation,
|
||||
"There is another Dhcpv4Srv instance already.");
|
||||
}
|
||||
server_ = this; // remember this instance for later use in handlers
|
||||
}
|
||||
|
||||
void ControlledDhcpv4Srv::shutdown() {
|
||||
@ -270,22 +172,19 @@ void ControlledDhcpv4Srv::shutdown() {
|
||||
}
|
||||
|
||||
ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
|
||||
disconnectSession();
|
||||
|
||||
server_ = NULL; // forget this instance. There should be no callback anymore
|
||||
// at this stage anyway.
|
||||
cleanup();
|
||||
|
||||
server_ = NULL; // forget this instance. Noone should call any handlers at
|
||||
// this stage.
|
||||
}
|
||||
|
||||
isc::data::ConstElementPtr
|
||||
ControlledDhcpv4Srv::execDhcpv4ServerCommand(const std::string& command_id,
|
||||
isc::data::ConstElementPtr args) {
|
||||
try {
|
||||
return (dhcp4CommandHandler(command_id, args));
|
||||
} catch (const Exception& ex) {
|
||||
ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
|
||||
return (answer);
|
||||
void ControlledDhcpv4Srv::sessionReader(void) {
|
||||
// Process one asio event. If there are more events, iface_mgr will call
|
||||
// this callback more than once.
|
||||
if (getInstance()) {
|
||||
getInstance()->io_service_.run_one();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-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
|
||||
@ -26,12 +26,10 @@ namespace dhcp {
|
||||
|
||||
/// @brief Controlled version of the DHCPv4 server
|
||||
///
|
||||
/// This is a class that is responsible for establishing connection
|
||||
/// with msqg (receving commands and configuration). This is an extended
|
||||
/// version of Dhcpv4Srv class that is purely a DHCPv4 server, without
|
||||
/// external control. ControlledDhcpv4Srv should be used in typical BIND10
|
||||
/// (i.e. featuring msgq) environment, while Dhcpv4Srv should be used in
|
||||
/// embedded environments.
|
||||
/// This is a class that is responsible for DHCPv4 server being controllable.
|
||||
/// It does various things, depending on the configuration backend.
|
||||
/// For Bundy backend it establishes a connection with msqg and later receives
|
||||
/// commands over it. For Kea backend, it reads configuration file from disk.
|
||||
///
|
||||
/// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
|
||||
/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
|
||||
@ -46,44 +44,57 @@ public:
|
||||
/// @brief Destructor.
|
||||
~ControlledDhcpv4Srv();
|
||||
|
||||
/// @brief Establishes msgq session.
|
||||
/// @brief Initializes the server.
|
||||
///
|
||||
/// Creates session that will be used to receive commands and updated
|
||||
/// configuration from cfgmgr (or indirectly from user via bindctl).
|
||||
void establishSession();
|
||||
/// 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
|
||||
///
|
||||
/// This method may throw if initialization fails. Exception types may be
|
||||
/// specific to used configuration backend.
|
||||
void 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 Dhcpv4Srv 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.
|
||||
void disconnectSession();
|
||||
/// For specific details, see actual implementation in *_backend.cc
|
||||
void cleanup();
|
||||
|
||||
/// @brief Initiates shutdown procedure for the whole DHCPv4 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.
|
||||
///
|
||||
/// Currently supported commands are:
|
||||
/// - shutdown
|
||||
/// - libreload
|
||||
/// - config-reload
|
||||
///
|
||||
/// @note It never throws.
|
||||
///
|
||||
/// @param command Text represenation of the command (e.g. "shutdown")
|
||||
/// @param args Optional parameters
|
||||
/// @param command text represenation of the command (e.g. "shutdown")
|
||||
/// @param args optional parameters
|
||||
///
|
||||
/// @return status of the command
|
||||
static isc::data::ConstElementPtr
|
||||
execDhcpv4ServerCommand(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 ControlledDhcpv4Srv* server_;
|
||||
|
||||
/// @brief A callback for handling incoming configuration updates.
|
||||
/// This is a method 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.
|
||||
@ -92,54 +103,72 @@ protected:
|
||||
///
|
||||
/// @return status of the config update
|
||||
static isc::data::ConstElementPtr
|
||||
dhcp4ConfigHandler(isc::data::ConstElementPtr new_config);
|
||||
processConfig(isc::data::ConstElementPtr new_config);
|
||||
|
||||
/// @brief A dummy configuration handler that always returns success.
|
||||
/// @brief Returns pointer to the sole instance of Dhcpv4Srv
|
||||
///
|
||||
/// 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 dhcp4ConfigHandler 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
|
||||
dhcp4StubConfigHandler(isc::data::ConstElementPtr new_config);
|
||||
/// @return server instance (may return NULL, if called before server is spawned)
|
||||
static ControlledDhcpv4Srv* getInstance() {
|
||||
return (server_);
|
||||
}
|
||||
|
||||
/// @brief A callback for handling incoming commands.
|
||||
///
|
||||
/// @param command textual representation of the command
|
||||
/// @param args parameters of the command
|
||||
///
|
||||
/// @return status of the processed command
|
||||
static isc::data::ConstElementPtr
|
||||
dhcp4CommandHandler(const std::string& command, isc::data::ConstElementPtr args);
|
||||
|
||||
/// @brief Callback that will be called from iface_mgr when command/config arrives.
|
||||
protected:
|
||||
/// @brief Static pointer to the sole instance of the DHCP server.
|
||||
///
|
||||
/// This static callback method is called from IfaceMgr::receive4() method,
|
||||
/// when there is a new command or configuration sent over msgq.
|
||||
/// This is required for config and command handlers to gain access to
|
||||
/// the server
|
||||
static ControlledDhcpv4Srv* server_;
|
||||
|
||||
/// @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 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 Session that receives configuration and commands
|
||||
isc::config::ModuleCCSession* config_session_;
|
||||
/// @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 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
|
||||
|
@ -191,6 +191,46 @@ being passed in isc::dhcp::Dhcpv4Srv::selectSubnet() to isc::dhcp::CfgMgr::getSu
|
||||
Currently this capability is usable, but the number of scenarios it supports is
|
||||
limited.
|
||||
|
||||
@section dhcpv4ConfigBackend Configuration backend for DHCPv4
|
||||
|
||||
There are many theoretical ways in which server configuration can be stored. Kea 0.8 and
|
||||
earlier versions used BIND10 framework and its internal storage for DHCPv6 server configuration.
|
||||
The legacy ISC-DHCP implementation uses flat files. Configuration stored in JSON files is
|
||||
becoming more and more popular among various projects. There are unofficial patches for
|
||||
ISC-DHCP that keep parts of the configuration in LDAP. It was also suggested that in some
|
||||
cases it would be convenient to keep configuration in XML files.
|
||||
|
||||
Kea 0.9 introduces configuration backends that are switchable during compilation phase.
|
||||
There is a new parameter for configure script: --with-kea-config. It currently supports
|
||||
two values: BUNDY and JSON.
|
||||
|
||||
BUNDY (which is the default value as of May 2014) means that Kea4 is linked with the
|
||||
Bundy (former BIND10) configuration backend that connects to the BIND10 framework and in general works
|
||||
exactly the same as Kea 0.8 and earlier versions. The benefits of that backend are uniform
|
||||
integration with Bundy/BIND10 framework, easy on-line reconfiguration using bindctl, available
|
||||
RESTful API. On the other hand, it requires the whole heavy Bundy framework that requires
|
||||
Python3 to be present. That framework is going away with the release of Kea 0.9.
|
||||
|
||||
JSON is a new configuration backend that causes Kea to read JSON configuration file from
|
||||
disk. It does not require any framework and thus is considered more lightweight. It will
|
||||
allow dynamic on-line reconfiguration, but will lack remote capabilities (i.e. no RESTful
|
||||
API). This configuration backend is expected to be the default for upcoming Kea 0.9. It
|
||||
requires <tt> -c config-file </tt> command-line option.
|
||||
|
||||
Internally, configuration backends are implemented as different implementations of the
|
||||
isc::dhcp::ControlledDhcpv4Srv class, stored in {kea,bundy}_controller.cc files. Depending on
|
||||
the choice made by ./configure script, only one of those files is compiled and linked.
|
||||
There are backend specific tests in src/bin/dhcp4/tests/{kea,bundy}_controller_unittest.cc.
|
||||
Only tests specific to selected backend are linked and executed during make distcheck.
|
||||
|
||||
While it is unlikely that ISC will support more than one backend at any given time, there
|
||||
are several aspects that make that approach appealing in the long term. First, having
|
||||
two backends is essential during transition time, where both old and new backend is used.
|
||||
Second, there are external organizations that develop and presumably maintain LDAP backend
|
||||
for ISC-DHCP. Is at least possible that similar will happen for Kea. Finally, if we ever
|
||||
extend the isc::dhcp::CfgMgr with configuration export, this approach could be used as
|
||||
a migration tool.
|
||||
|
||||
@section dhcpv4Other Other DHCPv4 topics
|
||||
|
||||
For hooks API support in DHCPv4, see @ref dhcpv4Hooks.
|
||||
|
@ -44,6 +44,10 @@ name was malformed or due to internal server error.
|
||||
A debug message listing the command (and possible arguments) received
|
||||
from the BIND 10 control system by the DHCPv4 server.
|
||||
|
||||
% DHCP4_CONFIG_RECEIVED received configuration %1
|
||||
A debug message listing the configuration received by the DHCPv4 server.
|
||||
The source of that configuration depends on used configuration backend.
|
||||
|
||||
% DHCP4_CONFIG_COMPLETE DHCPv4 server has completed configuration: %1
|
||||
This is an informational message announcing the successful processing of a
|
||||
new configuration. It is output during server startup, and when an updated
|
||||
@ -339,17 +343,11 @@ core component within the DHCPv4 server (the Dhcpv4 server object)
|
||||
has failed. As a result, the server will exit. The reason for the
|
||||
failure is given within the message.
|
||||
|
||||
% DHCP4_STANDALONE skipping message queue, running standalone
|
||||
This is a debug message indicating that the DHCPv4 server is running in
|
||||
standalone mode, not connected to the message queue. Standalone mode
|
||||
is only useful during program development, and should not be used in a
|
||||
production environment.
|
||||
|
||||
% DHCP4_STARTING server starting
|
||||
This informational message indicates that the DHCPv4 server has
|
||||
processed any command-line switches and is starting.
|
||||
|
||||
% DHCP4_START_INFO pid: %1, port: %2, verbose: %3, standalone: %4
|
||||
% DHCP4_START_INFO pid: %1, port: %2, verbose: %3
|
||||
This is a debug message issued during the DHCPv4 server startup.
|
||||
It lists some information about the parameters with which the server
|
||||
is running.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2011-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
|
||||
@ -26,6 +26,7 @@
|
||||
#include <dhcpsrv/subnet.h>
|
||||
#include <dhcpsrv/alloc_engine.h>
|
||||
#include <hooks/callout_handle.h>
|
||||
#include <dhcpsrv/daemon.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
@ -57,7 +58,7 @@ public:
|
||||
///
|
||||
/// For detailed explanation or relations between main(), ControlledDhcpv4Srv,
|
||||
/// Dhcpv4Srv and other classes, see \ref dhcpv4Session.
|
||||
class Dhcpv4Srv : public boost::noncopyable {
|
||||
class Dhcpv4Srv : public Daemon {
|
||||
|
||||
public:
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option_definition.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
#include <dhcpsrv/dbaccess_parser.h>
|
||||
#include <dhcpsrv/dhcp_parsers.h>
|
||||
#include <dhcpsrv/option_space_container.h>
|
124
src/bin/dhcp4/kea_controller.cc
Normal file
124
src/bin/dhcp4/kea_controller.cc
Normal file
@ -0,0 +1,124 @@
|
||||
// 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 <dhcp4/json_config_parser.h>
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
void
|
||||
ControlledDhcpv4Srv::init(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 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(BadValue, "JSON configuration file not specified. Please "
|
||||
"use -c command line option.");
|
||||
}
|
||||
|
||||
// Read contents of the file and parse it as JSON
|
||||
json = isc::data::Element::fromJSONFile(file_name, true);
|
||||
|
||||
if (!json) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
|
||||
.arg("Config file " + file_name + " missing or empty.");
|
||||
isc_throw(BadValue, "Unable to process JSON configuration file:"
|
||||
+ file_name);
|
||||
}
|
||||
|
||||
// Get Dhcp4 component from the config
|
||||
dhcp4 = json->get("Dhcp4");
|
||||
|
||||
if (!dhcp4) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
|
||||
.arg("Config file " + file_name + " does not include 'Dhcp4' entry.");
|
||||
isc_throw(BadValue, "Unable to process JSON configuration file:"
|
||||
+ file_name);
|
||||
}
|
||||
|
||||
// Use parsed JSON structures to configure the server
|
||||
result = processCommand("config-reload", dhcp4);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
|
||||
isc_throw(BadValue, "Unable to process JSON configuration file:"
|
||||
+ file_name);
|
||||
}
|
||||
|
||||
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.
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
|
||||
.arg("Configuration failed: Undefined result of processCommand("
|
||||
"config-reload, " + file_name + ")");
|
||||
isc_throw(BadValue, "Configuration failed: Undefined result of "
|
||||
"processCommand('config-reload', " + file_name + ")");
|
||||
}
|
||||
|
||||
// Now check is the returned result is successful (rcode=0) or not
|
||||
isc::data::ConstElementPtr comment; /// see @ref isc::config::parseAnswer
|
||||
int rcode;
|
||||
comment = isc::config::parseAnswer(rcode, result);
|
||||
if (rcode != 0) {
|
||||
string reason = "";
|
||||
if (comment) {
|
||||
reason = string(" (") + comment->stringValue() + string(")");
|
||||
}
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(reason);
|
||||
isc_throw(BadValue, "Failed to apply configuration:" << reason);
|
||||
}
|
||||
|
||||
// We don't need to call openActiveSockets() or startD2() as these
|
||||
// methods are called in processConfig() which is called by
|
||||
// processCommand("reload-config", ...)
|
||||
}
|
||||
|
||||
void ControlledDhcpv4Srv::cleanup() {
|
||||
// Nothing to do here. No need to disconnect from anything.
|
||||
}
|
||||
|
||||
/// This is a logger initialization for JSON file backend.
|
||||
/// For now, it's just setting log messages to be printed on stdout.
|
||||
/// @todo: Implement this properly (see #3427)
|
||||
void Daemon::loggerInit(const char*, bool verbose) {
|
||||
|
||||
setenv("B10_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
|
||||
setenv("B10_LOGGER_ROOT", "kea", 0);
|
||||
setenv("B10_LOGGER_SEVERITY", (verbose ? "DEBUG":"INFO"), 0);
|
||||
setenv("B10_LOGGER_DBGLEVEL", "99", 0);
|
||||
setenv("B10_LOGGER_DESTINATION", "stdout", 0);
|
||||
isc::log::initLogger();
|
||||
}
|
||||
|
||||
};
|
||||
};
|
@ -39,13 +39,15 @@ namespace {
|
||||
|
||||
const char* const DHCP4_NAME = "b10-dhcp4";
|
||||
|
||||
const char* const DHCP4_LOGGER_NAME = "kea";
|
||||
|
||||
void
|
||||
usage() {
|
||||
cerr << "Usage: " << DHCP4_NAME << " [-v] [-s] [-p number]" << endl;
|
||||
cerr << "Usage: " << DHCP4_NAME << " [-v] [-p number] [-c file]" << endl;
|
||||
cerr << " -v: verbose output" << endl;
|
||||
cerr << " -s: stand-alone mode (don't connect to BIND10)" << endl;
|
||||
cerr << " -p number: specify non-standard port number 1-65535 "
|
||||
<< "(useful for testing only)" << endl;
|
||||
cerr << " -c file: specify configuration file" << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} // end of anonymous namespace
|
||||
@ -55,19 +57,17 @@ main(int argc, char* argv[]) {
|
||||
int ch;
|
||||
int port_number = DHCP4_SERVER_PORT; // The default. any other values are
|
||||
// useful for testing only.
|
||||
bool stand_alone = false; // Should be connect to BIND10 msgq?
|
||||
bool verbose_mode = false; // Should server be verbose?
|
||||
|
||||
while ((ch = getopt(argc, argv, "vsp:")) != -1) {
|
||||
// The standard config file
|
||||
std::string config_file("");
|
||||
|
||||
while ((ch = getopt(argc, argv, "vp:c:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'v':
|
||||
verbose_mode = true;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
stand_alone = true;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
try {
|
||||
port_number = boost::lexical_cast<int>(optarg);
|
||||
@ -83,6 +83,10 @@ main(int argc, char* argv[]) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c': // config file
|
||||
config_file = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
@ -93,36 +97,39 @@ main(int argc, char* argv[]) {
|
||||
usage();
|
||||
}
|
||||
|
||||
// Initialize logging. If verbose, we'll use maximum verbosity.
|
||||
// If standalone is enabled, do not buffer initial log messages
|
||||
isc::log::initLogger(DHCP4_NAME,
|
||||
(verbose_mode ? isc::log::DEBUG : isc::log::INFO),
|
||||
isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
|
||||
LOG_INFO(dhcp4_logger, DHCP4_STARTING);
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
|
||||
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
|
||||
.arg(stand_alone ? "yes" : "no" );
|
||||
|
||||
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
try {
|
||||
// Initialize logging. If verbose, we'll use maximum verbosity.
|
||||
// If standalone is enabled, do not buffer initial log messages
|
||||
Daemon::loggerInit(DHCP4_LOGGER_NAME, verbose_mode);
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_START_INFO)
|
||||
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no");
|
||||
|
||||
LOG_INFO(dhcp4_logger, DHCP4_STARTING);
|
||||
|
||||
// Create the server instance.
|
||||
ControlledDhcpv4Srv server(port_number);
|
||||
if (!stand_alone) {
|
||||
try {
|
||||
server.establishSession();
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_SESSION_FAIL).arg(ex.what());
|
||||
// Let's continue. It is useful to have the ability to run
|
||||
// DHCP server in stand-alone mode, e.g. for testing
|
||||
// We do need to make sure logging is no longer buffered
|
||||
// since then it would not print until dhcp6 is stopped
|
||||
isc::log::LoggerManager log_manager;
|
||||
log_manager.process();
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_STANDALONE);
|
||||
|
||||
try {
|
||||
// Initialize the server.
|
||||
server.init(config_file);
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(dhcp4_logger, DHCP4_SESSION_FAIL).arg(ex.what());
|
||||
|
||||
// We should not continue if were told to configure (either read
|
||||
// config file or establish Bundy control session).
|
||||
|
||||
isc::log::LoggerManager log_manager;
|
||||
log_manager.process();
|
||||
|
||||
cerr << "Failed to initialize server: " << ex.what() << endl;
|
||||
return (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// And run the main loop of the server.
|
||||
server.run();
|
||||
|
||||
LOG_INFO(dhcp4_logger, DHCP4_SHUTDOWN);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
|
@ -33,6 +33,7 @@ AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
|
||||
|
||||
CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
|
||||
CLEANFILES += $(builddir)/load_marker.txt $(builddir)/unload_marker.txt
|
||||
CLEANFILES += *.json
|
||||
|
||||
AM_CXXFLAGS = $(B10_CXXFLAGS)
|
||||
if USE_CLANGPP
|
||||
@ -77,7 +78,7 @@ TESTS += dhcp4_unittests
|
||||
|
||||
dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc
|
||||
dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc
|
||||
dhcp4_unittests_SOURCES += ../config_parser.cc ../config_parser.h
|
||||
dhcp4_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h
|
||||
dhcp4_unittests_SOURCES += d2_unittest.h d2_unittest.cc
|
||||
dhcp4_unittests_SOURCES += dhcp4_test_utils.h
|
||||
dhcp4_unittests_SOURCES += dhcp4_unittests.cc
|
||||
@ -89,6 +90,19 @@ dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
|
||||
dhcp4_unittests_SOURCES += config_parser_unittest.cc
|
||||
dhcp4_unittests_SOURCES += fqdn_unittest.cc
|
||||
dhcp4_unittests_SOURCES += marker_file.cc
|
||||
|
||||
if CONFIG_BACKEND_BUNDY
|
||||
# For Bundy backend, we only need to run the usual tests. There are no
|
||||
# Bundy-specific tests yet.
|
||||
dhcp4_unittests_SOURCES += ../bundy_controller.cc
|
||||
dhcp4_unittests_SOURCES += bundy_controller_unittest.cc
|
||||
endif
|
||||
|
||||
if CONFIG_BACKEND_JSON
|
||||
dhcp4_unittests_SOURCES += ../kea_controller.cc
|
||||
dhcp4_unittests_SOURCES += kea_controller_unittest.cc
|
||||
endif
|
||||
|
||||
nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
|
||||
nodist_dhcp4_unittests_SOURCES += marker_file.h test_libraries.h
|
||||
|
||||
@ -109,4 +123,6 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
|
||||
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la
|
||||
endif
|
||||
|
||||
noinst_EXTRA_DIST = configs-list.txt
|
||||
|
||||
noinst_PROGRAMS = $(TESTS)
|
||||
|
30
src/bin/dhcp4/tests/bundy_controller_unittest.cc
Normal file
30
src/bin/dhcp4/tests/bundy_controller_unittest.cc
Normal file
@ -0,0 +1,30 @@
|
||||
// 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 <gtest/gtest.h>
|
||||
|
||||
namespace {
|
||||
|
||||
/// As of May 2014, maintaining or extending Bundy support is very low
|
||||
/// prority for Kea team. We are looking for contributors, who would
|
||||
/// like to maintain this backend.
|
||||
|
||||
// Bundy framework specific tests should be added here.
|
||||
TEST(BundyBackendTest, dummy) {
|
||||
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include <config/ccsession.h>
|
||||
#include <dhcp4/dhcp4_srv.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
#include <dhcp/option4_addrlst.h>
|
||||
#include <dhcp/option_custom.h>
|
||||
#include <dhcp/option_int.h>
|
||||
|
5
src/bin/dhcp4/tests/configs-list.txt
Normal file
5
src/bin/dhcp4/tests/configs-list.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# This is a list of config files that the unit-tests (specifically
|
||||
# JSONFileBackendTest.loadAllConfigs) is going to load.
|
||||
|
||||
../../../../doc/examples/kea4/single-subnet.json
|
||||
../../../../doc/examples/kea4/several-subnets.json
|
@ -85,12 +85,12 @@ TEST_F(CtrlDhcpv4SrvTest, commands) {
|
||||
int rcode = -1;
|
||||
|
||||
// Case 1: send bogus command
|
||||
ConstElementPtr result = ControlledDhcpv4Srv::execDhcpv4ServerCommand("blah", params);
|
||||
ConstElementPtr result = ControlledDhcpv4Srv::processCommand("blah", params);
|
||||
ConstElementPtr comment = parseAnswer(rcode, result);
|
||||
EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
|
||||
|
||||
// Case 2: send shutdown command without any parameters
|
||||
result = ControlledDhcpv4Srv::execDhcpv4ServerCommand("shutdown", params);
|
||||
result = ControlledDhcpv4Srv::processCommand("shutdown", params);
|
||||
comment = parseAnswer(rcode, result);
|
||||
EXPECT_EQ(0, rcode); // expect success
|
||||
|
||||
@ -99,7 +99,7 @@ TEST_F(CtrlDhcpv4SrvTest, commands) {
|
||||
params->set("pid", x);
|
||||
|
||||
// Case 3: send shutdown command with 1 parameter: pid
|
||||
result = ControlledDhcpv4Srv::execDhcpv4ServerCommand("shutdown", params);
|
||||
result = ControlledDhcpv4Srv::processCommand("shutdown", params);
|
||||
comment = parseAnswer(rcode, result);
|
||||
EXPECT_EQ(0, rcode); // expect success
|
||||
}
|
||||
@ -107,6 +107,14 @@ TEST_F(CtrlDhcpv4SrvTest, commands) {
|
||||
// Check that the "libreload" command will reload libraries
|
||||
|
||||
TEST_F(CtrlDhcpv4SrvTest, libreload) {
|
||||
|
||||
// Sending commands for processing now requires a server that can process
|
||||
// them.
|
||||
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
|
||||
ASSERT_NO_THROW(
|
||||
srv.reset(new ControlledDhcpv4Srv(0))
|
||||
);
|
||||
|
||||
// Ensure no marker files to start with.
|
||||
ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
|
||||
ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
|
||||
@ -137,7 +145,7 @@ TEST_F(CtrlDhcpv4SrvTest, libreload) {
|
||||
int rcode = -1;
|
||||
|
||||
ConstElementPtr result =
|
||||
ControlledDhcpv4Srv::execDhcpv4ServerCommand("libreload", params);
|
||||
ControlledDhcpv4Srv::processCommand("libreload", params);
|
||||
ConstElementPtr comment = parseAnswer(rcode, result);
|
||||
EXPECT_EQ(0, rcode); // Expect success
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
#include <dhcp4/tests/d2_unittest.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <dhcp/tests/iface_mgr_test_config.h>
|
||||
#include <dhcp4/dhcp4_srv.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
#include <hooks/server_hooks.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcpsrv/lease_mgr.h>
|
||||
|
@ -13,7 +13,7 @@
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
from init import ProcessInfo, parse_args, dump_pid, unlink_pid_file, _BASETIME
|
||||
from init import ProcessInfo
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
@ -207,22 +207,5 @@ class TestDhcpv4Daemon(unittest.TestCase):
|
||||
# Check that there is an error message about invalid port number printed on stderr
|
||||
self.assertEqual( str(error).count("Failed to parse port number"), 1)
|
||||
|
||||
def test_portnumber_nonroot(self):
|
||||
print("Check that specifying unprivileged port number will work.")
|
||||
|
||||
# Check that there is a message about running with an unprivileged port
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-s', '-p', '10057'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP4_OPEN_SOCKET opening sockets on port 10057"), 1)
|
||||
|
||||
def test_skip_msgq(self):
|
||||
print("Check that connection to BIND10 msgq can be disabled.")
|
||||
|
||||
# Check that the system outputs a message on one of its streams about running
|
||||
# standalone.
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp4', '-v', '-s', '-p', '10057'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP4_STANDALONE"), 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <asiolink/io_address.h>
|
||||
#include <cc/data.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
#include <dhcp4/tests/dhcp4_test_utils.h>
|
||||
#include <dhcp/option4_addrlst.h>
|
||||
#include <dhcp/option_int_array.h>
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcpsrv/lease_mgr_factory.h>
|
||||
#include <dhcpsrv/subnet.h>
|
||||
#include <dhcp4/config_parser.h>
|
||||
#include <dhcp4/json_config_parser.h>
|
||||
#include <dhcp4/tests/dhcp4_test_utils.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
|
296
src/bin/dhcp4/tests/kea_controller_unittest.cc
Normal file
296
src/bin/dhcp4/tests/kea_controller_unittest.cc
Normal file
@ -0,0 +1,296 @@
|
||||
// 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 <config/ccsession.h>
|
||||
#include <dhcp/dhcp4.h>
|
||||
#include <dhcp4/ctrl_dhcp4_srv.h>
|
||||
#include <dhcpsrv/cfgmgr.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::hooks;
|
||||
|
||||
namespace {
|
||||
|
||||
class NakedControlledDhcpv4Srv: public ControlledDhcpv4Srv {
|
||||
// "Naked" DHCPv4 server, exposes internal fields
|
||||
public:
|
||||
NakedControlledDhcpv4Srv():ControlledDhcpv4Srv(0) { }
|
||||
};
|
||||
|
||||
/// @brief test class for Kea configuration backend
|
||||
///
|
||||
/// This class is used for testing Kea configuration backend.
|
||||
/// It is very simple and currently focuses on reading
|
||||
/// config file from disk. It is expected to be expanded in the
|
||||
/// near future.
|
||||
class JSONFileBackendTest : public ::testing::Test {
|
||||
public:
|
||||
JSONFileBackendTest() {
|
||||
}
|
||||
|
||||
~JSONFileBackendTest() {
|
||||
static_cast<void>(unlink(TEST_FILE));
|
||||
};
|
||||
|
||||
/// @brief writes specified content to a well known file
|
||||
///
|
||||
/// Writes specified content to TEST_FILE. Tests will
|
||||
/// attempt to read that file.
|
||||
///
|
||||
/// @param content content to be written to file
|
||||
void writeFile(const std::string& content) {
|
||||
static_cast<void>(unlink(TEST_FILE));
|
||||
|
||||
ofstream out(TEST_FILE, ios::trunc);
|
||||
EXPECT_TRUE(out.is_open());
|
||||
out << content;
|
||||
out.close();
|
||||
}
|
||||
|
||||
/// Name of a config file used during tests
|
||||
static const char* TEST_FILE;
|
||||
};
|
||||
|
||||
const char* JSONFileBackendTest::TEST_FILE = "test-config.json";
|
||||
|
||||
// This test checks if configuration can be read from a JSON file.
|
||||
TEST_F(JSONFileBackendTest, jsonFile) {
|
||||
|
||||
// Prepare configuration file.
|
||||
string config = "{ \"Dhcp4\": { \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
||||
" \"subnet\": \"192.0.2.0/24\" "
|
||||
" },"
|
||||
" {"
|
||||
" \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
|
||||
" \"subnet\": \"192.0.3.0/24\", "
|
||||
" \"id\": 0 "
|
||||
" },"
|
||||
" {"
|
||||
" \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
|
||||
" \"subnet\": \"192.0.4.0/24\" "
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }"
|
||||
"}";
|
||||
|
||||
writeFile(config);
|
||||
|
||||
// Now initialize the server
|
||||
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
|
||||
ASSERT_NO_THROW(
|
||||
srv.reset(new ControlledDhcpv4Srv(0))
|
||||
);
|
||||
|
||||
// And configure it using the config file.
|
||||
EXPECT_NO_THROW(srv->init(TEST_FILE));
|
||||
|
||||
// Now check if the configuration has been applied correctly.
|
||||
const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
|
||||
ASSERT_TRUE(subnets);
|
||||
ASSERT_EQ(3, subnets->size()); // We expect 3 subnets.
|
||||
|
||||
|
||||
// Check subnet 1.
|
||||
EXPECT_EQ("192.0.2.0", subnets->at(0)->get().first.toText());
|
||||
EXPECT_EQ(24, subnets->at(0)->get().second);
|
||||
|
||||
// Check pools in the first subnet.
|
||||
const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_V4);
|
||||
ASSERT_EQ(1, pools1.size());
|
||||
EXPECT_EQ("192.0.2.1", pools1.at(0)->getFirstAddress().toText());
|
||||
EXPECT_EQ("192.0.2.100", pools1.at(0)->getLastAddress().toText());
|
||||
EXPECT_EQ(Lease::TYPE_V4, pools1.at(0)->getType());
|
||||
|
||||
// Check subnet 2.
|
||||
EXPECT_EQ("192.0.3.0", subnets->at(1)->get().first.toText());
|
||||
EXPECT_EQ(24, subnets->at(1)->get().second);
|
||||
|
||||
// Check pools in the second subnet.
|
||||
const PoolCollection& pools2 = subnets->at(1)->getPools(Lease::TYPE_V4);
|
||||
ASSERT_EQ(1, pools2.size());
|
||||
EXPECT_EQ("192.0.3.101", pools2.at(0)->getFirstAddress().toText());
|
||||
EXPECT_EQ("192.0.3.150", pools2.at(0)->getLastAddress().toText());
|
||||
EXPECT_EQ(Lease::TYPE_V4, pools2.at(0)->getType());
|
||||
|
||||
// And finally check subnet 3.
|
||||
EXPECT_EQ("192.0.4.0", subnets->at(2)->get().first.toText());
|
||||
EXPECT_EQ(24, subnets->at(2)->get().second);
|
||||
|
||||
// ... and it's only pool.
|
||||
const PoolCollection& pools3 = subnets->at(2)->getPools(Lease::TYPE_V4);
|
||||
EXPECT_EQ("192.0.4.101", pools3.at(0)->getFirstAddress().toText());
|
||||
EXPECT_EQ("192.0.4.150", pools3.at(0)->getLastAddress().toText());
|
||||
EXPECT_EQ(Lease::TYPE_V4, pools3.at(0)->getType());
|
||||
}
|
||||
|
||||
// This test checks if configuration can be read from a JSON file.
|
||||
TEST_F(JSONFileBackendTest, comments) {
|
||||
|
||||
string config_hash_comments = "# This is a comment. It should be \n"
|
||||
"#ignored. Real config starts in line below\n"
|
||||
"{ \"Dhcp4\": { \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, \n"
|
||||
"# comments in the middle should be ignored, too\n"
|
||||
"\"subnet4\": [ { "
|
||||
" \"pool\": [ \"192.0.2.0/24\" ],"
|
||||
" \"subnet\": \"192.0.2.0/22\" "
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }"
|
||||
"}";
|
||||
|
||||
/// @todo: Implement C++-style (// ...) comments
|
||||
/// @todo: Implement C-style (/* ... */) comments
|
||||
|
||||
writeFile(config_hash_comments);
|
||||
|
||||
// Now initialize the server
|
||||
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
|
||||
ASSERT_NO_THROW(
|
||||
srv.reset(new ControlledDhcpv4Srv(0))
|
||||
);
|
||||
|
||||
// And configure it using config with comments.
|
||||
EXPECT_NO_THROW(srv->init(TEST_FILE));
|
||||
|
||||
// Now check if the configuration has been applied correctly.
|
||||
const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
|
||||
ASSERT_TRUE(subnets);
|
||||
ASSERT_EQ(1, subnets->size());
|
||||
|
||||
// Check subnet 1.
|
||||
EXPECT_EQ("192.0.2.0", subnets->at(0)->get().first.toText());
|
||||
EXPECT_EQ(22, subnets->at(0)->get().second);
|
||||
|
||||
// Check pools in the first subnet.
|
||||
const PoolCollection& pools1 = subnets->at(0)->getPools(Lease::TYPE_V4);
|
||||
ASSERT_EQ(1, pools1.size());
|
||||
EXPECT_EQ("192.0.2.0", pools1.at(0)->getFirstAddress().toText());
|
||||
EXPECT_EQ("192.0.2.255", pools1.at(0)->getLastAddress().toText());
|
||||
EXPECT_EQ(Lease::TYPE_V4, pools1.at(0)->getType());
|
||||
}
|
||||
|
||||
// This test checks if configuration detects failure when trying:
|
||||
// - empty file
|
||||
// - empty filename
|
||||
// - no Dhcp4 element
|
||||
// - Config file that contains Dhcp4 but has a content error
|
||||
TEST_F(JSONFileBackendTest, configBroken) {
|
||||
|
||||
// Empty config is not allowed, because Dhcp4 element is missing
|
||||
string config_empty = "";
|
||||
|
||||
// This config does not have mandatory Dhcp4 element
|
||||
string config_v4 = "{ \"Dhcp6\": { \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
" \"pool\": [ \"2001:db8::/80\" ],"
|
||||
" \"subnet\": \"2001:db8::/64\" "
|
||||
" } ]}";
|
||||
|
||||
// This has Dhcp4 element, but it's utter nonsense
|
||||
string config_nonsense = "{ \"Dhcp4\": { \"reviews\": \"are so much fun\" } }";
|
||||
|
||||
// Now initialize the server
|
||||
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
|
||||
ASSERT_NO_THROW(
|
||||
srv.reset(new ControlledDhcpv4Srv(0))
|
||||
);
|
||||
|
||||
// Try to configure without filename. Should fail.
|
||||
EXPECT_THROW(srv->init(""), BadValue);
|
||||
|
||||
// Try to configure it using empty file. Should fail.
|
||||
writeFile(config_empty);
|
||||
EXPECT_THROW(srv->init(TEST_FILE), BadValue);
|
||||
|
||||
// Now try to load a config that does not have Dhcp4 component.
|
||||
writeFile(config_v4);
|
||||
EXPECT_THROW(srv->init(TEST_FILE), BadValue);
|
||||
|
||||
// Now try to load a config with Dhcp4 full of nonsense.
|
||||
writeFile(config_nonsense);
|
||||
EXPECT_THROW(srv->init(TEST_FILE), BadValue);
|
||||
}
|
||||
|
||||
/// This unit-test reads all files enumerated in configs-test.txt file, loads
|
||||
/// each of them and verify that they can be loaded.
|
||||
///
|
||||
/// @todo: Unfortunately, we have this test disabled, because all loaded
|
||||
/// configs use memfile, which attempts to create lease file in
|
||||
/// /usr/local/var/bind10/kea-leases4.csv. We have couple options here:
|
||||
/// a) disable persistence in example configs - a very bad thing to do
|
||||
/// as users will forget to reenable it and then will be surprised when their
|
||||
/// leases disappear
|
||||
/// b) change configs to store lease file in /tmp. It's almost as bad as the
|
||||
/// previous one. Users will then be displeased when all their leases are
|
||||
/// wiped. (most systems wipe /tmp during boot)
|
||||
/// c) read each config and rewrite it on the fly, so persistence is disabled.
|
||||
/// This is probably the way to go, but this is a work for a dedicated ticket.
|
||||
///
|
||||
/// Hence I'm leaving the test in, but it is disabled.
|
||||
TEST_F(JSONFileBackendTest, DISABLED_loadAllConfigs) {
|
||||
|
||||
// Create server first
|
||||
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
|
||||
ASSERT_NO_THROW(
|
||||
srv.reset(new ControlledDhcpv4Srv(0))
|
||||
);
|
||||
|
||||
const char* configs_list = "configs-list.txt";
|
||||
fstream configs(configs_list, ios::in);
|
||||
ASSERT_TRUE(configs.is_open());
|
||||
std::string config_name;
|
||||
while (std::getline(configs, config_name)) {
|
||||
|
||||
// Ignore empty and commented lines
|
||||
if (config_name.empty() || config_name[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unit-tests usually do not print out anything, but in this case I
|
||||
// think printing out tests configs is warranted.
|
||||
std::cout << "Loading config file " << config_name << std::endl;
|
||||
|
||||
try {
|
||||
srv->init(config_name);
|
||||
} catch (const std::exception& ex) {
|
||||
ADD_FAILURE() << "Exception thrown" << ex.what() << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
@ -220,10 +220,10 @@ void ControlledDhcpv6Srv::cleanup() {
|
||||
}
|
||||
|
||||
void
|
||||
Daemon::loggerInit(const char* log_name, bool verbose, bool stand_alone) {
|
||||
Daemon::loggerInit(const char* log_name, bool verbose) {
|
||||
isc::log::initLogger(log_name,
|
||||
(verbose ? isc::log::DEBUG : isc::log::INFO),
|
||||
isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
|
||||
isc::log::MAX_DEBUG_LEVEL, NULL, true);
|
||||
}
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
|
@ -43,8 +43,8 @@ ControlledDhcpv6Srv::commandShutdownHandler(const string&, ConstElementPtr) {
|
||||
|
||||
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.
|
||||
/// @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) {
|
||||
@ -99,9 +99,12 @@ ControlledDhcpv6Srv::processCommand(const std::string& command,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isc::data::ConstElementPtr
|
||||
ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_RECEIVED)
|
||||
.arg(config->str());
|
||||
|
||||
ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
|
||||
|
||||
if (!srv) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-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
|
||||
@ -26,12 +26,10 @@ namespace dhcp {
|
||||
|
||||
/// @brief Controlled version of the DHCPv6 server
|
||||
///
|
||||
/// This is a class that is responsible for establishing connection
|
||||
/// with msqg (receving commands and configuration). This is an extended
|
||||
/// version of Dhcpv6Srv class that is purely a DHCPv6 server, without
|
||||
/// external control. ControlledDhcpv6Srv should be used in typical BIND10
|
||||
/// (i.e. featuring msgq) environment, while Dhcpv6Srv should be used in
|
||||
/// embedded environments.
|
||||
/// This is a class that is responsible for DHCPv6 server being controllable.
|
||||
/// It does various things, depending on the configuration backend.
|
||||
/// For Bundy backend it establishes a connection with msqg and later receives
|
||||
/// commands over it. For Kea backend, it reads configuration file from disk.
|
||||
///
|
||||
/// For detailed explanation or relations between main(), ControlledDhcpv6Srv,
|
||||
/// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
|
||||
@ -53,7 +51,8 @@ public:
|
||||
/// operation. For specific details, see actual implementation in
|
||||
/// *_backend.cc
|
||||
///
|
||||
/// @return true if initialization was successful, false if it failed
|
||||
/// This method may throw if initialization fails. Exception types may be
|
||||
/// specific to used configuration backend.
|
||||
void init(const std::string& config_file);
|
||||
|
||||
/// @brief Performs cleanup, immediately before termination
|
||||
@ -77,6 +76,11 @@ public:
|
||||
/// wrapper that calls process*Command() methods and catches exceptions
|
||||
/// in them.
|
||||
///
|
||||
/// Currently supported commands are:
|
||||
/// - shutdown
|
||||
/// - libreload
|
||||
/// - config-reload
|
||||
///
|
||||
/// @note It never throws.
|
||||
///
|
||||
/// @param command Text represenation of the command (e.g. "shutdown")
|
||||
@ -88,7 +92,7 @@ public:
|
||||
|
||||
/// @brief configuration processor
|
||||
///
|
||||
/// This is a callback for handling incoming configuration updates.
|
||||
/// This is a method 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.
|
||||
///
|
||||
@ -103,7 +107,7 @@ public:
|
||||
|
||||
/// @brief returns pointer to the sole instance of Dhcpv6Srv
|
||||
///
|
||||
/// @note may return NULL, if called before server is spawned
|
||||
/// @return server instance (may return NULL, if called before server is spawned)
|
||||
static ControlledDhcpv6Srv* getInstance() {
|
||||
return (server_);
|
||||
}
|
||||
|
@ -234,24 +234,25 @@ cases it would be convenient to keep configuration in XML files.
|
||||
|
||||
Kea 0.9 introduces configuration backends that are switchable during compilation phase.
|
||||
There is a new parameter for configure script: --with-kea-config. It currently supports
|
||||
two values: BIND10 and JSON.
|
||||
two values: BUNDY and JSON.
|
||||
|
||||
BIND10 (which is the default value as of April 2014) means that Kea6 is linked with the
|
||||
BIND10 configuration backend that connects to the BIND10 framework and in general works
|
||||
BUNDY (which is the default value as of April 2014) means that Kea6 is linked with the
|
||||
Bundy (former BIND10) configuration backend that connects to the BIND10 framework and in general works
|
||||
exactly the same as Kea 0.8 and earlier versions. The benefits of that backend are uniform
|
||||
integration with BIND10 framework, easy on-line reconfiguration using bindctl, available
|
||||
RESTful API. On the other hand, it requires the whole heavy BIND10 framework that requires
|
||||
Python3 to be present. That backend is likely to go away with the release of Kea 0.9.
|
||||
integration with Bundy/BIND10 framework, easy on-line reconfiguration using bindctl, available
|
||||
RESTful API. On the other hand, it requires the whole heavy Bundy framework that requires
|
||||
Python3 to be present. That framework is going away with the release of Kea 0.9.
|
||||
|
||||
JSON is a new configuration backend that causes Kea to read JSON configuration file from
|
||||
disk. It does not require any framework and thus is considered more lightweight. It will
|
||||
allow dynamic on-line reconfiguration, but will lack remote capabilities (i.e. no RESTful
|
||||
API). This configuration backend is expected to be the default for upcoming Kea 0.9.
|
||||
API). This configuration backend is expected to be the default for upcoming Kea 0.9. It
|
||||
requires <tt> -c config-file </tt> command-line option.
|
||||
|
||||
Internally, configuration backends are implemented as different implementations of the
|
||||
isc::dhcp::ControlledDhcpv6Srv class, stored in ctrl_*_dhcpv6_srv.cc files. Depending on
|
||||
isc::dhcp::ControlledDhcpv6Srv class, stored in {kea,bundy}_controller.cc files. Depending on
|
||||
the choice made by ./configure script, only one of those files is compiled and linked.
|
||||
There are backend specific tests in src/bin/dhcp6/tests/ctrl_*_dhcpv6_srv_unittest.cc.
|
||||
There are backend specific tests in src/bin/dhcp6/tests/{kea,bundy}_controller_unittest.cc.
|
||||
Only tests specific to selected backend are linked and executed during make distcheck.
|
||||
|
||||
While it is unlikely that ISC will support more than one backend at any given time, there
|
||||
|
@ -40,6 +40,10 @@ address assignment. The most likely cause is a problem with the client.
|
||||
A debug message listing the command (and possible arguments) received
|
||||
from the BIND 10 control system by the IPv6 DHCP server.
|
||||
|
||||
% DHCP6_CONFIG_RECEIVED received configuration: %1
|
||||
A debug message listing the configuration received by the DHCPv6 server.
|
||||
The source of that configuration depends on used configuration backend.
|
||||
|
||||
% DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1
|
||||
This is an informational message announcing the successful processing of a
|
||||
new configuration. it is output during server startup, and when an updated
|
||||
@ -540,7 +544,7 @@ production environment.
|
||||
This informational message indicates that the IPv6 DHCP server has
|
||||
processed any command-line switches and is starting.
|
||||
|
||||
% DHCP6_START_INFO pid: %1, port: %2, verbose: %3, standalone: %4
|
||||
% DHCP6_START_INFO pid: %1, port: %2, verbose: %3
|
||||
This is a debug message issued during the IPv6 DHCP server startup.
|
||||
It lists some information about the parameters with which the server
|
||||
is running.
|
||||
|
@ -15,35 +15,19 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcpsrv/dhcp_config_parser.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
#include <dhcp6/json_config_parser.h>
|
||||
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
#include <dhcp6/spec_config.h>
|
||||
#include <log/logger_level.h>
|
||||
#include <log/logger_name.h>
|
||||
#include <log/logger_manager.h>
|
||||
#include <log/logger_specification.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <log/output_option.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/buffer.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::cc;
|
||||
using namespace isc::config;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::log;
|
||||
using namespace isc::util;
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
@ -74,7 +58,7 @@ void configure(const std::string& file_name) {
|
||||
}
|
||||
|
||||
// Read contents of the file and parse it as JSON
|
||||
json = Element::fromJSONFile(file_name, true);
|
||||
json = isc::data::Element::fromJSONFile(file_name, true);
|
||||
|
||||
if (!json) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL)
|
||||
@ -113,9 +97,9 @@ void configure(const std::string& file_name) {
|
||||
}
|
||||
|
||||
// Now check is the returned result is successful (rcode=0) or not
|
||||
ConstElementPtr comment; /// see @ref isc::config::parseAnswer
|
||||
isc::data::ConstElementPtr comment; /// see @ref isc::config::parseAnswer
|
||||
int rcode;
|
||||
comment = parseAnswer(rcode, result);
|
||||
comment = isc::config::parseAnswer(rcode, result);
|
||||
if (rcode != 0) {
|
||||
string reason = "";
|
||||
if (comment) {
|
||||
@ -151,7 +135,7 @@ void signalHandler(int signo) {
|
||||
.arg(file);
|
||||
}
|
||||
} else if ((signo == SIGTERM) || (signo == SIGINT)) {
|
||||
ElementPtr params(new isc::data::MapElement());
|
||||
isc::data::ElementPtr params(new isc::data::MapElement());
|
||||
ControlledDhcpv6Srv::processCommand("shutdown", params);
|
||||
}
|
||||
}
|
||||
@ -188,7 +172,7 @@ void ControlledDhcpv6Srv::cleanup() {
|
||||
/// This is a logger initialization for JSON file backend.
|
||||
/// For now, it's just setting log messages to be printed on stdout.
|
||||
/// @todo: Implement this properly (see #3427)
|
||||
void Daemon::loggerInit(const char*, bool verbose, bool ) {
|
||||
void Daemon::loggerInit(const char*, bool verbose) {
|
||||
|
||||
setenv("B10_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
|
||||
setenv("B10_LOGGER_ROOT", "kea", 0);
|
||||
|
@ -43,9 +43,8 @@ const char* const DHCP6_LOGGER_NAME = "kea";
|
||||
|
||||
void
|
||||
usage() {
|
||||
cerr << "Usage: " << DHCP6_NAME << " [-v] [-s] [-p port_number] [-c cfgfile]" << endl;
|
||||
cerr << "Usage: " << DHCP6_NAME << " [-v] [-p port_number] [-c cfgfile]" << endl;
|
||||
cerr << " -v: verbose output" << endl;
|
||||
cerr << " -s: skip configuration (don't connect to BIND10 or don't read config file)" << endl;
|
||||
cerr << " -p number: specify non-standard port number 1-65535 "
|
||||
<< "(useful for testing only)" << endl;
|
||||
cerr << " -c file: specify configuration file" << endl;
|
||||
@ -58,22 +57,17 @@ main(int argc, char* argv[]) {
|
||||
int ch;
|
||||
int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
|
||||
// useful for testing only.
|
||||
bool stand_alone = false; // Should be connect to BIND10 msgq?
|
||||
bool verbose_mode = false; // Should server be verbose?
|
||||
|
||||
// The standard config file
|
||||
std::string config_file("");
|
||||
|
||||
while ((ch = getopt(argc, argv, "vsp:c:")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "vp:c:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'v':
|
||||
verbose_mode = true;
|
||||
break;
|
||||
|
||||
case 's': // stand-alone
|
||||
stand_alone = true;
|
||||
break;
|
||||
|
||||
case 'p': // port number
|
||||
try {
|
||||
port_number = boost::lexical_cast<int>(optarg);
|
||||
@ -107,39 +101,37 @@ main(int argc, char* argv[]) {
|
||||
int ret = EXIT_SUCCESS;
|
||||
try {
|
||||
// Initialize logging. If verbose, we'll use maximum verbosity.
|
||||
// If standalone is enabled, do not buffer initial log messages
|
||||
Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode, stand_alone);
|
||||
Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
|
||||
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
|
||||
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
|
||||
.arg(stand_alone ? "yes" : "no" );
|
||||
.arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no");
|
||||
|
||||
LOG_INFO(dhcp6_logger, DHCP6_STARTING);
|
||||
|
||||
// Create the server instance.
|
||||
ControlledDhcpv6Srv server(port_number);
|
||||
|
||||
if (!stand_alone) {
|
||||
try {
|
||||
// Initialize the server, i.e. establish control session
|
||||
// if BIND10 backend is used or read a configuration file
|
||||
server.init(config_file);
|
||||
try {
|
||||
// Initialize the server, i.e. establish control session
|
||||
// if BIND10 backend is used or read a configuration file
|
||||
// if Kea backend is used.
|
||||
server.init(config_file);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what());
|
||||
} catch (const std::exception& ex) {
|
||||
LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what());
|
||||
|
||||
// We should not continue if were told to configure (either read
|
||||
// config file or establish BIND10 control session).
|
||||
isc::log::LoggerManager log_manager;
|
||||
log_manager.process();
|
||||
// We should not continue if were told to configure (either read
|
||||
// config file or establish BIND10 control session).
|
||||
isc::log::LoggerManager log_manager;
|
||||
log_manager.process();
|
||||
|
||||
cerr << "Failed to initialize server: " << ex.what() << endl;
|
||||
return (EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_STANDALONE);
|
||||
cerr << "Failed to initialize server: " << ex.what() << endl;
|
||||
return (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// And run the main loop of the server.
|
||||
server.run();
|
||||
|
||||
LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
namespace {
|
||||
|
||||
/// As of May 2014, maintaining or extending Bundy support is very low
|
||||
/// prority for Kea team. We are looking for contributors, who would
|
||||
/// like to maintain this backend.
|
||||
|
||||
// Bundy framework specific tests should be added here.
|
||||
TEST(BundyBackendTest, dummy) {
|
||||
|
||||
|
@ -210,22 +210,5 @@ class TestDhcpv6Daemon(unittest.TestCase):
|
||||
# Check that there is an error message about invalid port number printed on stderr
|
||||
self.assertEqual( str(error).count("Failed to parse port number"), 1)
|
||||
|
||||
def test_portnumber_nonroot(self):
|
||||
print("Check that specifying unprivileged port number will work.")
|
||||
|
||||
# Check that there is a message about running with an unprivileged port
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp6', '-v', '-s', '-p', '10547'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP6_OPEN_SOCKET opening sockets on port 10547"), 1)
|
||||
|
||||
def test_skip_msgq(self):
|
||||
print("Check that connection to BIND10 msgq can be disabled.")
|
||||
|
||||
# Check that the system outputs a message on one of its streams about running
|
||||
# standalone.
|
||||
(returncode, output, error) = self.runCommand(['../b10-dhcp6', '-v', '-s', '-p', '10547'])
|
||||
output_text = str(output) + str(error)
|
||||
self.assertEqual(output_text.count("DHCP6_STANDALONE"), 1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -100,8 +100,12 @@ public:
|
||||
|
||||
/// Initializes logger
|
||||
///
|
||||
/// This method initializes logger.
|
||||
static void loggerInit(const char* log_name, bool verbose, bool stand_alone);
|
||||
/// This method initializes logger. Currently its implementation is specific
|
||||
/// to each configuration backend.
|
||||
///
|
||||
/// @param log_name name used in logger initialization
|
||||
/// @param verbose verbose mode (true usually enables DEBUG messages)
|
||||
static void loggerInit(const char* log_name, bool verbose);
|
||||
|
||||
private:
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user