mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 05:27:55 +00:00
[master] Implement set-config command in kea-dhcp4/6
Merges in branch 'trac5046'
This commit is contained in:
commit
4afbdcf89c
@ -184,6 +184,65 @@ will be sent to Kea and the responses received from Kea printed to standard outp
|
||||
</para>
|
||||
</section> <!-- end of command-list-commands -->
|
||||
|
||||
<section id="command-set-config">
|
||||
<title>set-config</title>
|
||||
|
||||
<para>
|
||||
The <emphasis>set-config</emphasis> command instructs the server to replace
|
||||
its current configuration with the new configuration supplied in the
|
||||
command's arguments. The supplied configuration is expected to be the full
|
||||
configuration for the target server along with an optional Logger
|
||||
configuration. While optional, the Logger configuration is highly
|
||||
recommended as without it the server will revert to its default logging
|
||||
configuration. The structure of the command is as follows:
|
||||
</para>
|
||||
<screen>
|
||||
{
|
||||
"command": "set-config",
|
||||
"arguments": {
|
||||
"<server>": {
|
||||
},
|
||||
"Logging": {
|
||||
}
|
||||
}
|
||||
}
|
||||
</screen>
|
||||
<para>
|
||||
where <server> is the configuration element name for a given server
|
||||
such as "Dhcp4" or "Dhcp6". For example:
|
||||
</para>
|
||||
<screen>
|
||||
{
|
||||
"command": "set-config",
|
||||
"arguments": {
|
||||
"Dhcp6": {
|
||||
:
|
||||
},
|
||||
"Logging": {
|
||||
:
|
||||
}
|
||||
}
|
||||
}
|
||||
</screen>
|
||||
<para>
|
||||
If the new configuration proves to be invalid the server will retain
|
||||
its current configuration. Please note that the new configuration is
|
||||
retained in memory only. If the server is restarted or a configuration
|
||||
reload is triggered via a signal, the server will use the configuration
|
||||
stored in its configuration file.
|
||||
|
||||
The server's response will contain a numeric code, "result" (0 for success,
|
||||
non-zero on failure), and a string, "text", describing the outcome:
|
||||
<screen>
|
||||
{"result": 0, "text": "Configuration successful." }
|
||||
|
||||
or
|
||||
|
||||
{"result": 1, "text": "unsupported parameter: BOGUS (<string>:16:26)" }
|
||||
</screen>
|
||||
</para>
|
||||
</section> <!-- end of command-set-config -->
|
||||
|
||||
<section id="command-shutdown">
|
||||
<title>shutdown</title>
|
||||
|
||||
@ -205,6 +264,8 @@ will be sent to Kea and the responses received from Kea printed to standard outp
|
||||
</para>
|
||||
</section> <!-- end of command-shutdown -->
|
||||
|
||||
|
||||
|
||||
</section> <!-- end of commands supported by both servers -->
|
||||
|
||||
</chapter>
|
||||
|
@ -3649,17 +3649,30 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
|
||||
|
||||
<para>
|
||||
Communication over control channel is conducted using JSON structures.
|
||||
See the Control Channel section in the Kea Developer's Guide for more details.
|
||||
See the Control Channel section in the Kea Developer's Guide for more
|
||||
details.
|
||||
</para>
|
||||
|
||||
<para>The DHCPv4 server supports the following operational commands:
|
||||
<itemizedlist>
|
||||
<listitem>leases-reclaim</listitem>
|
||||
<listitem>list-commands</listitem>
|
||||
<listitem>set-config</listitem>
|
||||
<listitem>shutdown</listitem>
|
||||
</itemizedlist>
|
||||
as described in <xref linkend="commands-common"/>. In addition,
|
||||
it supports the following statistics related commands:
|
||||
<itemizedlist>
|
||||
<listitem>statistic-get</listitem>
|
||||
<listitem>statistic-reset</listitem>
|
||||
<listitem>statistic-remove</listitem>
|
||||
<listitem>statistic-get-all</listitem>
|
||||
<listitem>statistic-reset-all</listitem>
|
||||
<listitem>statistic-remove-all</listitem>
|
||||
</itemizedlist>
|
||||
as described here <xref linkend="command-stats"/>.
|
||||
</para>
|
||||
|
||||
<para>The DHCPv4 server supports <command>statistic-get</command>,
|
||||
<command>statistic-reset</command>, <command>statistic-remove</command>,
|
||||
<command>statistic-get-all</command>, <command>statistic-reset-all</command>
|
||||
and <command>statistic-remove-all</command>, specified in
|
||||
<xref linkend="command-stats"/>. It also supports
|
||||
<command>list-commands</command> and <command>shutdown</command>,
|
||||
specified in <xref linkend="command-list-commands" /> and
|
||||
<xref linkend="command-shutdown" />, respectively.</para>
|
||||
</section>
|
||||
|
||||
<section id="dhcp4-std">
|
||||
|
@ -4062,16 +4062,27 @@ If not specified, the default value is:
|
||||
See the Control Channel section in the Kea Developer's Guide for more details.
|
||||
</para>
|
||||
|
||||
<para>The DHCPv6 server supports <command>statistic-get</command>,
|
||||
<command>statistic-reset</command>, <command>statistic-remove</command>,
|
||||
<command>statistic-get-all</command>, <command>statistic-reset-all</command>
|
||||
and <command>statistic-remove-all</command>, specified in
|
||||
<xref linkend="command-stats"/>. It also supports
|
||||
<command>list-commands</command> and <command>shutdown</command>,
|
||||
specified in <xref linkend="command-list-commands" /> and
|
||||
<xref linkend="command-shutdown" />, respectively.</para>
|
||||
</section>
|
||||
<para>The DHCPv6 server supports the following operational commands:
|
||||
<itemizedlist>
|
||||
<listitem>leases-reclaim</listitem>
|
||||
<listitem>list-commands</listitem>
|
||||
<listitem>set-config</listitem>
|
||||
<listitem>shutdown</listitem>
|
||||
</itemizedlist>
|
||||
as described in <xref linkend="commands-common"/>. In addition,
|
||||
it supports the following statistics related commands:
|
||||
<itemizedlist>
|
||||
<listitem>statistic-get</listitem>
|
||||
<listitem>statistic-reset</listitem>
|
||||
<listitem>statistic-remove</listitem>
|
||||
<listitem>statistic-get-all</listitem>
|
||||
<listitem>statistic-reset-all</listitem>
|
||||
<listitem>statistic-remove-all</listitem>
|
||||
</itemizedlist>
|
||||
as described here <xref linkend="command-stats"/>.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>User context in IPv6 pools</title>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -62,7 +62,69 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::commandConfigReloadHandler(const string&,
|
||||
ConstElementPtr args) {
|
||||
return (processConfig(args));
|
||||
// Use set-config as it handles logging and server config
|
||||
return (commandSetConfigHandler("set-config", args));
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv4Srv::commandSetConfigHandler(const string&,
|
||||
ConstElementPtr args) {
|
||||
const int status_code = 1; // 1 indicates an error
|
||||
ConstElementPtr dhcp4;
|
||||
string message;
|
||||
|
||||
// Command arguments are expected to be:
|
||||
// { "Dhcp4": { ... }, "Logging": { ... } }
|
||||
// The Logging component is technically optional. If it's not supplied
|
||||
// logging will revert to default logging.
|
||||
if (!args) {
|
||||
message = "Missing mandatory 'arguments' parameter.";
|
||||
} else {
|
||||
dhcp4 = args->get("Dhcp4");
|
||||
if (!dhcp4) {
|
||||
message = "Missing mandatory 'Dhcp4' parameter.";
|
||||
} else if (dhcp4->getType() != Element::map) {
|
||||
message = "'Dhcp4' parameter expected to be a map.";
|
||||
}
|
||||
}
|
||||
|
||||
if (!message.empty()) {
|
||||
// Something is amiss with arguments, return a failure response.
|
||||
ConstElementPtr result = isc::config::createAnswer(status_code,
|
||||
message);
|
||||
return (result);
|
||||
}
|
||||
|
||||
// We are starting the configuration process so we should remove any
|
||||
// staging configuration that has been created during previous
|
||||
// configuration attempts.
|
||||
CfgMgr::instance().rollback();
|
||||
|
||||
// Logging is a sibling element and must be parsed explicitly.
|
||||
// The call to configureLogger parses the given Logging element if
|
||||
// not null, into the staging config. Note this does not alter the
|
||||
// current loggers, they remain in effect until we apply the
|
||||
// logging config below. If no logging is supplied logging will
|
||||
// revert to default logging.
|
||||
Daemon::configureLogger(args->get("Logging"),
|
||||
CfgMgr::instance().getStagingCfg());
|
||||
|
||||
// Now we configure the server proper.
|
||||
ConstElementPtr result = processConfig(dhcp4);
|
||||
|
||||
// If the configuration parsed successfully, apply the new logger
|
||||
// configuration and the commit the new configuration. We apply
|
||||
// the logging first in case there's a configuration failure.
|
||||
int rcode = 0;
|
||||
isc::config::parseAnswer(rcode, result);
|
||||
if (rcode == 0) {
|
||||
CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
|
||||
|
||||
// Use new configuration.
|
||||
CfgMgr::instance().commit();
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
@ -116,6 +178,9 @@ ControlledDhcpv4Srv::processCommand(const string& command,
|
||||
} else if (command == "config-reload") {
|
||||
return (srv->commandConfigReloadHandler(command, args));
|
||||
|
||||
} else if (command == "set-config") {
|
||||
return (srv->commandSetConfigHandler(command, args));
|
||||
|
||||
} else if (command == "leases-reclaim") {
|
||||
return (srv->commandLeasesReclaimHandler(command, args));
|
||||
}
|
||||
@ -235,8 +300,6 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (answer);
|
||||
}
|
||||
|
||||
@ -257,6 +320,9 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
|
||||
CommandMgr::instance().registerCommand("libreload",
|
||||
boost::bind(&ControlledDhcpv4Srv::commandLibReloadHandler, this, _1, _2));
|
||||
|
||||
CommandMgr::instance().registerCommand("set-config",
|
||||
boost::bind(&ControlledDhcpv4Srv::commandSetConfigHandler, this, _1, _2));
|
||||
|
||||
CommandMgr::instance().registerCommand("leases-reclaim",
|
||||
boost::bind(&ControlledDhcpv4Srv::commandLeasesReclaimHandler, this, _1, _2));
|
||||
|
||||
@ -300,6 +366,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
|
||||
// Deregister any registered commands
|
||||
CommandMgr::instance().deregisterCommand("shutdown");
|
||||
CommandMgr::instance().deregisterCommand("libreload");
|
||||
CommandMgr::instance().deregisterCommand("set-config");
|
||||
CommandMgr::instance().deregisterCommand("leases-reclaim");
|
||||
CommandMgr::instance().deregisterCommand("statistic-get");
|
||||
CommandMgr::instance().deregisterCommand("statistic-reset");
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -143,6 +143,19 @@ private:
|
||||
commandConfigReloadHandler(const std::string& command,
|
||||
isc::data::ConstElementPtr args);
|
||||
|
||||
/// @brief handler for processing 'set-config' command
|
||||
///
|
||||
/// This handler processes set-config command, which processes
|
||||
/// configuration specified in args parameter.
|
||||
/// @param command (parameter ignored)
|
||||
/// @param args configuration to be processed. Expected format:
|
||||
/// map containing Dhcp4 map that contains DHCPv4 server configuration.
|
||||
/// May also contain Logging map that specifies logging configuration.
|
||||
///
|
||||
/// @return status of the command
|
||||
isc::data::ConstElementPtr
|
||||
commandSetConfigHandler(const std::string& command,
|
||||
isc::data::ConstElementPtr args);
|
||||
|
||||
/// @brief Handler for processing 'leases-reclaim' command
|
||||
///
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -497,6 +497,47 @@ void setGlobalParameters4() {
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Initialize the command channel based on the staging configuration
|
||||
///
|
||||
/// Only close the current channel, if the new channel configuration is
|
||||
/// different. This avoids disconnecting a client and hence not sending them
|
||||
/// a command result, unless they specifically alter the channel configuration.
|
||||
/// In that case the user simply has to accept they'll be disconnected.
|
||||
///
|
||||
void configureCommandChannel() {
|
||||
// Get new socket configuration.
|
||||
ConstElementPtr sock_cfg =
|
||||
CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
|
||||
|
||||
// Get current socket configuration.
|
||||
ConstElementPtr current_sock_cfg =
|
||||
CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
|
||||
|
||||
// Determine if the socket configuration has changed. It has if
|
||||
// both old and new configuration is specified but respective
|
||||
// data elements are't equal.
|
||||
bool sock_changed = (sock_cfg && current_sock_cfg &&
|
||||
!sock_cfg->equals(*current_sock_cfg));
|
||||
|
||||
// If the previous or new socket configuration doesn't exist or
|
||||
// the new configuration differs from the old configuration we
|
||||
// close the exisitng socket and open a new socket as appropriate.
|
||||
// Note that closing an existing socket means the clien will not
|
||||
// receive the configuration result.
|
||||
if (!sock_cfg || !current_sock_cfg || sock_changed) {
|
||||
// Close the existing socket (if any).
|
||||
isc::config::CommandMgr::instance().closeCommandSocket();
|
||||
|
||||
if (sock_cfg) {
|
||||
// This will create a control socket and install the external
|
||||
// socket in IfaceMgr. That socket will be monitored when
|
||||
// Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
|
||||
// callback in CommandMgr will be called, if necessary.
|
||||
isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isc::data::ConstElementPtr
|
||||
configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
if (!config_set) {
|
||||
@ -646,25 +687,8 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
subnet_parser->build(subnet_config->second);
|
||||
}
|
||||
|
||||
// Get command socket configuration from the config file.
|
||||
// This code expects the following structure:
|
||||
// {
|
||||
// "socket-type": "unix",
|
||||
// "socket-name": "/tmp/kea4.sock"
|
||||
// }
|
||||
ConstElementPtr sock_cfg =
|
||||
CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
|
||||
|
||||
// Close existing socket (if any).
|
||||
isc::config::CommandMgr::instance().closeCommandSocket();
|
||||
if (sock_cfg) {
|
||||
// This will create a control socket and will install external socket
|
||||
// in IfaceMgr. That socket will be monitored when Dhcp4Srv::receivePacket()
|
||||
// calls IfaceMgr::receive4() and callback in CommandMgr will be called,
|
||||
// if necessary. If there were previously open command socket, it will
|
||||
// be closed.
|
||||
isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
|
||||
}
|
||||
// Setup the command channel.
|
||||
configureCommandChannel();
|
||||
|
||||
// the leases database parser is the last to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator leases_config =
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -34,11 +34,6 @@ void configure(const std::string& file_name) {
|
||||
// This is a configuration backend implementation that reads the
|
||||
// configuration from a JSON file.
|
||||
|
||||
// We are starting the configuration process so we should remove any
|
||||
// staging configuration that has been created during previous
|
||||
// configuration attempts.
|
||||
CfgMgr::instance().rollback();
|
||||
|
||||
isc::data::ConstElementPtr json;
|
||||
isc::data::ConstElementPtr dhcp4;
|
||||
isc::data::ConstElementPtr logger;
|
||||
@ -70,26 +65,14 @@ void configure(const std::string& file_name) {
|
||||
" Did you forget to add { } around your configuration?");
|
||||
}
|
||||
|
||||
// If there's no logging element, we'll just pass NULL pointer,
|
||||
// which will be handled by configureLogger().
|
||||
Daemon::configureLogger(json->get("Logging"),
|
||||
CfgMgr::instance().getStagingCfg());
|
||||
|
||||
// Get Dhcp4 component from the config
|
||||
dhcp4 = json->get("Dhcp4");
|
||||
if (!dhcp4) {
|
||||
isc_throw(isc::BadValue, "no mandatory 'Dhcp4' entry in"
|
||||
" the configuration");
|
||||
}
|
||||
|
||||
// Use parsed JSON structures to configure the server
|
||||
result = ControlledDhcpv4Srv::processCommand("config-reload", dhcp4);
|
||||
result = ControlledDhcpv4Srv::processCommand("set-config", json);
|
||||
if (!result) {
|
||||
// Undetermined status of the configuration. This should never
|
||||
// happen, but as the configureDhcp4Server returns a pointer, it is
|
||||
// theoretically possible that it will return NULL.
|
||||
isc_throw(isc::BadValue, "undefined result of "
|
||||
"processCommand(\"config-reload\", dhcp4)");
|
||||
"processCommand(\"set-config\", json)");
|
||||
}
|
||||
|
||||
// Now check is the returned result is successful (rcode=0) or not
|
||||
@ -102,17 +85,6 @@ void configure(const std::string& file_name) {
|
||||
"no details available";
|
||||
isc_throw(isc::BadValue, reason);
|
||||
}
|
||||
|
||||
// If configuration was parsed successfully, apply the new logger
|
||||
// configuration to log4cplus. It is done before commit in case
|
||||
// something goes wrong.
|
||||
CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
|
||||
|
||||
// Use new configuration.
|
||||
/// @todo: This commit should be moved to
|
||||
/// CtrlDhcp4Srv::commandConfigReloadHandler.
|
||||
CfgMgr::instance().commit();
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
// If configuration failed at any stage, we drop the staging
|
||||
// configuration and continue to use the previous one.
|
||||
@ -167,7 +139,7 @@ ControlledDhcpv4Srv::init(const std::string& file_name) {
|
||||
|
||||
// We don't need to call openActiveSockets() or startD2() as these
|
||||
// methods are called in processConfig() which is called by
|
||||
// processCommand("reload-config", ...)
|
||||
// processCommand("set-config", ...)
|
||||
|
||||
// Set signal handlers. When the SIGHUP is received by the process
|
||||
// the server reconfiguration will be triggered. When SIGTERM or
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -15,7 +15,9 @@
|
||||
#include <dhcpsrv/lease.h>
|
||||
#include <dhcpsrv/lease_mgr_factory.h>
|
||||
#include <hooks/hooks_manager.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <stats/stats_mgr.h>
|
||||
#include <testutils/io_utils.h>
|
||||
#include <testutils/unix_control_client.h>
|
||||
|
||||
#include "marker_file.h"
|
||||
@ -121,6 +123,13 @@ public:
|
||||
ConstElementPtr config;
|
||||
ASSERT_NO_THROW(config = parseDHCP4(config_txt));
|
||||
ConstElementPtr answer = server_->processConfig(config);
|
||||
|
||||
// Commit the configuration so any subsequent reconfigurations
|
||||
// will only close the command channel if its configuration has
|
||||
// changed.
|
||||
CfgMgr::instance().commit();
|
||||
|
||||
|
||||
ASSERT_TRUE(answer);
|
||||
|
||||
int status = 0;
|
||||
@ -472,4 +481,147 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) {
|
||||
response);
|
||||
}
|
||||
|
||||
// Check that the "set-config" command will replace current configuration
|
||||
TEST_F(CtrlChannelDhcpv4SrvTest, set_config) {
|
||||
createUnixChannelServer();
|
||||
|
||||
// Define strings to permutate the config arguments
|
||||
// (Note the line feeds makes errors easy to find)
|
||||
string set_config_txt = "{ \"command\": \"set-config\" \n";
|
||||
string args_txt = " \"arguments\": { \n";
|
||||
string dhcp4_cfg_txt =
|
||||
" \"Dhcp4\": { \n"
|
||||
" \"interfaces-config\": { \n"
|
||||
" \"interfaces\": [\"*\"] \n"
|
||||
" }, \n"
|
||||
" \"valid-lifetime\": 4000, \n"
|
||||
" \"renew-timer\": 1000, \n"
|
||||
" \"rebind-timer\": 2000, \n"
|
||||
" \"lease-database\": { \n"
|
||||
" \"type\": \"memfile\", \n"
|
||||
" \"persist\":false, \n"
|
||||
" \"lfc-interval\": 0 \n"
|
||||
" }, \n"
|
||||
" \"expired-leases-processing\": { \n"
|
||||
" \"reclaim-timer-wait-time\": 0, \n"
|
||||
" \"hold-reclaimed-time\": 0, \n"
|
||||
" \"flush-reclaimed-timer-wait-time\": 0 \n"
|
||||
" },"
|
||||
" \"subnet4\": [ \n";
|
||||
string subnet1 =
|
||||
" {\"subnet\": \"192.2.0.0/24\", \n"
|
||||
" \"pools\": [{ \"pool\": \"192.2.0.1-192.2.0.50\" }]}\n";
|
||||
string subnet2 =
|
||||
" {\"subnet\": \"192.2.1.0/24\", \n"
|
||||
" \"pools\": [{ \"pool\": \"192.2.1.1-192.2.1.50\" }]}\n";
|
||||
string bad_subnet =
|
||||
" {\"BOGUS\": \"192.2.2.0/24\", \n"
|
||||
" \"pools\": [{ \"pool\": \"192.2.2.1-192.2.2.50\" }]}\n";
|
||||
string subnet_footer =
|
||||
" ] \n";
|
||||
string control_socket_header =
|
||||
" ,\"control-socket\": { \n"
|
||||
" \"socket-type\": \"unix\", \n"
|
||||
" \"socket-name\": \"";
|
||||
string control_socket_footer =
|
||||
"\" \n} \n";
|
||||
string logger_txt =
|
||||
" \"Logging\": { \n"
|
||||
" \"loggers\": [ { \n"
|
||||
" \"name\": \"kea\", \n"
|
||||
" \"severity\": \"FATAL\", \n"
|
||||
" \"output_options\": [{ \n"
|
||||
" \"output\": \"/dev/null\" \n"
|
||||
" }] \n"
|
||||
" }] \n"
|
||||
" } \n";
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
// Create a valid config with all the parts should parse
|
||||
os << set_config_txt << ","
|
||||
<< args_txt
|
||||
<< dhcp4_cfg_txt
|
||||
<< subnet1
|
||||
<< subnet_footer
|
||||
<< control_socket_header
|
||||
<< socket_path_
|
||||
<< control_socket_footer
|
||||
<< "}\n" // close dhcp4
|
||||
<< ","
|
||||
<< logger_txt
|
||||
<< "}}";
|
||||
|
||||
// Send the set-config command
|
||||
std::string response;
|
||||
sendUnixCommand(os.str(), response);
|
||||
|
||||
// Verify the configuration was successful.
|
||||
EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
|
||||
response);
|
||||
|
||||
// Check that the config was indeed applied.
|
||||
const Subnet4Collection* subnets =
|
||||
CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
|
||||
EXPECT_EQ(1, subnets->size());
|
||||
|
||||
// Create a config with malformed subnet that should fail to parse.
|
||||
os.str("");
|
||||
os << set_config_txt << ","
|
||||
<< args_txt
|
||||
<< dhcp4_cfg_txt
|
||||
<< bad_subnet
|
||||
<< subnet_footer
|
||||
<< control_socket_header
|
||||
<< socket_path_
|
||||
<< control_socket_footer
|
||||
<< "}\n" // close dhcp4
|
||||
"}}";
|
||||
|
||||
// Send the set-config command
|
||||
sendUnixCommand(os.str(), response);
|
||||
|
||||
// Should fail with a syntax error
|
||||
EXPECT_EQ("{ \"result\": 1, "
|
||||
"\"text\": \"unsupported parameter: BOGUS (<string>:20:26)\" }",
|
||||
response);
|
||||
|
||||
// Check that the config was not lost
|
||||
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
|
||||
EXPECT_EQ(1, subnets->size());
|
||||
|
||||
// Create a valid config with two subnets and no command channel.
|
||||
// It should succeed, client should still receive the response
|
||||
os.str("");
|
||||
os << set_config_txt << ","
|
||||
<< args_txt
|
||||
<< dhcp4_cfg_txt
|
||||
<< subnet1
|
||||
<< ",\n"
|
||||
<< subnet2
|
||||
<< subnet_footer
|
||||
<< "}\n" // close dhcp4
|
||||
<< "}}";
|
||||
|
||||
/* Verify the control channel socket exists */
|
||||
ASSERT_TRUE(fileExists(socket_path_));
|
||||
|
||||
// Send the set-config command
|
||||
sendUnixCommand(os.str(), response);
|
||||
|
||||
/* Verify the control channel socket no longer exists */
|
||||
EXPECT_FALSE(fileExists(socket_path_));
|
||||
|
||||
// With no command channel, should still receive the response.
|
||||
EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
|
||||
response);
|
||||
|
||||
// Check that the config was not lost
|
||||
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll();
|
||||
EXPECT_EQ(2, subnets->size());
|
||||
|
||||
// Clean up after the test.
|
||||
CfgMgr::instance().clear();
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -24,6 +24,7 @@
|
||||
#include <dhcpsrv/lease.h>
|
||||
#include <dhcpsrv/lease_mgr.h>
|
||||
#include <dhcpsrv/lease_mgr_factory.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <stats/stats_mgr.h>
|
||||
|
||||
using namespace std;
|
||||
@ -47,6 +48,9 @@ BaseServerTest::~BaseServerTest() {
|
||||
|
||||
// Revert to original data directory.
|
||||
CfgMgr::instance().setDataDir(original_datadir_);
|
||||
|
||||
// Revert to unit test logging, in case the test reconfigured it.
|
||||
isc::log::initLogger();
|
||||
}
|
||||
|
||||
Dhcpv4SrvTest::Dhcpv4SrvTest()
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -63,7 +63,6 @@ public:
|
||||
|
||||
~JSONFileBackendTest() {
|
||||
LeaseMgrFactory::destroy();
|
||||
isc::log::setDefaultLoggingOutput();
|
||||
static_cast<void>(remove(TEST_FILE));
|
||||
static_cast<void>(remove(TEST_INCLUDE));
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -67,10 +67,72 @@ ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv6Srv::commandConfigReloadHandler(const string&, ConstElementPtr args) {
|
||||
|
||||
return (processConfig(args));
|
||||
// Use set-config as it handles logging and server config
|
||||
return (commandSetConfigHandler("set-config", args));
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv6Srv::commandSetConfigHandler(const string&,
|
||||
ConstElementPtr args) {
|
||||
const int status_code = 1; // 1 indicates an error
|
||||
ConstElementPtr dhcp6;
|
||||
string message;
|
||||
|
||||
// Command arguments are expected to be:
|
||||
// { "Dhcp6": { ... }, "Logging": { ... } }
|
||||
// The Logging component is technically optional. If it's not supplied
|
||||
// logging will revert to default logging.
|
||||
if (!args) {
|
||||
message = "Missing mandatory 'arguments' parameter.";
|
||||
} else {
|
||||
dhcp6 = args->get("Dhcp6");
|
||||
if (!dhcp6) {
|
||||
message = "Missing mandatory 'Dhcp6' parameter.";
|
||||
} else if (dhcp6->getType() != Element::map) {
|
||||
message = "'Dhcp6' parameter expected to be a map.";
|
||||
}
|
||||
}
|
||||
|
||||
if (!message.empty()) {
|
||||
// Something is amiss with arguments, return a failure response.
|
||||
ConstElementPtr result = isc::config::createAnswer(status_code,
|
||||
message);
|
||||
return (result);
|
||||
}
|
||||
|
||||
// We are starting the configuration process so we should remove any
|
||||
// staging configuration that has been created during previous
|
||||
// configuration attempts.
|
||||
CfgMgr::instance().rollback();
|
||||
|
||||
// Logging is a sibling element and must be parsed explicitly.
|
||||
// The call to configureLogger parses the given Logging element if
|
||||
// not null, into the staging config. Note this does not alter the
|
||||
// current loggers, they remain in effect until we apply the
|
||||
// logging config below. If no logging is supplied logging will
|
||||
// revert to default logging.
|
||||
Daemon::configureLogger(args->get("Logging"),
|
||||
CfgMgr::instance().getStagingCfg());
|
||||
|
||||
// Now we configure the server proper.
|
||||
ConstElementPtr result = processConfig(dhcp6);
|
||||
|
||||
// If the configuration parsed successfully, apply the new logger
|
||||
// configuration and the commit the new configuration. We apply
|
||||
// the logging first in case there's a configuration failure.
|
||||
int rcode = 0;
|
||||
isc::config::parseAnswer(rcode, result);
|
||||
if (rcode == 0) {
|
||||
CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
|
||||
|
||||
// Use new configuration.
|
||||
CfgMgr::instance().commit();
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
|
||||
ConstElementPtr
|
||||
ControlledDhcpv6Srv::commandLeasesReclaimHandler(const string&,
|
||||
ConstElementPtr args) {
|
||||
@ -116,12 +178,17 @@ ControlledDhcpv6Srv::processCommand(const std::string& command,
|
||||
if (command == "shutdown") {
|
||||
return (srv->commandShutdownHandler(command, args));
|
||||
|
||||
/// @todo: register config-reload (see CtrlDhcpv6Srv::commandConfigReloadHandler)
|
||||
|
||||
} else if (command == "libreload") {
|
||||
return (srv->commandLibReloadHandler(command, args));
|
||||
|
||||
} else if (command == "config-reload") {
|
||||
return (srv->commandConfigReloadHandler(command, args));
|
||||
|
||||
} else if (command == "set-config") {
|
||||
return (srv->commandSetConfigHandler(command, args));
|
||||
|
||||
} else if (command == "leases-reclaim") {
|
||||
return (srv->commandLeasesReclaimHandler(command, args));
|
||||
}
|
||||
@ -279,11 +346,12 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)
|
||||
CommandMgr::instance().registerCommand("shutdown",
|
||||
boost::bind(&ControlledDhcpv6Srv::commandShutdownHandler, this, _1, _2));
|
||||
|
||||
/// @todo: register config-reload (see CtrlDhcpv4Srv::commandConfigReloadHandler)
|
||||
|
||||
CommandMgr::instance().registerCommand("libreload",
|
||||
boost::bind(&ControlledDhcpv6Srv::commandLibReloadHandler, this, _1, _2));
|
||||
|
||||
CommandMgr::instance().registerCommand("set-config",
|
||||
boost::bind(&ControlledDhcpv6Srv::commandSetConfigHandler, this, _1, _2));
|
||||
|
||||
CommandMgr::instance().registerCommand("leases-reclaim",
|
||||
boost::bind(&ControlledDhcpv6Srv::commandLeasesReclaimHandler, this, _1, _2));
|
||||
|
||||
@ -327,6 +395,7 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
|
||||
// Deregister any registered commands
|
||||
CommandMgr::instance().deregisterCommand("shutdown");
|
||||
CommandMgr::instance().deregisterCommand("libreload");
|
||||
CommandMgr::instance().deregisterCommand("set-config");
|
||||
CommandMgr::instance().deregisterCommand("leases-reclaim");
|
||||
CommandMgr::instance().deregisterCommand("statistic-get");
|
||||
CommandMgr::instance().deregisterCommand("statistic-reset");
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -143,6 +143,20 @@ private:
|
||||
commandConfigReloadHandler(const std::string& command,
|
||||
isc::data::ConstElementPtr args);
|
||||
|
||||
/// @brief handler for processing 'set-config' command
|
||||
///
|
||||
/// This handler processes set-config command, which processes
|
||||
/// configuration specified in args parameter.
|
||||
/// @param command (parameter ignored)
|
||||
/// @param args configuration to be processed. Expected format:
|
||||
/// map containing Dhcp6 map that contains DHCPv6 server configuration.
|
||||
/// May also contain Logging map that specifies logging configuration.
|
||||
///
|
||||
/// @return status of the command
|
||||
isc::data::ConstElementPtr
|
||||
commandSetConfigHandler(const std::string& command,
|
||||
isc::data::ConstElementPtr args);
|
||||
|
||||
/// @brief Handler for processing 'leases-reclaim' command
|
||||
///
|
||||
/// This handler processes leases-reclaim command, which triggers
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -770,6 +770,47 @@ void setGlobalParameters6() {
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Initialize the command channel based on the staging configuration
|
||||
///
|
||||
/// Only close the current channel, if the new channel configuration is
|
||||
/// different. This avoids disconnecting a client and hence not sending them
|
||||
/// a command result, unless they specifically alter the channel configuration.
|
||||
/// In that case the user simply has to accept they'll be disconnected.
|
||||
///
|
||||
void configureCommandChannel() {
|
||||
// Get new socket configuration.
|
||||
ConstElementPtr sock_cfg =
|
||||
CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
|
||||
|
||||
// Get current socket configuration.
|
||||
ConstElementPtr current_sock_cfg =
|
||||
CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
|
||||
|
||||
// Determine if the socket configuration has changed. It has if
|
||||
// both old and new configuration is specified but respective
|
||||
// data elements are't equal.
|
||||
bool sock_changed = (sock_cfg && current_sock_cfg &&
|
||||
!sock_cfg->equals(*current_sock_cfg));
|
||||
|
||||
// If the previous or new socket configuration doesn't exist or
|
||||
// the new configuration differs from the old configuration we
|
||||
// close the exisitng socket and open a new socket as appropriate.
|
||||
// Note that closing an existing socket means the clien will not
|
||||
// receive the configuration result.
|
||||
if (!sock_cfg || !current_sock_cfg || sock_changed) {
|
||||
// Close the existing socket (if any).
|
||||
isc::config::CommandMgr::instance().closeCommandSocket();
|
||||
|
||||
if (sock_cfg) {
|
||||
// This will create a control socket and install the external
|
||||
// socket in IfaceMgr. That socket will be monitored when
|
||||
// Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
|
||||
// callback in CommandMgr will be called, if necessary.
|
||||
isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isc::data::ConstElementPtr
|
||||
configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
if (!config_set) {
|
||||
@ -921,25 +962,8 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
subnet_parser->build(subnet_config->second);
|
||||
}
|
||||
|
||||
// Get command socket configuration from the config file.
|
||||
// This code expects the following structure:
|
||||
// {
|
||||
// "socket-type": "unix",
|
||||
// "socket-name": "/tmp/kea6.sock"
|
||||
// }
|
||||
ConstElementPtr sock_cfg =
|
||||
CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
|
||||
|
||||
// Close existing socket (if any).
|
||||
isc::config::CommandMgr::instance().closeCommandSocket();
|
||||
if (sock_cfg) {
|
||||
// This will create a control socket and will install external socket
|
||||
// in IfaceMgr. That socket will be monitored when Dhcp4Srv::receivePacket()
|
||||
// calls IfaceMgr::receive4() and callback in CommandMgr will be called,
|
||||
// if necessary. If there were previously open command socket, it will
|
||||
// be closed.
|
||||
isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
|
||||
}
|
||||
// Setup the command channel.
|
||||
configureCommandChannel();
|
||||
|
||||
// The lease database parser is the last to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator leases_config =
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -38,11 +38,6 @@ void configure(const std::string& file_name) {
|
||||
// This is a configuration backend implementation that reads the
|
||||
// configuration from a JSON file.
|
||||
|
||||
// We are starting the configuration process so we should remove any
|
||||
// staging configuration that has been created during previous
|
||||
// configuration attempts.
|
||||
CfgMgr::instance().rollback();
|
||||
|
||||
isc::data::ConstElementPtr json;
|
||||
isc::data::ConstElementPtr dhcp6;
|
||||
isc::data::ConstElementPtr logger;
|
||||
@ -74,29 +69,14 @@ void configure(const std::string& file_name) {
|
||||
" Did you forget to add { } around your configuration?");
|
||||
}
|
||||
|
||||
// Let's configure logging before applying the configuration,
|
||||
// so we can log things during configuration process.
|
||||
// If there's no logging element, we'll just pass NULL pointer,
|
||||
// which will be handled by configureLogger().
|
||||
Daemon::configureLogger(json->get("Logging"),
|
||||
CfgMgr::instance().getStagingCfg());
|
||||
|
||||
// Get Dhcp6 component from the config
|
||||
dhcp6 = json->get("Dhcp6");
|
||||
|
||||
if (!dhcp6) {
|
||||
isc_throw(isc::BadValue, "no mandatory 'Dhcp6' entry in"
|
||||
" the configuration");
|
||||
}
|
||||
|
||||
// Use parsed JSON structures to configure the server
|
||||
result = ControlledDhcpv6Srv::processCommand("config-reload", dhcp6);
|
||||
result = ControlledDhcpv6Srv::processCommand("set-config", json);
|
||||
if (!result) {
|
||||
// Undetermined status of the configuration. This should never
|
||||
// happen, but as the configureDhcp6Server returns a pointer, it is
|
||||
// theoretically possible that it will return NULL.
|
||||
isc_throw(isc::BadValue, "undefined result of "
|
||||
"processCommand(\"config-reload\", dhcp6)");
|
||||
"processCommand(\"set-config\", json)");
|
||||
}
|
||||
|
||||
// Now check is the returned result is successful (rcode=0) or not
|
||||
@ -109,15 +89,6 @@ void configure(const std::string& file_name) {
|
||||
"no details available";
|
||||
isc_throw(isc::BadValue, reason);
|
||||
}
|
||||
|
||||
// If configuration was parsed successfully, apply the new logger
|
||||
// configuration to log4cplus. It is done before commit in case
|
||||
// something goes wrong.
|
||||
CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
|
||||
|
||||
// Use new configuration.
|
||||
CfgMgr::instance().commit();
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
// If configuration failed at any stage, we drop the staging
|
||||
// configuration and continue to use the previous one.
|
||||
@ -128,7 +99,6 @@ void configure(const std::string& file_name) {
|
||||
isc_throw(isc::BadValue, "configuration error using file '"
|
||||
<< file_name << "': " << ex.what());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @brief Signals handler for DHCPv6 server.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -15,8 +15,10 @@
|
||||
#include <dhcp6/ctrl_dhcp6_srv.h>
|
||||
#include <dhcp6/tests/dhcp6_test_utils.h>
|
||||
#include <hooks/hooks_manager.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <stats/stats_mgr.h>
|
||||
#include <testutils/unix_control_client.h>
|
||||
#include <testutils/io_utils.h>
|
||||
|
||||
#include "marker_file.h"
|
||||
#include "test_libraries.h"
|
||||
@ -25,6 +27,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <cstdlib>
|
||||
|
||||
@ -148,6 +151,12 @@ public:
|
||||
ConstElementPtr config;
|
||||
ASSERT_NO_THROW(config = parseDHCP6(config_txt));
|
||||
ConstElementPtr answer = server_->processConfig(config);
|
||||
|
||||
// Commit the configuration so any subsequent reconfigurations
|
||||
// will only close the command channel if its configuration has
|
||||
// changed.
|
||||
CfgMgr::instance().commit();
|
||||
|
||||
ASSERT_TRUE(answer);
|
||||
|
||||
int status = 0;
|
||||
@ -291,7 +300,7 @@ TEST_F(CtrlDhcpv6SrvTest, configReload) {
|
||||
|
||||
// Use empty parameters list
|
||||
// Prepare configuration file.
|
||||
string config_txt = "{ \"interfaces-config\": {"
|
||||
string config_txt = "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
"},"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
@ -310,10 +319,10 @@ TEST_F(CtrlDhcpv6SrvTest, configReload) {
|
||||
" \"pools\": [ { \"pool\": \"2001:db8:3::/80\" } ],"
|
||||
" \"subnet\": \"2001:db8:3::/64\" "
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
"\"valid-lifetime\": 4000 }}";
|
||||
|
||||
ConstElementPtr config;
|
||||
ASSERT_NO_THROW(config = parseDHCP6(config_txt));
|
||||
ASSERT_NO_THROW(config = parseJSON(config_txt));
|
||||
|
||||
// Make sure there are no subnets configured.
|
||||
CfgMgr::instance().clear();
|
||||
@ -327,13 +336,158 @@ TEST_F(CtrlDhcpv6SrvTest, configReload) {
|
||||
|
||||
// Check that the config was indeed applied.
|
||||
const Subnet6Collection* subnets =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
|
||||
CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
|
||||
EXPECT_EQ(3, subnets->size());
|
||||
|
||||
// Clean up after the test.
|
||||
CfgMgr::instance().clear();
|
||||
}
|
||||
|
||||
// Check that the "set-config" command will replace current configuration
|
||||
TEST_F(CtrlChannelDhcpv6SrvTest, set_config) {
|
||||
createUnixChannelServer();
|
||||
|
||||
// Define strings to permutate the config arguments
|
||||
// (Note the line feeds makes errors easy to find)
|
||||
string set_config_txt = "{ \"command\": \"set-config\" \n";
|
||||
string args_txt = " \"arguments\": { \n";
|
||||
string dhcp6_cfg_txt =
|
||||
" \"Dhcp6\": { \n"
|
||||
" \"interfaces-config\": { \n"
|
||||
" \"interfaces\": [\"*\"] \n"
|
||||
" }, \n"
|
||||
" \"preferred-lifetime\": 3000, \n"
|
||||
" \"valid-lifetime\": 4000, \n"
|
||||
" \"renew-timer\": 1000, \n"
|
||||
" \"rebind-timer\": 2000, \n"
|
||||
" \"lease-database\": { \n"
|
||||
" \"type\": \"memfile\", \n"
|
||||
" \"persist\":false, \n"
|
||||
" \"lfc-interval\": 0 \n"
|
||||
" }, \n"
|
||||
" \"expired-leases-processing\": { \n"
|
||||
" \"reclaim-timer-wait-time\": 0, \n"
|
||||
" \"hold-reclaimed-time\": 0, \n"
|
||||
" \"flush-reclaimed-timer-wait-time\": 0 \n"
|
||||
" },"
|
||||
" \"subnet6\": [ \n";
|
||||
string subnet1 =
|
||||
" {\"subnet\": \"3002::/64\", \n"
|
||||
" \"pools\": [{ \"pool\": \"3002::100-3002::200\" }]}\n";
|
||||
string subnet2 =
|
||||
" {\"subnet\": \"3003::/64\", \n"
|
||||
" \"pools\": [{ \"pool\": \"3003::100-3003::200\" }]}\n";
|
||||
string bad_subnet =
|
||||
" {\"BOGUS\": \"3005::/64\", \n"
|
||||
" \"pools\": [{ \"pool\": \"3005::100-3005::200\" }]}\n";
|
||||
string subnet_footer =
|
||||
" ] \n";
|
||||
string control_socket_header =
|
||||
" ,\"control-socket\": { \n"
|
||||
" \"socket-type\": \"unix\", \n"
|
||||
" \"socket-name\": \"";
|
||||
string control_socket_footer =
|
||||
"\" \n} \n";
|
||||
string logger_txt =
|
||||
" \"Logging\": { \n"
|
||||
" \"loggers\": [ { \n"
|
||||
" \"name\": \"kea\", \n"
|
||||
" \"severity\": \"FATAL\", \n"
|
||||
" \"output_options\": [{ \n"
|
||||
" \"output\": \"/dev/null\" \n"
|
||||
" }] \n"
|
||||
" }] \n"
|
||||
" } \n";
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
// Create a valid config with all the parts should parse
|
||||
os << set_config_txt << ","
|
||||
<< args_txt
|
||||
<< dhcp6_cfg_txt
|
||||
<< subnet1
|
||||
<< subnet_footer
|
||||
<< control_socket_header
|
||||
<< socket_path_
|
||||
<< control_socket_footer
|
||||
<< "}\n" // close dhcp6
|
||||
<< ","
|
||||
<< logger_txt
|
||||
<< "}}";
|
||||
|
||||
// Send the set-config command
|
||||
std::string response;
|
||||
sendUnixCommand(os.str(), response);
|
||||
|
||||
// Verify the configuration was successful.
|
||||
EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
|
||||
response);
|
||||
|
||||
// Check that the config was indeed applied.
|
||||
const Subnet6Collection* subnets =
|
||||
CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
|
||||
EXPECT_EQ(1, subnets->size());
|
||||
|
||||
// Create a config with malformed subnet that should fail to parse.
|
||||
os.str("");
|
||||
os << set_config_txt << ","
|
||||
<< args_txt
|
||||
<< dhcp6_cfg_txt
|
||||
<< bad_subnet
|
||||
<< subnet_footer
|
||||
<< control_socket_header
|
||||
<< socket_path_
|
||||
<< control_socket_footer
|
||||
<< "}\n" // close dhcp6
|
||||
"}}";
|
||||
|
||||
// Send the set-config command
|
||||
sendUnixCommand(os.str(), response);
|
||||
|
||||
// Should fail with a syntax error
|
||||
EXPECT_EQ("{ \"result\": 1, "
|
||||
"\"text\": \"unsupported parameter: BOGUS (<string>:21:26)\" }",
|
||||
response);
|
||||
|
||||
// Check that the config was not lost
|
||||
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
|
||||
EXPECT_EQ(1, subnets->size());
|
||||
|
||||
// Create a valid config with two subnets and no command channel.
|
||||
// It should succeed but client will not receive a the response
|
||||
os.str("");
|
||||
os << set_config_txt << ","
|
||||
<< args_txt
|
||||
<< dhcp6_cfg_txt
|
||||
<< subnet1
|
||||
<< ",\n"
|
||||
<< subnet2
|
||||
<< subnet_footer
|
||||
<< "}\n" // close dhcp6
|
||||
<< "}}";
|
||||
|
||||
/* Verify the control channel socket exists */
|
||||
ASSERT_TRUE(fileExists(socket_path_));
|
||||
|
||||
// Send the set-config command
|
||||
sendUnixCommand(os.str(), response);
|
||||
|
||||
/* Verify the control channel socket no longer exists */
|
||||
EXPECT_FALSE(fileExists(socket_path_));
|
||||
|
||||
// With no command channel, should still receive the response.
|
||||
EXPECT_EQ("{ \"result\": 0, \"text\": \"Configuration successful.\" }",
|
||||
response);
|
||||
|
||||
// Check that the config was not lost
|
||||
subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
|
||||
EXPECT_EQ(2, subnets->size());
|
||||
|
||||
// Clean up after the test.
|
||||
CfgMgr::instance().clear();
|
||||
}
|
||||
|
||||
|
||||
typedef std::map<std::string, isc::data::ConstElementPtr> ElementMap;
|
||||
|
||||
// This test checks which commands are registered by the DHCPv4 server.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -10,6 +10,7 @@
|
||||
#include <dhcp6/tests/dhcp6_test_utils.h>
|
||||
#include <dhcp6/json_config_parser.h>
|
||||
#include <dhcp/tests/pkt_captures.h>
|
||||
#include <log/logger_support.h>
|
||||
#include <util/pointer_util.h>
|
||||
#include <cc/command_interpreter.h>
|
||||
#include <stats/stats_mgr.h>
|
||||
@ -46,6 +47,9 @@ BaseServerTest::~BaseServerTest() {
|
||||
|
||||
// Revert to original data directory.
|
||||
CfgMgr::instance().setDataDir(original_datadir_);
|
||||
|
||||
// Revert to unit test logging in case the test reconfigured logging.
|
||||
isc::log::initLogger();
|
||||
}
|
||||
|
||||
Dhcpv6SrvTest::Dhcpv6SrvTest()
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -11,6 +11,7 @@
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <config/config_log.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace isc::data;
|
||||
|
||||
@ -145,6 +146,19 @@ CommandMgr::commandReader(int sockfd) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Duplicate the connection's socket in the event, the command causes the
|
||||
// channel to close (like a reconfig). This permits us to always have
|
||||
// a socket on which to respond. If for some reason we can't fall back
|
||||
// to the connection socket.
|
||||
int rsp_fd = dup(sockfd);
|
||||
if (rsp_fd < 0 ) {
|
||||
// Highly unlikely
|
||||
const char* errmsg = strerror(errno);
|
||||
LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_SOCKET_DUP_WARN)
|
||||
.arg(errmsg);
|
||||
rsp_fd = sockfd;
|
||||
}
|
||||
|
||||
LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_SOCKET_READ).arg(rval).arg(sockfd);
|
||||
|
||||
// Ok, we received something. Let's see if we can make any sense of it.
|
||||
@ -163,6 +177,11 @@ CommandMgr::commandReader(int sockfd) {
|
||||
|
||||
if (!rsp) {
|
||||
LOG_WARN(command_logger, COMMAND_RESPONSE_ERROR);
|
||||
// Only close the duped socket if it's different (should be)
|
||||
if (rsp_fd != sockfd) {
|
||||
close(rsp_fd);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -179,7 +198,8 @@ CommandMgr::commandReader(int sockfd) {
|
||||
}
|
||||
|
||||
// Send the data back over socket.
|
||||
rval = write(sockfd, txt.c_str(), len);
|
||||
rval = write(rsp_fd, txt.c_str(), len);
|
||||
int saverr = errno;
|
||||
|
||||
LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_SOCKET_WRITE).arg(len).arg(sockfd);
|
||||
|
||||
@ -187,7 +207,13 @@ CommandMgr::commandReader(int sockfd) {
|
||||
// Response transmission failed. Since the response failed, it doesn't
|
||||
// make sense to send any status codes. Let's log it and be done with
|
||||
// it.
|
||||
LOG_ERROR(command_logger, COMMAND_SOCKET_WRITE_FAIL).arg(len).arg(sockfd);
|
||||
LOG_ERROR(command_logger, COMMAND_SOCKET_WRITE_FAIL)
|
||||
.arg(len).arg(sockfd).arg(strerror(saverr));
|
||||
}
|
||||
|
||||
// Only close the duped socket if it's different (should be)
|
||||
if (rsp_fd != sockfd) {
|
||||
close(rsp_fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
# Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -57,6 +57,13 @@ This error message indicates that the server failed to set non-blocking mode
|
||||
on just created socket. That socket was created for accepting specific
|
||||
incoming connection. Additional information may be provided as third parameter.
|
||||
|
||||
% COMMAND_SOCKET_DUP_WARN Failed to duplicate socket for response: %1
|
||||
This debug message indicates that the commandReader was unable to duplicate
|
||||
the connection socket prior to executing the command. This is most likely a
|
||||
system resource issue. The command should still be processed and the response
|
||||
sent, unless the command caused the command channel to be closed (e.g. a
|
||||
reconfiguration command).
|
||||
|
||||
% COMMAND_SOCKET_READ Received %1 bytes over command socket %2
|
||||
This debug message indicates that specified number of bytes was received
|
||||
over command socket identified by specified file descriptor.
|
||||
@ -86,6 +93,6 @@ descriptor and path specified.
|
||||
This debug message indicates that the specified number of bytes was sent
|
||||
over command socket identifier by the specified file descriptor.
|
||||
|
||||
% COMMAND_SOCKET_WRITE_FAIL Error while writing %1 bytes to command socket %2
|
||||
% COMMAND_SOCKET_WRITE_FAIL Error while writing %1 bytes to command socket %2 : %3
|
||||
This error message indicates that an error was encountered while
|
||||
attempting to send a response to the command socket.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -15,10 +15,8 @@ namespace dhcp {
|
||||
namespace test {
|
||||
|
||||
bool fileExists(const std::string& file_path) {
|
||||
std::ifstream fs(file_path.c_str());
|
||||
const bool file_exists = fs.good();
|
||||
fs.close();
|
||||
return (file_exists);
|
||||
struct stat statbuf;
|
||||
return(stat(file_path.c_str(), &statbuf) == 0);
|
||||
}
|
||||
|
||||
std::string readFile(const std::string& file_path) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -8,6 +8,7 @@
|
||||
#define TEST_IO_UTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
Loading…
x
Reference in New Issue
Block a user