Commit 7bed043f authored by Stephen Morris's avatar Stephen Morris
Browse files

[2642] Merge branch 'master' into trac2642

Conflicts:
	doc/guide/bind10-guide.xml
parents 8765402a 8b05639c
248. [func] vorner
556. [bug] marcin
Fixed DHCP servers configuration whereby the servers did not
receive a configuration stored in the database on their startup.
Also, the configuration handler function now uses full configuration
instead of partial to configure the server. This guarantees that
dependencies between various configuration parameters are
fulfilled.
(Trac #2637, git 91aa998226f1f91a232f2be59a53c9568c4ece77)
555. [func] marcin
The encapsulated option space name can be specified for
a DHCP option. It comprises sub-options being sent within
an option that encapsulates this option space.
(Trac #2314, git 27e6119093723a1e46a239ec245a8b4b10677635)
554. [func] jinmei
b10-loadzone: improved completion log message and intermediate
reports: It now logs the precise number of loaded RRs on
completion, and intermediate reports show additional information
such as the estimated progress in percentage and estimated time
to complete.
(Trac #2574, git 5b8a824054313bdecb8988b46e55cb2e94cb2d6c)
553. [func] stephen
Values of the parameters to access the DHCP server lease database
can now be set through the BIND 10 configuration mechanism.
(Trac #2559, git 6c6f405188cc02d2358e114c33daff58edabd52a)
552. [bug] shane
Build on Raspberry PI.
The main issue was use of char for reading from input streams,
which is incorrect, as EOF is returned as an int -1, which would
then get cast into a char -1.
A number of other minor issues were also fixed.
(Trac #2571, git 525333e187cc4bbbbde288105c9582c1024caa4a)
551. [bug] shane
Kill msgq if we cannot connect to it on startup.
When the boss process was unable to connect to the msgq, it would
exit. However, it would leave the msgq process running. This has
been fixed, and the msgq is now stopped in this case.
(Trac #2608, git 016925ef2437e0396127e135c937d3a55539d224)
550. [func] tomek
b10-dhcp4: The DHCPv4 server now generates a server identifier
the first time it is run. The identifier is preserved in a file
across server restarts.
b10-dhcp6: The server identifier is now preserved in a file across
server restarts.
(Trac #2597, git fa342a994de5dbefe32996be7eebe58f6304cff7)
549. [func] tomek
b10-dhcp6: It is now possible to specify that a configured subnet
is reachable locally over specified interface (see "interface"
parameter in Subnet6 configuration).
(Trac #2596, git a70f6172194a976b514cd7d67ce097bbca3c2798)
548. [func] vorner
The message queue daemon now appears on the bus. This has two
effects, one is it obeys logging configuration and logs to the
correct place like the rest of the modules. The other is it
......
......@@ -232,7 +232,7 @@ AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes)
AC_SUBST(SET_ENV_LIBRARY_PATH)
AC_SUBST(ENV_LIBRARY_PATH)
m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3.2 python3.1 python3])
m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3.3 python3.2 python3.1 python3])
AC_ARG_WITH([pythonpath],
AC_HELP_STRING([--with-pythonpath=PATH],
[specify an absolute path to python executable when automatic version check (incorrectly) fails]),
......@@ -282,7 +282,10 @@ AC_SUBST(PYTHON_LOGMSGPKG_DIR)
# This is python package paths commonly used in python tests. See
# README of log_messages for why it's included.
COMMON_PYTHON_PATH="\$(abs_top_builddir)/src/lib/python/isc/log_messages:\$(abs_top_srcdir)/src/lib/python:\$(abs_top_builddir)/src/lib/python"
# lib/dns/python/.libs is necessary because __init__.py of isc package
# automatically imports isc.datasrc, which then requires the DNS loadable
# module. #2145 should eliminate the need for it.
COMMON_PYTHON_PATH="\$(abs_top_builddir)/src/lib/python/isc/log_messages:\$(abs_top_srcdir)/src/lib/python:\$(abs_top_builddir)/src/lib/python:\$(abs_top_builddir)/src/lib/dns/python/.libs"
AC_SUBST(COMMON_PYTHON_PATH)
# Check for python development environments
......@@ -1214,6 +1217,8 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/server_common/tests/Makefile
src/lib/python/isc/sysinfo/Makefile
src/lib/python/isc/sysinfo/tests/Makefile
src/lib/python/isc/statistics/Makefile
src/lib/python/isc/statistics/tests/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
......
......@@ -3611,6 +3611,26 @@ Dhcp4/subnet4 [] list (default)
</section>
</section>
<section id="dhcp4-serverid">
<title>Server Identifier in DHCPv4</title>
<para>The DHCPv4 protocol uses a "server identifier" for clients to be able
to discriminate between several servers present on the same link: this
value is an IPv4 address of the server. When started for the first time,
the DHCPv4 server will choose one of its IPv4 addresses as its server-id,
and store the chosen value to a file. (The file is named b10-dhcp4-serverid and is
stored in the "local state directory". This is set during installation
when "configure" is run, and can be changed by using "--localstatedir"
on the "configure" command line.) That file will be read by the server
and the contained value used whenever the server is subsequently started.
</para>
<para>
It is unlikely that this parameter needs to be changed. If such a need
arises, please stop the server, edit the file and restart the server.
It is a text file that should contain an IPv4 address. Spaces are
ignored. No extra characters are allowed in this file.
</para>
</section>
<section id="dhcp4-std">
<title>Supported Standards</title>
<para>The following standards and draft standards are currently
......@@ -3978,6 +3998,31 @@ Dhcp6/subnet6/ list
</section>
<section id="dhcp6-serverid">
<title>Server Identifier in DHCPv6</title>
<para>The DHCPv6 protocol uses a "server identifier" (also known
as a DUID) for clients to be able to discriminate between several
servers present on the same link. There are several types of
DUIDs defined, but RFC 3315 instructs servers to use DUID-LLT if
possible. This format consists of a link-layer (MAC) address and a
timestamp. When started for the first time, the DHCPv6 server will
automatically generate such a DUID and store the chosen value to
a file (The file is named b10-dhcp6-serverid and is stored in the
"local state directory". This is set during installation when
"configure" is run, and can be changed by using "--localstatedir"
on the "configure" command line.) That file will be read by the server
and the contained value used whenever the server is subsequently started.
</para>
<para>
It is unlikely that this parameter needs to be changed. If such a need
arises, please stop the server, edit the file and start the server
again. It is a text file that contains double digit hexadecimal values
separated by colons. This format is similar to typical MAC address
format. Spaces are ignored. No extra characters are allowed in this
file.
</para>
</section>
<section id="dhcp6-std">
<title>Supported DHCPv6 Standards</title>
<para>The following standards and draft standards are currently
......
......@@ -328,7 +328,8 @@ TEST_F(DataSrcClientsBuilderTest,
{
// Prepare the database first
const std::string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
std::stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
std::stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n"
"example.org. 3600 IN NS ns1.example.org.\n");
createSQLite3DB(rrclass, Name("example.org"), test_db.c_str(), ss);
// This describes the data source in the configuration
const ConstElementPtr config(Element::fromJSON("{"
......
......@@ -491,6 +491,8 @@ class BoB:
# if we have been trying for "a while" give up
if (time.time() - cc_connect_start) > self.msgq_timeout:
if msgq_proc.process:
msgq_proc.process.kill()
logger.error(BIND10_CONNECTING_TO_CC_FAIL)
raise CChannelConnectError("Unable to connect to c-channel after 5 seconds")
......
......@@ -20,7 +20,10 @@ export PYTHON_EXEC
BINDCTL_PATH=@abs_top_builddir@/src/bin/bindctl
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
# Note: lib/dns/python/.libs is necessary because __init__.py of isc package
# automatically imports isc.datasrc, which then requires the DNS loadable
# module. #2145 should eliminate the need for it.
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs
export PYTHONPATH
# If necessary (rare cases), explicitly specify paths to dynamic libraries
......
......@@ -20,7 +20,10 @@ export PYTHON_EXEC
DBUTIL_PATH=@abs_top_builddir@/src/bin/dbutil
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
# Note: lib/dns/python/.libs is necessary because __init__.py of isc package
# automatically imports isc.datasrc, which then requires the DNS loadable
# module. #2145 should eliminate the need for it.
PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs
export PYTHONPATH
# If necessary (rare cases), explicitly specify paths to dynamic libraries
......
......@@ -18,6 +18,7 @@
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dbaccess_parser.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/option_space_container.h>
#include <util/encode/hex.h>
......@@ -49,9 +50,6 @@ typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
/// @brief auxiliary type used for storing element name and its parser
typedef pair<string, ConstElementPtr> ConfigPair;
/// @brief a factory method that will create a parser for a given element name
typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
......@@ -745,30 +743,34 @@ private:
void createOption() {
// Option code is held in the uint32_t storage but is supposed to
// be uint16_t value. We need to check that value in the configuration
// does not exceed range of uint16_t and is not zero.
// does not exceed range of uint8_t and is not zero.
uint32_t option_code = getParam<uint32_t>("code", uint32_values_);
if (option_code == 0) {
isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
<< " be equal to zero. Option code '0' is reserved in"
<< " DHCPv4.");
} else if (option_code > std::numeric_limits<uint16_t>::max()) {
isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
<< " exceed " << std::numeric_limits<uint16_t>::max());
isc_throw(DhcpConfigError, "option code must not be zero."
<< " Option code '0' is reserved in DHCPv4.");
} else if (option_code > std::numeric_limits<uint8_t>::max()) {
isc_throw(DhcpConfigError, "invalid option code '" << option_code
<< "', it must not exceed '"
<< std::numeric_limits<uint8_t>::max() << "'");
}
// Check that the option name has been specified, is non-empty and does not
// contain spaces.
// @todo possibly some more restrictions apply here?
std::string option_name = getParam<std::string>("name", string_values_);
if (option_name.empty()) {
isc_throw(DhcpConfigError, "Parser error: option name must not be"
<< " empty");
isc_throw(DhcpConfigError, "name of the option with code '"
<< option_code << "' is empty");
} else if (option_name.find(" ") != std::string::npos) {
isc_throw(DhcpConfigError, "Parser error: option name must not contain"
<< " spaces");
isc_throw(DhcpConfigError, "invalid option name '" << option_name
<< "', space character is not allowed");
}
std::string option_space = getParam<std::string>("space", string_values_);
/// @todo Validate option space once #2313 is merged.
if (!OptionSpace::validateName(option_space)) {
isc_throw(DhcpConfigError, "invalid option space name '"
<< option_space << "' specified for option '"
<< option_name << "' (code '" << option_code
<< "')");
}
OptionDefinitionPtr def;
if (option_space == "dhcp4" &&
......@@ -822,7 +824,7 @@ private:
try {
util::encode::decodeHex(option_data, binary);
} catch (...) {
isc_throw(DhcpConfigError, "Parser error: option data is not a valid"
isc_throw(DhcpConfigError, "option data is not a valid"
<< " string of hexadecimal digits: " << option_data);
}
}
......@@ -857,7 +859,7 @@ private:
// definition of option value makes sense.
if (def->getName() != option_name) {
isc_throw(DhcpConfigError, "specified option name '"
<< option_name << " does not match the "
<< option_name << "' does not match the "
<< "option definition: '" << option_space
<< "." << def->getName() << "'");
}
......@@ -877,6 +879,7 @@ private:
<< ", code: " << option_code << "): "
<< ex.what());
}
}
// All went good, so we can set the option space name.
option_space_ = option_space;
......@@ -978,7 +981,7 @@ public:
/// @brief Parser for a single option definition.
///
/// This parser creates an instance of a single option definition.
class OptionDefParser: DhcpConfigParser {
class OptionDefParser : public DhcpConfigParser {
public:
/// @brief Constructor.
......@@ -1005,7 +1008,8 @@ public:
std::string entry(param.first);
ParserPtr parser;
if (entry == "name" || entry == "type" ||
entry == "record-types" || entry == "space") {
entry == "record-types" || entry == "space" ||
entry == "encapsulate") {
StringParserPtr
str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
if (str_parser) {
......@@ -1055,8 +1059,8 @@ public:
/// @brief Stores the parsed option definition in a storage.
void commit() {
// @todo validate option space name once 2313 is merged.
if (storage_ && option_definition_) {
if (storage_ && option_definition_ &&
OptionSpace::validateName(option_space_name_)) {
storage_->addItem(option_definition_, option_space_name_);
}
}
......@@ -1078,11 +1082,10 @@ private:
void createOptionDef() {
// Get the option space name and validate it.
std::string space = getParam<std::string>("space", string_values_);
// @todo uncomment the code below when the #2313 is merged.
/* if (!OptionSpace::validateName()) {
if (!OptionSpace::validateName(space)) {
isc_throw(DhcpConfigError, "invalid option space name '"
<< space << "'");
} */
}
// Get other parameters that are needed to create the
// option definition.
......@@ -1090,9 +1093,35 @@ private:
uint32_t code = getParam<uint32_t>("code", uint32_values_);
std::string type = getParam<std::string>("type", string_values_);
bool array_type = getParam<bool>("array", boolean_values_);
std::string encapsulates = getParam<std::string>("encapsulate",
string_values_);
// Create option definition.
OptionDefinitionPtr def;
// We need to check if user has set encapsulated option space
// name. If so, different constructor will be used.
if (!encapsulates.empty()) {
// Arrays can't be used together with sub-options.
if (array_type) {
isc_throw(DhcpConfigError, "option '" << space << "."
<< "name" << "', comprising an array of data"
<< " fields may not encapsulate any option space");
} else if (encapsulates == space) {
isc_throw(DhcpConfigError, "option must not encapsulate"
<< " an option space it belongs to: '"
<< space << "." << name << "' is set to"
<< " encapsulate '" << space << "'");
OptionDefinitionPtr def(new OptionDefinition(name, code,
type, array_type));
} else {
def.reset(new OptionDefinition(name, code, type,
encapsulates.c_str()));
}
} else {
def.reset(new OptionDefinition(name, code, type, array_type));
}
// The record-types field may carry a list of comma separated names
// of data types that form a record.
std::string record_types = getParam<std::string>("record-types",
......@@ -1110,7 +1139,7 @@ private:
}
} catch (const Exception& ex) {
isc_throw(DhcpConfigError, "invalid record type values"
<< " specified for the option definition: "
<< " specified for the option definition: "
<< ex.what());
}
}
......@@ -1332,6 +1361,63 @@ private:
return (false);
}
/// @brief Append sub-options to an option.
///
/// @param option_space a name of the encapsulated option space.
/// @param option option instance to append sub-options to.
void appendSubOptions(const std::string& option_space, OptionPtr& option) {
// Only non-NULL options are stored in option container.
// If this option pointer is NULL this is a serious error.
assert(option);
OptionDefinitionPtr def;
if (option_space == "dhcp4" &&
LibDHCP::isStandardOption(Option::V4, option->getType())) {
def = LibDHCP::getOptionDef(Option::V4, option->getType());
// Definitions for some of the standard options hasn't been
// implemented so it is ok to leave here.
if (!def) {
return;
}
} else {
const OptionDefContainerPtr defs =
option_def_intermediate.getItems(option_space);
const OptionDefContainerTypeIndex& idx = defs->get<1>();
const OptionDefContainerTypeRange& range =
idx.equal_range(option->getType());
// There is no definition so we have to leave.
if (std::distance(range.first, range.second) == 0) {
return;
}
def = *range.first;
// If the definition exists, it must be non-NULL.
// Otherwise it is a programming error.
assert(def);
}
// We need to get option definition for the particular option space
// and code. This definition holds the information whether our
// option encapsulates any option space.
// Get the encapsulated option space name.
std::string encapsulated_space = def->getEncapsulatedSpace();
// If option space name is empty it means that our option does not
// encapsulate any option space (does not include sub-options).
if (!encapsulated_space.empty()) {
// Get the sub-options that belong to the encapsulated
// option space.
const Subnet::OptionContainerPtr sub_opts =
option_defaults.getItems(encapsulated_space);
// Append sub-options to the option.
BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
if (desc.option) {
option->addOption(desc.option);
}
}
}
}
/// @brief Create a new subnet using a data from child parsers.
///
/// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
......@@ -1407,6 +1493,8 @@ private:
LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
.arg(desc.option->getType()).arg(addr.toText());
}
// Add sub-options (if any).
appendSubOptions(option_space, desc.option);
// In any case, we add the option to the subnet.
subnet_->addOption(desc.option, false, option_space);
}
......@@ -1434,6 +1522,9 @@ private:
Subnet::OptionDescriptor existing_desc =
subnet_->getOptionDescriptor(option_space, desc.option->getType());
if (!existing_desc.option) {
// Add sub-options (if any).
appendSubOptions(option_space, desc.option);
subnet_->addOption(desc.option, false, option_space);
}
}
......@@ -1607,6 +1698,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
factories["option-data"] = OptionDataListParser::factory;
factories["option-def"] = OptionDefListParser::factory;
factories["version"] = StringParser::factory;
factories["lease-database"] = DbAccessParser::factory;
FactoryMap::iterator f = factories.find(config_id);
if (f == factories.end()) {
......@@ -1661,13 +1753,18 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
// rollback informs whether error occured and original data
// have to be restored to global storages.
bool rollback = false;
// config_pair holds the details of the current parser when iterating over
// the parsers. It is declared outside the loops so in case of an error,
// the name of the failing parser can be retrieved in the "catch" clause.
ConfigPair config_pair;
try {
// Make parsers grouping.
const std::map<std::string, ConstElementPtr>& values_map =
config_set->mapValue();
BOOST_FOREACH(ConfigPair config_pair, values_map) {
BOOST_FOREACH(config_pair, values_map) {
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first));
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
.arg(config_pair.first);
if (config_pair.first == "subnet4") {
subnet_parser = parser;
......@@ -1702,16 +1799,19 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
}
} catch (const isc::Exception& ex) {
answer =
isc::config::createAnswer(1, string("Configuration parsing failed: ") + ex.what());
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
.arg(config_pair.first).arg(ex.what());
answer = isc::config::createAnswer(1,
string("Configuration parsing failed: ") + ex.what());
// An error occured, so make sure that we restore original data.
rollback = true;
} catch (...) {
// for things like bad_cast in boost::lexical_cast
answer =
isc::config::createAnswer(1, string("Configuration parsing failed"));
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(config_pair.first);
answer = isc::config::createAnswer(1,
string("Configuration parsing failed"));
// An error occured, so make sure that we restore original data.
rollback = true;
......@@ -1728,14 +1828,16 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
}
}
catch (const isc::Exception& ex) {
answer =
isc::config::createAnswer(2, string("Configuration commit failed: ") + ex.what());
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(2,
string("Configuration commit failed: ") + ex.what());
rollback = true;
} catch (...) {
// for things like bad_cast in boost::lexical_cast
answer =
isc::config::createAnswer(2, string("Configuration commit failed"));
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(2,
string("Configuration commit failed"));
rollback = true;
}
......
......@@ -46,19 +46,62 @@ 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."));
}
ConstElementPtr
ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
.arg(new_config->str());
if (server_) {
return (configureDhcp4Server(*server_, 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.");
return (answer);
}
// 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 = 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());
// 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 (configureDhcp4Server(*server_, merged_config));
}
ConstElementPtr
......@@ -109,8 +152,15 @@ void ControlledDhcpv4Srv::establishSession() {
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
// commited 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_,
NULL,