Commit 650a6c1b authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[2270] Configuration parser for DHCPv4

parent 0375b659
...@@ -44,6 +44,7 @@ pkglibexec_PROGRAMS = b10-dhcp4 ...@@ -44,6 +44,7 @@ pkglibexec_PROGRAMS = b10-dhcp4
b10_dhcp4_SOURCES = main.cc b10_dhcp4_SOURCES = main.cc
b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h b10_dhcp4_SOURCES += ctrl_dhcp4_srv.cc ctrl_dhcp4_srv.h
b10_dhcp4_SOURCES += config_parser.cc config_parser.h
b10_dhcp4_SOURCES += dhcp4_log.cc dhcp4_log.h b10_dhcp4_SOURCES += dhcp4_log.cc dhcp4_log.h
b10_dhcp4_SOURCES += dhcp4_srv.cc dhcp4_srv.h b10_dhcp4_SOURCES += dhcp4_srv.cc dhcp4_srv.h
...@@ -57,6 +58,7 @@ b10_dhcp4_CXXFLAGS = -Wno-unused-parameter ...@@ -57,6 +58,7 @@ b10_dhcp4_CXXFLAGS = -Wno-unused-parameter
endif endif
b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
......
This diff is collapsed.
// Copyright (C) 2012 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 <string>
#include <exceptions/exceptions.h>
#include <cc/data.h>
#ifndef DHCP4_CONFIG_PARSER_H
#define DHCP4_CONFIG_PARSER_H
namespace isc {
namespace dhcp {
class Dhcpv4Srv;
/// An exception that is thrown if an error occurs while configuring an
/// \c Dhcpv4Srv object.
class Dhcp4ConfigError : public isc::Exception {
public:
/// @brief constructor
///
/// @param file name of the file, where exception occurred
/// @param line line of the file, where exception occurred
/// @param what text description of the issue that caused exception
Dhcp4ConfigError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
class DhcpConfigParser {
///
/// \name Constructors and Destructor
///
/// Note: The copy constructor and the assignment operator are
/// intentionally defined as private to make it explicit that this is a
/// pure base class.
//@{
private:
DhcpConfigParser(const DhcpConfigParser& source);
DhcpConfigParser& operator=(const DhcpConfigParser& source);
protected:
/// \brief The default constructor.
///
/// This is intentionally defined as \c protected as this base class should
/// never be instantiated (except as part of a derived class).
DhcpConfigParser() {}
public:
/// The destructor.
virtual ~DhcpConfigParser() {}
//@}
/// \brief Prepare configuration value.
///
/// This method parses the "value part" of the configuration identifier
/// that corresponds to this derived class and prepares a new value to
/// apply to the server.
///
/// This method must validate the given value both in terms of syntax
/// and semantics of the configuration, so that the server will be
/// validly configured at the time of \c commit(). Note: the given
/// configuration value is normally syntactically validated, but the
/// \c build() implementation must also expect invalid input. If it
/// detects an error it may throw an exception of a derived class
/// of \c isc::Exception.
///
/// Preparing a configuration value will often require resource
/// allocation. If it fails, it may throw a corresponding standard
/// exception.
///
/// This method is not expected to be called more than once in the
/// life of the object. Although multiple calls are not prohibited
/// by the interface, the behavior is undefined.
///
/// \param config_value The configuration value for the identifier
/// corresponding to the derived class.
virtual void build(isc::data::ConstElementPtr config_value) = 0;
/// \brief Apply the prepared configuration value to the server.
///
/// This method is expected to be exception free, and, as a consequence,
/// it should normally not involve resource allocation.
/// Typically it would simply perform exception free assignment or swap
/// operation on the value prepared in \c build().
/// In some cases, however, it may be very difficult to meet this
/// condition in a realistic way, while the failure case should really
/// be very rare. In such a case it may throw, and, if the parser is
/// called via \c configureDhcp4Server(), the caller will convert the
/// exception as a fatal error.
///
/// This method is expected to be called after \c build(), and only once.
/// The result is undefined otherwise.
virtual void commit() = 0;
};
/// @brief a pointer to configuration parser
typedef boost::shared_ptr<DhcpConfigParser> ParserPtr;
/// @brief a collection of parsers
///
/// This container is used to store pointer to parsers for a given scope.
typedef std::vector<ParserPtr> ParserCollection;
/// \brief Configure an \c Dhcpv4Srv object with a set of configuration values.
///
/// This function parses configuration information stored in \c config_set
/// and configures the \c server by applying the configuration to it.
/// It provides the strong exception guarantee as long as the underlying
/// derived class implementations of \c DhcpConfigParser meet the assumption,
/// that is, it ensures that either configuration is fully applied or the
/// state of the server is intact.
///
/// If a syntax or semantics level error happens during the configuration
/// (such as malformed configuration or invalid configuration parameter),
/// this function throws an exception of class \c Dhcp4ConfigError.
/// If the given configuration requires resource allocation and it fails,
/// a corresponding standard exception will be thrown.
/// Other exceptions may also be thrown, depending on the implementation of
/// the underlying derived class of \c Dhcp4ConfigError.
/// In any case the strong guarantee is provided as described above except
/// in the very rare cases where the \c commit() method of a parser throws
/// an exception. If that happens this function converts the exception
/// into a \c FatalError exception and rethrows it. This exception is
/// expected to be caught at the highest level of the application to terminate
/// the program gracefully.
///
/// \param server The \c Dhcpv4Srv object to be configured.
/// \param config_set A JSON style configuration to apply to \c server.
isc::data::ConstElementPtr
configureDhcp4Server(Dhcpv4Srv& server,
isc::data::ConstElementPtr config_set);
}; // end of isc::dhcp namespace
}; // end of isc namespace
#endif // DHCP4_CONFIG_PARSER_H
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <dhcp4/ctrl_dhcp4_srv.h> #include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h> #include <dhcp4/dhcp4_log.h>
#include <dhcp4/spec_config.h> #include <dhcp4/spec_config.h>
#include <dhcp4/config_parser.h>
#include <dhcp/iface_mgr.h> #include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
#include <util/buffer.h> #include <util/buffer.h>
...@@ -47,8 +48,14 @@ ConstElementPtr ...@@ -47,8 +48,14 @@ ConstElementPtr
ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) { ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE) LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_UPDATE)
.arg(new_config->str()); .arg(new_config->str());
ConstElementPtr answer = isc::config::createAnswer(0, if (server_) {
"Thank you for sending config."); return (configureDhcp4Server(*server_, new_config));
}
// 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); return (answer);
} }
...@@ -101,10 +108,22 @@ void ControlledDhcpv4Srv::establishSession() { ...@@ -101,10 +108,22 @@ void ControlledDhcpv4Srv::establishSession() {
.arg(specfile); .arg(specfile);
cc_session_ = new Session(io_service_.get_io_service()); cc_session_ = new Session(io_service_.get_io_service());
config_session_ = new ModuleCCSession(specfile, *cc_session_, config_session_ = new ModuleCCSession(specfile, *cc_session_,
dhcp4ConfigHandler, NULL,
dhcp4CommandHandler, false); dhcp4CommandHandler, false);
config_session_->start(); 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());
} catch (const Dhcp4ConfigError& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
}
/// Integrate the asynchronous I/O model of BIND 10 configuration /// Integrate the asynchronous I/O model of BIND 10 configuration
/// control with the "select" model of the DHCP server. This is /// control with the "select" model of the DHCP server. This is
/// fully explained in \ref dhcpv4Session. /// fully explained in \ref dhcpv4Session.
......
...@@ -4,9 +4,85 @@ ...@@ -4,9 +4,85 @@
"module_description": "DHCPv4 server daemon", "module_description": "DHCPv4 server daemon",
"config_data": [ "config_data": [
{ "item_name": "interface", { "item_name": "interface",
"item_type": "string", "item_type": "list",
"item_optional": false, "item_optional": false,
"item_default": "eth0" "item_default": [ "all" ],
"list_item_spec":
{
"item_name": "interface_name",
"item_type": "string",
"item_optional": false,
"item_default": "all"
}
} ,
{ "item_name": "renew-timer",
"item_type": "integer",
"item_optional": false,
"item_default": 1000
},
{ "item_name": "rebind-timer",
"item_type": "integer",
"item_optional": false,
"item_default": 2000
},
{ "item_name": "valid-lifetime",
"item_type": "integer",
"item_optional": false,
"item_default": 4000
},
{ "item_name": "subnet4",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "single-subnet4",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "subnet",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "renew-timer",
"item_type": "integer",
"item_optional": false,
"item_default": 1000
},
{ "item_name": "rebind-timer",
"item_type": "integer",
"item_optional": false,
"item_default": 2000
},
{ "item_name": "valid-lifetime",
"item_type": "integer",
"item_optional": false,
"item_default": 7200
},
{ "item_name": "pool",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "type",
"item_type": "string",
"item_optional": false,
"item_default": ""
}
}
]
}
} }
], ],
"commands": [ "commands": [
......
...@@ -26,10 +26,30 @@ to establish a session with the BIND 10 control channel. ...@@ -26,10 +26,30 @@ to establish a session with the BIND 10 control channel.
A debug message listing the command (and possible arguments) received A debug message listing the command (and possible arguments) received
from the BIND 10 control system by the IPv4 DHCP server. from the BIND 10 control system by the IPv4 DHCP server.
% DHCP4_CONFIG_LOAD_FAIL failed to load configuration: %1
This critical error message indicates that the initial DHCPv4
configuration has failed. The server will start, but nothing will be
served until the configuration has been corrected.
% DHCP4_CONFIG_UPDATE updated configuration received: %1 % DHCP4_CONFIG_UPDATE updated configuration received: %1
A debug message indicating that the IPv4 DHCP server has received an A debug message indicating that the IPv4 DHCP server has received an
updated configuration from the BIND 10 configuration system. updated configuration from the BIND 10 configuration system.
% DHCP4_CONFIG_START DHCPv4 server is processing the following configuration: %1
This is a debug message that is issued every time the server receives a
configuration. That happens start up and also when a server configuration
change is committed by the administrator.
% DHCP4_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1
This is an informational message reporting that the configuration has
been extended to include the specified IPv4 subnet.
% 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
configuration is committed by the administrator. Additional information
may be provided.
% DHCP4_NOT_RUNNING IPv4 DHCP server is not running % DHCP4_NOT_RUNNING IPv4 DHCP server is not running
A warning message is issued when an attempt is made to shut down the A warning message is issued when an attempt is made to shut down the
IPv4 DHCP server but it is not running. IPv4 DHCP server but it is not running.
......
...@@ -49,9 +49,11 @@ TESTS += dhcp4_unittests ...@@ -49,9 +49,11 @@ TESTS += dhcp4_unittests
dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc 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 += ../dhcp4_log.h ../dhcp4_log.cc
dhcp4_unittests_SOURCES += ../config_parser.cc ../config_parser.h
dhcp4_unittests_SOURCES += dhcp4_unittests.cc dhcp4_unittests_SOURCES += dhcp4_unittests.cc
dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += config_parser_unittest.cc
nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc nodist_dhcp4_unittests_SOURCES = ../dhcp4_messages.h ../dhcp4_messages.cc
if USE_CLANGPP if USE_CLANGPP
...@@ -65,6 +67,7 @@ dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) ...@@ -65,6 +67,7 @@ dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
dhcp4_unittests_LDADD = $(GTEST_LDADD) dhcp4_unittests_LDADD = $(GTEST_LDADD)
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
......
// Copyright (C) 2012 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 <iostream>
#include <fstream>
#include <sstream>
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp4/config_parser.h>
#include <config/ccsession.h>
#include <dhcp/subnet.h>
#include <dhcp/cfgmgr.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::config;
namespace {
class Dhcp4ParserTest : public ::testing::Test {
public:
Dhcp4ParserTest()
:rcode_(-1) {
// Open port 0 means to not do anything at all. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
srv_ = new Dhcpv4Srv(0);
}
~Dhcp4ParserTest() {
delete srv_;
};
Dhcpv4Srv* srv_;
int rcode_;
ConstElementPtr comment_;
};
// Goal of this test is a verification if a very simple config update
// with just a bumped version number. That's the simplest possible
// config update.
TEST_F(Dhcp4ParserTest, version) {
ConstElementPtr x;
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
Element::fromJSON("{\"version\": 0}")));
// returned value must be 0 (configuration accepted)
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
EXPECT_EQ(0, rcode_);
}
/// The goal of this test is to verify that the code accepts only
/// valid commands and malformed or unsupported parameters are rejected.
TEST_F(Dhcp4ParserTest, bogus_command) {
ConstElementPtr x;
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_,
Element::fromJSON("{\"bogus\": 5}")));
// returned value must be 1 (configuration parse error)
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
EXPECT_EQ(1, rcode_);
}
/// The goal of this test is to verify if wrongly defined subnet will
/// be rejected. Properly defined subnet must include at least one
/// pool definition.
TEST_F(Dhcp4ParserTest, empty_subnet) {
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_,
Element::fromJSON("{ \"interface\": [ \"all\" ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ ], "
"\"valid-lifetime\": 4000 }")));
// returned value should be 0 (success)
ASSERT_TRUE(status);
comment_ = parseAnswer(rcode_, status);
EXPECT_EQ(0, rcode_);
}
/// The goal of this test is to verify if defined subnet uses global
/// parameter timer definitions.
TEST_F(Dhcp4ParserTest, subnet_global_defaults) {
ConstElementPtr status;
string config = "{ \"interface\": [ \"all\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
cout << config << endl;
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// check if returned status is OK
ASSERT_TRUE(status);
comment_ = parseAnswer(rcode_, status);
EXPECT_EQ(0, rcode_);
// Now check if the configuration was indeed handled and we have
// expected pool configured.
Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
ASSERT_TRUE(subnet);
EXPECT_EQ(1000, subnet->getT1());
EXPECT_EQ(2000, subnet->getT2());
EXPECT_EQ(4000, subnet->getValid());
}
// This test checks if it is possible to override global values
// on a per subnet basis.
TEST_F(Dhcp4ParserTest, subnet_local) {
ConstElementPtr status;
string config = "{ \"interface\": [ \"all\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
" \"renew-timer\": 1, "
" \"rebind-timer\": 2, "
" \"valid-lifetime\": 4,"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
cout << config << endl;
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// returned value should be 0 (configuration success)
ASSERT_TRUE(status);
comment_ = parseAnswer(rcode_, status);
EXPECT_EQ(0, rcode_);
Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
ASSERT_TRUE(subnet);
EXPECT_EQ(1, subnet->getT1());
EXPECT_EQ(2, subnet->getT2());
EXPECT_EQ(4, subnet->getValid());
}
// Test verifies that a subnet with pool values that do not belong to that
// pool are rejected.
TEST_F(Dhcp4ParserTest, pool_out_of_subnet) {
ConstElementPtr status;
string config = "{ \"interface\": [ \"all\" ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.4.0/28\" ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
cout << config << endl;
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
// returned value must be 2 (values error)
// as the pool does not belong to that subnet
ASSERT_TRUE(status);
comment_ = parseAnswer(rcode_, status);
EXPECT_EQ(2, rcode_);
}
// Goal of this test is to verify if pools can be defined
// using prefix/length notation. There is no separate test for min-max
// notation as it was tested in several previous tests.
TEST_F(Dhcp4ParserTest, pool_prefix_len) {
ConstElementPtr x;
string config = "{ \"interface\": [ \"all\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pool\": [ \"192.0.2.128/28\" ],"
" \"subnet\": \"192.0.2.0/24\" } ],"
"\"valid-lifetime\": 4000 }";
cout << config << endl;
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));