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_expiration.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/lib/dhcpsrv/cfg_expiration.cc') diff --git a/src/lib/dhcpsrv/cfg_expiration.cc b/src/lib/dhcpsrv/cfg_expiration.cc index 6c5bce2a37..b421f90e3d 100644 --- a/src/lib/dhcpsrv/cfg_expiration.cc +++ b/src/lib/dhcpsrv/cfg_expiration.cc @@ -94,7 +94,7 @@ CfgExpiration::rangeCheck(const int64_t value, const uint64_t max_value, const std::string& config_parameter_name) const { if (value < 0) { isc_throw(OutOfRange, "value for configuration parameter '" - << config_parameter_name << "' must not be negtive"); + << config_parameter_name << "' must not be negative"); } else if (value > max_value) { isc_throw(OutOfRange, "out range value '" << value << "' for configuration" -- 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_expiration.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