From 9082500187f9e0ebf99d5ecb67d7e815a7da2239 Mon Sep 17 00:00:00 2001 From: Andrei Pavel Date: Wed, 14 Dec 2016 16:57:44 +0200 Subject: Corrected typos --- src/lib/dhcpsrv/cfg_subnets4.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index f1c7a8f658..f317a91fac 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -85,7 +85,7 @@ CfgSubnets4::selectSubnet(const SubnetSelector& selector) const { // If relayed message has been received, try to match the giaddr with the // relay address specified for a subnet. It is also possible that the relay - // address will not match with any of the relay addresses accross all + // address will not match with any of the relay addresses across all // subnets, but we need to verify that for all subnets before we can try // to use the giaddr to match with the subnet prefix. if (!selector.giaddr_.isV4Zero()) { -- cgit v1.2.3 From 0beffc6e25c8413b4f9439e9f5b02f38bc75a076 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Sun, 5 Mar 2017 09:03:09 +0100 Subject: [fdunparse2] Rebased, still reservations to do --- configure.ac | 113 +++++-- src/bin/dhcp4/dhcp4to6_ipc.cc | 7 +- src/bin/dhcp4/json_config_parser.cc | 17 +- src/bin/dhcp4/main.cc | 3 + src/bin/dhcp4/simple_parser4.cc | 3 +- src/bin/dhcp4/tests/config_parser_unittest.cc | 8 - src/bin/dhcp4/tests/simple_parser4_unittest.cc | 3 +- src/bin/dhcp6/dhcp6to4_ipc.cc | 7 +- src/bin/dhcp6/json_config_parser.cc | 29 +- src/bin/dhcp6/main.cc | 3 + src/bin/dhcp6/simple_parser6.cc | 3 +- src/bin/dhcp6/tests/config_parser_unittest.cc | 7 - src/bin/dhcp6/tests/simple_parser6_unittest.cc | 5 +- src/lib/cc/data.cc | 236 +++++++++++++ src/lib/cc/data.h | 44 +++ src/lib/cc/simple_parser.cc | 2 +- src/lib/cc/simple_parser.h | 44 ++- src/lib/cc/tests/data_unittests.cc | 196 ++++++++++- src/lib/cc/tests/simple_parser_unittest.cc | 11 +- src/lib/dhcpsrv/Makefile.am | 2 +- src/lib/dhcpsrv/addr_utilities.cc | 77 ++++- src/lib/dhcpsrv/addr_utilities.h | 14 +- src/lib/dhcpsrv/cfg_4o6.h | 12 +- src/lib/dhcpsrv/cfg_db_access.cc | 66 +++- src/lib/dhcpsrv/cfg_db_access.h | 41 ++- src/lib/dhcpsrv/cfg_duid.cc | 38 ++- src/lib/dhcpsrv/cfg_duid.h | 10 +- src/lib/dhcpsrv/cfg_expiration.cc | 34 +- src/lib/dhcpsrv/cfg_expiration.h | 10 +- src/lib/dhcpsrv/cfg_host_operations.cc | 16 +- src/lib/dhcpsrv/cfg_host_operations.h | 10 +- src/lib/dhcpsrv/cfg_iface.cc | 32 +- src/lib/dhcpsrv/cfg_iface.h | 10 +- src/lib/dhcpsrv/cfg_mac_source.cc | 68 ++-- src/lib/dhcpsrv/cfg_mac_source.h | 8 +- src/lib/dhcpsrv/cfg_option.cc | 91 ++++- src/lib/dhcpsrv/cfg_option.h | 10 +- src/lib/dhcpsrv/cfg_option_def.cc | 59 +++- src/lib/dhcpsrv/cfg_option_def.h | 10 +- src/lib/dhcpsrv/cfg_rsoo.cc | 16 +- src/lib/dhcpsrv/cfg_rsoo.h | 10 +- src/lib/dhcpsrv/cfg_subnets4.cc | 114 ++++++- src/lib/dhcpsrv/cfg_subnets4.h | 10 +- src/lib/dhcpsrv/cfg_subnets6.cc | 181 +++++++++- src/lib/dhcpsrv/cfg_subnets6.h | 10 +- src/lib/dhcpsrv/cfgmgr.cc | 3 +- src/lib/dhcpsrv/cfgmgr.h | 13 + src/lib/dhcpsrv/client_class_def.cc | 46 ++- src/lib/dhcpsrv/client_class_def.h | 32 +- src/lib/dhcpsrv/d2_client_cfg.cc | 47 ++- src/lib/dhcpsrv/d2_client_cfg.h | 11 +- src/lib/dhcpsrv/logging_info.cc | 58 +++- src/lib/dhcpsrv/logging_info.h | 10 +- src/lib/dhcpsrv/parsers/client_class_def_parser.cc | 115 +++---- src/lib/dhcpsrv/parsers/dbaccess_parser.h | 2 +- src/lib/dhcpsrv/parsers/dhcp_parsers.cc | 109 ++---- src/lib/dhcpsrv/parsers/dhcp_parsers.h | 31 +- src/lib/dhcpsrv/parsers/duid_config_parser.cc | 139 +++----- src/lib/dhcpsrv/parsers/duid_config_parser.h | 51 --- .../dhcpsrv/parsers/expiration_config_parser.cc | 63 ++-- src/lib/dhcpsrv/parsers/ifaces_config_parser.h | 4 +- src/lib/dhcpsrv/pool.h | 4 +- src/lib/dhcpsrv/srv_config.cc | 98 ++++++ src/lib/dhcpsrv/srv_config.h | 14 +- src/lib/dhcpsrv/subnet.h | 12 +- src/lib/dhcpsrv/tests/Makefile.am | 1 + src/lib/dhcpsrv/tests/addr_utilities_unittest.cc | 91 ++++- src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc | 23 +- src/lib/dhcpsrv/tests/cfg_duid_unittest.cc | 28 +- src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc | 16 +- .../dhcpsrv/tests/cfg_host_operations_unittest.cc | 10 +- src/lib/dhcpsrv/tests/cfg_iface_unittest.cc | 35 +- src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc | 39 ++- src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc | 58 +++- src/lib/dhcpsrv/tests/cfg_option_unittest.cc | 44 ++- src/lib/dhcpsrv/tests/cfg_rsoo_unittest.cc | 11 +- src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc | 118 ++++++- src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc | 178 +++++++++- src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 12 + .../tests/client_class_def_parser_unittest.cc | 42 +-- src/lib/dhcpsrv/tests/client_class_def_unittest.cc | 80 ++++- src/lib/dhcpsrv/tests/d2_client_unittest.cc | 23 +- src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc | 367 ++++++++++++++++++--- .../dhcpsrv/tests/duid_config_parser_unittest.cc | 20 +- .../tests/expiration_config_parser_unittest.cc | 9 - .../dhcpsrv/tests/ifaces_config_parser_unittest.cc | 14 +- src/lib/dhcpsrv/tests/logging_info_unittest.cc | 18 +- src/lib/dhcpsrv/tests/srv_config_unittest.cc | 66 +++- src/lib/dhcpsrv/tests/subnet_unittest.cc | 10 +- src/lib/hooks/hooks_config.cc | 4 +- src/lib/testutils/Makefile.am | 1 + 91 files changed, 3238 insertions(+), 642 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/configure.ac b/configure.ac index 2d7b098732..f8fa48a12f 100644 --- a/configure.ac +++ b/configure.ac @@ -167,12 +167,11 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do feature="final method" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( - [], [class Foo { public: virtual ~Foo() {}; virtual void bar() final; - };])], + };],[])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) continue]) @@ -201,6 +200,31 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do [AC_MSG_RESULT([no]) continue]) + AC_MSG_CHECKING(static_assert support) + feature="static_assert" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [static_assert(1 + 1 == 2, "");], + [])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + continue]) + + AC_MSG_CHECKING(template alias) + feature="template alias" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [template + class I { + public: int get() { return i; }; + }; + using Zero = I<0>;], + [Zero Z; + return Z.get();])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + continue]) + AC_MSG_CHECKING(lambda support) feature="lambda" AC_COMPILE_IFELSE( @@ -213,6 +237,19 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do continue]) done +# Check for std::is_base_of support +AC_MSG_CHECKING([for std::is_base_of]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include + class A {}; + class B : A {};] + [static_assert(std::is_base_of::value, "");])], + [AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_IS_BASE_OF], [1], + [Define to 1 if std::is_base_of is available])], + [AC_MSG_RESULT(no)]) + dnl Determine if we are using GNU sed GNU_SED=no $SED --version 2> /dev/null | grep GNU > /dev/null 2>&1 @@ -1415,6 +1452,31 @@ if test $enable_gtest != "no"; then CPPFLAGS=$CPPFLAGS_SAVED fi +# Check for CreateUnifiedDiff from gtest >= 1.8.0 +if test $enable_gtest != "no"; then + AC_MSG_CHECKING([for CreateUnifiedDiff in $GTEST_INCLUDES/gtest.h]) + CPPFLAGS_SAVED=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES $GTEST_INCLUDES" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [#include + #include + #include + #include + std::string nodiff(std::string text) { + std::vector lines; + boost::split(lines, text, boost::is_any_of("\n")); + using namespace testing::internal; + return (edit_distance::CreateUnifiedDiff(lines, lines)); + }], + [return 0;])], + [AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_CREATE_UNIFIED_DIFF], [1], + [Define to 1 if gtest defines edit_distance::CreateUnifiedDiff])], + [AC_MSG_RESULT(no)]) + CPPFLAGS=$CPPFLAGS_SAVED +fi + # # ASIO: we extensively use it as the C++ event management module. # @@ -1467,11 +1529,11 @@ AC_ARG_ENABLE(generate_parser, [AC_HELP_STRING([--enable-generate-parser], enable_generate_parser=$enableval, enable_generate_parser=no) # Check if flex is avaible. Flex is not needed for building Kea sources, -# unless you want to regenerate grammar in src/lib/eval +# unless you want to regenerate grammars AC_PROG_LEX # Check if bison is available. Bison is not needed for building Kea sources, -# unless you want to regenerate grammar in src/lib/eval +# unless you want to regenerate grammars AC_PROG_YACC if test "x$enable_generate_parser" != "xno"; then @@ -1485,7 +1547,7 @@ if test "x$enable_generate_parser" != "xno"; then fi # Ok, let's check if we have at least 3.0.0 version of the bison. The code used -# to generate src/lib/eval parser is roughly based on bison 3.0 examples. +# to generate parsers is roughly based on bison 3.0 examples. cat > bisontest.y << EOF %require "3.0.0" %token X @@ -1599,20 +1661,28 @@ AM_COND_IF([HAVE_OPTRESET], [AC_DEFINE([HAVE_OPTRESET], [1], [Check for optreset AC_DEFINE([CONFIG_H_WAS_INCLUDED], [1], [config.h inclusion marker]) -AC_CONFIG_FILES([compatcheck/Makefile +AC_CONFIG_FILES([Makefile + compatcheck/Makefile dns++.pc - doc/design/datasrc/Makefile + doc/Makefile doc/design/Makefile + doc/design/datasrc/Makefile doc/guide/Makefile - doc/Makefile doc/version.ent + ext/Makefile ext/coroutine/Makefile ext/gtest/Makefile - ext/Makefile m4macros/Makefile - Makefile src/Makefile src/bin/Makefile + src/bin/admin/Makefile + src/bin/admin/kea-admin + src/bin/admin/tests/Makefile + src/bin/admin/tests/cql_tests.sh + src/bin/admin/tests/data/Makefile + src/bin/admin/tests/memfile_tests.sh + src/bin/admin/tests/mysql_tests.sh + src/bin/admin/tests/pgsql_tests.sh src/bin/agent/Makefile src/bin/agent/tests/Makefile src/bin/agent/tests/ca_process_tests.sh @@ -1644,15 +1714,6 @@ AC_CONFIG_FILES([compatcheck/Makefile src/bin/perfdhcp/Makefile src/bin/perfdhcp/tests/Makefile src/bin/perfdhcp/tests/testdata/Makefile - src/bin/admin/Makefile - src/bin/admin/kea-admin - src/bin/admin/tests/Makefile - src/bin/admin/tests/data/Makefile - src/bin/admin/tests/memfile_tests.sh - src/bin/admin/tests/mysql_tests.sh - src/bin/admin/tests/pgsql_tests.sh - src/bin/admin/tests/cql_tests.sh - src/bin/agent/tests/test_libraries.h src/hooks/Makefile src/hooks/dhcp/Makefile src/hooks/dhcp/user_chk/Makefile @@ -1685,6 +1746,8 @@ AC_CONFIG_FILES([compatcheck/Makefile src/lib/dns/gen-rdatacode.py src/lib/dns/tests/Makefile src/lib/dns/tests/testdata/Makefile + src/lib/eval/Makefile + src/lib/eval/tests/Makefile src/lib/exceptions/Makefile src/lib/exceptions/tests/Makefile src/lib/hooks/Makefile @@ -1710,10 +1773,10 @@ AC_CONFIG_FILES([compatcheck/Makefile src/lib/process/spec_config.h.pre src/lib/process/tests/Makefile src/lib/process/testutils/Makefile - src/lib/testutils/Makefile - src/lib/testutils/dhcp_test_lib.sh src/lib/stats/Makefile src/lib/stats/tests/Makefile + src/lib/testutils/Makefile + src/lib/testutils/dhcp_test_lib.sh src/lib/util/Makefile src/lib/util/io/Makefile src/lib/util/python/Makefile @@ -1723,11 +1786,10 @@ AC_CONFIG_FILES([compatcheck/Makefile src/lib/util/threads/Makefile src/lib/util/threads/tests/Makefile src/lib/util/unittests/Makefile - src/lib/eval/Makefile - src/lib/eval/tests/Makefile src/share/Makefile src/share/database/Makefile src/share/database/scripts/Makefile + src/share/database/scripts/cql/Makefile src/share/database/scripts/mysql/Makefile src/share/database/scripts/mysql/upgrade_1.0_to_2.0.sh src/share/database/scripts/mysql/upgrade_2.0_to_3.0.sh @@ -1737,17 +1799,16 @@ AC_CONFIG_FILES([compatcheck/Makefile src/share/database/scripts/pgsql/Makefile src/share/database/scripts/pgsql/upgrade_1.0_to_2.0.sh src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh - src/share/database/scripts/cql/Makefile tools/Makefile tools/path_replacer.sh ]) - AC_CONFIG_COMMANDS([permissions], [ +AC_CONFIG_COMMANDS([permissions], [ + chmod +x src/bin/admin/kea-admin chmod +x src/bin/dhcp4/tests/dhcp4_process_tests.sh chmod +x src/bin/dhcp6/tests/dhcp6_process_tests.sh chmod +x src/bin/keactrl/keactrl chmod +x src/bin/keactrl/tests/keactrl_tests.sh - chmod +x src/bin/admin/kea-admin chmod +x src/lib/dns/gen-rdatacode.py chmod +x src/lib/log/tests/console_test.sh chmod +x src/lib/log/tests/destination_test.sh diff --git a/src/bin/dhcp4/dhcp4to6_ipc.cc b/src/bin/dhcp4/dhcp4to6_ipc.cc index 573078cb3e..b219207265 100644 --- a/src/bin/dhcp4/dhcp4to6_ipc.cc +++ b/src/bin/dhcp4/dhcp4to6_ipc.cc @@ -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 @@ -35,7 +35,7 @@ Dhcp4to6Ipc& Dhcp4to6Ipc::instance() { } void Dhcp4to6Ipc::open() { - uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port(); + uint16_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port(); if (port == 0) { Dhcp4o6IpcBase::close(); return; @@ -45,8 +45,7 @@ void Dhcp4to6Ipc::open() { } int old_fd = socket_fd_; - socket_fd_ = Dhcp4o6IpcBase::open(static_cast(port), - ENDPOINT_TYPE_V4); + socket_fd_ = Dhcp4o6IpcBase::open(port, ENDPOINT_TYPE_V4); if ((old_fd == -1) && (socket_fd_ != old_fd)) { IfaceMgr::instance().addExternalSocket(socket_fd_, Dhcp4to6Ipc::handler); diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index f809d742b6..fafa485174 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -341,24 +341,9 @@ public: cfg->setDeclinePeriod(probation_period); // Set the DHCPv4-over-DHCPv6 interserver port. - // @todo Change for uint16_t - uint32_t dhcp4o6_port = getUint32(global, "dhcp4o6-port"); + uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port"); cfg->setDhcp4o6Port(dhcp4o6_port); } - -private: - - /// @brief Returns a value converted to uint32_t - /// - /// Instantiation of getIntType() to uint32_t - /// - /// @param scope specified parameter will be extracted from this scope - /// @param name name of the parameter - /// @return an uint32_t value - uint32_t getUint32(isc::data::ConstElementPtr scope, - const std::string& name) { - return (getIntType(scope, name)); - } }; } // anonymous namespace diff --git a/src/bin/dhcp4/main.cc b/src/bin/dhcp4/main.cc index dc09de4a7b..b1997db0a8 100644 --- a/src/bin/dhcp4/main.cc +++ b/src/bin/dhcp4/main.cc @@ -124,6 +124,9 @@ main(int argc, char* argv[]) { usage(); } + // This is the DHCPv4 server + CfgMgr::instance().setFamily(AF_INET); + if (check_mode) { try { diff --git a/src/bin/dhcp4/simple_parser4.cc b/src/bin/dhcp4/simple_parser4.cc index a02b2a8c04..5487c7913a 100644 --- a/src/bin/dhcp4/simple_parser4.cc +++ b/src/bin/dhcp4/simple_parser4.cc @@ -45,8 +45,7 @@ const SimpleDefaults SimpleParser4::OPTION4_DEF_DEFAULTS = { /// for those option-data declarations. const SimpleDefaults SimpleParser4::OPTION4_DEFAULTS = { { "space", Element::string, "dhcp4"}, - { "csv-format", Element::boolean, "true"}, - { "encapsulate", Element::string, "" } + { "csv-format", Element::boolean, "true"} }; /// @brief This table defines default global values for DHCPv4 diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 61844b2978..3f281b6770 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -4557,14 +4557,6 @@ TEST_F(Dhcp4ParserTest, invalidClientClassDictionary) { " } ] \n" "} \n"; - ConstElementPtr json; - ASSERT_NO_THROW(json = parseJSON(config)); - - ConstElementPtr status; - EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); - ASSERT_TRUE(status); - checkResult(status, 1); - EXPECT_THROW(parseDHCP4(config), Dhcp4ParseError); } diff --git a/src/bin/dhcp4/tests/simple_parser4_unittest.cc b/src/bin/dhcp4/tests/simple_parser4_unittest.cc index 72957c47e8..0f6d0f1d99 100644 --- a/src/bin/dhcp4/tests/simple_parser4_unittest.cc +++ b/src/bin/dhcp4/tests/simple_parser4_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -176,7 +176,6 @@ TEST_F(SimpleParser4Test, optionDataDefaults4) { // we should have appropriate default value set. See // SimpleParser4::OPTION4_DEFAULTS for a list of default values. checkStringValue(option, "space", "dhcp4"); - checkStringValue(option, "encapsulate", ""); checkBoolValue(option, "csv-format", true); } diff --git a/src/bin/dhcp6/dhcp6to4_ipc.cc b/src/bin/dhcp6/dhcp6to4_ipc.cc index 72001a4f87..7ec9397973 100644 --- a/src/bin/dhcp6/dhcp6to4_ipc.cc +++ b/src/bin/dhcp6/dhcp6to4_ipc.cc @@ -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 @@ -34,7 +34,7 @@ Dhcp6to4Ipc& Dhcp6to4Ipc::instance() { } void Dhcp6to4Ipc::open() { - uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port(); + uint16_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port(); if (port == 0) { Dhcp4o6IpcBase::close(); return; @@ -44,8 +44,7 @@ void Dhcp6to4Ipc::open() { } int old_fd = socket_fd_; - socket_fd_ = Dhcp4o6IpcBase::open(static_cast(port), - ENDPOINT_TYPE_V6); + socket_fd_ = Dhcp4o6IpcBase::open(port, ENDPOINT_TYPE_V6); if ((old_fd == -1) && (socket_fd_ != old_fd)) { IfaceMgr::instance().addExternalSocket(socket_fd_, Dhcp6to4Ipc::handler); diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 59b021d65f..7e11de03a6 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -214,18 +214,6 @@ public: private: - /// @brief Get an uint8_t value - /// - /// Instantiation of getIntType() to uint8_t - /// - /// @param scope specified parameter will be extracted from this scope - /// @param name name of the parameter - /// @return uint8_t value - /// @throw isc::dhcp::DhcpConfigError when it is not an uint8_t - uint8_t getUint8(ConstElementPtr scope, const std::string& name) { - return (getIntType(scope, name)); - } - /// Pointer to the created pool object. isc::dhcp::Pool6Ptr pool_; @@ -547,24 +535,9 @@ public: srv_config->setDeclinePeriod(probation_period); // Set the DHCPv4-over-DHCPv6 interserver port. - // @todo Change for uint16_t - uint32_t dhcp4o6_port = getUint32(global, "dhcp4o6-port"); + uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port"); srv_config->setDhcp4o6Port(dhcp4o6_port); } - -private: - - /// @brief Returns a value converted to uint32_t - /// - /// Instantiation of getIntType() to uint32_t - /// - /// @param scope specified parameter will be extracted from this scope - /// @param name name of the parameter - /// @return an uint32_t value - uint32_t getUint32(isc::data::ConstElementPtr scope, - const std::string& name) { - return (getIntType(scope, name)); - } }; } // anonymous namespace diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc index cc5c9e766e..6d848aa964 100644 --- a/src/bin/dhcp6/main.cc +++ b/src/bin/dhcp6/main.cc @@ -127,6 +127,9 @@ main(int argc, char* argv[]) { usage(); } + // This is the DHCPv6 server + CfgMgr::instance().setFamily(AF_INET6); + if (check_mode) { try { // We need to initialize logging, in case any error messages are to be printed. diff --git a/src/bin/dhcp6/simple_parser6.cc b/src/bin/dhcp6/simple_parser6.cc index a3e53fa552..c6883cd70d 100644 --- a/src/bin/dhcp6/simple_parser6.cc +++ b/src/bin/dhcp6/simple_parser6.cc @@ -45,8 +45,7 @@ const SimpleDefaults SimpleParser6::OPTION6_DEF_DEFAULTS = { /// for those option-data declarations. const SimpleDefaults SimpleParser6::OPTION6_DEFAULTS = { { "space", Element::string, "dhcp6"}, - { "csv-format", Element::boolean, "true"}, - { "encapsulate", Element::string, "" } + { "csv-format", Element::boolean, "true"} }; /// @brief This table defines default global values for DHCPv6 diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 93f8c047af..1d4ec0bfe2 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -4969,13 +4969,6 @@ TEST_F(Dhcp6ParserTest, invalidClientClassDictionary) { " } ] \n" "} \n"; - ConstElementPtr json = parseJSON(config); - - ConstElementPtr status; - EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); - ASSERT_TRUE(status); - checkResult(status, 1); - EXPECT_THROW(parseDHCP6(config), Dhcp6ParseError); } diff --git a/src/bin/dhcp6/tests/simple_parser6_unittest.cc b/src/bin/dhcp6/tests/simple_parser6_unittest.cc index 2f09c46d3a..db2995e0c6 100644 --- a/src/bin/dhcp6/tests/simple_parser6_unittest.cc +++ b/src/bin/dhcp6/tests/simple_parser6_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -159,7 +159,7 @@ TEST_F(SimpleParser6Test, subnetDefaults6) { // This test checks if the parameters in option-data are assigned default values // if not explicitly specified. -TEST_F(SimpleParser6Test, optionDataDefaults4) { +TEST_F(SimpleParser6Test, optionDataDefaults6) { ElementPtr global = parseJSON("{ \"renew-timer\": 1," " \"rebind-timer\": 2," " \"preferred-lifetime\": 3," @@ -179,7 +179,6 @@ TEST_F(SimpleParser6Test, optionDataDefaults4) { // we should have appropriate default value set. See // SimpleParser4::OPTION4_DEFAULTS for a list of default values. checkStringValue(option, "space", "dhcp6"); - checkStringValue(option, "encapsulate", ""); checkBoolValue(option, "csv-format", true); } diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc index a733923230..b52a6ab5af 100644 --- a/src/lib/cc/data.cc +++ b/src/lib/cc/data.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1067,6 +1068,241 @@ merge(ElementPtr element, ConstElementPtr other) { } } +ElementPtr +copy(ConstElementPtr from, int level) { + if (isNull(from)) { + isc_throw(BadValue, "copy got a null pointer"); + } + int from_type = from->getType(); + if (from_type == Element::integer) { + return (ElementPtr(new IntElement(from->intValue()))); + } else if (from_type == Element::real) { + return (ElementPtr(new DoubleElement(from->doubleValue()))); + } else if (from_type == Element::boolean) { + return (ElementPtr(new BoolElement(from->boolValue()))); + } else if (from_type == Element::null) { + return (ElementPtr(new NullElement())); + } else if (from_type == Element::string) { + return (ElementPtr(new StringElement(from->stringValue()))); + } else if (from_type == Element::list) { + ElementPtr result = ElementPtr(new ListElement()); + typedef std::vector ListType; + const ListType& value = from->listValue(); + for (ListType::const_iterator it = value.cbegin(); + it != value.cend(); ++it) { + if (level == 0) { + result->add(*it); + } else { + result->add(copy(*it, level - 1)); + } + } + return (result); + } else if (from_type == Element::map) { + ElementPtr result = ElementPtr(new MapElement()); + typedef std::map MapType; + const MapType& value = from->mapValue(); + for (MapType::const_iterator it = value.cbegin(); + it != value.cend(); ++it) { + if (level == 0) { + result->set(it->first, it->second); + } else { + result->set(it->first, copy(it->second, level - 1)); + } + } + return (result); + } else { + isc_throw(BadValue, "copy got an element of type: " << from_type); + } +} + +namespace { + +// Helper function which blocks infinite recursion +bool +isEquivalent0(ConstElementPtr a, ConstElementPtr b, unsigned level) +{ + // check looping forever on cycles + if (!level) { + isc_throw(BadValue, "isEquivalent got infinite recursion: " + "arguments include cycles"); + } + if (!a || !b) { + isc_throw(BadValue, "isEquivalent got a null pointer"); + } + // check types + if (a->getType() != b->getType()) { + return (false); + } + if (a->getType() == Element::list) { + // check empty + if (a->empty()) { + return (b->empty()); + } + // check size + if (a->size() != b->size()) { + return (false); + } + + // copy b into a list + const size_t s = a->size(); + typedef std::list ListType; + ListType l; + for (size_t i = 0; i < s; ++i) { + l.push_back(b->get(i)); + } + + // iterate on a + for (size_t i = 0; i < s; ++i) { + ConstElementPtr item = a->get(i); + // lookup this item in the list + bool found = false; + for (ListType::iterator it = l.begin(); + it != l.end(); ++it) { + // if found in the list remove it + if (isEquivalent0(item, *it, level - 1)) { + found = true; + l.erase(it); + break; + } + } + // if not found argument differs + if (!found) { + return (false); + } + } + + // sanity check: the list must be empty + if (!l.empty()) { + isc_throw(Unexpected, "isEquivalent internal error"); + } + return (true); + } else if (a->getType() == Element::map) { + // iterate on the first map + typedef std::map MapType; + const MapType& ma = a->mapValue(); + for (MapType::const_iterator it = ma.begin(); + it != ma.end() ; ++it) { + // get the b value for the given keyword and recurse + ConstElementPtr item = b->get(it->first); + if (!item || !isEquivalent0(it->second, item, level - 1)) { + return (false); + } + } + // iterate on the second map + const MapType& mb = b->mapValue(); + for (MapType::const_iterator it = mb.begin(); + it != mb.end() ; ++it) { + // check if the keyword exists + if (!a->contains(it->first)) { + return (false); + } + } + return (true); + } else { + return (a->equals(*b)); + } +} + +} + +bool +isEquivalent(ConstElementPtr a, ConstElementPtr b) { + return (isEquivalent0(a, b, 100)); +} + +void +prettyPrint(ConstElementPtr element, std::ostream& out, + unsigned indent, unsigned step) { + if (!element) { + isc_throw(BadValue, "prettyPrint got a null pointer"); + } + if (element->getType() == Element::list) { + // empty list case + if (element->empty()) { + out << "[ ]"; + return; + } + + // complex ? multiline : oneline + if (!element->get(0)) { + isc_throw(BadValue, "prettyPrint got a null pointer"); + } + int first_type = element->get(0)->getType(); + bool complex = false; + if ((first_type == Element::list) || (first_type == Element::map)) { + complex = true; + } + std::string separator = complex ? ",\n" : ", "; + + // open the list + out << "[" << (complex ? "\n" : " "); + + // iterate on items + typedef std::vector ListType; + const ListType& l = element->listValue(); + for (ListType::const_iterator it = l.begin(); + it != l.end(); ++it) { + // add the separator if not the first item + if (it != l.begin()) { + out << separator; + } + // add indentation + if (complex) { + out << std::string(indent + step, ' '); + } + // recursive call + prettyPrint(*it, out, indent + step, step); + } + + // close the list + if (complex) { + out << "\n" << std::string(indent, ' '); + } else { + out << " "; + } + out << "]"; + } else if (element->getType() == Element::map) { + // empty map case + if (element->size() == 0) { + out << "{ }"; + return; + } + + // open the map + out << "{\n"; + + // iterate on keyword: value + typedef std::map MapType; + const MapType& m = element->mapValue(); + for (MapType::const_iterator it = m.begin(); + it != m.end(); ++it) { + // add the separator if not the first item + if (it != m.begin()) { + out << ",\n"; + } + // add indentation + out << std::string(indent + step, ' '); + // add keyword: + out << "\"" << it->first << "\": "; + // recusive call + prettyPrint(it->second, out, indent + step, step); + } + + // close the map + out << "\n" << std::string(indent, ' ') << "}"; + } else { + // not a list or a map + element->toJSON(out); + } +} + +std::string +prettyPrint(ConstElementPtr element, unsigned indent, unsigned step) { + std::stringstream ss; + prettyPrint(element, ss, indent, step); + return (ss.str()); +} + void Element::preprocess(std::istream& in, std::stringstream& out) { std::string line; diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h index 95feb6adee..3d1b65e3e9 100644 --- a/src/lib/cc/data.h +++ b/src/lib/cc/data.h @@ -732,6 +732,50 @@ ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b); /// Raises a TypeError if either ElementPtr is not a MapElement void merge(ElementPtr element, ConstElementPtr other); +/// \brief Copy the data up to a nesting level. +/// +/// The copy is a deep copy so nothing is shared if it is not +/// under the given nesting level. +/// +/// \param from the pointer to the element to copy +/// \param level nesting level (default is 100, 0 means shallow copy, +/// negative means outbound and perhaps looping forever). +/// \return a pointer to a fresh copy +/// \throw raises a BadValue is a null pointer occurs. +ElementPtr copy(ConstElementPtr from, int level = 100); + +/// \brief Compares the data with other using unordered lists +/// +/// This comparison function handles lists (JSON arrays) as +/// unordered multi sets (multi means an item can occurs more +/// than once as soon as it occurs the same number of times). +bool isEquivalent(ConstElementPtr a, ConstElementPtr b); + +/// \brief Pretty prints the data into stream. +/// +/// This operator converts the \c ConstElementPtr into a string and +/// inserts it into the output stream \c out with an initial +/// indentation \c indent and add at each level \c step spaces. +/// +/// \param element A \c ConstElementPtr to pretty print +/// \param out A \c std::ostream on which the print operation is performed +/// \param indent An initial number of spaces to add each new line +/// \param step A number of spaces to add to indentation at a new level +void prettyPrint(ConstElementPtr element, std::ostream& out, + unsigned indent = 0, unsigned step = 2); + +/// \brief Pretty prints the data into string +/// +/// This operator converts the \c ConstElementPtr into a string with +/// an initial indentation \c indent and add at each level \c step spaces. +/// +/// \param element A \c ConstElementPtr to pretty print +/// \param indent An initial number of spaces to add each new line +/// \param step A number of spaces to add to indentation at a new level +/// \return a string where element was pretty printed +std::string prettyPrint(ConstElementPtr element, + unsigned indent = 0, unsigned step = 2); + /// /// \brief Insert Element::Position as a string into stream. /// diff --git a/src/lib/cc/simple_parser.cc b/src/lib/cc/simple_parser.cc index f5d91dc966..240576d3ba 100644 --- a/src/lib/cc/simple_parser.cc +++ b/src/lib/cc/simple_parser.cc @@ -74,7 +74,7 @@ SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr p } ConstElementPtr elem = parent->get(name); if (!elem) { - return (data::Element::ZERO_POSITION()); + return (parent->getPosition()); } return (elem->getPosition()); } diff --git a/src/lib/cc/simple_parser.h b/src/lib/cc/simple_parser.h index a7352a5ed3..3ff60b7bb8 100644 --- a/src/lib/cc/simple_parser.h +++ b/src/lib/cc/simple_parser.h @@ -104,9 +104,9 @@ class SimpleParser { /// @brief Utility method that returns position of an element /// - /// It's mostly useful for logging. When any necessary parameter is - /// missing (either parent is null or it doesn't contain specified - /// name) ZERO_POSITION is returned. + /// It's mostly useful for logging. If the element is missing + /// the parent position is returned or ZERO_POSITION if parent + /// is null. /// /// @param name position of that element will be returned /// @param parent parent element (optional) @@ -203,6 +203,44 @@ protected: << "' (" << getPosition(name, scope) << ")"); } } + + /// @brief Returns a value converted to uint32_t + /// + /// Instantiation of getIntType() to uint32_t + /// + /// @param scope specified parameter will be extracted from this scope + /// @param name name of the parameter + /// @return an uint32_t value + /// @throw isc::dhcp::DhcpConfigError when it is not an uint32_t + uint32_t getUint32(isc::data::ConstElementPtr scope, + const std::string& name) { + return (getIntType(scope, name)); + } + + /// @brief Returns a value converted to uint16_t + /// + /// Instantiation of getIntType() to uint16_t + /// + /// @param scope specified parameter will be extracted from this scope + /// @param name name of the parameter + /// @return an uint16_t value + /// @throw isc::dhcp::DhcpConfigError when it is not an uint16_t + uint16_t getUint16(isc::data::ConstElementPtr scope, + const std::string& name) { + return (getIntType(scope, name)); + } + + /// @brief Get an uint8_t value + /// + /// Instantiation of getIntType() to uint8_t + /// + /// @param scope specified parameter will be extracted from this scope + /// @param name name of the parameter + /// @return uint8_t value + /// @throw isc::dhcp::DhcpConfigError when it is not an uint8_t + uint8_t getUint8(ConstElementPtr scope, const std::string& name) { + return (getIntType(scope, name)); + } }; }; diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc index 1c8054a800..e1455ec56f 100644 --- a/src/lib/cc/tests/data_unittests.cc +++ b/src/lib/cc/tests/data_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2009-2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2009-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 @@ -6,10 +6,12 @@ #include #include +#include #include #include #include +#include using namespace isc::data; @@ -100,7 +102,7 @@ TEST(Element, from_and_to_json) { BOOST_FOREACH(const std::string& s, sv) { // Test two types of fromJSON(): with string and istream. - for (int i = 0; i < 2; ++i) { + for (unsigned i = 0; i < 2; ++i) { // test << operator, which uses Element::str() if (i == 0) { el = Element::fromJSON(s); @@ -555,6 +557,35 @@ TEST(Element, escape) { EXPECT_NO_THROW(Element::fromJSON("\"\\\"\\\"\"")); // A whitespace test EXPECT_NO_THROW(Element::fromJSON("\" \n \r \t \f \n \n \t\"")); + // Escape for forward slash is optional + ASSERT_NO_THROW(Element::fromJSON("\"foo\\/bar\"")); + EXPECT_EQ("foo/bar", Element::fromJSON("\"foo\\/bar\"")->stringValue()); + // Control characters + StringElement bell("foo\abar"); + EXPECT_EQ("\"foo\\u0007bar\"", bell.str()); +} + +// This test verifies that strings are copied. +TEST(Element, stringCopy) { + // StringElement constructor copies its string argument. + std::string foo = "foo"; + ElementPtr elem = ElementPtr(new StringElement(foo)); + EXPECT_EQ(foo, elem->stringValue()); + foo[1] = 'O'; + EXPECT_EQ("fOo", foo); + EXPECT_NE(foo, elem->stringValue()); + + // Map keys are copied too. + ElementPtr map = ElementPtr(new MapElement()); + std::string bar = "bar"; + map->set(bar, ElementPtr(new IntElement(1))); + ConstElementPtr item = map->get("bar"); + ASSERT_TRUE(item); + EXPECT_EQ(1, item->intValue()); + bar[0] = 'B'; + EXPECT_EQ("Bar", bar); + EXPECT_TRUE(map->get("bar")); + EXPECT_FALSE(map->get(bar)); } // This test verifies that a backslash can be used in element content @@ -655,6 +686,12 @@ TEST(Element, MapElement) { el->set(long_maptag, Element::create("bar")); EXPECT_EQ("bar", el->find(long_maptag)->stringValue()); + // Null pointer value + el.reset(new MapElement()); + ConstElementPtr null_ptr; + el->set("value", null_ptr); + EXPECT_FALSE(el->get("value")); + EXPECT_EQ("{ \"value\": None }", el->str()); } TEST(Element, to_and_from_wire) { @@ -857,8 +894,9 @@ TEST(Element, constRemoveIdentical) { c = Element::fromJSON("{ \"a\": 1 }"); EXPECT_EQ(*removeIdentical(a, b), *c); - EXPECT_THROW(removeIdentical(Element::create(1), Element::create(2)), - TypeError); + // removeIdentical() is overloaded so force the first argument to const + ConstElementPtr bad = Element::create(1); + EXPECT_THROW(removeIdentical(bad, Element::create(2)), TypeError); } TEST(Element, merge) { @@ -947,6 +985,152 @@ TEST(Element, merge) { } +// This test checks copy. +TEST(Element, copy) { + // Null pointer + ElementPtr elem; + EXPECT_THROW(copy(elem, 0), isc::BadValue); + EXPECT_THROW(copy(elem), isc::BadValue); + EXPECT_THROW(copy(elem, -1), isc::BadValue); + + // Basic types + elem.reset(new IntElement(1)); + EXPECT_TRUE(elem->equals(*Element::fromJSON("1"))); + EXPECT_EQ("1", elem->str()); + ElementPtr copied; + ASSERT_NO_THROW(copied = copy(elem, 0)); + EXPECT_TRUE(elem->equals(*copied)); + + elem.reset(new DoubleElement(1.0)); + EXPECT_TRUE(elem->equals(*Element::fromJSON("1.0"))); + ASSERT_NO_THROW(copied = copy(elem, 0)); + EXPECT_TRUE(elem->equals(*copied)); + + elem.reset(new BoolElement(true)); + EXPECT_TRUE(elem->equals(*Element::fromJSON("true"))); + ASSERT_NO_THROW(copied = copy(elem, 0)); + EXPECT_TRUE(elem->equals(*copied)); + + elem.reset(new NullElement()); + EXPECT_TRUE(elem->equals(*Element::fromJSON("null"))); + ASSERT_NO_THROW(copied = copy(elem, 0)); + EXPECT_TRUE(elem->equals(*copied)); + + elem.reset(new StringElement("foo")); + EXPECT_TRUE(elem->equals(*Element::fromJSON("\"foo\""))); + ASSERT_NO_THROW(copied = copy(elem, 0)); + EXPECT_TRUE(elem->equals(*copied)); + ASSERT_NO_THROW(elem->setValue(std::string("bar"))); + EXPECT_TRUE(elem->equals(*Element::fromJSON("\"bar\""))); + EXPECT_FALSE(elem->equals(*copied)); + + elem.reset(new ListElement()); + ElementPtr item = ElementPtr(new IntElement(1)); + elem->add(item); + EXPECT_TRUE(elem->equals(*Element::fromJSON("[ 1 ]"))); + ASSERT_NO_THROW(copied = copy(elem, 0)); + EXPECT_TRUE(elem->equals(*copied)); + ElementPtr deep; + ASSERT_NO_THROW(deep = copy(elem)); + EXPECT_TRUE(elem->equals(*deep)); + ASSERT_NO_THROW(item = elem->getNonConst(0)); + ASSERT_NO_THROW(item->setValue(2)); + EXPECT_TRUE(elem->equals(*Element::fromJSON("[ 2 ]"))); + EXPECT_TRUE(elem->equals(*copied)); + EXPECT_FALSE(elem->equals(*deep)); + + elem.reset(new MapElement()); + item.reset(new StringElement("bar")); + elem->set("foo", item); + EXPECT_TRUE(elem->equals(*Element::fromJSON("{ \"foo\": \"bar\" }"))); + ASSERT_NO_THROW(copied = copy(elem, 0)); + EXPECT_TRUE(elem->equals(*copied)); + ASSERT_NO_THROW(deep = copy(elem)); + EXPECT_TRUE(elem->equals(*deep)); + ASSERT_NO_THROW(item->setValue(std::string("Bar"))); + EXPECT_TRUE(elem->equals(*Element::fromJSON("{ \"foo\": \"Bar\" }"))); + EXPECT_TRUE(elem->equals(*copied)); + EXPECT_FALSE(elem->equals(*deep)); + + // Complex example + std::string input = "{ \n" + "\"integer\": 1,\n" + "\"double\": 1.0,\n" + "\"boolean\": true,\n" + "\"null\": null,\n" + "\"string\": \"foobar\",\n" + "\"list\": [ 1, 2 ],\n" + "\"map\": { \"foo\": \"bar\" } }\n"; + ConstElementPtr complex; + ASSERT_NO_THROW(complex = Element::fromJSON(input)); + ASSERT_NO_THROW(copied = copy(complex, 0)); + EXPECT_TRUE(copied->equals(*complex)); + ASSERT_NO_THROW(deep = copy(complex)); + EXPECT_TRUE(deep->equals(*complex)); + ElementPtr shallow; + ASSERT_NO_THROW(shallow = copy(complex, 1)); + EXPECT_TRUE(shallow->equals(*complex)); + // Try to modify copies + ASSERT_NO_THROW(item = deep->get("list")->getNonConst(1)); + ASSERT_NO_THROW(item->setValue(3)); + EXPECT_FALSE(deep->equals(*complex)); + EXPECT_TRUE(shallow->equals(*complex)); + ASSERT_NO_THROW(item = boost::const_pointer_cast(shallow->get("string"))); + ASSERT_NO_THROW(item->setValue(std::string("FooBar"))); + EXPECT_FALSE(shallow->equals(*complex)); + EXPECT_TRUE(copied->equals(*complex)); +} + +// This test checks the isEquivalent function. +TEST(Element, isEquivalent) { + // All are different but a is equivalent to b + string texta = "{ \"a\": 1, \"b\": [ ], \"c\": [ 1, 1, 2 ] }"; + string textb = "{ \"b\": [ ], \"a\": 1, \"c\": [ 1, 2, 1 ] }"; + string textc = "{ \"a\": 2, \"b\": [ ], \"c\": [ 1, 1, 2 ] }"; + string textd = "{ \"a\": 1, \"c\": [ ], \"b\": [ 1, 1, 2 ] }"; + string texte = "{ \"a\": 1, \"b\": [ ], \"c\": [ 1, 2, 2 ] }"; + + ElementPtr a = Element::fromJSON(texta); + ElementPtr b = Element::fromJSON(textb); + ElementPtr c = Element::fromJSON(textc); + ElementPtr d = Element::fromJSON(textd); + ElementPtr e = Element::fromJSON(texte); + + EXPECT_TRUE(isEquivalent(a, b)); + EXPECT_NE(a, b); + EXPECT_FALSE(isEquivalent(a, c)); + EXPECT_FALSE(isEquivalent(a, d)); + EXPECT_FALSE(isEquivalent(a, e)); + + // Verifies isEquivalent handles cycles + if (isc::util::unittests::runningOnValgrind()) { + ElementPtr l = Element::createList(); + l->add(l); + EXPECT_THROW(isEquivalent(l, l), isc::BadValue); + } +} + + +// This test checks the pretty print function. +TEST(Element, prettyPrint) { + + // default step is 2, order is alphabetic, no \n at the end + string text = "{\n" + " \"boolean\": true,\n" + " \"empty-list\": [ ],\n" + " \"empty-map\": { },\n" + " \"integer\": 1,\n" + " \"list\": [ 1, 2, 3 ],\n" + " \"map\": {\n" + " \"item\": null\n" + " },\n" + " \"string\": \"foobar\"\n" + "}"; + ElementPtr json = Element::fromJSON(text); + string pprinted = prettyPrint(json); + EXPECT_EQ(text, pprinted); +} + // This test checks whether it is possible to ignore comments. It also checks // that the comments are ignored only when told to. TEST(Element, preprocessor) { @@ -999,6 +1183,10 @@ TEST(Element, preprocessor) { EXPECT_THROW(Element::fromJSON(dbl_head_comment), JSONError); EXPECT_THROW(Element::fromJSON(dbl_mid_comment), JSONError); EXPECT_THROW(Element::fromJSON(dbl_tail_comment), JSONError); + + // For coverage + std::istringstream iss(no_comment); + EXPECT_TRUE(exp->equals(*Element::fromJSON(iss, true))); } TEST(Element, getPosition) { diff --git a/src/lib/cc/tests/simple_parser_unittest.cc b/src/lib/cc/tests/simple_parser_unittest.cc index dfe384c990..0a8bf57988 100644 --- a/src/lib/cc/tests/simple_parser_unittest.cc +++ b/src/lib/cc/tests/simple_parser_unittest.cc @@ -58,14 +58,9 @@ public: class SimpleParserClassTest : public SimpleParser { public: - /// @brief Instantiation of getIntType for uint8_t - /// - /// @param scope specified parameter will be extracted from this scope - /// @param name name of the parameter for error report - /// @return an uint8_t value - uint8_t getUint8(ConstElementPtr scope, const std::string& name) { - return (getIntType(scope, name)); - } + + /// Make getUint8 public + using SimpleParser::getUint8; /// @brief Instantiation of getAndConvert /// diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 6c8f48bb0f..004e3eb76f 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -85,7 +85,7 @@ libkea_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h libkea_dhcpsrv_la_SOURCES += alloc_engine_log.cc alloc_engine_log.h libkea_dhcpsrv_la_SOURCES += base_host_data_source.h libkea_dhcpsrv_la_SOURCES += callout_handle_store.h -libkea_dhcpsrv_la_SOURCES += cfg_4o6.h +libkea_dhcpsrv_la_SOURCES += cfg_4o6.cc cfg_4o6.h libkea_dhcpsrv_la_SOURCES += cfg_db_access.cc cfg_db_access.h libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h diff --git a/src/lib/dhcpsrv/addr_utilities.cc b/src/lib/dhcpsrv/addr_utilities.cc index b55d8ec2d5..2513a1cee8 100644 --- a/src/lib/dhcpsrv/addr_utilities.cc +++ b/src/lib/dhcpsrv/addr_utilities.cc @@ -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 @@ -32,6 +32,10 @@ const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, /// @brief mask used for first/last address calculation in a IPv6 prefix const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; +/// @brief mask used for IPv6 prefix calculation +const uint8_t revMask6[]= { 0xff, 0x7f, 0x3f, 0x1f, 0xf, 0x7, 0x3, 0x1 }; + + /// @brief calculates the first IPv6 address in a IPv6 prefix /// /// Note: This is a private function. Do not use it directly. @@ -270,6 +274,77 @@ addrsInRange(const isc::asiolink::IOAddress& min, } } +int +prefixLengthFromRange(const isc::asiolink::IOAddress& min, + const isc::asiolink::IOAddress& max) { + if (min.getFamily() != max.getFamily()) { + isc_throw(BadValue, "Both addresses have to be the same family"); + } + + if (max < min) { + isc_throw(BadValue, min.toText() << " must not be greater than " + << max.toText()); + } + + if (min.isV4()) { + // Get addresses as integers + uint32_t max_numeric = max.toUint32(); + uint32_t min_numeric = min.toUint32(); + + // Get the exclusive or which must be one of the bit masks + uint32_t xor_numeric = max_numeric ^ min_numeric; + for (uint8_t prefix_len = 0; prefix_len <= 32; ++prefix_len) { + if (xor_numeric == bitMask4[prefix_len]) { + // Got it: the wanted value is also the index + return (static_cast(prefix_len)); + } + } + + // If it was not found the range is not from a prefix / prefix_len + return (-1); + } else { + // Get addresses as 16 bytes + uint8_t min_packed[V6ADDRESS_LEN]; + memcpy(min_packed, &min.toBytes()[0], 16); + uint8_t max_packed[V6ADDRESS_LEN]; + memcpy(max_packed, &max.toBytes()[0], 16); + + // Scan the exclusive or of addresses to find a difference + int candidate = 128; + bool zeroes = true; + for (uint8_t i = 0; i < 16; ++i) { + uint8_t xor_byte = min_packed[i] ^ max_packed[i]; + if (zeroes) { + // Skipping zero bits searching for one bits + if (xor_byte == 0) { + continue; + } + // Found a one bit: note the fact + zeroes = false; + // Compare the exclusive or to masks + for (uint8_t j = 0; j < 8; ++j) { + if (xor_byte == revMask6[j]) { + // Got it the prefix length: note it + candidate = static_cast((i * 8) + j); + } + } + if (candidate == 128) { + // Not found? The range is not from a prefix / prefix_len + return (-1); + } + } else { + // Checking that trailing bits are on bits + if (xor_byte == 0xff) { + continue; + } + // Not all ones is bad + return (-1); + } + } + return (candidate); + } +} + uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len) { if (delegated_len < pool_len) { return (0); diff --git a/src/lib/dhcpsrv/addr_utilities.h b/src/lib/dhcpsrv/addr_utilities.h index f39c0a2c1f..77786e0749 100644 --- a/src/lib/dhcpsrv/addr_utilities.h +++ b/src/lib/dhcpsrv/addr_utilities.h @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-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 @@ -63,6 +63,18 @@ isc::asiolink::IOAddress getNetmask4(uint8_t len); uint64_t addrsInRange(const isc::asiolink::IOAddress& min, const isc::asiolink::IOAddress& max); +/// @brief Returns prefix length from the specified range (min - max). +/// +/// This can be considered as log2(addrsInRange) +/// +/// @throw BadValue if min and max do not define a prefix. +/// +/// @param min the first address in range +/// @param max the last address in range +/// @return the prefix length or -1 if the range is not from a prefix +int prefixLengthFromRange(const isc::asiolink::IOAddress& min, + const isc::asiolink::IOAddress& max); + /// @brief Returns number of available IPv6 prefixes in the specified prefix. /// /// Note that if the answer is bigger than uint64_t can hold, it will return diff --git a/src/lib/dhcpsrv/cfg_4o6.h b/src/lib/dhcpsrv/cfg_4o6.h index 90bbf460ec..4027efbd96 100644 --- a/src/lib/dhcpsrv/cfg_4o6.h +++ b/src/lib/dhcpsrv/cfg_4o6.h @@ -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 @@ -7,8 +7,9 @@ #ifndef CFG_4OVER6_H #define CFG_4OVER6_H -#include #include +#include +#include namespace isc { namespace dhcp { @@ -17,7 +18,7 @@ namespace dhcp { /// /// DHCP4o6 is completely optional. If it is not enabled, this structure /// does not contain any information. -struct Cfg4o6 { +struct Cfg4o6 : public isc::data::CfgToElement { /// the default constructor. /// @@ -78,6 +79,11 @@ struct Cfg4o6 { enabled_ = true; } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// Specifies if 4o6 is enabled on this subnet. diff --git a/src/lib/dhcpsrv/cfg_db_access.cc b/src/lib/dhcpsrv/cfg_db_access.cc index 4601dc93e6..e93631dc7b 100644 --- a/src/lib/dhcpsrv/cfg_db_access.cc +++ b/src/lib/dhcpsrv/cfg_db_access.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -9,7 +9,13 @@ #include #include #include +#include +#include +#include #include +#include + +using namespace isc::data; namespace isc { namespace dhcp { @@ -59,7 +65,63 @@ CfgDbAccess::getAccessString(const std::string& access_string) const { return (s.str()); } - +ElementPtr +CfgDbAccess::toElementDbAccessString(const std::string& dbaccess) { + ElementPtr result = Element::createMap(); + // Code from DatabaseConnection::parse + if (dbaccess.empty()) { + return (result); + } + std::vector tokens; + boost::split(tokens, dbaccess, boost::is_any_of(std::string("\t "))); + BOOST_FOREACH(std::string token, tokens) { + size_t pos = token.find("="); + if (pos != std::string::npos) { + std::string keyword = token.substr(0, pos); + std::string value = token.substr(pos + 1); + if ((keyword == "lfc-interval") || + (keyword == "connect-timeout") || + (keyword == "port")) { + // integer parameters + int64_t int_value; + try { + int_value = boost::lexical_cast(value); + result->set(keyword, Element::create(int_value)); + } catch (...) { + isc_throw(ToElementError, "invalid DB access " + << "integer parameter: " + << keyword << "=" << value); + } + } else if ((keyword == "persist") || + (keyword == "readonly")) { + if (value == "true") { + result->set(keyword, Element::create(true)); + } else if (value == "false") { + result->set(keyword, Element::create(false)); + } else { + isc_throw(ToElementError, "invalid DB access " + << "boolean parameter: " + << keyword << "=" << value); + } + } else if ((keyword == "type") || + (keyword == "user") || + (keyword == "password") || + (keyword == "host") || + (keyword == "name") || + (keyword == "contact_points") || + (keyword == "keyspace")) { + result->set(keyword, Element::create(value)); + } else { + isc_throw(ToElementError, "unknown DB access parameter: " + << keyword << "=" << value); + } + } else { + isc_throw(ToElementError, "Cannot unparse " << token + << ", expected format is name=value"); + } + } + return (result); +} } // end of isc::dhcp namespace } // end of isc namespace diff --git a/src/lib/dhcpsrv/cfg_db_access.h b/src/lib/dhcpsrv/cfg_db_access.h index 77fda0d18a..a22e730676 100644 --- a/src/lib/dhcpsrv/cfg_db_access.h +++ b/src/lib/dhcpsrv/cfg_db_access.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -7,6 +7,7 @@ #ifndef CFG_DBACCESS_H #define CFG_DBACCESS_H +#include #include #include @@ -63,7 +64,14 @@ public: /// according to the configuration specified. void createManagers() const; -private: + /// @brief Unparse an access string + /// + /// @param dbaccess the database access string + /// @return a pointer to configuration + static + isc::data::ElementPtr toElementDbAccessString(const std::string& dbaccess); + +protected: /// @brief Returns lease or host database access string. /// @@ -88,6 +96,35 @@ typedef boost::shared_ptr CfgDbAccessPtr; /// @brief A pointer to the const @c CfgDbAccess. typedef boost::shared_ptr ConstCfgDbAccessPtr; +/// @brief utility class for unparsing +struct CfgLeaseDbAccess : public CfgDbAccess, public isc::data::CfgToElement { + /// @brief Constructor + CfgLeaseDbAccess(const CfgDbAccess& super) : CfgDbAccess(super) { } + + /// @brief Unparse + /// + /// @ref CfgToElement::toElement + /// + /// @result a pointer to a configuration + virtual isc::data::ElementPtr toElement() const { + return (CfgDbAccess::toElementDbAccessString(lease_db_access_)); + } +}; + +struct CfgHostDbAccess : public CfgDbAccess, public isc::data::CfgToElement { + /// @brief Constructor + CfgHostDbAccess(const CfgDbAccess& super) : CfgDbAccess(super) { } + + /// @brief Unparse + /// + /// @ref CfgToElement::toElement + /// + /// @result a pointer to a configuration + virtual isc::data::ElementPtr toElement() const { + return (CfgDbAccess::toElementDbAccessString(host_db_access_)); + } +}; + } } diff --git a/src/lib/dhcpsrv/cfg_duid.cc b/src/lib/dhcpsrv/cfg_duid.cc index d9c6e382ed..fffae1aa7e 100644 --- a/src/lib/dhcpsrv/cfg_duid.cc +++ b/src/lib/dhcpsrv/cfg_duid.cc @@ -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 @@ -9,8 +9,11 @@ #include #include #include +#include +#include using namespace isc; +using namespace isc::data; using namespace isc::util::encode; using namespace isc::util::str; @@ -71,6 +74,39 @@ CfgDUID::create(const std::string& duid_file_path) const { return (factory.get()); } +ElementPtr +CfgDUID::toElement() const { + ElementPtr result = Element::createMap(); + // The type item is required + std::string duid_type = "LLT"; + switch (type_) { + case DUID::DUID_LLT: + break; + case DUID::DUID_EN: + duid_type = "EN"; + break; + case DUID::DUID_LL: + duid_type = "LL"; + break; + default: + isc_throw(ToElementError, "invalid DUID type: " << getType()); + break; + } + result->set("type", Element::create(duid_type)); + // Set the identifier + result->set("identifier", + Element::create(util::encode::encodeHex(identifier_))); + // Set the hardware type + result->set("htype", Element::create(htype_)); + // Set the time + result->set("time", Element::create(static_cast(time_))); + // Set the enterprise id + result->set("enterprise-id", + Element::create(static_cast(enterprise_id_))); + // Set the persistence flag + result->set("persist", Element::create(persist_)); + return (result); +} } } diff --git a/src/lib/dhcpsrv/cfg_duid.h b/src/lib/dhcpsrv/cfg_duid.h index b33057c5b6..ee1e117c93 100644 --- a/src/lib/dhcpsrv/cfg_duid.h +++ b/src/lib/dhcpsrv/cfg_duid.h @@ -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 CFG_DUID_H #include +#include #include #include #include @@ -25,7 +26,7 @@ namespace dhcp { /// generate. It also allows for overriding entire default DUID or parts of /// it via configuration file. This class holds the DUID configuration /// specified in the server configuration file. -class CfgDUID { +class CfgDUID : public isc::data::CfgToElement { public: /// @brief Constructor. @@ -114,6 +115,11 @@ public: /// @return Pointer to an instance of new DUID. DuidPtr create(const std::string& duid_file_path) const; + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief DUID type. diff --git a/src/lib/dhcpsrv/cfg_expiration.cc b/src/lib/dhcpsrv/cfg_expiration.cc index b421f90e3d..f4f1898cf9 100644 --- a/src/lib/dhcpsrv/cfg_expiration.cc +++ b/src/lib/dhcpsrv/cfg_expiration.cc @@ -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,8 @@ #include #include +using namespace isc::data; + namespace isc { namespace dhcp { @@ -103,5 +105,35 @@ CfgExpiration::rangeCheck(const int64_t value, const uint64_t max_value, } } +ElementPtr +CfgExpiration::toElement() const { + ElementPtr result = Element::createMap(); + // Set reclaim-timer-wait-time + result->set("reclaim-timer-wait-time", + Element::create(static_cast + (reclaim_timer_wait_time_))); + // Set flush-reclaimed-timer-wait-time + result->set("flush-reclaimed-timer-wait-time", + Element::create(static_cast + (flush_reclaimed_timer_wait_time_))); + // Set hold-reclaimed-time + result->set("hold-reclaimed-time", + Element::create(static_cast + (hold_reclaimed_time_))); + // Set max-reclaim-leases + result->set("max-reclaim-leases", + Element::create(static_cast + (max_reclaim_leases_))); + // Set max-reclaim-time + result->set("max-reclaim-time", + Element::create(static_cast + (max_reclaim_time_))); + // Set unwarned-reclaim-cycles + result->set("unwarned-reclaim-cycles", + Element::create(static_cast + (unwarned_reclaim_cycles_))); + return (result); +} + } } diff --git a/src/lib/dhcpsrv/cfg_expiration.h b/src/lib/dhcpsrv/cfg_expiration.h index 23ef3036c1..f570b5cb5d 100644 --- a/src/lib/dhcpsrv/cfg_expiration.h +++ b/src/lib/dhcpsrv/cfg_expiration.h @@ -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 CFG_EXPIRATION_H #include +#include #include #include #include @@ -57,7 +58,7 @@ namespace dhcp { /// The @c CfgExpiration class provides a collection of accessors and /// modifiers to manage the data. Each accessor checks if the given value /// is in range allowed for this value. -class CfgExpiration { +class CfgExpiration : public isc::data::CfgToElement { public: /// @name Default values. @@ -223,6 +224,11 @@ public: void (Instance::*delete_fun)(const uint32_t), Instance* instance_ptr) const; + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Checks if the value being set by one of the modifiers is diff --git a/src/lib/dhcpsrv/cfg_host_operations.cc b/src/lib/dhcpsrv/cfg_host_operations.cc index 6a19f1b454..8910c36000 100644 --- a/src/lib/dhcpsrv/cfg_host_operations.cc +++ b/src/lib/dhcpsrv/cfg_host_operations.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -7,6 +7,9 @@ #include #include #include +#include + +using namespace isc::data; namespace isc { namespace dhcp { @@ -52,5 +55,16 @@ CfgHostOperations::clearIdentifierTypes() { identifier_types_.clear(); } +ElementPtr +CfgHostOperations::toElement() const { + ElementPtr result = Element::createList(); + for (IdentifierTypes::const_iterator id = identifier_types_.begin(); + id != identifier_types_.end(); ++id) { + const std::string& name = Host::getIdentifierName(*id); + result->add(Element::create(name)); + } + return (result); +} + } } diff --git a/src/lib/dhcpsrv/cfg_host_operations.h b/src/lib/dhcpsrv/cfg_host_operations.h index eace0f8694..c87425b0f0 100644 --- a/src/lib/dhcpsrv/cfg_host_operations.h +++ b/src/lib/dhcpsrv/cfg_host_operations.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 @@ -7,6 +7,7 @@ #ifndef CFG_HOST_OPERATIONS_H #define CFG_HOST_OPERATIONS_H +#include #include #include #include @@ -39,7 +40,7 @@ ConstCfgHostOperationsPtr; /// An administrator selects which identifiers the server should /// use and in which order to search for host reservations to /// optimize performance of the server. -class CfgHostOperations { +class CfgHostOperations : public isc::data::CfgToElement { public: /// @brief Type of the container holding ordered list of identifiers. @@ -77,6 +78,11 @@ public: /// @brief Removes existing identifier types. void clearIdentifierTypes(); + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Holds ordered collection of identifiers to be used by the diff --git a/src/lib/dhcpsrv/cfg_iface.cc b/src/lib/dhcpsrv/cfg_iface.cc index 6ff26f10f6..cf7a7ffbec 100644 --- a/src/lib/dhcpsrv/cfg_iface.cc +++ b/src/lib/dhcpsrv/cfg_iface.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -14,6 +14,7 @@ #include using namespace isc::asiolink; +using namespace isc::data; namespace isc { namespace dhcp { @@ -398,5 +399,34 @@ CfgIface::useSocketType(const uint16_t family, useSocketType(family, textToSocketType(socket_type_name)); } +ElementPtr +CfgIface::toElement() const { + ElementPtr result = Element::createMap(); + + // Set interfaces + ElementPtr ifaces = Element::createList(); + if (wildcard_used_) { + ifaces->add(Element::create(std::string(ALL_IFACES_KEYWORD))); + } + for (IfaceSet::const_iterator iface = iface_set_.cbegin(); + iface != iface_set_.cend(); ++iface) { + ifaces->add(Element::create(*iface)); + } + for (ExplicitAddressMap::const_iterator address = address_map_.cbegin(); + address != address_map_.cend(); ++address) { + std::string spec = address->first + "/" + address->second.toText(); + ifaces->add(Element::create(spec)); + } + result->set("interfaces", ifaces); + + // Set dhcp-socket-type (no default because it is DHCPv4 specific) + // @todo emit raw if and only if DHCPv4 + if (socket_type_ != SOCKET_RAW) { + result->set("dhcp-socket-type", Element::create(std::string("udp"))); + } + + return (result); +} + } // end of isc::dhcp namespace } // end of isc namespace diff --git a/src/lib/dhcpsrv/cfg_iface.h b/src/lib/dhcpsrv/cfg_iface.h index f91fbf06aa..f3338cd005 100644 --- a/src/lib/dhcpsrv/cfg_iface.h +++ b/src/lib/dhcpsrv/cfg_iface.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -125,7 +126,7 @@ public: /// to which it is bound. It is allowed to select multiple addresses on the /// particular interface explicitly, e.g. "eth0/192.168.8.1", /// "eth0/192.168.8.2". -class CfgIface { +class CfgIface : public isc::data::CfgToElement { public: /// @brief Socket type used by the DHCPv4 server. @@ -251,6 +252,11 @@ public: return (!equals(other)); } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Checks if multiple IPv4 addresses has been activated on any diff --git a/src/lib/dhcpsrv/cfg_mac_source.cc b/src/lib/dhcpsrv/cfg_mac_source.cc index 9cc1dbd35f..9475f6e14d 100644 --- a/src/lib/dhcpsrv/cfg_mac_source.cc +++ b/src/lib/dhcpsrv/cfg_mac_source.cc @@ -9,6 +9,32 @@ #include #include +using namespace isc::data; + +namespace { + +using namespace isc::dhcp; + +struct { + const char * name; + uint32_t type; +} sources[] = { + { "any", HWAddr::HWADDR_SOURCE_ANY }, + { "raw", HWAddr::HWADDR_SOURCE_RAW }, + { "duid", HWAddr::HWADDR_SOURCE_DUID }, + { "ipv6-link-local", HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL }, + { "client-link-addr-option", HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION }, + { "rfc6939", HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION }, + { "remote-id", HWAddr::HWADDR_SOURCE_REMOTE_ID }, + { "rfc4649", HWAddr::HWADDR_SOURCE_REMOTE_ID }, + { "subscriber-id", HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID }, + { "rfc4580", HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID }, + { "docsis-cmts", HWAddr::HWADDR_SOURCE_DOCSIS_CMTS }, + { "docsis-modem", HWAddr::HWADDR_SOURCE_DOCSIS_MODEM } +}; + +}; + namespace isc { namespace dhcp { @@ -19,26 +45,7 @@ CfgMACSource::CfgMACSource() { } uint32_t CfgMACSource::MACSourceFromText(const std::string& name) { - - struct { - const char * name; - uint32_t type; - } sources[] = { - { "any", HWAddr::HWADDR_SOURCE_ANY }, - { "raw", HWAddr::HWADDR_SOURCE_RAW }, - { "duid", HWAddr::HWADDR_SOURCE_DUID }, - { "ipv6-link-local", HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL }, - { "client-link-addr-option", HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION }, - { "rfc6939", HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION }, - { "remote-id", HWAddr::HWADDR_SOURCE_REMOTE_ID }, - { "rfc4649", HWAddr::HWADDR_SOURCE_REMOTE_ID }, - { "subscriber-id", HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID }, - { "rfc4580", HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID }, - { "docsis-cmts", HWAddr::HWADDR_SOURCE_DOCSIS_CMTS }, - { "docsis-modem", HWAddr::HWADDR_SOURCE_DOCSIS_MODEM } - }; - - for (int i=0; i < sizeof(sources)/sizeof(sources[0]); ++i) { + for (unsigned i = 0; i < sizeof(sources)/sizeof(sources[0]); ++i) { if (name.compare(sources[i].name) == 0) { return (sources[i].type); } @@ -51,12 +58,31 @@ void CfgMACSource::add(uint32_t source) { for (CfgMACSources::const_iterator it = mac_sources_.begin(); it != mac_sources_.end(); ++it) { if (*it == source) { - isc_throw(InvalidParameter, "mac-source paramter " << source + isc_throw(InvalidParameter, "mac-source parameter " << source << "' specified twice."); } } mac_sources_.push_back(source); } +ElementPtr CfgMACSource::toElement() const { + ElementPtr result = Element::createList(); + for (CfgMACSources::const_iterator source = mac_sources_.cbegin(); + source != mac_sources_.cend(); ++source) { + std::string name; + for (unsigned i = 0; i < sizeof(sources)/sizeof(sources[0]); ++i) { + if (sources[i].type == *source) { + name = sources[i].name; + break; + } + } + if (name.empty()) { + isc_throw(ToElementError, "invalid MAC source: " << *source); + } + result->add(Element::create(name)); + } + return (result); +} + }; }; diff --git a/src/lib/dhcpsrv/cfg_mac_source.h b/src/lib/dhcpsrv/cfg_mac_source.h index 802a780fa8..cfb9fb110b 100644 --- a/src/lib/dhcpsrv/cfg_mac_source.h +++ b/src/lib/dhcpsrv/cfg_mac_source.h @@ -7,6 +7,7 @@ #ifndef CFG_MAC_SOURCE_H #define CFG_MAC_SOURCE_H +#include #include #include #include @@ -21,7 +22,7 @@ typedef std::vector CfgMACSources; /// /// It's a simple wrapper around a vector of uint32_t, with each entry /// holding one MAC source. -class CfgMACSource { +class CfgMACSource : public isc::data::CfgToElement { public: /// @brief Default constructor. @@ -68,6 +69,11 @@ class CfgMACSource { mac_sources_.clear(); } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + protected: /// @brief Actual MAC sources storage CfgMACSources mac_sources_; diff --git a/src/lib/dhcpsrv/cfg_option.cc b/src/lib/dhcpsrv/cfg_option.cc index 8947dd5335..6bcd2f9015 100644 --- a/src/lib/dhcpsrv/cfg_option.cc +++ b/src/lib/dhcpsrv/cfg_option.cc @@ -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 @@ -8,7 +8,12 @@ #include #include #include +#include #include +#include +#include + +using namespace isc::data; namespace isc { namespace dhcp { @@ -183,5 +188,89 @@ CfgOption::getAll(const uint32_t vendor_id) const { return (vendor_options_.getItems(vendor_id)); } +ElementPtr +CfgOption::toElement() const { + // option-data value is a list of maps + ElementPtr result = Element::createList(); + // Iterate first on options using space names + const std::list& names = options_.getOptionSpaceNames(); + for (std::list::const_iterator name = names.begin(); + name != names.end(); ++name) { + OptionContainerPtr opts = getAll(*name); + for (OptionContainer::const_iterator opt = opts->begin(); + opt != opts->end(); ++opt) { + // Get and fill the map for this option + ElementPtr map = Element::createMap(); + // First set space from parent iterator + map->set("space", Element::create(*name)); + // Set the code + uint16_t code = opt->option_->getType(); + map->set("code", Element::create(code)); + // Set the name (always for standard options else when asked for) + OptionDefinitionPtr def = LibDHCP::getOptionDef(*name, code); + if (!def) { + def = LibDHCP::getRuntimeOptionDef(*name, code); + } + if (def) { + map->set("name", Element::create(def->getName())); + } + // Set the data item + if (!opt->formatted_value_.empty()) { + map->set("csv-format", Element::create(true)); + map->set("data", Element::create(opt->formatted_value_)); + } else { + map->set("csv-format", Element::create(false)); + std::vector bin = opt->option_->toBinary(); + std::string repr = util::encode::encodeHex(bin); + map->set("data", Element::create(repr)); + } + // Push on the list + result->add(map); + } + } + // Iterate first on vendor_options using vendor ids + const std::list& ids = vendor_options_.getOptionSpaceNames(); + for (std::list::const_iterator id = ids.begin(); + id != ids.end(); ++id) { + OptionContainerPtr opts = getAll(*id); + for (OptionContainer::const_iterator opt = opts->begin(); + opt != opts->end(); ++opt) { + // Get and fill the map for this option + ElementPtr map = Element::createMap(); + // First set space from parent iterator + std::ostringstream oss; + oss << "vendor-" << *id; + map->set("space", Element::create(oss.str())); + // Set the code + uint16_t code = opt->option_->getType(); + map->set("code", Element::create(code)); + // Set the name + Option::Universe universe = opt->option_->getUniverse(); + OptionDefinitionPtr def = + LibDHCP::getVendorOptionDef(universe, *id, code); + if (!def) { + // vendor-XXX space is in oss + def = LibDHCP::getRuntimeOptionDef(oss.str(), code); + } + if (def) { + map->set("name", Element::create(def->getName())); + } + // Set the data item + if (!opt->formatted_value_.empty()) { + map->set("csv-format", Element::create(true)); + map->set("data", Element::create(opt->formatted_value_)); + } else { + map->set("csv-format", Element::create(false)); + std::vector bin = opt->option_->toBinary(); + std::string repr = util::encode::encodeHex(bin); + map->set("data", Element::create(repr)); + } + // Push on the list + result->add(map); + } + } + return (result); +} + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/cfg_option.h b/src/lib/dhcpsrv/cfg_option.h index 61f3f13318..6fcd5c1e21 100644 --- a/src/lib/dhcpsrv/cfg_option.h +++ b/src/lib/dhcpsrv/cfg_option.h @@ -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 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -210,7 +211,7 @@ typedef OptionContainer::nth_index<2>::type OptionContainerPersistIndex; /// options is useful when the client requests stateless configuration from /// the DHCP server and no subnet is selected for this client. This client /// will only receive global options. -class CfgOption { +class CfgOption : public isc::data::CfgToElement { public: /// @brief default constructor @@ -394,6 +395,11 @@ public: /// @return List comprising option space names for vendor options. std::list getVendorIdsSpaceNames() const; + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Appends encapsulated options to the options in an option space. diff --git a/src/lib/dhcpsrv/cfg_option_def.cc b/src/lib/dhcpsrv/cfg_option_def.cc index 4c1309dc79..3a61bb1ef6 100644 --- a/src/lib/dhcpsrv/cfg_option_def.cc +++ b/src/lib/dhcpsrv/cfg_option_def.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -6,9 +6,13 @@ #include #include +#include #include #include #include +#include + +using namespace isc::data; namespace isc { namespace dhcp { @@ -148,5 +152,58 @@ CfgOptionDef::get(const std::string& option_space, return (OptionDefinitionPtr()); } +ElementPtr +CfgOptionDef::toElement() const { + // option-defs value is a list of maps + ElementPtr result = Element::createList(); + // Iterate through the container by names and definitions + const std::list& names = + option_definitions_.getOptionSpaceNames(); + for (std::list::const_iterator name = names.begin(); + name != names.end(); ++name) { + OptionDefContainerPtr defs = getAll(*name); + for (OptionDefContainer::const_iterator def = defs->begin(); + def != defs->end(); ++def) { + // Get and fill the map for this definition + ElementPtr map = Element::createMap(); + // First set space from parent iterator + map->set("space", Element::create(*name)); + // Set required items: name, code and type + map->set("name", Element::create((*def)->getName())); + map->set("code", Element::create((*def)->getCode())); + std::string data_type = + OptionDataTypeUtil::getDataTypeName((*def)->getType()); + map->set("type", Element::create(data_type)); + // Set the array type + bool array_type = (*def)->getArrayType(); + map->set("array", Element::create(array_type)); + // Set the encapsulate space + std::string encapsulates = (*def)->getEncapsulatedSpace(); + map->set("encapsulate", Element::create(encapsulates)); + // Set the record field types + OptionDefinition::RecordFieldsCollection fields = + (*def)->getRecordFields(); + if (!fields.empty()) { + std::ostringstream oss; + for (OptionDefinition::RecordFieldsCollection::const_iterator + field = fields.begin(); + field != fields.end(); ++field) { + if (field != fields.begin()) { + oss << ", "; + } + oss << OptionDataTypeUtil::getDataTypeName(*field); + } + map->set("record-types", Element::create(oss.str())); + } else { + map->set("record-types", Element::create(std::string())); + } + // Push on the list + result->add(map); + } + } + return (result); +} + + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/cfg_option_def.h b/src/lib/dhcpsrv/cfg_option_def.h index a391d4a27b..547fc2fc29 100644 --- a/src/lib/dhcpsrv/cfg_option_def.h +++ b/src/lib/dhcpsrv/cfg_option_def.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -9,6 +9,7 @@ #include #include +#include #include namespace isc { @@ -26,7 +27,7 @@ namespace dhcp { /// following names: "dhcp4" and "dhcp6" are reserved, though. They are /// names of option spaces used for standard top-level DHCPv4 and DHCPv6 /// options respectively. -class CfgOptionDef { +class CfgOptionDef : public isc::data::CfgToElement { public: /// @brief Copies this configuration to a new configuration. @@ -117,6 +118,11 @@ public: return (option_definitions_); } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief A collection of option definitions. diff --git a/src/lib/dhcpsrv/cfg_rsoo.cc b/src/lib/dhcpsrv/cfg_rsoo.cc index fbcbd90048..b54262e57f 100644 --- a/src/lib/dhcpsrv/cfg_rsoo.cc +++ b/src/lib/dhcpsrv/cfg_rsoo.cc @@ -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 @@ -6,6 +6,9 @@ #include #include +#include + +using namespace isc::data; namespace isc { namespace dhcp { @@ -33,6 +36,17 @@ CfgRSOO::enable(const uint16_t code) { } } +ElementPtr +CfgRSOO::toElement() const { + ElementPtr result = Element::createList(); + // We can use LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, *opt) too... + for (std::set::const_iterator opt = rsoo_options_.cbegin(); + opt != rsoo_options_.cend(); ++opt) { + const std::string& code = boost::lexical_cast(*opt); + result->add(Element::create(code)); + } + return (result); +} } } diff --git a/src/lib/dhcpsrv/cfg_rsoo.h b/src/lib/dhcpsrv/cfg_rsoo.h index bd5a87db13..ed9116feae 100644 --- a/src/lib/dhcpsrv/cfg_rsoo.h +++ b/src/lib/dhcpsrv/cfg_rsoo.h @@ -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 @@ -7,6 +7,7 @@ #ifndef CFG_RSOO_H #define CFG_RSOO_H +#include #include #include #include @@ -21,7 +22,7 @@ namespace dhcp { /// 65 is officially RSSO-enabled. The list may be extended in the future /// and this class allows for specifying any future RSOO-enabled options. /// The administrator may also use existing options as RSOO-enabled. -class CfgRSOO { +class CfgRSOO : public isc::data::CfgToElement { public: /// @brief Constructor. @@ -46,6 +47,11 @@ public: /// @param code option to be enabled in RSOO void enable(const uint16_t code); + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Contains a set of options that are allowed in RSOO option diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index f317a91fac..55c205dcb5 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -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 @@ -13,8 +13,10 @@ #include #include #include +#include using namespace isc::asiolink; +using namespace isc::data; namespace isc { namespace dhcp { @@ -273,5 +275,115 @@ CfgSubnets4::updateStatistics() { } } +ElementPtr +CfgSubnets4::toElement() const { + ElementPtr result = Element::createList(); + // Iterate subnets + for (Subnet4Collection::const_iterator subnet = subnets_.cbegin(); + subnet != subnets_.cend(); ++subnet) { + // Prepare the map + ElementPtr map = Element::createMap(); + // Set subnet id + SubnetID id = (*subnet)->getID(); + map->set("id", Element::create(static_cast(id))); + // Set relay info + const Subnet::RelayInfo& relay_info = (*subnet)->getRelayInfo(); + ElementPtr relay = Element::createMap(); + relay->set("ip-address", Element::create(relay_info.addr_.toText())); + map->set("relay", relay); + // Set subnet + map->set("subnet", Element::create((*subnet)->toText())); + // Set interface + const std::string& iface = (*subnet)->getIface(); + map->set("interface", Element::create(iface)); + // Set renew-timer + map->set("renew-timer", + Element::create(static_cast + ((*subnet)->getT1().get()))); + // Set rebind-timer + map->set("rebind-timer", + Element::create(static_cast + ((*subnet)->getT2().get()))); + // Set valid-lifetime + map->set("valid-lifetime", + Element::create(static_cast + ((*subnet)->getValid().get()))); + // Set pools + const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_V4); + ElementPtr pool_list = Element::createList(); + for (PoolCollection::const_iterator pool = pools.cbegin(); + pool != pools.cend(); ++pool) { + // Prepare the map for a pool + ElementPtr pool_map = Element::createMap(); + // Set pool + const IOAddress& first = (*pool)->getFirstAddress(); + const IOAddress& last = (*pool)->getLastAddress(); + std::string range = first.toText() + "-" + last.toText(); + // Try to output a prefix (vs a range) + int prefix_len = prefixLengthFromRange(first, last); + if (prefix_len >= 0) { + std::ostringstream oss; + oss << first.toText() << "/" << prefix_len; + range = oss.str(); + } + pool_map->set("pool", Element::create(range)); + // Set user-context + ConstElementPtr context = (*pool)->getContext(); + if (!isNull(context)) { + pool_map->set("user-context", context); + } + // Set pool options + ConstCfgOptionPtr opts = (*pool)->getCfgOption(); + pool_map->set("option-data", opts->toElement()); + // Push on the pool list + pool_list->add(pool_map); + } + map->set("pools", pool_list); + // Set host reservation-mode + Subnet::HRMode hrmode = (*subnet)->getHostReservationMode(); + std::string mode; + switch (hrmode) { + case Subnet::HR_DISABLED: + mode = "disabled"; + break; + case Subnet::HR_OUT_OF_POOL: + mode = "out-of-pool"; + break; + case Subnet::HR_ALL: + mode = "all"; + break; + default: + isc_throw(ToElementError, + "invalid host reservation mode: " << hrmode); + } + map->set("reservation-mode", Element::create(mode)); + // Set match-client-id + map->set("match-client-id", + Element::create((*subnet)->getMatchClientId())); + // Set next-server + map->set("next-server", + Element::create((*subnet)->getSiaddr().toText())); + // Set DHCP4o6 + const Cfg4o6& d4o6 = (*subnet)->get4o6(); + merge(map, d4o6.toElement()); + // Set client-class + const ClientClasses& cclasses = (*subnet)->getClientClasses(); + if (cclasses.size() > 1) { + isc_throw(ToElementError, "client-classes has too many items: " + << cclasses.size()); + } else if (!cclasses.empty()) { + map->set("client-class", Element::create(*cclasses.cbegin())); + } + // Set options + ConstCfgOptionPtr opts = (*subnet)->getCfgOption(); + map->set("option-data", opts->toElement()); + // Not supported: interface-id + // Not supported: rapid-commit + // Push on the list + result->add(map); + } + return (result); +} + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index bcadc44473..80c35cda64 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -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 @@ -8,6 +8,7 @@ #define CFG_SUBNETS4_H #include +#include #include #include #include @@ -25,7 +26,7 @@ namespace dhcp { /// /// See @c CfgSubnets4::selectSubnet documentation for more details on how the /// subnet is selected for the client. -class CfgSubnets4 { +class CfgSubnets4 : public isc::data::CfgToElement { public: /// @brief Adds new subnet to the configuration. @@ -183,6 +184,11 @@ public: /// configuration and also subnet-ids may change. void removeStatistics(); + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Checks that the IPv4 subnet with the given id already exists. diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index 3136763f3c..b74e70e0e2 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -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 @@ -9,9 +9,13 @@ #include #include #include +#include #include +#include +#include using namespace isc::asiolink; +using namespace isc::data; namespace isc { namespace dhcp { @@ -227,5 +231,180 @@ CfgSubnets6::updateStatistics() { } } +ElementPtr +CfgSubnets6::toElement() const { + ElementPtr result = Element::createList(); + // Iterate subnets + for (Subnet6Collection::const_iterator subnet = subnets_.cbegin(); + subnet != subnets_.cend(); ++subnet) { + // Prepare the map + ElementPtr map = Element::createMap(); + // Set subnet id + SubnetID id = (*subnet)->getID(); + map->set("id", Element::create(static_cast(id))); + // Set relay info + const Subnet::RelayInfo& relay_info = (*subnet)->getRelayInfo(); + ElementPtr relay = Element::createMap(); + relay->set("ip-address", Element::create(relay_info.addr_.toText())); + map->set("relay", relay); + // Set subnet + map->set("subnet", Element::create((*subnet)->toText())); + // Set interface + const std::string& iface = (*subnet)->getIface(); + map->set("interface", Element::create(iface)); + // Set interface-id + const OptionPtr& ifaceid = (*subnet)->getInterfaceId(); + if (ifaceid) { + std::vector bin = ifaceid->getData(); + std::string ifid; + ifid.resize(bin.size()); + if (!bin.empty()) { + std::memcpy(&ifid[0], &bin[0], bin.size()); + } + map->set("interface-id", Element::create(ifid)); + } else { + map->set("interface-id", Element::create(std::string())); + } + // Set renew-timer + map->set("renew-timer", + Element::create(static_cast + ((*subnet)->getT1().get()))); + // Set rebind-timer + map->set("rebind-timer", + Element::create(static_cast + ((*subnet)->getT2().get()))); + // Set preferred-lifetime + map->set("preferred-lifetime", + Element::create(static_cast + ((*subnet)->getPreferred().get()))); + // Set valid-lifetime + map->set("valid-lifetime", + Element::create(static_cast + ((*subnet)->getValid().get()))); + // Set rapid-commit + bool rapid_commit = (*subnet)->getRapidCommit(); + map->set("rapid-commit", Element::create(rapid_commit)); + // Set pools + const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_NA); + ElementPtr pool_list = Element::createList(); + for (PoolCollection::const_iterator pool = pools.cbegin(); + pool != pools.cend(); ++pool) { + // Prepare the map for a pool + ElementPtr pool_map = Element::createMap(); + // Set pool + const IOAddress& first = (*pool)->getFirstAddress(); + const IOAddress& last = (*pool)->getLastAddress(); + std::string range = first.toText() + "-" + last.toText(); + // Try to output a prefix (vs a range) + int prefix_len = prefixLengthFromRange(first, last); + if (prefix_len >= 0) { + std::ostringstream oss; + oss << first.toText() << "/" << prefix_len; + range = oss.str(); + } + pool_map->set("pool", Element::create(range)); + // Set user-context + ConstElementPtr context = (*pool)->getContext(); + if (!isNull(context)) { + pool_map->set("user-context", context); + } + // Set pool options + ConstCfgOptionPtr opts = (*pool)->getCfgOption(); + pool_map->set("option-data", opts->toElement()); + // Push on the pool list + pool_list->add(pool_map); + } + map->set("pools", pool_list); + // Set pd-pools + const PoolCollection& pdpools = (*subnet)->getPools(Lease::TYPE_PD); + ElementPtr pdpool_list = Element::createList(); + for (PoolCollection::const_iterator pool = pdpools.cbegin(); + pool != pdpools.cend(); ++pool) { + // Get it as a Pool6 + const Pool6* pdpool = dynamic_cast(pool->get()); + if (!pdpool) { + isc_throw(ToElementError, "invalid pd-pool pointer"); + } + // Prepare the map for a pd-pool + ElementPtr pool_map = Element::createMap(); + // Set prefix + const IOAddress& prefix = pdpool->getFirstAddress(); + pool_map->set("prefix", Element::create(prefix.toText())); + // Set prefix-len (get it from min - max) + const IOAddress& last = pdpool->getLastAddress(); + int prefix_len = prefixLengthFromRange(prefix, last); + if (prefix_len < 0) { + // The pool is bad: give up + isc_throw(ToElementError, "invalid prefix range " + << prefix.toText() << "-" << last.toText()); + } + pool_map->set("prefix-len", Element::create(prefix_len)); + // Set delegated-len + uint8_t len = pdpool->getLength(); + pool_map->set("delegated-len", + Element::create(static_cast(len))); + // Set excluded prefix + const Option6PDExcludePtr& xopt = + pdpool->getPrefixExcludeOption(); + if (xopt) { + const IOAddress& xprefix = + xopt->getExcludedPrefix(prefix, len); + pool_map->set("excluded-prefix", + Element::create(xprefix.toText())); + uint8_t xlen = xopt->getExcludedPrefixLength(); + pool_map->set("excluded-prefix-len", + Element::create(static_cast(xlen))); + } else { + pool_map->set("excluded-prefix", + Element::create(std::string("::"))); + pool_map->set("excluded-prefix-len", Element::create(0)); + } + // Set user-context + ConstElementPtr context = pdpool->getContext(); + if (!isNull(context)) { + pool_map->set("user-context", context); + } + // Set pool options + ConstCfgOptionPtr opts = pdpool->getCfgOption(); + pool_map->set("option-data", opts->toElement()); + // Push on the pool list + pdpool_list->add(pool_map); + } + map->set("pd-pools", pdpool_list); + // Set host reservation-mode + Subnet::HRMode hrmode = (*subnet)->getHostReservationMode(); + std::string mode; + switch (hrmode) { + case Subnet::HR_DISABLED: + mode = "disabled"; + break; + case Subnet::HR_OUT_OF_POOL: + mode = "out-of-pool"; + break; + case Subnet::HR_ALL: + mode = "all"; + break; + default: + isc_throw(ToElementError, + "invalid host reservation mode: " << hrmode); + } + map->set("reservation-mode", Element::create(mode)); + // Set client-class + const ClientClasses& cclasses = (*subnet)->getClientClasses(); + if (cclasses.size() > 1) { + isc_throw(ToElementError, "client-classes has too many items: " + << cclasses.size()); + } else if (!cclasses.empty()) { + map->set("client-class", Element::create(*cclasses.cbegin())); + } + // Set options + ConstCfgOptionPtr opts = (*subnet)->getCfgOption(); + map->set("option-data", opts->toElement()); + // Push on the list + result->add(map); + } + return (result); +} + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/cfg_subnets6.h b/src/lib/dhcpsrv/cfg_subnets6.h index 5494c21076..398c4d2516 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.h +++ b/src/lib/dhcpsrv/cfg_subnets6.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -26,7 +27,7 @@ namespace dhcp { /// /// See @c CfgSubnets6::selectSubnet documentation for more details on how the subnet /// is selected for the client. -class CfgSubnets6 { +class CfgSubnets6 : public isc::data::CfgToElement { public: /// @brief Adds new subnet to the configuration. @@ -141,6 +142,11 @@ public: /// configuration and also subnet-ids may change. void removeStatistics(); + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Selects a subnet using the interface name. diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc index bf37710b26..201ae97e19 100644 --- a/src/lib/dhcpsrv/cfgmgr.cc +++ b/src/lib/dhcpsrv/cfgmgr.cc @@ -167,7 +167,8 @@ CfgMgr::getStagingCfg() { } CfgMgr::CfgMgr() - : datadir_(DHCP_DATA_DIR), d2_client_mgr_(), verbose_mode_(false) { + : datadir_(DHCP_DATA_DIR), d2_client_mgr_(), + verbose_mode_(false), family_(AF_INET) { // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am // Note: the definition of DHCP_DATA_DIR needs to include quotation marks // See AM_CPPFLAGS definition in Makefile.am diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h index 3f1a87aad1..15f57efaf6 100644 --- a/src/lib/dhcpsrv/cfgmgr.h +++ b/src/lib/dhcpsrv/cfgmgr.h @@ -248,6 +248,16 @@ public: return (default_logger_name_); } + /// @brief Sets address family (AF_INET or AF_INET6) + void setFamily(uint16_t family) { + family_ = family == AF_INET ? AF_INET : AF_INET6; + } + + /// @brief Returns address family. + uint16_t getFamily() const { + return (family_); + } + //@} protected: @@ -299,6 +309,9 @@ private: /// @brief Default logger name. std::string default_logger_name_; + + /// @brief Address family. + uint16_t family_; }; } // namespace isc::dhcp diff --git a/src/lib/dhcpsrv/client_class_def.cc b/src/lib/dhcpsrv/client_class_def.cc index 010457eaed..03bd6d5393 100644 --- a/src/lib/dhcpsrv/client_class_def.cc +++ b/src/lib/dhcpsrv/client_class_def.cc @@ -1,12 +1,14 @@ -// 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 // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#include "client_class_def.h" +#include #include +using namespace isc::data; + namespace isc { namespace dhcp { @@ -74,6 +76,16 @@ ClientClassDef::setMatchExpr(const ExpressionPtr& match_expr) { match_expr_ = match_expr; } +std::string +ClientClassDef::getTest() const { + return (test_); +} + +void +ClientClassDef::setTest(const std::string& test) { + test_ = test; +} + const CfgOptionPtr& ClientClassDef::getCfgOption() const { return (cfg_option_); @@ -98,6 +110,24 @@ ClientClassDef::equals(const ClientClassDef& other) const { (filename_ == other.filename_)); } +ElementPtr +ClientClassDef:: toElement() const { + ElementPtr result = Element::createMap(); + // Set name + result->set("name", Element::create(name_)); + // Set original match expression + result->set("test", Element::create(test_)); + // Set option-data + result->set("option-data", cfg_option_->toElement()); + // Set next-server + result->set("next-server", Element::create(next_server_.toText())); + // Set server-hostname + result->set("server-hostname", Element::create(sname_)); + // Set boot-file-name + result->set("boot-file-name", Element::create(filename_)); + return (result); +} + std::ostream& operator<<(std::ostream& os, const ClientClassDef& x) { os << "ClientClassDef:" << x.getName(); return (os); @@ -123,11 +153,13 @@ ClientClassDictionary::~ClientClassDictionary() { void ClientClassDictionary::addClass(const std::string& name, const ExpressionPtr& match_expr, + const std::string& test, const CfgOptionPtr& cfg_option, asiolink::IOAddress next_server, const std::string& sname, const std::string& filename) { ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option)); + cclass->setTest(test); cclass->setNextServer(next_server); cclass->setSname(sname); cclass->setFilename(filename); @@ -191,6 +223,16 @@ ClientClassDictionary::equals(const ClientClassDictionary& other) const { return (true); } +ElementPtr +ClientClassDictionary::toElement() const { + ElementPtr result = Element::createList(); + // Iterate on the map + for (ClientClassDefMap::iterator this_class = classes_->begin(); + this_class != classes_->end(); ++this_class) { + result->add(this_class->second->toElement()); + } + return (result); +} } // namespace isc::dhcp } // namespace isc diff --git a/src/lib/dhcpsrv/client_class_def.h b/src/lib/dhcpsrv/client_class_def.h index 51fb7e2544..4ffa34d41b 100644 --- a/src/lib/dhcpsrv/client_class_def.h +++ b/src/lib/dhcpsrv/client_class_def.h @@ -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 @@ -7,6 +7,7 @@ #ifndef CLIENT_CLASS_DEF_H #define CLIENT_CLASS_DEF_H +#include #include #include #include @@ -37,7 +38,7 @@ public: }; /// @brief Embodies a single client class definition -class ClientClassDef { +class ClientClassDef : public isc::data::CfgToElement { public: /// @brief Constructor /// @@ -70,6 +71,14 @@ public: /// @param match_expr the expression to assign the class void setMatchExpr(const ExpressionPtr& match_expr); + /// @brief Fetches the class's original match expression + std::string getTest() const; + + /// @brief Sets the class's original match expression + /// + /// @param test the original expression to assign the class + void setTest(const std::string& test); + /// @brief Fetches the class's option collection const CfgOptionPtr& getCfgOption() const; @@ -145,6 +154,11 @@ public: return (filename_); } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Unique text identifier by which this class is known. std::string name_; @@ -153,6 +167,10 @@ private: /// this class. ExpressionPtr match_expr_; + /// @brief The original expression which determines membership in + /// this class. + std::string test_; + /// @brief The option data configuration for this class CfgOptionPtr cfg_option_; @@ -188,7 +206,7 @@ typedef boost::shared_ptr ClientClassDefMapPtr; typedef std::pair ClientClassMapPair; /// @brief Maintains a list of ClientClassDef's -class ClientClassDictionary { +class ClientClassDictionary : public isc::data::CfgToElement { public: /// @brief Constructor @@ -203,6 +221,7 @@ public: /// /// @param name Name to assign to this class /// @param match_expr Expression the class will use to determine membership + /// @param test Original version of match_expr /// @param options Collection of options members should be given /// @param next_server next-server value for this class (optional) /// @param sname server-name value for this class (optional) @@ -212,7 +231,7 @@ public: /// dictionary. See @ref dhcp::ClientClassDef::ClientClassDef() for /// others. void addClass(const std::string& name, const ExpressionPtr& match_expr, - const CfgOptionPtr& options, + const std::string& test, const CfgOptionPtr& options, asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"), const std::string& sname = std::string(), const std::string& filename = std::string()); @@ -271,6 +290,11 @@ public: return (!equals(other)); } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Map of the class definitions diff --git a/src/lib/dhcpsrv/d2_client_cfg.cc b/src/lib/dhcpsrv/d2_client_cfg.cc index 55df0be0bf..a5fa141150 100644 --- a/src/lib/dhcpsrv/d2_client_cfg.cc +++ b/src/lib/dhcpsrv/d2_client_cfg.cc @@ -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 @@ -15,6 +15,8 @@ #include using namespace std; +using namespace isc::asiolink; +using namespace isc::data; namespace isc { namespace dhcp { @@ -217,6 +219,49 @@ D2ClientConfig::toText() const { return (stream.str()); } +ElementPtr +D2ClientConfig::toElement() const { + ElementPtr result = Element::createMap(); + // Set enable-updates + result->set("enable-updates", Element::create(enable_updates_)); + // Set qualifying-suffix + result->set("qualifying-suffix", Element::create(qualifying_suffix_)); + // Set server-ip + result->set("server-ip", Element::create(server_ip_.toText())); + // Set server-port + result->set("server-port", + Element::create(static_cast(server_port_))); + // Set sender-ip + result->set("sender-ip", Element::create(sender_ip_.toText())); + // Set sender-port + result->set("sender-port", + Element::create(static_cast(sender_port_))); + // Set max-queue-size + result->set("max-queue-size", + Element::create(static_cast(max_queue_size_))); + // Set ncr-protocol + result->set("ncr-protocol", + Element::create(dhcp_ddns:: + ncrProtocolToString(ncr_protocol_))); + // Set ncr-format + result->set("ncr-format", + Element::create(dhcp_ddns::ncrFormatToString(ncr_format_))); + // Set always-include-fqdn + result->set("always-include-fqdn", Element::create(always_include_fqdn_)); + // Set override-no-update + result->set("override-no-update", Element::create(override_no_update_)); + // Set override-client-update + result->set("override-client-update", + Element::create(override_client_update_)); + // Set replace-client-name + result->set("replace-client-name", + Element::create(replaceClientNameModeToString + (replace_client_name_mode_))); + // Set generated-prefix + result->set("generated-prefix", Element::create(generated_prefix_)); + return (result); +} + std::ostream& operator<<(std::ostream& os, const D2ClientConfig& config) { os << config.toText(); diff --git a/src/lib/dhcpsrv/d2_client_cfg.h b/src/lib/dhcpsrv/d2_client_cfg.h index 4ac80a3f29..190ea81d88 100644 --- a/src/lib/dhcpsrv/d2_client_cfg.h +++ b/src/lib/dhcpsrv/d2_client_cfg.h @@ -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 @@ -11,7 +11,9 @@ /// This file defines the classes Kea uses to manage configuration needed to /// act as a client of the kea-dhcp-ddns module (aka D2). /// + #include +#include #include #include @@ -46,7 +48,7 @@ public: /// parameters associated with DHCP-DDNS and acting as a client of D2. /// Instances of this class may be constructed through configuration parsing. /// -class D2ClientConfig { +class D2ClientConfig : public isc::data::CfgToElement { public: /// @brief Default configuration constants. /// @todo For now these are hard-coded as configuration layer cannot @@ -230,6 +232,11 @@ public: /// "unknown" if not. static std::string replaceClientNameModeToString(const ReplaceClientNameMode& mode); + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + protected: /// @brief Validates member values. /// diff --git a/src/lib/dhcpsrv/logging_info.cc b/src/lib/dhcpsrv/logging_info.cc index aacd52c45e..2872a7e8ef 100644 --- a/src/lib/dhcpsrv/logging_info.cc +++ b/src/lib/dhcpsrv/logging_info.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -10,6 +10,7 @@ #include using namespace isc::log; +using namespace isc::data; namespace isc { namespace dhcp { @@ -137,5 +138,60 @@ LoggingInfo::toSpec() const { return (spec); } +ElementPtr +LoggingInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set name + result->set("name", Element::create(name_)); + // Set output_options + ElementPtr options = Element::createList(); + for (std::vector::const_iterator dest = + destinations_.cbegin(); + dest != destinations_.cend(); ++dest) { + ElementPtr map = Element::createMap(); + // Set output + map->set("output", Element::create(dest->output_)); + // Set maxver + map->set("maxver", Element::create(dest->maxver_)); + // Set maxsize + map->set("maxsize", + Element::create(static_cast(dest->maxsize_))); + // Set flush + map->set("flush", Element::create(dest->flush_)); + // Push on output option list + options->add(map); + } + result->set("output_options", options); + // Set severity + std::string severity; + switch (severity_) { + case isc::log::DEBUG: + severity = "DEBUG"; + break; + case isc::log::INFO: + severity = "INFO"; + break; + case isc::log::WARN: + severity = "WARN"; + break; + case isc::log::ERROR: + severity = "ERROR"; + break; + case isc::log::FATAL: + severity = "FATAL"; + break; + case isc::log::NONE: + severity = "NONE"; + break; + default: + isc_throw(ToElementError, "illegal severity: " << severity_); + break; + } + result->set("severity", Element::create(severity)); + // Set debug level + result->set("debuglevel", Element::create(debuglevel_)); + return (result); +} + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/logging_info.h b/src/lib/dhcpsrv/logging_info.h index ac5ec01d99..fb6edf2d31 100644 --- a/src/lib/dhcpsrv/logging_info.h +++ b/src/lib/dhcpsrv/logging_info.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -65,7 +66,7 @@ struct LoggingDestination { /// "severity": "WARN", /// "debuglevel": 99 /// }, -struct LoggingInfo { +struct LoggingInfo : public isc::data::CfgToElement { /// @brief logging name std::string name_; @@ -116,6 +117,11 @@ struct LoggingInfo { /// @brief Converts logger configuration to a spec. isc::log::LoggerSpecification toSpec() const; + + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; }; /// @brief storage for logging information in log4cplus format diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc index 2194cffc31..97a434ac3d 100644 --- a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc +++ b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc @@ -62,88 +62,91 @@ void ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary, ConstElementPtr class_def_cfg, uint16_t family) { + // name is now mandatory + std::string name = getString(class_def_cfg, "name"); + if (name.empty()) { + isc_throw(DhcpConfigError, + "not empty parameter 'name' is required " + << getPosition("name", class_def_cfg) << ")"); + } - try { - std::string name; - std::string next_server_txt = "0.0.0.0"; - std::string sname; - std::string filename; - ExpressionPtr match_expr; - CfgOptionPtr options(new CfgOption()); - - // Parse the elements that make up the client class definition. - BOOST_FOREACH(ConfigPair param, class_def_cfg->mapValue()) { - std::string entry(param.first); - ConstElementPtr value(param.second); - - if (entry == "name") { - name = value->stringValue(); - - } else if (entry == "test") { - ExpressionParser parser; - parser.parse(match_expr, value, family); - - } else if (entry == "option-data") { - OptionDataListParser opts_parser(family); - opts_parser.parse(options, value); - - } else if (entry == "next-server") { - next_server_txt = value->stringValue(); - - } else if (entry == "server-hostname") { - sname = value->stringValue(); - - } else if (entry == "boot-file-name") { - filename = value->stringValue(); - - } else { - isc_throw(DhcpConfigError, "invalid parameter '" << entry - << "' (" << value->getPosition() << ")"); - } - } + // Parse matching expression + ExpressionPtr match_expr; + ConstElementPtr test_cfg = class_def_cfg->get("test"); + std::string test; + if (test_cfg) { + ExpressionParser parser; + parser.parse(match_expr, test_cfg, family); + test = test_cfg->stringValue(); + } - // name is now mandatory - if (name.empty()) { - isc_throw(DhcpConfigError, - "not empty parameter 'name' is required"); - } + // Parse option data + CfgOptionPtr options(new CfgOption()); + ConstElementPtr option_data = class_def_cfg->get("option-data"); + if (option_data) { + OptionDataListParser opts_parser(family); + opts_parser.parse(options, option_data); + } - // Let's parse the next-server field - IOAddress next_server("0.0.0.0"); + // Let's try to parse the next-server field + IOAddress next_server("0.0.0.0"); + if (class_def_cfg->contains("next-server")) { + std::string next_server_txt = getString(class_def_cfg, "next-server"); try { next_server = IOAddress(next_server_txt); } catch (const IOError& ex) { - isc_throw(DhcpConfigError, "Invalid next-server value specified: '" - << next_server_txt); + isc_throw(DhcpConfigError, + "Invalid next-server value specified: '" + << next_server_txt << "' (" + << getPosition("next-server", class_def_cfg) << ")"); } if (next_server.getFamily() != AF_INET) { isc_throw(DhcpConfigError, "Invalid next-server value: '" - << next_server_txt << "', must be IPv4 address"); + << next_server_txt + << "', must be IPv4 address (" + << getPosition("next-server", class_def_cfg) << ")"); } if (next_server.isV4Bcast()) { isc_throw(DhcpConfigError, "Invalid next-server value: '" - << next_server_txt << "', must not be a broadcast"); + << next_server_txt + << "', must not be a broadcast (" + << getPosition("next-server", class_def_cfg) << ")"); } + } + + // Let's try to parse server-hostname + std::string sname; + if (class_def_cfg->contains("server-hostname")) { + sname = getString(class_def_cfg, "server-hostname"); - // Let's try to parse server-hostname if (sname.length() >= Pkt4::MAX_SNAME_LEN) { isc_throw(DhcpConfigError, "server-hostname must be at most " << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is " - << sname.length()); + << sname.length() << " (" + << getPosition("server-hostname", class_def_cfg) << ")"); } + } + + // Let's try to parse boot-file-name + std::string filename; + if (class_def_cfg->contains("boot-file-name")) { + filename = getString(class_def_cfg, "boot-file-name"); - // Let's try to parse boot-file-name if (filename.length() > Pkt4::MAX_FILE_LEN) { isc_throw(DhcpConfigError, "boot-file-name must be at most " << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is " - << filename.length()); + << filename.length() << " (" + << getPosition("boot-file-name", class_def_cfg) << ")"); } - // Add the client class definition - class_dictionary->addClass(name, match_expr, options, next_server, - sname, filename); + } + + // Add the client class definition + try { + class_dictionary->addClass(name, match_expr, test, options, + next_server, sname, filename); } catch (const std::exception& ex) { isc_throw(DhcpConfigError, ex.what() << " (" << class_def_cfg->getPosition() << ")"); diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.h b/src/lib/dhcpsrv/parsers/dbaccess_parser.h index 5460504d79..4e662d43f1 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.h +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.h @@ -43,7 +43,7 @@ public: /// @brief Constructor /// /// @param db_type Specifies database type (lease or hosts) - DbAccessParser(DBType db_type); + explicit DbAccessParser(DBType db_type); /// The destructor. virtual ~DbAccessParser() diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index 0073a35d17..b778de67b4 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -295,10 +295,7 @@ OptionDataParser::extractSpace(ConstElementPtr parent) const { } } catch (std::exception& ex) { - // Append position of the option space parameter. Note, that in the case - // when 'space' was not specified a default value will be used and we - // should never get here. Therefore, it is ok to call getPosition for - // the space parameter here as this parameter will always be specified. + // Append position of the option space parameter. isc_throw(DhcpConfigError, ex.what() << " (" << getPosition("space", parent) << ")"); } @@ -445,12 +442,15 @@ OptionDataParser::createOption(ConstElementPtr option_data) { // Option definition has been found so let's use it to create // an instance of our option. try { - OptionPtr option = - !csv_format_param.isSpecified() || csv_format_param ? + bool use_csv = !csv_format_param.isSpecified() || csv_format_param; + OptionPtr option = use_csv ? def->optionFactory(universe, def->getCode(), data_tokens) : def->optionFactory(universe, def->getCode(), binary); desc.option_ = option; desc.persistent_ = false; + if (use_csv) { + desc.formatted_value_ = data_param; + } } catch (const isc::Exception& ex) { isc_throw(DhcpConfigError, "option data does not match" << " option definition (space: " << space_param @@ -479,7 +479,8 @@ void OptionDataListParser::parse(const CfgOptionPtr& cfg, BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) { std::pair option = option_parser.parse(data); - cfg->add(option.first.option_, option.first.persistent_, option.second); + // Use the option description to keep the formatted value + cfg->add(option.first, option.second); cfg->encapsulate(); } } @@ -600,57 +601,31 @@ RelayInfoParser::RelayInfoParser(const Option::Universe& family) : family_(family) { }; +// Can't use a constructor as a function +namespace { +IOAddress buildIOAddress(const std::string& str) { return (IOAddress(str)); } +}; + +IOAddress +RelayInfoParser::getIOAddress(ConstElementPtr scope, + const std::string& name) { + return (getAndConvert(scope, name, "address")); +} + void RelayInfoParser::parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg, ConstElementPtr relay_info) { - // Let's start with some sanity checks. - if (!relay_info || !cfg) { - isc_throw(DhcpConfigError, "Logic error: RelayInfoParser::parse() called " - "with at least one NULL parameter."); - } - - if (relay_info->getType() != Element::map) { - isc_throw(DhcpConfigError, "Configuration error: RelayInfoParser::parse() " - "called with non-map parameter"); - } - - // Now create the default value. - isc::asiolink::IOAddress ip(family_ == Option::V4 ? IOAddress::IPV4_ZERO_ADDRESS() - : IOAddress::IPV6_ZERO_ADDRESS()); - - // Now iterate over all parameters. Currently there's only one supported - // parameter, so it should be an easy thing to check. - bool ip_address_specified = false; - BOOST_FOREACH(ConfigPair param, relay_info->mapValue()) { - if (param.first == "ip-address") { - ip_address_specified = true; - - try { - ip = asiolink::IOAddress(param.second->stringValue()); - } catch (...) { - isc_throw(DhcpConfigError, "Failed to parse ip-address " - "value: " << param.second - << " (" << param.second->getPosition() << ")"); - } - - // Check if the address family matches. - if ( (ip.isV4() && family_ != Option::V4) || - (ip.isV6() && family_ != Option::V6) ) { - isc_throw(DhcpConfigError, "ip-address field " << ip.toText() - << " does not have IP address of expected family type: " - << (family_ == Option::V4 ? "IPv4" : "IPv6") - << " (" << param.second->getPosition() << ")"); - } - } else { - isc_throw(NotImplemented, - "parser error: RelayInfoParser parameter not supported: " - << param.second); - } - } - - if (!ip_address_specified) { - isc_throw(DhcpConfigError, "'relay' specified, but mandatory 'ip-address' " - "paramter in it is missing"); + // There is only one parameter which is mandatory + IOAddress ip = getIOAddress(relay_info, "ip-address"); + + // Check if the address family matches. + if ((ip.isV4() && family_ != Option::V4) || + (ip.isV6() && family_ != Option::V6) ) { + isc_throw(DhcpConfigError, "ip-address field " << ip.toText() + << " does not have IP address of expected family type: " + << (family_ == Option::V4 ? "IPv4" : "IPv6") + << " (" << getPosition("ip-address", relay_info) << ")"); } // Ok, we're done with parsing. Let's store the result in the structure @@ -914,17 +889,10 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) { try { std::string hr_mode = getString(params, "reservation-mode"); subnet_->setHostReservationMode(hrModeFromText(hr_mode)); - } catch (const BadValue& ex) { - ConstElementPtr mode = params->get("reservation-mode"); - string pos; - if (mode) { - pos = mode->getPosition().str(); - } else { - pos = params->getPosition().str(); - } - isc_throw(DhcpConfigError, "Failed to process specified value " + } catch (const BadValue& ex) { + isc_throw(DhcpConfigError, "Failed to process specified value " " of reservation-mode parameter: " << ex.what() - << "(" << pos << ")"); + << "(" << getPosition("reservation-mode", params) << ")"); } // Try setting up client class. @@ -943,17 +911,6 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) { //**************************** D2ClientConfigParser ********************** -uint32_t -D2ClientConfigParser::getUint32(ConstElementPtr scope, - const std::string& name) { - return (getIntType(scope, name)); -} - -// Can't use a constructor as a function -namespace { -IOAddress buildIOAddress(const std::string& str) { return (IOAddress(str)); } -}; - IOAddress D2ClientConfigParser::getIOAddress(ConstElementPtr scope, const std::string& name) { diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index d84d976ec7..6ddf10bf5d 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h @@ -365,7 +365,7 @@ public: /// @brief Constructor. /// /// @param address_family Address family: @c AF_INET or @c AF_INET6. - OptionDataParser(const uint16_t address_family); + explicit OptionDataParser(const uint16_t address_family); /// @brief Parses ElementPtr containing option definition /// @@ -477,7 +477,7 @@ public: /// @brief Constructor. /// /// @param address_family Address family: @c AF_INET or AF_INET6 - OptionDataListParser(const uint16_t address_family); + explicit OptionDataListParser(const uint16_t address_family); /// @brief Parses a list of options, instantiates them and stores in cfg /// @@ -620,7 +620,7 @@ public: /// @brief constructor /// @param family specifies protocol family (IPv4 or IPv6) - RelayInfoParser(const isc::dhcp::Option::Universe& family); + explicit RelayInfoParser(const isc::dhcp::Option::Universe& family); /// @brief parses the actual relay parameters /// @@ -632,7 +632,18 @@ public: void parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg, isc::data::ConstElementPtr relay_info); -protected: +private: + + /// @brief Returns a value converted to IOAddress + /// + /// Instantiation of getAndConvert() to IOAddress + /// + /// @param scope specified parameter will be extracted from this scope + /// @param name name of the parameter + /// @return an IOAddress value + isc::asiolink::IOAddress + getIOAddress(isc::data::ConstElementPtr scope, const std::string& name); + /// Protocol family (IPv4 or IPv6) Option::Universe family_; }; @@ -664,7 +675,7 @@ public: /// @brief constructor /// /// @param family address family: @c AF_INET or @c AF_INET6 - SubnetConfigParser(uint16_t family); + explicit SubnetConfigParser(uint16_t family); /// @brief virtual destructor (does nothing) virtual ~SubnetConfigParser() { } @@ -776,16 +787,6 @@ public: private: - /// @brief Returns a value converted to uint32_t - /// - /// Instantiation of getIntType() to uint32_t - /// - /// @param scope specified parameter will be extracted from this scope - /// @param name name of the parameter - /// @return an uint32_t value - uint32_t - getUint32(isc::data::ConstElementPtr scope, const std::string& name); - /// @brief Returns a value converted to IOAddress /// /// Instantiation of getAndConvert() to IOAddress diff --git a/src/lib/dhcpsrv/parsers/duid_config_parser.cc b/src/lib/dhcpsrv/parsers/duid_config_parser.cc index 980bd298b8..00116d8d8e 100644 --- a/src/lib/dhcpsrv/parsers/duid_config_parser.cc +++ b/src/lib/dhcpsrv/parsers/duid_config_parser.cc @@ -24,107 +24,62 @@ namespace isc { namespace dhcp { void -DUIDConfigParser::parse(const CfgDUIDPtr& cfg, isc::data::ConstElementPtr duid_configuration) { - if (!cfg) { - isc_throw(DhcpConfigError, "Must provide valid pointer to cfg when parsing duid"); - } - - bool type_present = false; - BOOST_FOREACH(ConfigPair element, duid_configuration->mapValue()) { - try { - if (element.first == "type") { - type_present = true; - setType(cfg, element.second->stringValue()); - } else if (element.first == "identifier") { - setIdentifier(cfg, element.second->stringValue()); - } else if (element.first == "htype") { - setHType(cfg, element.second->intValue()); - } else if (element.first == "time") { - setTime(cfg, element.second->intValue()); - } else if (element.first == "enterprise-id") { - setEnterpriseId(cfg, element.second->intValue()); - } else if (element.first == "persist") { - setPersist(cfg, element.second->boolValue()); - } else { - isc_throw(DhcpConfigError, "unsupported configuration " - "parameter '" << element.first << "'"); - } - } catch (const std::exception& ex) { - // Append position. - isc_throw(DhcpConfigError, ex.what() << " (" - << element.second->getPosition() << ")"); +DUIDConfigParser::parse(const CfgDUIDPtr& cfg, + isc::data::ConstElementPtr duid_configuration) { + + std::string param; + try { + param = "type"; + std::string duid_type = getString(duid_configuration, "type"); + // Map DUID type represented as text into numeric value. + DUID::DUIDType numeric_type = DUID::DUID_UNKNOWN; + if (duid_type == "LLT") { + numeric_type = DUID::DUID_LLT; + } else if (duid_type == "EN") { + numeric_type = DUID::DUID_EN; + } else if (duid_type == "LL") { + numeric_type = DUID::DUID_LL; + } else { + isc_throw(BadValue, "unsupported DUID type '" + << duid_type << "'. Expected: LLT, EN or LL"); } - } - - // "type" is mandatory - if (!type_present) { - isc_throw(DhcpConfigError, "mandatory parameter \"type\" not specified" - " for the DUID configuration (" - << duid_configuration->getPosition() << ")"); - } - - LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_CONFIGURE_SERVERID); -} -void -DUIDConfigParser::setType(const CfgDUIDPtr& cfg, const std::string& duid_type) const { - // Map DUID type represented as text into numeric value. - DUID::DUIDType numeric_type = DUID::DUID_UNKNOWN; - if (duid_type == "LLT") { - numeric_type = DUID::DUID_LLT; - } else if (duid_type == "EN") { - numeric_type = DUID::DUID_EN; - } else if (duid_type == "LL") { - numeric_type = DUID::DUID_LL; - } else { - isc_throw(DhcpConfigError, "unsupported DUID type '" - << duid_type << "'. Expected: LLT, EN or LL"); - } - - cfg->setType(static_cast(numeric_type)); -} + cfg->setType(static_cast(numeric_type)); -void -DUIDConfigParser::setIdentifier(const CfgDUIDPtr& cfg, const std::string& identifier) const { - cfg->setIdentifier(identifier); -} - -void -DUIDConfigParser::setHType(const CfgDUIDPtr& cfg, const int64_t htype) const { - checkRange("htype", htype); - cfg->setHType(static_cast(htype)); -} + param = "identifier"; + if (duid_configuration->contains(param)) { + cfg->setIdentifier(getString(duid_configuration, param)); + } -void -DUIDConfigParser::setTime(const CfgDUIDPtr& cfg, const int64_t new_time) const { - checkRange("time", new_time); - cfg->setTime(static_cast(new_time)); -} + param = "htype"; + if (duid_configuration->contains(param)) { + cfg->setHType(getUint16(duid_configuration, param)); + } -void -DUIDConfigParser::setEnterpriseId(const CfgDUIDPtr& cfg, const int64_t enterprise_id) const { - checkRange("enterprise-id", enterprise_id); - cfg->setEnterpriseId(static_cast(enterprise_id)); -} + param = "time"; + if (duid_configuration->contains(param)) { + cfg->setTime(getUint32(duid_configuration, param)); + } -void -DUIDConfigParser::setPersist(const CfgDUIDPtr& cfg, const bool persist) { - cfg->setPersist(persist); -} + param = "enterprise-id"; + if (duid_configuration->contains(param)) { + cfg->setEnterpriseId(getUint32(duid_configuration, param)); + } -template -void -DUIDConfigParser::checkRange(const std::string& parameter_name, - const int64_t parameter_value) const { - if ((parameter_value < 0) || - (parameter_value > std::numeric_limits::max())) { - isc_throw(DhcpConfigError, "out of range value '" << parameter_value - << "' specified for parameter '" << parameter_name - << "'; expected value in range of [0.." - << std::numeric_limits::max() << "]"); + param = "persist"; + if (duid_configuration->contains(param)) { + cfg->setPersist(getBoolean(duid_configuration, param)); + } + } catch (const DhcpConfigError&) { + throw; + } catch (const std::exception& ex) { + // Append position. + isc_throw(DhcpConfigError, ex.what() << " (" + << getPosition(param, duid_configuration) << ")"); } -} + LOG_WARN(dhcpsrv_logger, DHCPSRV_CFGMGR_CONFIGURE_SERVERID); +} } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/parsers/duid_config_parser.h b/src/lib/dhcpsrv/parsers/duid_config_parser.h index e745a050ff..8780031147 100644 --- a/src/lib/dhcpsrv/parsers/duid_config_parser.h +++ b/src/lib/dhcpsrv/parsers/duid_config_parser.h @@ -33,57 +33,6 @@ public: /// /// @throw DhcpConfigError If the configuration is invalid. void parse(const CfgDUIDPtr& cfg, isc::data::ConstElementPtr duid_configuration); -private: - - /// @brief Validate and set DUID type. - /// - /// @param cfg parsed information will be stored here - /// @param duid_type DUID type in textual format. - void setType(const CfgDUIDPtr& cfg, const std::string& duid_type) const; - - /// @brief Validate and set identifier. - /// - /// @param cfg parsed information will be stored here - /// @param identifier Identifier. - void setIdentifier(const CfgDUIDPtr& cfg, const std::string& identifier) const; - - /// @brief Validate and set hardware type. - /// - /// @param cfg parsed information will be stored here - /// @param htype Hardware type. - void setHType(const CfgDUIDPtr& cfg, const int64_t htype) const; - - /// @brief Validate and set time value. - /// - /// @param cfg parsed information will be stored here - /// @param new_time Time value to be used for DUID. - void setTime(const CfgDUIDPtr& cfg, const int64_t new_time) const; - - /// @brief Validate and set enterprise id. - /// - /// @param cfg parsed information will be stored here - /// @param enterprise_id Enterprise id. - void setEnterpriseId(const CfgDUIDPtr& cfg, const int64_t enterprise_id) const; - - /// @brief Set persistence flag. - /// - /// @param cfg parsed information will be stored here - /// @param persist A boolean value indicating if the server - /// identifier should be stored on the disk (if true) or - /// not (if false). - void setPersist(const CfgDUIDPtr& cfg, const bool persist); - - /// @brief Verifies if the specified parameter is in range. - /// - /// Each numeric value must be in range of [0 .. max_value], where - /// max_value is a maximum value for the numeric type used for this - /// parameter. - /// - /// @param parameter_name Parameter name. - /// @tparam Numeric type of the specified parameter. - template - void checkRange(const std::string& parameter_name, - const int64_t parameter_value) const; }; } diff --git a/src/lib/dhcpsrv/parsers/expiration_config_parser.cc b/src/lib/dhcpsrv/parsers/expiration_config_parser.cc index 170e95abba..94e30e9580 100644 --- a/src/lib/dhcpsrv/parsers/expiration_config_parser.cc +++ b/src/lib/dhcpsrv/parsers/expiration_config_parser.cc @@ -20,43 +20,46 @@ void ExpirationConfigParser::parse(ConstElementPtr expiration_config) { CfgExpirationPtr cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration(); - BOOST_FOREACH(ConfigPair config_element, expiration_config->mapValue()) { + std::string param; - // Get parameter name and value. - std::string param_name = config_element.first; - ConstElementPtr param_value = config_element.second; - - try { - // Set configuration parameters. - if (param_name == "reclaim-timer-wait-time") { - cfg->setReclaimTimerWaitTime(param_value->intValue()); - - } else if (param_name == "flush-reclaimed-timer-wait-time") { - cfg->setFlushReclaimedTimerWaitTime(param_value->intValue()); - - } else if (param_name == "hold-reclaimed-time") { - cfg->setHoldReclaimedTime(param_value->intValue()); + try { + param = "reclaim-timer-wait-time"; + if (expiration_config->contains(param)) { + cfg->setReclaimTimerWaitTime(getInteger(expiration_config, param)); + } - } else if (param_name == "max-reclaim-leases") { - cfg->setMaxReclaimLeases(param_value->intValue()); + param = "flush-reclaimed-timer-wait-time"; + if (expiration_config->contains(param)) { + cfg->setFlushReclaimedTimerWaitTime(getInteger(expiration_config, + param)); + } - } else if (param_name == "max-reclaim-time") { - cfg->setMaxReclaimTime(param_value->intValue()); + param = "hold-reclaimed-time"; + if (expiration_config->contains(param)) { + cfg->setHoldReclaimedTime(getInteger(expiration_config, param)); + } - } else if (param_name == "unwarned-reclaim-cycles") { - cfg->setUnwarnedReclaimCycles(param_value->intValue()); + param = "max-reclaim-leases"; + if (expiration_config->contains(param)) { + cfg->setMaxReclaimLeases(getInteger(expiration_config, param)); + } - } else { - isc_throw(DhcpConfigError, "unsupported parameter '" - << param_name << "'"); - } + param = "max-reclaim-time"; + if (expiration_config->contains(param)) { + cfg->setMaxReclaimTime(getInteger(expiration_config, param)); + } - } catch (const std::exception& ex) { - // Append position of the configuration parameter to the error - // message. - isc_throw(DhcpConfigError, ex.what() << " (" - << param_value->getPosition() << ")"); + param = "unwarned-reclaim-cycles"; + if (expiration_config->contains(param)) { + cfg->setUnwarnedReclaimCycles( + getInteger(expiration_config, param)); } + } catch (const DhcpConfigError&) { + throw; + } catch (const std::exception& ex) { + // Append position of the configuration parameter to the error message. + isc_throw(DhcpConfigError, ex.what() << " (" + << getPosition(param, expiration_config) << ")"); } } diff --git a/src/lib/dhcpsrv/parsers/ifaces_config_parser.h b/src/lib/dhcpsrv/parsers/ifaces_config_parser.h index a4c346e36d..d23a716dc5 100644 --- a/src/lib/dhcpsrv/parsers/ifaces_config_parser.h +++ b/src/lib/dhcpsrv/parsers/ifaces_config_parser.h @@ -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 @@ -29,7 +29,7 @@ public: /// @brief Constructor /// /// @param protocol AF_INET for DHCPv4 and AF_INET6 for DHCPv6. - IfacesConfigParser(const uint16_t protocol); + explicit IfacesConfigParser(const uint16_t protocol); /// @brief Parses content of the "interfaces-config". /// diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h index cbeb71ae96..48dc0588ed 100644 --- a/src/lib/dhcpsrv/pool.h +++ b/src/lib/dhcpsrv/pool.h @@ -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 @@ -263,7 +263,7 @@ public: /// This may be useful for "prefix/len" style definition for /// addresses, but is mostly useful for prefix pools. /// @return prefix length (1-128) - uint8_t getLength() { + uint8_t getLength() const { return (prefix_len_); } diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index fe8b121952..13949c7da7 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -15,6 +15,7 @@ #include using namespace isc::log; +using namespace isc::data; namespace isc { namespace dhcp { @@ -200,5 +201,102 @@ SrvConfig::updateStatistics() { } } +ElementPtr +SrvConfig::toElement() const { + // Get family for the configuration manager + uint16_t family = CfgMgr::instance().getFamily(); + // Toplevel map + ElementPtr result = Element::createMap(); + // DhcpX global map + ElementPtr dhcp = Element::createMap(); + // Set decline-probation-period + dhcp->set("decline-probation-period", + Element::create(static_cast(decline_timer_))); + // Set echo-client-id (DHCPv4) + if (family == AF_INET) { + dhcp->set("echo-client-id", Element::create(echo_v4_client_id_)); + } + // Set dhcp4o6-port + dhcp->set("dhcp4o6-port", + Element::create(static_cast(dhcp4o6_port_))); + // Set dhcp-ddns + dhcp->set("dhcp-ddns", d2_client_config_->toElement()); + // Set interfaces-config + ConstElementPtr ifaces = cfg_iface_->toElement(); + dhcp->set("interfaces-config", cfg_iface_->toElement()); + // Set option-def + ConstElementPtr option_def = cfg_option_def_->toElement(); + dhcp->set("option-def", option_def); + // Set option-data + ConstElementPtr option_data = cfg_option_->toElement(); + dhcp->set("option-data", option_data); + // Set subnets + if (family == AF_INET) { + ConstElementPtr subnets = cfg_subnets4_->toElement(); + // @todo Insert reservations + dhcp->set("subnet4", subnets); + } else { + ConstElementPtr subnets = cfg_subnets6_->toElement(); + // @todo Insert reservations + dhcp->set("subnet6", subnets); + } + // Set relay-supplied-options (DHCPv6) + if (family == AF_INET6) { + dhcp->set("relay-supplied-options", cfg_rsoo_->toElement()); + } + // Set expired-leases-processing + ConstElementPtr expired = cfg_expiration_->toElement(); + dhcp->set("expired-leases-processing", expired); + // Set server-id (DHCPv6) + if (family == AF_INET6) { + dhcp->set("server-id", cfg_duid_->toElement()); + } + // Set lease-database + CfgLeaseDbAccess lease_db(*cfg_db_access_); + dhcp->set("lease-database", lease_db.toElement()); + // Set hosts-database + CfgHostDbAccess host_db(*cfg_db_access_); + dhcp->set("hosts-database", host_db.toElement()); + // Set host-reservation-identifiers + ConstElementPtr host_ids; + if (family == AF_INET) { + host_ids = cfg_host_operations4_->toElement(); + } else { + host_ids = cfg_host_operations6_->toElement(); + } + dhcp->set("host-reservation-identifiers", host_ids); + // Set mac-sources (DHCPv6) + if (family == AF_INET6) { + dhcp->set("mac-sources", cfg_mac_source_.toElement()); + } + // Set control-socket (skip if null as empty is not legal) + if (!isNull(control_socket_)) { + dhcp->set("control-socket", control_socket_); + } + // Set client-classes + ConstElementPtr client_classes = class_dictionary_->toElement(); + dhcp->set("client-classes", client_classes); + // Set hooks-libraries + ConstElementPtr hooks_libs = hooks_config_.toElement(); + dhcp->set("hooks-libraries", hooks_libs); + // Set DhcpX + result->set(family == AF_INET ? "Dhcp4" : "Dhcp6", dhcp); + + // Logging global map (skip if loggers is empty) + ElementPtr logging = Element::createMap(); + // Set loggers list + ElementPtr loggers = Element::createList(); + for (LoggingInfoStorage::const_iterator logger = logging_info_.cbegin(); + logger != logging_info_.cend(); ++logger) { + loggers->add(logger->toElement()); + } + if (!loggers->empty()) { + logging->set("loggers", loggers); + result->set("Logging", logging); + } + + return (result); +} + } } diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h index 6c12548dfd..020d95e447 100644 --- a/src/lib/dhcpsrv/srv_config.h +++ b/src/lib/dhcpsrv/srv_config.h @@ -7,6 +7,7 @@ #ifndef DHCPSRV_CONFIG_H #define DHCPSRV_CONFIG_H +#include #include #include #include @@ -37,7 +38,7 @@ class CfgMgr; /// @brief Specifies current DHCP configuration /// /// @todo Migrate all other configuration parameters from cfgmgr.h here -class SrvConfig { +class SrvConfig : public isc::data::CfgToElement { public: /// @name Constants for selection of parameters returned by @c getConfigSummary /// @@ -507,7 +508,7 @@ public: /// this socket is bound and connected to this port and port + 1 /// /// @param port port and port + 1 to use - void setDhcp4o6Port(uint32_t port) { + void setDhcp4o6Port(uint16_t port) { /// @todo: Port is supposed to be uint16_t, not uint32_t dhcp4o6_port_ = port; } @@ -516,7 +517,7 @@ public: /// /// See @ref setDhcp4o6Port for brief discussion. /// @return value of DHCP4o6 IPC port - uint32_t getDhcp4o6Port() { + uint16_t getDhcp4o6Port() { return (dhcp4o6_port_); } @@ -536,6 +537,11 @@ public: d2_client_config_ = d2_client_config; } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Sequence number identifying the configuration. @@ -624,7 +630,7 @@ private: /// /// DHCPv4-over-DHCPv6 uses a UDP socket for interserver communication, /// this socket is bound and connected to this port and port + 1 - uint32_t dhcp4o6_port_; + uint16_t dhcp4o6_port_; D2ClientConfigPtr d2_client_config_; }; diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 6aa833aaba..198bf457cb 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -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 @@ -310,6 +310,16 @@ public: void allowClientClass(const isc::dhcp::ClientClass& class_name); + /// @brief returns the client class white list + /// + /// @note The returned reference is only valid as long as the object + /// returned it is valid. + /// + /// @return client classes @ref white_list_ + const isc::dhcp::ClientClasses& getClientClasses() const { + return (white_list_); + } + /// @brief Specifies what type of Host Reservations are supported. /// /// Host reservations may be either in-pool (they reserve an address that diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index 8729de987d..97368608d0 100644 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/src/lib/dhcpsrv/tests/Makefile.am @@ -160,6 +160,7 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la +libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la diff --git a/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc b/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc index 1b51fa5b19..f71c6a420d 100644 --- a/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc +++ b/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-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 @@ -244,6 +244,95 @@ TEST(AddrUtilitiesTest, addrsInRange6) { isc::BadValue); } +// Checks if IPv4 address ranges can be converted to prefix / prefix_len +TEST(AddrUtilitiesTest, prefixLengthFromRange4) { + // Use a shorter name + const auto& plfr = prefixLengthFromRange; + + // Let's start with something simple + EXPECT_EQ(32, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.0"))); + EXPECT_EQ(31, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.1"))); + EXPECT_EQ(30, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.3"))); + EXPECT_EQ(29, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.7"))); + EXPECT_EQ(28, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.15"))); + EXPECT_EQ(27, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.31"))); + EXPECT_EQ(26, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.63"))); + EXPECT_EQ(25, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.127"))); + EXPECT_EQ(24, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.2.255"))); + EXPECT_EQ(23, plfr(IOAddress("192.0.2.0"), IOAddress("192.0.3.255"))); + EXPECT_EQ(16, plfr(IOAddress("10.0.0.0"), IOAddress("10.0.255.255"))); + EXPECT_EQ(8, plfr(IOAddress("10.0.0.0"), IOAddress("10.255.255.255"))); + EXPECT_EQ(0, plfr(IOAddress("0.0.0.0"), IOAddress("255.255.255.255"))); + + // Fail if a network boundary is crossed + EXPECT_EQ(-1, plfr(IOAddress("10.0.0.255"), IOAddress("10.0.1.1"))); + + // The upper bound cannot be smaller than the lower bound + EXPECT_THROW(plfr(IOAddress("192.0.2.5"), IOAddress("192.0.2.4")), + isc::BadValue); +} + +// Checks if IPv6 address ranges can be converted to prefix / prefix_len +TEST(AddrUtilitiesTest, prefixLengthFromRange6) { + // Use a shorter name + const auto& plfr = prefixLengthFromRange; + + // Let's start with something simple + EXPECT_EQ(128, plfr(IOAddress("::"), IOAddress("::"))); + EXPECT_EQ(112, plfr(IOAddress("fe80::"), IOAddress("fe80::ffff"))); + EXPECT_EQ(96, plfr(IOAddress("fe80::"), IOAddress("fe80::ffff:ffff"))); + EXPECT_EQ(80, plfr(IOAddress("fe80::"), + IOAddress("fe80::ffff:ffff:ffff"))); + EXPECT_EQ(64, plfr(IOAddress("fe80::"), + IOAddress("fe80::ffff:ffff:ffff:ffff"))); + EXPECT_EQ(63, plfr(IOAddress("fe80::"), + IOAddress("fe80::1:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(62, plfr(IOAddress("fe80::"), + IOAddress("fe80::3:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(61, plfr(IOAddress("fe80::"), + IOAddress("fe80::7:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(60, plfr(IOAddress("fe80::"), + IOAddress("fe80::f:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(59, plfr(IOAddress("fe80::"), + IOAddress("fe80::1f:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(58, plfr(IOAddress("fe80::"), + IOAddress("fe80::3f:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(57, plfr(IOAddress("fe80::"), + IOAddress("fe80::7f:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(56, plfr(IOAddress("fe80::"), + IOAddress("fe80::ff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(55, plfr(IOAddress("fe80::"), + IOAddress("fe80::1ff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(54, plfr(IOAddress("fe80::"), + IOAddress("fe80::3ff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(53, plfr(IOAddress("fe80::"), + IOAddress("fe80::7ff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(52, plfr(IOAddress("fe80::"), + IOAddress("fe80::fff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(51, plfr(IOAddress("fe80::"), + IOAddress("fe80::1fff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(50, plfr(IOAddress("fe80::"), + IOAddress("fe80::3fff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(49, plfr(IOAddress("fe80::"), + IOAddress("fe80::7fff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(48, plfr(IOAddress("fe80::"), + IOAddress("fe80::ffff:ffff:ffff:ffff:ffff"))); + EXPECT_EQ(0, plfr(IOAddress("::"), + IOAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))); + + // Fail if a network boundary is crossed + EXPECT_EQ(-1, plfr(IOAddress("2001:db8::ffff"), + IOAddress("2001:db8::1:1"))); + + // The upper bound cannot be smaller than the lower bound + EXPECT_THROW(plfr(IOAddress("fe80::5"), IOAddress("fe80::4")), + isc::BadValue); + + // Address family must match + EXPECT_THROW(plfr(IOAddress("192.0.2.0"), IOAddress("fe80::1")), + isc::BadValue); +} + // Checks if prefixInRange returns valid number of prefixes in specified range. TEST(AddrUtilitiesTest, prefixesInRange) { diff --git a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc index 80e39370b1..cc8361ae50 100644 --- a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc @@ -1,21 +1,24 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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 // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include #include #include #include #include #include +#include #include using namespace isc; using namespace isc::dhcp; using namespace isc::dhcp::test; +using namespace isc::test; namespace { @@ -23,7 +26,11 @@ namespace { TEST(CfgDbAccessTest, defaults) { CfgDbAccess cfg; EXPECT_EQ("type=memfile", cfg.getLeaseDbAccessString()); + std::string expected = "{ \"type\": \"memfile\" }"; + runToElementTest(expected, CfgLeaseDbAccess(cfg)); + EXPECT_TRUE(cfg.getHostDbAccessString().empty()); + runToElementTest("{ }", CfgHostDbAccess(cfg)); } // This test verifies that it is possible to set the lease database @@ -33,10 +40,17 @@ TEST(CfgDbAccessTest, setLeaseDbAccessString) { ASSERT_NO_THROW(cfg.setLeaseDbAccessString("type=mysql")); EXPECT_EQ("type=mysql", cfg.getLeaseDbAccessString()); + // Check unparse + std::string expected = "{ \"type\": \"mysql\" }"; + runToElementTest(expected, CfgLeaseDbAccess(cfg)); + // Append additional parameter. ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4")); EXPECT_EQ("type=mysql universe=4", cfg.getLeaseDbAccessString()); + // Additional parameters are not in lease_db_access_ + runToElementTest(expected, CfgLeaseDbAccess(cfg)); + // If access string is empty, no parameters will be appended. ASSERT_NO_THROW(cfg.setLeaseDbAccessString("")); EXPECT_TRUE(cfg.getLeaseDbAccessString().empty()); @@ -50,10 +64,17 @@ TEST(CfgDbAccessTest, setHostDbAccessString) { ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql")); EXPECT_EQ("type=mysql", cfg.getHostDbAccessString()); + // Check unparse + std::string expected = "{ \"type\": \"mysql\" }"; + runToElementTest(expected, CfgHostDbAccess(cfg)); + // Append additional parameter. ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4")); EXPECT_EQ("type=mysql universe=4", cfg.getHostDbAccessString()); + // Additional parameters are not in host_db_access_ + runToElementTest(expected, CfgHostDbAccess(cfg)); + // If access string is empty, no parameters will be appended. ASSERT_NO_THROW(cfg.setHostDbAccessString("")); EXPECT_TRUE(cfg.getHostDbAccessString().empty()); diff --git a/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc b/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc index 1d7699383d..df9c7daf77 100644 --- a/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_duid_unittest.cc @@ -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 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ using namespace isc; using namespace isc::dhcp; +using namespace isc::test; namespace { @@ -92,6 +94,14 @@ TEST_F(CfgDUIDTest, defaults) { EXPECT_EQ(0, cfg_duid.getTime()); EXPECT_EQ(0, cfg_duid.getEnterpriseId()); EXPECT_TRUE(cfg_duid.persist()); + + std::string expected = "{ \"type\": \"LLT\",\n" + "\"identifier\": \"\",\n" + "\"htype\": 0,\n" + "\"time\": 0,\n" + "\"enterprise-id\": 0,\n" + "\"persist\": true }"; + runToElementTest(expected, cfg_duid); } // This test verifies that it is possible to set values for the CfgDUID. @@ -112,6 +122,14 @@ TEST_F(CfgDUIDTest, setValues) { EXPECT_EQ(32100, cfg_duid.getTime()); EXPECT_EQ(10, cfg_duid.getEnterpriseId()); EXPECT_FALSE(cfg_duid.persist()); + + std::string expected = "{ \"type\": \"EN\",\n" + " \"identifier\": \"ABCDEF\",\n" + " \"htype\": 100,\n" + " \"time\": 32100,\n" + " \"enterprise-id\": 10,\n" + " \"persist\": false }"; + runToElementTest(expected, cfg_duid); } // This test checks positive scenarios for setIdentifier. @@ -165,7 +183,7 @@ TEST_F(CfgDUIDTest, createLLT) { duid->toText()); // Verify that the DUID file has been created. - EXPECT_TRUE(isc::test::fileExists(absolutePath(DUID_FILE_NAME))); + EXPECT_TRUE(fileExists(absolutePath(DUID_FILE_NAME))); } // This method checks that the DUID-EN can be created from the @@ -185,7 +203,7 @@ TEST_F(CfgDUIDTest, createEN) { EXPECT_EQ("00:02:00:00:10:10:25:0f:3e:26:a7:62", duid->toText()); // Verify that the DUID file has been created. - EXPECT_TRUE(isc::test::fileExists(absolutePath(DUID_FILE_NAME))); + EXPECT_TRUE(fileExists(absolutePath(DUID_FILE_NAME))); } // This method checks that the DUID-LL can be created from the @@ -205,7 +223,7 @@ TEST_F(CfgDUIDTest, createLL) { EXPECT_EQ("00:03:00:02:12:41:34:a4:b3:67", duid->toText()); // Verify that the DUID file has been created. - EXPECT_TRUE(isc::test::fileExists(absolutePath(DUID_FILE_NAME))); + EXPECT_TRUE(fileExists(absolutePath(DUID_FILE_NAME))); } // This test verifies that it is possible to disable storing @@ -226,7 +244,7 @@ TEST_F(CfgDUIDTest, createDisableWrite) { EXPECT_EQ("00:02:00:00:10:10:25:0f:3e:26:a7:62", duid->toText()); // DUID persistence is disabled so there should be no DUID file. - EXPECT_FALSE(isc::test::fileExists(absolutePath(DUID_FILE_NAME))); + EXPECT_FALSE(fileExists(absolutePath(DUID_FILE_NAME))); } } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc b/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc index fc05584b14..21f2aca2d7 100644 --- a/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc @@ -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 @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,19 @@ TEST(CfgExpirationTest, defaults) { cfg.getUnwarnedReclaimCycles()); } +/// @brief Tests that unparse returns an expected value +TEST(CfgExpirationTest, unparse) { + CfgExpiration cfg; + std::string defaults = "{\n" + "\"reclaim-timer-wait-time\": 10,\n" + "\"flush-reclaimed-timer-wait-time\": 25,\n" + "\"hold-reclaimed-time\": 3600,\n" + "\"max-reclaim-leases\": 100,\n" + "\"max-reclaim-time\": 250,\n" + "\"unwarned-reclaim-cycles\": 5 }"; + isc::test::runToElementTest(defaults, cfg); +} + // Test the {get,set}ReclaimTimerWaitTime. TEST(CfgExpirationTest, getReclaimTimerWaitTime) { testAccessModify(CfgExpiration::LIMIT_RECLAIM_TIMER_WAIT_TIME, diff --git a/src/lib/dhcpsrv/tests/cfg_host_operations_unittest.cc b/src/lib/dhcpsrv/tests/cfg_host_operations_unittest.cc index 9e304cd910..a38152ab2a 100644 --- a/src/lib/dhcpsrv/tests/cfg_host_operations_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_host_operations_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016-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,12 +8,14 @@ #include #include #include +#include #include #include #include using namespace isc; using namespace isc::dhcp; +using namespace isc::test; namespace { @@ -51,6 +53,7 @@ identifierAtPosition(const CfgHostOperations& cfg, const Host::IdentifierType& i TEST(CfgHostOperationsTest, defaults) { CfgHostOperations cfg; EXPECT_TRUE(cfg.getIdentifierTypes().empty()); + runToElementTest("[ ]", cfg); } // This test verifies that identifier types can be added into an @@ -76,9 +79,14 @@ TEST(CfgHostOperationsTest, addIdentifier) { EXPECT_TRUE(identifierAtPosition(cfg, Host::IDENT_DUID, 1)); EXPECT_TRUE(identifierAtPosition(cfg, Host::IDENT_CIRCUIT_ID, 2)); + // Check unparse + std::string ids = "[ \"hw-address\", \"duid\", \"circuit-id\" ]"; + runToElementTest(ids, cfg); + // Let's clear and make sure no identifiers are present. ASSERT_NO_THROW(cfg.clearIdentifierTypes()); EXPECT_TRUE(cfg.getIdentifierTypes().empty()); + runToElementTest("[ ]", cfg); } // This test verifies that the default DHCPv4 configuration is created diff --git a/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc b/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc index 457741085b..63951dd911 100644 --- a/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_iface_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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,11 +8,13 @@ #include #include #include +#include #include using namespace isc; using namespace isc::dhcp; using namespace isc::dhcp::test; +using namespace isc::test; namespace { @@ -358,6 +360,31 @@ TEST_F(CfgIfaceTest, equality) { EXPECT_FALSE(cfg1 != cfg2); } +// This test verifies that it is possible to unparse the interface config. +TEST_F(CfgIfaceTest, unparse) { + CfgIface cfg4; + + // Add things in it + EXPECT_NO_THROW(cfg4.use(AF_INET, "*")); + EXPECT_NO_THROW(cfg4.use(AF_INET, "eth0")); + EXPECT_NO_THROW(cfg4.use(AF_INET, "eth1/192.0.2.3")); + + // Check unparse + std::string expected = + "{ \"interfaces\": [ \"*\", \"eth0\", \"eth1/192.0.2.3\" ] }"; + runToElementTest(expected, cfg4); + + // Now check IPv6 + CfgIface cfg6; + EXPECT_NO_THROW(cfg6.use(AF_INET6, "*")); + EXPECT_NO_THROW(cfg6.use(AF_INET6, "eth1")); + EXPECT_NO_THROW(cfg6.use(AF_INET6, "eth0/2001:db8:1::1")); + + expected = + "{ \"interfaces\": [ \"*\", \"eth1\", \"eth0/2001:db8:1::1\" ] }"; + runToElementTest(expected, cfg6); +} + // This test verifies that it is possible to specify the socket // type to be used by the DHCPv4 server. // This test is enabled on LINUX and BSD only, because the @@ -372,6 +399,12 @@ TEST(CfgIfaceNoStubTest, useSocketType) { // For datagram sockets, the direct traffic is not supported. ASSERT_TRUE(!IfaceMgr::instance().isDirectResponseSupported()); + // Check unparse + std::string expected = "{\n" + " \"interfaces\": [ ],\n" + " \"dhcp-socket-type\": \"udp\" }"; + runToElementTest(expected, cfg); + // Select raw sockets. ASSERT_NO_THROW(cfg.useSocketType(AF_INET, "raw")); EXPECT_EQ("raw", cfg.socketTypeToText()); diff --git a/src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc b/src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc index c54158db36..b02c791d63 100644 --- a/src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc @@ -1,18 +1,23 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 // file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include + #include #include #include +#include #include +#include namespace { using namespace isc; using namespace isc::dhcp; +using namespace isc::test; // Checks whether CfgMACSource::MACSourceFromText is working correctly. // Technically, this is a Pkt, not Pkt6 test, but since there is no separate @@ -44,4 +49,36 @@ TEST(CfgMACSourceTest, MACSourceFromText) { CfgMACSource::MACSourceFromText("docsis-modem")); } +// Checks whether the opposite operation is working correctly. +TEST(CfgMACSourceTest, unparse) { + CfgMACSource cfg; + // any was added by the constructor + cfg.add(HWAddr::HWADDR_SOURCE_RAW); + cfg.add(HWAddr::HWADDR_SOURCE_DUID); + cfg.add(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL); + cfg.add(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION); + cfg.add(HWAddr::HWADDR_SOURCE_REMOTE_ID); + cfg.add(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID); + cfg.add(HWAddr::HWADDR_SOURCE_DOCSIS_CMTS); + cfg.add(HWAddr::HWADDR_SOURCE_DOCSIS_MODEM); + + // Unparse + std::string expected = "[" + "\"any\"," + "\"raw\"," + "\"duid\"," + "\"ipv6-link-local\"," + "\"client-link-addr-option\"," + "\"remote-id\"," + "\"subscriber-id\"," + "\"docsis-cmts\"," + "\"docsis-modem\"" + "]"; + runToElementTest(expected, cfg); + + // Add an unknown type + cfg.add(0x12345678); + ASSERT_THROW(cfg.toElement(), ToElementError); +} + }; diff --git a/src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc b/src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc index cb77858781..3abcdd2d27 100644 --- a/src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc @@ -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 @@ -9,6 +9,7 @@ #include #include #include +#include #include using namespace isc; @@ -245,4 +246,59 @@ TEST(CfgOptionDefTest, addNegative) { EXPECT_THROW(cfg.add(def, "isc"), DuplicateOptionDefinition); } +// This test verifies that the funtion that unparses configuration +// works as expected. +TEST(CfgOptionDefTest, unparse) { + CfgOptionDef cfg; + + // Add some options. + cfg.add(OptionDefinitionPtr(new + OptionDefinition("option-foo", 5, "uint16")), "isc"); + cfg.add(OptionDefinitionPtr(new + OptionDefinition("option-bar", 5, "uint16", true)), "dns"); + cfg.add(OptionDefinitionPtr(new + OptionDefinition("option-baz", 6, "uint16", "dns")), "isc"); + OptionDefinitionPtr rec(new OptionDefinition("option-rec", 6, "record")); + rec->addRecordField("uint16"); + rec->addRecordField("uint16"); + cfg.add(rec, "dns"); + + // Unparse + std::string expected = "[\n" + "{\n" + " \"name\": \"option-bar\",\n" + " \"code\": 5,\n" + " \"type\": \"uint16\",\n" + " \"array\": true,\n" + " \"record-types\": \"\",\n" + " \"encapsulate\": \"\",\n" + " \"space\": \"dns\"\n" + "},{\n" + " \"name\": \"option-rec\",\n" + " \"code\": 6,\n" + " \"type\": \"record\",\n" + " \"array\": false,\n" + " \"record-types\": \"uint16, uint16\",\n" + " \"encapsulate\": \"\",\n" + " \"space\": \"dns\"\n" + "},{\n" + " \"name\": \"option-foo\",\n" + " \"code\": 5,\n" + " \"type\": \"uint16\",\n" + " \"array\": false,\n" + " \"record-types\": \"\",\n" + " \"encapsulate\": \"\",\n" + " \"space\": \"isc\"\n" + "},{\n" + " \"name\": \"option-baz\",\n" + " \"code\": 6,\n" + " \"type\": \"uint16\",\n" + " \"array\": false,\n" + " \"record-types\": \"\",\n" + " \"encapsulate\": \"dns\",\n" + " \"space\": \"isc\"\n" + "}]\n"; + isc::test::runToElementTest(expected, cfg); +} + } diff --git a/src/lib/dhcpsrv/tests/cfg_option_unittest.cc b/src/lib/dhcpsrv/tests/cfg_option_unittest.cc index 64070b1aa7..d538e6893c 100644 --- a/src/lib/dhcpsrv/tests/cfg_option_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_option_unittest.cc @@ -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 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -598,5 +599,46 @@ TEST_F(CfgOptionTest, getVendorIdsSpaceNames) { } } +// This test verifies that the unparse function returns what is expected. +TEST_F(CfgOptionTest, unparse) { + CfgOption cfg; + + // Add some options. + OptionPtr opt1(new Option(Option::V6, 100, OptionBuffer(4, 0x12))); + cfg.add(opt1, false, "dns"); + OptionPtr opt2(new Option(Option::V6, 101, OptionBuffer(4, 12))); + OptionDescriptor desc2(opt2, false, "12, 12, 12, 12"); + cfg.add(desc2, "dns"); + OptionPtr opt3(new Option(Option::V6, D6O_STATUS_CODE, OptionBuffer(2, 0))); + cfg.add(opt3, false, DHCP6_OPTION_SPACE); + OptionPtr opt4(new Option(Option::V6, 100, OptionBuffer(4, 0x21))); + cfg.add(opt4, false, "vendor-1234"); + + // Unparse + std::string expected = "[\n" + "{\n" + " \"code\": 100,\n" + " \"space\": \"dns\",\n" + " \"csv-format\": false,\n" + " \"data\": \"12121212\"\n" + "},{\n" + " \"code\": 101,\n" + " \"space\": \"dns\",\n" + " \"csv-format\": true,\n" + " \"data\": \"12, 12, 12, 12\"\n" + "},{\n" + " \"code\": 13,\n" + " \"name\": \"status-code\",\n" + " \"space\": \"dhcp6\",\n" + " \"csv-format\": false,\n" + " \"data\": \"0000\"\n" + "},{\n" + " \"code\": 100,\n" + " \"space\": \"vendor-1234\",\n" + " \"csv-format\": false,\n" + " \"data\": \"21212121\"\n" + "}]\n"; + isc::test::runToElementTest(expected, cfg); +} } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/cfg_rsoo_unittest.cc b/src/lib/dhcpsrv/tests/cfg_rsoo_unittest.cc index 3f0c59f3b1..de2d5bb754 100644 --- a/src/lib/dhcpsrv/tests/cfg_rsoo_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_rsoo_unittest.cc @@ -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 @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -88,4 +89,12 @@ TEST(CfgRSOOTest, enableTwice) { ASSERT_FALSE(rsoo.enabled(88)); } +// This test verifies that the unparse function returns what is expected. +TEST(CfgRSOOTest, unparse) { + CfgRSOO rsoo; + // option codes are put in strings + isc::test::runToElementTest("[ \"65\" ]", rsoo); + // isc::test::runToElementTest("[ 65 ]", rsoo); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index f913ab142b..ab39f44264 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -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 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,7 @@ using namespace isc; using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::dhcp::test; +using namespace isc::test; namespace { @@ -438,5 +440,119 @@ TEST(CfgSubnets4Test, 4o6subnetMatchByInterfaceName) { EXPECT_EQ(subnet2, cfg.selectSubnet4o6(selector)); } +// This test check if IPv4 subnets can be unparsed in a predictable way, +TEST(CfgSubnets4Test, unparseSubnet) { + CfgSubnets4 cfg; + + // Add some subnets. + Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123)); + Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 124)); + Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 125)); + subnet1->allowClientClass("foo"); + subnet2->setIface("lo"); + subnet2->setRelayInfo(IOAddress("10.0.0.1")); + subnet3->setIface("eth1"); + + cfg.add(subnet1); + cfg.add(subnet2); + cfg.add(subnet3); + + // Unparse + std::string expected = "[\n" + "{\n" + " \"id\": 123,\n" + " \"subnet\": \"192.0.2.0/26\",\n" + " \"relay\": { \"ip-address\": \"0.0.0.0\" },\n" + " \"interface\": \"\",\n" + " \"match-client-id\": true,\n" + " \"next-server\": \"0.0.0.0\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"valid-lifetime\": 3,\n" + " \"client-class\": \"foo\",\n" + " \"4o6-interface\": \"\",\n" + " \"4o6-interface-id\": \"\",\n" + " \"4o6-subnet\": \"\",\n" + " \"reservation-mode\": \"all\",\n" + " \"option-data\": [ ],\n" + " \"pools\": [ ]\n" + "},{\n" + " \"id\": 124,\n" + " \"subnet\": \"192.0.2.64/26\",\n" + " \"relay\": { \"ip-address\": \"10.0.0.1\" },\n" + " \"interface\": \"lo\",\n" + " \"match-client-id\": true,\n" + " \"next-server\": \"0.0.0.0\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"valid-lifetime\": 3,\n" + " \"4o6-interface\": \"\",\n" + " \"4o6-interface-id\": \"\",\n" + " \"4o6-subnet\": \"\",\n" + " \"reservation-mode\": \"all\",\n" + " \"option-data\": [ ],\n" + " \"pools\": [ ]\n" + "},{\n" + " \"id\": 125,\n" + " \"subnet\": \"192.0.2.128/26\",\n" + " \"relay\": { \"ip-address\": \"0.0.0.0\" },\n" + " \"interface\": \"eth1\",\n" + " \"match-client-id\": true,\n" + " \"next-server\": \"0.0.0.0\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"valid-lifetime\": 3,\n" + " \"4o6-interface\": \"\",\n" + " \"4o6-interface-id\": \"\",\n" + " \"4o6-subnet\": \"\",\n" + " \"reservation-mode\": \"all\",\n" + " \"option-data\": [ ],\n" + " \"pools\": [ ]\n" + "} ]\n"; + runToElementTest(expected, cfg); +} + +// This test check if IPv4 pools can be unparsed in a predictable way, +TEST(CfgSubnets4Test, unparsePool) { + CfgSubnets4 cfg; + + // Add a subnet with pools + Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 123)); + Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.1"), IOAddress("192.0.2.10"))); + Pool4Ptr pool2(new Pool4(IOAddress("192.0.2.64"), 26)); + + subnet->addPool(pool1); + subnet->addPool(pool2); + cfg.add(subnet); + + // Unparse + std::string expected = "[\n" + "{\n" + " \"id\": 123,\n" + " \"subnet\": \"192.0.2.0/24\",\n" + " \"relay\": { \"ip-address\": \"0.0.0.0\" },\n" + " \"interface\": \"\",\n" + " \"match-client-id\": true,\n" + " \"next-server\": \"0.0.0.0\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"valid-lifetime\": 3,\n" + " \"4o6-interface\": \"\",\n" + " \"4o6-interface-id\": \"\",\n" + " \"4o6-subnet\": \"\",\n" + " \"reservation-mode\": \"all\",\n" + " \"option-data\": [],\n" + " \"pools\": [\n" + " {\n" + " \"pool\": \"192.0.2.1-192.0.2.10\",\n" + " \"option-data\": []\n" + " },{\n" + " \"pool\": \"192.0.2.64/26\",\n" + " \"option-data\": []\n" + " }\n" + " ]\n" + "} ]\n"; + runToElementTest(expected, cfg); +} } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index 0411d71d02..5df42700a6 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -12,12 +12,14 @@ #include #include #include +#include #include #include using namespace isc; using namespace isc::asiolink; using namespace isc::dhcp; +using namespace isc::test; namespace { @@ -342,4 +344,178 @@ TEST(CfgSubnets6Test, duplication) { EXPECT_THROW(cfg.add(subnet3), isc::dhcp::DuplicateSubnetID); } +// This test check if IPv6 subnets can be unparsed in a predictable way, +TEST(CfgSubnets6Test, unparseSubnet) { + CfgSubnets6 cfg; + + // Add some subnets. + Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), + 48, 1, 2, 3, 4, 123)); + Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), + 48, 1, 2, 3, 4, 124)); + Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), + 48, 1, 2, 3, 4, 125)); + + OptionPtr ifaceid = generateInterfaceId("relay.eth0"); + subnet1->setInterfaceId(ifaceid); + subnet1->allowClientClass("foo"); + subnet2->setIface("lo"); + subnet2->setRelayInfo(IOAddress("2001:db8:ff::2")); + subnet3->setIface("eth1"); + + cfg.add(subnet1); + cfg.add(subnet2); + cfg.add(subnet3); + + // Unparse + std::string expected = "[\n" + "{\n" + " \"id\": 123,\n" + " \"subnet\": \"2001:db8:1::/48\",\n" + " \"relay\": { \"ip-address\": \"::\" },\n" + " \"interface-id\": \"relay.eth0\",\n" + " \"interface\": \"\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"preferred-lifetime\": 3,\n" + " \"valid-lifetime\": 4,\n" + " \"rapid-commit\": false,\n" + " \"reservation-mode\": \"all\",\n" + " \"client-class\": \"foo\",\n" + " \"pools\": [ ],\n" + " \"pd-pools\": [ ],\n" + " \"option-data\": [ ]\n" + "},{\n" + " \"id\": 124,\n" + " \"subnet\": \"2001:db8:2::/48\",\n" + " \"relay\": { \"ip-address\": \"2001:db8:ff::2\" },\n" + " \"interface-id\": \"\",\n" + " \"interface\": \"lo\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"preferred-lifetime\": 3,\n" + " \"valid-lifetime\": 4,\n" + " \"rapid-commit\": false,\n" + " \"reservation-mode\": \"all\",\n" + " \"pools\": [ ],\n" + " \"pd-pools\": [ ],\n" + " \"option-data\": [ ]\n" + "},{\n" + " \"id\": 125,\n" + " \"subnet\": \"2001:db8:3::/48\",\n" + " \"relay\": { \"ip-address\": \"::\" },\n" + " \"interface-id\": \"\",\n" + " \"interface\": \"eth1\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"preferred-lifetime\": 3,\n" + " \"valid-lifetime\": 4,\n" + " \"rapid-commit\": false,\n" + " \"reservation-mode\": \"all\",\n" + " \"pools\": [ ],\n" + " \"pd-pools\": [ ],\n" + " \"option-data\": [ ]\n" + "} ]\n"; + runToElementTest(expected, cfg); +} + +// This test check if IPv6 pools can be unparsed in a predictable way, +TEST(CfgSubnets6Test, unparsePool) { + CfgSubnets6 cfg; + + // Add a subnet with pools + Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), + 48, 1, 2, 3, 4, 123)); + Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, + IOAddress("2001:db8:1::100"), + IOAddress("2001:db8:1::199"))); + Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64)); + + subnet->addPool(pool1); + subnet->addPool(pool2); + cfg.add(subnet); + + // Unparse + std::string expected = "[\n" + "{\n" + " \"id\": 123,\n" + " \"subnet\": \"2001:db8:1::/48\",\n" + " \"relay\": { \"ip-address\": \"::\" },\n" + " \"interface-id\": \"\",\n" + " \"interface\": \"\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"preferred-lifetime\": 3,\n" + " \"valid-lifetime\": 4,\n" + " \"rapid-commit\": false,\n" + " \"reservation-mode\": \"all\",\n" + " \"pools\": [\n" + " {\n" + " \"pool\": \"2001:db8:1::100-2001:db8:1::199\",\n" + " \"option-data\": [ ]\n" + " },{\n" + " \"pool\": \"2001:db8:1:1::/64\",\n" + " \"option-data\": [ ]\n" + " }\n" + " ],\n" + " \"pd-pools\": [ ],\n" + " \"option-data\": [ ]\n" + "} ]\n"; + runToElementTest(expected, cfg); +} + +// This test check if IPv6 prefix delegation pools can be unparsed +// in a predictable way, +TEST(CfgSubnets6Test, unparsePdPool) { + CfgSubnets6 cfg; + + // Add a subnet with pd-pools + Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), + 48, 1, 2, 3, 4, 123)); + Pool6Ptr pdpool1(new Pool6(Lease::TYPE_PD, + IOAddress("2001:db8:2::"), 48, 64)); + Pool6Ptr pdpool2(new Pool6(IOAddress("2001:db8:3::"), 48, 56, + IOAddress("2001:db8:3::"), 64)); + + subnet->addPool(pdpool1); + subnet->addPool(pdpool2); + cfg.add(subnet); + + // Unparse + std::string expected = "[\n" + "{\n" + " \"id\": 123,\n" + " \"subnet\": \"2001:db8:1::/48\",\n" + " \"relay\": { \"ip-address\": \"::\" },\n" + " \"interface-id\": \"\",\n" + " \"interface\": \"\",\n" + " \"renew-timer\": 1,\n" + " \"rebind-timer\": 2,\n" + " \"preferred-lifetime\": 3,\n" + " \"valid-lifetime\": 4,\n" + " \"rapid-commit\": false,\n" + " \"reservation-mode\": \"all\",\n" + " \"pools\": [ ],\n" + " \"pd-pools\": [\n" + " {\n" + " \"prefix\": \"2001:db8:2::\",\n" + " \"prefix-len\": 48,\n" + " \"delegated-len\": 64,\n" + " \"excluded-prefix\": \"::\",\n" + " \"excluded-prefix-len\": 0,\n" + " \"option-data\": [ ]\n" + " },{\n" + " \"prefix\": \"2001:db8:3::\",\n" + " \"prefix-len\": 48,\n" + " \"delegated-len\": 56,\n" + " \"excluded-prefix\": \"2001:db8:3::\",\n" + " \"excluded-prefix-len\": 64,\n" + " \"option-data\": [ ]\n" + " }\n" + " ],\n" + " \"option-data\": [ ]\n" + "} ]\n"; + runToElementTest(expected, cfg); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc index 34e5d266a8..8c1bfbd560 100644 --- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc @@ -505,6 +505,18 @@ TEST_F(CfgMgrTest, verbosity) { EXPECT_FALSE(CfgMgr::instance().isVerbose()); } +// This test verifies that the address family can be set and obtained +// from the configuration manager. +TEST_F(CfgMgrTest, family) { + ASSERT_EQ(AF_INET, CfgMgr::instance().getFamily()); + + CfgMgr::instance().setFamily(AF_INET6); + ASSERT_EQ(AF_INET6, CfgMgr::instance().getFamily()); + + CfgMgr::instance().setFamily(AF_INET); + EXPECT_EQ(AF_INET, CfgMgr::instance().getFamily()); +} + // This test verifies that once the configuration is committed, statistics // are updated appropriately. TEST_F(CfgMgrTest, commitStats4) { diff --git a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc index 17eeb9a97f..aff01882c8 100644 --- a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc @@ -295,10 +295,11 @@ TEST_F(ClientClassDefParserTest, nameOnlyValid) { // @todo same with AF_INET6 TEST_F(ClientClassDefParserTest, nameAndExpressionClass) { + std::string test = "option[100].text == 'works right'"; std::string cfg_text = "{ \n" " \"name\": \"class_one\", \n" - " \"test\": \"option[100].text == 'works right'\" \n" + " \"test\": \"" + test + "\" \n" "} \n"; ClientClassDefPtr cclass; @@ -322,6 +323,9 @@ TEST_F(ClientClassDefParserTest, nameAndExpressionClass) { ExpressionPtr match_expr = cclass->getMatchExpr(); ASSERT_TRUE(match_expr); + // Verify the original expression was saved. + EXPECT_EQ(test, cclass->getTest()); + // Build a packet that will fail evaluation. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 123)); EXPECT_FALSE(evaluate(*match_expr, *pkt4)); @@ -373,10 +377,11 @@ TEST_F(ClientClassDefParserTest, nameAndOptionsClass) { // @todo same with AF_INET6 TEST_F(ClientClassDefParserTest, basicValidClass) { + std::string test = "option[100].text == 'booya'"; std::string cfg_text = "{ \n" " \"name\": \"MICROSOFT\", \n" - " \"test\": \"option[100].text == 'booya'\", \n" + " \"test\": \"" + test + "\", \n" " \"option-data\": [ \n" " { \n" " \"name\": \"domain-name-servers\", \n" @@ -404,6 +409,9 @@ TEST_F(ClientClassDefParserTest, basicValidClass) { ExpressionPtr match_expr = cclass->getMatchExpr(); ASSERT_TRUE(match_expr); + // Verify the original expression was saved. + EXPECT_EQ(test, cclass->getTest()); + // Build a packet that will fail evaluation. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 123)); EXPECT_FALSE(evaluate(*match_expr, *pkt4)); @@ -459,20 +467,6 @@ TEST_F(ClientClassDefParserTest, blankClassName) { DhcpConfigError); } - -// Verifies that a class with an unknown element, fails to parse. -TEST_F(ClientClassDefParserTest, unknownElement) { - std::string cfg_text = - "{ \n" - " \"name\": \"one\", \n" - " \"bogus\": \"bad\" \n" - "} \n"; - - ClientClassDefPtr cclass; - ASSERT_THROW(cclass = parseClientClassDef(cfg_text, AF_INET), - DhcpConfigError); -} - // Verifies that a class with an invalid expression, fails to parse. TEST_F(ClientClassDefParserTest, invalidExpression) { std::string cfg_text = @@ -565,22 +559,6 @@ TEST_F(ClientClassDefListParserTest, duplicateClass) { DhcpConfigError); } -// Verifies that a class list containing an invalid class entry, fails to -// parse. -TEST_F(ClientClassDefListParserTest, invalidClass) { - std::string cfg_text = - "[ \n" - " { \n" - " \"name\": \"one\", \n" - " \"bogus\": \"bad\" \n" - " } \n" - "] \n"; - - ClientClassDictionaryPtr dictionary; - ASSERT_THROW(dictionary = parseClientClassDefList(cfg_text, AF_INET6), - DhcpConfigError); -} - // Test verifies that without any class specified, the fixed fields have their // default, empty value. // @todo same with AF_INET6 diff --git a/src/lib/dhcpsrv/tests/client_class_def_unittest.cc b/src/lib/dhcpsrv/tests/client_class_def_unittest.cc index 72c0ca9abe..e3a8a55c24 100644 --- a/src/lib/dhcpsrv/tests/client_class_def_unittest.cc +++ b/src/lib/dhcpsrv/tests/client_class_def_unittest.cc @@ -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 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ using namespace std; using namespace isc::dhcp; using namespace isc::util; using namespace isc::asiolink; +using namespace isc::test; using namespace isc; namespace { @@ -44,7 +46,7 @@ TEST(ClientClassDef, construction) { // Verify we get an empty collection of cfg_option cfg_option = cclass->getCfgOption(); ASSERT_TRUE(cfg_option); - //EXPECT_EQ(0, cfg_option->size()); + EXPECT_TRUE(cfg_option->empty()); } // Tests options operations. Note we just do the basics @@ -205,15 +207,15 @@ TEST(ClientClassDictionary, basics) { // Verify that we can add classes with both addClass variants // First addClass(name, expression, cfg_option) - ASSERT_NO_THROW(dictionary->addClass("cc1", expr, cfg_option)); - ASSERT_NO_THROW(dictionary->addClass("cc2", expr, cfg_option)); + ASSERT_NO_THROW(dictionary->addClass("cc1", expr, "", cfg_option)); + ASSERT_NO_THROW(dictionary->addClass("cc2", expr, "", cfg_option)); // Verify duplicate add attempt throws - ASSERT_THROW(dictionary->addClass("cc2", expr, cfg_option), + ASSERT_THROW(dictionary->addClass("cc2", expr, "", cfg_option), DuplicateClientClassDef); // Verify that you cannot add a class with no name. - ASSERT_THROW(dictionary->addClass("", expr, cfg_option), BadValue); + ASSERT_THROW(dictionary->addClass("", expr, "", cfg_option), BadValue); // Now with addClass(class pointer) ASSERT_NO_THROW(cclass.reset(new ClientClassDef("cc3", expr, cfg_option))); @@ -269,9 +271,9 @@ TEST(ClientClassDictionary, copyAndEquality) { CfgOptionPtr options; dictionary.reset(new ClientClassDictionary()); - ASSERT_NO_THROW(dictionary->addClass("one", expr, options)); - ASSERT_NO_THROW(dictionary->addClass("two", expr, options)); - ASSERT_NO_THROW(dictionary->addClass("three", expr, options)); + ASSERT_NO_THROW(dictionary->addClass("one", expr, "", options)); + ASSERT_NO_THROW(dictionary->addClass("two", expr, "", options)); + ASSERT_NO_THROW(dictionary->addClass("three", expr, "", options)); // Copy constructor should succeed. ASSERT_NO_THROW(dictionary2.reset(new ClientClassDictionary(*dictionary))); @@ -353,4 +355,64 @@ TEST(ClientClassDef, fixedFieldsBasics) { } +// Verifies the unparse method of option class definitions +TEST(ClientClassDef, unparseDef) { + boost::scoped_ptr cclass; + + // Get a client class definition and fill it + std::string name = "class1"; + ExpressionPtr expr; + ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr))); + std::string test = "option[12].text == 'foo'"; + cclass->setTest(test); + std::string next_server = "1.2.3.4"; + cclass->setNextServer(IOAddress(next_server)); + std::string sname = "my-server.example.com"; + cclass->setSname(sname); + std::string filename = "/boot/kernel"; + cclass->setFilename(filename); + + // Unparse it + std::string expected = "{\n" + "\"name\": \"" + name + "\",\n" + "\"test\": \"" + test + "\",\n" + "\"next-server\": \"" + next_server + "\",\n" + "\"server-hostname\": \"" + sname + "\",\n" + "\"boot-file-name\": \"" + filename + "\",\n" + "\"option-data\": [ ] }\n"; + runToElementTest(expected, *cclass); +} + +// Verifies the unparse method of client class dictionaries +TEST(ClientClassDictionary, unparseDict) { + ClientClassDictionaryPtr dictionary; + ExpressionPtr expr; + CfgOptionPtr options; + + // Get a client class dictionary and fill it + dictionary.reset(new ClientClassDictionary()); + ASSERT_NO_THROW(dictionary->addClass("one", expr, "", options)); + ASSERT_NO_THROW(dictionary->addClass("two", expr, "", options)); + ASSERT_NO_THROW(dictionary->addClass("three", expr, "", options)); + + // Unparse it + auto add_defaults = + [](std::string name) { + return ("{\n" + "\"name\": \"" + name + "\",\n" + "\"test\": \"\",\n" + "\"next-server\": \"0.0.0.0\",\n" + "\"server-hostname\": \"\",\n" + "\"boot-file-name\": \"\",\n" + "\"option-data\": [ ] }"); + }; + + std::string expected = "[\n" + + add_defaults("one") + ",\n" + + add_defaults("two") + ",\n" + + add_defaults("three") + "]\n"; + + runToElementTest(expected, *dictionary); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc index 5bed6600e7..a85cf0a8e5 100644 --- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc +++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc @@ -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 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -16,6 +17,7 @@ using namespace std; using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::util; +using namespace isc::test; using namespace isc; namespace { @@ -121,6 +123,25 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) { ASSERT_NO_THROW(std::cout << "toText test:" << std::endl << *d2_client_config << std::endl); + // Verify what toElement returns. + std::string expected = "{\n" + "\"enable-updates\": true,\n" + "\"server-ip\": \"127.0.0.1\",\n" + "\"server-port\": 477,\n" + "\"sender-ip\": \"127.0.0.1\",\n" + "\"sender-port\": 478,\n" + "\"max-queue-size\": 2048,\n" + "\"ncr-protocol\": \"UDP\",\n" + "\"ncr-format\": \"JSON\",\n" + "\"always-include-fqdn\": true,\n" + "\"override-no-update\": true,\n" + "\"override-client-update\": true,\n" + "\"replace-client-name\": \"when-present\",\n" + "\"generated-prefix\": \"the_prefix\",\n" + "\"qualifying-suffix\": \"the.suffix.\"\n" + "}\n"; + runToElementTest(expected, *d2_client_config); + // Verify that constructor does not allow use of NCR_TCP. /// @todo obviously this becomes invalid once TCP is supported. ASSERT_THROW(d2_client_config.reset(new diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc index d43fc2c5ed..8392484e69 100644 --- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ using namespace isc::data; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::hooks; +using namespace isc::test; namespace { @@ -467,6 +469,21 @@ public: return (cnt); } + /// This table defines default values for option definitions in DHCPv6 + static const SimpleDefaults OPTION6_DEF_DEFAULTS; + + /// This table defines default values for option definitions in DHCPv4 + static const SimpleDefaults OPTION4_DEF_DEFAULTS; + + /// This table defines default values for options in DHCPv6 + static const SimpleDefaults OPTION6_DEFAULTS; + + /// This table defines default values for options in DHCPv4 + static const SimpleDefaults OPTION4_DEFAULTS; + + /// This table defines default values for both DHCPv4 and DHCPv6 + static const SimpleDefaults GLOBAL6_DEFAULTS; + /// @brief sets all default values for DHCPv4 and DHCPv6 /// /// This function largely duplicates what SimpleParser4 and SimpleParser6 classes @@ -482,44 +499,6 @@ public: /// @param config configuration structure to be filled with default values /// @param v6 true = DHCPv6, false = DHCPv4 void setAllDefaults(ElementPtr config, bool v6) { - /// This table defines default values for option definitions in DHCPv6 - const SimpleDefaults OPTION6_DEF_DEFAULTS = { - { "record-types", Element::string, ""}, - { "space", Element::string, "dhcp6"}, - { "array", Element::boolean, "false"}, - { "encapsulate", Element::string, "" } - }; - - /// This table defines default values for option definitions in DHCPv4 - const SimpleDefaults OPTION4_DEF_DEFAULTS = { - { "record-types", Element::string, ""}, - { "space", Element::string, "dhcp4"}, - { "array", Element::boolean, "false"}, - { "encapsulate", Element::string, "" } - }; - - /// This table defines default values for options in DHCPv6 - const SimpleDefaults OPTION6_DEFAULTS = { - { "space", Element::string, "dhcp6"}, - { "csv-format", Element::boolean, "true"}, - { "encapsulate", Element::string, "" } - }; - - /// This table defines default values for options in DHCPv4 - const SimpleDefaults OPTION4_DEFAULTS = { - { "space", Element::string, "dhcp4"}, - { "csv-format", Element::boolean, "true"}, - { "encapsulate", Element::string, "" } - }; - - /// This table defines default values for both DHCPv4 and DHCPv6 - const SimpleDefaults GLOBAL6_DEFAULTS = { - { "renew-timer", Element::integer, "900" }, - { "rebind-timer", Element::integer, "1800" }, - { "preferred-lifetime", Element::integer, "3600" }, - { "valid-lifetime", Element::integer, "7200" } - }; - if (v6) { setAllDefaults(config, GLOBAL6_DEFAULTS, OPTION6_DEFAULTS, OPTION6_DEF_DEFAULTS); @@ -623,6 +602,132 @@ public: std::string error_text_; }; +/// This table defines default values for option definitions in DHCPv6 +const SimpleDefaults ParseConfigTest::OPTION6_DEF_DEFAULTS = { + { "record-types", Element::string, ""}, + { "space", Element::string, "dhcp6"}, + { "array", Element::boolean, "false"}, + { "encapsulate", Element::string, "" } +}; + +/// This table defines default values for option definitions in DHCPv4 +const SimpleDefaults ParseConfigTest::OPTION4_DEF_DEFAULTS = { + { "record-types", Element::string, ""}, + { "space", Element::string, "dhcp4"}, + { "array", Element::boolean, "false"}, + { "encapsulate", Element::string, "" } +}; + +/// This table defines default values for options in DHCPv6 +const SimpleDefaults ParseConfigTest::OPTION6_DEFAULTS = { + { "space", Element::string, "dhcp6"}, + { "csv-format", Element::boolean, "true"} +}; + +/// This table defines default values for options in DHCPv4 +const SimpleDefaults ParseConfigTest::OPTION4_DEFAULTS = { + { "space", Element::string, "dhcp4"}, + { "csv-format", Element::boolean, "true"} +}; + +/// This table defines default values for both DHCPv4 and DHCPv6 +const SimpleDefaults ParseConfigTest::GLOBAL6_DEFAULTS = { + { "renew-timer", Element::integer, "900" }, + { "rebind-timer", Element::integer, "1800" }, + { "preferred-lifetime", Element::integer, "3600" }, + { "valid-lifetime", Element::integer, "7200" } +}; + +/// @brief Option configuration class +/// +/// This class handles option-def and option-data which can be recovered +/// using the toElement() method +class CfgOptionsTest : public CfgToElement { +public: + /// @brief Constructor + /// + /// @param cfg the server configuration where to get option-{def,data} + CfgOptionsTest(SrvConfigPtr cfg) : + cfg_option_def_(cfg->getCfgOptionDef()), + cfg_option_(cfg->getCfgOption()) { } + + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration (a map with + /// not empty option-def and option-data lists) + ElementPtr toElement() const { + ElementPtr result = Element::createMap(); + // Set option-def + ConstElementPtr option_def = cfg_option_def_->toElement(); + if (!option_def->empty()) { + result->set("option-def", option_def); + } + // Set option-data + ConstElementPtr option_data = cfg_option_->toElement(); + if (!option_data->empty()) { + result->set("option-data", option_data); + } + return (result); + } + + /// @brief Run a toElement test (Element version) + /// + /// Use the runToElementTest template but add defaults to the config + /// + /// @param family the address family + /// @param config the expected result without defaults + void runCfgOptionsTest(uint16_t family, ConstElementPtr expected) { + ConstElementPtr option_def = expected->get("option-def"); + if (option_def) { + SimpleParser::setListDefaults(option_def, + family == AF_INET ? + ParseConfigTest::OPTION4_DEF_DEFAULTS : + ParseConfigTest::OPTION6_DEF_DEFAULTS); + } + ConstElementPtr option_data = expected->get("option-data"); + if (option_data) { + SimpleParser::setListDefaults(option_data, + family == AF_INET ? + ParseConfigTest::OPTION4_DEFAULTS : + ParseConfigTest::OPTION6_DEFAULTS); + } + runToElementTest(expected, *this); + } + + /// @brief Run a toElement test + /// + /// Use the runToElementTest template but add defaults to the config + /// + /// @param family the address family + /// @param expected the expected result without defaults + void runCfgOptionsTest(uint16_t family, std::string config) { + ConstElementPtr json; + ASSERT_NO_THROW(json = Element::fromJSON(config)) << config; + ConstElementPtr option_def = json->get("option-def"); + if (option_def) { + SimpleParser::setListDefaults(option_def, + family == AF_INET ? + ParseConfigTest::OPTION4_DEF_DEFAULTS : + ParseConfigTest::OPTION6_DEF_DEFAULTS); + } + ConstElementPtr option_data = json->get("option-data"); + if (option_data) { + SimpleParser::setListDefaults(option_data, + family == AF_INET ? + ParseConfigTest::OPTION4_DEFAULTS : + ParseConfigTest::OPTION6_DEFAULTS); + } + runToElementTest(json, *this); + } + +private: + /// @brief Pointer to option definitions configuration. + CfgOptionDefPtr cfg_option_def_; + + /// @brief Reference to options (data) configuration. + CfgOptionPtr cfg_option_; +}; + /// @brief Check basic parsing of option definitions. /// /// Note that this tests basic operation of the OptionDefinitionListParser and @@ -669,6 +774,10 @@ TEST_F(ParseConfigTest, basicOptionDefTest) { // but the values should be equal. EXPECT_TRUE(def_libdhcp != def); EXPECT_TRUE(*def_libdhcp == *def); + + // Check if it can be unparsed. + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, config); } /// @brief Check minimal parsing of option definitions. @@ -702,6 +811,10 @@ TEST_F(ParseConfigTest, minimalOptionDefTest) { EXPECT_FALSE(def->getArrayType()); EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType()); EXPECT_TRUE(def->getEncapsulatedSpace().empty()); + + // Check if it can be unparsed. + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, config); } /// @brief Check parsing of option definitions using default dhcp6 space. @@ -735,6 +848,10 @@ TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) { EXPECT_FALSE(def->getArrayType()); EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType()); EXPECT_TRUE(def->getEncapsulatedSpace().empty()); + + // Check if it can be unparsed. + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, config); } /// @brief Check basic parsing of options. @@ -774,6 +891,10 @@ TEST_F(ParseConfigTest, basicOptionDataTest) { std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)"; EXPECT_EQ(val, opt_ptr->toText()); + + // Check if it can be unparsed. + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, config); } /// @brief Check minimal parsing of options. @@ -808,6 +929,12 @@ TEST_F(ParseConfigTest, minimalOptionDataTest) { std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)"; EXPECT_EQ(val, opt_ptr->toText()); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(100)); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } /// @brief Check parsing of options with escape characters. @@ -851,6 +978,12 @@ TEST_F(ParseConfigTest, escapedOptionDataTest) { EXPECT_EQ(Option::OPTION4_HDR_LEN + 23, buf.getLength()); EXPECT_TRUE(0 == memcmp(buf.getData(), exp, 25)); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(DHO_BOOT_FILE_NAME)); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } // This test checks behavior of the configuration parser for option data @@ -879,6 +1012,9 @@ TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) { ASSERT_TRUE(addr_opt); EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText()); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, config); + // Explicitly enable csv-format. CfgMgr::instance().clear(); config = @@ -899,6 +1035,8 @@ TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) { ASSERT_TRUE(addr_opt); EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText()); + // To make runToElementTest to work the csv-format must be removed... + // Explicitly disable csv-format and use hex instead. CfgMgr::instance().clear(); config = @@ -918,6 +1056,9 @@ TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) { OptionCustom>(getOptionPtr(DHCP4_OPTION_SPACE, 16)); ASSERT_TRUE(addr_opt); EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText()); + + CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg()); + cfg2.runCfgOptionsTest(family_, config); } // This test verifies that definitions of standard encapsulated @@ -962,6 +1103,12 @@ TEST_F(ParseConfigTest, encapsulatedOptionData) { EXPECT_EQ("192.0.2.0", ipv4_prefix.toText()); EXPECT_EQ(64, ipv6_prefix.first.asUnsigned()); EXPECT_EQ("2001:db8:1::", ipv6_prefix.second.toText()); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(D6O_S46_RULE)); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } // This test checks behavior of the configuration parser for option data @@ -1019,6 +1166,13 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) { ASSERT_EQ(1, opt->getData().size()); EXPECT_EQ(0, opt->getData()[0]); + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->remove("name"); + opt_data->set("data", Element::create(std::string("00"))); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); + CfgMgr::instance().clear(); // When csv-format is not specified, the parser will check if the definition // exists or not. Since there is no definition, the parser will accept the @@ -1040,6 +1194,12 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) { EXPECT_EQ(0x12, opt->getData()[0]); EXPECT_EQ(0x34, opt->getData()[1]); EXPECT_EQ(0x56, opt->getData()[2]); + + expected = Element::fromJSON(config); + opt_data = expected->get("option-data")->getNonConst(0); + opt_data->remove("name"); + CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg()); + cfg2.runCfgOptionsTest(family_, expected); } // This test verifies that the option name is not mandatory, if the option @@ -1060,6 +1220,12 @@ TEST_F(ParseConfigTest, optionDataNoName) { ASSERT_TRUE(opt); ASSERT_EQ(1, opt->getAddresses().size()); EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText()); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("name", Element::create(std::string("dns-servers"))); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } // This test verifies that the option code is not mandatory, if the option @@ -1080,6 +1246,12 @@ TEST_F(ParseConfigTest, optionDataNoCode) { ASSERT_TRUE(opt); ASSERT_EQ(1, opt->getAddresses().size()); EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText()); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(D6O_NAME_SERVERS)); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } // This test verifies that the option data configuration with a minimal @@ -1100,6 +1272,13 @@ TEST_F(ParseConfigTest, optionDataMinimal) { ASSERT_EQ(1, opt->getAddresses().size()); EXPECT_EQ( "2001:db8:1::10", opt->getAddresses()[0].toText()); + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(D6O_NAME_SERVERS)); + opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE))); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); + CfgMgr::instance().clear(); // This time using an option code. config = @@ -1116,6 +1295,13 @@ TEST_F(ParseConfigTest, optionDataMinimal) { ASSERT_TRUE(opt); ASSERT_EQ(1, opt->getAddresses().size()); EXPECT_EQ( "2001:db8:1::20", opt->getAddresses()[0].toText()); + + expected = Element::fromJSON(config); + opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("name", Element::create(std::string("dns-servers"))); + opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE))); + CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg()); + cfg2.runCfgOptionsTest(family_, expected); } // This test verifies that the option data configuration with a minimal @@ -1147,6 +1333,13 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) { EXPECT_EQ("2001:db8:1::10", opt->getAddresses()[0].toText()); EXPECT_EQ("2001:db8:1::123", opt->getAddresses()[1].toText()); + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(2345)); + opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE))); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); + CfgMgr::instance().clear(); // Do the same test but now use an option code. config = @@ -1173,6 +1366,12 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) { EXPECT_EQ("2001:db8:1::10", opt->getAddresses()[0].toText()); EXPECT_EQ("2001:db8:1::123", opt->getAddresses()[1].toText()); + expected = Element::fromJSON(config); + opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("name", Element::create(std::string("foo-name"))); + opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE))); + CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg()); + cfg2.runCfgOptionsTest(family_, expected); } // This test verifies an empty option data configuration is supported. @@ -1191,6 +1390,15 @@ TEST_F(ParseConfigTest, emptyOptionData) { Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, D6O_DHCPV4_O_DHCPV6_SERVER)); ASSERT_TRUE(opt); ASSERT_EQ(0, opt->getAddresses().size()); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(D6O_DHCPV4_O_DHCPV6_SERVER)); + opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE))); + opt_data->set("csv-format", Element::create(false)); + opt_data->set("data", Element::create(std::string(""))); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } // This test verifies an option data without suboptions is supported @@ -1211,6 +1419,15 @@ TEST_F(ParseConfigTest, optionDataNoSubOpion) { const OptionPtr opt = getOptionPtr(DHCP4_OPTION_SPACE, DHO_VENDOR_ENCAPSULATED_OPTIONS); ASSERT_TRUE(opt); ASSERT_EQ(0, opt->getOptions().size()); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->set("code", Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS)); + opt_data->set("space", Element::create(std::string(DHCP4_OPTION_SPACE))); + opt_data->set("csv-format", Element::create(false)); + opt_data->set("data", Element::create(std::string(""))); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } // This tests option-data in CSV format and embedded commas. @@ -1242,6 +1459,13 @@ TEST_F(ParseConfigTest, commaCSVFormatOptionData) { // Verify that the option data is correct. string val = "EST5EDT4,M3.2.0/02:00,M11.1.0/02:00"; EXPECT_EQ(val, opt_str->getValue()); + + ElementPtr expected = Element::fromJSON(config); + ElementPtr opt_data = expected->get("option-data")->getNonConst(0); + opt_data->remove("csv-format"); + opt_data->set("name", Element::create(std::string("new-posix-timezone"))); + CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg()); + cfg.runCfgOptionsTest(family_, expected); } /// The next set of tests check basic operation of the HooksLibrariesParser. @@ -1306,6 +1530,15 @@ TEST_F(ParseConfigTest, noHooksLibraries) { const int rcode = parseConfiguration(config); ASSERT_TRUE(rcode == 0) << error_text_; + // Verify that the configuration object unparses. + ConstElementPtr expected; + ASSERT_NO_THROW(expected = + Element::fromJSON(config)->get("hooks-libraries")); + ASSERT_TRUE(expected); + const HooksConfig& cfg = + CfgMgr::instance().getStagingCfg()->getHooksConfig(); + runToElementTest(expected, cfg); + // Check that the parser recorded nothing. isc::hooks::HookLibsCollection libraries = getLibraries(); EXPECT_TRUE(libraries.empty()); @@ -1328,6 +1561,15 @@ TEST_F(ParseConfigTest, oneHooksLibrary) { const int rcode = parseConfiguration(config); ASSERT_TRUE(rcode == 0) << error_text_; + // Verify that the configuration object unparses. + ConstElementPtr expected; + ASSERT_NO_THROW(expected = + Element::fromJSON(config)->get("hooks-libraries")); + ASSERT_TRUE(expected); + const HooksConfig& cfg = + CfgMgr::instance().getStagingCfg()->getHooksConfig(); + runToElementTest(expected, cfg); + // Check that the parser recorded a single library. isc::hooks::HookLibsCollection libraries = getLibraries(); ASSERT_EQ(1, libraries.size()); @@ -1353,6 +1595,15 @@ TEST_F(ParseConfigTest, twoHooksLibraries) { const int rcode = parseConfiguration(config); ASSERT_TRUE(rcode == 0) << error_text_; + // Verify that the configuration object unparses. + ConstElementPtr expected; + ASSERT_NO_THROW(expected = + Element::fromJSON(config)->get("hooks-libraries")); + ASSERT_TRUE(expected); + const HooksConfig& cfg = + CfgMgr::instance().getStagingCfg()->getHooksConfig(); + runToElementTest(expected, cfg); + // Check that the parser recorded two libraries in the expected order. isc::hooks::HookLibsCollection libraries = getLibraries(); ASSERT_EQ(2, libraries.size()); @@ -1381,6 +1632,15 @@ TEST_F(ParseConfigTest, reconfigureSameHooksLibraries) { int rcode = parseConfiguration(config); ASSERT_TRUE(rcode == 0) << error_text_; + // Verify that the configuration object unparses. + ConstElementPtr expected; + ASSERT_NO_THROW(expected = + Element::fromJSON(config)->get("hooks-libraries")); + ASSERT_TRUE(expected); + const HooksConfig& cfg = + CfgMgr::instance().getStagingCfg()->getHooksConfig(); + runToElementTest(expected, cfg); + // The previous test shows that the parser correctly recorded the two // libraries and that they loaded correctly. @@ -1391,6 +1651,9 @@ TEST_F(ParseConfigTest, reconfigureSameHooksLibraries) { // The list has not changed between the two parse operations. However, // the paramters (or the files they could point to) could have // changed, so the libraries are reloaded anyway. + const HooksConfig& cfg2 = + CfgMgr::instance().getStagingCfg()->getHooksConfig(); + runToElementTest(expected, cfg2); isc::hooks::HookLibsCollection libraries = getLibraries(); ASSERT_EQ(2, libraries.size()); EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first); @@ -1464,6 +1727,15 @@ TEST_F(ParseConfigTest, reconfigureZeroHooksLibraries) { rcode = parseConfiguration(config); ASSERT_TRUE(rcode == 0) << error_text_; + // Verify that the configuration object unparses. + ConstElementPtr expected; + ASSERT_NO_THROW(expected = + Element::fromJSON(config)->get("hooks-libraries")); + ASSERT_TRUE(expected); + const HooksConfig& cfg = + CfgMgr::instance().getStagingCfg()->getHooksConfig(); + runToElementTest(expected, cfg); + // The list has changed, and this is what we should see. isc::hooks::HookLibsCollection libraries = getLibraries(); EXPECT_TRUE(libraries.empty()); @@ -1640,6 +1912,15 @@ TEST_F(ParseConfigTest, HooksLibrariesParameters) { const int rcode = parseConfiguration(config); ASSERT_EQ(0, rcode); + // Verify that the configuration object unparses. + ConstElementPtr expected; + ASSERT_NO_THROW(expected = + Element::fromJSON(config)->get("hooks-libraries")); + ASSERT_TRUE(expected); + const HooksConfig& cfg = + CfgMgr::instance().getStagingCfg()->getHooksConfig(); + runToElementTest(expected, cfg); + // Check that the parser recorded the names. isc::hooks::HookLibsCollection libraries = getLibraries(); ASSERT_EQ(3, libraries.size()); @@ -1732,6 +2013,12 @@ TEST_F(ParseConfigTest, validD2Config) { EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix()); EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix()); + // Verify that the configuration object unparses. + ConstElementPtr expected; + ASSERT_NO_THROW(expected = Element::fromJSON(config_str)->get("dhcp-ddns")); + ASSERT_TRUE(expected); + runToElementTest(expected, *d2_client_config); + // Another valid Configuration string. // This one is disabled, has IPV6 server ip, control flags false, // empty prefix/suffix @@ -1776,6 +2063,10 @@ TEST_F(ParseConfigTest, validD2Config) { EXPECT_EQ(D2ClientConfig::RCM_NEVER, d2_client_config->getReplaceClientNameMode()); EXPECT_EQ("", d2_client_config->getGeneratedPrefix()); EXPECT_EQ("", d2_client_config->getQualifyingSuffix()); + + ASSERT_NO_THROW(expected = Element::fromJSON(config_str2)->get("dhcp-ddns")); + ASSERT_TRUE(expected); + runToElementTest(expected, *d2_client_config); } /// @brief Checks that D2 client can be configured with enable flag of diff --git a/src/lib/dhcpsrv/tests/duid_config_parser_unittest.cc b/src/lib/dhcpsrv/tests/duid_config_parser_unittest.cc index b0692c232f..be7e96bf9b 100644 --- a/src/lib/dhcpsrv/tests/duid_config_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/duid_config_parser_unittest.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -182,13 +183,15 @@ TEST_F(DUIDConfigParserTest, noType) { // This test verifies that all parameters can be set. TEST_F(DUIDConfigParserTest, allParameters) { // Set all parameters. - ASSERT_NO_THROW(build("{ \"type\": \"EN\"," - " \"identifier\": \"ABCDEF\"," - " \"time\": 100," - " \"htype\": 8," - " \"enterprise-id\": 2024," - " \"persist\": false" - "}")); + std::string config = "{" + " \"type\": \"EN\"," + " \"identifier\": \"ABCDEF\"," + " \"time\": 100," + " \"htype\": 8," + " \"enterprise-id\": 2024," + " \"persist\": false" + "}"; + ASSERT_NO_THROW(build(config)); // Verify that parameters have been set correctly. ASSERT_TRUE(cfg_duid_); @@ -198,6 +201,9 @@ TEST_F(DUIDConfigParserTest, allParameters) { EXPECT_EQ(100, cfg_duid_->getTime()); EXPECT_EQ(2024, cfg_duid_->getEnterpriseId()); EXPECT_FALSE(cfg_duid_->persist()); + + // Check the config can be got back. + isc::test::runToElementTest(config, *cfg_duid_); } // Test out of range values for time. diff --git a/src/lib/dhcpsrv/tests/expiration_config_parser_unittest.cc b/src/lib/dhcpsrv/tests/expiration_config_parser_unittest.cc index 53c5953b42..fd76507232 100644 --- a/src/lib/dhcpsrv/tests/expiration_config_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/expiration_config_parser_unittest.cc @@ -221,15 +221,6 @@ TEST_F(ExpirationConfigParserTest, otherParameters) { EXPECT_EQ(20, cfg->getUnwarnedReclaimCycles()); } -// This test verifies that the exception is thrown if unsupported -// parameter is specified. -TEST_F(ExpirationConfigParserTest, invalidParameter) { - addParam("reclaim-timer-wait-time", 20); - addParam("invalid-parameter", 20); - - EXPECT_THROW(renderConfig(), DhcpConfigError); -} - // This test verifies that negative parameter values are not allowed. TEST_F(ExpirationConfigParserTest, outOfRangeValues) { testOutOfRange("reclaim-timer-wait-time", diff --git a/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc b/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc index 9c88b27f31..bfdebaef25 100644 --- a/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/ifaces_config_parser_unittest.cc @@ -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 @@ -10,11 +10,13 @@ #include #include #include +#include #include using namespace isc::data; using namespace isc::dhcp; using namespace isc::dhcp::test; +using namespace isc::test; namespace { @@ -60,6 +62,9 @@ TEST_F(IfacesConfigParserTest, interfaces) { CfgIfacePtr cfg_iface = CfgMgr::instance().getStagingCfg()->getCfgIface(); ASSERT_NO_THROW(parser.parse(cfg_iface, config_element)); + // Check it can be unparsed. + runToElementTest(config, *cfg_iface); + // Open sockets according to the parsed configuration. SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg(); ASSERT_TRUE(cfg); @@ -81,6 +86,8 @@ TEST_F(IfacesConfigParserTest, interfaces) { cfg_iface = CfgMgr::instance().getStagingCfg()->getCfgIface(); ASSERT_NO_THROW(parser.parse(cfg_iface, config_element)); + runToElementTest(config, *cfg_iface); + cfg = CfgMgr::instance().getStagingCfg(); ASSERT_NO_THROW(cfg->getCfgIface()->openSockets(AF_INET, 10000)); @@ -122,7 +129,7 @@ TEST_F(IfacesConfigParserTest, socketTypeDatagram) { CfgIface cfg_ref; // Configuration with a datagram socket selected. - std::string config = "{ ""\"interfaces\": [ ]," + std::string config = "{ \"interfaces\": [ ]," " \"dhcp-socket-type\": \"udp\" }"; ElementPtr config_element = Element::fromJSON(config); @@ -132,6 +139,9 @@ TEST_F(IfacesConfigParserTest, socketTypeDatagram) { CfgIfacePtr cfg_iface = CfgMgr::instance().getStagingCfg()->getCfgIface(); ASSERT_NO_THROW(parser.parse(cfg_iface, config_element)); + // Check it can be unparsed. + runToElementTest(config, *cfg_iface); + // Compare the resulting configuration with a reference // configuration using the raw socket. SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg(); diff --git a/src/lib/dhcpsrv/tests/logging_info_unittest.cc b/src/lib/dhcpsrv/tests/logging_info_unittest.cc index 6bfadbbfef..f1369bc280 100644 --- a/src/lib/dhcpsrv/tests/logging_info_unittest.cc +++ b/src/lib/dhcpsrv/tests/logging_info_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-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 @@ -7,9 +7,11 @@ #include #include #include +#include #include using namespace isc::dhcp; +using namespace isc::test; namespace { @@ -69,6 +71,17 @@ TEST_F(LoggingInfoTest, defaults) { ASSERT_EQ(1, info_non_verbose.destinations_.size()); EXPECT_EQ("stdout", info_non_verbose.destinations_[0].output_); + std::string header = "{\n" + "\"name\": \"kea\",\n" + "\"output_options\": [ {\n" + " \"output\": \"stdout\",\n \"maxsize\": 204800,\n" + " \"maxver\": 1,\n \"flush\": true } ],\n" + "\"severity\": \""; + std::string dbglvl = "\",\n\"debuglevel\": "; + std::string trailer = "\n}\n"; + std::string expected = header + "INFO" + dbglvl + "0" + trailer; + runToElementTest(expected, info_non_verbose); + CfgMgr::instance().setVerbose(true); LoggingInfo info_verbose; EXPECT_EQ("kea", info_verbose.name_); @@ -77,6 +90,9 @@ TEST_F(LoggingInfoTest, defaults) { ASSERT_EQ(1, info_verbose.destinations_.size()); EXPECT_EQ("stdout", info_verbose.destinations_[0].output_); + + expected = header + "DEBUG" + dbglvl + "99" + trailer; + runToElementTest(expected, info_verbose); } // Checks if (in)equality operators work for LoggingInfo. diff --git a/src/lib/dhcpsrv/tests/srv_config_unittest.cc b/src/lib/dhcpsrv/tests/srv_config_unittest.cc index 877610ee0c..c2ab73ecc9 100644 --- a/src/lib/dhcpsrv/tests/srv_config_unittest.cc +++ b/src/lib/dhcpsrv/tests/srv_config_unittest.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include using namespace isc::asiolink; @@ -64,9 +65,9 @@ public: } // Build our reference dictionary of client classes - ref_dictionary_->addClass("cc1", ExpressionPtr(), CfgOptionPtr()); - ref_dictionary_->addClass("cc2", ExpressionPtr(), CfgOptionPtr()); - ref_dictionary_->addClass("cc3", ExpressionPtr(), CfgOptionPtr()); + ref_dictionary_->addClass("cc1", ExpressionPtr(), "", CfgOptionPtr()); + ref_dictionary_->addClass("cc2", ExpressionPtr(), "", CfgOptionPtr()); + ref_dictionary_->addClass("cc3", ExpressionPtr(), "", CfgOptionPtr()); } @@ -427,4 +428,63 @@ TEST_F(SrvConfigTest, hooksLibraries) { EXPECT_TRUE(copied.getHooksConfig().equal(conf.getHooksConfig())); } +// Verifies that the toElement method works well (tests limited to +// direct parameters) +TEST_F(SrvConfigTest, unparse) { + SrvConfig conf(32); + std::string header4 = "{\n\"Dhcp4\": {\n"; + std::string header6 = "{\n\"Dhcp6\": {\n"; + + std::string defaults = "\"decline-probation-period\": 0,\n"; + defaults += "\"dhcp4o6-port\": 0,\n"; + defaults += "\"interfaces-config\": { \"interfaces\": [ ] },\n"; + defaults += "\"option-def\": [ ],\n"; + defaults += "\"option-data\": [ ],\n"; + defaults += "\"expired-leases-processing\": "; + defaults += conf.getCfgExpiration()->toElement()->str() + ",\n"; + defaults += "\"lease-database\": { \"type\": \"memfile\" },\n"; + defaults += "\"hosts-database\": { },\n"; + defaults += "\"client-classes\": [ ],\n"; + defaults += "\"hooks-libraries\": [ ],\n"; + defaults += "\"dhcp-ddns\": \n"; + defaults += conf.getD2ClientConfig()->toElement()->str() + ",\n"; + + std::string defaults4 = "\"echo-client-id\": true,\n"; + defaults4 += "\"subnet4\": [ ],\n"; + defaults4 += "\"host-reservation-identifiers\": "; + defaults4 += "[ \"hw-address\", \"duid\", \"circuit-id\" ],\n"; + + std::string defaults6 = "\"relay-supplied-options\": [ \"65\" ],\n"; + defaults6 += "\"subnet6\": [ ],\n"; + defaults6 += "\"server-id\": "; + defaults6 += conf.getCfgDUID()->toElement()->str() + ",\n"; + defaults6 += "\"host-reservation-identifiers\": "; + defaults6 += "[ \"hw-address\", \"duid\" ],\n"; + defaults6 += "\"dhcp4o6-port\": 0,\n"; + defaults6 += "\"mac-sources\": [ \"any\" ]\n"; + + std::string params = "\"echo-client-id\": true,\n"; + params += "\"dhcp4o6-port\": 0\n"; + std::string trailer = "}\n}\n"; + + // Verify DHCPv4 + CfgMgr::instance().setFamily(AF_INET); + isc::test::runToElementTest + (header4 + defaults + defaults4 + params + trailer, conf); + + // Verify DHCPv6 + CfgMgr::instance().setFamily(AF_INET6); + isc::test::runToElementTest + (header6 + defaults + defaults6 + trailer, conf); + + // Verify direct non-default parameters + CfgMgr::instance().setFamily(AF_INET); + conf.setEchoClientId(false); + conf.setDhcp4o6Port(6767); + params = "\"echo-client-id\": false,\n"; + params += "\"dhcp4o6-port\": 6767\n"; + isc::test::runToElementTest + (header4 + defaults + defaults4 + params + trailer, conf); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc index fd88883011..ea9a6dbc2f 100644 --- a/src/lib/dhcpsrv/tests/subnet_unittest.cc +++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-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 @@ -293,6 +293,7 @@ TEST(Subnet4Test, clientClasses) { three_classes.insert("baz"); // No class restrictions defined, any client should be supported + EXPECT_EQ(0, subnet->getClientClasses().size()); EXPECT_TRUE(subnet->clientSupported(no_class)); EXPECT_TRUE(subnet->clientSupported(foo_class)); EXPECT_TRUE(subnet->clientSupported(bar_class)); @@ -300,6 +301,7 @@ TEST(Subnet4Test, clientClasses) { // Let's allow only clients belonging to "bar" class. subnet->allowClientClass("bar"); + EXPECT_EQ(1, subnet->getClientClasses().size()); EXPECT_FALSE(subnet->clientSupported(no_class)); EXPECT_FALSE(subnet->clientSupported(foo_class)); @@ -325,6 +327,7 @@ TEST(Subnet4Test, clientClassesMultiple) { bar_class.insert("bar"); // No class restrictions defined, any client should be supported + EXPECT_EQ(0, subnet->getClientClasses().size()); EXPECT_TRUE(subnet->clientSupported(no_class)); EXPECT_TRUE(subnet->clientSupported(foo_class)); EXPECT_TRUE(subnet->clientSupported(bar_class)); @@ -332,6 +335,7 @@ TEST(Subnet4Test, clientClassesMultiple) { // Let's allow clients belonging to "bar" or "foo" class. subnet->allowClientClass("bar"); subnet->allowClientClass("foo"); + EXPECT_EQ(2, subnet->getClientClasses().size()); // Class-less clients are to be rejected. EXPECT_FALSE(subnet->clientSupported(no_class)); @@ -740,6 +744,7 @@ TEST(Subnet6Test, clientClasses) { three_classes.insert("baz"); // No class restrictions defined, any client should be supported + EXPECT_EQ(0, subnet->getClientClasses().size()); EXPECT_TRUE(subnet->clientSupported(no_class)); EXPECT_TRUE(subnet->clientSupported(foo_class)); EXPECT_TRUE(subnet->clientSupported(bar_class)); @@ -747,6 +752,7 @@ TEST(Subnet6Test, clientClasses) { // Let's allow only clients belonging to "bar" class. subnet->allowClientClass("bar"); + EXPECT_EQ(1, subnet->getClientClasses().size()); EXPECT_FALSE(subnet->clientSupported(no_class)); EXPECT_FALSE(subnet->clientSupported(foo_class)); @@ -772,6 +778,7 @@ TEST(Subnet6Test, clientClassesMultiple) { bar_class.insert("bar"); // No class restrictions defined, any client should be supported + EXPECT_EQ(0, subnet->getClientClasses().size()); EXPECT_TRUE(subnet->clientSupported(no_class)); EXPECT_TRUE(subnet->clientSupported(foo_class)); EXPECT_TRUE(subnet->clientSupported(bar_class)); @@ -779,6 +786,7 @@ TEST(Subnet6Test, clientClassesMultiple) { // Let's allow only clients belonging to "foo" or "bar" class. subnet->allowClientClass("foo"); subnet->allowClientClass("bar"); + EXPECT_EQ(2, subnet->getClientClasses().size()); // Class-less clients are to be rejected. EXPECT_FALSE(subnet->clientSupported(no_class)); diff --git a/src/lib/hooks/hooks_config.cc b/src/lib/hooks/hooks_config.cc index 72a0ad0c4c..ce0e268561 100644 --- a/src/lib/hooks/hooks_config.cc +++ b/src/lib/hooks/hooks_config.cc @@ -113,11 +113,9 @@ HooksConfig::toElement() const { ElementPtr map = Element::createMap(); // Set the library name map->set("library", Element::create(hl->first)); - // Set parameters + // Set parameters (not set vs set empty map) if (!isNull(hl->second)) { map->set("parameters", hl->second); - } else { - map->set("parameters", Element::createMap()); } // Push to the list result->add(map); diff --git a/src/lib/testutils/Makefile.am b/src/lib/testutils/Makefile.am index 8aa2cc2c71..cfd0c8d058 100644 --- a/src/lib/testutils/Makefile.am +++ b/src/lib/testutils/Makefile.am @@ -9,6 +9,7 @@ noinst_LTLIBRARIES = libkea-testutils.la libkea_testutils_la_SOURCES = io_utils.cc io_utils.h libkea_testutils_la_SOURCES += log_utils.cc log_utils.h +libkea_testutils_la_SOURCES += test_to_element.cc test_to_element.h libkea_testutils_la_SOURCES += unix_control_client.h unix_control_client.cc libkea_testutils_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) libkea_testutils_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la -- cgit v1.2.3 From c186258594afe10b3a72be69fce9f15be40d50ad Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Sun, 5 Mar 2017 22:51:55 +0100 Subject: [fdunparse2] Snapshot for 4o6-subnet fix --- src/bin/dhcp4/tests/Makefile.am | 1 + src/bin/dhcp4/tests/config_parser_unittest.cc | 68 +++- src/bin/dhcp4/tests/dhcp4_test_utils.cc | 1 + src/bin/dhcp4/tests/get_config_unittest.cc | 345 +++++++++++++++++++++ src/bin/dhcp4/tests/get_config_unittest.cc.skel | 345 +++++++++++++++++++++ src/bin/dhcp4/tests/get_config_unittest.h | 27 ++ src/bin/dhcp6/tests/Makefile.am | 1 + src/bin/dhcp6/tests/config_parser_unittest.cc | 146 ++++++--- src/bin/dhcp6/tests/dhcp6_test_utils.cc | 1 + src/lib/dhcpsrv/Makefile.am | 1 + src/lib/dhcpsrv/cfg_hosts.cc | 147 ++++++++- src/lib/dhcpsrv/cfg_hosts.h | 34 +- src/lib/dhcpsrv/cfg_hosts_util.cc | 94 ++++++ src/lib/dhcpsrv/cfg_hosts_util.h | 53 ++++ src/lib/dhcpsrv/cfg_subnets4.cc | 6 +- src/lib/dhcpsrv/srv_config.cc | 36 ++- src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc | 202 +++++++++++- src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc | 6 +- src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 1 + .../tests/host_reservation_parser_unittest.cc | 255 +++++++++++++++ .../host_reservations_list_parser_unittest.cc | 111 ++++++- src/lib/dhcpsrv/tests/srv_config_unittest.cc | 2 - 22 files changed, 1807 insertions(+), 76 deletions(-) create mode 100644 src/bin/dhcp4/tests/get_config_unittest.cc create mode 100644 src/bin/dhcp4/tests/get_config_unittest.cc.skel create mode 100644 src/bin/dhcp4/tests/get_config_unittest.h create mode 100644 src/lib/dhcpsrv/cfg_hosts_util.cc create mode 100644 src/lib/dhcpsrv/cfg_hosts_util.h (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am index 68c923eaaf..63955a0ab9 100644 --- a/src/bin/dhcp4/tests/Makefile.am +++ b/src/bin/dhcp4/tests/Makefile.am @@ -95,6 +95,7 @@ dhcp4_unittests_SOURCES += decline_unittest.cc dhcp4_unittests_SOURCES += kea_controller_unittest.cc dhcp4_unittests_SOURCES += dhcp4to6_ipc_unittest.cc dhcp4_unittests_SOURCES += simple_parser4_unittest.cc +dhcp4_unittests_SOURCES += get_config_unittest.cc get_config_unittest.h nodist_dhcp4_unittests_SOURCES = marker_file.h test_libraries.h diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 3f281b6770..f07934f8f9 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -31,6 +31,7 @@ #include "test_libraries.h" #include "test_data_files_config.h" #include "dhcp4_test_utils.h" +#include "get_config_unittest.h" #include #include @@ -628,12 +629,15 @@ TEST_F(Dhcp4ParserTest, bogusCommand) { /// pool definition. TEST_F(Dhcp4ParserTest, emptySubnet) { + std::string config = "{ " + genIfaceConfig() + "," + + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ ], " + "\"valid-lifetime\": 4000 }"; + ConstElementPtr json; - EXPECT_NO_THROW(json = parseDHCP4("{ " + genIfaceConfig() + "," + - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - "\"subnet4\": [ ], " - "\"valid-lifetime\": 4000 }")); + EXPECT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -655,6 +659,7 @@ TEST_F(Dhcp4ParserTest, unspecifiedRenewTimer) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -689,6 +694,7 @@ TEST_F(Dhcp4ParserTest, unspecifiedRebindTimer) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -723,6 +729,7 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -774,6 +781,7 @@ TEST_F(Dhcp4ParserTest, multipleSubnets) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); int cnt = 0; // Number of reconfigurations @@ -832,6 +840,7 @@ TEST_F(Dhcp4ParserTest, multipleSubnetsExplicitIDs) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); int cnt = 0; // Number of reconfigurations do { @@ -1040,6 +1049,7 @@ TEST_F(Dhcp4ParserTest, nextServerGlobal) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1070,6 +1080,7 @@ TEST_F(Dhcp4ParserTest, nextServerSubnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1168,6 +1179,7 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1206,8 +1218,10 @@ TEST_F(Dhcp4ParserTest, echoClientId) { ConstElementPtr json_false; ASSERT_NO_THROW(json_false = parseDHCP4(config_false)); + extractConfig(config_false); ConstElementPtr json_true; ASSERT_NO_THROW(json_true = parseDHCP4(config_true)); + extractConfig(config_true); // Let's check the default. It should be true ASSERT_TRUE(CfgMgr::instance().getStagingCfg()->getEchoClientId()); @@ -1248,6 +1262,7 @@ TEST_F(Dhcp4ParserTest, matchClientIdNoGlobal) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1285,6 +1300,7 @@ TEST_F(Dhcp4ParserTest, matchClientIdGlobal) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1317,6 +1333,7 @@ TEST_F(Dhcp4ParserTest, subnetLocal) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1357,6 +1374,7 @@ TEST_F(Dhcp4ParserTest, multiplePools) { "\"valid-lifetime\": 4000 }"; ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1429,6 +1447,7 @@ TEST_F(Dhcp4ParserTest, poolPrefixLen) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -1585,6 +1604,7 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config, true)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -1651,6 +1671,7 @@ TEST_F(Dhcp4ParserTest, optionDefRecord) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -1705,6 +1726,7 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the option definitions do not exist yet. ASSERT_FALSE(CfgMgr::instance().getStagingCfg()-> @@ -1813,6 +1835,7 @@ TEST_F(Dhcp4ParserTest, optionDefArray) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -1855,6 +1878,7 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulate) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -2056,6 +2080,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> getCfgOptionDef()->get(DHCP4_OPTION_SPACE, 109); @@ -2112,6 +2137,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) { " } ]" "}"; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Use the configuration string to create new option definition. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -2155,6 +2181,7 @@ TEST_F(Dhcp4ParserTest, optionDataDefaultsGlobal) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); checkResult(x, 0); @@ -2225,6 +2252,7 @@ TEST_F(Dhcp4ParserTest, optionDataDefaultsSubnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); checkResult(x, 0); @@ -2306,6 +2334,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -2381,6 +2410,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -2437,6 +2467,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) { "}"; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); ASSERT_TRUE(status); @@ -2498,6 +2529,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); checkResult(x, 0); @@ -2646,6 +2678,7 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); checkResult(x, 0); @@ -2920,6 +2953,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -2976,6 +3010,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) { ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); ASSERT_TRUE(status); @@ -3055,6 +3090,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -3110,6 +3146,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -3287,6 +3324,7 @@ TEST_F(Dhcp4ParserTest, selectedInterfaces) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; @@ -3326,6 +3364,7 @@ TEST_F(Dhcp4ParserTest, allInterfaces) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; @@ -3424,6 +3463,7 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) { // Convert the JSON string to configuration elements. ConstElementPtr config; ASSERT_NO_THROW(config = parseDHCP4(config_str, true)); + extractConfig(config_str); // Pass the configuration in for parsing. EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config)); @@ -3523,6 +3563,7 @@ TEST_F(Dhcp4ParserTest, subnetRelayInfo) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -3710,6 +3751,7 @@ TEST_F(Dhcp4ParserTest, reservations) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); checkResult(x, 0); @@ -3861,6 +3903,7 @@ TEST_F(Dhcp4ParserTest, reservationWithOptionDefinition) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config, true)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); checkResult(x, 0); @@ -4045,6 +4088,7 @@ TEST_F(Dhcp4ParserTest, hostReservationPerSubnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(hr_config)); + extractConfig(hr_config); ConstElementPtr result; EXPECT_NO_THROW(result = configureDhcp4Server(*srv_, json)); @@ -4092,6 +4136,7 @@ TEST_F(Dhcp4ParserTest, declineTimerDefault) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4113,6 +4158,7 @@ TEST_F(Dhcp4ParserTest, dhcp4o6portDefault) { "}"; ConstElementPtr config; ASSERT_NO_THROW(config = parseDHCP4(config_txt)); + extractConfig(config_txt); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config)); @@ -4138,6 +4184,7 @@ TEST_F(Dhcp4ParserTest, declineTimer) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4193,6 +4240,7 @@ TEST_F(Dhcp4ParserTest, expiredLeasesProcessing) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4263,6 +4311,7 @@ TEST_F(Dhcp4ParserTest, 4o6default) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4297,6 +4346,7 @@ TEST_F(Dhcp4ParserTest, 4o6subnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4393,6 +4443,7 @@ TEST_F(Dhcp4ParserTest, 4o6iface) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4429,6 +4480,7 @@ TEST_F(Dhcp4ParserTest, 4o6subnetIface) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4467,6 +4519,7 @@ TEST_F(Dhcp4ParserTest, 4o6subnetInterfaceId) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4517,6 +4570,7 @@ TEST_F(Dhcp4ParserTest, validClientClassDictionary) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json)); @@ -4563,6 +4617,7 @@ TEST_F(Dhcp4ParserTest, invalidClientClassDictionary) { // Test verifies that regular configuration does not provide any user context // in the address pool. TEST_F(Dhcp4ParserTest, poolUserContextMissing) { + extractConfig(PARSER_CONFIGS[0]); PoolPtr pool; getPool(string(PARSER_CONFIGS[0]), 0, 0, pool); ASSERT_TRUE(pool); @@ -4572,6 +4627,7 @@ TEST_F(Dhcp4ParserTest, poolUserContextMissing) { // Test verifies that it's possible to specify empty user context in the // address pool. TEST_F(Dhcp4ParserTest, poolUserContextEmpty) { + extractConfig(PARSER_CONFIGS[1]); PoolPtr pool; getPool(string(PARSER_CONFIGS[1]), 0, 0, pool); ASSERT_TRUE(pool); @@ -4586,6 +4642,7 @@ TEST_F(Dhcp4ParserTest, poolUserContextEmpty) { // Test verifies that it's possible to specify parameters in the user context // in the address pool. TEST_F(Dhcp4ParserTest, poolUserContextData) { + extractConfig(PARSER_CONFIGS[2]); PoolPtr pool; getPool(string(PARSER_CONFIGS[2]), 0, 0, pool); ASSERT_TRUE(pool); @@ -4619,6 +4676,7 @@ TEST_F(Dhcp4ParserTest, poolUserContextData) { // Test verifies that it's possible to specify parameters in the user context // in the min-max address pool. TEST_F(Dhcp4ParserTest, pooMinMaxlUserContext) { + extractConfig(PARSER_CONFIGS[3]); PoolPtr pool; getPool(string(PARSER_CONFIGS[3]), 0, 0, pool); ASSERT_TRUE(pool); diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index 7924125962..ca17ad2b3c 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -70,6 +70,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest() subnet_->getCfgOption()->add(opt_routers, false, DHCP4_OPTION_SPACE); CfgMgr::instance().clear(); + CfgMgr::instance().setFamily(AF_INET); CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_); CfgMgr::instance().commit(); diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc new file mode 100644 index 0000000000..59ac527a1d --- /dev/null +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -0,0 +1,345 @@ +// Copyright (C) 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace isc::config; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::dhcp::test; + +namespace { + +/// @name How to fill configurations +/// +/// Copy get_config_unittest.cc.skel into get_config_unittest.cc +/// +/// For the extracted configurations define the EXTRACT_CONFIG and +/// recompile this file. Run dhcp4_unittests on Dhcp4ParserTest +/// redirecting the standard error to a temporary file, e.g. by +/// @code +/// ./dhcp4_unittests --gtest_filter="Dhcp4Parser*" > /dev/null 2> x +/// @endcode +/// +/// Update EXTRACTED_CONFIGS with the file content +/// +/// When configurations have been extracted the corresponding unparsed +/// configurations must be generated. To do that define GENERATE_ACTION +/// and recompile this file. Run dhcp4_unittests on Dhcp4GetConfigTest +/// redirecting the standard error to a temporary file, e.g. by +/// @code +/// ./dhcp4_unittests --gtest_filter="Dhcp4GetConfig*" > /dev/null 2> u +/// @endcode +/// +/// Update UNPARSED_CONFIGS with the file content, recompile this file +/// without EXTRACT_CONFIG and GENERATE_ACTION. +/// +/// @note Check for failures at each step! +/// @note The tests of this file do not check if configs returned +/// by @ref isc::dhcp::CfgToElement::ToElement() are complete. +/// This has to be done manually. +/// +///@{ +/// @brief extracted configurations +const char* EXTRACTED_CONFIGS[] = { + // "to be replaced" +}; + +/// @brief unparsed configurations +const char* UNPARSED_CONFIGS[] = { + // "to be replaced" +}; + +/// @brief the number of configurations +const size_t max_config_counter = sizeof(EXTRACTED_CONFIGS) / sizeof(char*); +///@} + +/// @brief the extraction counter +/// +/// < 0 means do not extract, >= 0 means extract on extractConfig() calls +/// and increment +#ifdef EXTRACT_CONFIG +int extract_count = 0; +#else +int extract_count = -1; +#endif + +/// @brief the generate action +/// false means do nothing, true means unparse extracted configurations +#ifdef GENERATE_ACTION +const bool generate_action = true; +#else +const bool generate_action = false; +static_assert(max_config_counter == sizeof(UNPARSED_CONFIGS) / sizeof(char*), + "unparsed configurations must be generated"); +#endif + +/// @brief format and output a configuration +void +outputFormatted(const std::string& config) { + // pretty print it + ConstElementPtr json = parseJSON(config); + std::string prettier = prettyPrint(json, 4, 4); + // get it as a line array + std::list lines; + boost::split(lines, prettier, boost::is_any_of("\n")); + // add escapes using again JSON + std::list escapes; + while (!lines.empty()) { + const std::string& line = lines.front(); + ConstElementPtr escaping = Element::create(line + "\n"); + escapes.push_back(escaping->str()); + lines.pop_front(); + } + // output them on std::cerr + while (!escapes.empty()) { + std::cerr << "\n" << escapes.front(); + escapes.pop_front(); + } +} + +}; + +namespace isc { +namespace dhcp { +namespace test { + +/// @ref isc::dhcp::test::extractConfig in the header +void +extractConfig(const std::string& config) { + // skip when disable + if (extract_count < 0) { + return; + } + // mark beginning + if (extract_count == 0) { + // header (note there is no trailer) + std::cerr << "/// put this after const char* EXTRACTED_CONFIGS[] = {\n"; + } else { + // end of previous configuration + std::cerr << ",\n"; + } + std::cerr << " // CONFIGURATION " << extract_count; + try { + outputFormatted(config); + } catch (...) { + // mark error + std::cerr << "\n//// got an error\n"; + } + ++extract_count; +} + +}; +}; +}; + +namespace { + +/// Test fixture class (code from Dhcp4ParserTest) +class Dhcp4GetConfigTest : public ::testing::TestWithParam { +public: + Dhcp4GetConfigTest() + : 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_.reset(new Dhcpv4Srv(0)); + // Create fresh context. + resetConfiguration(); + } + + ~Dhcp4GetConfigTest() { + resetConfiguration(); + }; + + /// @brief Parse and Execute configuration + /// + /// Parses a configuration and executes a configuration of the server. + /// If the operation fails, the current test will register a failure. + /// + /// @param config Configuration to parse + /// @param operation Operation being performed. In the case of an error, + /// the error text will include the string "unable to .". + /// + /// @return true if the configuration succeeded, false if not. + bool + executeConfiguration(const std::string& config, const char* operation) { + // clear config manager + CfgMgr::instance().clear(); + + // enable fake network interfaces + IfaceMgrTestConfig test_config(true); + + // try JSON parser + ConstElementPtr json; + try { + json = parseJSON(config); + } catch (const std::exception& ex) { + ADD_FAILURE() << "invalid JSON for " << operation + << " failed with " << ex.what() + << " on\n" << config << "\n"; + return (false); + } + + // try DHCP4 parser + try { + json = parseDHCP4(config, true); + } catch (...) { + ADD_FAILURE() << "parsing failed for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // try DHCP4 configure + ConstElementPtr status; + try { + status = configureDhcp4Server(*srv_, json); + } catch (const std::exception& ex) { + ADD_FAILURE() << "configure for " << operation + << " failed with " << ex.what() + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // The status object must not be NULL + if (!status) { + ADD_FAILURE() << "configure for " << operation + << " returned null on\n" + << prettyPrint(json) << "\n"; + return (false); + } + + // Returned value should be 0 (configuration success) + comment_ = parseAnswer(rcode_, status); + if (rcode_ != 0) { + string reason = ""; + if (comment_) { + reason = string(" (") + comment_->stringValue() + string(")"); + } + ADD_FAILURE() << "configure for " << operation + << " returned error code " + << rcode_ << reason << " on\n" + << prettyPrint(json) << "\n"; + return (false); + } + return (true); + } + + /// @brief Reset configuration database. + /// + /// This function resets configuration data base by + /// removing all subnets and option-data. Reset must + /// be performed after each test to make sure that + /// contents of the database do not affect result of + /// subsequent tests. + void resetConfiguration() { + string config = "{" + "\"interfaces-config\": { \"interfaces\": [ \"*\" ] }," + "\"valid-lifetime\": 4000, " + "\"subnet4\": [ ], " + "\"dhcp-ddns\": { \"enable-updates\" : false }, " + "\"option-def\": [ ], " + "\"option-data\": [ ] }"; + EXPECT_TRUE(executeConfiguration(config, "reset configuration")); + CfgMgr::instance().clear(); + CfgMgr::instance().setFamily(AF_INET); + } + + boost::scoped_ptr srv_; ///< DHCP4 server under test + int rcode_; ///< Return code from element parsing + ConstElementPtr comment_; ///< Reason for parse fail +}; + +/// Test a configuration +TEST_P(Dhcp4GetConfigTest, run) { + // configurations have not been extracted yet + if (max_config_counter == 0) { + return; + } + + // get the index of configurations to test + size_t config_counter = GetParam(); + + // emit unparsed header if wanted + if ((config_counter == 0) && generate_action) { + std::cerr << "///put this after const char* UNPARSED_CONFIGS[] = {\n"; + } + + // get the extracted configuration + std::string config = EXTRACTED_CONFIGS[config_counter]; + std::ostringstream ss; + ss << "extracted config #" << config_counter; + + // execute the extracted configuration + ASSERT_TRUE(executeConfiguration(config, ss.str().c_str())); + + // unparse it + ConstSrvConfigPtr extracted = CfgMgr::instance().getStagingCfg(); + ConstElementPtr unparsed; + ASSERT_NO_THROW(unparsed = extracted->toElement()); + ConstElementPtr dhcp; + ASSERT_NO_THROW(dhcp = unparsed->get("Dhcp4")); + ASSERT_TRUE(dhcp); + + // dump if wanted else check + std::string expected; + if (generate_action) { + if (config_counter > 0) { + std::cerr << ",\n"; + } + std::cerr << " // CONFIGURATION " << config_counter; + ASSERT_NO_THROW(expected = prettyPrint(dhcp)); + ASSERT_NO_THROW(outputFormatted(dhcp->str())); + } else { + expected = UNPARSED_CONFIGS[config_counter]; + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP4(expected, true)); + EXPECT_TRUE(isEquivalent(dhcp, json)); + std::string current = prettyPrint(dhcp, 4, 4) + "\n"; + EXPECT_EQ(expected, current); + if (expected != current) { + expected = current; + } + } + + // execute the dhcp configuration + ss.str(""); + ss << "unparsed config #" << config_counter; + EXPECT_TRUE(executeConfiguration(expected, ss.str().c_str())); + + // is it a fixed point? + ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg(); + ConstElementPtr unparsed2; + ASSERT_NO_THROW(unparsed2 = extracted2->toElement()); + ASSERT_TRUE(unparsed2); + EXPECT_TRUE(isEquivalent(unparsed, unparsed2)); +} + +/// Define the parametrized test loop +INSTANTIATE_TEST_CASE_P(Dhcp4GetConfigTest, Dhcp4GetConfigTest, + ::testing::Range(0UL, max_config_counter)); + +}; diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc.skel b/src/bin/dhcp4/tests/get_config_unittest.cc.skel new file mode 100644 index 0000000000..59ac527a1d --- /dev/null +++ b/src/bin/dhcp4/tests/get_config_unittest.cc.skel @@ -0,0 +1,345 @@ +// Copyright (C) 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace isc::config; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::dhcp::test; + +namespace { + +/// @name How to fill configurations +/// +/// Copy get_config_unittest.cc.skel into get_config_unittest.cc +/// +/// For the extracted configurations define the EXTRACT_CONFIG and +/// recompile this file. Run dhcp4_unittests on Dhcp4ParserTest +/// redirecting the standard error to a temporary file, e.g. by +/// @code +/// ./dhcp4_unittests --gtest_filter="Dhcp4Parser*" > /dev/null 2> x +/// @endcode +/// +/// Update EXTRACTED_CONFIGS with the file content +/// +/// When configurations have been extracted the corresponding unparsed +/// configurations must be generated. To do that define GENERATE_ACTION +/// and recompile this file. Run dhcp4_unittests on Dhcp4GetConfigTest +/// redirecting the standard error to a temporary file, e.g. by +/// @code +/// ./dhcp4_unittests --gtest_filter="Dhcp4GetConfig*" > /dev/null 2> u +/// @endcode +/// +/// Update UNPARSED_CONFIGS with the file content, recompile this file +/// without EXTRACT_CONFIG and GENERATE_ACTION. +/// +/// @note Check for failures at each step! +/// @note The tests of this file do not check if configs returned +/// by @ref isc::dhcp::CfgToElement::ToElement() are complete. +/// This has to be done manually. +/// +///@{ +/// @brief extracted configurations +const char* EXTRACTED_CONFIGS[] = { + // "to be replaced" +}; + +/// @brief unparsed configurations +const char* UNPARSED_CONFIGS[] = { + // "to be replaced" +}; + +/// @brief the number of configurations +const size_t max_config_counter = sizeof(EXTRACTED_CONFIGS) / sizeof(char*); +///@} + +/// @brief the extraction counter +/// +/// < 0 means do not extract, >= 0 means extract on extractConfig() calls +/// and increment +#ifdef EXTRACT_CONFIG +int extract_count = 0; +#else +int extract_count = -1; +#endif + +/// @brief the generate action +/// false means do nothing, true means unparse extracted configurations +#ifdef GENERATE_ACTION +const bool generate_action = true; +#else +const bool generate_action = false; +static_assert(max_config_counter == sizeof(UNPARSED_CONFIGS) / sizeof(char*), + "unparsed configurations must be generated"); +#endif + +/// @brief format and output a configuration +void +outputFormatted(const std::string& config) { + // pretty print it + ConstElementPtr json = parseJSON(config); + std::string prettier = prettyPrint(json, 4, 4); + // get it as a line array + std::list lines; + boost::split(lines, prettier, boost::is_any_of("\n")); + // add escapes using again JSON + std::list escapes; + while (!lines.empty()) { + const std::string& line = lines.front(); + ConstElementPtr escaping = Element::create(line + "\n"); + escapes.push_back(escaping->str()); + lines.pop_front(); + } + // output them on std::cerr + while (!escapes.empty()) { + std::cerr << "\n" << escapes.front(); + escapes.pop_front(); + } +} + +}; + +namespace isc { +namespace dhcp { +namespace test { + +/// @ref isc::dhcp::test::extractConfig in the header +void +extractConfig(const std::string& config) { + // skip when disable + if (extract_count < 0) { + return; + } + // mark beginning + if (extract_count == 0) { + // header (note there is no trailer) + std::cerr << "/// put this after const char* EXTRACTED_CONFIGS[] = {\n"; + } else { + // end of previous configuration + std::cerr << ",\n"; + } + std::cerr << " // CONFIGURATION " << extract_count; + try { + outputFormatted(config); + } catch (...) { + // mark error + std::cerr << "\n//// got an error\n"; + } + ++extract_count; +} + +}; +}; +}; + +namespace { + +/// Test fixture class (code from Dhcp4ParserTest) +class Dhcp4GetConfigTest : public ::testing::TestWithParam { +public: + Dhcp4GetConfigTest() + : 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_.reset(new Dhcpv4Srv(0)); + // Create fresh context. + resetConfiguration(); + } + + ~Dhcp4GetConfigTest() { + resetConfiguration(); + }; + + /// @brief Parse and Execute configuration + /// + /// Parses a configuration and executes a configuration of the server. + /// If the operation fails, the current test will register a failure. + /// + /// @param config Configuration to parse + /// @param operation Operation being performed. In the case of an error, + /// the error text will include the string "unable to .". + /// + /// @return true if the configuration succeeded, false if not. + bool + executeConfiguration(const std::string& config, const char* operation) { + // clear config manager + CfgMgr::instance().clear(); + + // enable fake network interfaces + IfaceMgrTestConfig test_config(true); + + // try JSON parser + ConstElementPtr json; + try { + json = parseJSON(config); + } catch (const std::exception& ex) { + ADD_FAILURE() << "invalid JSON for " << operation + << " failed with " << ex.what() + << " on\n" << config << "\n"; + return (false); + } + + // try DHCP4 parser + try { + json = parseDHCP4(config, true); + } catch (...) { + ADD_FAILURE() << "parsing failed for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // try DHCP4 configure + ConstElementPtr status; + try { + status = configureDhcp4Server(*srv_, json); + } catch (const std::exception& ex) { + ADD_FAILURE() << "configure for " << operation + << " failed with " << ex.what() + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // The status object must not be NULL + if (!status) { + ADD_FAILURE() << "configure for " << operation + << " returned null on\n" + << prettyPrint(json) << "\n"; + return (false); + } + + // Returned value should be 0 (configuration success) + comment_ = parseAnswer(rcode_, status); + if (rcode_ != 0) { + string reason = ""; + if (comment_) { + reason = string(" (") + comment_->stringValue() + string(")"); + } + ADD_FAILURE() << "configure for " << operation + << " returned error code " + << rcode_ << reason << " on\n" + << prettyPrint(json) << "\n"; + return (false); + } + return (true); + } + + /// @brief Reset configuration database. + /// + /// This function resets configuration data base by + /// removing all subnets and option-data. Reset must + /// be performed after each test to make sure that + /// contents of the database do not affect result of + /// subsequent tests. + void resetConfiguration() { + string config = "{" + "\"interfaces-config\": { \"interfaces\": [ \"*\" ] }," + "\"valid-lifetime\": 4000, " + "\"subnet4\": [ ], " + "\"dhcp-ddns\": { \"enable-updates\" : false }, " + "\"option-def\": [ ], " + "\"option-data\": [ ] }"; + EXPECT_TRUE(executeConfiguration(config, "reset configuration")); + CfgMgr::instance().clear(); + CfgMgr::instance().setFamily(AF_INET); + } + + boost::scoped_ptr srv_; ///< DHCP4 server under test + int rcode_; ///< Return code from element parsing + ConstElementPtr comment_; ///< Reason for parse fail +}; + +/// Test a configuration +TEST_P(Dhcp4GetConfigTest, run) { + // configurations have not been extracted yet + if (max_config_counter == 0) { + return; + } + + // get the index of configurations to test + size_t config_counter = GetParam(); + + // emit unparsed header if wanted + if ((config_counter == 0) && generate_action) { + std::cerr << "///put this after const char* UNPARSED_CONFIGS[] = {\n"; + } + + // get the extracted configuration + std::string config = EXTRACTED_CONFIGS[config_counter]; + std::ostringstream ss; + ss << "extracted config #" << config_counter; + + // execute the extracted configuration + ASSERT_TRUE(executeConfiguration(config, ss.str().c_str())); + + // unparse it + ConstSrvConfigPtr extracted = CfgMgr::instance().getStagingCfg(); + ConstElementPtr unparsed; + ASSERT_NO_THROW(unparsed = extracted->toElement()); + ConstElementPtr dhcp; + ASSERT_NO_THROW(dhcp = unparsed->get("Dhcp4")); + ASSERT_TRUE(dhcp); + + // dump if wanted else check + std::string expected; + if (generate_action) { + if (config_counter > 0) { + std::cerr << ",\n"; + } + std::cerr << " // CONFIGURATION " << config_counter; + ASSERT_NO_THROW(expected = prettyPrint(dhcp)); + ASSERT_NO_THROW(outputFormatted(dhcp->str())); + } else { + expected = UNPARSED_CONFIGS[config_counter]; + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP4(expected, true)); + EXPECT_TRUE(isEquivalent(dhcp, json)); + std::string current = prettyPrint(dhcp, 4, 4) + "\n"; + EXPECT_EQ(expected, current); + if (expected != current) { + expected = current; + } + } + + // execute the dhcp configuration + ss.str(""); + ss << "unparsed config #" << config_counter; + EXPECT_TRUE(executeConfiguration(expected, ss.str().c_str())); + + // is it a fixed point? + ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg(); + ConstElementPtr unparsed2; + ASSERT_NO_THROW(unparsed2 = extracted2->toElement()); + ASSERT_TRUE(unparsed2); + EXPECT_TRUE(isEquivalent(unparsed, unparsed2)); +} + +/// Define the parametrized test loop +INSTANTIATE_TEST_CASE_P(Dhcp4GetConfigTest, Dhcp4GetConfigTest, + ::testing::Range(0UL, max_config_counter)); + +}; diff --git a/src/bin/dhcp4/tests/get_config_unittest.h b/src/bin/dhcp4/tests/get_config_unittest.h new file mode 100644 index 0000000000..e1718e0b94 --- /dev/null +++ b/src/bin/dhcp4/tests/get_config_unittest.h @@ -0,0 +1,27 @@ +// Copyright (C) 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef GET_CONFIG_UNITTEST_H +#define GET_CONFIG_UNITTEST_H + +#include + +namespace isc { +namespace dhcp { +namespace test { + +/// @brief Extract a configuration as a C++ source for JSON on std::cerr +/// +/// This function should be called when a configuration in an unit test +/// is interesting and should be extracted. Do nothing when extract_count +/// is negative. +void extractConfig(const std::string& config); + +}; +}; +}; + +#endif // GET_CONFIG_UNITTEST_H diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 2d844b78e4..d13b2a3792 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -96,6 +96,7 @@ dhcp6_unittests_SOURCES += dhcp6to4_ipc_unittest.cc dhcp6_unittests_SOURCES += classify_unittests.cc dhcp6_unittests_SOURCES += parser_unittest.cc dhcp6_unittests_SOURCES += simple_parser6_unittest.cc +dhcp6_unittests_SOURCES += get_config_unittest.cc get_config_unittest.h nodist_dhcp6_unittests_SOURCES = marker_file.h test_libraries.h diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 1d4ec0bfe2..71273d0bcf 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -30,6 +30,7 @@ #include "test_libraries.h" #include "marker_file.h" #include "dhcp6_test_utils.h" +#include "get_config_unittest.h" #include #include @@ -777,16 +778,20 @@ TEST_F(Dhcp6ParserTest, bogusCommand) { /// subnets defined can be accepted. TEST_F(Dhcp6ParserTest, emptySubnet) { + string config = "{ " + genIfaceConfig() + "," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ ], " + "\"valid-lifetime\": 4000 }"; + ConstElementPtr json; - ASSERT_NO_THROW(json = parseDHCP6("{ " + genIfaceConfig() + "," - "\"preferred-lifetime\": 3000," - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - "\"subnet6\": [ ], " - "\"valid-lifetime\": 4000 }")); + ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); + // returned value should be 0 (success) checkResult(status, 0); @@ -807,6 +812,7 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -860,6 +866,7 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); do { EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); @@ -919,6 +926,7 @@ TEST_F(Dhcp6ParserTest, multipleSubnetsExplicitIDs) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); do { EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); @@ -1062,6 +1070,7 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config4)); + extractConfig(config4); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -1134,6 +1143,7 @@ TEST_F(Dhcp6ParserTest, subnetLocal) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -1156,19 +1166,22 @@ TEST_F(Dhcp6ParserTest, subnetInterface) { // There should be at least one interface - string config = "{ " + genIfaceConfig() + "," - "\"preferred-lifetime\": 3000," - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - "\"subnet6\": [ { " - " \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ]," - " \"interface\": \"" + valid_iface_ + "\"," - " \"subnet\": \"2001:db8:1::/64\" } ]," - "\"valid-lifetime\": 4000 }"; - cout << config << endl; + auto config = [this](string iface) { + return ("{ " + genIfaceConfig() + "," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { " + " \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ]," + " \"interface\": \"" + iface + "\"," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"); }; + cout << config(valid_iface_) << endl; ConstElementPtr json; - ASSERT_NO_THROW(json = parseDHCP6(config)); + ASSERT_NO_THROW(json = parseDHCP6(config(valid_iface_))); + extractConfig(config("eth0")); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -1264,6 +1277,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -1414,6 +1428,7 @@ TEST_F(Dhcp6ParserTest, multiplePools) { "\"valid-lifetime\": 4000 }"; ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -1494,6 +1509,7 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); @@ -1667,6 +1683,7 @@ TEST_F(Dhcp6ParserTest, pdPoolBasics) { // Convert the JSON string into Elements. ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); // Verify that DHCP6 configuration processing succeeds. // Returned value must be non-empty ConstElementPtr to config result. @@ -1725,6 +1742,7 @@ TEST_F(Dhcp6ParserTest, pdPoolPrefixExclude) { // Convert the JSON string into Elements. ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); // Verify that DHCP6 configuration processing succeeds. // Returned value must be non-empty ConstElementPtr to config result. @@ -1805,6 +1823,7 @@ TEST_F(Dhcp6ParserTest, pdPoolList) { // Convert the JSON string into Elements. ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); // Verify that DHCP6 configuration processing succeeds. // Returned value must be non-empty ConstElementPtr to config result. @@ -1861,6 +1880,7 @@ TEST_F(Dhcp6ParserTest, subnetAndPrefixDelegated) { // Convert the JSON string into Elements. ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); // Verify that DHCP6 configuration processing succeeds. // Returned value must be non-empty ConstElementPtr to config result. @@ -1989,6 +2009,7 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -2053,6 +2074,7 @@ TEST_F(Dhcp6ParserTest, optionDefRecord) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -2106,6 +2128,7 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the option definitions do not exist yet. ASSERT_FALSE(CfgMgr::instance().getStagingCfg()-> @@ -2212,6 +2235,7 @@ TEST_F(Dhcp6ParserTest, optionDefArray) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -2252,6 +2276,7 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulate) { "}"; ConstElementPtr json; ASSERT_NO_THROW(json = parseOPTION_DEF(config)); + extractConfig(config); // Make sure that the particular option definition does not exist. OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()-> @@ -2551,6 +2576,7 @@ TEST_F(Dhcp6ParserTest, optionDataDefaultsGlobal) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -2623,6 +2649,7 @@ TEST_F(Dhcp6ParserTest, optionDataDefaultsSubnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -2713,6 +2740,7 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -2789,6 +2817,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -2846,6 +2875,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) { "}"; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); ASSERT_TRUE(status); @@ -2905,6 +2935,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -3005,6 +3036,7 @@ TEST_F(Dhcp6ParserTest, optionDataMultiplePools) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -3367,6 +3399,7 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -3426,6 +3459,7 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); ASSERT_TRUE(status); @@ -3491,6 +3525,7 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -3543,6 +3578,7 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) { "}"; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); ASSERT_TRUE(status); checkResult(status, 0); @@ -3756,6 +3792,7 @@ TEST_F(Dhcp6ParserTest, selectedInterfaces) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -3794,6 +3831,7 @@ TEST_F(Dhcp6ParserTest, allInterfaces) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -3825,6 +3863,7 @@ TEST_F(Dhcp6ParserTest, subnetRelayInfo) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -3869,6 +3908,7 @@ TEST_F(Dhcp6ParserTest, classifySubnets) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -3964,6 +4004,7 @@ TEST_F(Dhcp6ParserTest, d2ClientConfig) { // Convert the JSON string to configuration elements. ConstElementPtr config; ASSERT_NO_THROW(config = parseDHCP6(config_str)); + extractConfig(config_str); // Pass the configuration in for parsing. ConstElementPtr status; @@ -4141,6 +4182,7 @@ TEST_F(Dhcp6ParserTest, reservations) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -4297,6 +4339,7 @@ TEST_F(Dhcp6ParserTest, reservationWithOptionDefinition) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json)); checkResult(x, 0); @@ -4453,15 +4496,17 @@ TEST_F(Dhcp6ParserTest, reservationBogus) { /// rfc4649 = remote-id, rfc4580 = subscriber-id). TEST_F(Dhcp6ParserTest, macSources1) { + string config = "{ " + genIfaceConfig() + "," + "\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ ], " + "\"valid-lifetime\": 4000 }"; + ConstElementPtr json; - ASSERT_NO_THROW(json = - parseDHCP6("{ " + genIfaceConfig() + "," - "\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\" ]," - "\"preferred-lifetime\": 3000," - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - "\"subnet6\": [ ], " - "\"valid-lifetime\": 4000 }")); + ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -4480,16 +4525,18 @@ TEST_F(Dhcp6ParserTest, macSources1) { /// MAC/Hardware sources. This uses specific method names. TEST_F(Dhcp6ParserTest, macSources2) { + string config = "{ " + genIfaceConfig() + "," + "\"mac-sources\": [ \"client-link-addr-option\", \"remote-id\", " + " \"subscriber-id\"]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ ], " + "\"valid-lifetime\": 4000 }"; + ConstElementPtr json; - ASSERT_NO_THROW(json = - parseDHCP6("{ " + genIfaceConfig() + "," - "\"mac-sources\": [ \"client-link-addr-option\", \"remote-id\", " - " \"subscriber-id\"]," - "\"preferred-lifetime\": 3000," - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - "\"subnet6\": [ ], " - "\"valid-lifetime\": 4000 }")); + ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -4582,6 +4629,7 @@ TEST_F(Dhcp6ParserTest, hostReservationPerSubnet) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(HR_CONFIG)); + extractConfig(HR_CONFIG); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -4659,15 +4707,17 @@ TEST_F(Dhcp6ParserTest, rsooNumbers) { /// Relay Supplied options (specified as names). TEST_F(Dhcp6ParserTest, rsooNames) { + string config = "{ " + genIfaceConfig() + "," + "\"relay-supplied-options\": [ \"dns-servers\", \"remote-id\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ ], " + "\"valid-lifetime\": 4000 }"; + ConstElementPtr json; - ASSERT_NO_THROW(json = - parseDHCP6("{ " + genIfaceConfig() + "," - "\"relay-supplied-options\": [ \"dns-servers\", \"remote-id\" ]," - "\"preferred-lifetime\": 3000," - "\"rebind-timer\": 2000, " - "\"renew-timer\": 1000, " - "\"subnet6\": [ ], " - "\"valid-lifetime\": 4000 }")); + ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -4753,6 +4803,7 @@ TEST_F(Dhcp6ParserTest, declineTimerDefault) { "}"; ConstElementPtr config; ASSERT_NO_THROW(config = parseDHCP6(config_txt)); + extractConfig(config_txt); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config)); @@ -4775,6 +4826,7 @@ TEST_F(Dhcp6ParserTest, dhcp4o6portDefault) { "}"; ConstElementPtr config; ASSERT_NO_THROW(config = parseDHCP6(config_txt)); + extractConfig(config_txt); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config)); @@ -4797,6 +4849,7 @@ TEST_F(Dhcp6ParserTest, declineTimer) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -4851,6 +4904,7 @@ TEST_F(Dhcp6ParserTest, expiredLeasesProcessing) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -4929,6 +4983,7 @@ TEST_F(Dhcp6ParserTest, validClientClassDictionary) { ConstElementPtr json; ASSERT_NO_THROW(json = parseDHCP6(config)); + extractConfig(config); ConstElementPtr status; EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); @@ -4975,6 +5030,7 @@ TEST_F(Dhcp6ParserTest, invalidClientClassDictionary) { // Test verifies that regular configuration does not provide any user context // in the address pool. TEST_F(Dhcp6ParserTest, poolUserContextMissing) { + extractConfig(PARSER_CONFIGS[0]); PoolPtr pool; getPool(string(PARSER_CONFIGS[0]), 0, 0, Lease::TYPE_NA, pool); ASSERT_TRUE(pool); @@ -4984,6 +5040,7 @@ TEST_F(Dhcp6ParserTest, poolUserContextMissing) { // Test verifies that it's possible to specify empty user context in the // address pool. TEST_F(Dhcp6ParserTest, poolUserContextEmpty) { + extractConfig(PARSER_CONFIGS[1]); PoolPtr pool; getPool(string(PARSER_CONFIGS[1]), 0, 0, Lease::TYPE_NA, pool); ASSERT_TRUE(pool); @@ -4998,6 +5055,7 @@ TEST_F(Dhcp6ParserTest, poolUserContextEmpty) { // Test verifies that it's possible to specify parameters in the user context // in the address pool. TEST_F(Dhcp6ParserTest, poolUserContextlw4over6) { + extractConfig(PARSER_CONFIGS[2]); PoolPtr pool; getPool(string(PARSER_CONFIGS[2]), 0, 0, Lease::TYPE_NA, pool); ASSERT_TRUE(pool); @@ -5037,6 +5095,7 @@ TEST_F(Dhcp6ParserTest, poolUserContextlw4over6) { // Test verifies that it's possible to specify parameters in the user context // in the min-max address pool. TEST_F(Dhcp6ParserTest, poolMinMaxUserContext) { + extractConfig(PARSER_CONFIGS[3]); PoolPtr pool; getPool(string(PARSER_CONFIGS[3]), 0, 0, Lease::TYPE_NA, pool); ASSERT_TRUE(pool); @@ -5076,6 +5135,7 @@ TEST_F(Dhcp6ParserTest, poolMinMaxUserContext) { // Test verifies that regular configuration does not provide any user context // in the address pool. TEST_F(Dhcp6ParserTest, pdPoolUserContextMissing) { + extractConfig(PARSER_CONFIGS[4]); PoolPtr pool; getPool(string(PARSER_CONFIGS[4]), 0, 0, Lease::TYPE_PD, pool); ASSERT_TRUE(pool); @@ -5085,6 +5145,7 @@ TEST_F(Dhcp6ParserTest, pdPoolUserContextMissing) { // Test verifies that it's possible to specify empty user context in the // address pool. TEST_F(Dhcp6ParserTest, pdPoolUserContextEmpty) { + extractConfig(PARSER_CONFIGS[5]); PoolPtr pool; getPool(string(PARSER_CONFIGS[5]), 0, 0, Lease::TYPE_PD, pool); ASSERT_TRUE(pool); @@ -5099,6 +5160,7 @@ TEST_F(Dhcp6ParserTest, pdPoolUserContextEmpty) { // Test verifies that it's possible to specify parameters in the user context // in the address pool. TEST_F(Dhcp6ParserTest, pdPoolUserContextlw4over6) { + extractConfig(PARSER_CONFIGS[6]); PoolPtr pool; getPool(string(PARSER_CONFIGS[6]), 0, 0, Lease::TYPE_PD, pool); ASSERT_TRUE(pool); diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index 94a74e71b1..3aa93f599a 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -66,6 +66,7 @@ Dhcpv6SrvTest::Dhcpv6SrvTest() subnet_->addPool(pool_); isc::dhcp::CfgMgr::instance().clear(); + CfgMgr::instance().setFamily(AF_INET6); isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_); isc::dhcp::CfgMgr::instance().commit(); diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 004e3eb76f..9a0190cc24 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -89,6 +89,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_4o6.cc cfg_4o6.h libkea_dhcpsrv_la_SOURCES += cfg_db_access.cc cfg_db_access.h libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h +libkea_dhcpsrv_la_SOURCES += cfg_hosts_util.cc cfg_hosts_util.h libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h libkea_dhcpsrv_la_SOURCES += cfg_expiration.cc cfg_expiration.h libkea_dhcpsrv_la_SOURCES += cfg_host_operations.cc cfg_host_operations.h diff --git a/src/lib/dhcpsrv/cfg_hosts.cc b/src/lib/dhcpsrv/cfg_hosts.cc index bc0c1167d2..a5b1933b00 100644 --- a/src/lib/dhcpsrv/cfg_hosts.cc +++ b/src/lib/dhcpsrv/cfg_hosts.cc @@ -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 @@ -6,11 +6,17 @@ #include #include +#include #include +#include #include +#include #include +#include +#include using namespace isc::asiolink; +using namespace isc::data; namespace isc { namespace dhcp { @@ -667,5 +673,144 @@ CfgHosts::add6(const HostPtr& host) { } } +ElementPtr +CfgHosts::toElement() const { + uint16_t family = CfgMgr::instance().getFamily(); + if (family == AF_INET) { + return (toElement4()); + } else if (family == AF_INET6) { + return (toElement6()); + } else { + isc_throw(ToElementError, "CfgHosts::toElement: unknown " + "address family: " << family); + } +} + +ElementPtr +CfgHosts::toElement4() const { + CfgHostsList result; + // Iterate using arbitrary the index 0 + const HostContainerIndex0& idx = hosts_.get<0>(); + for (HostContainerIndex0::const_iterator host = idx.begin(); + host != idx.end(); ++host) { + // Get the subnet ID + SubnetID subnet_id = (*host)->getIPv4SubnetID(); + // Prepare the map + ElementPtr map = Element::createMap(); + // Set the identifier + Host::IdentifierType id_type = (*host)->getIdentifierType(); + if (id_type == Host::IDENT_HWADDR) { + HWAddrPtr hwaddr = (*host)->getHWAddress(); + map->set("hw-address", Element::create(hwaddr->toText(false))); + } else if (id_type == Host::IDENT_DUID) { + DuidPtr duid = (*host)->getDuid(); + map->set("duid", Element::create(duid->toText())); + } else if (id_type == Host::IDENT_CIRCUIT_ID) { + const std::vector& bin = (*host)->getIdentifier(); + std::string circuit_id = util::encode::encodeHex(bin); + map->set("circuit-id", Element::create(circuit_id)); + } else if (id_type == Host::IDENT_CLIENT_ID) { + const std::vector& bin = (*host)->getIdentifier(); + std::string client_id = util::encode::encodeHex(bin); + map->set("client-id", Element::create(client_id)); + } else { + isc_throw(ToElementError, "invalid DUID type: " << id_type); + } + // Set the reservation + const IOAddress& address = (*host)->getIPv4Reservation(); + map->set("ip-address", Element::create(address.toText())); + // Set the hostname + const std::string& hostname = (*host)->getHostname(); + map->set("hostname", Element::create(hostname)); + // Set next-server + const IOAddress& next_server = (*host)->getNextServer(); + map->set("next-server", Element::create(next_server.toText())); + // Set server-hostname + const std::string& server_hostname = (*host)->getServerHostname(); + map->set("server-hostname", Element::create(server_hostname)); + // Set boot-file-name + const std::string& boot_file_name = (*host)->getBootFileName(); + map->set("boot-file-name", Element::create(boot_file_name)); + // Set client-classes + const ClientClasses& cclasses = (*host)->getClientClasses4(); + ElementPtr classes = Element::createList(); + for (ClientClasses::const_iterator cclass = cclasses.cbegin(); + cclass != cclasses.end(); ++cclass) { + classes->add(Element::create(*cclass)); + } + map->set("client-classes", classes); + // Set option-data + ConstCfgOptionPtr opts = (*host)->getCfgOption4(); + map->set("option-data", opts->toElement()); + // Push the map on the list + result.add(subnet_id, map); + } + return (result.externalize()); +} + +ElementPtr +CfgHosts::toElement6() const { + CfgHostsList result; + // Iterate using arbitrary the index 0 + const HostContainerIndex0& idx = hosts_.get<0>(); + for (HostContainerIndex0::const_iterator host = idx.begin(); + host != idx.end(); ++host) { + // Get the subnet ID + SubnetID subnet_id = (*host)->getIPv6SubnetID(); + // Prepare the map + ElementPtr map = Element::createMap(); + // Set the identifier + Host::IdentifierType id_type = (*host)->getIdentifierType(); + if (id_type == Host::IDENT_HWADDR) { + HWAddrPtr hwaddr = (*host)->getHWAddress(); + map->set("hw-address", Element::create(hwaddr->toText(false))); + } else if (id_type == Host::IDENT_DUID) { + DuidPtr duid = (*host)->getDuid(); + map->set("duid", Element::create(duid->toText())); + } else if (id_type == Host::IDENT_CIRCUIT_ID) { + isc_throw(ToElementError, "unexpected circuit-id DUID type"); + } else if (id_type == Host::IDENT_CLIENT_ID) { + isc_throw(ToElementError, "unexpected client-id DUID type"); + } else { + isc_throw(ToElementError, "invalid DUID type: " << id_type); + } + // Set reservations (ip-addresses) + IPv6ResrvRange na_resv = + (*host)->getIPv6Reservations(IPv6Resrv::TYPE_NA); + ElementPtr resvs = Element::createList(); + for (IPv6ResrvIterator resv = na_resv.first; + resv != na_resv.second; ++resv) { + resvs->add(Element::create(resv->second.toText())); + } + map->set("ip-addresses", resvs); + // Set reservations (prefixes) + IPv6ResrvRange pd_resv = + (*host)->getIPv6Reservations(IPv6Resrv::TYPE_PD); + resvs = Element::createList(); + for (IPv6ResrvIterator resv = pd_resv.first; + resv != pd_resv.second; ++resv) { + resvs->add(Element::create(resv->second.toText())); + } + map->set("prefixes", resvs); + // Set the hostname + const std::string& hostname = (*host)->getHostname(); + map->set("hostname", Element::create(hostname)); + // Set client-classes + const ClientClasses& cclasses = (*host)->getClientClasses6(); + ElementPtr classes = Element::createList(); + for (ClientClasses::const_iterator cclass = cclasses.cbegin(); + cclass != cclasses.end(); ++cclass) { + classes->add(Element::create(*cclass)); + } + map->set("client-classes", classes); + // Set option-data + ConstCfgOptionPtr opts = (*host)->getCfgOption6(); + map->set("option-data", opts->toElement()); + // Push the map on the list + result.add(subnet_id, map); + } + return (result.externalize()); +} + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/cfg_hosts.h b/src/lib/dhcpsrv/cfg_hosts.h index c53a7d5a32..1e74bc689e 100644 --- a/src/lib/dhcpsrv/cfg_hosts.h +++ b/src/lib/dhcpsrv/cfg_hosts.h @@ -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 @@ -8,6 +8,7 @@ #define CFG_HOSTS_H #include +#include #include #include #include @@ -35,7 +36,8 @@ namespace dhcp { /// when the new configuration is applied for the server. The reservations /// are retrieved by the @c HostMgr class when the server is allocating or /// renewing an address or prefix for the particular client. -class CfgHosts : public BaseHostDataSource, public WritableHostDataSource { +class CfgHosts : public BaseHostDataSource, public WritableHostDataSource, + public isc::data::CfgToElement { public: /// @brief Destructor. @@ -329,6 +331,24 @@ public: return (std::string("configuration file")); } + /// @brief Unparse a configuration objet + /// + /// host reservation lists are not autonomous so they are + /// not returned directly but with the subnet where they are + /// declared as: + /// @code + /// [ + /// { "id": 123, "reservations": [ , ] }, + /// { "id": 456, "reservations": [ +#include +#include +#include +#include + +using namespace isc::data; + +namespace isc { +namespace dhcp { + +void CfgHostsList::internalize(ConstElementPtr list) { + if (!list) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "argument is NULL"); + } + if (list->getType() != Element::list) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "argument is not a list Element"); + } + for (size_t i = 0; i < list->size(); ++i) { + ConstElementPtr item = list->get(i); + if (!item) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "null pointer from the list at " << i); + } + if (item->getType() != Element::map) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "not a map from the list at " << i); + } + if (item->size() != 2) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "bad map size from the list at " << i); + } + ConstElementPtr id = item->get("id"); + if (!id) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "no id from a map at " << i); + } + if (id->getType() != Element::integer) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "not integer id from a map at " <(id->intValue()); + ConstElementPtr resvs = item->get("reservations"); + if (!resvs) { + isc_throw(BadValue, "internal error: CfgHostsList::internalize: " + "no reservations for subnet ID " << subnet_id); + } + map_.insert(std::make_pair(subnet_id, + boost::const_pointer_cast(resvs))); + } +} + +ElementPtr CfgHostsList::externalize() const { + ElementPtr result = Element::createList(); + for (CfgHostsMap::const_iterator item = map_.begin(); + item != map_.end(); ++item) { + ElementPtr pair = Element::createMap(); + pair->set("id", Element::create(static_cast(item->first))); + pair->set("reservations", item->second); + result->add(pair); + } + return (result); +} + +void CfgHostsList::add(SubnetID id, isc::data::ElementPtr resv) { + CfgHostsMap::iterator item = map_.find(id); + if (item != map_.end()) { + item->second->add(resv); + } else { + ElementPtr resvs = Element::createList(); + resvs->add(resv); + map_.insert(std::make_pair(id, resvs)); + } +} + +ConstElementPtr CfgHostsList::get(SubnetID id) const { + CfgHostsMap::const_iterator item = map_.find(id); + if (item != map_.end()) { + return (item->second); + } else { + return (Element::createList()); + } +} + +} +} diff --git a/src/lib/dhcpsrv/cfg_hosts_util.h b/src/lib/dhcpsrv/cfg_hosts_util.h new file mode 100644 index 0000000000..67803b9990 --- /dev/null +++ b/src/lib/dhcpsrv/cfg_hosts_util.h @@ -0,0 +1,53 @@ +// Copyright (C) 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CFG_HOSTS_UTIL_H +#define CFG_HOSTS_UTIL_H + +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Utility class to represent host reservation configurations +/// internally as a map keyed by subnet IDs, externally as a list Element. +class CfgHostsList { +public: + + /// The type of the internal map + typedef std::map CfgHostsMap; + + /// @brief Internalize a list Element + /// + /// This method gets a list Element and builds the internal map from it. + /// + /// @param list the list Element + void internalize(isc::data::ConstElementPtr list); + + /// @brief Externalize the map to a list Element + /// + /// @return a list Element representing all host reservations + isc::data::ElementPtr externalize() const; + + /// @brief Add a host reservation to the map + void add(SubnetID id, isc::data::ElementPtr resv); + + /// @brief Return the host reservations for a subnet ID + /// + /// @param id the subnet ID + /// @return a list Element with host reservations + isc::data::ConstElementPtr get(SubnetID id) const; + +private: + /// @brief The internal map + CfgHostsMap map_; +}; + +} +} + +#endif // CFG_HOSTS_UTIL_H diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 55c205dcb5..e00ce13445 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -332,9 +332,7 @@ CfgSubnets4::toElement() const { if (!isNull(context)) { pool_map->set("user-context", context); } - // Set pool options - ConstCfgOptionPtr opts = (*pool)->getCfgOption(); - pool_map->set("option-data", opts->toElement()); + // Set pool options (not yet supported) // Push on the pool list pool_list->add(pool_map); } @@ -365,7 +363,7 @@ CfgSubnets4::toElement() const { Element::create((*subnet)->getSiaddr().toText())); // Set DHCP4o6 const Cfg4o6& d4o6 = (*subnet)->get4o6(); - merge(map, d4o6.toElement()); + isc::data::merge(map, d4o6.toElement()); // Set client-class const ClientClasses& cclasses = (*subnet)->getClientClasses(); if (cclasses.size() > 1) { diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 13949c7da7..8cf0291a27 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include // Needed for HWADDR_SOURCE_* @@ -231,13 +232,29 @@ SrvConfig::toElement() const { ConstElementPtr option_data = cfg_option_->toElement(); dhcp->set("option-data", option_data); // Set subnets + ConstElementPtr subnets; + if (family == AF_INET) { + subnets = cfg_subnets4_->toElement(); + } else { + subnets = cfg_subnets6_->toElement(); + } + // Insert reservations + CfgHostsList resv_list; + resv_list.internalize(cfg_hosts_->toElement()); + const std::vector& sn_list = subnets->listValue(); + for (std::vector::const_iterator subnet = sn_list.begin(); + subnet != sn_list.end(); ++subnet) { + ConstElementPtr id = (*subnet)->get("id"); + if (isNull(id)) { + isc_throw(ToElementError, "subnet has no id"); + } + SubnetID subnet_id = id->intValue(); + ConstElementPtr resvs = resv_list.get(subnet_id); + (*subnet)->set("reservations", resvs); + } if (family == AF_INET) { - ConstElementPtr subnets = cfg_subnets4_->toElement(); - // @todo Insert reservations dhcp->set("subnet4", subnets); } else { - ConstElementPtr subnets = cfg_subnets6_->toElement(); - // @todo Insert reservations dhcp->set("subnet6", subnets); } // Set relay-supplied-options (DHCPv6) @@ -256,7 +273,11 @@ SrvConfig::toElement() const { dhcp->set("lease-database", lease_db.toElement()); // Set hosts-database CfgHostDbAccess host_db(*cfg_db_access_); - dhcp->set("hosts-database", host_db.toElement()); + // @todo accept empty map + ConstElementPtr hosts_database = host_db.toElement(); + if (hosts_database->size() > 0) { + dhcp->set("hosts-database", hosts_database); + } // Set host-reservation-identifiers ConstElementPtr host_ids; if (family == AF_INET) { @@ -275,7 +296,10 @@ SrvConfig::toElement() const { } // Set client-classes ConstElementPtr client_classes = class_dictionary_->toElement(); - dhcp->set("client-classes", client_classes); + // @todo accept empty list + if (!client_classes->empty()) { + dhcp->set("client-classes", client_classes); + } // Set hooks-libraries ConstElementPtr hooks_libs = hooks_config_.toElement(); dhcp->set("hooks-libraries", hooks_libs); diff --git a/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc b/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc index f8529d34b6..73dc6a4e53 100644 --- a/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc @@ -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 @@ -9,7 +9,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -78,7 +80,7 @@ CfgHostsTest::CfgHostsTest() { const uint32_t addra_template = 0xc0000205; // 192.0.2.5 const uint32_t addrb_template = 0xc00a020a; // 192.10.2.10 - for (int i = 0; i < 50; ++i) { + for (unsigned i = 0; i < 50; ++i) { IOAddress addra(addra_template + i); addressesa_.push_back(addra); IOAddress addrb(addrb_template + i); @@ -102,7 +104,7 @@ TEST_F(CfgHostsTest, getAllNonRepeatingHosts) { CfgHosts cfg; // Add 25 hosts identified by HW address and 25 hosts identified by // DUID. They are added to different subnets. - for (int i = 0; i < 25; ++i) { + for (unsigned i = 0; i < 25; ++i) { cfg.add(HostPtr(new Host(hwaddrs_[i]->toText(false), "hw-address", SubnetID(i % 10 + 1), SubnetID(i % 5 + 1), @@ -151,7 +153,7 @@ TEST_F(CfgHostsTest, getAllNonRepeatingHosts) { TEST_F(CfgHostsTest, getAllRepeatingHosts) { CfgHosts cfg; // Add hosts. - for (int i = 0; i < 25; ++i) { + for (unsigned i = 0; i < 25; ++i) { // Add two hosts, using the same HW address to two distinct subnets. cfg.add(HostPtr(new Host(hwaddrs_[i]->toText(false), "hw-address", @@ -172,7 +174,7 @@ TEST_F(CfgHostsTest, getAllRepeatingHosts) { } // Verify that hosts can be retrieved. - for (int i = 0; i < 25; ++i) { + for (unsigned i = 0; i < 25; ++i) { // Get host by HW address. The DUID is non-null but the reservation // should be returned for the HW address because there are no // reservations for the DUIDs from the range of 25 to 49. @@ -205,7 +207,7 @@ TEST_F(CfgHostsTest, getAllRepeatingHosts) { TEST_F(CfgHostsTest, getAll4ByAddress) { CfgHosts cfg; // Add hosts. - for (int i = 0; i < 25; ++i) { + for (unsigned i = 0; i < 25; ++i) { // Add host identified by the HW address. cfg.add(HostPtr(new Host(hwaddrs_[i]->toText(false), "hw-address", @@ -268,6 +270,90 @@ TEST_F(CfgHostsTest, get4) { } } +// This test checks that the DHCPv4 reservations can be unparsed +TEST_F(CfgHostsTest, unparsed4) { + CfgMgr::instance().setFamily(AF_INET); + CfgHosts cfg; + CfgHostsList list; + // Add hosts. + for (unsigned i = 0; i < 25; ++i) { + // Add host identified by HW address. + cfg.add(HostPtr(new Host(hwaddrs_[i]->toText(false), + "hw-address", + SubnetID(1 + i), SubnetID(13), + increase(IOAddress("192.0.2.5"), i)))); + + // Add host identified by DUID. + cfg.add(HostPtr(new Host(duids_[i]->toText(), "duid", + SubnetID(1 + i), SubnetID(13), + increase(IOAddress("192.0.2.100"), i)))); + } + + using namespace isc::data; + ConstElementPtr cfg_unparsed; + ASSERT_NO_THROW(cfg_unparsed = cfg.toElement()); + ASSERT_NO_THROW(list.internalize(cfg_unparsed)); + for (unsigned i = 0; i < 25; ++i) { + ConstElementPtr unparsed = list.get(SubnetID(1 + i)); + ASSERT_TRUE(unparsed); + ASSERT_EQ(Element::list, unparsed->getType()); + EXPECT_EQ(2, unparsed->size()); + ASSERT_NE(0, unparsed->size()); + + // Check by HW address entries + bool checked_hw = false; + for (unsigned j = 0; j < unparsed->size(); ++j) { + ConstElementPtr host = unparsed->get(j); + ASSERT_TRUE(host); + ASSERT_EQ(Element::map, host->getType()); + if (!host->contains("hw-address")) { + continue; + } + checked_hw = true; + // Not both hw-address and duid + EXPECT_FALSE(host->contains("duid")); + // Check the HW address + ConstElementPtr hw = host->get("hw-address"); + ASSERT_TRUE(hw); + ASSERT_EQ(Element::string, hw->getType()); + EXPECT_EQ(hwaddrs_[i]->toText(false), hw->stringValue()); + // Check the reservation + ConstElementPtr resv = host->get("ip-address"); + ASSERT_TRUE(resv); + ASSERT_EQ(Element::string, resv->getType()); + EXPECT_EQ(increase(IOAddress("192.0.2.5"), i), + IOAddress(resv->stringValue())); + } + ASSERT_TRUE(checked_hw); + + // Check by DUID entries + bool checked_duid = false; + for (unsigned j = 0; j < unparsed->size(); ++j) { + ConstElementPtr host = unparsed->get(j); + ASSERT_TRUE(host); + ASSERT_EQ(Element::map, host->getType()); + if (!host->contains("duid")) { + continue; + } + checked_duid = true; + // Not both hw-address and duid + EXPECT_FALSE(host->contains("hw-address")); + // Check the DUID + ConstElementPtr duid = host->get("duid"); + ASSERT_TRUE(duid); + ASSERT_EQ(Element::string, duid->getType()); + EXPECT_EQ(duids_[i]->toText(), duid->stringValue()); + // Check the reservation + ConstElementPtr resv = host->get("ip-address"); + ASSERT_TRUE(resv); + ASSERT_EQ(Element::string, resv->getType()); + EXPECT_EQ(increase(IOAddress("192.0.2.100"), i), + IOAddress(resv->stringValue())); + } + ASSERT_TRUE(checked_duid); + } +} + // This test checks that the reservations can be retrieved for the particular // host connected to the specific IPv6 subnet (by subnet id). TEST_F(CfgHostsTest, get6) { @@ -319,6 +405,108 @@ TEST_F(CfgHostsTest, get6) { } } +// This test checks that the DHCPv6 reservations can be unparsed +TEST_F(CfgHostsTest, unparse6) { + CfgMgr::instance().setFamily(AF_INET6); + CfgHosts cfg; + CfgHostsList list; + // Add hosts. + for (unsigned i = 0; i < 25; ++i) { + // Add host identified by HW address. + HostPtr host = HostPtr(new Host(hwaddrs_[i]->toText(false), + "hw-address", + SubnetID(10), SubnetID(1 + i), + IOAddress("0.0.0.0"))); + host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + increase(IOAddress("2001:db8:1::1"), + i))); + cfg.add(host); + + // Add host identified by DUID. + host = HostPtr(new Host(duids_[i]->toText(), "duid", + SubnetID(10), SubnetID(1 + i), + IOAddress("0.0.0.0"))); + host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, + increase(IOAddress("2001:db8:2::1"), + i))); + cfg.add(host); + } + + using namespace isc::data; + ConstElementPtr cfg_unparsed; + ASSERT_NO_THROW(cfg_unparsed = cfg.toElement()); + ASSERT_NO_THROW(list.internalize(cfg_unparsed)); + for (unsigned i = 0; i < 25; ++i) { + ConstElementPtr unparsed = list.get(SubnetID(1 + i)); + ASSERT_TRUE(unparsed); + ASSERT_EQ(Element::list, unparsed->getType()); + EXPECT_EQ(2, unparsed->size()); + ASSERT_NE(0, unparsed->size()); + + // Check by HW address entries + bool checked_hw = false; + for (unsigned j = 0; j < unparsed->size(); ++j) { + ConstElementPtr host = unparsed->get(j); + ASSERT_TRUE(host); + ASSERT_EQ(Element::map, host->getType()); + if (!host->contains("hw-address")) { + continue; + } + checked_hw = true; + // Not both hw-address and duid + EXPECT_FALSE(host->contains("duid")); + // Check the HW address + ConstElementPtr hw = host->get("hw-address"); + ASSERT_TRUE(hw); + ASSERT_EQ(Element::string, hw->getType()); + EXPECT_EQ(hwaddrs_[i]->toText(false), hw->stringValue()); + // Check the reservation + ConstElementPtr resvs = host->get("ip-addresses"); + ASSERT_TRUE(resvs); + ASSERT_EQ(Element::list, resvs->getType()); + EXPECT_EQ(1, resvs->size()); + ASSERT_GE(1, resvs->size()); + ConstElementPtr resv = resvs->get(0); + ASSERT_TRUE(resv); + ASSERT_EQ(Element::string, resv->getType()); + EXPECT_EQ(increase(IOAddress("2001:db8:1::1"), i), + IOAddress(resv->stringValue())); + } + ASSERT_TRUE(checked_hw); + + // Check by DUID entries + bool checked_duid = false; + for (unsigned j = 0; j < unparsed->size(); ++j) { + ConstElementPtr host = unparsed->get(j); + ASSERT_TRUE(host); + ASSERT_EQ(Element::map, host->getType()); + if (!host->contains("duid")) { + continue; + } + checked_duid = true; + // Not both hw-address and duid + EXPECT_FALSE(host->contains("hw-address")); + // Check the DUID + ConstElementPtr duid = host->get("duid"); + ASSERT_TRUE(duid); + ASSERT_EQ(Element::string, duid->getType()); + EXPECT_EQ(duids_[i]->toText(), duid->stringValue()); + // Check the reservation + ConstElementPtr resvs = host->get("ip-addresses"); + ASSERT_TRUE(resvs); + ASSERT_EQ(Element::list, resvs->getType()); + EXPECT_EQ(1, resvs->size()); + ASSERT_GE(1, resvs->size()); + ConstElementPtr resv = resvs->get(0); + ASSERT_TRUE(resv); + ASSERT_EQ(Element::string, resv->getType()); + EXPECT_EQ(increase(IOAddress("2001:db8:2::1"), i), + IOAddress(resv->stringValue())); + } + ASSERT_TRUE(checked_duid); + } +} + // This test checks that the IPv6 reservations can be retrieved for a particular // (subnet-id, address) tuple. TEST_F(CfgHostsTest, get6ByAddr) { @@ -365,7 +553,7 @@ TEST_F(CfgHostsTest, get6MultipleAddrs) { IOAddress("0.0.0.0"))); // Generate 5 unique addresses for this host. - for (int j = 0; j < 5; ++j) { + for (unsigned j = 0; j < 5; ++j) { std::stringstream address_stream; address_stream << "2001:db8:" << i << "::" << j; host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index ab39f44264..dfec17744e 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -544,11 +544,9 @@ TEST(CfgSubnets4Test, unparsePool) { " \"option-data\": [],\n" " \"pools\": [\n" " {\n" - " \"pool\": \"192.0.2.1-192.0.2.10\",\n" - " \"option-data\": []\n" + " \"pool\": \"192.0.2.1-192.0.2.10\"\n" " },{\n" - " \"pool\": \"192.0.2.64/26\",\n" - " \"option-data\": []\n" + " \"pool\": \"192.0.2.64/26\"\n" " }\n" " ]\n" "} ]\n"; diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc index 8c1bfbd560..e35e7ad783 100644 --- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc @@ -280,6 +280,7 @@ public: void clear() { CfgMgr::instance().setVerbose(false); + CfgMgr::instance().setFamily(AF_INET); CfgMgr::instance().clear(); LeaseMgrFactory::destroy(); } diff --git a/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc b/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc index b7b683736d..4fa43342f0 100644 --- a/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc @@ -14,11 +14,14 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include #include #include @@ -28,6 +31,7 @@ using namespace isc::asiolink; using namespace isc::data; using namespace isc::dhcp; +using namespace isc::test; namespace { @@ -216,6 +220,77 @@ HostReservationParserTest::TearDown() { CfgMgr::instance().clear(); } +/// @brief class of subnet_id reservations +class CfgHostsSubnet : public CfgToElement { +public: + /// @brief constructor + CfgHostsSubnet(ConstCfgHostsPtr hosts, SubnetID id) + : hosts_(hosts), id_(id) { } + + /// @brief unparse method + ElementPtr toElement() const; + +private: + /// @brief the host reservation configuration + ConstCfgHostsPtr hosts_; + + /// @brief the subnet ID + SubnetID id_; +}; + +ElementPtr +CfgHostsSubnet::toElement() const { + CfgHostsList list; + try { + list.internalize(hosts_->toElement()); + } catch (const std::exception& ex) { + ADD_FAILURE() << "CfgHostsSubnet::toElement: " << ex.what(); + } + ElementPtr result = boost::const_pointer_cast(list.get(id_)); + + // Strip + for (size_t i = 0; i < result->size(); ++i) { + ElementPtr resv = result->getNonConst(i); + ConstElementPtr ip_address = resv->get("ip-address"); + if (ip_address && (ip_address->stringValue() == "0.0.0.0")) { + resv->remove("ip-address"); + } + ConstElementPtr ip_addresses = resv->get("ip-addresses"); + if (ip_addresses && ip_addresses->empty()) { + resv->remove("ip-addresses"); + } + ConstElementPtr prefixes = resv->get("prefixes"); + if (prefixes && prefixes->empty()) { + resv->remove("prefixes"); + } + ConstElementPtr hostname = resv->get("hostname"); + if (hostname && hostname->stringValue().empty()) { + resv->remove("hostname"); + } + ConstElementPtr next_server = resv->get("next-server"); + if (next_server && (next_server->stringValue() == "0.0.0.0")) { + resv->remove("next-server"); + } + ConstElementPtr server_hostname = resv->get("server-hostname"); + if (server_hostname && server_hostname->stringValue().empty()) { + resv->remove("server-hostname"); + } + ConstElementPtr boot_file_name = resv->get("boot-file-name"); + if (boot_file_name && boot_file_name->stringValue().empty()) { + resv->remove("boot-file-name"); + } + ConstElementPtr client_classess = resv->get("client-classes"); + if (client_classess && client_classess->empty()) { + resv->remove("client-classes"); + } + ConstElementPtr option_data = resv->get("option-data"); + if (option_data && option_data->empty()) { + resv->remove("option-data"); + } + } + return (result); +} + // This test verifies that the parser can parse the reservation entry for // which hw-address is a host identifier. TEST_F(HostReservationParserTest, dhcp4HWaddr) { @@ -299,6 +374,20 @@ TEST_F(HostReservationParserTest, dhcp4NoHostname) { EXPECT_EQ(0, hosts[0]->getIPv6SubnetID()); EXPECT_EQ("192.0.2.10", hosts[0]->getIPv4Reservation().toText()); EXPECT_TRUE(hosts[0]->getHostname().empty()); + + // lower duid value + boost::algorithm::to_lower(config); + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest("[" + config + "]", cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet6(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet6); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that it is possible to specify DHCPv4 client classes @@ -322,6 +411,18 @@ TEST_F(HostReservationParserTest, dhcp4ClientClasses) { ASSERT_EQ(2, classes.size()); EXPECT_EQ(1, classes.count("foo")); EXPECT_EQ(1, classes.count("bar")); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest("[" + config + "]", cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet6(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet6); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that the parser can parse reservation entry @@ -350,6 +451,24 @@ TEST_F(HostReservationParserTest, dhcp4MessageFields) { EXPECT_EQ("192.0.2.11", hosts[0]->getNextServer().toText()); EXPECT_EQ("some-name.example.org", hosts[0]->getServerHostname()); EXPECT_EQ("/tmp/some-file.efi", hosts[0]->getBootFileName()); + + // canonize hw-address + config_element->set("hw-address", + Element::create(std::string("01:02:03:04:05:06"))); + ElementPtr expected = Element::createList(); + expected->add(config_element); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest(expected, cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet6(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet6); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that the invalid value of the next server is rejected. @@ -435,6 +554,20 @@ TEST_F(HostReservationParserTest, noIPAddress) { EXPECT_EQ(0, hosts[0]->getIPv6SubnetID()); EXPECT_EQ("0.0.0.0", hosts[0]->getIPv4Reservation().toText()); EXPECT_EQ("foo.example.com", hosts[0]->getHostname()); + + // lower duid value + boost::algorithm::to_lower(config); + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest("[" + config + "]", cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet6(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet6); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that the configuration parser for host reservations @@ -552,6 +685,24 @@ TEST_F(HostReservationParserTest, dhcp6HWaddr) { 64), prefixes)); + // canonize prefixes + config_element->set("prefixes", + Element::fromJSON("[ \"2001:db8:2000:101::/64\", " + "\"2001:db8:2000:102::/64\" ]")); + ElementPtr expected = Element::createList(); + expected->add(config_element); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest(expected, cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet4(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet4); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that the parser can parse the IPv6 reservation entry for @@ -590,6 +741,23 @@ TEST_F(HostReservationParserTest, dhcp6DUID) { IPv6ResrvRange prefixes = hosts[0]->getIPv6Reservations(IPv6Resrv::TYPE_PD); ASSERT_EQ(0, std::distance(prefixes.first, prefixes.second)); + + // remove prefixes and lower duid value + config_element->remove("prefixes"); + config = prettyPrint(config_element); + boost::algorithm::to_lower(config); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(12)); + runToElementTest("[" + config + "]", cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet4(cfg_hosts, SubnetID(12)); + runToElementTest("[ ]", cfg_subnet4); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that host reservation parser for DHCPv6 rejects @@ -649,6 +817,23 @@ TEST_F(HostReservationParserTest, dhcp6NoHostname) { IPv6ResrvRange prefixes = hosts[0]->getIPv6Reservations(IPv6Resrv::TYPE_PD); ASSERT_EQ(0, std::distance(prefixes.first, prefixes.second)); + + // remove prefixes and lower duid value + config_element->remove("prefixes"); + config = prettyPrint(config_element); + boost::algorithm::to_lower(config); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(12)); + runToElementTest("[" + config + "]", cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet4(cfg_hosts, SubnetID(12)); + runToElementTest("[ ]", cfg_subnet4); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that it is possible to specify DHCPv4 client classes @@ -673,6 +858,20 @@ TEST_F(HostReservationParserTest, dhcp6ClientClasses) { ASSERT_EQ(2, classes.size()); EXPECT_EQ(1, classes.count("foo")); EXPECT_EQ(1, classes.count("bar")); + + // lower duid value + boost::algorithm::to_lower(config); + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest("[" + config + "]", cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet4(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet4); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that the configuration parser throws an exception @@ -800,6 +999,32 @@ TEST_F(HostReservationParserTest, options4) { OptionUint8>(retrieveOption(*hosts[0], DHCP4_OPTION_SPACE, DHO_DEFAULT_IP_TTL)); ASSERT_TRUE(opt_ttl); EXPECT_EQ(64, opt_ttl->getValue()); + + // Canonize the config + ElementPtr option = config_element->get("option-data")->getNonConst(0); + option->set("code", Element::create(DHO_NAME_SERVERS)); + option->set("space", Element::create(std::string(DHCP4_OPTION_SPACE))); + option->set("csv-format", Element::create(true)); + option = config_element->get("option-data")->getNonConst(1); + option = config_element->get("option-data")->getNonConst(2); + option->set("code", Element::create(DHO_DEFAULT_IP_TTL)); + option->set("space", Element::create(std::string(DHCP4_OPTION_SPACE))); + option->set("csv-format", Element::create(true)); + ElementPtr expected = Element::createList(); + expected->add(config_element); + + // Try to unparse it. + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest(expected, cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet6(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet6); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that it is possible to specify DHCPv6 options for @@ -858,6 +1083,32 @@ TEST_F(HostReservationParserTest, options6) { OptionUint8>(retrieveOption(*hosts[0], DHCP6_OPTION_SPACE, D6O_PREFERENCE)); ASSERT_TRUE(opt_prf); EXPECT_EQ(11, opt_prf->getValue()); + + // Canonize the config + ElementPtr option = config_element->get("option-data")->getNonConst(0); + option->set("code", Element::create(D6O_NAME_SERVERS)); + option->set("space", Element::create(std::string(DHCP6_OPTION_SPACE))); + option->set("csv-format", Element::create(true)); + option = config_element->get("option-data")->getNonConst(1); + option = config_element->get("option-data")->getNonConst(2); + option->set("code", Element::create(D6O_PREFERENCE)); + option->set("space", Element::create(std::string(DHCP6_OPTION_SPACE))); + option->set("csv-format", Element::create(true)); + config = prettyPrint(config_element); + boost::algorithm::to_lower(config); + + // Try to unparse it. + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(10)); + runToElementTest("[" + config + "]", cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet4(cfg_hosts, SubnetID(10)); + runToElementTest("[ ]", cfg_subnet4); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that it is possible to specify an empty list of @@ -1008,6 +1259,8 @@ TEST_F(HostReservationIdsParserTest, dhcp4Identifiers) { EXPECT_EQ(*id++, Host::IDENT_DUID); EXPECT_EQ(*id++, Host::IDENT_HWADDR); EXPECT_EQ(*id++, Host::IDENT_CLIENT_ID); + + runToElementTest(config, *cfg); } // Test that list of supported DHCPv6 identifiers list is correctly @@ -1028,6 +1281,8 @@ TEST_F(HostReservationIdsParserTest, dhcp6Identifiers) { CfgHostOperations::IdentifierTypes::const_iterator id = ids.begin(); EXPECT_EQ(*id++, Host::IDENT_DUID); EXPECT_EQ(*id++, Host::IDENT_HWADDR); + + runToElementTest(config, *cfg); } // Test that invalid DHCPv4 identifier causes error. diff --git a/src/lib/dhcpsrv/tests/host_reservations_list_parser_unittest.cc b/src/lib/dhcpsrv/tests/host_reservations_list_parser_unittest.cc index 9c12d9174c..1d0ec65604 100644 --- a/src/lib/dhcpsrv/tests/host_reservations_list_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_reservations_list_parser_unittest.cc @@ -11,11 +11,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -23,6 +26,7 @@ using namespace isc::data; using namespace isc::dhcp; +using namespace isc::test; namespace { @@ -67,9 +71,82 @@ HostReservationsListParserTest::TearDown() { CfgMgr::instance().clear(); } +/// @brief class of subnet_id reservations +class CfgHostsSubnet : public CfgToElement { +public: + /// @brief constructor + CfgHostsSubnet(ConstCfgHostsPtr hosts, SubnetID id) + : hosts_(hosts), id_(id) { } + + /// @brief unparse method + ElementPtr toElement() const; + +private: + /// @brief the host reservation configuration + ConstCfgHostsPtr hosts_; + + /// @brief the subnet ID + SubnetID id_; +}; + +ElementPtr +CfgHostsSubnet::toElement() const { + CfgHostsList list; + try { + list.internalize(hosts_->toElement()); + } catch (const std::exception& ex) { + ADD_FAILURE() << "CfgHostsSubnet::toElement: " << ex.what(); + } + ElementPtr result = boost::const_pointer_cast(list.get(id_)); + + // Strip + for (size_t i = 0; i < result->size(); ++i) { + ElementPtr resv = result->getNonConst(i); + ConstElementPtr ip_address = resv->get("ip-address"); + if (ip_address && (ip_address->stringValue() == "0.0.0.0")) { + resv->remove("ip-address"); + } + ConstElementPtr ip_addresses = resv->get("ip-addresses"); + if (ip_addresses && ip_addresses->empty()) { + resv->remove("ip-addresses"); + } + ConstElementPtr prefixes = resv->get("prefixes"); + if (prefixes && prefixes->empty()) { + resv->remove("prefixes"); + } + ConstElementPtr hostname = resv->get("hostname"); + if (hostname && hostname->stringValue().empty()) { + resv->remove("hostname"); + } + ConstElementPtr next_server = resv->get("next-server"); + if (next_server && (next_server->stringValue() == "0.0.0.0")) { + resv->remove("next-server"); + } + ConstElementPtr server_hostname = resv->get("server-hostname"); + if (server_hostname && server_hostname->stringValue().empty()) { + resv->remove("server-hostname"); + } + ConstElementPtr boot_file_name = resv->get("boot-file-name"); + if (boot_file_name && boot_file_name->stringValue().empty()) { + resv->remove("boot-file-name"); + } + ConstElementPtr client_classess = resv->get("client-classes"); + if (client_classess && client_classess->empty()) { + resv->remove("client-classes"); + } + ConstElementPtr option_data = resv->get("option-data"); + if (option_data && option_data->empty()) { + resv->remove("option-data"); + } + } + return (result); +} + // This test verifies that the parser for the list of the host reservations // parses IPv4 reservations correctly. TEST_F(HostReservationsListParserTest, ipv4Reservations) { + CfgMgr::instance().setFamily(AF_INET); + // hexadecimal in lower case for toElement() std::string config = "[ " " { " @@ -109,6 +186,19 @@ TEST_F(HostReservationsListParserTest, ipv4Reservations) { EXPECT_EQ(0, hosts[0]->getIPv6SubnetID()); EXPECT_EQ("192.0.2.110", hosts[0]->getIPv4Reservation().toText()); EXPECT_EQ("bar.example.com", hosts[0]->getHostname()); + + // Get back the config from cfg_hosts + boost::algorithm::to_lower(config); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(1)); + runToElementTest(config, cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet6(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet6); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(0)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that an attempt to add two reservations with the @@ -147,6 +237,7 @@ TEST_F(HostReservationsListParserTest, duplicatedIdentifierValue4) { // This test verifies that the parser for the list of the host reservations // parses IPv6 reservations correctly. TEST_F(HostReservationsListParserTest, ipv6Reservations) { + // hexadecimal in lower case for toElement() std::string config = "[ " " { \"duid\": \"01:02:03:04:05:06:07:08:09:0A\"," @@ -156,7 +247,6 @@ TEST_F(HostReservationsListParserTest, ipv6Reservations) { " }, " " { \"hw-address\": \"01:02:03:04:05:06\"," " \"ip-addresses\": [ \"2001:db8:1::123\" ]," - " \"prefixes\": [ ]," " \"hostname\": \"bar.example.com\" " " } " "]"; @@ -202,6 +292,23 @@ TEST_F(HostReservationsListParserTest, ipv6Reservations) { EXPECT_EQ(IPv6Resrv::TYPE_PD, prefixes.first->second.getType()); EXPECT_EQ("2001:db8:1:2::", prefixes.first->second.getPrefix().toText()); EXPECT_EQ(80, prefixes.first->second.getPrefixLen()); + + // Get back the config from cfg_hosts + ElementPtr resv = config_element->getNonConst(0); + resv->remove("ip-addresses"); + config = prettyPrint(config_element); + boost::algorithm::to_lower(config); + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet(cfg_hosts, SubnetID(2)); + runToElementTest(config, cfg_subnet); + + CfgMgr::instance().setFamily(AF_INET); + CfgHostsSubnet cfg_subnet4(cfg_hosts, SubnetID(2)); + runToElementTest("[ ]", cfg_subnet4); + + CfgMgr::instance().setFamily(AF_INET6); + CfgHostsSubnet cfg_subnet1(cfg_hosts, SubnetID(1)); + runToElementTest("[ ]", cfg_subnet1); } // This test verifies that an attempt to add two reservations with the @@ -236,6 +343,4 @@ TEST_F(HostReservationsListParserTest, duplicatedIdentifierValue6) { } } - - } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/srv_config_unittest.cc b/src/lib/dhcpsrv/tests/srv_config_unittest.cc index c2ab73ecc9..8b68a0c585 100644 --- a/src/lib/dhcpsrv/tests/srv_config_unittest.cc +++ b/src/lib/dhcpsrv/tests/srv_config_unittest.cc @@ -443,8 +443,6 @@ TEST_F(SrvConfigTest, unparse) { defaults += "\"expired-leases-processing\": "; defaults += conf.getCfgExpiration()->toElement()->str() + ",\n"; defaults += "\"lease-database\": { \"type\": \"memfile\" },\n"; - defaults += "\"hosts-database\": { },\n"; - defaults += "\"client-classes\": [ ],\n"; defaults += "\"hooks-libraries\": [ ],\n"; defaults += "\"dhcp-ddns\": \n"; defaults += conf.getD2ClientConfig()->toElement()->str() + ",\n"; -- cgit v1.2.3 From 406bb64b2086f5fab96a0aca1a5fb5db2c4a5205 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Mon, 6 Mar 2017 16:12:37 +0100 Subject: [fdunparse2] Some fixes, did D2, began CA --- configure.ac | 1 + src/bin/agent/ca_cfg_mgr.cc | 28 + src/bin/agent/ca_cfg_mgr.h | 11 + src/bin/d2/d2_cfg_mgr.cc | 46 + src/bin/d2/d2_cfg_mgr.h | 5 + src/bin/d2/d2_config.cc | 67 + src/bin/d2/d2_config.h | 36 +- src/bin/d2/tests/Makefile.am | 3 +- src/bin/d2/tests/get_config_unittest.cc | 260 + src/bin/d2/tests/testdata/get_config.json | 74 + src/bin/dhcp4/tests/get_config_unittest.cc | 5537 +++++++++++++++++++- src/lib/dhcpsrv/cfg_subnets4.cc | 2 +- src/lib/dhcpsrv/cfg_subnets6.cc | 2 +- src/lib/dhcpsrv/client_class_def.cc | 12 +- src/lib/dhcpsrv/logging_info.cc | 29 +- src/lib/dhcpsrv/logging_info.h | 7 +- src/lib/dhcpsrv/srv_config.cc | 19 +- src/lib/dhcpsrv/tests/client_class_def_unittest.cc | 4 +- src/lib/process/d_cfg_mgr.h | 14 +- src/lib/process/tests/d_cfg_mgr_unittests.cc | 4 +- src/lib/process/testutils/d_test_stubs.cc | 5 + src/lib/process/testutils/d_test_stubs.h | 5 + 22 files changed, 6134 insertions(+), 37 deletions(-) create mode 100644 src/bin/d2/tests/get_config_unittest.cc create mode 100644 src/bin/d2/tests/testdata/get_config.json (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/configure.ac b/configure.ac index f8fa48a12f..931f753cfc 100644 --- a/configure.ac +++ b/configure.ac @@ -1686,6 +1686,7 @@ AC_CONFIG_FILES([Makefile src/bin/agent/Makefile src/bin/agent/tests/Makefile src/bin/agent/tests/ca_process_tests.sh + src/bin/agent/tests/test_libraries.h src/bin/d2/Makefile src/bin/d2/tests/Makefile src/bin/d2/tests/d2_process_tests.sh diff --git a/src/bin/agent/ca_cfg_mgr.cc b/src/bin/agent/ca_cfg_mgr.cc index bde6859e44..8b5273ecd6 100644 --- a/src/bin/agent/ca_cfg_mgr.cc +++ b/src/bin/agent/ca_cfg_mgr.cc @@ -158,6 +158,34 @@ CtrlAgentCfgContext::setControlSocketInfo(const isc::data::ConstElementPtr& cont ctrl_sockets_[static_cast(type)] = control_socket; } +ElementPtr +CtrlAgentCfgContext::toElement() const { + ElementPtr result = Element::createMap(); + // Set http-host + result->set("http-host", Element::create(http_host_)); + // Set http-port + result->set("http-port", + Element::create(static_cast(http_port_))); + // hooks-libraries + result->set("hooks-libraries", hooks_config_.toElement()); + // control-sockets + ElementPtr control_sockets = Element::createMap(); + // dhcp4-server + if (ctrl_sockets_[TYPE_DHCP4]) { + control_sockets->set("dhcp4-server", ctrl_sockets_[TYPE_DHCP4]); + } + // dhcp6-server + if (ctrl_sockets_[TYPE_DHCP6]) { + control_sockets->set("dhcp6-server", ctrl_sockets_[TYPE_DHCP6]); + } + // d2-server + if (ctrl_sockets_[TYPE_D2]) { + control_sockets->set("d2-server", ctrl_sockets_[TYPE_D2]); + } + result->set("control-sockets", control_sockets); + + return (result); +} } // namespace isc::agent } // namespace isc diff --git a/src/bin/agent/ca_cfg_mgr.h b/src/bin/agent/ca_cfg_mgr.h index 46401a67d6..cb7fd54767 100644 --- a/src/bin/agent/ca_cfg_mgr.h +++ b/src/bin/agent/ca_cfg_mgr.h @@ -116,6 +116,17 @@ public: return (hooks_config_); } + /// @brief Unparse a configuration objet + /// + /// Returns an element which must parse into the same objet, i.e. + /// @code + /// for all valid config C parse(parse(C)->toElement()) == parse(C) + /// @endcode + /// + /// @return a pointer to a configuration which can be parsed into + /// the initial configuration object + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Private copy constructor diff --git a/src/bin/d2/d2_cfg_mgr.cc b/src/bin/d2/d2_cfg_mgr.cc index a7e49a3df5..d7900c94fe 100644 --- a/src/bin/d2/d2_cfg_mgr.cc +++ b/src/bin/d2/d2_cfg_mgr.cc @@ -13,6 +13,8 @@ #include +using namespace isc::asiolink; +using namespace isc::data; using namespace isc::process; namespace isc { @@ -51,6 +53,50 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) { D2CfgContext::~D2CfgContext() { } +ElementPtr +D2CfgContext::toElement() const { + ElementPtr d2 = Element::createMap(); + // Set ip-address + const IOAddress& ip_address = d2_params_->getIpAddress(); + d2->set("ip-address", Element::create(ip_address.toText())); + // Set port + size_t port = d2_params_->getPort(); + d2->set("port", Element::create(static_cast(port))); + // Set dns-server-timeout + size_t dns_server_timeout = d2_params_->getDnsServerTimeout(); + d2->set("dns-server-timeout", + Element::create(static_cast(dns_server_timeout))); + // Set ncr-protocol + const dhcp_ddns::NameChangeProtocol& ncr_protocol = + d2_params_->getNcrProtocol(); + d2->set("ncr-protocol", + Element::create(dhcp_ddns::ncrProtocolToString(ncr_protocol))); + // Set ncr-format + const dhcp_ddns::NameChangeFormat& ncr_format = d2_params_->getNcrFormat(); + d2->set("ncr-format", + Element::create(dhcp_ddns::ncrFormatToString(ncr_format))); + // Set forward-ddns + ElementPtr forward_ddns = Element::createMap(); + forward_ddns->set("ddns-domains", forward_mgr_->toElement()); + d2->set("forward-ddns", forward_ddns); + // Set reverse-ddns + ElementPtr reverse_ddns = Element::createMap(); + reverse_ddns->set("ddns-domains", reverse_mgr_->toElement()); + d2->set("reverse-ddns", reverse_ddns); + // Set tsig-keys + ElementPtr tsig_keys = Element::createList(); + for (TSIGKeyInfoMap::const_iterator key = keys_->begin(); + key != keys_->end(); ++key) { + tsig_keys->add(key->second->toElement()); + } + d2->set("tsig-keys", tsig_keys); + // Set DhcpDdns + ElementPtr result = Element::createMap(); + result->set("DhcpDdns", d2); + + return (result); +} + // *********************** D2CfgMgr ************************* const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa."; diff --git a/src/bin/d2/d2_cfg_mgr.h b/src/bin/d2/d2_cfg_mgr.h index e85ebcda7b..564540020a 100644 --- a/src/bin/d2/d2_cfg_mgr.h +++ b/src/bin/d2/d2_cfg_mgr.h @@ -90,6 +90,11 @@ public: keys_ = keys; } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + protected: /// @brief Copy constructor for use by derivations in clone(). D2CfgContext(const D2CfgContext& rhs); diff --git a/src/bin/d2/d2_config.cc b/src/bin/d2/d2_config.cc index 5b441fe43d..37dba622c6 100644 --- a/src/bin/d2/d2_config.cc +++ b/src/bin/d2/d2_config.cc @@ -20,6 +20,7 @@ #include using namespace isc::process; +using namespace isc::data; namespace isc { namespace d2 { @@ -180,6 +181,22 @@ TSIGKeyInfo::remakeKey() { } } +ElementPtr +TSIGKeyInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set name + result->set("name", Element::create(name_)); + // Set algorithm + result->set("algorithm", Element::create(algorithm_)); + // Set secret + result->set("secret", Element::create(secret_)); + // Set digest-bits + result->set("digest-bits", + Element::create(static_cast(digestbits_))); + + return (result); +} + // *********************** DnsServerInfo ************************* DnsServerInfo::DnsServerInfo(const std::string& hostname, isc::asiolink::IOAddress ip_address, uint32_t port, @@ -198,6 +215,19 @@ DnsServerInfo::toText() const { return (stream.str()); } +ElementPtr +DnsServerInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set hostname + result->set("hostname", Element::create(hostname_)); + // Set ip-address + result->set("ip-address", Element::create(ip_address_.toText())); + // Set port + result->set("port", Element::create(static_cast(port_))); + + return (result); +} + std::ostream& operator<<(std::ostream& os, const DnsServerInfo& server) { @@ -226,6 +256,30 @@ DdnsDomain::getKeyName() const { return (""); } +ElementPtr +DdnsDomain::toElement() const { + ElementPtr result = Element::createMap(); + // Set name + result->set("name", Element::create(name_)); + // Set servers + ElementPtr servers = Element::createList(); + for (DnsServerInfoStorage::const_iterator server = servers_->begin(); + server != servers_->end(); ++server) { + ElementPtr dns_server = (*server)->toElement(); + servers->add(dns_server); + } + // the dns server list may not be empty + if (!servers->empty()) { + result->set("dns-servers", servers); + } + // Set key-name + if (tsig_key_info_) { + result->set("key-name", Element::create(tsig_key_info_->getName())); + } + + return (result); +} + // *********************** DdnsDomainLstMgr ************************* const char* DdnsDomainListMgr::wildcard_domain_name_ = "*"; @@ -321,6 +375,19 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) { return (true); } +ElementPtr +DdnsDomainListMgr::toElement() const { + ElementPtr result = Element::createList(); + // Iterate on ddns domains + for (DdnsDomainMap::const_iterator domain = domains_->begin(); + domain != domains_->end(); ++domain) { + ElementPtr ddns_domain = domain->second->toElement(); + result->add(ddns_domain); + } + + return (result); +} + // *************************** PARSERS *********************************** // *********************** TSIGKeyInfoParser ************************* diff --git a/src/bin/d2/d2_config.h b/src/bin/d2/d2_config.h index 4dda6327bc..b70d1fcaed 100644 --- a/src/bin/d2/d2_config.h +++ b/src/bin/d2/d2_config.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -255,7 +256,7 @@ typedef boost::shared_ptr D2ParamsPtr; /// instance of the actual key (@ref isc::dns::TSIGKey) that can be used /// by the IO layer for signing and verifying messages. /// -class TSIGKeyInfo { +class TSIGKeyInfo : public isc::data::CfgToElement { public: /// @brief Defines string values for the supported TSIG algorithms //@{ @@ -356,6 +357,11 @@ public: static const dns::Name& stringToAlgorithmName(const std::string& algorithm_id); + /// @brief Unparse a configuration objet + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief Creates the actual TSIG key instance member /// @@ -405,7 +411,7 @@ typedef boost::shared_ptr TSIGKeyInfoMapPtr; /// belongs to a list of servers supporting DNS for a given domain. It will /// be used to establish communications with the server to carry out DNS /// updates. -class DnsServerInfo { +class DnsServerInfo : public isc::data::CfgToElement { public: /// @brief defines DNS standard port value static const uint32_t STANDARD_DNS_PORT = 53; @@ -472,6 +478,11 @@ public: /// @brief Returns a text representation for the server. std::string toText() const; + /// @brief Unparse a configuration objet + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief The resolvable name of the server. If not blank, then the @@ -510,7 +521,7 @@ typedef boost::shared_ptr DnsServerInfoStoragePtr; /// @todo Currently the name entry for a domain is just an std::string. It /// may be worthwhile to change this to a dns::Name for purposes of better /// validation and matching capabilities. -class DdnsDomain { +class DdnsDomain : public isc::data::CfgToElement { public: /// @brief Constructor /// @@ -553,6 +564,11 @@ public: return (tsig_key_info_); } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief The domain name of the domain. std::string name_; @@ -588,7 +604,7 @@ typedef boost::shared_ptr DdnsDomainMapPtr; /// specify the wild card domain as the only forward domain. All forward DNS /// updates would be sent to that one list of servers, regardless of the FQDN. /// As matching capabilities evolve this class is expected to expand. -class DdnsDomainListMgr { +class DdnsDomainListMgr : public isc::data::CfgToElement { public: /// @brief defines the domain name for denoting the wildcard domain. static const char* wildcard_domain_name_; @@ -655,6 +671,11 @@ public: /// set the internal wild card domain pointer accordingly. void setDomains(DdnsDomainMapPtr domains); + /// @brief Unparse a configuration objet + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + private: /// @brief An arbitrary label assigned to this manager. std::string name_; @@ -697,6 +718,13 @@ public: return (process::DCfgContextBasePtr(new DScalarContext(*this))); } + /// @brief Unparse a configuration objet + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const { + isc_throw(isc::NotImplemented, "DScalarContext::ElementPtr"); + } + protected: /// @brief Copy constructor DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs) { diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am index 14b8ea06f0..67b9e0b219 100644 --- a/src/bin/d2/tests/Makefile.am +++ b/src/bin/d2/tests/Makefile.am @@ -6,7 +6,7 @@ SHTESTS += d2_process_tests.sh noinst_SCRIPTS = d2_process_tests.sh EXTRA_DIST = d2_process_tests.sh.in -EXTRA_DIST += testdata/d2_cfg_tests.json +EXTRA_DIST += testdata/d2_cfg_tests.json testdata/get_config.json # test using command-line arguments, so use check-local target instead of TESTS check-local: @@ -58,6 +58,7 @@ d2_unittests_SOURCES += nc_trans_unittests.cc d2_unittests_SOURCES += d2_controller_unittests.cc d2_unittests_SOURCES += d2_simple_parser_unittest.cc d2_unittests_SOURCES += parser_unittest.cc parser_unittest.h +d2_unittests_SOURCES += get_config_unittest.cc d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) diff --git a/src/bin/d2/tests/get_config_unittest.cc b/src/bin/d2/tests/get_config_unittest.cc new file mode 100644 index 0000000000..6b72367601 --- /dev/null +++ b/src/bin/d2/tests/get_config_unittest.cc @@ -0,0 +1,260 @@ +// Copyright (C) 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "test_data_files_config.h" + +using namespace isc::config; +using namespace isc::d2; +using namespace isc::data; +using namespace isc::process; + +namespace { + +/// @name How to generate the testdata/get_config.json file +/// +/// Define GENERATE_ACTION and recompile. Run d2_unittests on +/// D2GetConfigTest redirecting the standard error to a temporary +/// file, e.g. by +/// @code +/// ./d2_unittests --gtest_filter="D2GetConfig*" > /dev/null 2> u +/// @endcode +/// +/// Update testdata/get_config.json using the temporary file content, +/// recompile without GENERATE_ACTION. + +/// @brief the generate action +/// false means do nothing, true means unparse extracted configurations +#ifdef GENERATE_ACTION +const bool generate_action = true; +#else +const bool generate_action = false; +#endif + +/// @brief Read a file into a string +std::string +readFile(const std::string& file_path) { + std::ifstream ifs(file_path); + if (!ifs.is_open()) { + ADD_FAILURE() << "readFile cannot open " << file_path; + isc_throw(isc::Unexpected, "readFile cannot open " << file_path); + } + std::string lines; + std::string line; + while (std::getline(ifs, line)) { + lines += line + "\n"; + } + ifs.close(); + return (lines); +} + +/// @brief Runs parser in JSON mode +ElementPtr +parseJSON(const std::string& in, bool verbose = false) { + try { + D2ParserContext ctx; + return (ctx.parseString(in, D2ParserContext::PARSER_JSON)); + } catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +/// @brief Runs parser in DHCPDDNS mode +ElementPtr +parseDHCPDDNS(const std::string& in, bool verbose = false) { + try { + D2ParserContext ctx; + return (ctx.parseString(in, D2ParserContext::PARSER_DHCPDDNS)); + } catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +} + +/// Test fixture class +class D2GetConfigTest : public ConfigParseTest { +public: + D2GetConfigTest() + : rcode_(-1) { + srv_.reset(new D2CfgMgr()); + // Create fresh context. + resetConfiguration(); + } + + ~D2GetConfigTest() { + resetConfiguration(); + } + + /// @brief Parse and Execute configuration + /// + /// Parses a configuration and executes a configuration of the server. + /// If the operation fails, the current test will register a failure. + /// + /// @param config Configuration to parse + /// @param operation Operation being performed. In the case of an error, + /// the error text will include the string "unable to .". + /// + /// @return true if the configuration succeeded, false if not. + bool + executeConfiguration(const std::string& config, const char* operation) { + // try JSON parser + ConstElementPtr json; + try { + json = parseJSON(config, true); + } catch (const std::exception& ex) { + ADD_FAILURE() << "invalid JSON for " << operation + << " failed with " << ex.what() + << " on\n" << config << "\n"; + return (false); + } + + // try DHCPDDNS parser + try { + json = parseDHCPDDNS(config, true); + } catch (...) { + ADD_FAILURE() << "parsing failed for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // get DhcpDdns element + ConstElementPtr d2 = json->get("DhcpDdns"); + if (!d2) { + ADD_FAILURE() << "cannot get DhcpDdns for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // try DHCPDDNS configure + ConstElementPtr status; + try { + status = srv_->parseConfig(d2); + } catch (const std::exception& ex) { + ADD_FAILURE() << "configure for " << operation + << " failed with " << ex.what() + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // The status object must not be NULL + if (!status) { + ADD_FAILURE() << "configure for " << operation + << " returned null on\n" + << prettyPrint(json) << "\n"; + return (false); + } + + // Returned value should be 0 (configuration success) + comment_ = parseAnswer(rcode_, status); + if (rcode_ != 0) { + string reason = ""; + if (comment_) { + reason = string(" (") + comment_->stringValue() + string(")"); + } + ADD_FAILURE() << "configure for " << operation + << " returned error code " + << rcode_ << reason << " on\n" + << prettyPrint(json) << "\n"; + return (false); + } + return (true); + } + + /// @brief Reset configuration database. + /// + /// This function resets configuration data base by + /// removing control sockets and domain lists. Reset must + /// be performed after each test to make sure that + /// contents of the database do not affect result of + /// subsequent tests. + void resetConfiguration() { + string config = "{ \"DhcpDdns\": {" + " \"ip-address\": \"127.0.0.1\"," + " \"port\": 53001," + " \"dns-server-timeout\": 100," + " \"ncr-protocol\": \"UDP\"," + " \"ncr-format\": \"JSON\"," + " \"tsig-keys\": [ ]," + " \"forward-ddns\": { }," + " \"reverse-ddns\": { } } }"; + EXPECT_TRUE(executeConfiguration(config, "reset config")); + } + + boost::scoped_ptr srv_; ///< D2 server under test + int rcode_; ///< Return code from element parsing + ConstElementPtr comment_; ///< Reason for parse fail +}; + +/// Test a configuration +TEST_F(D2GetConfigTest, sample1) { + + // get the sample1 configuration + std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json"; + std::string config; + ASSERT_NO_THROW(config = readFile(sample1_file)); + + // get the expected configuration + std::string expected_file = + std::string(D2_TEST_DATA_DIR) + "/" + "get_config.json"; + std::string expected; + ASSERT_NO_THROW(expected = readFile(expected_file)); + + // execute the sample configuration + ASSERT_TRUE(executeConfiguration(config, "sample1 config")); + + // unparse it + D2CfgContextPtr context = srv_->getD2CfgContext(); + ConstElementPtr unparsed; + ASSERT_NO_THROW(unparsed = context->toElement()); + + // dump if wanted else check + if (generate_action) { + std::cerr << "/ Generated Configuration (remove this line)\n"; + ASSERT_NO_THROW(expected = prettyPrint(unparsed)); + prettyPrint(unparsed, std::cerr, 0, 4); + std::cerr << "\n"; + } else { + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCPDDNS(expected, true)); + EXPECT_TRUE(isEquivalent(unparsed, json)); + std::string current = prettyPrint(unparsed, 0, 4) + "\n"; + EXPECT_EQ(expected, current); + if (expected != current) { + expected = current; + } + } + + // execute the d2 configuration + EXPECT_TRUE(executeConfiguration(expected, "unparsed config")); + + // is it a fixed point? + D2CfgContextPtr context2 = srv_->getD2CfgContext(); + ConstElementPtr unparsed2; + ASSERT_NO_THROW(unparsed2 = context2->toElement()); + ASSERT_TRUE(unparsed2); + EXPECT_TRUE(isEquivalent(unparsed, unparsed2)); +} diff --git a/src/bin/d2/tests/testdata/get_config.json b/src/bin/d2/tests/testdata/get_config.json new file mode 100644 index 0000000000..89e7326b4e --- /dev/null +++ b/src/bin/d2/tests/testdata/get_config.json @@ -0,0 +1,74 @@ +{ + "DhcpDdns": { + "dns-server-timeout": 1000, + "forward-ddns": { + "ddns-domains": [ + { + "dns-servers": [ + { + "hostname": "", + "ip-address": "172.16.1.1", + "port": 53 + } + ], + "key-name": "d2.md5.key", + "name": "four.example.com." + }, + { + "dns-servers": [ + { + "hostname": "", + "ip-address": "2001:db8:1::10", + "port": 7802 + } + ], + "name": "six.example.com." + } + ] + }, + "ip-address": "172.16.1.10", + "ncr-format": "JSON", + "ncr-protocol": "UDP", + "port": 53001, + "reverse-ddns": { + "ddns-domains": [ + { + "dns-servers": [ + { + "hostname": "", + "ip-address": "172.16.1.1", + "port": 53001 + }, + { + "hostname": "", + "ip-address": "192.168.2.10", + "port": 53 + } + ], + "key-name": "d2.sha1.key", + "name": "2.0.192.in-addr.arpa." + } + ] + }, + "tsig-keys": [ + { + "algorithm": "HMAC-MD5", + "digest-bits": 0, + "name": "d2.md5.key", + "secret": "LSWXnfkKZjdPJI5QxlpnfQ==" + }, + { + "algorithm": "HMAC-SHA1", + "digest-bits": 0, + "name": "d2.sha1.key", + "secret": "hRrp29wzUv3uzSNRLlY68w==" + }, + { + "algorithm": "HMAC-SHA512", + "digest-bits": 256, + "name": "d2.sha512.key", + "secret": "/4wklkm04jeH4anx2MKGJLcya+ZLHldL5d6mK+4q6UXQP7KJ9mS2QG29hh0SJR4LA0ikxNJTUMvir42gLx6fGQ==" + } + ] + } +} diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc index 59ac527a1d..d260842234 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -65,12 +65,5545 @@ namespace { ///@{ /// @brief extracted configurations const char* EXTRACTED_CONFIGS[] = { - // "to be replaced" + // CONFIGURATION 0 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [ ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 1 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 2 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 3 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 4 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"id\": 0,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101 - 192.0.3.150\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" },\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.101 - 192.0.4.150\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.4.0/24\"\n" +" },\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.5.101 - 192.0.5.150\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.5.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 5 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"id\": 1024,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"id\": 100,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101 - 192.0.3.150\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" },\n" +" {\n" +" \"id\": 1,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.101 - 192.0.4.150\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.4.0/24\"\n" +" },\n" +" {\n" +" \"id\": 34,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.5.101 - 192.0.5.150\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.5.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 6 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"next-server\": \"1.2.3.4\",\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 7 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"next-server\": \"1.2.3.4\",\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 8 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"next-server\": \"192.0.0.1\",\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"next-server\": \"1.2.3.4\",\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 9 +"{\n" +" \"echo-client-id\": false,\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 10 +"{\n" +" \"echo-client-id\": true,\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 11 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"match-client-id\": true,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"match-client-id\": false,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.1 - 192.0.3.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 12 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"match-client-id\": true,\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"match-client-id\": false,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.1 - 192.0.3.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 13 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2,\n" +" \"renew-timer\": 1,\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 14 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\"\n" +" },\n" +" {\n" +" \"pool\": \"192.0.2.200-192.0.2.255\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.0/25\"\n" +" },\n" +" {\n" +" \"pool\": \"192.0.3.128/25\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 15 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.128/28\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 16 +"{\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 100,\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 17 +"{\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 100,\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"uint16, ipv4-address, ipv6-address, string\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"record\"\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 18 +"{\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 100,\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"code\": 101,\n" +" \"name\": \"foo-2\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 19 +"{\n" +" \"option-def\": [\n" +" {\n" +" \"array\": true,\n" +" \"code\": 100,\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 20 +"{\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 100,\n" +" \"encapsulate\": \"sub-opts-space\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 21 +"{\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 109,\n" +" \"name\": \"foo\",\n" +" \"space\": \"dhcp4\",\n" +" \"type\": \"string\"\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 22 +"{\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 213,\n" +" \"name\": \"access-network-domain-name\",\n" +" \"space\": \"dhcp4\",\n" +" \"type\": \"string\"\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 23 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\"\n" +" },\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 24 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\"\n" +" },\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 25 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\"\n" +" },\n" +" {\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 56,\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 26 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" },\n" +" {\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 1,\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"name\": \"foo2\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 27 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"data\": \"11\",\n" +" \"name\": \"base-option\"\n" +" },\n" +" {\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" },\n" +" {\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 222,\n" +" \"encapsulate\": \"isc\",\n" +" \"name\": \"base-option\",\n" +" \"space\": \"dhcp4\",\n" +" \"type\": \"uint8\"\n" +" },\n" +" {\n" +" \"code\": 1,\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"name\": \"foo2\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 3000\n" +" }\n", + // CONFIGURATION 28 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"AB\",\n" +" \"name\": \"dhcp-message\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\"\n" +" },\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 29 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"0102030405060708090A\",\n" +" \"name\": \"dhcp-message\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"FF\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101 - 192.0.3.150\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 30 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" },\n" +" {\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 1,\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"name\": \"foo2\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 31 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"name\": \"vendor-encapsulated-options\"\n" +" },\n" +" {\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 1,\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"name\": \"foo2\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 3000\n" +" }\n", + // CONFIGURATION 32 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 100,\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"option-one\",\n" +" \"space\": \"vendor-4491\"\n" +" },\n" +" {\n" +" \"code\": 100,\n" +" \"csv-format\": false,\n" +" \"data\": \"1234\",\n" +" \"name\": \"option-two\",\n" +" \"space\": \"vendor-1234\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.10\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 33 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 100,\n" +" \"data\": \"this is a string vendor-opt\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-4491\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 100,\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-4491\",\n" +" \"type\": \"string\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 34 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"eth0\", \"eth1\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 35 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"eth0\", \"*\", \"eth1\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 36 +"{\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": true,\n" +" \"enable-updates\": true,\n" +" \"generated-prefix\": \"test.prefix\",\n" +" \"max-queue-size\": 2048,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": true,\n" +" \"override-no-update\": true,\n" +" \"qualifying-suffix\": \"test.suffix.\",\n" +" \"replace-client-name\": \"when-present\",\n" +" \"sender-ip\": \"192.168.2.2\",\n" +" \"sender-port\": 778,\n" +" \"server-ip\": \"192.168.2.1\",\n" +" \"server-port\": 777\n" +" },\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 37 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2,\n" +" \"relay\": {\n" +" \"ip-address\": \"192.0.2.123\"\n" +" },\n" +" \"renew-timer\": 1,\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 38 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"id\": 123,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"id\": 234,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101 - 192.0.3.150\"\n" +" }\n" +" ],\n" +" \"reservations\": [\n" +" {\n" +" \"duid\": \"01:02:03:04:05:06:07:08:09:0A\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.3.112\",\n" +" \"option-data\": [\n" +" {\n" +" \"data\": \"192.0.3.15\",\n" +" \"name\": \"name-servers\"\n" +" },\n" +" {\n" +" \"data\": \"32\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ]\n" +" },\n" +" {\n" +" \"hostname\": \"\",\n" +" \"hw-address\": \"01:02:03:04:05:06\",\n" +" \"ip-address\": \"192.0.3.120\",\n" +" \"option-data\": [\n" +" {\n" +" \"data\": \"192.0.3.95\",\n" +" \"name\": \"name-servers\"\n" +" },\n" +" {\n" +" \"data\": \"11\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ]\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" },\n" +" {\n" +" \"id\": 542,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.101 - 192.0.4.150\"\n" +" }\n" +" ],\n" +" \"reservations\": [\n" +" {\n" +" \"duid\": \"0A:09:08:07:06:05:04:03:02:01\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.4.101\",\n" +" \"option-data\": [\n" +" {\n" +" \"data\": \"192.0.4.11\",\n" +" \"name\": \"name-servers\"\n" +" },\n" +" {\n" +" \"data\": \"95\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ]\n" +" },\n" +" {\n" +" \"circuit-id\": \"060504030201\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.4.102\"\n" +" },\n" +" {\n" +" \"client-id\": \"05:01:02:03:04:05:06\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.4.103\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.4.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 39 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"option-def\": [\n" +" {\n" +" \"code\": 100,\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"id\": 234,\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101 - 192.0.3.150\"\n" +" }\n" +" ],\n" +" \"reservations\": [\n" +" {\n" +" \"duid\": \"01:02:03:04:05:06:07:08:09:0A\",\n" +" \"ip-address\": \"192.0.3.112\",\n" +" \"option-data\": [\n" +" {\n" +" \"data\": \"123\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ]\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 40 +"{\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"reservation-mode\": \"all\",\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" },\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.0/24\"\n" +" }\n" +" ],\n" +" \"reservation-mode\": \"out-of-pool\",\n" +" \"subnet\": \"192.0.3.0/24\"\n" +" },\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.0/24\"\n" +" }\n" +" ],\n" +" \"reservation-mode\": \"disabled\",\n" +" \"subnet\": \"192.0.4.0/24\"\n" +" },\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.5.0/24\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.5.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 41 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 42 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 43 +"{\n" +" \"decline-probation-period\": 12345,\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 44 +"{\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 35,\n" +" \"hold-reclaimed-time\": 1800,\n" +" \"max-reclaim-leases\": 50,\n" +" \"max-reclaim-time\": 100,\n" +" \"reclaim-timer-wait-time\": 20,\n" +" \"unwarned-reclaim-cycles\": 10\n" +" },\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 45 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 46 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-subnet\": \"2001:db8::123/45\",\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 47 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"ethX\",\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 48 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"ethX\",\n" +" \"4o6-subnet\": \"2001:db8::543/21\",\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 49 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface-id\": \"vlan123\",\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 50 +"{\n" +" \"client-classes\": [\n" +" {\n" +" \"name\": \"one\"\n" +" },\n" +" {\n" +" \"name\": \"two\"\n" +" },\n" +" {\n" +" \"name\": \"three\"\n" +" }\n" +" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 51 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 52 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\",\n" +" \"user-context\": { }\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 53 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\",\n" +" \"user-context\": {\n" +" \"bool-param\": true,\n" +" \"integer-param\": 42,\n" +" \"string-param\": \"Sagittarius\"\n" +" }\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 54 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0 - 192.0.2.15\",\n" +" \"user-context\": {\n" +" \"bool-param\": true,\n" +" \"integer-param\": 42,\n" +" \"string-param\": \"Sagittarius\"\n" +" }\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n" }; /// @brief unparsed configurations const char* UNPARSED_CONFIGS[] = { - // "to be replaced" + // CONFIGURATION 0 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 1 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 900,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 2 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 1800,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 3 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 4 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 2,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101-192.0.3.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 3,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.101-192.0.4.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.4.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 4,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.5.101-192.0.5.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.5.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 5 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1024,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 100,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101-192.0.3.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.101-192.0.4.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.4.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 34,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.5.101-192.0.5.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.5.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 6 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"1.2.3.4\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 7 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"1.2.3.4\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 8 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"1.2.3.4\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 9 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": false,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 10 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 11 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 2,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": false,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.1-192.0.3.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 12 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": false,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 2,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.1-192.0.3.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 13 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 14 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\"\n" +" },\n" +" {\n" +" \"pool\": \"192.0.2.200-192.0.2.255\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 2,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.0/25\"\n" +" },\n" +" {\n" +" \"pool\": \"192.0.3.128/25\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 15 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.128/28\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 16 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 100,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 17 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 100,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"uint16, ipv4-address, ipv6-address, string\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"record\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 18 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 100,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"array\": false,\n" +" \"code\": 101,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo-2\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 19 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": true,\n" +" \"code\": 100,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 20 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 100,\n" +" \"encapsulate\": \"sub-opts-space\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 21 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 109,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"dhcp4\",\n" +" \"type\": \"string\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 22 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 213,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"access-network-domain-name\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"dhcp4\",\n" +" \"type\": \"string\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 23 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 56,\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 23,\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 24 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 56,\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 23,\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 25 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 56,\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 56,\n" +" \"csv-format\": true,\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 56,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 26 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 1,\n" +" \"csv-format\": true,\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 1,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"array\": false,\n" +" \"code\": 2,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo2\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 27 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 222,\n" +" \"csv-format\": true,\n" +" \"data\": \"11\",\n" +" \"name\": \"base-option\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 1,\n" +" \"csv-format\": true,\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 222,\n" +" \"encapsulate\": \"isc\",\n" +" \"name\": \"base-option\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"dhcp4\",\n" +" \"type\": \"uint8\"\n" +" },\n" +" {\n" +" \"array\": false,\n" +" \"code\": 1,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"array\": false,\n" +" \"code\": 2,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo2\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 3000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 28 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 56,\n" +" \"csv-format\": false,\n" +" \"data\": \"AB\",\n" +" \"name\": \"dhcp-message\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 56,\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 23,\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 29 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 56,\n" +" \"csv-format\": false,\n" +" \"data\": \"0102030405060708090A\",\n" +" \"name\": \"dhcp-message\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 2,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 23,\n" +" \"csv-format\": false,\n" +" \"data\": \"FF\",\n" +" \"name\": \"default-ip-ttl\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101-192.0.3.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 30 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 1,\n" +" \"csv-format\": true,\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 1,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"array\": false,\n" +" \"code\": 2,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo2\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 31 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 43,\n" +" \"csv-format\": false,\n" +" \"data\": \"0104000004D20204C0A80201\",\n" +" \"name\": \"vendor-encapsulated-options\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 1,\n" +" \"csv-format\": true,\n" +" \"data\": \"1234\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" },\n" +" {\n" +" \"code\": 2,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.168.2.1\",\n" +" \"name\": \"foo2\",\n" +" \"space\": \"vendor-encapsulated-options-space\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 1,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"uint32\"\n" +" },\n" +" {\n" +" \"array\": false,\n" +" \"code\": 2,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo2\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"vendor-encapsulated-options-space\",\n" +" \"type\": \"ipv4-address\"\n" +" }\n" +" ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 3000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 32 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 100,\n" +" \"csv-format\": false,\n" +" \"data\": \"1234\",\n" +" \"space\": \"vendor-1234\"\n" +" },\n" +" {\n" +" \"code\": 100,\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"space\": \"vendor-4491\"\n" +" }\n" +" ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.10\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 33 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 100,\n" +" \"csv-format\": true,\n" +" \"data\": \"this is a string vendor-opt\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"vendor-4491\"\n" +" }\n" +" ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 100,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"vendor-4491\",\n" +" \"type\": \"string\"\n" +" }\n" +" ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 34 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"eth0\", \"eth1\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 35 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\", \"eth0\", \"eth1\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 36 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": true,\n" +" \"enable-updates\": true,\n" +" \"generated-prefix\": \"test.prefix\",\n" +" \"max-queue-size\": 2048,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": true,\n" +" \"override-no-update\": true,\n" +" \"qualifying-suffix\": \"test.suffix.\",\n" +" \"replace-client-name\": \"when-present\",\n" +" \"sender-ip\": \"192.168.2.2\",\n" +" \"sender-port\": 778,\n" +" \"server-ip\": \"192.168.2.1\",\n" +" \"server-port\": 777\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 37 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2,\n" +" \"relay\": {\n" +" \"ip-address\": \"192.0.2.123\"\n" +" },\n" +" \"renew-timer\": 1,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 38 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 123,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 234,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101-192.0.3.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"client-classes\": [ ],\n" +" \"hostname\": \"\",\n" +" \"hw-address\": \"01:02:03:04:05:06\",\n" +" \"ip-address\": \"192.0.3.120\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 5,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.0.3.95\",\n" +" \"name\": \"name-servers\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 23,\n" +" \"csv-format\": true,\n" +" \"data\": \"11\",\n" +" \"name\": \"default-ip-ttl\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"server-hostname\": \"\"\n" +" },\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"client-classes\": [ ],\n" +" \"duid\": \"01:02:03:04:05:06:07:08:09:0a\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.3.112\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 5,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.0.3.15\",\n" +" \"name\": \"name-servers\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 23,\n" +" \"csv-format\": true,\n" +" \"data\": \"32\",\n" +" \"name\": \"default-ip-ttl\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"server-hostname\": \"\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 542,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.101-192.0.4.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"client-classes\": [ ],\n" +" \"client-id\": \"05010203040506\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.4.103\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"server-hostname\": \"\"\n" +" },\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"circuit-id\": \"060504030201\",\n" +" \"client-classes\": [ ],\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.4.102\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"server-hostname\": \"\"\n" +" },\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"client-classes\": [ ],\n" +" \"duid\": \"0a:09:08:07:06:05:04:03:02:01\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.4.101\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 5,\n" +" \"csv-format\": true,\n" +" \"data\": \"192.0.4.11\",\n" +" \"name\": \"name-servers\",\n" +" \"space\": \"dhcp4\"\n" +" },\n" +" {\n" +" \"code\": 23,\n" +" \"csv-format\": true,\n" +" \"data\": \"95\",\n" +" \"name\": \"default-ip-ttl\",\n" +" \"space\": \"dhcp4\"\n" +" }\n" +" ],\n" +" \"server-hostname\": \"\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.4.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 39 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [\n" +" {\n" +" \"array\": false,\n" +" \"code\": 100,\n" +" \"encapsulate\": \"\",\n" +" \"name\": \"foo\",\n" +" \"record-types\": \"\",\n" +" \"space\": \"isc\",\n" +" \"type\": \"uint32\"\n" +" }\n" +" ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 234,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.101-192.0.3.150\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"client-classes\": [ ],\n" +" \"duid\": \"01:02:03:04:05:06:07:08:09:0a\",\n" +" \"hostname\": \"\",\n" +" \"ip-address\": \"192.0.3.112\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [\n" +" {\n" +" \"code\": 100,\n" +" \"csv-format\": true,\n" +" \"data\": \"123\",\n" +" \"name\": \"foo\",\n" +" \"space\": \"isc\"\n" +" }\n" +" ],\n" +" \"server-hostname\": \"\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 40 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 2,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.3.0/24\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"out-of-pool\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.3.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 3,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.4.0/24\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"disabled\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.4.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" },\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 4,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.5.0/24\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.5.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 41 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 42 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 43 +"{\n" +" \"decline-probation-period\": 12345,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 44 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 35,\n" +" \"hold-reclaimed-time\": 1800,\n" +" \"max-reclaim-leases\": 50,\n" +" \"max-reclaim-time\": 100,\n" +" \"reclaim-timer-wait-time\": 20,\n" +" \"unwarned-reclaim-cycles\": 10\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [ ]\n" +" }\n", + // CONFIGURATION 45 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 46 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"2001:db8::123/45\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 47 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"ethX\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 48 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"ethX\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"2001:db8::543/21\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 49 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"vlan123\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 50 +"{\n" +" \"client-classes\": [\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"name\": \"one\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"server-hostname\": \"\"\n" +" },\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"name\": \"three\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"server-hostname\": \"\"\n" +" },\n" +" {\n" +" \"boot-file-name\": \"\",\n" +" \"name\": \"two\",\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"server-hostname\": \"\"\n" +" }\n" +" ],\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 51 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 52 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\",\n" +" \"user-context\": { }\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 53 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\",\n" +" \"user-context\": {\n" +" \"bool-param\": true,\n" +" \"integer-param\": 42,\n" +" \"string-param\": \"Sagittarius\"\n" +" }\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 54 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.0/28\",\n" +" \"user-context\": {\n" +" \"bool-param\": true,\n" +" \"integer-param\": 42,\n" +" \"string-param\": \"Sagittarius\"\n" +" }\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n" }; /// @brief the number of configurations diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index e00ce13445..425c5faa0b 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -367,7 +367,7 @@ CfgSubnets4::toElement() const { // Set client-class const ClientClasses& cclasses = (*subnet)->getClientClasses(); if (cclasses.size() > 1) { - isc_throw(ToElementError, "client-classes has too many items: " + isc_throw(ToElementError, "client-class has too many items: " << cclasses.size()); } else if (!cclasses.empty()) { map->set("client-class", Element::create(*cclasses.cbegin())); diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index b74e70e0e2..2407976bdb 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -392,7 +392,7 @@ CfgSubnets6::toElement() const { // Set client-class const ClientClasses& cclasses = (*subnet)->getClientClasses(); if (cclasses.size() > 1) { - isc_throw(ToElementError, "client-classes has too many items: " + isc_throw(ToElementError, "client-class has too many items: " << cclasses.size()); } else if (!cclasses.empty()) { map->set("client-class", Element::create(*cclasses.cbegin())); diff --git a/src/lib/dhcpsrv/client_class_def.cc b/src/lib/dhcpsrv/client_class_def.cc index 03bd6d5393..135982d708 100644 --- a/src/lib/dhcpsrv/client_class_def.cc +++ b/src/lib/dhcpsrv/client_class_def.cc @@ -5,6 +5,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include +#include #include using namespace isc::data; @@ -112,13 +113,20 @@ ClientClassDef::equals(const ClientClassDef& other) const { ElementPtr ClientClassDef:: toElement() const { + uint16_t family = CfgMgr::instance().getFamily(); ElementPtr result = Element::createMap(); // Set name result->set("name", Element::create(name_)); - // Set original match expression - result->set("test", Element::create(test_)); + // Set original match expression (empty string won't parse) + if (!test_.empty()) { + result->set("test", Element::create(test_)); + } // Set option-data result->set("option-data", cfg_option_->toElement()); + if (family != AF_INET) { + // Other parameters are DHCPv4 specific + return (result); + } // Set next-server result->set("next-server", Element::create(next_server_.toText())); // Set server-hostname diff --git a/src/lib/dhcpsrv/logging_info.cc b/src/lib/dhcpsrv/logging_info.cc index 2872a7e8ef..12f5c21735 100644 --- a/src/lib/dhcpsrv/logging_info.cc +++ b/src/lib/dhcpsrv/logging_info.cc @@ -23,6 +23,22 @@ LoggingDestination::equals(const LoggingDestination& other) const { flush_ == other.flush_); } +ElementPtr +LoggingDestination::toElement() const { + ElementPtr result = Element::createMap(); + + // Set output + result->set("output", Element::create(output_)); + // Set maxver + result->set("maxver", Element::create(maxver_)); + // Set maxsize + result->set("maxsize", Element::create(static_cast(maxsize_))); + // Set flush + result->set("flush", Element::create(flush_)); + + return(result); +} + LoggingInfo::LoggingInfo() : name_("kea"), severity_(isc::log::INFO), debuglevel_(0) { // If configuration Manager is in the verbose mode, we need to modify the @@ -148,18 +164,7 @@ LoggingInfo::toElement() const { for (std::vector::const_iterator dest = destinations_.cbegin(); dest != destinations_.cend(); ++dest) { - ElementPtr map = Element::createMap(); - // Set output - map->set("output", Element::create(dest->output_)); - // Set maxver - map->set("maxver", Element::create(dest->maxver_)); - // Set maxsize - map->set("maxsize", - Element::create(static_cast(dest->maxsize_))); - // Set flush - map->set("flush", Element::create(dest->flush_)); - // Push on output option list - options->add(map); + options->add(dest->toElement()); } result->set("output_options", options); // Set severity diff --git a/src/lib/dhcpsrv/logging_info.h b/src/lib/dhcpsrv/logging_info.h index fb6edf2d31..25f113e190 100644 --- a/src/lib/dhcpsrv/logging_info.h +++ b/src/lib/dhcpsrv/logging_info.h @@ -19,7 +19,7 @@ namespace dhcp { /// @brief Defines single logging destination /// /// This structure is used to keep log4cplus configuration parameters. -struct LoggingDestination { +struct LoggingDestination : public isc::data::CfgToElement { /// @brief defines logging destination output /// @@ -47,6 +47,11 @@ struct LoggingDestination { LoggingDestination() : output_("stdout"), maxver_(1), maxsize_(204800), flush_(true) { } + + /// @brief Unparse a configuration objet + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; }; /// @brief structure that describes one logging entry diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 8cf0291a27..f0412f91e8 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -306,15 +306,16 @@ SrvConfig::toElement() const { // Set DhcpX result->set(family == AF_INET ? "Dhcp4" : "Dhcp6", dhcp); - // Logging global map (skip if loggers is empty) - ElementPtr logging = Element::createMap(); - // Set loggers list - ElementPtr loggers = Element::createList(); - for (LoggingInfoStorage::const_iterator logger = logging_info_.cbegin(); - logger != logging_info_.cend(); ++logger) { - loggers->add(logger->toElement()); - } - if (!loggers->empty()) { + // Logging global map (skip if empty) + if (!logging_info_.empty()) { + ElementPtr logging = Element::createMap(); + // Set loggers list + ElementPtr loggers = Element::createList(); + for (LoggingInfoStorage::const_iterator logger = + logging_info_.cbegin(); + logger != logging_info_.cend(); ++logger) { + loggers->add(logger->toElement()); + } logging->set("loggers", loggers); result->set("Logging", logging); } diff --git a/src/lib/dhcpsrv/tests/client_class_def_unittest.cc b/src/lib/dhcpsrv/tests/client_class_def_unittest.cc index e3a8a55c24..ffd716cab6 100644 --- a/src/lib/dhcpsrv/tests/client_class_def_unittest.cc +++ b/src/lib/dhcpsrv/tests/client_class_def_unittest.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -357,6 +358,7 @@ TEST(ClientClassDef, fixedFieldsBasics) { // Verifies the unparse method of option class definitions TEST(ClientClassDef, unparseDef) { + CfgMgr::instance().setFamily(AF_INET); boost::scoped_ptr cclass; // Get a client class definition and fill it @@ -385,6 +387,7 @@ TEST(ClientClassDef, unparseDef) { // Verifies the unparse method of client class dictionaries TEST(ClientClassDictionary, unparseDict) { + CfgMgr::instance().setFamily(AF_INET); ClientClassDictionaryPtr dictionary; ExpressionPtr expr; CfgOptionPtr options; @@ -400,7 +403,6 @@ TEST(ClientClassDictionary, unparseDict) { [](std::string name) { return ("{\n" "\"name\": \"" + name + "\",\n" - "\"test\": \"\",\n" "\"next-server\": \"0.0.0.0\",\n" "\"server-hostname\": \"\",\n" "\"boot-file-name\": \"\",\n" diff --git a/src/lib/process/d_cfg_mgr.h b/src/lib/process/d_cfg_mgr.h index 01904f9fae..da241dd9a4 100644 --- a/src/lib/process/d_cfg_mgr.h +++ b/src/lib/process/d_cfg_mgr.h @@ -8,6 +8,7 @@ #define D_CFG_MGR_H #include +#include #include #include @@ -57,7 +58,7 @@ typedef boost::shared_ptr DCfgContextBasePtr; /// // Restore from backup /// context_ = backup_copy; /// -class DCfgContextBase { +class DCfgContextBase : public isc::data::CfgToElement { public: /// @brief Indicator that a configuration parameter is optional. static const bool OPTIONAL = true; @@ -180,6 +181,17 @@ public: /// @return returns a pointer to the new clone. virtual DCfgContextBasePtr clone() = 0; + /// @brief Unparse a configuration objet + /// + /// Returns an element which must parse into the same objet, i.e. + /// @code + /// for all valid config C parse(parse(C)->toElement()) == parse(C) + /// @endcode + /// + /// @return a pointer to a configuration which can be parsed into + /// the initial configuration object + virtual isc::data::ElementPtr toElement() const = 0; + protected: /// @brief Copy constructor for use by derivations in clone(). DCfgContextBase(const DCfgContextBase& rhs); diff --git a/src/lib/process/tests/d_cfg_mgr_unittests.cc b/src/lib/process/tests/d_cfg_mgr_unittests.cc index 6df2742460..b060b6a517 100644 --- a/src/lib/process/tests/d_cfg_mgr_unittests.cc +++ b/src/lib/process/tests/d_cfg_mgr_unittests.cc @@ -54,8 +54,8 @@ public: /// @brief Test fixture class for testing DCfgMgrBase class. /// It maintains an member instance of DStubCfgMgr and derives from /// ConfigParseTest fixture, thus providing methods for converting JSON -/// strings to configuration element sets, checking parse results, and -/// accessing the configuration context. +/// strings to configuration element sets, checking parse results, +/// accessing the configuration context and trying to unparse. class DStubCfgMgrTest : public ConfigParseTest { public: diff --git a/src/lib/process/testutils/d_test_stubs.cc b/src/lib/process/testutils/d_test_stubs.cc index c829c7988a..b013ffbf6f 100644 --- a/src/lib/process/testutils/d_test_stubs.cc +++ b/src/lib/process/testutils/d_test_stubs.cc @@ -342,6 +342,11 @@ DStubContext::DStubContext(const DStubContext& rhs): DCfgContextBase(rhs), object_values_(new ObjectStorage(*(rhs.object_values_))) { } +isc::data::ElementPtr +DStubContext::toElement() const { + return (isc::data::Element::createMap()); +} + //************************** DStubCfgMgr ************************* DStubCfgMgr::DStubCfgMgr() diff --git a/src/lib/process/testutils/d_test_stubs.h b/src/lib/process/testutils/d_test_stubs.h index 2809f28641..0e293d7324 100644 --- a/src/lib/process/testutils/d_test_stubs.h +++ b/src/lib/process/testutils/d_test_stubs.h @@ -646,6 +646,11 @@ private: /// @brief Stores non-scalar configuration elements ObjectStoragePtr object_values_; + + /// @brief Unparse a configuration objet + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; }; /// @brief Defines a pointer to DStubContext. -- cgit v1.2.3 From 7e81bfc698cc83b24bdd8db1a3d78873181e1710 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Tue, 7 Mar 2017 11:45:23 +0100 Subject: [fdunparse2] Finished agent and added some @todos --- src/bin/agent/tests/testdata/get_config.json | 2 +- src/lib/dhcpsrv/cfg_mac_source.cc | 1 + src/lib/dhcpsrv/cfg_subnets4.cc | 2 +- src/lib/dhcpsrv/cfg_subnets6.cc | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/bin/agent/tests/testdata/get_config.json b/src/bin/agent/tests/testdata/get_config.json index b52f7910bf..3c264b0354 100644 --- a/src/bin/agent/tests/testdata/get_config.json +++ b/src/bin/agent/tests/testdata/get_config.json @@ -16,7 +16,7 @@ }, "hooks-libraries": [ { - "library": "/tmp/kea/src/bin/agent/tests/.libs/libbasic.so", + "library": "/opt/local/control-agent-commands.so", "parameters": { "param1": "foo" } diff --git a/src/lib/dhcpsrv/cfg_mac_source.cc b/src/lib/dhcpsrv/cfg_mac_source.cc index 9475f6e14d..7a0db260b3 100644 --- a/src/lib/dhcpsrv/cfg_mac_source.cc +++ b/src/lib/dhcpsrv/cfg_mac_source.cc @@ -81,6 +81,7 @@ ElementPtr CfgMACSource::toElement() const { } result->add(Element::create(name)); } + // @todo check if the list is empty (including a new unit test) return (result); } diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 425c5faa0b..14f1c15be6 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -313,7 +313,7 @@ CfgSubnets4::toElement() const { ElementPtr pool_list = Element::createList(); for (PoolCollection::const_iterator pool = pools.cbegin(); pool != pools.cend(); ++pool) { - // Prepare the map for a pool + // Prepare the map for a pool (@todo move this code to pool.cc) ElementPtr pool_map = Element::createMap(); // Set pool const IOAddress& first = (*pool)->getFirstAddress(); diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index 2407976bdb..c2c8c5f165 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -289,7 +289,7 @@ CfgSubnets6::toElement() const { ElementPtr pool_list = Element::createList(); for (PoolCollection::const_iterator pool = pools.cbegin(); pool != pools.cend(); ++pool) { - // Prepare the map for a pool + // Prepare the map for a pool (@todo move this code to pool.cc) ElementPtr pool_map = Element::createMap(); // Set pool const IOAddress& first = (*pool)->getFirstAddress(); @@ -320,7 +320,7 @@ CfgSubnets6::toElement() const { ElementPtr pdpool_list = Element::createList(); for (PoolCollection::const_iterator pool = pdpools.cbegin(); pool != pdpools.cend(); ++pool) { - // Get it as a Pool6 + // Get it as a Pool6 (@todo move this code to pool.cc) const Pool6* pdpool = dynamic_cast(pool->get()); if (!pdpool) { isc_throw(ToElementError, "invalid pd-pool pointer"); -- cgit v1.2.3 From fe3766aedd24a402ad08e4323b6f9668dfce878c Mon Sep 17 00:00:00 2001 From: Thomas Markwalder Date: Fri, 21 Apr 2017 11:54:45 -0400 Subject: [5247] Corrected issues with assigned- stats doc/guide/dhcp4-srv.xml doc/guide/dhcp6-srv.xml Added entries for reclaimed-leases src/lib/dhcpsrv/alloc_engine.cc AllocEngine::reuseExpiredLease(Lease6Ptr&...) - increment assigned- for real allocations AllocEngine::extendLease6() - increment assigned- for real allocations if the lease expired AllocEngine::renewLease4(const Lease4Ptr&...) - set lease state to STATE_DEFAULT for real allocations - increment assigned-leases if lease is expired or reclaimed for real allocations AllocEngine::reuseExpiredLease4(Lease4Ptr&...) - increment assigned-leases for real allocations src/lib/dhcpsrv/cfg_subnets4.cc CfgSubnets4::removeStatistics() - added "reclaimed-leases" src/lib/dhcpsrv/cfg_subnets6.cc CfgSubnets4::removeStatistics() - added "reclaimed-leases" src/lib/dhcpsrv/lease_mgr.cc LeaseMgr::recountLeaseStats4() LeaseMgr::recountLeaseStats6() - added handling of "reclaimed-leases" - fixed name of "reclaimed-declined-addresses" src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc TEST_F(AllocEngine4Test, simpleRenew4) - new test to verify stats on a normal renew, non-expired Added EXPECT_TRUE around calls to testStatistics for invocation line numbers Added stat checks to several tests src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc Added EXPECT_TRUE around calls to testStatistics for invocation line numbers Added stat checks to several tests src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc Added checks of assigned stats src/lib/dhcpsrv/tests/alloc_engine_utils.cc AllocEngine6Test::initSubnet() - removed artificial stat values NakedAllocEngine::addHost() - new method to add a host to the current configuration, rather than use staging/commit as the latter --- doc/guide/dhcp4-srv.xml | 20 +- doc/guide/dhcp6-srv.xml | 23 +++ src/lib/dhcpsrv/alloc_engine.cc | 34 +++- src/lib/dhcpsrv/cfg_subnets4.cc | 3 + src/lib/dhcpsrv/cfg_subnets6.cc | 3 + src/lib/dhcpsrv/lease_mgr.cc | 19 +- src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc | 120 +++++++----- src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc | 201 +++++++++++++-------- .../tests/alloc_engine_expiration_unittest.cc | 4 +- src/lib/dhcpsrv/tests/alloc_engine_utils.cc | 21 +-- src/lib/dhcpsrv/tests/alloc_engine_utils.h | 25 ++- .../dhcpsrv/tests/generic_lease_mgr_unittest.cc | 14 +- 12 files changed, 334 insertions(+), 153 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index 9807138b1c..c7b2e19195 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -3614,7 +3614,25 @@ src/lib/dhcpsrv/cfg_host_operations.cc --> separately. This statistic is reset during reconfiguration event. - + + reclaimed-leases + integer + This statistic shows the number of expired leases that have + have been reclaimed since server startup. It increases every time an + expired lease undergoes expiration reclamation. This statistic is reset + during reconfiguration event. + + + + subnet[id].reclaimed-leases + integer + This statistic shows the number of expired leases that have + and been reclaimed since server startup. It increases every time an expired + lease undergoes expiration reclamation. The id is + the subnet-id of the subnet. This statistic is exposed for each subnet + separately. This statistic is reset during reconfiguration event. + + declined-addresses integer diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index 5fdde731bb..a61ba959b2 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -3856,6 +3856,29 @@ If not specified, the default value is: + + reclaimed-leases + integer + This statistic shows the number of expired leases that have + and been reclaimed since server startup. It increases every time an + expired lease undergoes expiration reclamation. This statistic is reset + during reconfiguration event. Note it counts both NA and PD reclamations. + This statistic is reset during reconfiguration event. + + + + + subnet[id].reclaimed-leases + integer + This statistic shows the number of expired leases that have + and been reclaimed since server startup. It increases every time an expired + lease undergoes expiration reclamation. The id is + the subnet-id of the subnet. This statistic is exposed for each subnet + separately. Note it counts both NA and PD reclamations. + This statistic is reset during reconfiguration event. + + + declined-addresses integer diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 13eb1cc875..95e75012f7 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -1093,6 +1093,16 @@ AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx, if (!ctx.fake_allocation_) { // for REQUEST we do update the lease LeaseMgrFactory::instance().updateLease6(expired); + + // If the lease is in the current subnet we need to account + // for the re-assignment of The lease. + if (ctx.subnet_->inPool(ctx.currentIA().type_, expired->addr_)) { + StatsMgr::instance().addValue( + StatsMgr::generateName("subnet", ctx.subnet_->getID(), + ctx.currentIA().type_ == Lease::TYPE_NA ? + "assigned-nas" : "assigned-pds"), + static_cast(1)); + } } // We do nothing for SOLICIT. We'll just update database when @@ -1387,6 +1397,15 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) { if (old_data->expired()) { reclaimExpiredLease(old_data, ctx.callout_handle_); + // If the lease is in the current subnet we need to account + // for the re-assignment of The lease. + if (ctx.subnet_->inPool(ctx.currentIA().type_, old_data->addr_)) { + StatsMgr::instance().addValue( + StatsMgr::generateName("subnet", ctx.subnet_->getID(), + ctx.currentIA().type_ == Lease::TYPE_NA ? + "assigned-nas" : "assigned-pds"), + static_cast(1)); + } } else { if (!lease->hasIdenticalFqdn(*old_data)) { // We're not reclaiming the lease but since the FQDN has changed @@ -2656,13 +2675,14 @@ AllocEngine::renewLease4(const Lease4Ptr& lease, // involves execution of hooks and DNS update. if (ctx.old_lease_->expired()) { reclaimExpiredLease(ctx.old_lease_, ctx.callout_handle_); - lease->state_ = Lease::STATE_DEFAULT; } else if (!lease->hasIdenticalFqdn(*ctx.old_lease_)) { // The lease is not expired but the FQDN information has // changed. So, we have to remove the previous DNS entry. queueNCR(CHG_REMOVE, ctx.old_lease_); } + + lease->state_ = Lease::STATE_DEFAULT; } bool skip = false; @@ -2710,6 +2730,13 @@ AllocEngine::renewLease4(const Lease4Ptr& lease, if (!ctx.fake_allocation_ && !skip) { // for REQUEST we do update the lease LeaseMgrFactory::instance().updateLease4(lease); + + // We need to account for the re-assignment of The lease. + if (ctx.old_lease_->expired() || ctx.old_lease_->state_ == Lease::STATE_EXPIRED_RECLAIMED) { + StatsMgr::instance().addValue( + StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"), + static_cast(1)); + } } if (skip) { // Rollback changes (really useful only for memfile) @@ -2796,6 +2823,11 @@ AllocEngine::reuseExpiredLease4(Lease4Ptr& expired, if (!ctx.fake_allocation_) { // for REQUEST we do update the lease LeaseMgrFactory::instance().updateLease4(expired); + + // We need to account for the re-assignment of The lease. + StatsMgr::instance().addValue( + StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"), + static_cast(1)); } // We do nothing for SOLICIT. We'll just update database when diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 14f1c15be6..df70d79517 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -250,6 +250,9 @@ CfgSubnets4::removeStatistics() { stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "declined-reclaimed-addresses")); + + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "reclaimed-leases")); } } diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index c2c8c5f165..a795fe4a60 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -201,6 +201,9 @@ CfgSubnets6::removeStatistics() { stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "declined-reclaimed-addresses")); + + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "reclaimed-leases")); } } diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc index e57348655a..f34f92713d 100644 --- a/src/lib/dhcpsrv/lease_mgr.cc +++ b/src/lib/dhcpsrv/lease_mgr.cc @@ -62,7 +62,8 @@ LeaseMgr::recountLeaseStats4() { // Zero out the global stats. int64_t zero = 0; stats_mgr.setValue("declined-addresses", zero); - stats_mgr.setValue("declined-reclaimed-addresses", zero); + stats_mgr.setValue("reclaimed-declined-addresses", zero); + stats_mgr.setValue("reclaimed-leases", zero); // Clear subnet level stats. This ensures we don't end up with corner // cases that leave stale values in place. @@ -79,8 +80,13 @@ LeaseMgr::recountLeaseStats4() { stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, "declined-addresses"), zero); + + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "reclaimed-declined-addresses"), + zero); + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, - "declined-reclaimed-addresses"), + "reclaimed-leases"), zero); } @@ -133,7 +139,8 @@ LeaseMgr::recountLeaseStats6() { // clearing it when we clear the rest. int64_t zero = 0; stats_mgr.setValue("declined-addresses", zero); - stats_mgr.setValue("declined-reclaimed-addresses", zero); + stats_mgr.setValue("reclaimed-declined-addresses", zero); + stats_mgr.setValue("reclaimed-leases", zero); // Clear subnet level stats. This ensures we don't end up with corner // cases that leave stale values in place. @@ -153,12 +160,16 @@ LeaseMgr::recountLeaseStats6() { stats_mgr.setValue(StatsMgr:: generateName("subnet", subnet_id, - "declined-reclaimed-addresses"), + "reclaimed-declined-addresses"), zero); stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, "assigned-pds"), zero); + + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "reclaimed-leases"), + zero); } // Get counts per state per subnet. Iterate over the result set diff --git a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc index 0cf7b89648..00ae10ea95 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc @@ -44,7 +44,6 @@ TEST_F(AllocEngine4Test, constructor) { EXPECT_THROW(x->getAllocator(Lease::TYPE_PD), BadValue); } - // This test checks if the simple IPv4 allocation can succeed TEST_F(AllocEngine4Test, simpleAlloc4) { boost::scoped_ptr engine; @@ -52,6 +51,9 @@ TEST_F(AllocEngine4Test, simpleAlloc4) { 100, false))); ASSERT_TRUE(engine); + // Assigned addresses should be zero. + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"), false, true, "somehost.example.com.", false); ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234)); @@ -72,6 +74,9 @@ TEST_F(AllocEngine4Test, simpleAlloc4) { // Now check that the lease in LeaseMgr has the same parameters detailCompareLease(lease, from_mgr); + + // Assigned addresses should have incremented. + EXPECT_TRUE(testStatistics("assigned-addresses", 1, subnet_->getID())); } // This test checks if the fake allocation (for DHCPDISCOVER) can succeed @@ -81,6 +86,9 @@ TEST_F(AllocEngine4Test, fakeAlloc4) { 100, false))); ASSERT_TRUE(engine); + // Assigned addresses should be zero. + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"), false, true, "host.example.com.", true); @@ -100,6 +108,9 @@ TEST_F(AllocEngine4Test, fakeAlloc4) { // Check that the lease is NOT in LeaseMgr Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_); ASSERT_FALSE(from_mgr); + + // Assigned addresses should still be zero. + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); } @@ -267,7 +278,45 @@ TEST_F(AllocEngine4Test, allocateLease4Nulls) { detailCompareLease(lease, from_mgr); } +// This test checks if a returning client can renew an +// an existing lease and assigned-leases increments accordingly +TEST_F(AllocEngine4Test, simpleRenew4) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 100, false))); + ASSERT_TRUE(engine); + + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); + AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"), + false, true, "somehost.example.com.", false); + ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234)); + + Lease4Ptr lease = engine->allocateLease4(ctx); + + // Check that we got a lease and it's sane + ASSERT_TRUE(lease); + checkLease4(lease); + + // The new lease has been allocated, so the old lease should not exist. + EXPECT_FALSE(ctx.old_lease_); + + // We should have incremented assigned-addresses + EXPECT_TRUE(testStatistics("assigned-addresses", 1, subnet_->getID())); + + // Do it again, this should amount to the renew of an existing lease + Lease4Ptr lease2 = engine->allocateLease4(ctx); + + // Check that we got a lease and it's sane + ASSERT_TRUE(lease2); + checkLease4(lease2); + + // Lease already existsed, so old_lease should be set. + EXPECT_TRUE(ctx.old_lease_); + + // Should NOT have bumped assigned-addresses + EXPECT_TRUE(testStatistics("assigned-addresses", 1, subnet_->getID())); +} // This test verifies that the allocator picks addresses that belong to the // pool @@ -504,14 +553,18 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) { IOAddress addr("192.0.2.105"); + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-leases", 0)); + EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID())); + // Just a different hw/client-id for the second client uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe}; HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER)); uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 }; time_t now = time(NULL) - 500; // Allocated 500 seconds ago + Lease4Ptr lease(new Lease4(addr, hwaddr2, clientid2, sizeof(clientid2), - sizeof(hwaddr2), 495, 100, 200, now, - subnet_->getID())); + 495, 100, 200, now, subnet_->getID())); // Make a copy of the lease, so as we can compare that with the old lease // instance returned by the allocation engine. Lease4 original_lease(*lease); @@ -543,6 +596,13 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) { // lease should be equal to the original lease. ASSERT_TRUE(ctx.old_lease_); EXPECT_TRUE(*ctx.old_lease_ == original_lease); + + // Check that the stats declined stats were modified correctly. Note, because + // added the lease directly, assigned-leases never bumped to one, so when we + // reclaime it gets decremented to -1, then on assignment back to 0. + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-leases", 1)); + EXPECT_TRUE(testStatistics("reclaimed-leases", 1, subnet_->getID())); } // This test checks if an expired declined lease can be reused when responding @@ -599,35 +659,23 @@ TEST_F(AllocEngine4Test, discoverReuseDeclinedLease4Stats) { pool_ = Pool4Ptr(new Pool4(addr, addr)); // just a single address subnet_->addPool(pool_); cfg_mgr.getStagingCfg()->getCfgSubnets4()->add(subnet_); + cfg_mgr.commit(); // so we will recalc stats // Now create a declined lease, decline it and rewind its cltt, so it // is expired. Lease4Ptr declined = generateDeclinedLease("192.0.2.15", 100, -10); - // Let's fix some global stats... - StatsMgr& stats_mgr = StatsMgr::instance(); - stats_mgr.setValue("declined-addresses", static_cast(1000)); - stats_mgr.setValue("reclaimed-declined-addresses", static_cast(1000)); - - // ...and subnet specific stats as well. - string stat1 = StatsMgr::generateName("subnet", subnet_->getID(), - "declined-addresses"); - string stat2 = StatsMgr::generateName("subnet", subnet_->getID(), - "reclaimed-declined-addresses"); - stats_mgr.setValue(stat1, static_cast(1000)); - stats_mgr.setValue(stat2, static_cast(1000)); - // Ask for any address. There's only one address in the pool, so it doesn't // matter much. Lease4Ptr assigned; testReuseLease4(engine, declined, "0.0.0.0", true, SHOULD_PASS, assigned); - // Check that the stats were not modified - testStatistics("declined-addresses", 1000); - testStatistics("reclaimed-declined-addresses", 1000); - - testStatistics(stat1, 1000); - testStatistics(stat2, 1000); + // Check that the stats declined stats were not modified + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("declined-addresses", 0)); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0)); + EXPECT_TRUE(testStatistics("declined-addresses", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0, subnet_->getID())); } // This test checks if an expired declined lease can be reused when responding @@ -682,36 +730,26 @@ TEST_F(AllocEngine4Test, requestReuseDeclinedLease4Stats) { pool_ = Pool4Ptr(new Pool4(addr, addr)); // just a single address subnet_->addPool(pool_); cfg_mgr.getStagingCfg()->getCfgSubnets4()->add(subnet_); + cfg_mgr.commit(); // Now create a declined lease, decline it and rewind its cltt, so it // is expired. Lease4Ptr declined = generateDeclinedLease("192.0.2.15", 100, -10); - // Let's fix some global stats... - StatsMgr& stats_mgr = StatsMgr::instance(); - stats_mgr.setValue("declined-addresses", static_cast(1000)); - stats_mgr.setValue("reclaimed-declined-addresses", static_cast(1000)); - - // ...and subnet specific stats as well. - string stat1 = StatsMgr::generateName("subnet", subnet_->getID(), - "declined-addresses"); - string stat2 = StatsMgr::generateName("subnet", subnet_->getID(), - "reclaimed-declined-addresses"); - stats_mgr.setValue(stat1, static_cast(1000)); - stats_mgr.setValue(stat2, static_cast(1000)); - // Asking specifically for this address Lease4Ptr assigned; testReuseLease4(engine, declined, "192.0.2.15", false, SHOULD_PASS, assigned); // Check that we got it. ASSERT_TRUE(assigned); - // Check that the stats were modified - testStatistics("declined-addresses", 999); - testStatistics("reclaimed-declined-addresses", 1001); - - testStatistics(stat1, 999); - testStatistics(stat2, 1001); + // Check that the stats are correct. Note that assigned-addresses does + // not get decremented when a lease is declined, ergo not incremented + // when it is reused. + EXPECT_TRUE(testStatistics("assigned-addresses", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("declined-addresses", -1)); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 1)); + EXPECT_TRUE(testStatistics("declined-addresses", -1, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 1, subnet_->getID())); } // This test checks that the Allocation Engine correctly identifies the diff --git a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc index a5d011e63d..47b268db51 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc @@ -77,7 +77,7 @@ TEST_F(AllocEngine6Test, simpleAlloc6) { string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"); ObservationPtr stat = StatsMgr::instance().getObservation(name); ASSERT_TRUE(stat); - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_EQ(1, stat->getInteger().first); } // This test checks if the simple PD allocation (REQUEST) can succeed @@ -90,7 +90,7 @@ TEST_F(AllocEngine6Test, pdSimpleAlloc6) { string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-pds"); ObservationPtr stat = StatsMgr::instance().getObservation(name); ASSERT_TRUE(stat); - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_EQ(1, stat->getInteger().first); } // This test checks if the fake allocation (for SOLICIT) can succeed @@ -103,7 +103,7 @@ TEST_F(AllocEngine6Test, fakeAlloc6) { string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"); ObservationPtr stat = StatsMgr::instance().getObservation(name); ASSERT_TRUE(stat); - EXPECT_EQ(100, stat->getInteger().first); + EXPECT_EQ(0, stat->getInteger().first); } // This test checks if the fake PD allocation (for SOLICIT) can succeed @@ -115,7 +115,7 @@ TEST_F(AllocEngine6Test, pdFakeAlloc6) { string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-pds"); ObservationPtr stat = StatsMgr::instance().getObservation(name); ASSERT_TRUE(stat); - EXPECT_EQ(100, stat->getInteger().first); + EXPECT_EQ(0, stat->getInteger().first); }; // This test checks if the allocation with a hint that is valid (in range, @@ -545,6 +545,11 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) { // Check that we got that single lease ASSERT_TRUE(lease); EXPECT_EQ(addr, lease->addr_); + + // Verify the none of relelvant stats were altered. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-leases", 0)); + EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID())); } // This test checks if an expired lease can be reused in REQUEST (actual allocation) @@ -567,10 +572,12 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) { // Let's create an expired lease DuidPtr other_duid = DuidPtr(new DUID(vector(12, 0xff))); const uint32_t other_iaid = 3568; + const SubnetID other_subnetid = 999; Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid, 501, 502, 503, 504, other_subnetid, HWAddrPtr(), 0)); + lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago lease->valid_lft_ = 495; // Lease was valid for 495 seconds lease->fqdn_fwd_ = true; @@ -578,10 +585,6 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) { lease->hostname_ = "myhost.example.com."; ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease)); - // By default we pretend our subnet has 100 addresses - string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"); - StatsMgr::instance().setValue(name, static_cast(100)); - // A client comes along, asking specifically for this address AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234))); @@ -615,12 +618,12 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) { // Now check that the lease in LeaseMgr has the same parameters detailCompareLease(lease, from_mgr); - // We should not have bumped the address counter - // NOTE: when we start expiring addresses and removing them from - // the stats this will no longer be true. - ObservationPtr stat = StatsMgr::instance().getObservation(name); - ASSERT_TRUE(stat); - EXPECT_EQ(100, stat->getInteger().first); + // Verify the stats got adjusted correctly + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); + EXPECT_TRUE(testStatistics("assigned-nas", -1, other_subnetid)); + EXPECT_TRUE(testStatistics("reclaimed-leases", 1)); + EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-leases", 1, other_subnetid)); } // Checks if the lease lifetime is extended when the client sends the @@ -776,18 +779,15 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolRequestNoHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); - // By default we pretend our subnet has 100 addresses - string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"); - StatsMgr::instance().setValue(name, static_cast(100)); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), false); ASSERT_TRUE(lease); EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText()); - // We should have bumped the address counter - ObservationPtr stat = StatsMgr::instance().getObservation(name); - ASSERT_TRUE(stat); - EXPECT_EQ(101, stat->getInteger().first); + // Assigned count should have been incremented by one. + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); } // Checks that a client gets the address reserved (in-pool case) @@ -807,12 +807,18 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolSolicitValidHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), true); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText()); + + // Assigned count should not have been incremented. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); } // Checks that a client gets the address reserved (in-pool case) @@ -832,12 +838,18 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolRequestValidHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), false); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText()); + + // Assigned count should have been incremented. + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); } // Checks that a client gets the address reserved (in-pool case) @@ -857,12 +869,18 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolSolicitMatchingHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), true); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText()); + + // Assigned count should not have been incremented. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); } // Checks that a client gets the address reserved (in-pool case) @@ -882,12 +900,18 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolRequestMatchingHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), false); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText()); + + // Assigned count should have been incremented. + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); } // Checks that a client gets the address reserved (out-of-pool case) @@ -907,10 +931,16 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitNoHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), true, false); ASSERT_TRUE(lease); EXPECT_EQ("2001:db8::abcd", lease->addr_.toText()); + // Assigned count should not have been incremented. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + } // Checks that a client gets the address reserved (out-of-pool case) @@ -930,18 +960,15 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestNoHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); - // By default we pretend our subnet has 100 addresses - string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"); - StatsMgr::instance().setValue(name, static_cast(100)); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), false, false); ASSERT_TRUE(lease); EXPECT_EQ("2001:db8::abcd", lease->addr_.toText()); // We should not have bumped the address counter - ObservationPtr stat = StatsMgr::instance().getObservation(name); - ASSERT_TRUE(stat); - EXPECT_EQ(100, stat->getInteger().first); + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); } // Checks that a client gets the address reserved (in-pool case) @@ -961,12 +988,18 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitValidHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), true, false); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8::abcd", lease->addr_.toText()); + + // We should not have bumped the address counter + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); } // Checks that a client gets the address reserved (out-of-pool case) @@ -986,12 +1019,18 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestValidHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), false, false); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8::abcd", lease->addr_.toText()); + + // We should not have bumped the address counter + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); } // Checks that a client gets the address reserved (out-of-pool case) @@ -1011,12 +1050,18 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitMatchingHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), true, false); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8::abcd", lease->addr_.toText()); + + // We should not have bumped the address counter + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); } // Checks that a client gets the address reserved (out-of-pool case) @@ -1036,12 +1081,18 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestMatchingHint) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Let's pretend the client sends hint 2001:db8:1::10. Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), false, false); ASSERT_TRUE(lease); // The hint should be ignored and the reserved address should be assigned EXPECT_EQ("2001:db8::abcd", lease->addr_.toText()); + + // We should not have bumped the address counter + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); } // In the following situation: @@ -1054,15 +1105,15 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestMatchingHint) { TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Client gets an address Lease6Ptr lease1 = simpleAlloc6Test(pool_, IOAddress("::"), false); ASSERT_TRUE(lease1); // We should have bumped the address counter - string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"); - ObservationPtr stat = StatsMgr::instance().getObservation(name); - ASSERT_TRUE(stat); - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); // Just check that if the client requests again, it will get the same // address. @@ -1071,7 +1122,7 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) { detailCompareLease(lease1, lease2); // We should not have bumped the address counter again - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); // Now admin creates a reservation for this client. This is in-pool // reservation, as the pool is 2001:db8:1::10 - 2001:db8:1::20. @@ -1101,9 +1152,9 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) { // Now check that the lease in LeaseMgr has the same parameters detailCompareLease(lease3, from_mgr); - // Lastly check to see that the address counter is still 101 we should have + // Lastly check to see that the address counter is still 1, we should have // have decremented it on the implied release and incremented it on the reserved - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); } // In the following situation: // - client X is assigned an address A @@ -1114,15 +1165,15 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) { TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedOther) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false); + // Assigned count should be zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + // Client gets an address Lease6Ptr lease1 = simpleAlloc6Test(pool_, IOAddress("::"), false); ASSERT_TRUE(lease1); // We should have bumped the address counter - string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"); - ObservationPtr stat = StatsMgr::instance().getObservation(name); - ASSERT_TRUE(stat); - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); // Just check that if the client requests again, it will get the same // address. @@ -1131,7 +1182,7 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedOther) { detailCompareLease(lease1, lease2); // We should not have bumped the address counter again - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); // Now admin creates a reservation for this client. Let's use the // address client X just received. Let's generate a host, but don't add it @@ -1143,8 +1194,7 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedOther) { host->setIdentifier(&other_duid[0], other_duid.size(), Host::IDENT_DUID); // Ok, now add it to the HostMgr - CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host); - CfgMgr::instance().commit(); + addHost(host); // Just check that this time the client will get a different lease. Lease6Ptr lease3 = simpleAlloc6Test(pool_, lease1->addr_, false); @@ -1167,9 +1217,9 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedOther) { // Now check that the lease in LeaseMgr has the same parameters detailCompareLease(lease3, from_mgr); - // Lastly check to see that the address counter is still 101 we should have + // Lastly check to see that the address counter is still 1 we should have // have decremented it on the implied release and incremented it on the reserved - EXPECT_EQ(101, stat->getInteger().first); + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); } // Checks that a reserved address for client A is not assigned when @@ -1267,11 +1317,17 @@ TEST_F(AllocEngine6Test, allocateLeasesInvalidData) { TEST_F(AllocEngine6Test, addressRenewal) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100); + // Assigned count should zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + Lease6Collection leases; leases = allocateTest(engine, pool_, IOAddress("::"), false, true); ASSERT_EQ(1, leases.size()); + // Assigned count should be one. + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); + // This is what the client will send in his renew message. AllocEngine::HintContainer hints; hints.push_back(make_pair(leases[0]->addr_, 128)); @@ -1287,6 +1343,9 @@ TEST_F(AllocEngine6Test, addressRenewal) { EXPECT_EQ(leases[0]->type_, renewed[0]->type_); EXPECT_EQ(leases[0]->preferred_lft_, renewed[0]->preferred_lft_); EXPECT_EQ(leases[0]->valid_lft_, renewed[0]->valid_lft_); + + // Assigned count should still be one. + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); } // Checks whether an address can be renewed (in-pool reservation) @@ -1297,12 +1356,18 @@ TEST_F(AllocEngine6Test, reservedAddressRenewal) { AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100); + // Assigned count should zero. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + Lease6Collection leases; leases = allocateTest(engine, pool_, IOAddress("::"), false, true); ASSERT_EQ(1, leases.size()); ASSERT_EQ("2001:db8:1::1c", leases[0]->addr_.toText()); + // Assigned count should be one. + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); + // This is what the client will send in his renew message. AllocEngine::HintContainer hints; hints.push_back(make_pair(leases[0]->addr_, 128)); @@ -1310,6 +1375,9 @@ TEST_F(AllocEngine6Test, reservedAddressRenewal) { Lease6Collection renewed = renewTest(engine, pool_, hints, true); ASSERT_EQ(1, renewed.size()); ASSERT_EQ("2001:db8:1::1c", leases[0]->addr_.toText()); + + // Assigned count should still be one. + EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID())); } // Checks whether a single host can have more than one reservation. @@ -1775,30 +1843,17 @@ TEST_F(AllocEngine6Test, solicitReuseDeclinedLease6Stats) { // is expired. Lease6Ptr declined = generateDeclinedLease(addr_txt, 100, -10); - // Let's fix some global stats... - StatsMgr& stats_mgr = StatsMgr::instance(); - stats_mgr.setValue("declined-addresses", static_cast(1000)); - stats_mgr.setValue("reclaimed-declined-addresses", static_cast(1000)); - - // ...and subnet specific stats as well. - string stat1 = StatsMgr::generateName("subnet", subnet_->getID(), - "declined-addresses"); - string stat2 = StatsMgr::generateName("subnet", subnet_->getID(), - "reclaimed-declined-addresses"); - stats_mgr.setValue(stat1, static_cast(1000)); - stats_mgr.setValue(stat2, static_cast(1000)); - // Ask for any address. There's only one address in the pool, so it doesn't // matter much. Lease6Ptr assigned; testReuseLease6(engine, declined, "::", true, SHOULD_PASS, assigned); // Check that the stats were not modified - testStatistics("declined-addresses", 1000); - testStatistics("reclaimed-declined-addresses", 1000); - - testStatistics(stat1, 1000); - testStatistics(stat2, 1000); + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("declined-addresses", 0)); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0)); + EXPECT_TRUE(testStatistics("declined-addresses", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0, subnet_->getID())); } // This test checks if statistics are not updated when expired declined lease @@ -1819,30 +1874,18 @@ TEST_F(AllocEngine6Test, requestReuseDeclinedLease6Stats) { // is expired. Lease6Ptr declined = generateDeclinedLease(addr_txt, 100, -10); - // Let's fix some global stats... - StatsMgr& stats_mgr = StatsMgr::instance(); - stats_mgr.setValue("declined-addresses", static_cast(1000)); - stats_mgr.setValue("reclaimed-declined-addresses", static_cast(1000)); - - // ...and subnet specific stats as well. - string stat1 = StatsMgr::generateName("subnet", subnet_->getID(), - "declined-addresses"); - string stat2 = StatsMgr::generateName("subnet", subnet_->getID(), - "reclaimed-declined-addresses"); - stats_mgr.setValue(stat1, static_cast(1000)); - stats_mgr.setValue(stat2, static_cast(1000)); - // Ask for any address. There's only one address in the pool, so it doesn't // matter much. Lease6Ptr assigned; testReuseLease6(engine, declined, "::", false, SHOULD_PASS, assigned); - // Check that the stats were not modified - testStatistics("declined-addresses", 999); - testStatistics("reclaimed-declined-addresses", 1001); + // Check that the stats were modified as expected. + EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID())); + EXPECT_TRUE(testStatistics("declined-addresses", -1)); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 1)); + EXPECT_TRUE(testStatistics("declined-addresses", -1, subnet_->getID())); + EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 1, subnet_->getID())); - testStatistics(stat1, 999); - testStatistics(stat2, 1001); } }; // namespace test diff --git a/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc index 94b4c3827c..ffb812cb3d 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc @@ -1437,13 +1437,12 @@ ExpirationAllocEngine6Test::testReclaimReusedLeases(const uint16_t msg_type, // initially reclaimed. if (use_reclaimed || (msg_type == DHCPV6_SOLICIT)) { EXPECT_TRUE(testStatistics("reclaimed-leases", 0)); - } else { EXPECT_TRUE(testStatistics("reclaimed-leases", TEST_LEASES_NUM)); + EXPECT_TRUE(testStatistics("assigned-nas", TEST_LEASES_NUM, subnet->getID())); // Leases should have been updated in the lease database and their // state should not be 'expired-reclaimed' anymore. EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes)); - } } @@ -2099,6 +2098,7 @@ ExpirationAllocEngine4Test::testReclaimReusedLeases(const uint8_t msg_type, } else if (msg_type == DHCPREQUEST) { // Re-allocation of expired leases should result in reclamations. EXPECT_TRUE(testStatistics("reclaimed-leases", TEST_LEASES_NUM)); + EXPECT_TRUE(testStatistics("assigned-addresses", TEST_LEASES_NUM, subnet->getID())); // Leases should have been updated in the lease database and their // state should not be 'expired-reclaimed' anymore. EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes)); diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc index a38fb8970b..ee380ad9dc 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc @@ -39,20 +39,23 @@ namespace isc { namespace dhcp { namespace test { -bool testStatistics(const std::string& stat_name, const int64_t exp_value) { +bool testStatistics(const std::string& stat_name, const int64_t exp_value, + const SubnetID subnet_id) { try { - ObservationPtr observation = StatsMgr::instance().getObservation(stat_name); + std::string name = (!subnet_id ? stat_name : + StatsMgr::generateName("subnet", subnet_id, stat_name)); + ObservationPtr observation = StatsMgr::instance().getObservation(name); if (observation) { if (observation->getInteger().first != exp_value) { ADD_FAILURE() << "value of the observed statistics '" - << stat_name << "' " << "(" + << name << "' " << "(" << observation->getInteger().first << ") " << "doesn't match expected value (" << exp_value << ")"; } return (observation->getInteger().first == exp_value); } else { - ADD_FAILURE() << "Expected statistic " << stat_name + ADD_FAILURE() << "Expected statistic " << name << " not found."; } @@ -136,7 +139,6 @@ AllocEngine6Test::AllocEngine6Test() { // hardcoded anywhere. const uint8_t mac[] = { 0, 1, 22, 33, 44, 55}; hwaddr_ = HWAddrPtr(new HWAddr(mac, sizeof(mac), HTYPE_FDDI)); - // Initialize a subnet and short address pool. initSubnet(IOAddress("2001:db8:1::"), IOAddress("2001:db8:1::10"), @@ -145,7 +147,6 @@ AllocEngine6Test::AllocEngine6Test() { 64, 80); initFqdn("", false, false); - } void @@ -170,14 +171,6 @@ AllocEngine6Test::initSubnet(const asiolink::IOAddress& subnet, cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_); cfg_mgr.commit(); - - // By default we pretend our subnet has 100 addresses and prefixes allocated. - StatsMgr::instance().setValue( - StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"), - static_cast(100)); - StatsMgr::instance().setValue( - StatsMgr::generateName("subnet", subnet_->getID(), "assigned-pds"), - static_cast(100)); } void diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.h b/src/lib/dhcpsrv/tests/alloc_engine_utils.h index 73631cd4f2..63d3a63ccd 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_utils.h +++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.h @@ -1,7 +1,6 @@ // 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 +// 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 // file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef LIBDHCPSRV_ALLOC_ENGINE_UTILS_H @@ -39,10 +38,12 @@ namespace test { /// /// @param stat_name Statistic name. /// @param exp_value Expected value. +/// @param subnet_id subnet_id of the desired subnet, if not zero /// /// @return true if the statistic manager holds a particular value, /// false otherwise. -bool testStatistics(const std::string& stat_name, const int64_t exp_value); +bool testStatistics(const std::string& stat_name, const int64_t exp_value, + const SubnetID subnet_id = 0); /// @brief Allocation engine with some internal methods exposed class NakedAllocEngine : public AllocEngine { @@ -360,12 +361,26 @@ public: host->addReservation(resv); if (add_to_host_mgr) { - CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host); - CfgMgr::instance().commit(); + addHost(host); } + return (host); } + /// @brief Add a host reservation to the curent configuration + /// + /// Adds the given host reservation to the current configuration by + /// casting it to non-const. We do it this way rather than adding it to + /// staging and then committing as that wipes out the current configuration + /// such as subnets. + /// + /// @param host host reservation to add + void + addHost(HostPtr& host) { + SrvConfigPtr cfg = boost::const_pointer_cast(CfgMgr::instance().getCurrentCfg()); + cfg->getCfgHosts()->add(host); + } + /// @brief Utility function that creates a host reservation (hwaddr) /// /// @param add_to_host_mgr true if the reservation should be added diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index 6f255caf78..b3d46c69e0 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -2404,7 +2404,7 @@ void GenericLeaseMgrTest::checkLeaseStats(const StatValMapList& expectedStats) { // Global accumulators int64_t declined_addresses = 0; - int64_t declined_reclaimed_addresses = 0; + int64_t reclaimed_declined_addresses = 0; // Iterate over all stats for each subnet for (int subnet_idx = 0; subnet_idx < expectedStats.size(); ++subnet_idx) { @@ -2417,15 +2417,15 @@ GenericLeaseMgrTest::checkLeaseStats(const StatValMapList& expectedStats) { // Add the value to globals as needed. if (expectedStat.first == "declined-addresses") { declined_addresses += expectedStat.second; - } else if (expectedStat.first == "declined-reclaimed-addresses") { - declined_reclaimed_addresses += expectedStat.second; + } else if (expectedStat.first == "reclaimed-declined-addresses") { + reclaimed_declined_addresses += expectedStat.second; } } } // Verify the globals. checkStat("declined-addresses", declined_addresses); - checkStat("declined-reclaimed-addresses", declined_reclaimed_addresses); + checkStat("reclaimed-declined-addresses", reclaimed_declined_addresses); } void @@ -2501,7 +2501,8 @@ GenericLeaseMgrTest::testRecountLeaseStats4() { expectedStats[i]["total-addresses"] = 256; expectedStats[i]["assigned-addresses"] = 0; expectedStats[i]["declined-addresses"] = 0; - expectedStats[i]["declined-reclaimed-addresses"] = 0; + expectedStats[i]["reclaimed-declined-addresses"] = 0; + expectedStats[i]["reclaimed-leases"] = 0; } // Make sure stats are as expected. @@ -2604,8 +2605,9 @@ GenericLeaseMgrTest::testRecountLeaseStats6() { for (int i = 0; i < num_subnets; ++i) { expectedStats[i]["assigned-nas"] = 0; expectedStats[i]["declined-addresses"] = 0; - expectedStats[i]["declined-reclaimed-addresses"] = 0; + expectedStats[i]["reclaimed-declined-addresses"] = 0; expectedStats[i]["assigned-pds"] = 0; + expectedStats[i]["reclaimed-leases"] = 0; } // Make sure stats are as expected. -- cgit v1.2.3 From 556f6591bed33a424d02e47b8f15df8467ff04d4 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Mon, 3 Jul 2017 23:09:12 +0200 Subject: [5288] Added option-data in DHCPv4 pools --- doc/examples/kea4/multiple-options.json | 18 +- doc/guide/dhcp4-srv.xml | 51 ++++ src/bin/dhcp4/tests/config_parser_unittest.cc | 151 ++++++++++++ src/bin/dhcp4/tests/get_config_unittest.cc | 298 ++++++++++++++++++++---- src/bin/dhcp4/tests/get_config_unittest.cc.skel | 2 +- src/lib/dhcpsrv/cfg_subnets4.cc | 4 +- src/lib/dhcpsrv/parsers/dhcp_parsers.cc | 6 - src/lib/dhcpsrv/tests/pool_unittest.cc | 54 ++++- 8 files changed, 523 insertions(+), 61 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/doc/examples/kea4/multiple-options.json b/doc/examples/kea4/multiple-options.json index f48012a96d..c417365b69 100644 --- a/doc/examples/kea4/multiple-options.json +++ b/doc/examples/kea4/multiple-options.json @@ -32,9 +32,11 @@ // clients connected to this subnet. The first two options are // identified by the name. The third option is identified by the // option code. +// There is an address pool defined within this subnet. Pool +// specific value for option domain-name-servers is defined +// for the pool. "subnet4": [ { - "pools": [ { "pool": "192.0.2.10 - 192.0.2.200" } ], "subnet": "192.0.2.0/24", "interface": "ethX", "option-data": [ @@ -124,9 +126,17 @@ "name": "default-ip-ttl", "data": "0xf0" } - ] - } - ] + ], + "pools": [ { + "pool": "192.0.2.10 - 192.0.2.200", + "option-data": [ + { + "name": "domain-name-servers", + "data": "192.0.2.3, 192.0.2.4" + } + ] + } ] + } ] }, // The following configures logging. It assumes that messages with at diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index ff3873ac9a..e4af768777 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -1057,6 +1057,57 @@ temporarily override a list of interface names and listen on all interfaces. + + In some cases it is useful to associate some options with an + address pool from which a client is assigned a lease. Pool + specific option values override subnet specific and global + option values. If the client is assigned multiple leases from + different pools, the server will assign options from all pools + from which the leases have been obtained. However, if the + particular option is specified in multiple pools from which + the client obtains the leases, only one instance of this + option will be handed out to the client. The server's + administrator must not try to prioritize assignment of pool + specific options by trying to order pools declarations in the + server configuration. Future Kea releases may change the order + in which options are assigned from the pools without any + notice. + + + + The following configuration snippet demonstrates how to specify the + DNS servers option, which will be assigned to a client only if the + client obtains an address from the given pool: + +"Dhcp4": { + "subnet4": [ + { + "pools": [ + { + "pool": "192.0.2.1 - 192.0.2.200", + "option-data": [ + { + "name": "domain-name-servers", + "code": 6, + "space": "dhcp4", + "csv-format": true, + "data": "192.0.2.3" + }, + ... + ], + ... + }, + ... + ], + ... + }, + ... + ], + ... +} + + + The currently supported standard DHCPv4 options are listed in . diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index f997ef2535..3dc3bc8e1b 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -2739,6 +2739,157 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) { testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected)); } +// This test verifies that it is possible to specify options on +// pool levels. +TEST_F(Dhcp4ParserTest, optionDataSinglePool) { + ConstElementPtr x; + string config = "{ " + genIfaceConfig() + "," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { " + " \"pool\": \"192.0.2.1 - 192.0.2.100\"," + " \"option-data\": [ {" + " \"name\": \"dhcp-message\"," + " \"data\": \"ABCDEF0105\"," + " \"csv-format\": false" + " }," + " {" + " \"name\": \"default-ip-ttl\"," + " \"data\": \"01\"," + " \"csv-format\": false" + " } ]" + " } ]," + " \"subnet\": \"192.0.2.0/24\"" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); + + EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); + checkResult(x, 0); + + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets4()-> + selectSubnet(IOAddress("192.0.2.24"), classify_); + ASSERT_TRUE(subnet); + + PoolPtr pool = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.24"), false); + ASSERT_TRUE(pool); + Pool4Ptr pool4 = boost::dynamic_pointer_cast(pool); + ASSERT_TRUE(pool4); + + OptionContainerPtr options = pool4->getCfgOption()->getAll("dhcp4"); + ASSERT_EQ(2, options->size()); + + // Get the search index. Index #1 is to search using option code. + const OptionContainerTypeIndex& idx = options->get<1>(); + + // Get the options for specified index. Expecting one option to be + // returned but in theory we may have multiple options with the same + // code so we get the range. + std::pair range = + idx.equal_range(56); + // Expect a single option with the code equal to 100. + ASSERT_EQ(1, std::distance(range.first, range.second)); + const uint8_t foo_expected[] = { + 0xAB, 0xCD, 0xEF, 0x01, 0x05 + }; + // Check if option is valid in terms of code and carried data. + testOption(*range.first, 56, foo_expected, sizeof(foo_expected)); + + range = idx.equal_range(23); + ASSERT_EQ(1, std::distance(range.first, range.second)); + // Do another round of testing with second option. + const uint8_t foo2_expected[] = { + 0x01 + }; + testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected)); +} + +TEST_F(Dhcp4ParserTest, optionDataMultiplePools) { + ConstElementPtr x; + string config = "{ " + genIfaceConfig() + "," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { " + " \"pool\": \"192.0.2.1 - 192.0.2.100\"," + " \"option-data\": [ {" + " \"name\": \"dhcp-message\"," + " \"data\": \"ABCDEF0105\"," + " \"csv-format\": false" + " } ]" + " }," + " {" + " \"pool\": \"192.0.2.200 - 192.0.2.250\"," + " \"option-data\": [ {" + " \"name\": \"default-ip-ttl\"," + " \"data\": \"01\"," + " \"csv-format\": false" + " } ]" + " } ]," + " \"subnet\": \"192.0.2.0/24\"" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP4(config)); + extractConfig(config); + + EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json)); + checkResult(x, 0); + + Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets4()-> + selectSubnet(IOAddress("192.0.2.24"), classify_); + ASSERT_TRUE(subnet); + + PoolPtr pool1 = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.24"), false); + ASSERT_TRUE(pool1); + Pool4Ptr pool41 = boost::dynamic_pointer_cast(pool1); + ASSERT_TRUE(pool41); + + OptionContainerPtr options1 = pool41->getCfgOption()->getAll("dhcp4"); + ASSERT_EQ(1, options1->size()); + + // Get the search index. Index #1 is to search using option code. + const OptionContainerTypeIndex& idx1 = options1->get<1>(); + + // Get the options for specified index. Expecting one option to be + // returned but in theory we may have multiple options with the same + // code so we get the range. + std::pair range1 = + idx1.equal_range(56); + // Expect a single option with the code equal to 100. + ASSERT_EQ(1, std::distance(range1.first, range1.second)); + const uint8_t foo_expected[] = { + 0xAB, 0xCD, 0xEF, 0x01, 0x05 + }; + // Check if option is valid in terms of code and carried data. + testOption(*range1.first, 56, foo_expected, sizeof(foo_expected)); + + // Test another pool in the same way. + PoolPtr pool2 = subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.2.240"), false); + ASSERT_TRUE(pool2); + Pool4Ptr pool42 = boost::dynamic_pointer_cast(pool2); + ASSERT_TRUE(pool42); + + OptionContainerPtr options2 = pool42->getCfgOption()->getAll("dhcp4"); + ASSERT_EQ(1, options2->size()); + + const OptionContainerTypeIndex& idx2 = options2->get<1>(); + std::pair range2 = + idx2.equal_range(23); + ASSERT_EQ(1, std::distance(range2.first, range2.second)); + const uint8_t foo2_expected[] = { + 0x01 + }; + testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected)); +} // Verify that empty option name is rejected in the configuration. diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc index 5c4bff4879..ef0fc38028 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -816,6 +816,73 @@ const char* EXTRACTED_CONFIGS[] = { " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" " },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\"\n" +" },\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ],\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 31 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"rebind-timer\": 2000,\n" +" \"renew-timer\": 1000,\n" +" \"subnet4\": [\n" +" {\n" +" \"pools\": [\n" +" {\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"ABCDEF0105\",\n" +" \"name\": \"dhcp-message\"\n" +" }\n" +" ],\n" +" \"pool\": \"192.0.2.1 - 192.0.2.100\"\n" +" },\n" +" {\n" +" \"option-data\": [\n" +" {\n" +" \"csv-format\": false,\n" +" \"data\": \"01\",\n" +" \"name\": \"default-ip-ttl\"\n" +" }\n" +" ],\n" +" \"pool\": \"192.0.2.200 - 192.0.2.250\"\n" +" }\n" +" ],\n" +" \"subnet\": \"192.0.2.0/24\"\n" +" }\n" +" ],\n" +" \"valid-lifetime\": 4000\n" +" }\n", + // CONFIGURATION 32 +"{\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" " \"option-data\": [\n" " {\n" " \"data\": \"1234\",\n" @@ -846,7 +913,7 @@ const char* EXTRACTED_CONFIGS[] = { " \"renew-timer\": 1000,\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 31 + // CONFIGURATION 33 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -897,7 +964,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 3000\n" " }\n", - // CONFIGURATION 32 + // CONFIGURATION 34 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -932,7 +999,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 33 + // CONFIGURATION 35 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -967,7 +1034,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 34 + // CONFIGURATION 36 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"eth0\", \"eth1\" ]\n" @@ -976,7 +1043,7 @@ const char* EXTRACTED_CONFIGS[] = { " \"renew-timer\": 1000,\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 35 + // CONFIGURATION 37 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"eth0\", \"*\", \"eth1\" ]\n" @@ -985,7 +1052,7 @@ const char* EXTRACTED_CONFIGS[] = { " \"renew-timer\": 1000,\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 36 + // CONFIGURATION 38 "{\n" " \"dhcp-ddns\": {\n" " \"always-include-fqdn\": true,\n" @@ -1020,7 +1087,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 37 + // CONFIGURATION 39 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1045,7 +1112,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 38 + // CONFIGURATION 40 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1143,7 +1210,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 39 + // CONFIGURATION 41 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1184,7 +1251,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 40 + // CONFIGURATION 42 "{\n" " \"rebind-timer\": 2000,\n" " \"renew-timer\": 1000,\n" @@ -1227,21 +1294,21 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 41 + // CONFIGURATION 43 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" " },\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 42 + // CONFIGURATION 44 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" " },\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 43 + // CONFIGURATION 45 "{\n" " \"decline-probation-period\": 12345,\n" " \"interfaces-config\": {\n" @@ -1249,7 +1316,7 @@ const char* EXTRACTED_CONFIGS[] = { " },\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 44 + // CONFIGURATION 46 "{\n" " \"expired-leases-processing\": {\n" " \"flush-reclaimed-timer-wait-time\": 35,\n" @@ -1264,7 +1331,7 @@ const char* EXTRACTED_CONFIGS[] = { " },\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 45 + // CONFIGURATION 47 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1283,7 +1350,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 46 + // CONFIGURATION 48 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1303,7 +1370,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 47 + // CONFIGURATION 49 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1323,7 +1390,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 48 + // CONFIGURATION 50 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1344,7 +1411,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 49 + // CONFIGURATION 51 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1364,7 +1431,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 50 + // CONFIGURATION 52 "{\n" " \"client-classes\": [\n" " {\n" @@ -1394,7 +1461,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 51 + // CONFIGURATION 53 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1413,7 +1480,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 52 + // CONFIGURATION 54 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1433,7 +1500,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 53 + // CONFIGURATION 55 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -1457,7 +1524,7 @@ const char* EXTRACTED_CONFIGS[] = { " ],\n" " \"valid-lifetime\": 4000\n" " }\n", - // CONFIGURATION 54 + // CONFIGURATION 56 "{\n" " \"interfaces-config\": {\n" " \"interfaces\": [ \"*\" ]\n" @@ -3780,6 +3847,141 @@ const char* UNPARSED_CONFIGS[] = { " \"lease-database\": {\n" " \"type\": \"memfile\"\n" " },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 31 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\", \"client-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" +" \"option-data\": [ ],\n" +" \"option-def\": [ ],\n" +" \"subnet4\": [\n" +" {\n" +" \"4o6-interface\": \"\",\n" +" \"4o6-interface-id\": \"\",\n" +" \"4o6-subnet\": \"\",\n" +" \"id\": 1,\n" +" \"interface\": \"\",\n" +" \"match-client-id\": true,\n" +" \"next-server\": \"0.0.0.0\",\n" +" \"option-data\": [ ],\n" +" \"pools\": [\n" +" {\n" +" \"pool\": \"192.0.2.1-192.0.2.100\"\n" +" },\n" +" {\n" +" \"pool\": \"192.0.2.200-192.0.2.250\"\n" +" }\n" +" ],\n" +" \"rebind-timer\": 2000,\n" +" \"relay\": {\n" +" \"ip-address\": \"0.0.0.0\"\n" +" },\n" +" \"renew-timer\": 1000,\n" +" \"reservation-mode\": \"all\",\n" +" \"reservations\": [ ],\n" +" \"subnet\": \"192.0.2.0/24\",\n" +" \"valid-lifetime\": 4000\n" +" }\n" +" ]\n" +" }\n", + // CONFIGURATION 32 +"{\n" +" \"decline-probation-period\": 86400,\n" +" \"dhcp-ddns\": {\n" +" \"always-include-fqdn\": false,\n" +" \"enable-updates\": false,\n" +" \"generated-prefix\": \"myhost\",\n" +" \"max-queue-size\": 1024,\n" +" \"ncr-format\": \"JSON\",\n" +" \"ncr-protocol\": \"UDP\",\n" +" \"override-client-update\": false,\n" +" \"override-no-update\": false,\n" +" \"qualifying-suffix\": \"\",\n" +" \"replace-client-name\": \"never\",\n" +" \"sender-ip\": \"0.0.0.0\",\n" +" \"sender-port\": 0,\n" +" \"server-ip\": \"127.0.0.1\",\n" +" \"server-port\": 53001\n" +" },\n" +" \"dhcp4o6-port\": 0,\n" +" \"echo-client-id\": true,\n" +" \"expired-leases-processing\": {\n" +" \"flush-reclaimed-timer-wait-time\": 25,\n" +" \"hold-reclaimed-time\": 3600,\n" +" \"max-reclaim-leases\": 100,\n" +" \"max-reclaim-time\": 250,\n" +" \"reclaim-timer-wait-time\": 10,\n" +" \"unwarned-reclaim-cycles\": 5\n" +" },\n" +" \"hooks-libraries\": [ ],\n" +" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\", \"client-id\" ],\n" +" \"interfaces-config\": {\n" +" \"interfaces\": [ \"*\" ]\n" +" },\n" +" \"lease-database\": {\n" +" \"type\": \"memfile\"\n" +" },\n" " \"option-data\": [\n" " {\n" " \"code\": 1,\n" @@ -3818,7 +4020,7 @@ const char* UNPARSED_CONFIGS[] = { " ],\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 31 + // CONFIGURATION 33 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -3925,7 +4127,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 32 + // CONFIGURATION 34 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4004,7 +4206,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 33 + // CONFIGURATION 35 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4088,7 +4290,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 34 + // CONFIGURATION 36 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4129,7 +4331,7 @@ const char* UNPARSED_CONFIGS[] = { " \"option-def\": [ ],\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 35 + // CONFIGURATION 37 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4170,7 +4372,7 @@ const char* UNPARSED_CONFIGS[] = { " \"option-def\": [ ],\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 36 + // CONFIGURATION 38 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4236,7 +4438,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 37 + // CONFIGURATION 39 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4302,7 +4504,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 38 + // CONFIGURATION 40 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4513,7 +4715,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 39 + // CONFIGURATION 41 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4608,7 +4810,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 40 + // CONFIGURATION 42 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4746,7 +4948,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 41 + // CONFIGURATION 43 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4787,7 +4989,7 @@ const char* UNPARSED_CONFIGS[] = { " \"option-def\": [ ],\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 42 + // CONFIGURATION 44 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4828,7 +5030,7 @@ const char* UNPARSED_CONFIGS[] = { " \"option-def\": [ ],\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 43 + // CONFIGURATION 45 "{\n" " \"decline-probation-period\": 12345,\n" " \"dhcp-ddns\": {\n" @@ -4869,7 +5071,7 @@ const char* UNPARSED_CONFIGS[] = { " \"option-def\": [ ],\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 44 + // CONFIGURATION 46 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4910,7 +5112,7 @@ const char* UNPARSED_CONFIGS[] = { " \"option-def\": [ ],\n" " \"subnet4\": [ ]\n" " }\n", - // CONFIGURATION 45 + // CONFIGURATION 47 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -4976,7 +5178,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 46 + // CONFIGURATION 48 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -5042,7 +5244,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 47 + // CONFIGURATION 49 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -5108,7 +5310,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 48 + // CONFIGURATION 50 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -5174,7 +5376,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 49 + // CONFIGURATION 51 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -5240,7 +5442,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 50 + // CONFIGURATION 52 "{\n" " \"client-classes\": [\n" " {\n" @@ -5329,7 +5531,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 51 + // CONFIGURATION 53 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -5395,7 +5597,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 52 + // CONFIGURATION 54 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -5462,7 +5664,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 53 + // CONFIGURATION 55 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" @@ -5533,7 +5735,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ]\n" " }\n", - // CONFIGURATION 54 + // CONFIGURATION 56 "{\n" " \"decline-probation-period\": 86400,\n" " \"dhcp-ddns\": {\n" diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc.skel b/src/bin/dhcp4/tests/get_config_unittest.cc.skel index 59ac527a1d..477fbc0b43 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc.skel +++ b/src/bin/dhcp4/tests/get_config_unittest.cc.skel @@ -340,6 +340,6 @@ TEST_P(Dhcp4GetConfigTest, run) { /// Define the parametrized test loop INSTANTIATE_TEST_CASE_P(Dhcp4GetConfigTest, Dhcp4GetConfigTest, - ::testing::Range(0UL, max_config_counter)); + ::testing::Range(static_cast(0), max_config_counter)); }; diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index df70d79517..136f213d45 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -335,7 +335,9 @@ CfgSubnets4::toElement() const { if (!isNull(context)) { pool_map->set("user-context", context); } - // Set pool options (not yet supported) + // Set pool options + ConstCfgOptionPtr opts = (*pool)->getCfgOption(); + pool_map->set("option-data", opts->toElement()); // Push on the pool list pool_list->add(pool_map); } diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index 3b7f8f2986..73ceb07bbf 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -749,12 +749,6 @@ PoolParser::parse(PoolStoragePtr pools, ConstElementPtr option_data = pool_structure->get("option-data"); if (option_data) { try { - // Currently we don't support specifying options for the DHCPv4 server. - if (address_family == AF_INET) { - isc_throw(DhcpConfigError, "option-data is not supported for DHCPv4" - " address pools"); - } - CfgOptionPtr cfg = pool->getCfgOption(); OptionDataListParser option_parser(address_family); option_parser.parse(cfg, option_data); diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc index 143522d361..8da257fe59 100644 --- a/src/lib/dhcpsrv/tests/pool_unittest.cc +++ b/src/lib/dhcpsrv/tests/pool_unittest.cc @@ -120,6 +120,58 @@ TEST(Pool4Test, toText) { EXPECT_EQ("type=V4, 192.0.2.128-192.0.2.143", pool2.toText()); } +// This test checks that it is possible to specify pool specific options. +TEST(Pool4Test, addOptions) { + // Create a pool to add options to it. + Pool4Ptr pool(new Pool4(IOAddress("192.0.2.0"), + IOAddress("192.0.2.255"))); + + // Differentiate options by their codes (100-109) + for (uint16_t code = 100; code < 110; ++code) { + OptionPtr option(new Option(Option::V4, code, OptionBuffer(10, 0xFF))); + ASSERT_NO_THROW(pool->getCfgOption()->add(option, false, "dhcp4")); + } + + // Add 7 options to another option space. The option codes partially overlap + // with option codes that we have added to dhcp4 option space. + for (uint16_t code = 105; code < 112; ++code) { + OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF))); + ASSERT_NO_THROW(pool->getCfgOption()->add(option, false, "isc")); + } + + // Get options from the pool and check if all 10 are there. + OptionContainerPtr options = pool->getCfgOption()->getAll("dhcp4"); + ASSERT_TRUE(options); + ASSERT_EQ(10, options->size()); + + // Validate codes of options added to dhcp4 option space. + uint16_t expected_code = 100; + for (OptionContainer::const_iterator option_desc = options->begin(); + option_desc != options->end(); ++option_desc) { + ASSERT_TRUE(option_desc->option_); + EXPECT_EQ(expected_code, option_desc->option_->getType()); + ++expected_code; + } + + options = pool->getCfgOption()->getAll("isc"); + ASSERT_TRUE(options); + ASSERT_EQ(7, options->size()); + + // Validate codes of options added to isc option space. + expected_code = 105; + for (OptionContainer::const_iterator option_desc = options->begin(); + option_desc != options->end(); ++option_desc) { + ASSERT_TRUE(option_desc->option_); + EXPECT_EQ(expected_code, option_desc->option_->getType()); + ++expected_code; + } + + // Try to get options from a non-existing option space. + options = pool->getCfgOption()->getAll("abcd"); + ASSERT_TRUE(options); + EXPECT_TRUE(options->empty()); +} + TEST(Pool6Test, constructor_first_last) { // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool @@ -329,7 +381,7 @@ TEST(Pool6Test, unique_id) { } // Simple check if toText returns reasonable values -TEST(Pool6Test,toText) { +TEST(Pool6Test, toText) { Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8::1"), IOAddress("2001:db8::2")); EXPECT_EQ("type=IA_NA, 2001:db8::1-2001:db8::2, delegated_len=128", -- cgit v1.2.3 From 62d94947c2068583c66d894c2066d0bacc1f6608 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 19 Jul 2017 23:22:30 +0200 Subject: [5314] Subnet to Element conversion moved to SubnetX classes. --- src/lib/dhcpsrv/cfg_subnets4.cc | 101 +-------------- src/lib/dhcpsrv/cfg_subnets6.cc | 166 +----------------------- src/lib/dhcpsrv/subnet.cc | 275 ++++++++++++++++++++++++++++++++++++++++ src/lib/dhcpsrv/subnet.h | 26 +++- 4 files changed, 302 insertions(+), 266 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 136f213d45..e86580e351 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -284,106 +284,7 @@ CfgSubnets4::toElement() const { // Iterate subnets for (Subnet4Collection::const_iterator subnet = subnets_.cbegin(); subnet != subnets_.cend(); ++subnet) { - // Prepare the map - ElementPtr map = Element::createMap(); - // Set subnet id - SubnetID id = (*subnet)->getID(); - map->set("id", Element::create(static_cast(id))); - // Set relay info - const Subnet::RelayInfo& relay_info = (*subnet)->getRelayInfo(); - ElementPtr relay = Element::createMap(); - relay->set("ip-address", Element::create(relay_info.addr_.toText())); - map->set("relay", relay); - // Set subnet - map->set("subnet", Element::create((*subnet)->toText())); - // Set interface - const std::string& iface = (*subnet)->getIface(); - map->set("interface", Element::create(iface)); - // Set renew-timer - map->set("renew-timer", - Element::create(static_cast - ((*subnet)->getT1().get()))); - // Set rebind-timer - map->set("rebind-timer", - Element::create(static_cast - ((*subnet)->getT2().get()))); - // Set valid-lifetime - map->set("valid-lifetime", - Element::create(static_cast - ((*subnet)->getValid().get()))); - // Set pools - const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_V4); - ElementPtr pool_list = Element::createList(); - for (PoolCollection::const_iterator pool = pools.cbegin(); - pool != pools.cend(); ++pool) { - // Prepare the map for a pool (@todo move this code to pool.cc) - ElementPtr pool_map = Element::createMap(); - // Set pool - const IOAddress& first = (*pool)->getFirstAddress(); - const IOAddress& last = (*pool)->getLastAddress(); - std::string range = first.toText() + "-" + last.toText(); - // Try to output a prefix (vs a range) - int prefix_len = prefixLengthFromRange(first, last); - if (prefix_len >= 0) { - std::ostringstream oss; - oss << first.toText() << "/" << prefix_len; - range = oss.str(); - } - pool_map->set("pool", Element::create(range)); - // Set user-context - ConstElementPtr context = (*pool)->getContext(); - if (!isNull(context)) { - pool_map->set("user-context", context); - } - // Set pool options - ConstCfgOptionPtr opts = (*pool)->getCfgOption(); - pool_map->set("option-data", opts->toElement()); - // Push on the pool list - pool_list->add(pool_map); - } - map->set("pools", pool_list); - // Set host reservation-mode - Subnet::HRMode hrmode = (*subnet)->getHostReservationMode(); - std::string mode; - switch (hrmode) { - case Subnet::HR_DISABLED: - mode = "disabled"; - break; - case Subnet::HR_OUT_OF_POOL: - mode = "out-of-pool"; - break; - case Subnet::HR_ALL: - mode = "all"; - break; - default: - isc_throw(ToElementError, - "invalid host reservation mode: " << hrmode); - } - map->set("reservation-mode", Element::create(mode)); - // Set match-client-id - map->set("match-client-id", - Element::create((*subnet)->getMatchClientId())); - // Set next-server - map->set("next-server", - Element::create((*subnet)->getSiaddr().toText())); - // Set DHCP4o6 - const Cfg4o6& d4o6 = (*subnet)->get4o6(); - isc::data::merge(map, d4o6.toElement()); - // Set client-class - const ClientClasses& cclasses = (*subnet)->getClientClasses(); - if (cclasses.size() > 1) { - isc_throw(ToElementError, "client-class has too many items: " - << cclasses.size()); - } else if (!cclasses.empty()) { - map->set("client-class", Element::create(*cclasses.cbegin())); - } - // Set options - ConstCfgOptionPtr opts = (*subnet)->getCfgOption(); - map->set("option-data", opts->toElement()); - // Not supported: interface-id - // Not supported: rapid-commit - // Push on the list - result->add(map); + result->add((*subnet)->toElement()); } return (result); } diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index a795fe4a60..439bcbf616 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -240,171 +240,7 @@ CfgSubnets6::toElement() const { // Iterate subnets for (Subnet6Collection::const_iterator subnet = subnets_.cbegin(); subnet != subnets_.cend(); ++subnet) { - // Prepare the map - ElementPtr map = Element::createMap(); - // Set subnet id - SubnetID id = (*subnet)->getID(); - map->set("id", Element::create(static_cast(id))); - // Set relay info - const Subnet::RelayInfo& relay_info = (*subnet)->getRelayInfo(); - ElementPtr relay = Element::createMap(); - relay->set("ip-address", Element::create(relay_info.addr_.toText())); - map->set("relay", relay); - // Set subnet - map->set("subnet", Element::create((*subnet)->toText())); - // Set interface - const std::string& iface = (*subnet)->getIface(); - map->set("interface", Element::create(iface)); - // Set interface-id - const OptionPtr& ifaceid = (*subnet)->getInterfaceId(); - if (ifaceid) { - std::vector bin = ifaceid->getData(); - std::string ifid; - ifid.resize(bin.size()); - if (!bin.empty()) { - std::memcpy(&ifid[0], &bin[0], bin.size()); - } - map->set("interface-id", Element::create(ifid)); - } else { - map->set("interface-id", Element::create(std::string())); - } - // Set renew-timer - map->set("renew-timer", - Element::create(static_cast - ((*subnet)->getT1().get()))); - // Set rebind-timer - map->set("rebind-timer", - Element::create(static_cast - ((*subnet)->getT2().get()))); - // Set preferred-lifetime - map->set("preferred-lifetime", - Element::create(static_cast - ((*subnet)->getPreferred().get()))); - // Set valid-lifetime - map->set("valid-lifetime", - Element::create(static_cast - ((*subnet)->getValid().get()))); - // Set rapid-commit - bool rapid_commit = (*subnet)->getRapidCommit(); - map->set("rapid-commit", Element::create(rapid_commit)); - // Set pools - const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_NA); - ElementPtr pool_list = Element::createList(); - for (PoolCollection::const_iterator pool = pools.cbegin(); - pool != pools.cend(); ++pool) { - // Prepare the map for a pool (@todo move this code to pool.cc) - ElementPtr pool_map = Element::createMap(); - // Set pool - const IOAddress& first = (*pool)->getFirstAddress(); - const IOAddress& last = (*pool)->getLastAddress(); - std::string range = first.toText() + "-" + last.toText(); - // Try to output a prefix (vs a range) - int prefix_len = prefixLengthFromRange(first, last); - if (prefix_len >= 0) { - std::ostringstream oss; - oss << first.toText() << "/" << prefix_len; - range = oss.str(); - } - pool_map->set("pool", Element::create(range)); - // Set user-context - ConstElementPtr context = (*pool)->getContext(); - if (!isNull(context)) { - pool_map->set("user-context", context); - } - // Set pool options - ConstCfgOptionPtr opts = (*pool)->getCfgOption(); - pool_map->set("option-data", opts->toElement()); - // Push on the pool list - pool_list->add(pool_map); - } - map->set("pools", pool_list); - // Set pd-pools - const PoolCollection& pdpools = (*subnet)->getPools(Lease::TYPE_PD); - ElementPtr pdpool_list = Element::createList(); - for (PoolCollection::const_iterator pool = pdpools.cbegin(); - pool != pdpools.cend(); ++pool) { - // Get it as a Pool6 (@todo move this code to pool.cc) - const Pool6* pdpool = dynamic_cast(pool->get()); - if (!pdpool) { - isc_throw(ToElementError, "invalid pd-pool pointer"); - } - // Prepare the map for a pd-pool - ElementPtr pool_map = Element::createMap(); - // Set prefix - const IOAddress& prefix = pdpool->getFirstAddress(); - pool_map->set("prefix", Element::create(prefix.toText())); - // Set prefix-len (get it from min - max) - const IOAddress& last = pdpool->getLastAddress(); - int prefix_len = prefixLengthFromRange(prefix, last); - if (prefix_len < 0) { - // The pool is bad: give up - isc_throw(ToElementError, "invalid prefix range " - << prefix.toText() << "-" << last.toText()); - } - pool_map->set("prefix-len", Element::create(prefix_len)); - // Set delegated-len - uint8_t len = pdpool->getLength(); - pool_map->set("delegated-len", - Element::create(static_cast(len))); - // Set excluded prefix - const Option6PDExcludePtr& xopt = - pdpool->getPrefixExcludeOption(); - if (xopt) { - const IOAddress& xprefix = - xopt->getExcludedPrefix(prefix, len); - pool_map->set("excluded-prefix", - Element::create(xprefix.toText())); - uint8_t xlen = xopt->getExcludedPrefixLength(); - pool_map->set("excluded-prefix-len", - Element::create(static_cast(xlen))); - } else { - pool_map->set("excluded-prefix", - Element::create(std::string("::"))); - pool_map->set("excluded-prefix-len", Element::create(0)); - } - // Set user-context - ConstElementPtr context = pdpool->getContext(); - if (!isNull(context)) { - pool_map->set("user-context", context); - } - // Set pool options - ConstCfgOptionPtr opts = pdpool->getCfgOption(); - pool_map->set("option-data", opts->toElement()); - // Push on the pool list - pdpool_list->add(pool_map); - } - map->set("pd-pools", pdpool_list); - // Set host reservation-mode - Subnet::HRMode hrmode = (*subnet)->getHostReservationMode(); - std::string mode; - switch (hrmode) { - case Subnet::HR_DISABLED: - mode = "disabled"; - break; - case Subnet::HR_OUT_OF_POOL: - mode = "out-of-pool"; - break; - case Subnet::HR_ALL: - mode = "all"; - break; - default: - isc_throw(ToElementError, - "invalid host reservation mode: " << hrmode); - } - map->set("reservation-mode", Element::create(mode)); - // Set client-class - const ClientClasses& cclasses = (*subnet)->getClientClasses(); - if (cclasses.size() > 1) { - isc_throw(ToElementError, "client-class has too many items: " - << cclasses.size()); - } else if (!cclasses.empty()) { - map->set("client-class", Element::create(*cclasses.cbegin())); - } - // Set options - ConstCfgOptionPtr opts = (*subnet)->getCfgOption(); - map->set("option-data", opts->toElement()); - // Push on the list - result->add(map); + result->add((*subnet)->toElement()); } return (result); } diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index f0874099cb..6752b76b0d 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -14,6 +14,7 @@ #include using namespace isc::asiolink; +using namespace isc::data; using namespace isc::dhcp; namespace { @@ -472,5 +473,279 @@ void Subnet6::checkType(Lease::Type type) const { } } +data::ElementPtr +Subnet4::toElement() const { + // Prepare the map + ElementPtr map = Element::createMap(); + // Set subnet id + SubnetID id = getID(); + map->set("id", Element::create(static_cast(id))); + // Set relay info + const Subnet::RelayInfo& relay_info = getRelayInfo(); + ElementPtr relay = Element::createMap(); + relay->set("ip-address", Element::create(relay_info.addr_.toText())); + map->set("relay", relay); + // Set subnet + map->set("subnet", Element::create(toText())); + // Set interface + const std::string& iface = getIface(); + map->set("interface", Element::create(iface)); + // Set renew-timer + map->set("renew-timer", + Element::create(static_cast + (getT1().get()))); + // Set rebind-timer + map->set("rebind-timer", + Element::create(static_cast + (getT2().get()))); + // Set valid-lifetime + map->set("valid-lifetime", + Element::create(static_cast + (getValid().get()))); + // Set pools + const PoolCollection& pools = getPools(Lease::TYPE_V4); + ElementPtr pool_list = Element::createList(); + for (PoolCollection::const_iterator pool = pools.cbegin(); + pool != pools.cend(); ++pool) { + // Prepare the map for a pool (@todo move this code to pool.cc) + ElementPtr pool_map = Element::createMap(); + // Set pool + const IOAddress& first = (*pool)->getFirstAddress(); + const IOAddress& last = (*pool)->getLastAddress(); + std::string range = first.toText() + "-" + last.toText(); + // Try to output a prefix (vs a range) + int prefix_len = prefixLengthFromRange(first, last); + if (prefix_len >= 0) { + std::ostringstream oss; + oss << first.toText() << "/" << prefix_len; + range = oss.str(); + } + pool_map->set("pool", Element::create(range)); + // Set user-context + ConstElementPtr context = (*pool)->getContext(); + if (!isNull(context)) { + pool_map->set("user-context", context); + } + // Set pool options + ConstCfgOptionPtr opts = (*pool)->getCfgOption(); + pool_map->set("option-data", opts->toElement()); + // Push on the pool list + pool_list->add(pool_map); + } + map->set("pools", pool_list); + // Set host reservation-mode + Subnet::HRMode hrmode = getHostReservationMode(); + std::string mode; + switch (hrmode) { + case Subnet::HR_DISABLED: + mode = "disabled"; + break; + case Subnet::HR_OUT_OF_POOL: + mode = "out-of-pool"; + break; + case Subnet::HR_ALL: + mode = "all"; + break; + default: + isc_throw(ToElementError, + "invalid host reservation mode: " << hrmode); + } + map->set("reservation-mode", Element::create(mode)); + // Set match-client-id + map->set("match-client-id", + Element::create(getMatchClientId())); + // Set next-server + map->set("next-server", + Element::create(getSiaddr().toText())); + // Set DHCP4o6 + const Cfg4o6& d4o6 = get4o6(); + isc::data::merge(map, d4o6.toElement()); + // Set client-class + const ClientClasses& cclasses = getClientClasses(); + if (cclasses.size() > 1) { + isc_throw(ToElementError, "client-class has too many items: " + << cclasses.size()); + } else if (!cclasses.empty()) { + map->set("client-class", Element::create(*cclasses.cbegin())); + } + // Set options + ConstCfgOptionPtr opts = getCfgOption(); + map->set("option-data", opts->toElement()); + // Not supported: interface-id + // Not supported: rapid-commit + + return (map); +} + +data::ElementPtr +Subnet6::toElement() const { + // Prepare the map + ElementPtr map = Element::createMap(); + // Set subnet id + SubnetID id = getID(); + map->set("id", Element::create(static_cast(id))); + // Set relay info + const Subnet::RelayInfo& relay_info = getRelayInfo(); + ElementPtr relay = Element::createMap(); + relay->set("ip-address", Element::create(relay_info.addr_.toText())); + map->set("relay", relay); + // Set subnet + map->set("subnet", Element::create(toText())); + // Set interface + const std::string& iface = getIface(); + map->set("interface", Element::create(iface)); + // Set interface-id + const OptionPtr& ifaceid = getInterfaceId(); + if (ifaceid) { + std::vector bin = ifaceid->getData(); + std::string ifid; + ifid.resize(bin.size()); + if (!bin.empty()) { + std::memcpy(&ifid[0], &bin[0], bin.size()); + } + map->set("interface-id", Element::create(ifid)); + } else { + map->set("interface-id", Element::create(std::string())); + } + // Set renew-timer + map->set("renew-timer", + Element::create(static_cast + (getT1().get()))); + // Set rebind-timer + map->set("rebind-timer", + Element::create(static_cast + (getT2().get()))); + // Set preferred-lifetime + map->set("preferred-lifetime", + Element::create(static_cast + (getPreferred().get()))); + // Set valid-lifetime + map->set("valid-lifetime", + Element::create(static_cast + (getValid().get()))); + // Set rapid-commit + bool rapid_commit = getRapidCommit(); + map->set("rapid-commit", Element::create(rapid_commit)); + // Set pools + const PoolCollection& pools = getPools(Lease::TYPE_NA); + ElementPtr pool_list = Element::createList(); + for (PoolCollection::const_iterator pool = pools.cbegin(); + pool != pools.cend(); ++pool) { + // Prepare the map for a pool (@todo move this code to pool.cc) + ElementPtr pool_map = Element::createMap(); + // Set pool + const IOAddress& first = (*pool)->getFirstAddress(); + const IOAddress& last = (*pool)->getLastAddress(); + std::string range = first.toText() + "-" + last.toText(); + // Try to output a prefix (vs a range) + int prefix_len = prefixLengthFromRange(first, last); + if (prefix_len >= 0) { + std::ostringstream oss; + oss << first.toText() << "/" << prefix_len; + range = oss.str(); + } + pool_map->set("pool", Element::create(range)); + // Set user-context + ConstElementPtr context = (*pool)->getContext(); + if (!isNull(context)) { + pool_map->set("user-context", context); + } + // Set pool options + ConstCfgOptionPtr opts = (*pool)->getCfgOption(); + pool_map->set("option-data", opts->toElement()); + // Push on the pool list + pool_list->add(pool_map); + } + map->set("pools", pool_list); + // Set pd-pools + const PoolCollection& pdpools = getPools(Lease::TYPE_PD); + ElementPtr pdpool_list = Element::createList(); + for (PoolCollection::const_iterator pool = pdpools.cbegin(); + pool != pdpools.cend(); ++pool) { + // Get it as a Pool6 (@todo move this code to pool.cc) + const Pool6* pdpool = dynamic_cast(pool->get()); + if (!pdpool) { + isc_throw(ToElementError, "invalid pd-pool pointer"); + } + // Prepare the map for a pd-pool + ElementPtr pool_map = Element::createMap(); + // Set prefix + const IOAddress& prefix = pdpool->getFirstAddress(); + pool_map->set("prefix", Element::create(prefix.toText())); + // Set prefix-len (get it from min - max) + const IOAddress& last = pdpool->getLastAddress(); + int prefix_len = prefixLengthFromRange(prefix, last); + if (prefix_len < 0) { + // The pool is bad: give up + isc_throw(ToElementError, "invalid prefix range " + << prefix.toText() << "-" << last.toText()); + } + pool_map->set("prefix-len", Element::create(prefix_len)); + // Set delegated-len + uint8_t len = pdpool->getLength(); + pool_map->set("delegated-len", + Element::create(static_cast(len))); + // Set excluded prefix + const Option6PDExcludePtr& xopt = + pdpool->getPrefixExcludeOption(); + if (xopt) { + const IOAddress& xprefix = + xopt->getExcludedPrefix(prefix, len); + pool_map->set("excluded-prefix", + Element::create(xprefix.toText())); + uint8_t xlen = xopt->getExcludedPrefixLength(); + pool_map->set("excluded-prefix-len", + Element::create(static_cast(xlen))); + } else { + pool_map->set("excluded-prefix", + Element::create(std::string("::"))); + pool_map->set("excluded-prefix-len", Element::create(0)); + } + // Set user-context + ConstElementPtr context = pdpool->getContext(); + if (!isNull(context)) { + pool_map->set("user-context", context); + } + // Set pool options + ConstCfgOptionPtr opts = pdpool->getCfgOption(); + pool_map->set("option-data", opts->toElement()); + // Push on the pool list + pdpool_list->add(pool_map); + } + map->set("pd-pools", pdpool_list); + // Set host reservation-mode + Subnet::HRMode hrmode = getHostReservationMode(); + std::string mode; + switch (hrmode) { + case Subnet::HR_DISABLED: + mode = "disabled"; + break; + case Subnet::HR_OUT_OF_POOL: + mode = "out-of-pool"; + break; + case Subnet::HR_ALL: + mode = "all"; + break; + default: + isc_throw(ToElementError, + "invalid host reservation mode: " << hrmode); + } + map->set("reservation-mode", Element::create(mode)); + // Set client-class + const ClientClasses& cclasses = getClientClasses(); + if (cclasses.size() > 1) { + isc_throw(ToElementError, "client-class has too many items: " + << cclasses.size()); + } else if (!cclasses.empty()) { + map->set("client-class", Element::create(*cclasses.cbegin())); + } + // Set options + ConstCfgOptionPtr opts = getCfgOption(); + map->set("option-data", opts->toElement()); + + return (map); +} + + } // end of isc::dhcp namespace } // end of isc namespace diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 683ca4b6da..e736fb6e89 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -8,6 +8,7 @@ #define SUBNET_H #include +#include #include #include #include @@ -280,7 +281,7 @@ public: /// returned it is valid. /// /// @return const reference to the relay information - const isc::dhcp::Subnet::RelayInfo& getRelayInfo() { + const isc::dhcp::Subnet::RelayInfo& getRelayInfo() const { return (relay_); } @@ -429,6 +430,11 @@ protected: /// type. bool poolOverlaps(const Lease::Type& pool_type, const PoolPtr& pool) const; + /// @brief Unparse a subnet object. + /// + /// @return A pointer to unparsed subnet configuration. + virtual data::ElementPtr toElement() const = 0; + /// @brief subnet-id /// /// Subnet-id is a unique value that can be used to find or identify @@ -577,6 +583,19 @@ public: return (dhcp4o6_); } + /// @brief Returns const DHCP4o6 configuration parameters. + /// + /// This structure is always available. If the 4o6 is not enabled, its + /// enabled_ field will be set to false. + const Cfg4o6& get4o6() const { + return (dhcp4o6_); + } + + /// @brief Unparse a subnet object. + /// + /// @return A pointer to unparsed subnet configuration. + virtual data::ElementPtr toElement() const; + private: /// @brief Returns default address for pool selection @@ -676,6 +695,11 @@ public: return (rapid_commit_); } + /// @brief Unparse a subnet object. + /// + /// @return A pointer to unparsed subnet configuration. + virtual data::ElementPtr toElement() const; + private: /// @brief Returns default address for pool selection -- cgit v1.2.3 From 63d6755e7c59091ff8ce942421ea393877c6ad25 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 20 Jul 2017 19:03:52 +0200 Subject: [5314] Added indexing to subnet searches. --- src/lib/dhcpsrv/cfg_subnets4.cc | 14 ++++ src/lib/dhcpsrv/cfg_subnets4.h | 36 ++++++++++ src/lib/dhcpsrv/cfg_subnets6.cc | 14 ++++ src/lib/dhcpsrv/cfg_subnets6.h | 38 ++++++++++- src/lib/dhcpsrv/subnet.h | 93 +++++++++++++++++++++++--- src/lib/dhcpsrv/tests/alloc_engine_utils.cc | 2 + src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc | 51 ++++++++++++++ src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc | 51 ++++++++++++++ src/lib/dhcpsrv/tests/srv_config_unittest.cc | 3 +- 9 files changed, 289 insertions(+), 13 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index e86580e351..26b079b2c9 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -34,6 +34,20 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) { subnets_.push_back(subnet); } +ConstSubnet4Ptr +CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const { + const auto& index = subnets_.get(); + auto subnet_it = index.find(subnet_id); + return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr()); +} + +ConstSubnet4Ptr +CfgSubnets4::getByPrefix(const std::string& subnet_text) const { + const auto& index = subnets_.get(); + auto subnet_it = index.find(subnet_text); + return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr()); +} + Subnet4Ptr CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const { diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index a065ddc7a5..5c0d31bcff 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -10,8 +10,10 @@ #include #include #include +#include #include #include +#include namespace isc { namespace dhcp { @@ -48,6 +50,40 @@ public: return (&subnets_); } + /// @brief Returns const pointer to a subnet identified by the specified + /// subnet identifier. + /// + /// The const pointer is returned by this method to prevent a caller from + /// modifying the subnet configuration. Modifications to subnet configuration + /// is dangerous and must be done carefully. The subnets' configruation is + /// held in the multi index container and any modifications to the subnet + /// id or subnet prefix must trigger re-indexing of multi index container. + /// There is no possibility to enforce this when the non-const pointer is + /// returned. + /// + /// @param subnet_id Subnet identifier. + /// + /// @return Pointer to the @c Subnet4 object or null pointer if such + /// subnet doesn't exist. + ConstSubnet4Ptr getBySubnetId(const SubnetID& subnet_id) const; + + /// @brief Returns const pointer to a subnet which matches the specified + /// prefix in the canonical form. + /// + /// The const pointer is returned by this method to prevent a caller from + /// modifying the subnet configuration. Modifications to subnet configuration + /// is dangerous and must be done carefully. The subnets' configruation is + /// held in the multi index container and any modifications to the subnet + /// id or subnet prefix must trigger re-indexing of multi index container. + /// There is no possibility to enforce this when the non-const pointer is + /// returned. + /// + /// @param subnet_prefix Subnet prefix, e.g. 10.2.3.0/24 + /// + /// @return Pointer to the @c Subnet4 object or null pointer if such + /// subnet doesn't exist. + ConstSubnet4Ptr getByPrefix(const std::string& subnet_prefix) const; + /// @brief Returns a pointer to the selected subnet. /// /// This method tries to retrieve the subnet for the client using various diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index 439bcbf616..491fdb227e 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -33,6 +33,20 @@ CfgSubnets6::add(const Subnet6Ptr& subnet) { subnets_.push_back(subnet); } +ConstSubnet6Ptr +CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const { + const auto& index = subnets_.get(); + auto subnet_it = index.find(subnet_id); + return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr()); +} + +ConstSubnet6Ptr +CfgSubnets6::getByPrefix(const std::string& subnet_text) const { + const auto& index = subnets_.get(); + auto subnet_it = index.find(subnet_text); + return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr()); +} + Subnet6Ptr CfgSubnets6::selectSubnet(const SubnetSelector& selector) const { Subnet6Ptr subnet; diff --git a/src/lib/dhcpsrv/cfg_subnets6.h b/src/lib/dhcpsrv/cfg_subnets6.h index a027dd7d31..8ea9151888 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.h +++ b/src/lib/dhcpsrv/cfg_subnets6.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015,2017 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 @@ -11,9 +11,11 @@ #include #include #include +#include #include #include #include +#include namespace isc { namespace dhcp { @@ -49,6 +51,40 @@ public: return (&subnets_); } + /// @brief Returns const pointer to a subnet identified by the specified + /// subnet identifier. + /// + /// The const pointer is returned by this method to prevent a caller from + /// modifying the subnet configuration. Modifications to subnet configuration + /// is dangerous and must be done carefully. The subnets' configruation is + /// held in the multi index container and any modifications to the subnet + /// id or subnet prefix must trigger re-indexing of multi index container. + /// There is no possibility to enforce this when the non-const pointer is + /// returned. + /// + /// @param subnet_id Subnet identifier. + /// + /// @return Pointer to the @c Subnet6 object or null pointer if such + /// subnet doesn't exist. + ConstSubnet6Ptr getBySubnetId(const SubnetID& subnet_id) const; + + /// @brief Returns const pointer to a subnet which matches the specified + /// prefix in the canonical form. + /// + /// The const pointer is returned by this method to prevent a caller from + /// modifying the subnet configuration. Modifications to subnet configuration + /// is dangerous and must be done carefully. The subnets' configruation is + /// held in the multi index container and any modifications to the subnet + /// id or subnet prefix must trigger re-indexing of multi index container. + /// There is no possibility to enforce this when the non-const pointer is + /// returned. + /// + /// @param subnet_prefix Subnet prefix, e.g. 2001:db8:1::/64 + /// + /// @return Pointer to the @c Subnet6 object or null pointer if such + /// subnet doesn't exist. + ConstSubnet6Ptr getByPrefix(const std::string& subnet_prefix) const; + /// @brief Selects a subnet using parameters specified in the selector. /// /// This method tries to retrieve the subnet for the client using various diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index e736fb6e89..c03925244d 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -19,6 +19,11 @@ #include #include +#include +#include +#include +#include +#include #include namespace isc { @@ -623,16 +628,12 @@ private: Cfg4o6 dhcp4o6_; }; -/// @brief A pointer to a @c Subnet4 object +/// @brief A const pointer to a @c Subnet4 object. +typedef boost::shared_ptr ConstSubnet4Ptr; + +/// @brief A pointer to a @c Subnet4 object. typedef boost::shared_ptr Subnet4Ptr; -/// @brief A collection of @c Subnet4 objects -/// -/// That is a simple vector of pointers. It does not make much sense to -/// optimize access time (e.g. using a map), because typical search -/// pattern will use calling inRange() method on each subnet until -/// a match is found. -typedef std::vector Subnet4Collection; /// @brief A configuration holder for IPv6 subnet. /// @@ -731,11 +732,83 @@ private: }; +/// @brief A const pointer to a @c Subnet6 object. +typedef boost::shared_ptr ConstSubnet6Ptr; + /// @brief A pointer to a Subnet6 object typedef boost::shared_ptr Subnet6Ptr; -/// @brief A collection of Subnet6 objects -typedef std::vector Subnet6Collection; +/// @name Definition of the multi index container holding subnet information +/// +//@{ + +/// @brief Tag for the random access index. +struct SubnetRandomAccessIndexTag { }; + +/// @brief Tag for the index for searching by subnet identifier. +struct SubnetIdIndexTag { }; + +/// @brief Tag for the index for searching by subnet prefix. +struct SubnetPrefixIndexTag { }; + +/// @brief Multi index container holding subnets. +/// +/// This multi index container can hold pointers to @ref Subnet4 or +/// @ref Subnet6 objects representing subnets. It provides indexes for +/// subnet lookups using subnet properties such as: subnet identifier +/// or subnet prefix. It also provides a random access index which +/// allows for using the container like a vector. +/// +/// The random access index is used by the DHCP servers which perform +/// a full scan on subnets to find the one that matches some specific +/// criteria for subnet selection. +/// +/// The remaining indexes are used for searching for a specific subnet +/// as a result of receiving a command over the control API, e.g. +/// when 'subnet-get' command is received. +/// +/// @todo We should consider optimizing subnet selection by leveraging +/// the indexing capabilities of this container, e.g. searching for +/// a subnet by interface name, relay address etc. +/// +/// @tparam SubnetType Type of the subnet: @ref Subnet4 or @ref Subnet6. +template +using SubnetCollection = boost::multi_index_container< + // Multi index container holds pointers to the subnets. + boost::shared_ptr, + // The following holds all indexes. + boost::multi_index::indexed_by< + // First is the random access index allowing for accessing + // objects just like we'd do with a vector. + boost::multi_index::random_access< + boost::multi_index::tag + >, + // Second index allows for searching using subnet identifier. + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun + >, + // Third index allows for searching using an output from toText function. + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun + > + > +>; + +/// @brief A collection of @c Subnet4 objects +/// +/// This container provides a set of indexes which can be used to retrieve +/// subnets by various properties. +typedef SubnetCollection Subnet4Collection; + +/// @brief A collection of @c Subnet6 objects +/// +/// This container provides a set of indexes which can be used to retrieve +/// subnets by various properties. +typedef SubnetCollection Subnet6Collection; + +//@} } // end of isc::dhcp namespace } // end of isc namespace diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc index 063c4b7daa..dc495a2e3e 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc @@ -529,6 +529,8 @@ AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start, AllocEngine4Test::AllocEngine4Test() { + CfgMgr::instance().clear(); + // This lease mgr needs to exist to before configuration commits. factory_.create("type=memfile universe=4 persist=false"); diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index d7433f2d8c..dc6dd1dae6 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -23,6 +23,57 @@ using namespace isc::test; namespace { +// This test verifies that specific subnet can be retrieved by specifying +// subnet identifier or subnet prefix. +TEST(CfgSubnets4Test, getSpecificSubnet) { + CfgSubnets4 cfg; + + // Create 3 subnets. + Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), + 26, 1, 2, 3, SubnetID(5))); + Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), + 26, 1, 2, 3, SubnetID(8))); + Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), + 26, 1, 2, 3, SubnetID(10))); + + // Store the subnets in a vector to make it possible to loop over + // all configured subnets. + std::vector subnets; + subnets.push_back(subnet1); + subnets.push_back(subnet2); + subnets.push_back(subnet3); + + // Add all subnets to the configuration. + for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) { + ASSERT_NO_THROW(cfg.add(*subnet)) << "failed to add subnet with id: " + << (*subnet)->getID(); + } + + // Iterate over all subnets and make sure they can be retrieved by + // subnet identifier. + for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) { + ConstSubnet4Ptr subnet_returned = cfg.getBySubnetId((*subnet)->getID()); + ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: " + << (*subnet)->getID(); + EXPECT_EQ((*subnet)->getID(), subnet_returned->getID()); + EXPECT_EQ((*subnet)->toText(), subnet_returned->toText()); + } + + // Repeat the previous test, but this time retrieve subnets by their + // prefixes. + for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) { + ConstSubnet4Ptr subnet_returned = cfg.getByPrefix((*subnet)->toText()); + ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: " + << (*subnet)->getID(); + EXPECT_EQ((*subnet)->getID(), subnet_returned->getID()); + EXPECT_EQ((*subnet)->toText(), subnet_returned->toText()); + } + + // Make sure that null pointers are returned for non-existing subnets. + EXPECT_FALSE(cfg.getBySubnetId(SubnetID(123))); + EXPECT_FALSE(cfg.getByPrefix("10.20.30.0/29")); +} + // This test verifies that it is possible to retrieve a subnet using an // IP address. TEST(CfgSubnets4Test, selectSubnetByCiaddr) { diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index 5df42700a6..822cf6b892 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -32,6 +32,57 @@ generateInterfaceId(const std::string& text) { return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, buffer)); } +// This test verifies that specific subnet can be retrieved by specifying +// subnet identifier or subnet prefix. +TEST(CfgSubnets6Test, getSpecificSubnet) { + CfgSubnets6 cfg; + + // Create 3 subnets. + Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, + SubnetID(5))); + Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4, + SubnetID(8))); + Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4, + SubnetID(10))); + + // Store the subnets in a vector to make it possible to loop over + // all configured subnets. + std::vector subnets; + subnets.push_back(subnet1); + subnets.push_back(subnet2); + subnets.push_back(subnet3); + + // Add all subnets to the configuration. + for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) { + ASSERT_NO_THROW(cfg.add(*subnet)) << "failed to add subnet with id: " + << (*subnet)->getID(); + } + + // Iterate over all subnets and make sure they can be retrieved by + // subnet identifier. + for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) { + ConstSubnet6Ptr subnet_returned = cfg.getBySubnetId((*subnet)->getID()); + ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: " + << (*subnet)->getID(); + EXPECT_EQ((*subnet)->getID(), subnet_returned->getID()); + EXPECT_EQ((*subnet)->toText(), subnet_returned->toText()); + } + + // Repeat the previous test, but this time retrieve subnets by their + // prefixes. + for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) { + ConstSubnet6Ptr subnet_returned = cfg.getByPrefix((*subnet)->toText()); + ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: " + << (*subnet)->getID(); + EXPECT_EQ((*subnet)->getID(), subnet_returned->getID()); + EXPECT_EQ((*subnet)->toText(), subnet_returned->toText()); + } + + // Make sure that null pointers are returned for non-existing subnets. + EXPECT_FALSE(cfg.getBySubnetId(SubnetID(123))); + EXPECT_FALSE(cfg.getByPrefix("3000::/16")); +} + // This test checks that the subnet can be selected using a relay agent's // link address. TEST(CfgSubnets6Test, selectSubnetByRelayAddress) { diff --git a/src/lib/dhcpsrv/tests/srv_config_unittest.cc b/src/lib/dhcpsrv/tests/srv_config_unittest.cc index 1a185dc7b8..fa465e7962 100644 --- a/src/lib/dhcpsrv/tests/srv_config_unittest.cc +++ b/src/lib/dhcpsrv/tests/srv_config_unittest.cc @@ -51,10 +51,10 @@ public: test_subnets4_.push_back(subnet); } // Create IPv6 subnets. + IOAddress prefix("2001:db8:1::0"); for (int i = 0; i < TEST_SUBNETS_NUM; ++i) { // This is a base prefix. All other prefixes will be created by // modifying this one. - IOAddress prefix("2001:db8:1::0"); std::vector prefix_bytes = prefix.toBytes(); // Modify 5th byte of the prefix, so 2001:db8:1::0 becomes // 2001:db8:2::0 etc. @@ -239,7 +239,6 @@ TEST_F(SrvConfigTest, summarySubnets) { addSubnet6(1); EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 2", conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET)); - } // Verifies that we can get and set the client class dictionary -- cgit v1.2.3 From c16e3e91e6430e9b7e37c5c91df1df93ea7501dd Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 1 Aug 2017 20:52:15 +0200 Subject: [5272] getSubnet implemented. --- src/lib/dhcpsrv/cfg_subnets4.cc | 13 +++++++++++++ src/lib/dhcpsrv/cfg_subnets4.h | 8 ++++++++ src/lib/dhcpsrv/cfg_subnets6.cc | 14 ++++++++++++++ src/lib/dhcpsrv/cfg_subnets6.h | 8 ++++++++ src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc | 22 +++++++++++++++++++++- src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc | 24 +++++++++++++++++++++--- 6 files changed, 85 insertions(+), 4 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 136f213d45..ef3579ce69 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -195,6 +195,19 @@ CfgSubnets4::selectSubnet(const std::string& iface, return (Subnet4Ptr()); } +Subnet4Ptr +CfgSubnets4::getSubnet(const SubnetID id) const { + + /// @todo: Once this code is migrated to multi-index container, use + /// an index rather than full scan. + for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) { + if ((*subnet)->getID() == id) { + return (*subnet); + } + } + return (Subnet4Ptr()); +} + Subnet4Ptr CfgSubnets4::selectSubnet(const IOAddress& address, const ClientClasses& client_classes) const { diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index c2b72429e0..d37aa112af 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -97,6 +97,14 @@ public: /// or they are insufficient to select a subnet. Subnet4Ptr selectSubnet(const SubnetSelector& selector) const; + /// @brief Returns subnet with specified subnet-id value + /// + /// Warning: this method uses full scan. Its use is not recommeded for + /// packet processing. + /// + /// @return Subnet (or NULL) + Subnet4Ptr getSubnet(const SubnetID id) const; + /// @brief Returns a pointer to a subnet if provided address is in its range. /// /// This method returns a pointer to the subnet if the address passed in diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index a795fe4a60..03ec44f84e 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -166,6 +166,20 @@ CfgSubnets6::selectSubnet(const OptionPtr& interface_id, return (Subnet6Ptr()); } +Subnet6Ptr +CfgSubnets6::getSubnet(const SubnetID id) const { + + /// @todo: Once this code is migrated to multi-index container, use + /// an index rather than full scan. + for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) { + if ((*subnet)->getID() == id) { + return (*subnet); + } + } + return (Subnet6Ptr()); +} + + bool CfgSubnets6::isDuplicate(const Subnet6& subnet) const { for (Subnet6Collection::const_iterator subnet_it = subnets_.begin(); diff --git a/src/lib/dhcpsrv/cfg_subnets6.h b/src/lib/dhcpsrv/cfg_subnets6.h index 18e1ca866f..e5ed5adb06 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.h +++ b/src/lib/dhcpsrv/cfg_subnets6.h @@ -87,6 +87,14 @@ public: /// @return Pointer to the selected subnet or NULL if no subnet found. Subnet6Ptr selectSubnet(const SubnetSelector& selector) const; + /// @brief Returns subnet with specified subnet-id value + /// + /// Warning: this method uses full scan. Its use is not recommeded for + /// packet processing. + /// + /// @return Subnet (or NULL) + Subnet6Ptr getSubnet(const SubnetID id) const; + /// @brief Selects the subnet using a specified address. /// /// This method searches for the subnet using the specified address. If diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index d7433f2d8c..2ff407a829 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -524,7 +524,7 @@ TEST(CfgSubnets4Test, unparsePool) { subnet->addPool(pool1); subnet->addPool(pool2); cfg.add(subnet); - + // Unparse std::string expected = "[\n" "{\n" @@ -555,4 +555,24 @@ TEST(CfgSubnets4Test, unparsePool) { runToElementTest(expected, cfg); } +// This test verifies that it is possible to retrieve a subnet using subnet-id. +TEST(CfgSubnets4Test, getSubnet) { + CfgSubnets4 cfg; + + // Create 3 subnets. + Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 100)); + Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 200)); + Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 300)); + + // Add one subnet and make sure it is returned. + cfg.add(subnet1); + cfg.add(subnet2); + cfg.add(subnet3); + + EXPECT_EQ(subnet1, cfg.getSubnet(100)); + EXPECT_EQ(subnet2, cfg.getSubnet(200)); + EXPECT_EQ(subnet3, cfg.getSubnet(300)); + EXPECT_EQ(Subnet4Ptr(), cfg.getSubnet(400)); // no such subnet +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index c655686597..a9bcb3de80 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015,2017 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 @@ -434,7 +434,7 @@ TEST(CfgSubnets6Test, unparsePool) { subnet->addPool(pool1); subnet->addPool(pool2); cfg.add(subnet); - + // Unparse std::string expected = "[\n" "{\n" @@ -480,7 +480,7 @@ TEST(CfgSubnets6Test, unparsePdPool) { subnet->addPool(pdpool1); subnet->addPool(pdpool2); cfg.add(subnet); - + // Unparse std::string expected = "[\n" "{\n" @@ -518,4 +518,22 @@ TEST(CfgSubnets6Test, unparsePdPool) { runToElementTest(expected, cfg); } +// This test verifies that it is possible to retrieve a subnet using subnet-id. +TEST(CfgSubnets6Test, getSubnet) { + CfgSubnets6 cfg; + + // Let's configure 3 subnets + Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 100)); + Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4, 200)); + Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4, 300)); + cfg.add(subnet1); + cfg.add(subnet2); + cfg.add(subnet3); + + EXPECT_EQ(subnet1, cfg.getSubnet(100)); + EXPECT_EQ(subnet2, cfg.getSubnet(200)); + EXPECT_EQ(subnet3, cfg.getSubnet(300)); + EXPECT_EQ(Subnet6Ptr(), cfg.getSubnet(400)); // no such subnet +} + } // end of anonymous namespace -- cgit v1.2.3 From 6094638e34c853d09148dc26b6747a4869bdf558 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Wed, 26 Jul 2017 13:56:49 +0200 Subject: [5315] Check for subnet prefix duplicates when subnet is added. --- src/lib/dhcpsrv/cfg_subnets4.cc | 22 ++++++++-------------- src/lib/dhcpsrv/cfg_subnets4.h | 8 -------- src/lib/dhcpsrv/cfg_subnets6.cc | 11 ++++++++--- src/lib/dhcpsrv/cfg_subnets6.h | 8 -------- 4 files changed, 16 insertions(+), 33 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index f5fcd232c0..78ca8339c3 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -23,12 +23,17 @@ namespace dhcp { void CfgSubnets4::add(const Subnet4Ptr& subnet) { - /// @todo: Check that this new subnet does not cross boundaries of any - /// other already defined subnet. - if (isDuplicate(*subnet)) { + if (getBySubnetId(subnet->getID())) { isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '" << subnet->getID() << "' is already in use"); + + } else if (getByPrefix(subnet->toText())) { + /// @todo: Check that this new subnet does not cross boundaries of any + /// other already defined subnet. + isc_throw(isc::dhcp::DuplicateSubnetID, "subnet with the prefix of '" + << subnet->toText() << "' already exists"); } + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4) .arg(subnet->toText()); subnets_.push_back(subnet); @@ -246,17 +251,6 @@ CfgSubnets4::selectSubnet(const IOAddress& address, return (Subnet4Ptr()); } -bool -CfgSubnets4::isDuplicate(const Subnet4& subnet) const { - for (Subnet4Collection::const_iterator subnet_it = subnets_.begin(); - subnet_it != subnets_.end(); ++subnet_it) { - if ((*subnet_it)->getID() == subnet.getID()) { - return (true); - } - } - return (false); -} - void CfgSubnets4::removeStatistics() { using namespace isc::stats; diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index 215060e346..ac609e3b89 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -235,14 +235,6 @@ public: private: - /// @brief Checks that the IPv4 subnet with the given id already exists. - /// - /// @param subnet Subnet for which this function will check if the other - /// subnet with equal id already exists. - /// - /// @return true if the duplicate subnet exists. - bool isDuplicate(const Subnet4& subnet) const; - /// @brief A container for IPv4 subnets. Subnet4Collection subnets_; diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index 111fed0997..468b563503 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -22,12 +22,17 @@ namespace dhcp { void CfgSubnets6::add(const Subnet6Ptr& subnet) { - /// @todo: Check that this new subnet does not cross boundaries of any - /// other already defined subnet. - if (isDuplicate(*subnet)) { + if (getBySubnetId(subnet->getID())) { isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '" << subnet->getID() << "' is already in use"); + + } else if (getByPrefix(subnet->toText())) { + /// @todo: Check that this new subnet does not cross boundaries of any + /// other already defined subnet. + isc_throw(isc::dhcp::DuplicateSubnetID, "subnet with the prefix of '" + << subnet->toText() << "' already exists"); } + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6) .arg(subnet->toText()); subnets_.push_back(subnet); diff --git a/src/lib/dhcpsrv/cfg_subnets6.h b/src/lib/dhcpsrv/cfg_subnets6.h index 0c27928926..b8e70a9610 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.h +++ b/src/lib/dhcpsrv/cfg_subnets6.h @@ -241,14 +241,6 @@ private: selectSubnet(const OptionPtr& interface_id, const ClientClasses& client_classes) const; - /// @brief Checks that the IPv6 subnet with the given id already exists. - /// - /// @param subnet Subnet for which this function will check if the other - /// subnet with equal id already exists. - /// - /// @return true if the duplicate subnet exists. - bool isDuplicate(const Subnet6& subnet) const; - /// @brief A container for IPv6 subnets. Subnet6Collection subnets_; -- cgit v1.2.3 From 03e1804d39b3221a8b1a7b580665fb45fa2c3e38 Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Thu, 27 Jul 2017 14:13:55 +0200 Subject: [5315] Added functions that remove subnets from a configuration. --- src/lib/dhcpsrv/cfg_subnets4.cc | 14 +++++++++++++ src/lib/dhcpsrv/cfg_subnets4.h | 7 +++++++ src/lib/dhcpsrv/cfg_subnets6.cc | 14 +++++++++++++ src/lib/dhcpsrv/cfg_subnets6.h | 7 +++++++ src/lib/dhcpsrv/dhcpsrv_messages.mes | 7 +++++++ src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc | 28 ++++++++++++++++++++++++++ src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc | 25 +++++++++++++++++++++++ 7 files changed, 102 insertions(+) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 78ca8339c3..af890d1100 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -39,6 +39,20 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) { subnets_.push_back(subnet); } +void +CfgSubnets4::del(const ConstSubnet4Ptr& subnet) { + auto& index = subnets_.get(); + auto subnet_it = index.find(subnet->getID()); + if (subnet_it == index.end()) { + isc_throw(BadValue, "no subnet with ID of '" << subnet->getID() + << "' found"); + } + index.erase(subnet_it); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DEL_SUBNET4) + .arg(subnet->toText()); +} + ConstSubnet4Ptr CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const { const auto& index = subnets_.get(); diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index ac609e3b89..16ecff5379 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -39,6 +39,13 @@ public: /// duplicates id of an existing subnet. void add(const Subnet4Ptr& subnet); + /// @brief Removes subnet from the configuration. + /// + /// @param subnet Pointer to the subnet to be removed. + /// + /// @throw isc::BadValue if such subnet doesn't exist. + void del(const ConstSubnet4Ptr& subnet); + /// @brief Returns pointer to the collection of all IPv4 subnets. /// /// This is used in a hook (subnet4_select), where the hook is able diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index 468b563503..f4278a1878 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -38,6 +38,20 @@ CfgSubnets6::add(const Subnet6Ptr& subnet) { subnets_.push_back(subnet); } +void +CfgSubnets6::del(const ConstSubnet6Ptr& subnet) { + auto& index = subnets_.get(); + auto subnet_it = index.find(subnet->getID()); + if (subnet_it == index.end()) { + isc_throw(BadValue, "no subnet with ID of '" << subnet->getID() + << "' found"); + } + index.erase(subnet_it); + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DEL_SUBNET6) + .arg(subnet->toText()); +} + ConstSubnet6Ptr CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const { const auto& index = subnets_.get(); diff --git a/src/lib/dhcpsrv/cfg_subnets6.h b/src/lib/dhcpsrv/cfg_subnets6.h index b8e70a9610..5e03152275 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.h +++ b/src/lib/dhcpsrv/cfg_subnets6.h @@ -40,6 +40,13 @@ public: /// duplicates id of an existing subnet. void add(const Subnet6Ptr& subnet); + /// @brief Removes subnet from the configuration. + /// + /// @param subnet Pointer to the subnet to be removed. + /// + /// @throw isc::BadValue if such subnet doesn't exist. + void del(const ConstSubnet6Ptr& subnet); + /// @brief Returns pointer to the collection of all IPv6 subnets. /// /// This is used in a hook (subnet6_select), where the hook is able diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 0b6b730c65..1f3961c23d 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -44,6 +44,13 @@ there is a good reason for it, to avoid increased number of renewals and a need for rebinding (increase of multicast traffic, which may be received by multiple servers). +% DHCPSRV_CFGMGR_DEL_SUBNET4 IPv4 subnet %1 removed +This debug message is issued when a subnet is successfully removed from the +server configuration. The argument identifies the subnet removed. + +% DHCPSRV_CFGMGR_DEL_SUBNET6 IPv6 subnet %1 removed +This debug message is issued when a subnet is successfully removed from the + % DHCPSRV_CFGMGR_NEW_SUBNET4 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. diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index 78345028a9..696c477f33 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -74,6 +74,34 @@ TEST(CfgSubnets4Test, getSpecificSubnet) { EXPECT_FALSE(cfg.getByPrefix("10.20.30.0/29")); } +// This test verifies that a single subnet can be removed from the configuration. +TEST(CfgSubnets4Test, deleteSubnet) { + CfgSubnets4 cfg; + + // Create 3 subnets. + Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), + 26, 1, 2, 3, SubnetID(5))); + Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.3.0"), + 26, 1, 2, 3, SubnetID(8))); + Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.4.0"), + 26, 1, 2, 3, SubnetID(10))); + + ASSERT_NO_THROW(cfg.add(subnet1)); + ASSERT_NO_THROW(cfg.add(subnet2)); + ASSERT_NO_THROW(cfg.add(subnet3)); + + // There should be three subnets. + ASSERT_EQ(3, cfg.getAll()->size()); + // We're going to remove the subnet #2. Let's make sure it exists before + // we remove it. + ASSERT_TRUE(cfg.getByPrefix("192.0.3.0/26")); + + // Remove the subnet and make sure it is gone. + ASSERT_NO_THROW(cfg.del(subnet2)); + ASSERT_EQ(2, cfg.getAll()->size()); + EXPECT_FALSE(cfg.getByPrefix("192.0.3.0/26")); +} + // This test verifies that it is possible to retrieve a subnet using an // IP address. TEST(CfgSubnets4Test, selectSubnetByCiaddr) { diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index 12d1547c3e..575a0b8e90 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -83,6 +83,31 @@ TEST(CfgSubnets6Test, getSpecificSubnet) { EXPECT_FALSE(cfg.getByPrefix("3000::/16")); } +// This test verifies that a single subnet can be removed from the configuration. +TEST(CfgSubnets6Test, deleteSubnet) { + CfgSubnets6 cfg; + + // Create 3 subnets. + Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4)); + Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4)); + Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4)); + + ASSERT_NO_THROW(cfg.add(subnet1)); + ASSERT_NO_THROW(cfg.add(subnet2)); + ASSERT_NO_THROW(cfg.add(subnet3)); + + // There should be three subnets. + ASSERT_EQ(3, cfg.getAll()->size()); + // We're going to remove the subnet #2. Let's make sure it exists before + // we remove it. + ASSERT_TRUE(cfg.getByPrefix("2001:db8:2::/48")); + + // Remove the subnet and make sure it is gone. + ASSERT_NO_THROW(cfg.del(subnet2)); + ASSERT_EQ(2, cfg.getAll()->size()); + EXPECT_FALSE(cfg.getByPrefix("2001:db8:2::/48")); +} + // This test checks that the subnet can be selected using a relay agent's // link address. TEST(CfgSubnets6Test, selectSubnetByRelayAddress) { -- cgit v1.2.3 From 0bf132390a294362399931be836044d7d7dc8e41 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 15 Aug 2017 18:58:04 +0200 Subject: [5315_rebase] Changes after rebase and review: - Renamed SubnetIdIndexTag to avoid collision - Moved OptionDataParser to option_data_parser.cc|h - Updated hooks.xml to reflect recent changes --- doc/guide/hooks.xml | 69 ++++-- src/lib/dhcpsrv/cfg_subnets4.cc | 2 +- src/lib/dhcpsrv/cfg_subnets6.cc | 14 +- src/lib/dhcpsrv/parsers/dhcp_parsers.cc | 303 -------------------------- src/lib/dhcpsrv/parsers/dhcp_parsers.h | 157 ------------- src/lib/dhcpsrv/parsers/option_data_parser.cc | 20 +- src/lib/dhcpsrv/parsers/option_data_parser.h | 5 + 7 files changed, 80 insertions(+), 490 deletions(-) (limited to 'src/lib/dhcpsrv/cfg_subnets4.cc') diff --git a/doc/guide/hooks.xml b/doc/guide/hooks.xml index 70adbf7f10..9a4fb35ea1 100644 --- a/doc/guide/hooks.xml +++ b/doc/guide/hooks.xml @@ -1490,7 +1490,6 @@ as follows: Note: not all backends support this command. - @@ -1558,13 +1557,13 @@ as follows: "result": 0, "text": "2 IPv4 subnets found", "arguments": { - "subnet-ids": [ + "subnets": [ { - "subnet-id": 10, + "id": 10, "subnet": "10.0.0.0/8" }, { - "subnet-id": 100, + "id": 100, "subnet": "192.0.2.0/24" } ] @@ -1602,13 +1601,13 @@ as follows: "result": 0, "text": "2 IPv6 subnets found", "arguments": { - "subnet-ids": [ + "subnets": [ { - "subnet-id": 11, + "id": 11, "subnet": "2001:db8:1::/64" }, { - "subnet-id": 233, + "id": 233, "subnet": "3000::/16" } ] @@ -1656,7 +1655,7 @@ or "result": 0, "text": "Info about IPv4 subnet 10.0.0.0/8 (subnet-id 10) returned", "arguments": { - "subnet4": [ + "subnets": [ { "subnet": "10.0.0.0/8", "id": 1, @@ -1706,7 +1705,7 @@ If the subnet exists the response will be similar to this: "result": 0, "text": "Info about IPv6 subnet 2001:db8:1::/64 (subnet-id 11) returned", "arguments": { - "subnet6": [ + "subnets": [ { "subnet": "2001:db8:1::/64", "id": 1, @@ -1742,7 +1741,7 @@ If the subnet exists the response will be similar to this: { "command": "subnet4-add", "arguments": { - "subnet4": [ { + "subnets": [ { "id": 123, "subnet": "10.20.30.0/24", ... @@ -1759,7 +1758,7 @@ If the subnet exists the response will be similar to this: "result": 0, "text": "IPv4 subnet added", "arguments": { - "subnet4": [ + "subnets": [ { "id": 123, "subnet": "10.20.30.0/24" @@ -1818,6 +1817,33 @@ If the subnet exists the response will be similar to this: } + + + It is recommended, but not mandatory to specify subnet + id. If not specified, Kea will try to assign the next + subnet-id value. This automatic ID value generator is + simple. It returns a previously automatically assigned value + increased by 1. This works well, unless you manually create + a subnet with a value bigger than previously used. For + example, if you call subnet4-add five times, each without + id, Kea will assign IDs: 1,2,3,4 and 5 and it will work just + fine. However, if you try to call subnet4-add five times, + with the first subnet having subnet-id of value 3 and + remaining ones having no subnet-id, it will fail. The first + command (with explicit value) will use subnet-id 3, the + second command will create a subnet with id of 1, the third + will use value of 2 and finally the fourth will have the + subnet-id value auto-generated as 3. However, since there is + already a subnet with that id, it will fail. + + + The general recommendation is to either: never use explicit + values (so the auto-generated values will always work) or + always use explicit values (so the auto-generation is never + used). You can mix those two approaches only if you + understand how the internal automatic subnet-id generation works. + +
@@ -1860,7 +1886,15 @@ If the subnet exists the response will be similar to this: { "result": 0, - "text": "IPv4 subnet 192.0.2.0/24 (subnet-id 123) deleted" + "text": "IPv4 subnet 192.0.2.0/24 (subnet-id 123) deleted", + "arguments": { + "subnets": [ + { + "id": 123, + "subnet": "192.0.2.0/24" + } + ] + } } @@ -1906,7 +1940,13 @@ If the subnet exists the response will be similar to this: { "result": 0, - "text": "IPv6 subnet 2001:db8:1::/64 (subnet-id 234) deleted" + "text": "IPv6 subnet 2001:db8:1::/64 (subnet-id 234) deleted", + "subnets": [ + { + "id": 234, + "subnet": "2001:db8:1::/64" + } + ] } @@ -1934,4 +1974,7 @@ If the subnet exists the response will be similar to this: user context capability.
+ + + diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index af890d1100..6c4b4f9afb 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -41,7 +41,7 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) { void CfgSubnets4::del(const ConstSubnet4Ptr& subnet) { - auto& index = subnets_.get(); + auto& index = subnets_.get(); auto subnet_it = index.find(subnet->getID()); if (subnet_it == index.end()) { isc_throw(BadValue, "no subnet with ID of '" << subnet->getID() diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index f4278a1878..e35b3773d4 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -40,7 +40,7 @@ CfgSubnets6::add(const Subnet6Ptr& subnet) { void CfgSubnets6::del(const ConstSubnet6Ptr& subnet) { - auto& index = subnets_.get(); + auto& index = subnets_.get(); auto subnet_it = index.find(subnet->getID()); if (subnet_it == index.end()) { isc_throw(BadValue, "no subnet with ID of '" << subnet->getID() @@ -212,18 +212,6 @@ CfgSubnets6::getSubnet(const SubnetID id) const { return (Subnet6Ptr()); } - -bool -CfgSubnets6::isDuplicate(const Subnet6& subnet) const { - for (Subnet6Collection::const_iterator subnet_it = subnets_.begin(); - subnet_it != subnets_.end(); ++subnet_it) { - if ((*subnet_it)->getID() == subnet.getID()) { - return (true); - } - } - return (false); -} - void CfgSubnets6::removeStatistics() { using namespace isc::stats; diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index 58bddc0847..30995de0d0 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -168,157 +168,11 @@ void ControlSocketParser::parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr v srv_cfg.setControlSocketInfo(value); } -// **************************** OptionDataParser ************************* -OptionDataParser::OptionDataParser(const uint16_t address_family) - : address_family_(address_family) { -} - -std::pair -OptionDataParser::parse(isc::data::ConstElementPtr single_option) { - - // Try to create the option instance. - std::pair opt = createOption(single_option); - - if (!opt.first.option_) { - isc_throw(isc::InvalidOperation, - "parser logic error: no option has been configured and" - " thus there is nothing to commit. Has build() been called?"); - } - - return (opt); -} - -OptionalValue -OptionDataParser::extractCode(ConstElementPtr parent) const { - uint32_t code; - try { - code = getInteger(parent, "code"); - - } catch (const exception&) { - // The code parameter was not found. Return an unspecified - // value. - return (OptionalValue()); - } - - if (code == 0) { - isc_throw(DhcpConfigError, "option code must not be zero " - "(" << getPosition("code", parent) << ")"); - - } else if (address_family_ == AF_INET && - code > std::numeric_limits::max()) { - isc_throw(DhcpConfigError, "invalid option code '" << code - << "', it must not be greater than '" - << static_cast(std::numeric_limits::max()) - << "' (" << getPosition("code", parent) - << ")"); - - } else if (address_family_ == AF_INET6 && - code > std::numeric_limits::max()) { - isc_throw(DhcpConfigError, "invalid option code '" << code - << "', it must not exceed '" - << std::numeric_limits::max() - << "' (" << getPosition("code", parent) - << ")"); - - } - - return (OptionalValue(code, OptionalValueState(true))); -} - -OptionalValue -OptionDataParser::extractName(ConstElementPtr parent) const { - std::string name; - try { - name = getString(parent, "name"); - - } catch (...) { - return (OptionalValue()); - } - if (name.find(" ") != std::string::npos) { - isc_throw(DhcpConfigError, "invalid option name '" << name - << "', space character is not allowed (" - << getPosition("name", parent) << ")"); - } - - return (OptionalValue(name, OptionalValueState(true))); -} -std::string -OptionDataParser::extractData(ConstElementPtr parent) const { - std::string data; - try { - data = getString(parent, "data"); - - } catch (...) { - // The "data" parameter was not found. Return an empty value. - return (data); - } - return (data); -} -OptionalValue -OptionDataParser::extractCSVFormat(ConstElementPtr parent) const { - bool csv_format = true; - try { - csv_format = getBoolean(parent, "csv-format"); - } catch (...) { - return (OptionalValue(csv_format)); - } - - return (OptionalValue(csv_format, OptionalValueState(true))); -} - -std::string -OptionDataParser::extractSpace(ConstElementPtr parent) const { - std::string space = address_family_ == AF_INET ? - DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE; - try { - space = getString(parent, "space"); - - } catch (...) { - return (space); - } - - try { - if (!OptionSpace::validateName(space)) { - isc_throw(DhcpConfigError, "invalid option space name '" - << space << "'"); - } - - if ((space == DHCP4_OPTION_SPACE) && (address_family_ == AF_INET6)) { - isc_throw(DhcpConfigError, "'" << DHCP4_OPTION_SPACE - << "' option space name is reserved for DHCPv4 server"); - - } else if ((space == DHCP6_OPTION_SPACE) && - (address_family_ == AF_INET)) { - isc_throw(DhcpConfigError, "'" << DHCP6_OPTION_SPACE - << "' option space name is reserved for DHCPv6 server"); - } - - } catch (std::exception& ex) { - // Append position of the option space parameter. - isc_throw(DhcpConfigError, ex.what() << " (" - << getPosition("space", parent) << ")"); - } - - return (space); -} - -OptionalValue -OptionDataParser::extractPersistent(ConstElementPtr parent) const { - bool persist = false; - try { - persist = getBoolean(parent, "always-send"); - - } catch (...) { - return (OptionalValue(persist)); - } - - return (OptionalValue(persist, OptionalValueState(true))); -} template OptionDefinitionPtr @@ -346,163 +200,6 @@ OptionDataParser::findOptionDefinition(const std::string& option_space, return (def); } -std::pair -OptionDataParser::createOption(ConstElementPtr option_data) { - const Option::Universe universe = address_family_ == AF_INET ? - Option::V4 : Option::V6; - - OptionalValue code_param = extractCode(option_data); - OptionalValue name_param = extractName(option_data); - OptionalValue csv_format_param = extractCSVFormat(option_data); - OptionalValue persist_param = extractPersistent(option_data); - std::string data_param = extractData(option_data); - std::string space_param = extractSpace(option_data); - - // Require that option code or option name is specified. - if (!code_param.isSpecified() && !name_param.isSpecified()) { - isc_throw(DhcpConfigError, "option data configuration requires one of" - " 'code' or 'name' parameters to be specified" - << " (" << option_data->getPosition() << ")"); - } - - // Try to find a corresponding option definition using option code or - // option name. - OptionDefinitionPtr def = code_param.isSpecified() ? - findOptionDefinition(space_param, code_param) : - findOptionDefinition(space_param, name_param); - - // If there is no definition, the user must not explicitly enable the - // use of csv-format. - if (!def) { - // If explicitly requested that the CSV format is to be used, - // the option definition is a must. - if (csv_format_param.isSpecified() && csv_format_param) { - isc_throw(DhcpConfigError, "definition for the option '" - << space_param << "." << name_param - << "' having code '" << code_param - << "' does not exist (" - << getPosition("name", option_data) - << ")"); - - // If there is no option definition and the option code is not specified - // we have no means to find the option code. - } else if (name_param.isSpecified() && !code_param.isSpecified()) { - isc_throw(DhcpConfigError, "definition for the option '" - << space_param << "." << name_param - << "' does not exist (" - << getPosition("name", option_data) - << ")"); - } - } - - // Transform string of hexadecimal digits into binary format. - std::vector binary; - std::vector data_tokens; - - // If the definition is available and csv-format hasn't been explicitly - // disabled, we will parse the data as comma separated values. - if (def && (!csv_format_param.isSpecified() || csv_format_param)) { - // If the option data is specified as a string of comma - // separated values then we need to split this string into - // individual values - each value will be used to initialize - // one data field of an option. - // It is the only usage of the escape option: this allows - // to embed commas in individual values and to return - // for instance a string value with embedded commas. - data_tokens = isc::util::str::tokens(data_param, ",", true); - - } else { - // Otherwise, the option data is specified as a string of - // hexadecimal digits that we have to turn into binary format. - try { - // The decodeHex function expects that the string contains an - // even number of digits. If we don't meet this requirement, - // we have to insert a leading 0. - if (!data_param.empty() && ((data_param.length() % 2) != 0)) { - data_param = data_param.insert(0, "0"); - } - util::encode::decodeHex(data_param, binary); - } catch (...) { - isc_throw(DhcpConfigError, "option data is not a valid" - << " string of hexadecimal digits: " << data_param - << " (" - << getPosition("data", option_data) - << ")"); - } - } - - OptionPtr option; - OptionDescriptor desc(false); - - if (!def) { - // @todo We have a limited set of option definitions initialized at - // the moment. In the future we want to initialize option definitions - // for all options. Consequently an error will be issued if an option - // definition does not exist for a particular option code. For now it is - // ok to create generic option if definition does not exist. - OptionPtr option(new Option(universe, static_cast(code_param), - binary)); - - desc.option_ = option; - desc.persistent_ = persist_param.isSpecified() && persist_param; - } else { - - // Option name is specified it should match the name in the definition. - if (name_param.isSpecified() && (def->getName() != name_param.get())) { - isc_throw(DhcpConfigError, "specified option name '" - << name_param << "' does not match the " - << "option definition: '" << space_param - << "." << def->getName() << "' (" - << getPosition("name", option_data) - << ")"); - } - - // Option definition has been found so let's use it to create - // an instance of our option. - try { - bool use_csv = !csv_format_param.isSpecified() || csv_format_param; - OptionPtr option = use_csv ? - def->optionFactory(universe, def->getCode(), data_tokens) : - def->optionFactory(universe, def->getCode(), binary); - desc.option_ = option; - desc.persistent_ = persist_param.isSpecified() && persist_param; - if (use_csv) { - desc.formatted_value_ = data_param; - } - } catch (const isc::Exception& ex) { - isc_throw(DhcpConfigError, "option data does not match" - << " option definition (space: " << space_param - << ", code: " << def->getCode() << "): " - << ex.what() << " (" - << getPosition("data", option_data) - << ")"); - } - } - - // All went good, so we can set the option space name. - return make_pair(desc, space_param); -} - -// **************************** OptionDataListParser ************************* -OptionDataListParser::OptionDataListParser(//const std::string&, - //const CfgOptionPtr& cfg, - const uint16_t address_family) - : address_family_(address_family) { -} - - -void OptionDataListParser::parse(const CfgOptionPtr& cfg, - isc::data::ConstElementPtr option_data_list) { - OptionDataParser option_parser(address_family_); - BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) { - std::pair option = - option_parser.parse(data); - // Use the option description to keep the formatted value - cfg->add(option.first, option.second); - cfg->encapsulate(); - } -} - // ******************************** OptionDefParser **************************** std::pair diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index 5c1b5291ae..5e03b00d51 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h @@ -341,163 +341,6 @@ public: void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value); }; -/// @brief Parser for option data value. -/// -/// This parser parses configuration entries that specify value of -/// a single option. These entries include option name, option code -/// and data carried by the option. The option data can be specified -/// in one of the two available formats: binary value represented as -/// a string of hexadecimal digits or a list of comma separated values. -/// The format being used is controlled by csv-format configuration -/// parameter. When setting this value to True, the latter format is -/// used. The subsequent values in the CSV format apply to relevant -/// option data fields in the configured option. For example the -/// configuration: "data" : "192.168.2.0, 56, hello world" can be -/// used to set values for the option comprising IPv4 address, -/// integer and string data field. Note that order matters. If the -/// order of values does not match the order of data fields within -/// an option the configuration will not be accepted. If parsing -/// is successful then an instance of an option is created and -/// added to the storage provided by the calling class. -class OptionDataParser : public isc::data::SimpleParser { -public: - /// @brief Constructor. - /// - /// @param address_family Address family: @c AF_INET or @c AF_INET6. - explicit OptionDataParser(const uint16_t address_family); - - /// @brief Parses ElementPtr containing option definition - /// - /// This method parses ElementPtr containing the option definition, - /// instantiates the option for it and then returns a pair - /// of option descriptor (that holds that new option) and - /// a string that specifies the option space. - /// - /// Note: ElementPtr is expected to contain all fields. If your - /// ElementPtr does not have them, please use - /// @ref isc::data::SimpleParser::setDefaults to fill the missing fields - /// with default values. - /// - /// @param single_option ElementPtr containing option definition - /// @return Option object wrapped in option description and an option - /// space - std::pair - parse(isc::data::ConstElementPtr single_option); -private: - - /// @brief Finds an option definition within an option space - /// - /// Given an option space and an option code, find the corresponding - /// option definition within the option definition storage. - /// - /// @param option_space name of the parameter option space - /// @param search_key an option code or name to be used to lookup the - /// option definition. - /// @tparam A numeric type for searching using an option code or the - /// string for searching using the option name. - /// - /// @return OptionDefinitionPtr of the option definition or an - /// empty OptionDefinitionPtr if not found. - /// @throw DhcpConfigError if the option space requested is not valid - /// for this server. - template - OptionDefinitionPtr findOptionDefinition(const std::string& option_space, - const SearchKey& search_key) const; - - /// @brief Create option instance. - /// - /// Creates an instance of an option and adds it to the provided - /// options storage. If the option data parsed by \ref build function - /// are invalid or insufficient this function emits an exception. - /// - /// @param option_data An element holding data for a single option being - /// created. - /// - /// @return created option descriptor - /// - /// @throw DhcpConfigError if parameters provided in the configuration - /// are invalid. - std::pair - createOption(isc::data::ConstElementPtr option_data); - - /// @brief Retrieves parsed option code as an optional value. - /// - /// @param parent A data element holding full option data configuration. - /// - /// @return Option code, possibly unspecified. - /// @throw DhcpConfigError if option code is invalid. - util::OptionalValue - extractCode(data::ConstElementPtr parent) const; - - /// @brief Retrieves parsed option name as an optional value. - /// - /// @param parent A data element holding full option data configuration. - /// - /// @return Option name, possibly unspecified. - /// @throw DhcpConfigError if option name is invalid. - util::OptionalValue - extractName(data::ConstElementPtr parent) const; - - /// @brief Retrieves csv-format parameter as an optional value. - /// - /// @return Value of the csv-format parameter, possibly unspecified. - util::OptionalValue extractCSVFormat(data::ConstElementPtr parent) const; - - /// @brief Retrieves option data as a string. - /// - /// @param parent A data element holding full option data configuration. - /// @return Option data as a string. It will return empty string if - /// option data is unspecified. - std::string extractData(data::ConstElementPtr parent) const; - - /// @brief Retrieves option space name. - /// - /// If option space name is not specified in the configuration the - /// 'dhcp4' or 'dhcp6' option space name is returned, depending on - /// the universe specified in the parser context. - /// - /// @param parent A data element holding full option data configuration. - /// - /// @return Option space name. - std::string extractSpace(data::ConstElementPtr parent) const; - - /// @brief Retrieves persistent/always-send parameter as an optional value. - /// - /// @return Value of the persistent parameter, possibly unspecified. - util::OptionalValue extractPersistent(data::ConstElementPtr parent) const; - - /// @brief Address family: @c AF_INET or @c AF_INET6. - uint16_t address_family_; -}; - -/// @brief Parser for option data values within a subnet. -/// -/// This parser iterates over all entries that define options -/// data for a particular subnet and creates a collection of options. -/// If parsing is successful, all these options are added to the Subnet -/// object. -class OptionDataListParser : public isc::data::SimpleParser { -public: - /// @brief Constructor. - /// - /// @param address_family Address family: @c AF_INET or AF_INET6 - explicit OptionDataListParser(const uint16_t address_family); - - /// @brief Parses a list of options, instantiates them and stores in cfg - /// - /// This method expects to get a list of options in option_data_list, - /// iterates over them, creates option objects, wraps them with - /// option descriptor and stores in specified cfg. - /// - /// @param cfg created options will be stored here - /// @param option_data_list configuration that describes the options - void parse(const CfgOptionPtr& cfg, - isc::data::ConstElementPtr option_data_list); -private: - /// @brief Address family: @c AF_INET or @c AF_INET6 - uint16_t address_family_; -}; - typedef std::pair OptionDefinitionTuple; /// @brief Parser for a single option definition. diff --git a/src/lib/dhcpsrv/parsers/option_data_parser.cc b/src/lib/dhcpsrv/parsers/option_data_parser.cc index bce9da0094..ec989e0bfe 100644 --- a/src/lib/dhcpsrv/parsers/option_data_parser.cc +++ b/src/lib/dhcpsrv/parsers/option_data_parser.cc @@ -162,6 +162,19 @@ OptionDataParser::extractSpace(ConstElementPtr parent) const { return (space); } +OptionalValue +OptionDataParser::extractPersistent(ConstElementPtr parent) const { + bool persist = false; + try { + persist = getBoolean(parent, "always-send"); + + } catch (...) { + return (OptionalValue(persist)); + } + + return (OptionalValue(persist, OptionalValueState(true))); +} + template OptionDefinitionPtr OptionDataParser::findOptionDefinition(const std::string& option_space, @@ -203,6 +216,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) { OptionalValue code_param = extractCode(option_data); OptionalValue name_param = extractName(option_data); OptionalValue csv_format_param = extractCSVFormat(option_data); + OptionalValue persist_param = extractPersistent(option_data); std::string data_param = extractData(option_data); std::string space_param = extractSpace(option_data); @@ -283,7 +297,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) { OptionDescriptor desc(false); if (!def) { - // @todo We have a limited set of option definitions initalized at + // @todo We have a limited set of option definitions initialized at // the moment. In the future we want to initialize option definitions // for all options. Consequently an error will be issued if an option // definition does not exist for a particular option code. For now it is @@ -292,7 +306,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) { binary)); desc.option_ = option; - desc.persistent_ = false; + desc.persistent_ = persist_param.isSpecified() && persist_param; } else { // Option name is specified it should match the name in the definition. @@ -313,7 +327,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) { def->optionFactory(universe, def->getCode(), data_tokens) : def->optionFactory(universe, def->getCode(), binary); desc.option_ = option; - desc.persistent_ = false; + desc.persistent_ = persist_param.isSpecified() && persist_param; if (use_csv) { desc.formatted_value_ = data_param; } diff --git a/src/lib/dhcpsrv/parsers/option_data_parser.h b/src/lib/dhcpsrv/parsers/option_data_parser.h index 471f7a8d49..b4fefeb65e 100644 --- a/src/lib/dhcpsrv/parsers/option_data_parser.h +++ b/src/lib/dhcpsrv/parsers/option_data_parser.h @@ -138,6 +138,11 @@ private: /// @return Option space name. std::string extractSpace(data::ConstElementPtr parent) const; + /// @brief Retrieves persistent/always-send parameter as an optional value. + /// + /// @return Value of the persistent parameter, possibly unspecified. + util::OptionalValue extractPersistent(data::ConstElementPtr parent) const; + /// @brief Address family: @c AF_INET or @c AF_INET6. uint16_t address_family_; }; -- cgit v1.2.3