Browse Source

[master] Merge branch 'trac5114' (toElement aka unparse)

# Conflicts:
#	configure.ac
#	src/bin/agent/tests/Makefile.am
Tomek Mrugalski 8 years ago
parent
commit
df38c26f95
100 changed files with 16455 additions and 701 deletions
  1. 88 25
      configure.ac
  2. 32 0
      src/bin/agent/ca_cfg_mgr.cc
  3. 11 0
      src/bin/agent/ca_cfg_mgr.h
  4. 1 0
      src/bin/agent/tests/.gitignore
  5. 6 3
      src/bin/agent/tests/Makefile.am
  6. 282 0
      src/bin/agent/tests/get_config_unittest.cc
  7. 9 0
      src/bin/agent/tests/test_data_files_config.h.in
  8. 28 0
      src/bin/agent/tests/testdata/get_config.json
  9. 46 0
      src/bin/d2/d2_cfg_mgr.cc
  10. 5 0
      src/bin/d2/d2_cfg_mgr.h
  11. 67 0
      src/bin/d2/d2_config.cc
  12. 32 4
      src/bin/d2/d2_config.h
  13. 2 1
      src/bin/d2/tests/Makefile.am
  14. 23 1
      src/bin/d2/tests/d2_simple_parser_unittest.cc
  15. 260 0
      src/bin/d2/tests/get_config_unittest.cc
  16. 74 0
      src/bin/d2/tests/testdata/get_config.json
  17. 3 4
      src/bin/dhcp4/dhcp4to6_ipc.cc
  18. 1 16
      src/bin/dhcp4/json_config_parser.cc
  19. 3 0
      src/bin/dhcp4/main.cc
  20. 1 2
      src/bin/dhcp4/simple_parser4.cc
  21. 1 0
      src/bin/dhcp4/tests/Makefile.am
  22. 63 13
      src/bin/dhcp4/tests/config_parser_unittest.cc
  23. 1 0
      src/bin/dhcp4/tests/dhcp4_test_utils.cc
  24. 5878 0
      src/bin/dhcp4/tests/get_config_unittest.cc
  25. 345 0
      src/bin/dhcp4/tests/get_config_unittest.cc.skel
  26. 27 0
      src/bin/dhcp4/tests/get_config_unittest.h
  27. 1 2
      src/bin/dhcp4/tests/simple_parser4_unittest.cc
  28. 3 4
      src/bin/dhcp6/dhcp6to4_ipc.cc
  29. 1 28
      src/bin/dhcp6/json_config_parser.cc
  30. 3 0
      src/bin/dhcp6/main.cc
  31. 1 2
      src/bin/dhcp6/simple_parser6.cc
  32. 1 0
      src/bin/dhcp6/tests/Makefile.am
  33. 105 50
      src/bin/dhcp6/tests/config_parser_unittest.cc
  34. 1 0
      src/bin/dhcp6/tests/dhcp6_test_utils.cc
  35. 6105 0
      src/bin/dhcp6/tests/get_config_unittest.cc
  36. 348 0
      src/bin/dhcp6/tests/get_config_unittest.cc.skel
  37. 27 0
      src/bin/dhcp6/tests/get_config_unittest.h
  38. 2 3
      src/bin/dhcp6/tests/simple_parser6_unittest.cc
  39. 2 2
      src/lib/cc/cfg_to_element.h
  40. 236 0
      src/lib/cc/data.cc
  41. 160 116
      src/lib/cc/data.h
  42. 1 1
      src/lib/cc/simple_parser.cc
  43. 39 3
      src/lib/cc/simple_parser.h
  44. 192 4
      src/lib/cc/tests/data_unittests.cc
  45. 3 8
      src/lib/cc/tests/simple_parser_unittest.cc
  46. 2 1
      src/lib/dhcpsrv/Makefile.am
  47. 76 1
      src/lib/dhcpsrv/addr_utilities.cc
  48. 13 1
      src/lib/dhcpsrv/addr_utilities.h
  49. 49 0
      src/lib/dhcpsrv/cfg_4o6.cc
  50. 9 3
      src/lib/dhcpsrv/cfg_4o6.h
  51. 64 2
      src/lib/dhcpsrv/cfg_db_access.cc
  52. 39 2
      src/lib/dhcpsrv/cfg_db_access.h
  53. 37 1
      src/lib/dhcpsrv/cfg_duid.cc
  54. 8 2
      src/lib/dhcpsrv/cfg_duid.h
  55. 33 1
      src/lib/dhcpsrv/cfg_expiration.cc
  56. 8 2
      src/lib/dhcpsrv/cfg_expiration.h
  57. 15 1
      src/lib/dhcpsrv/cfg_host_operations.cc
  58. 8 2
      src/lib/dhcpsrv/cfg_host_operations.h
  59. 146 1
      src/lib/dhcpsrv/cfg_hosts.cc
  60. 32 2
      src/lib/dhcpsrv/cfg_hosts.h
  61. 94 0
      src/lib/dhcpsrv/cfg_hosts_util.cc
  62. 53 0
      src/lib/dhcpsrv/cfg_hosts_util.h
  63. 31 1
      src/lib/dhcpsrv/cfg_iface.cc
  64. 8 2
      src/lib/dhcpsrv/cfg_iface.h
  65. 48 21
      src/lib/dhcpsrv/cfg_mac_source.cc
  66. 7 1
      src/lib/dhcpsrv/cfg_mac_source.h
  67. 90 1
      src/lib/dhcpsrv/cfg_option.cc
  68. 8 2
      src/lib/dhcpsrv/cfg_option.h
  69. 58 1
      src/lib/dhcpsrv/cfg_option_def.cc
  70. 8 2
      src/lib/dhcpsrv/cfg_option_def.h
  71. 15 1
      src/lib/dhcpsrv/cfg_rsoo.cc
  72. 8 2
      src/lib/dhcpsrv/cfg_rsoo.h
  73. 111 1
      src/lib/dhcpsrv/cfg_subnets4.cc
  74. 8 2
      src/lib/dhcpsrv/cfg_subnets4.h
  75. 180 1
      src/lib/dhcpsrv/cfg_subnets6.cc
  76. 8 2
      src/lib/dhcpsrv/cfg_subnets6.h
  77. 2 1
      src/lib/dhcpsrv/cfgmgr.cc
  78. 13 0
      src/lib/dhcpsrv/cfgmgr.h
  79. 52 2
      src/lib/dhcpsrv/client_class_def.cc
  80. 28 4
      src/lib/dhcpsrv/client_class_def.h
  81. 38 1
      src/lib/dhcpsrv/d2_client_cfg.cc
  82. 9 2
      src/lib/dhcpsrv/d2_client_cfg.h
  83. 62 1
      src/lib/dhcpsrv/logging_info.cc
  84. 14 3
      src/lib/dhcpsrv/logging_info.h
  85. 59 56
      src/lib/dhcpsrv/parsers/client_class_def_parser.cc
  86. 1 1
      src/lib/dhcpsrv/parsers/dbaccess_parser.h
  87. 33 76
      src/lib/dhcpsrv/parsers/dhcp_parsers.cc
  88. 16 15
      src/lib/dhcpsrv/parsers/dhcp_parsers.h
  89. 47 92
      src/lib/dhcpsrv/parsers/duid_config_parser.cc
  90. 0 51
      src/lib/dhcpsrv/parsers/duid_config_parser.h
  91. 33 30
      src/lib/dhcpsrv/parsers/expiration_config_parser.cc
  92. 2 2
      src/lib/dhcpsrv/parsers/ifaces_config_parser.h
  93. 2 2
      src/lib/dhcpsrv/pool.h
  94. 116 0
      src/lib/dhcpsrv/srv_config.cc
  95. 10 5
      src/lib/dhcpsrv/srv_config.h
  96. 11 1
      src/lib/dhcpsrv/subnet.h
  97. 1 0
      src/lib/dhcpsrv/tests/Makefile.am
  98. 90 1
      src/lib/dhcpsrv/tests/addr_utilities_unittest.cc
  99. 22 1
      src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc
  100. 0 0
      src/lib/dhcpsrv/tests/cfg_duid_unittest.cc

+ 88 - 25
configure.ac

@@ -167,12 +167,11 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
 	feature="final method"
 	feature="final method"
 	AC_COMPILE_IFELSE(
 	AC_COMPILE_IFELSE(
 		[AC_LANG_PROGRAM(
 		[AC_LANG_PROGRAM(
-			[],
 			[class Foo {
 			[class Foo {
 			 public:
 			 public:
 			 	virtual ~Foo() {};
 			 	virtual ~Foo() {};
 				virtual void bar() final;
 				virtual void bar() final;
-			 };])],
+			 };],[])],
 		 [AC_MSG_RESULT([yes])],
 		 [AC_MSG_RESULT([yes])],
 		 [AC_MSG_RESULT([no])
 		 [AC_MSG_RESULT([no])
 		  continue])
 		  continue])
@@ -201,6 +200,31 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
 		[AC_MSG_RESULT([no])
 		[AC_MSG_RESULT([no])
 		 continue])
 		 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<int i>
+			 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)
 	AC_MSG_CHECKING(lambda support)
 	feature="lambda"
 	feature="lambda"
 	AC_COMPILE_IFELSE(
 	AC_COMPILE_IFELSE(
@@ -213,6 +237,19 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
 		 continue])
 		 continue])
 done
 done
 
 
+# Check for std::is_base_of support
+AC_MSG_CHECKING([for std::is_base_of])
+AC_COMPILE_IFELSE(
+	[AC_LANG_PROGRAM(
+		[#include <type_traits>
+		 class A {};
+		 class B : A {};]
+		[static_assert(std::is_base_of<A, B>::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
 dnl Determine if we are using GNU sed
 GNU_SED=no
 GNU_SED=no
 $SED --version 2> /dev/null | grep GNU > /dev/null 2>&1
 $SED --version 2> /dev/null | grep GNU > /dev/null 2>&1
@@ -1407,6 +1444,31 @@ if test $enable_gtest != "no"; then
     CPPFLAGS=$CPPFLAGS_SAVED
     CPPFLAGS=$CPPFLAGS_SAVED
 fi
 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 <boost/algorithm/string.hpp>
+	    #include <gtest/gtest.h>
+	    #include <string>
+	    #include <vector>
+	    std::string nodiff(std::string text) {
+	        std::vector<std::string> 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.
 # ASIO: we extensively use it as the C++ event management module.
 #
 #
@@ -1459,11 +1521,11 @@ AC_ARG_ENABLE(generate_parser, [AC_HELP_STRING([--enable-generate-parser],
    enable_generate_parser=$enableval, enable_generate_parser=no)
    enable_generate_parser=$enableval, enable_generate_parser=no)
 
 
 # Check if flex is avaible. Flex is not needed for building Kea sources,
 # 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
 AC_PROG_LEX
 
 
 # Check if bison is available. Bison is not needed for building Kea sources,
 # 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
 AC_PROG_YACC
 
 
 if test "x$enable_generate_parser" != "xno"; then
 if test "x$enable_generate_parser" != "xno"; then
@@ -1477,7 +1539,7 @@ if test "x$enable_generate_parser" != "xno"; then
     fi
     fi
 
 
 # Ok, let's check if we have at least 3.0.0 version of the bison. The code used
 # 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
    cat > bisontest.y << EOF
 %require "3.0.0"
 %require "3.0.0"
 %token X
 %token X
@@ -1591,23 +1653,33 @@ 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_DEFINE([CONFIG_H_WAS_INCLUDED], [1], [config.h inclusion marker])
 
 
-AC_CONFIG_FILES([compatcheck/Makefile
+AC_CONFIG_FILES([Makefile
+                 compatcheck/Makefile
                  dns++.pc
                  dns++.pc
-                 doc/design/datasrc/Makefile
+                 doc/Makefile
                  doc/design/Makefile
                  doc/design/Makefile
+                 doc/design/datasrc/Makefile
                  doc/guide/Makefile
                  doc/guide/Makefile
-                 doc/Makefile
                  doc/version.ent
                  doc/version.ent
+                 ext/Makefile
                  ext/coroutine/Makefile
                  ext/coroutine/Makefile
                  ext/gtest/Makefile
                  ext/gtest/Makefile
-                 ext/Makefile
                  m4macros/Makefile
                  m4macros/Makefile
-                 Makefile
                  src/Makefile
                  src/Makefile
                  src/bin/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/Makefile
                  src/bin/agent/tests/Makefile
                  src/bin/agent/tests/Makefile
                  src/bin/agent/tests/ca_process_tests.sh
                  src/bin/agent/tests/ca_process_tests.sh
+                 src/bin/agent/tests/test_data_files_config.h
+                 src/bin/agent/tests/test_libraries.h
                  src/bin/d2/Makefile
                  src/bin/d2/Makefile
                  src/bin/d2/tests/Makefile
                  src/bin/d2/tests/Makefile
                  src/bin/d2/tests/d2_process_tests.sh
                  src/bin/d2/tests/d2_process_tests.sh
@@ -1636,15 +1708,6 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/bin/perfdhcp/Makefile
                  src/bin/perfdhcp/Makefile
                  src/bin/perfdhcp/tests/Makefile
                  src/bin/perfdhcp/tests/Makefile
                  src/bin/perfdhcp/tests/testdata/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/bin/shell/Makefile
                  src/bin/shell/Makefile
                  src/bin/shell/kea-shell
                  src/bin/shell/kea-shell
                  src/bin/shell/tests/Makefile
                  src/bin/shell/tests/Makefile
@@ -1682,6 +1745,8 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/lib/dns/gen-rdatacode.py
                  src/lib/dns/gen-rdatacode.py
                  src/lib/dns/tests/Makefile
                  src/lib/dns/tests/Makefile
                  src/lib/dns/tests/testdata/Makefile
                  src/lib/dns/tests/testdata/Makefile
+                 src/lib/eval/Makefile
+                 src/lib/eval/tests/Makefile
                  src/lib/exceptions/Makefile
                  src/lib/exceptions/Makefile
                  src/lib/exceptions/tests/Makefile
                  src/lib/exceptions/tests/Makefile
                  src/lib/hooks/Makefile
                  src/lib/hooks/Makefile
@@ -1707,10 +1772,10 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/lib/process/spec_config.h.pre
                  src/lib/process/spec_config.h.pre
                  src/lib/process/tests/Makefile
                  src/lib/process/tests/Makefile
                  src/lib/process/testutils/Makefile
                  src/lib/process/testutils/Makefile
-                 src/lib/testutils/Makefile
-                 src/lib/testutils/dhcp_test_lib.sh
                  src/lib/stats/Makefile
                  src/lib/stats/Makefile
                  src/lib/stats/tests/Makefile
                  src/lib/stats/tests/Makefile
+                 src/lib/testutils/Makefile
+                 src/lib/testutils/dhcp_test_lib.sh
                  src/lib/util/Makefile
                  src/lib/util/Makefile
                  src/lib/util/io/Makefile
                  src/lib/util/io/Makefile
                  src/lib/util/python/Makefile
                  src/lib/util/python/Makefile
@@ -1720,11 +1785,10 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/lib/util/threads/Makefile
                  src/lib/util/threads/Makefile
                  src/lib/util/threads/tests/Makefile
                  src/lib/util/threads/tests/Makefile
                  src/lib/util/unittests/Makefile
                  src/lib/util/unittests/Makefile
-                 src/lib/eval/Makefile
-                 src/lib/eval/tests/Makefile
                  src/share/Makefile
                  src/share/Makefile
                  src/share/database/Makefile
                  src/share/database/Makefile
                  src/share/database/scripts/Makefile
                  src/share/database/scripts/Makefile
+                 src/share/database/scripts/cql/Makefile
                  src/share/database/scripts/mysql/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_1.0_to_2.0.sh
                  src/share/database/scripts/mysql/upgrade_2.0_to_3.0.sh
                  src/share/database/scripts/mysql/upgrade_2.0_to_3.0.sh
@@ -1734,12 +1798,11 @@ AC_CONFIG_FILES([compatcheck/Makefile
                  src/share/database/scripts/pgsql/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_1.0_to_2.0.sh
                  src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh
                  src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh
-                 src/share/database/scripts/cql/Makefile
                  tools/Makefile
                  tools/Makefile
                  tools/path_replacer.sh
                  tools/path_replacer.sh
 ])
 ])
 
 
- AC_CONFIG_COMMANDS([permissions], [
+AC_CONFIG_COMMANDS([permissions], [
            chmod +x src/bin/admin/kea-admin
            chmod +x src/bin/admin/kea-admin
            chmod +x src/bin/dhcp4/tests/dhcp4_process_tests.sh
            chmod +x src/bin/dhcp4/tests/dhcp4_process_tests.sh
            chmod +x src/bin/dhcp6/tests/dhcp6_process_tests.sh
            chmod +x src/bin/dhcp6/tests/dhcp6_process_tests.sh

+ 32 - 0
src/bin/agent/ca_cfg_mgr.cc

@@ -158,6 +158,38 @@ CtrlAgentCfgContext::setControlSocketInfo(const isc::data::ConstElementPtr& cont
     ctrl_sockets_[static_cast<uint8_t>(type)] = control_socket;
     ctrl_sockets_[static_cast<uint8_t>(type)] = control_socket;
 }
 }
 
 
+ElementPtr
+CtrlAgentCfgContext::toElement() const {
+    ElementPtr ca = Element::createMap();
+    // Set http-host
+    ca->set("http-host", Element::create(http_host_));
+    // Set http-port
+    ca->set("http-port", Element::create(static_cast<int64_t>(http_port_)));
+    // Set hooks-libraries
+    ca->set("hooks-libraries", hooks_config_.toElement());
+    // Set control-sockets
+    ElementPtr control_sockets = Element::createMap();
+    // Set dhcp4-server
+    if (ctrl_sockets_[TYPE_DHCP4]) {
+        control_sockets->set("dhcp4-server", ctrl_sockets_[TYPE_DHCP4]);
+    }
+    // Set dhcp6-server
+    if (ctrl_sockets_[TYPE_DHCP6]) {
+        control_sockets->set("dhcp6-server", ctrl_sockets_[TYPE_DHCP6]);
+    }
+    // Set d2-server
+    if (ctrl_sockets_[TYPE_D2]) {
+        control_sockets->set("d2-server", ctrl_sockets_[TYPE_D2]);
+    }
+    ca->set("control-sockets", control_sockets);
+    // Set Control-agent
+    ElementPtr result = Element::createMap();
+    result->set("Control-agent", ca);
+
+    // Set Logging (not yet)
+
+    return (result);
+}
 
 
 } // namespace isc::agent
 } // namespace isc::agent
 } // namespace isc
 } // namespace isc

+ 11 - 0
src/bin/agent/ca_cfg_mgr.h

@@ -116,6 +116,17 @@ public:
         return (hooks_config_);
         return (hooks_config_);
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// Returns an element which must parse into the same object, i.e.
+    /// @code
+    /// for all valid config C parse(parse(C)->toElement()) == parse(C)
+    /// @endcode
+    ///
+    /// @return a pointer to a configuration which can be parsed into
+    /// the initial configuration object
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Private copy constructor
     /// @brief Private copy constructor

+ 1 - 0
src/bin/agent/tests/.gitignore

@@ -1,3 +1,4 @@
 /ca_unittests
 /ca_unittests
 /ca_process_tests.sh
 /ca_process_tests.sh
+/test_data_files_config.h
 /test_libraries.h
 /test_libraries.h

+ 6 - 3
src/bin/agent/tests/Makefile.am

@@ -4,7 +4,11 @@ SHTESTS =
 SHTESTS += ca_process_tests.sh
 SHTESTS += ca_process_tests.sh
 
 
 noinst_SCRIPTS = ca_process_tests.sh
 noinst_SCRIPTS = ca_process_tests.sh
+
 EXTRA_DIST  = ca_process_tests.sh.in
 EXTRA_DIST  = ca_process_tests.sh.in
+EXTRA_DIST += testdata/get_config.json
+
+noinst_LTLIBRARIES = libbasic.la
 
 
 # test using command-line arguments, so use check-local target instead of TESTS
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
 check-local:
@@ -39,8 +43,6 @@ TESTS_ENVIRONMENT = \
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST
 
 
-noinst_LTLIBRARIES = libbasic.la
-
 TESTS += ca_unittests
 TESTS += ca_unittests
 
 
 ca_unittests_SOURCES  = ca_cfg_mgr_unittests.cc
 ca_unittests_SOURCES  = ca_cfg_mgr_unittests.cc
@@ -51,6 +53,7 @@ ca_unittests_SOURCES += ca_response_creator_unittests.cc
 ca_unittests_SOURCES += ca_response_creator_factory_unittests.cc
 ca_unittests_SOURCES += ca_response_creator_factory_unittests.cc
 ca_unittests_SOURCES += ca_unittests.cc
 ca_unittests_SOURCES += ca_unittests.cc
 ca_unittests_SOURCES += parser_unittests.cc
 ca_unittests_SOURCES += parser_unittests.cc
+ca_unittests_SOURCES += get_config_unittest.cc
 
 
 ca_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 ca_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 ca_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
 ca_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
@@ -88,7 +91,7 @@ libbasic_la_LIBADD  += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 libbasic_la_LIBADD  += $(top_builddir)/src/lib/log/libkea-log.la
 libbasic_la_LIBADD  += $(top_builddir)/src/lib/log/libkea-log.la
 libbasic_la_LDFLAGS  = -avoid-version -export-dynamic -module -rpath /nowhere
 libbasic_la_LDFLAGS  = -avoid-version -export-dynamic -module -rpath /nowhere
 
 
-nodist_ca_unittests_SOURCES = test_libraries.h
+nodist_ca_unittests_SOURCES = test_data_files_config.h test_libraries.h
 
 
 endif
 endif
 
 

+ 282 - 0
src/bin/agent/tests/get_config_unittest.cc

@@ -0,0 +1,282 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/data.h>
+#include <cc/command_interpreter.h>
+#include <process/testutils/d_test_stubs.h>
+#include <agent/ca_cfg_mgr.h>
+#include <agent/parser_context.h>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+
+#include "test_data_files_config.h"
+#include "test_libraries.h"
+
+using namespace isc::agent;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::process;
+
+namespace {
+
+/// @name How to generate the testdata/get_config.json file
+///
+/// Define GENERATE_ACTION and recompile. Run ca_unittests on
+/// CtrlAgentGetCfgTest redirecting the standard error to a temporary
+/// file, e.g. by
+/// @code
+///    ./ca_unittests --gtest_filter="CtrlAgentGetCfg*" > /dev/null 2> u
+/// @endcode
+///
+/// Update testdata/get_config.json using the temporary file content,
+/// (removing head comment and restoring hook library path),
+/// recompile without GENERATE_ACTION.
+
+/// @brief the generate action
+/// false means do nothing, true means unparse extracted configurations
+#ifdef GENERATE_ACTION
+const bool generate_action = true;
+#else
+const bool generate_action = false;
+#endif
+
+/// @brief Read a file into a string
+std::string
+readFile(const std::string& file_path) {
+    std::ifstream ifs(file_path);
+    if (!ifs.is_open()) {
+        ADD_FAILURE() << "readFile cannot open " << file_path;
+        isc_throw(isc::Unexpected, "readFile cannot open " << file_path);
+    }
+    std::string lines;
+    std::string line;
+    while (std::getline(ifs, line)) {
+        lines += line + "\n";
+    }
+    ifs.close();
+    return (lines);
+}
+
+/// @brief Runs parser in JSON mode
+ElementPtr
+parseJSON(const std::string& in,  bool verbose = false) {
+    try {
+        ParserContext ctx;
+        return (ctx.parseString(in, ParserContext::PARSER_JSON));
+    } catch (const std::exception& ex) {
+        if (verbose) {
+            std::cout << "EXCEPTION: " << ex.what() << std::endl;
+        }
+        throw;
+    }
+}
+
+/// @brief Runs parser in AGENT mode
+ElementPtr
+parseAGENT(const std::string& in,  bool verbose = false) {
+    try {
+        ParserContext ctx;
+        return (ctx.parseString(in, ParserContext::PARSER_AGENT));
+    } catch (const std::exception& ex) {
+        if (verbose) {
+            std::cout << "EXCEPTION: " << ex.what() << std::endl;
+        }
+        throw;
+    }
+}
+
+/// @brief Replace the library path
+void
+pathReplacer(ConstElementPtr ca_cfg) {
+    ConstElementPtr hooks_libs = ca_cfg->get("hooks-libraries");
+    if (!hooks_libs || hooks_libs->empty()) {
+        return;
+    }
+    ElementPtr first_lib = hooks_libs->getNonConst(0);
+    std::string lib_path(BASIC_CALLOUT_LIBRARY);
+    first_lib->set("library", Element::create(lib_path));
+}
+
+/// @brief Almost regular agent CfgMgr with internal parse method exposed.
+class NakedAgentCfgMgr : public CtrlAgentCfgMgr {
+public:
+    using CtrlAgentCfgMgr::parse;
+};
+
+}
+
+/// Test fixture class
+class CtrlAgentGetCfgTest : public ConfigParseTest {
+public:
+    CtrlAgentGetCfgTest()
+    : rcode_(-1) {
+        srv_.reset(new NakedAgentCfgMgr());
+        // Create fresh context.
+        resetConfiguration();
+    }
+
+    ~CtrlAgentGetCfgTest() {
+        resetConfiguration();
+    }
+
+    /// @brief Parse and Execute configuration
+    ///
+    /// Parses a configuration and executes a configuration of the server.
+    /// If the operation fails, the current test will register a failure.
+    ///
+    /// @param config Configuration to parse
+    /// @param operation Operation being performed.  In the case of an error,
+    ///        the error text will include the string "unable to <operation>.".
+    ///
+    /// @return true if the configuration succeeded, false if not.
+    bool
+    executeConfiguration(const std::string& config, const char* operation) {
+        // try JSON parser
+        ConstElementPtr json;
+        try {
+            json = parseJSON(config, true);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "invalid JSON for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << config << "\n";
+            return (false);
+        }
+
+        // try AGENT parser
+        try {
+            json = parseAGENT(config, true);
+        } catch (...) {
+            ADD_FAILURE() << "parsing failed for " << operation
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // get Control-agent element
+        ConstElementPtr ca = json->get("Control-agent");
+        if (!ca) {
+            ADD_FAILURE() << "cannot get Control-agent for " << operation
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // update hooks-libraries
+        pathReplacer(ca);
+
+        // try AGENT configure
+        ConstElementPtr status;
+        try {
+            status = srv_->parse(ca, true);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // The status object must not be NULL
+        if (!status) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned null on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // Returned value should be 0 (configuration success)
+        comment_ = parseAnswer(rcode_, status);
+        if (rcode_ != 0) {
+            string reason = "";
+            if (comment_) {
+                reason = string(" (") + comment_->stringValue() + string(")");
+            }
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned error code "
+                          << rcode_ << reason << " on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+        return (true);
+    }
+
+    /// @brief Reset configuration database.
+    ///
+    /// This function resets configuration data base by
+    /// removing control sockets and hooks. Reset must
+    /// be performed after each test to make sure that
+    /// contents of the database do not affect result of
+    /// subsequent tests.
+    void resetConfiguration() {
+        string config = "{ \"Control-agent\": {"
+            " \"http-host\": \"\","
+            " \"http-port\": 0 } }";
+        EXPECT_TRUE(executeConfiguration(config, "reset config"));
+    }
+
+    boost::scoped_ptr<NakedAgentCfgMgr> srv_; ///< CA server under test
+    int rcode_;                       ///< Return code from element parsing
+    ConstElementPtr comment_;         ///< Reason for parse fail
+};
+
+/// Test a configuration
+TEST_F(CtrlAgentGetCfgTest, simple) {
+
+    // get the simple configuration
+    std::string simple_file = string(CFG_EXAMPLES) + "/" + "simple.json";
+    std::string config;
+    ASSERT_NO_THROW(config = readFile(simple_file));
+
+    // get the expected configuration
+    std::string expected_file =
+        std::string(CA_TEST_DATA_DIR) + "/" + "get_config.json";
+    std::string expected;
+    ASSERT_NO_THROW(expected = readFile(expected_file));
+
+    // execute the sample configuration
+    ASSERT_TRUE(executeConfiguration(config, "simple config"));
+
+    // unparse it
+    CtrlAgentCfgContextPtr context = srv_->getCtrlAgentCfgContext();
+    ConstElementPtr unparsed;
+    ASSERT_NO_THROW(unparsed = context->toElement());
+
+    // dump if wanted else check
+    if (generate_action) {
+        std::cerr << "// Generated Configuration (remove this line)\n";
+        ASSERT_NO_THROW(expected = prettyPrint(unparsed));
+        prettyPrint(unparsed, std::cerr, 0, 4);
+        std::cerr << "\n";
+    } else {
+        ConstElementPtr json;
+        ASSERT_NO_THROW(json = parseAGENT(expected, true));
+        ConstElementPtr ca;
+        ASSERT_NO_THROW(ca = json->get("Control-agent"));
+        ASSERT_TRUE(ca);
+        pathReplacer(ca);
+        EXPECT_TRUE(isEquivalent(unparsed, json));
+        std::string current = prettyPrint(unparsed, 0, 4);
+        std::string expected2 = prettyPrint(json, 0, 4);
+        EXPECT_EQ(expected2, current);
+        if (expected2 != current) {
+            expected = current + "\n";
+        }
+    }
+
+    // execute the control agent configuration
+    EXPECT_TRUE(executeConfiguration(expected, "unparsed config"));
+
+    // is it a fixed point?
+    CtrlAgentCfgContextPtr context2 = srv_->getCtrlAgentCfgContext();
+    ConstElementPtr unparsed2;
+    ASSERT_NO_THROW(unparsed2 = context2->toElement());
+    ASSERT_TRUE(unparsed2);
+    EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
+}

+ 9 - 0
src/bin/agent/tests/test_data_files_config.h.in

@@ -0,0 +1,9 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @brief Path to agent source dir
+#define CA_SRC_DIR "@abs_top_srcdir@/src/bin/agent"
+#define CA_TEST_DATA_DIR "@abs_top_srcdir@/src/bin/agent/tests/testdata"

+ 28 - 0
src/bin/agent/tests/testdata/get_config.json

@@ -0,0 +1,28 @@
+{
+    "Control-agent": {
+        "control-sockets": {
+            "d2-server": {
+                "socket-name": "/path/to/the/unix/socket-d2",
+                "socket-type": "unix"
+            },
+            "dhcp4-server": {
+                "socket-name": "/path/to/the/unix/socket-v4",
+                "socket-type": "unix"
+            },
+            "dhcp6-server": {
+                "socket-name": "/path/to/the/unix/socket-v6",
+                "socket-type": "unix"
+            }
+        },
+        "hooks-libraries": [
+            {
+                "library": "/opt/local/control-agent-commands.so",
+                "parameters": {
+                    "param1": "foo"
+                }
+            }
+        ],
+        "http-host": "localhost",
+        "http-port": 8000
+    }
+}

+ 46 - 0
src/bin/d2/d2_cfg_mgr.cc

@@ -13,6 +13,8 @@
 
 
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 
 
+using namespace isc::asiolink;
+using namespace isc::data;
 using namespace isc::process;
 using namespace isc::process;
 
 
 namespace isc {
 namespace isc {
@@ -51,6 +53,50 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
 D2CfgContext::~D2CfgContext() {
 D2CfgContext::~D2CfgContext() {
 }
 }
 
 
+ElementPtr
+D2CfgContext::toElement() const {
+    ElementPtr d2 = Element::createMap();
+    // Set ip-address
+    const IOAddress& ip_address = d2_params_->getIpAddress();
+    d2->set("ip-address", Element::create(ip_address.toText()));
+    // Set port
+    size_t port = d2_params_->getPort();
+    d2->set("port", Element::create(static_cast<int64_t>(port)));
+    // Set dns-server-timeout
+    size_t dns_server_timeout = d2_params_->getDnsServerTimeout();
+    d2->set("dns-server-timeout",
+            Element::create(static_cast<int64_t>(dns_server_timeout)));
+    // Set ncr-protocol
+    const dhcp_ddns::NameChangeProtocol& ncr_protocol =
+        d2_params_->getNcrProtocol();
+    d2->set("ncr-protocol",
+            Element::create(dhcp_ddns::ncrProtocolToString(ncr_protocol)));
+    // Set ncr-format
+    const dhcp_ddns::NameChangeFormat& ncr_format = d2_params_->getNcrFormat();
+    d2->set("ncr-format",
+            Element::create(dhcp_ddns::ncrFormatToString(ncr_format)));
+    // Set forward-ddns
+    ElementPtr forward_ddns = Element::createMap();
+    forward_ddns->set("ddns-domains", forward_mgr_->toElement());
+    d2->set("forward-ddns", forward_ddns);
+    // Set reverse-ddns
+    ElementPtr reverse_ddns = Element::createMap();
+    reverse_ddns->set("ddns-domains", reverse_mgr_->toElement());
+    d2->set("reverse-ddns", reverse_ddns);
+    // Set tsig-keys
+    ElementPtr tsig_keys = Element::createList();
+    for (TSIGKeyInfoMap::const_iterator key = keys_->begin();
+         key != keys_->end(); ++key) {
+        tsig_keys->add(key->second->toElement());
+    }
+    d2->set("tsig-keys", tsig_keys);
+    // Set DhcpDdns
+    ElementPtr result = Element::createMap();
+    result->set("DhcpDdns", d2);
+
+    return (result);
+}
+
 // *********************** D2CfgMgr  *************************
 // *********************** D2CfgMgr  *************************
 
 
 const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";
 const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";

+ 5 - 0
src/bin/d2/d2_cfg_mgr.h

@@ -90,6 +90,11 @@ public:
         keys_ = keys;
         keys_ = keys;
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to a configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 protected:
 protected:
     /// @brief Copy constructor for use by derivations in clone().
     /// @brief Copy constructor for use by derivations in clone().
     D2CfgContext(const D2CfgContext& rhs);
     D2CfgContext(const D2CfgContext& rhs);

+ 67 - 0
src/bin/d2/d2_config.cc

@@ -20,6 +20,7 @@
 #include <string>
 #include <string>
 
 
 using namespace isc::process;
 using namespace isc::process;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace d2 {
 namespace d2 {
@@ -180,6 +181,22 @@ TSIGKeyInfo::remakeKey() {
     }
     }
 }
 }
 
 
+ElementPtr
+TSIGKeyInfo::toElement() const {
+    ElementPtr result = Element::createMap();
+    // Set name
+    result->set("name", Element::create(name_));
+    // Set algorithm
+    result->set("algorithm", Element::create(algorithm_));
+    // Set secret
+    result->set("secret", Element::create(secret_));
+    // Set digest-bits
+    result->set("digest-bits",
+                Element::create(static_cast<int64_t>(digestbits_)));
+
+    return (result);
+}
+
 // *********************** DnsServerInfo  *************************
 // *********************** DnsServerInfo  *************************
 DnsServerInfo::DnsServerInfo(const std::string& hostname,
 DnsServerInfo::DnsServerInfo(const std::string& hostname,
                              isc::asiolink::IOAddress ip_address, uint32_t port,
                              isc::asiolink::IOAddress ip_address, uint32_t port,
@@ -198,6 +215,19 @@ DnsServerInfo::toText() const {
     return (stream.str());
     return (stream.str());
 }
 }
 
 
+ElementPtr
+DnsServerInfo::toElement() const {
+    ElementPtr result = Element::createMap();
+    // Set hostname
+    result->set("hostname", Element::create(hostname_));
+    // Set ip-address
+    result->set("ip-address", Element::create(ip_address_.toText()));
+    // Set port
+    result->set("port", Element::create(static_cast<int64_t>(port_)));
+
+    return (result);
+}
+
 
 
 std::ostream&
 std::ostream&
 operator<<(std::ostream& os, const DnsServerInfo& server) {
 operator<<(std::ostream& os, const DnsServerInfo& server) {
@@ -226,6 +256,30 @@ DdnsDomain::getKeyName() const {
     return ("");
     return ("");
 }
 }
 
 
+ElementPtr
+DdnsDomain::toElement() const {
+    ElementPtr result = Element::createMap();
+    // Set name
+    result->set("name", Element::create(name_));
+    // Set servers
+    ElementPtr servers = Element::createList();
+    for (DnsServerInfoStorage::const_iterator server = servers_->begin();
+         server != servers_->end(); ++server) {
+        ElementPtr dns_server = (*server)->toElement();
+        servers->add(dns_server);
+    }
+    // the dns server list may not be empty
+    if (!servers->empty()) {
+        result->set("dns-servers", servers);
+    }
+    // Set key-name
+    if (tsig_key_info_) {
+        result->set("key-name", Element::create(tsig_key_info_->getName()));
+    }
+
+    return (result);
+}
+
 // *********************** DdnsDomainLstMgr  *************************
 // *********************** DdnsDomainLstMgr  *************************
 
 
 const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";
 const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";
@@ -321,6 +375,19 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
     return (true);
     return (true);
 }
 }
 
 
+ElementPtr
+DdnsDomainListMgr::toElement() const {
+    ElementPtr result = Element::createList();
+    // Iterate on ddns domains
+    for (DdnsDomainMap::const_iterator domain = domains_->begin();
+         domain != domains_->end(); ++domain) {
+        ElementPtr ddns_domain = domain->second->toElement();
+        result->add(ddns_domain);
+    }
+
+    return (result);
+}
+
 // *************************** PARSERS ***********************************
 // *************************** PARSERS ***********************************
 
 
 // *********************** TSIGKeyInfoParser  *************************
 // *********************** TSIGKeyInfoParser  *************************

+ 32 - 4
src/bin/d2/d2_config.h

@@ -10,6 +10,7 @@
 #include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 #include <cc/data.h>
 #include <cc/data.h>
 #include <cc/simple_parser.h>
 #include <cc/simple_parser.h>
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dns/tsig.h>
 #include <dns/tsig.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
@@ -255,7 +256,7 @@ typedef boost::shared_ptr<D2Params> D2ParamsPtr;
 /// instance of the actual key (@ref isc::dns::TSIGKey) that can be used
 /// instance of the actual key (@ref isc::dns::TSIGKey) that can be used
 /// by the IO layer for signing and verifying messages.
 /// by the IO layer for signing and verifying messages.
 ///
 ///
-class TSIGKeyInfo {
+class TSIGKeyInfo : public isc::data::CfgToElement {
 public:
 public:
     /// @brief Defines string values for the supported TSIG algorithms
     /// @brief Defines string values for the supported TSIG algorithms
     //@{
     //@{
@@ -356,6 +357,11 @@ public:
     static const dns::Name& stringToAlgorithmName(const std::string&
     static const dns::Name& stringToAlgorithmName(const std::string&
                                                   algorithm_id);
                                                   algorithm_id);
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to a configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
     /// @brief Creates the actual TSIG key instance member
     /// @brief Creates the actual TSIG key instance member
     ///
     ///
@@ -405,7 +411,7 @@ typedef boost::shared_ptr<TSIGKeyInfoMap> TSIGKeyInfoMapPtr;
 /// belongs to a list of servers supporting DNS for a given domain. It will
 /// belongs to a list of servers supporting DNS for a given domain. It will
 /// be used to establish communications with the server to carry out DNS
 /// be used to establish communications with the server to carry out DNS
 /// updates.
 /// updates.
-class DnsServerInfo {
+class DnsServerInfo : public isc::data::CfgToElement {
 public:
 public:
     /// @brief defines DNS standard port value
     /// @brief defines DNS standard port value
     static const uint32_t STANDARD_DNS_PORT = 53;
     static const uint32_t STANDARD_DNS_PORT = 53;
@@ -472,6 +478,11 @@ public:
     /// @brief Returns a text representation for the server.
     /// @brief Returns a text representation for the server.
     std::string toText() const;
     std::string toText() const;
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to a configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 
 
 private:
 private:
     /// @brief The resolvable name of the server. If not blank, then the
     /// @brief The resolvable name of the server. If not blank, then the
@@ -510,7 +521,7 @@ typedef boost::shared_ptr<DnsServerInfoStorage> DnsServerInfoStoragePtr;
 /// @todo Currently the name entry for a domain is just an std::string. It
 /// @todo Currently the name entry for a domain is just an std::string. It
 /// may be worthwhile to change this to a dns::Name for purposes of better
 /// may be worthwhile to change this to a dns::Name for purposes of better
 /// validation and matching capabilities.
 /// validation and matching capabilities.
-class DdnsDomain {
+class DdnsDomain : public isc::data::CfgToElement {
 public:
 public:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
@@ -553,6 +564,11 @@ public:
         return (tsig_key_info_);
         return (tsig_key_info_);
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to a configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
     /// @brief The domain name of the domain.
     /// @brief The domain name of the domain.
     std::string name_;
     std::string name_;
@@ -588,7 +604,7 @@ typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr;
 /// specify the wild card domain as the only forward domain. All forward DNS
 /// specify the wild card domain as the only forward domain. All forward DNS
 /// updates would be sent to that one list of servers, regardless of the FQDN.
 /// updates would be sent to that one list of servers, regardless of the FQDN.
 /// As matching capabilities evolve this class is expected to expand.
 /// As matching capabilities evolve this class is expected to expand.
-class DdnsDomainListMgr {
+class DdnsDomainListMgr : public isc::data::CfgToElement {
 public:
 public:
     /// @brief defines the domain name for denoting the wildcard domain.
     /// @brief defines the domain name for denoting the wildcard domain.
     static const char* wildcard_domain_name_;
     static const char* wildcard_domain_name_;
@@ -655,6 +671,11 @@ public:
     /// set the internal wild card domain pointer accordingly.
     /// set the internal wild card domain pointer accordingly.
     void setDomains(DdnsDomainMapPtr domains);
     void setDomains(DdnsDomainMapPtr domains);
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to a configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
     /// @brief An arbitrary label assigned to this manager.
     /// @brief An arbitrary label assigned to this manager.
     std::string name_;
     std::string name_;
@@ -697,6 +718,13 @@ public:
         return (process::DCfgContextBasePtr(new DScalarContext(*this)));
         return (process::DCfgContextBasePtr(new DScalarContext(*this)));
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to a configuration
+    virtual isc::data::ElementPtr toElement() const {
+        isc_throw(isc::NotImplemented, "DScalarContext::ElementPtr");
+    }
+
 protected:
 protected:
     /// @brief Copy constructor
     /// @brief Copy constructor
     DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs) {
     DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs) {

+ 2 - 1
src/bin/d2/tests/Makefile.am

@@ -6,7 +6,7 @@ SHTESTS += d2_process_tests.sh
 noinst_SCRIPTS = d2_process_tests.sh
 noinst_SCRIPTS = d2_process_tests.sh
 
 
 EXTRA_DIST  = d2_process_tests.sh.in
 EXTRA_DIST  = d2_process_tests.sh.in
-EXTRA_DIST += testdata/d2_cfg_tests.json
+EXTRA_DIST += testdata/d2_cfg_tests.json testdata/get_config.json
 
 
 # test using command-line arguments, so use check-local target instead of TESTS
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
 check-local:
@@ -58,6 +58,7 @@ d2_unittests_SOURCES += nc_trans_unittests.cc
 d2_unittests_SOURCES += d2_controller_unittests.cc
 d2_unittests_SOURCES += d2_controller_unittests.cc
 d2_unittests_SOURCES += d2_simple_parser_unittest.cc
 d2_unittests_SOURCES += d2_simple_parser_unittest.cc
 d2_unittests_SOURCES += parser_unittest.cc parser_unittest.h
 d2_unittests_SOURCES += parser_unittest.cc parser_unittest.h
+d2_unittests_SOURCES += get_config_unittest.cc
 
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS)
 d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS)

+ 23 - 1
src/bin/d2/tests/d2_simple_parser_unittest.cc

@@ -9,12 +9,14 @@
 #include <d2/d2_simple_parser.h>
 #include <d2/d2_simple_parser.h>
 #include <d2/tests/parser_unittest.h>
 #include <d2/tests/parser_unittest.h>
 #include <cc/data.h>
 #include <cc/data.h>
+#include <testutils/test_to_element.h>
 
 
 #include <boost/lexical_cast.hpp>
 #include <boost/lexical_cast.hpp>
 
 
 using namespace isc;
 using namespace isc;
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::d2;
 using namespace isc::d2;
+using namespace isc::test;
 
 
 namespace {
 namespace {
 
 
@@ -659,6 +661,9 @@ TEST_F(TSIGKeyInfoParserTest, validEntry) {
     // Verify the key contents.
     // Verify the key contents.
     EXPECT_TRUE(checkKey(key_, "d2_key_one", "HMAC-MD5",
     EXPECT_TRUE(checkKey(key_, "d2_key_one", "HMAC-MD5",
                          "dGhpcyBrZXkgd2lsbCBtYXRjaA==", 120));
                          "dGhpcyBrZXkgd2lsbCBtYXRjaA==", 120));
+
+    // Verify unparsing.
+    runToElementTest<TSIGKeyInfo>(config, *key_);
 }
 }
 
 
 /// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
 /// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
@@ -880,6 +885,9 @@ TEST_F(DnsServerInfoParserTest, validEntry) {
     ASSERT_TRUE(server_);
     ASSERT_TRUE(server_);
     EXPECT_TRUE(checkServer(server_, "", "127.0.0.1", 100));
     EXPECT_TRUE(checkServer(server_, "", "127.0.0.1", 100));
 
 
+    // Verify unparsing.
+    runToElementTest<DnsServerInfo>(config, *server_);
+
     // Valid entries for static ip, no port
     // Valid entries for static ip, no port
     // This will fail without invoking set defaults
     // This will fail without invoking set defaults
     config = " { \"ip-address\": \"192.168.2.5\" }";
     config = " { \"ip-address\": \"192.168.2.5\" }";
@@ -1022,6 +1030,21 @@ TEST_F(DdnsDomainParserTest, validDomain) {
     EXPECT_TRUE(server);
     EXPECT_TRUE(server);
 
 
     EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
     EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
+
+    // Verify unparsing.
+    ElementPtr json;
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+    ConstElementPtr servers_json;
+    ASSERT_NO_THROW(servers_json = json->get("dns-servers"));
+    ASSERT_TRUE(servers_json);
+    ASSERT_EQ(Element::list, servers_json->getType());
+    for (size_t i = 0; i < servers_json->size(); ++i) {
+        ElementPtr server_json;
+        ASSERT_NO_THROW(server_json = servers_json->getNonConst(i));
+        ASSERT_NO_THROW(server_json->set("hostname",
+                                         Element::create(std::string())));
+    }
+    runToElementTest<DdnsDomain>(json, *domain_);
 }
 }
 
 
 /// @brief Tests the fundamentals of parsing DdnsDomain lists.
 /// @brief Tests the fundamentals of parsing DdnsDomain lists.
@@ -1146,4 +1169,3 @@ TEST_F(DdnsDomainListParserTest, duplicateDomain) {
 }
 }
 
 
 };
 };
-

+ 260 - 0
src/bin/d2/tests/get_config_unittest.cc

@@ -0,0 +1,260 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/data.h>
+#include <cc/command_interpreter.h>
+#include <process/testutils/d_test_stubs.h>
+#include <d2/d2_config.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d2/parser_context.h>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+
+#include "test_data_files_config.h"
+
+using namespace isc::config;
+using namespace isc::d2;
+using namespace isc::data;
+using namespace isc::process;
+
+namespace {
+
+/// @name How to generate the testdata/get_config.json file
+///
+/// Define GENERATE_ACTION and recompile. Run d2_unittests on
+/// D2GetConfigTest redirecting the standard error to a temporary
+/// file, e.g. by
+/// @code
+///    ./d2_unittests --gtest_filter="D2GetConfig*" > /dev/null 2> u
+/// @endcode
+///
+/// Update testdata/get_config.json using the temporary file content,
+/// recompile without GENERATE_ACTION.
+
+/// @brief the generate action
+/// false means do nothing, true means unparse extracted configurations
+#ifdef GENERATE_ACTION
+const bool generate_action = true;
+#else
+const bool generate_action = false;
+#endif
+
+/// @brief Read a file into a string
+std::string
+readFile(const std::string& file_path) {
+    std::ifstream ifs(file_path);
+    if (!ifs.is_open()) {
+        ADD_FAILURE() << "readFile cannot open " << file_path;
+        isc_throw(isc::Unexpected, "readFile cannot open " << file_path);
+    }
+    std::string lines;
+    std::string line;
+    while (std::getline(ifs, line)) {
+        lines += line + "\n";
+    }
+    ifs.close();
+    return (lines);
+}
+
+/// @brief Runs parser in JSON mode
+ElementPtr
+parseJSON(const std::string& in,  bool verbose = false) {
+    try {
+        D2ParserContext ctx;
+        return (ctx.parseString(in, D2ParserContext::PARSER_JSON));
+    } catch (const std::exception& ex) {
+        if (verbose) {
+            std::cout << "EXCEPTION: " << ex.what() << std::endl;
+        }
+        throw;
+    }
+}
+
+/// @brief Runs parser in DHCPDDNS mode
+ElementPtr
+parseDHCPDDNS(const std::string& in,  bool verbose = false) {
+    try {
+        D2ParserContext ctx;
+        return (ctx.parseString(in, D2ParserContext::PARSER_DHCPDDNS));
+    } catch (const std::exception& ex) {
+        if (verbose) {
+            std::cout << "EXCEPTION: " << ex.what() << std::endl;
+        }
+        throw;
+    }
+}
+
+}
+
+/// Test fixture class
+class D2GetConfigTest : public ConfigParseTest {
+public:
+    D2GetConfigTest()
+    : rcode_(-1) {
+        srv_.reset(new D2CfgMgr());
+        // Create fresh context.
+        resetConfiguration();
+    }
+
+    ~D2GetConfigTest() {
+        resetConfiguration();
+    }
+
+    /// @brief Parse and Execute configuration
+    ///
+    /// Parses a configuration and executes a configuration of the server.
+    /// If the operation fails, the current test will register a failure.
+    ///
+    /// @param config Configuration to parse
+    /// @param operation Operation being performed.  In the case of an error,
+    ///        the error text will include the string "unable to <operation>.".
+    ///
+    /// @return true if the configuration succeeded, false if not.
+    bool
+    executeConfiguration(const std::string& config, const char* operation) {
+        // try JSON parser
+        ConstElementPtr json;
+        try {
+            json = parseJSON(config, true);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "invalid JSON for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << config << "\n";
+            return (false);
+        }
+
+        // try DHCPDDNS parser
+        try {
+            json = parseDHCPDDNS(config, true);
+        } catch (...) {
+            ADD_FAILURE() << "parsing failed for " << operation
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // get DhcpDdns element
+        ConstElementPtr d2 = json->get("DhcpDdns");
+        if (!d2) {
+            ADD_FAILURE() << "cannot get DhcpDdns for " << operation
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // try DHCPDDNS configure
+        ConstElementPtr status;
+        try {
+            status = srv_->parseConfig(d2);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // The status object must not be NULL
+        if (!status) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned null on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // Returned value should be 0 (configuration success)
+        comment_ = parseAnswer(rcode_, status);
+        if (rcode_ != 0) {
+            string reason = "";
+            if (comment_) {
+                reason = string(" (") + comment_->stringValue() + string(")");
+            }
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned error code "
+                          << rcode_ << reason << " on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+        return (true);
+    }
+
+    /// @brief Reset configuration database.
+    ///
+    /// This function resets configuration data base by
+    /// removing control sockets and domain lists. Reset must
+    /// be performed after each test to make sure that
+    /// contents of the database do not affect result of
+    /// subsequent tests.
+    void resetConfiguration() {
+        string config = "{ \"DhcpDdns\": {"
+            " \"ip-address\": \"127.0.0.1\","
+            " \"port\": 53001,"
+            " \"dns-server-timeout\": 100,"
+            " \"ncr-protocol\": \"UDP\","
+            " \"ncr-format\": \"JSON\","
+            " \"tsig-keys\": [ ],"
+            " \"forward-ddns\": { },"
+            " \"reverse-ddns\": { } } }";
+        EXPECT_TRUE(executeConfiguration(config, "reset config"));
+    }
+
+    boost::scoped_ptr<D2CfgMgr> srv_;  ///< D2 server under test
+    int rcode_;                        ///< Return code from element parsing
+    ConstElementPtr comment_;          ///< Reason for parse fail
+};
+
+/// Test a configuration
+TEST_F(D2GetConfigTest, sample1) {
+
+    // get the sample1 configuration
+    std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json";
+    std::string config;
+    ASSERT_NO_THROW(config = readFile(sample1_file));
+
+    // get the expected configuration
+    std::string expected_file =
+        std::string(D2_TEST_DATA_DIR) + "/" + "get_config.json";
+    std::string expected;
+    ASSERT_NO_THROW(expected = readFile(expected_file));
+
+    // execute the sample configuration
+    ASSERT_TRUE(executeConfiguration(config, "sample1 config"));
+
+    // unparse it
+    D2CfgContextPtr context = srv_->getD2CfgContext();
+    ConstElementPtr unparsed;
+    ASSERT_NO_THROW(unparsed = context->toElement());
+
+    // dump if wanted else check
+    if (generate_action) {
+        std::cerr << "/ Generated Configuration (remove this line)\n";
+        ASSERT_NO_THROW(expected = prettyPrint(unparsed));
+        prettyPrint(unparsed, std::cerr, 0, 4);
+        std::cerr << "\n";
+    } else {
+        ConstElementPtr json;
+        ASSERT_NO_THROW(json = parseDHCPDDNS(expected, true));
+        EXPECT_TRUE(isEquivalent(unparsed, json));
+        std::string current = prettyPrint(unparsed, 0, 4) + "\n";
+        EXPECT_EQ(expected, current);
+        if (expected != current) {
+            expected = current;
+        }
+    }
+
+    // execute the d2 configuration
+    EXPECT_TRUE(executeConfiguration(expected, "unparsed config"));
+
+    // is it a fixed point?
+    D2CfgContextPtr context2 = srv_->getD2CfgContext();
+    ConstElementPtr unparsed2;
+    ASSERT_NO_THROW(unparsed2 = context2->toElement());
+    ASSERT_TRUE(unparsed2);
+    EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
+}

+ 74 - 0
src/bin/d2/tests/testdata/get_config.json

@@ -0,0 +1,74 @@
+{
+    "DhcpDdns": {
+        "dns-server-timeout": 1000,
+        "forward-ddns": {
+            "ddns-domains": [
+                {
+                    "dns-servers": [
+                        {
+                            "hostname": "",
+                            "ip-address": "172.16.1.1",
+                            "port": 53
+                        }
+                    ],
+                    "key-name": "d2.md5.key",
+                    "name": "four.example.com."
+                },
+                {
+                    "dns-servers": [
+                        {
+                            "hostname": "",
+                            "ip-address": "2001:db8:1::10",
+                            "port": 7802
+                        }
+                    ],
+                    "name": "six.example.com."
+                }
+            ]
+        },
+        "ip-address": "172.16.1.10",
+        "ncr-format": "JSON",
+        "ncr-protocol": "UDP",
+        "port": 53001,
+        "reverse-ddns": {
+            "ddns-domains": [
+                {
+                    "dns-servers": [
+                        {
+                            "hostname": "",
+                            "ip-address": "172.16.1.1",
+                            "port": 53001
+                        },
+                        {
+                            "hostname": "",
+                            "ip-address": "192.168.2.10",
+                            "port": 53
+                        }
+                    ],
+                    "key-name": "d2.sha1.key",
+                    "name": "2.0.192.in-addr.arpa."
+                }
+            ]
+        },
+        "tsig-keys": [
+            {
+                "algorithm": "HMAC-MD5",
+                "digest-bits": 0,
+                "name": "d2.md5.key",
+                "secret": "LSWXnfkKZjdPJI5QxlpnfQ=="
+            },
+            {
+                "algorithm": "HMAC-SHA1",
+                "digest-bits": 0,
+                "name": "d2.sha1.key",
+                "secret": "hRrp29wzUv3uzSNRLlY68w=="
+            },
+            {
+                "algorithm": "HMAC-SHA512",
+                "digest-bits": 256,
+                "name": "d2.sha512.key",
+                "secret": "/4wklkm04jeH4anx2MKGJLcya+ZLHldL5d6mK+4q6UXQP7KJ9mS2QG29hh0SJR4LA0ikxNJTUMvir42gLx6fGQ=="
+            }
+        ]
+    }
+}

+ 3 - 4
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
 // 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
 // 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() {
 void Dhcp4to6Ipc::open() {
-    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
+    uint16_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
     if (port == 0) {
     if (port == 0) {
         Dhcp4o6IpcBase::close();
         Dhcp4o6IpcBase::close();
         return;
         return;
@@ -45,8 +45,7 @@ void Dhcp4to6Ipc::open() {
     }
     }
 
 
     int old_fd = socket_fd_;
     int old_fd = socket_fd_;
-    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port),
-                                      ENDPOINT_TYPE_V4);
+    socket_fd_ = Dhcp4o6IpcBase::open(port, ENDPOINT_TYPE_V4);
     if ((old_fd == -1) && (socket_fd_ != old_fd)) {
     if ((old_fd == -1) && (socket_fd_ != old_fd)) {
         IfaceMgr::instance().addExternalSocket(socket_fd_,
         IfaceMgr::instance().addExternalSocket(socket_fd_,
                                                Dhcp4to6Ipc::handler);
                                                Dhcp4to6Ipc::handler);

+ 1 - 16
src/bin/dhcp4/json_config_parser.cc

@@ -341,24 +341,9 @@ public:
         cfg->setDeclinePeriod(probation_period);
         cfg->setDeclinePeriod(probation_period);
 
 
         // Set the DHCPv4-over-DHCPv6 interserver port.
         // 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);
         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<uint32_t>(scope, name));
-    }
 };
 };
 
 
 } // anonymous namespace
 } // anonymous namespace

+ 3 - 0
src/bin/dhcp4/main.cc

@@ -124,6 +124,9 @@ main(int argc, char* argv[]) {
         usage();
         usage();
     }
     }
 
 
+    // This is the DHCPv4 server
+    CfgMgr::instance().setFamily(AF_INET);
+
     if (check_mode) {
     if (check_mode) {
         try {
         try {
 
 

+ 1 - 2
src/bin/dhcp4/simple_parser4.cc

@@ -45,8 +45,7 @@ const SimpleDefaults SimpleParser4::OPTION4_DEF_DEFAULTS = {
 /// for those option-data declarations.
 /// for those option-data declarations.
 const SimpleDefaults SimpleParser4::OPTION4_DEFAULTS = {
 const SimpleDefaults SimpleParser4::OPTION4_DEFAULTS = {
     { "space",        Element::string,  "dhcp4"},
     { "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
 /// @brief This table defines default global values for DHCPv4

+ 1 - 0
src/bin/dhcp4/tests/Makefile.am

@@ -95,6 +95,7 @@ dhcp4_unittests_SOURCES += decline_unittest.cc
 dhcp4_unittests_SOURCES += kea_controller_unittest.cc
 dhcp4_unittests_SOURCES += kea_controller_unittest.cc
 dhcp4_unittests_SOURCES += dhcp4to6_ipc_unittest.cc
 dhcp4_unittests_SOURCES += dhcp4to6_ipc_unittest.cc
 dhcp4_unittests_SOURCES += simple_parser4_unittest.cc
 dhcp4_unittests_SOURCES += simple_parser4_unittest.cc
+dhcp4_unittests_SOURCES += get_config_unittest.cc get_config_unittest.h
 
 
 nodist_dhcp4_unittests_SOURCES = marker_file.h test_libraries.h
 nodist_dhcp4_unittests_SOURCES = marker_file.h test_libraries.h
 
 

+ 63 - 13
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -31,6 +31,7 @@
 #include "test_libraries.h"
 #include "test_libraries.h"
 #include "test_data_files_config.h"
 #include "test_data_files_config.h"
 #include "dhcp4_test_utils.h"
 #include "dhcp4_test_utils.h"
+#include "get_config_unittest.h"
 
 
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
@@ -628,12 +629,15 @@ TEST_F(Dhcp4ParserTest, bogusCommand) {
 /// pool definition.
 /// pool definition.
 TEST_F(Dhcp4ParserTest, emptySubnet) {
 TEST_F(Dhcp4ParserTest, emptySubnet) {
 
 
+    std::string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [  ], "
+        "\"valid-lifetime\": 4000 }";
+
     ConstElementPtr json;
     ConstElementPtr json;
-    EXPECT_NO_THROW(json = parseDHCP4("{ " + genIfaceConfig() + "," +
-                                      "\"rebind-timer\": 2000, "
-                                      "\"renew-timer\": 1000, "
-                                      "\"subnet4\": [  ], "
-                                      "\"valid-lifetime\": 4000 }"));
+    EXPECT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -655,6 +659,7 @@ TEST_F(Dhcp4ParserTest, unspecifiedRenewTimer) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -689,6 +694,7 @@ TEST_F(Dhcp4ParserTest, unspecifiedRebindTimer) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -723,6 +729,7 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -774,6 +781,7 @@ TEST_F(Dhcp4ParserTest, multipleSubnets) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     int cnt = 0; // Number of reconfigurations
     int cnt = 0; // Number of reconfigurations
 
 
@@ -832,6 +840,7 @@ TEST_F(Dhcp4ParserTest, multipleSubnetsExplicitIDs) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     int cnt = 0; // Number of reconfigurations
     int cnt = 0; // Number of reconfigurations
     do {
     do {
@@ -1040,6 +1049,7 @@ TEST_F(Dhcp4ParserTest, nextServerGlobal) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1070,6 +1080,7 @@ TEST_F(Dhcp4ParserTest, nextServerSubnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1168,6 +1179,7 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1206,8 +1218,10 @@ TEST_F(Dhcp4ParserTest, echoClientId) {
 
 
     ConstElementPtr json_false;
     ConstElementPtr json_false;
     ASSERT_NO_THROW(json_false = parseDHCP4(config_false));
     ASSERT_NO_THROW(json_false = parseDHCP4(config_false));
+    extractConfig(config_false);
     ConstElementPtr json_true;
     ConstElementPtr json_true;
     ASSERT_NO_THROW(json_true = parseDHCP4(config_true));
     ASSERT_NO_THROW(json_true = parseDHCP4(config_true));
+    extractConfig(config_true);
 
 
     // Let's check the default. It should be true
     // Let's check the default. It should be true
     ASSERT_TRUE(CfgMgr::instance().getStagingCfg()->getEchoClientId());
     ASSERT_TRUE(CfgMgr::instance().getStagingCfg()->getEchoClientId());
@@ -1248,6 +1262,7 @@ TEST_F(Dhcp4ParserTest, matchClientIdNoGlobal) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1285,6 +1300,7 @@ TEST_F(Dhcp4ParserTest, matchClientIdGlobal) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1317,6 +1333,7 @@ TEST_F(Dhcp4ParserTest, subnetLocal) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1357,6 +1374,7 @@ TEST_F(Dhcp4ParserTest, multiplePools) {
         "\"valid-lifetime\": 4000 }";
         "\"valid-lifetime\": 4000 }";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     ASSERT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1429,6 +1447,7 @@ TEST_F(Dhcp4ParserTest, poolPrefixLen) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1585,6 +1604,7 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config, true));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config, true));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -1651,6 +1671,7 @@ TEST_F(Dhcp4ParserTest, optionDefRecord) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -1705,6 +1726,7 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the option definitions do not exist yet.
     // Make sure that the option definitions do not exist yet.
     ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->
     ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->
@@ -1813,6 +1835,7 @@ TEST_F(Dhcp4ParserTest, optionDefArray) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -1855,6 +1878,7 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulate) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -2056,6 +2080,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
         getCfgOptionDef()->get(DHCP4_OPTION_SPACE, 109);
         getCfgOptionDef()->get(DHCP4_OPTION_SPACE, 109);
@@ -2112,6 +2137,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
         "  } ]"
         "  } ]"
         "}";
         "}";
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Use the configuration string to create new option definition.
     // Use the configuration string to create new option definition.
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -2155,6 +2181,7 @@ TEST_F(Dhcp4ParserTest, optionDataDefaultsGlobal) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -2225,6 +2252,7 @@ TEST_F(Dhcp4ParserTest, optionDataDefaultsSubnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -2306,6 +2334,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -2381,6 +2410,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -2437,6 +2467,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
         "}";
         "}";
 
 
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
@@ -2498,6 +2529,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -2646,6 +2678,7 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -2920,6 +2953,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -2976,6 +3010,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
 
 
 
 
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
@@ -3055,6 +3090,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -3110,6 +3146,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -3287,6 +3324,7 @@ TEST_F(Dhcp4ParserTest, selectedInterfaces) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
 
 
@@ -3326,6 +3364,7 @@ TEST_F(Dhcp4ParserTest, allInterfaces) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
 
 
@@ -3424,6 +3463,7 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) {
     // Convert the JSON string to configuration elements.
     // Convert the JSON string to configuration elements.
     ConstElementPtr config;
     ConstElementPtr config;
     ASSERT_NO_THROW(config = parseDHCP4(config_str, true));
     ASSERT_NO_THROW(config = parseDHCP4(config_str, true));
+    extractConfig(config_str);
 
 
     // Pass the configuration in for parsing.
     // Pass the configuration in for parsing.
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
@@ -3523,6 +3563,7 @@ TEST_F(Dhcp4ParserTest, subnetRelayInfo) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -3710,6 +3751,7 @@ TEST_F(Dhcp4ParserTest, reservations) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -3861,6 +3903,7 @@ TEST_F(Dhcp4ParserTest, reservationWithOptionDefinition) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config, true));
     ASSERT_NO_THROW(json = parseDHCP4(config, true));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -4045,6 +4088,7 @@ TEST_F(Dhcp4ParserTest, hostReservationPerSubnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(hr_config));
     ASSERT_NO_THROW(json = parseDHCP4(hr_config));
+    extractConfig(hr_config);
     ConstElementPtr result;
     ConstElementPtr result;
     EXPECT_NO_THROW(result = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(result = configureDhcp4Server(*srv_, json));
 
 
@@ -4092,6 +4136,7 @@ TEST_F(Dhcp4ParserTest, declineTimerDefault) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -4113,6 +4158,7 @@ TEST_F(Dhcp4ParserTest, dhcp4o6portDefault) {
         "}";
         "}";
     ConstElementPtr config;
     ConstElementPtr config;
     ASSERT_NO_THROW(config = parseDHCP4(config_txt));
     ASSERT_NO_THROW(config = parseDHCP4(config_txt));
+    extractConfig(config_txt);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
@@ -4138,6 +4184,7 @@ TEST_F(Dhcp4ParserTest, declineTimer) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -4193,6 +4240,7 @@ TEST_F(Dhcp4ParserTest, expiredLeasesProcessing) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -4263,6 +4311,7 @@ TEST_F(Dhcp4ParserTest, 4o6default) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -4297,6 +4346,7 @@ TEST_F(Dhcp4ParserTest, 4o6subnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -4393,6 +4443,7 @@ TEST_F(Dhcp4ParserTest, 4o6iface) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -4429,6 +4480,7 @@ TEST_F(Dhcp4ParserTest, 4o6subnetIface) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -4467,6 +4519,7 @@ TEST_F(Dhcp4ParserTest, 4o6subnetInterfaceId) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
 
 
@@ -4517,6 +4570,7 @@ TEST_F(Dhcp4ParserTest, validClientClassDictionary) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP4(config));
     ASSERT_NO_THROW(json = parseDHCP4(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -4557,20 +4611,13 @@ TEST_F(Dhcp4ParserTest, invalidClientClassDictionary) {
         " } ] \n"
         " } ] \n"
         "} \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);
     EXPECT_THROW(parseDHCP4(config), Dhcp4ParseError);
 }
 }
 
 
 // Test verifies that regular configuration does not provide any user context
 // Test verifies that regular configuration does not provide any user context
 // in the address pool.
 // in the address pool.
 TEST_F(Dhcp4ParserTest, poolUserContextMissing) {
 TEST_F(Dhcp4ParserTest, poolUserContextMissing) {
+    extractConfig(PARSER_CONFIGS[0]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[0]), 0, 0, pool);
     getPool(string(PARSER_CONFIGS[0]), 0, 0, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -4580,6 +4627,7 @@ TEST_F(Dhcp4ParserTest, poolUserContextMissing) {
 // Test verifies that it's possible to specify empty user context in the
 // Test verifies that it's possible to specify empty user context in the
 // address pool.
 // address pool.
 TEST_F(Dhcp4ParserTest, poolUserContextEmpty) {
 TEST_F(Dhcp4ParserTest, poolUserContextEmpty) {
+    extractConfig(PARSER_CONFIGS[1]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[1]), 0, 0, pool);
     getPool(string(PARSER_CONFIGS[1]), 0, 0, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -4594,6 +4642,7 @@ TEST_F(Dhcp4ParserTest, poolUserContextEmpty) {
 // Test verifies that it's possible to specify parameters in the user context
 // Test verifies that it's possible to specify parameters in the user context
 // in the address pool.
 // in the address pool.
 TEST_F(Dhcp4ParserTest, poolUserContextData) {
 TEST_F(Dhcp4ParserTest, poolUserContextData) {
+    extractConfig(PARSER_CONFIGS[2]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[2]), 0, 0, pool);
     getPool(string(PARSER_CONFIGS[2]), 0, 0, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -4627,6 +4676,7 @@ TEST_F(Dhcp4ParserTest, poolUserContextData) {
 // Test verifies that it's possible to specify parameters in the user context
 // Test verifies that it's possible to specify parameters in the user context
 // in the min-max address pool.
 // in the min-max address pool.
 TEST_F(Dhcp4ParserTest, pooMinMaxlUserContext) {
 TEST_F(Dhcp4ParserTest, pooMinMaxlUserContext) {
+    extractConfig(PARSER_CONFIGS[3]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[3]), 0, 0, pool);
     getPool(string(PARSER_CONFIGS[3]), 0, 0, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);

+ 1 - 0
src/bin/dhcp4/tests/dhcp4_test_utils.cc

@@ -70,6 +70,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
     subnet_->getCfgOption()->add(opt_routers, false, DHCP4_OPTION_SPACE);
     subnet_->getCfgOption()->add(opt_routers, false, DHCP4_OPTION_SPACE);
 
 
     CfgMgr::instance().clear();
     CfgMgr::instance().clear();
+    CfgMgr::instance().setFamily(AF_INET);
     CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
     CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
     CfgMgr::instance().commit();
     CfgMgr::instance().commit();
 
 

File diff suppressed because it is too large
+ 5878 - 0
src/bin/dhcp4/tests/get_config_unittest.cc


+ 345 - 0
src/bin/dhcp4/tests/get_config_unittest.cc.skel

@@ -0,0 +1,345 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <cc/cfg_to_element.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp4/tests/get_config_unittest.h>
+#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/json_config_parser.h>
+#include <dhcp4/simple_parser4.h>
+
+#include <boost/algorithm/string.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <list>
+
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @name How to fill configurations
+///
+/// Copy get_config_unittest.cc.skel into get_config_unittest.cc
+///
+/// For the extracted configurations define the EXTRACT_CONFIG and
+/// recompile this file. Run dhcp4_unittests on Dhcp4ParserTest
+/// redirecting the standard error to a temporary file, e.g. by
+/// @code
+///    ./dhcp4_unittests --gtest_filter="Dhcp4Parser*" > /dev/null 2> x
+/// @endcode
+///
+/// Update EXTRACTED_CONFIGS with the file content
+///
+/// When configurations have been extracted the corresponding unparsed
+/// configurations must be generated. To do that define GENERATE_ACTION
+/// and recompile this file. Run dhcp4_unittests on Dhcp4GetConfigTest
+/// redirecting the standard error to a temporary file, e.g. by
+/// @code
+///    ./dhcp4_unittests --gtest_filter="Dhcp4GetConfig*" > /dev/null 2> u
+/// @endcode
+///
+/// Update UNPARSED_CONFIGS with the file content, recompile this file
+/// without EXTRACT_CONFIG and GENERATE_ACTION.
+///
+/// @note Check for failures at each step!
+/// @note The tests of this file do not check if configs returned
+/// by @ref isc::dhcp::CfgToElement::ToElement() are complete.
+/// This has to be done manually.
+///
+///@{
+/// @brief extracted configurations
+const char* EXTRACTED_CONFIGS[] = {
+    // "to be replaced"
+};
+
+/// @brief unparsed configurations
+const char* UNPARSED_CONFIGS[] = {
+    // "to be replaced"
+};
+
+/// @brief the number of configurations
+const size_t max_config_counter = sizeof(EXTRACTED_CONFIGS) / sizeof(char*);
+///@}
+
+/// @brief the extraction counter
+///
+/// < 0 means do not extract, >= 0 means extract on extractConfig() calls
+/// and increment
+#ifdef EXTRACT_CONFIG
+int extract_count = 0;
+#else
+int extract_count = -1;
+#endif
+
+/// @brief the generate action
+/// false means do nothing, true means unparse extracted configurations
+#ifdef GENERATE_ACTION
+const bool generate_action = true;
+#else
+const bool generate_action = false;
+static_assert(max_config_counter == sizeof(UNPARSED_CONFIGS) / sizeof(char*),
+              "unparsed configurations must be generated");
+#endif
+
+/// @brief format and output a configuration
+void
+outputFormatted(const std::string& config) {
+    // pretty print it
+    ConstElementPtr json = parseJSON(config);
+    std::string prettier = prettyPrint(json, 4, 4);
+    // get it as a line array
+    std::list<std::string> lines;
+    boost::split(lines, prettier, boost::is_any_of("\n"));
+    // add escapes using again JSON
+    std::list<std::string> escapes;
+    while (!lines.empty()) {
+        const std::string& line = lines.front();
+        ConstElementPtr escaping = Element::create(line + "\n");
+        escapes.push_back(escaping->str());
+        lines.pop_front();
+    }
+    // output them on std::cerr
+    while (!escapes.empty()) {
+        std::cerr << "\n" << escapes.front();
+        escapes.pop_front();
+    }
+}
+
+};
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @ref isc::dhcp::test::extractConfig in the header
+void
+extractConfig(const std::string& config) {
+    // skip when disable
+    if (extract_count < 0) {
+        return;
+    }
+    // mark beginning
+    if (extract_count == 0) {
+        // header (note there is no trailer)
+        std::cerr << "/// put this after const char* EXTRACTED_CONFIGS[] = {\n";
+    } else {
+        // end of previous configuration
+        std::cerr << ",\n";
+    }
+    std::cerr << "    // CONFIGURATION " << extract_count;
+    try {
+        outputFormatted(config);
+    } catch (...) {
+        // mark error
+        std::cerr << "\n//// got an error\n";
+    }
+    ++extract_count;
+}
+
+};
+};
+};
+
+namespace {
+
+/// Test fixture class (code from Dhcp4ParserTest)
+class Dhcp4GetConfigTest : public ::testing::TestWithParam<size_t> {
+public:
+    Dhcp4GetConfigTest()
+    : rcode_(-1) {
+        // Open port 0 means to not do anything at all. We don't want to
+        // deal with sockets here, just check if configuration handling
+        // is sane.
+        srv_.reset(new Dhcpv4Srv(0));
+        // Create fresh context.
+        resetConfiguration();
+    }
+
+    ~Dhcp4GetConfigTest() {
+        resetConfiguration();
+    };
+
+    /// @brief Parse and Execute configuration
+    ///
+    /// Parses a configuration and executes a configuration of the server.
+    /// If the operation fails, the current test will register a failure.
+    ///
+    /// @param config Configuration to parse
+    /// @param operation Operation being performed.  In the case of an error,
+    ///        the error text will include the string "unable to <operation>.".
+    ///
+    /// @return true if the configuration succeeded, false if not.
+    bool
+    executeConfiguration(const std::string& config, const char* operation) {
+        // clear config manager
+        CfgMgr::instance().clear();
+
+        // enable fake network interfaces
+        IfaceMgrTestConfig test_config(true);
+
+        // try JSON parser
+        ConstElementPtr json;
+        try {
+            json = parseJSON(config);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "invalid JSON for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << config << "\n";
+            return (false);
+        }
+
+        // try DHCP4 parser
+        try {
+            json = parseDHCP4(config, true);
+        } catch (...) {
+            ADD_FAILURE() << "parsing failed for " << operation
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // try DHCP4 configure
+        ConstElementPtr status;
+        try {
+            status = configureDhcp4Server(*srv_, json);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // The status object must not be NULL
+        if (!status) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned null on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // Returned value should be 0 (configuration success)
+        comment_ = parseAnswer(rcode_, status);
+        if (rcode_ != 0) {
+            string reason = "";
+            if (comment_) {
+                reason = string(" (") + comment_->stringValue() + string(")");
+            }
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned error code "
+                          << rcode_ << reason << " on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+        return (true);
+    }
+
+    /// @brief Reset configuration database.
+    ///
+    /// This function resets configuration data base by
+    /// removing all subnets and option-data. Reset must
+    /// be performed after each test to make sure that
+    /// contents of the database do not affect result of
+    /// subsequent tests.
+    void resetConfiguration() {
+        string config = "{"
+            "\"interfaces-config\": { \"interfaces\": [ \"*\" ] },"
+            "\"valid-lifetime\": 4000, "
+            "\"subnet4\": [ ], "
+            "\"dhcp-ddns\": { \"enable-updates\" : false }, "
+            "\"option-def\": [ ], "
+            "\"option-data\": [ ] }";
+        EXPECT_TRUE(executeConfiguration(config, "reset configuration"));
+        CfgMgr::instance().clear();
+        CfgMgr::instance().setFamily(AF_INET);
+    }
+
+    boost::scoped_ptr<Dhcpv4Srv> srv_;  ///< DHCP4 server under test
+    int rcode_;                         ///< Return code from element parsing
+    ConstElementPtr comment_;           ///< Reason for parse fail
+};
+
+/// Test a configuration
+TEST_P(Dhcp4GetConfigTest, run) {
+    // configurations have not been extracted yet
+    if (max_config_counter == 0) {
+        return;
+    }
+
+    // get the index of configurations to test
+    size_t config_counter = GetParam();
+
+    // emit unparsed header if wanted
+    if ((config_counter == 0) && generate_action) {
+        std::cerr << "///put this after const char* UNPARSED_CONFIGS[] = {\n";
+    }
+
+    // get the extracted configuration
+    std::string config = EXTRACTED_CONFIGS[config_counter];
+    std::ostringstream ss;
+    ss << "extracted config #" << config_counter;
+
+    // execute the extracted configuration
+    ASSERT_TRUE(executeConfiguration(config, ss.str().c_str()));
+
+    // unparse it
+    ConstSrvConfigPtr extracted = CfgMgr::instance().getStagingCfg();
+    ConstElementPtr unparsed;
+    ASSERT_NO_THROW(unparsed = extracted->toElement());
+    ConstElementPtr dhcp;
+    ASSERT_NO_THROW(dhcp = unparsed->get("Dhcp4"));
+    ASSERT_TRUE(dhcp);
+
+    // dump if wanted else check
+    std::string expected;
+    if (generate_action) {
+        if (config_counter > 0) {
+            std::cerr << ",\n";
+        }
+        std::cerr << "    // CONFIGURATION " << config_counter;
+        ASSERT_NO_THROW(expected = prettyPrint(dhcp));
+        ASSERT_NO_THROW(outputFormatted(dhcp->str()));
+    } else {
+        expected = UNPARSED_CONFIGS[config_counter];
+        ConstElementPtr json;
+        ASSERT_NO_THROW(json = parseDHCP4(expected, true));
+        EXPECT_TRUE(isEquivalent(dhcp, json));
+        std::string current = prettyPrint(dhcp, 4, 4) + "\n";
+        EXPECT_EQ(expected, current);
+        if (expected != current) {
+            expected = current;
+        }
+    }
+
+    // execute the dhcp configuration
+    ss.str("");
+    ss << "unparsed config #" << config_counter;
+    EXPECT_TRUE(executeConfiguration(expected, ss.str().c_str()));
+
+    // is it a fixed point?
+    ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg();
+    ConstElementPtr unparsed2;
+    ASSERT_NO_THROW(unparsed2 = extracted2->toElement());
+    ASSERT_TRUE(unparsed2);
+    EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
+}
+
+/// Define the parametrized test loop
+INSTANTIATE_TEST_CASE_P(Dhcp4GetConfigTest, Dhcp4GetConfigTest,
+                        ::testing::Range(0UL, max_config_counter));
+
+};

+ 27 - 0
src/bin/dhcp4/tests/get_config_unittest.h

@@ -0,0 +1,27 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef GET_CONFIG_UNITTEST_H
+#define GET_CONFIG_UNITTEST_H
+
+#include <string.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Extract a configuration as a C++ source for JSON on std::cerr
+///
+/// This function should be called when a configuration in an unit test
+/// is interesting and should be extracted. Do nothing when extract_count
+/// is negative.
+void extractConfig(const std::string& config);
+
+};
+};
+};
+
+#endif // GET_CONFIG_UNITTEST_H

+ 1 - 2
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
 // 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
 // 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
     // we should have appropriate default value set. See
     // SimpleParser4::OPTION4_DEFAULTS for a list of default values.
     // SimpleParser4::OPTION4_DEFAULTS for a list of default values.
     checkStringValue(option, "space", "dhcp4");
     checkStringValue(option, "space", "dhcp4");
-    checkStringValue(option, "encapsulate", "");
     checkBoolValue(option, "csv-format", true);
     checkBoolValue(option, "csv-format", true);
 }
 }
 
 

+ 3 - 4
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
 // 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
 // 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() {
 void Dhcp6to4Ipc::open() {
-    uint32_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
+    uint16_t port = CfgMgr::instance().getStagingCfg()->getDhcp4o6Port();
     if (port == 0) {
     if (port == 0) {
         Dhcp4o6IpcBase::close();
         Dhcp4o6IpcBase::close();
         return;
         return;
@@ -44,8 +44,7 @@ void Dhcp6to4Ipc::open() {
     }
     }
 
 
     int old_fd = socket_fd_;
     int old_fd = socket_fd_;
-    socket_fd_ = Dhcp4o6IpcBase::open(static_cast<uint16_t>(port),
-                                      ENDPOINT_TYPE_V6);
+    socket_fd_ = Dhcp4o6IpcBase::open(port, ENDPOINT_TYPE_V6);
     if ((old_fd == -1) && (socket_fd_ != old_fd)) {
     if ((old_fd == -1) && (socket_fd_ != old_fd)) {
         IfaceMgr::instance().addExternalSocket(socket_fd_,
         IfaceMgr::instance().addExternalSocket(socket_fd_,
                                                Dhcp6to4Ipc::handler);
                                                Dhcp6to4Ipc::handler);

+ 1 - 28
src/bin/dhcp6/json_config_parser.cc

@@ -214,18 +214,6 @@ public:
 
 
 private:
 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<uint8_t>(scope, name));
-    }
-
     /// Pointer to the created pool object.
     /// Pointer to the created pool object.
     isc::dhcp::Pool6Ptr pool_;
     isc::dhcp::Pool6Ptr pool_;
 
 
@@ -547,24 +535,9 @@ public:
         srv_config->setDeclinePeriod(probation_period);
         srv_config->setDeclinePeriod(probation_period);
 
 
         // Set the DHCPv4-over-DHCPv6 interserver port.
         // 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);
         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<uint32_t>(scope, name));
-    }
 };
 };
 
 
 } // anonymous namespace
 } // anonymous namespace

+ 3 - 0
src/bin/dhcp6/main.cc

@@ -127,6 +127,9 @@ main(int argc, char* argv[]) {
         usage();
         usage();
     }
     }
 
 
+    // This is the DHCPv6 server
+    CfgMgr::instance().setFamily(AF_INET6);
+
     if (check_mode) {
     if (check_mode) {
         try {
         try {
             // We need to initialize logging, in case any error messages are to be printed.
             // We need to initialize logging, in case any error messages are to be printed.

+ 1 - 2
src/bin/dhcp6/simple_parser6.cc

@@ -45,8 +45,7 @@ const SimpleDefaults SimpleParser6::OPTION6_DEF_DEFAULTS = {
 /// for those option-data declarations.
 /// for those option-data declarations.
 const SimpleDefaults SimpleParser6::OPTION6_DEFAULTS = {
 const SimpleDefaults SimpleParser6::OPTION6_DEFAULTS = {
     { "space",        Element::string,  "dhcp6"},
     { "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
 /// @brief This table defines default global values for DHCPv6

+ 1 - 0
src/bin/dhcp6/tests/Makefile.am

@@ -96,6 +96,7 @@ dhcp6_unittests_SOURCES += dhcp6to4_ipc_unittest.cc
 dhcp6_unittests_SOURCES += classify_unittests.cc
 dhcp6_unittests_SOURCES += classify_unittests.cc
 dhcp6_unittests_SOURCES += parser_unittest.cc
 dhcp6_unittests_SOURCES += parser_unittest.cc
 dhcp6_unittests_SOURCES += simple_parser6_unittest.cc
 dhcp6_unittests_SOURCES += simple_parser6_unittest.cc
+dhcp6_unittests_SOURCES += get_config_unittest.cc get_config_unittest.h
 
 
 nodist_dhcp6_unittests_SOURCES  = marker_file.h test_libraries.h
 nodist_dhcp6_unittests_SOURCES  = marker_file.h test_libraries.h
 
 

+ 105 - 50
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -30,6 +30,7 @@
 #include "test_libraries.h"
 #include "test_libraries.h"
 #include "marker_file.h"
 #include "marker_file.h"
 #include "dhcp6_test_utils.h"
 #include "dhcp6_test_utils.h"
+#include "get_config_unittest.h"
 
 
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
@@ -777,16 +778,20 @@ TEST_F(Dhcp6ParserTest, bogusCommand) {
 /// subnets defined can be accepted.
 /// subnets defined can be accepted.
 TEST_F(Dhcp6ParserTest, emptySubnet) {
 TEST_F(Dhcp6ParserTest, emptySubnet) {
 
 
+    string config = "{ " + genIfaceConfig() + ","
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [  ], "
+        "\"valid-lifetime\": 4000 }";
+
     ConstElementPtr json;
     ConstElementPtr json;
-    ASSERT_NO_THROW(json = parseDHCP6("{ " + genIfaceConfig() + ","
-                                      "\"preferred-lifetime\": 3000,"
-                                      "\"rebind-timer\": 2000, "
-                                      "\"renew-timer\": 1000, "
-                                      "\"subnet6\": [  ], "
-                                      "\"valid-lifetime\": 4000 }"));
+    ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    
 
 
     // returned value should be 0 (success)
     // returned value should be 0 (success)
     checkResult(status, 0);
     checkResult(status, 0);
@@ -807,6 +812,7 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -860,6 +866,7 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     do {
     do {
         EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
         EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
@@ -919,6 +926,7 @@ TEST_F(Dhcp6ParserTest, multipleSubnetsExplicitIDs) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     do {
     do {
         EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
         EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
@@ -1062,6 +1070,7 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config4));
     ASSERT_NO_THROW(json = parseDHCP6(config4));
+    extractConfig(config4);
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
 
 
@@ -1134,6 +1143,7 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -1155,20 +1165,23 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
 TEST_F(Dhcp6ParserTest, subnetInterface) {
 TEST_F(Dhcp6ParserTest, subnetInterface) {
 
 
     // There should be at least one interface
     // There should be at least one interface
-
-    string config = "{ " + genIfaceConfig() + ","
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
-        "    \"interface\": \"" + valid_iface_ + "\","
-        "    \"subnet\": \"2001:db8:1::/64\" } ],"
-        "\"valid-lifetime\": 4000 }";
-    cout << config << endl;
+    // As far as I can tell, this is the first lambda in Kea code. Cool.
+    auto config = [this](string iface) {
+        return ("{ " + genIfaceConfig() + ","
+                "\"preferred-lifetime\": 3000,"
+                "\"rebind-timer\": 2000, "
+                "\"renew-timer\": 1000, "
+                "\"subnet6\": [ { "
+                "    \"pools\": [ { "
+                "        \"pool\": \"2001:db8:1::1 - 2001:db8:1::ffff\" } ],"
+                "    \"interface\": \"" + iface + "\","
+                "    \"subnet\": \"2001:db8:1::/64\" } ],"
+                "\"valid-lifetime\": 4000 }"); };
+    cout << config(valid_iface_) << endl;
 
 
     ConstElementPtr json;
     ConstElementPtr json;
-    ASSERT_NO_THROW(json = parseDHCP6(config));
+    ASSERT_NO_THROW(json = parseDHCP6(config(valid_iface_)));
+    extractConfig(config("eth0"));
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -1264,6 +1277,7 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -1414,6 +1428,7 @@ TEST_F(Dhcp6ParserTest, multiplePools) {
         "\"valid-lifetime\": 4000 }";
         "\"valid-lifetime\": 4000 }";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
     ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -1494,6 +1509,7 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
 
 
@@ -1667,6 +1683,7 @@ TEST_F(Dhcp6ParserTest, pdPoolBasics) {
     // Convert the JSON string into Elements.
     // Convert the JSON string into Elements.
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     // Verify that DHCP6 configuration processing succeeds.
     // Verify that DHCP6 configuration processing succeeds.
     // Returned value must be non-empty ConstElementPtr to config result.
     // Returned value must be non-empty ConstElementPtr to config result.
@@ -1725,6 +1742,7 @@ TEST_F(Dhcp6ParserTest, pdPoolPrefixExclude) {
     // Convert the JSON string into Elements.
     // Convert the JSON string into Elements.
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     // Verify that DHCP6 configuration processing succeeds.
     // Verify that DHCP6 configuration processing succeeds.
     // Returned value must be non-empty ConstElementPtr to config result.
     // Returned value must be non-empty ConstElementPtr to config result.
@@ -1805,6 +1823,7 @@ TEST_F(Dhcp6ParserTest, pdPoolList) {
     // Convert the JSON string into Elements.
     // Convert the JSON string into Elements.
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     // Verify that DHCP6 configuration processing succeeds.
     // Verify that DHCP6 configuration processing succeeds.
     // Returned value must be non-empty ConstElementPtr to config result.
     // Returned value must be non-empty ConstElementPtr to config result.
@@ -1861,6 +1880,7 @@ TEST_F(Dhcp6ParserTest, subnetAndPrefixDelegated) {
     // Convert the JSON string into Elements.
     // Convert the JSON string into Elements.
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     // Verify that DHCP6 configuration processing succeeds.
     // Verify that DHCP6 configuration processing succeeds.
     // Returned value must be non-empty ConstElementPtr to config result.
     // Returned value must be non-empty ConstElementPtr to config result.
@@ -1989,6 +2009,7 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -2053,6 +2074,7 @@ TEST_F(Dhcp6ParserTest, optionDefRecord) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -2106,6 +2128,7 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the option definitions do not exist yet.
     // Make sure that the option definitions do not exist yet.
     ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->
     ASSERT_FALSE(CfgMgr::instance().getStagingCfg()->
@@ -2212,6 +2235,7 @@ TEST_F(Dhcp6ParserTest, optionDefArray) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -2252,6 +2276,7 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulate) {
         "}";
         "}";
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
     ASSERT_NO_THROW(json = parseOPTION_DEF(config));
+    extractConfig(config);
 
 
     // Make sure that the particular option definition does not exist.
     // Make sure that the particular option definition does not exist.
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
     OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
@@ -2551,6 +2576,7 @@ TEST_F(Dhcp6ParserTest, optionDataDefaultsGlobal) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -2623,6 +2649,7 @@ TEST_F(Dhcp6ParserTest, optionDataDefaultsSubnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -2713,6 +2740,7 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -2789,6 +2817,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -2846,6 +2875,7 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
         "}";
         "}";
 
 
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
@@ -2905,6 +2935,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -3005,6 +3036,7 @@ TEST_F(Dhcp6ParserTest, optionDataMultiplePools) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -3367,6 +3399,7 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -3426,6 +3459,7 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
@@ -3491,6 +3525,7 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -3543,6 +3578,7 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
         "}";
         "}";
 
 
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(status);
     ASSERT_TRUE(status);
     checkResult(status, 0);
     checkResult(status, 0);
@@ -3756,6 +3792,7 @@ TEST_F(Dhcp6ParserTest, selectedInterfaces) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -3794,6 +3831,7 @@ TEST_F(Dhcp6ParserTest, allInterfaces) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -3825,6 +3863,7 @@ TEST_F(Dhcp6ParserTest, subnetRelayInfo) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -3869,6 +3908,7 @@ TEST_F(Dhcp6ParserTest, classifySubnets) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -3964,6 +4004,7 @@ TEST_F(Dhcp6ParserTest, d2ClientConfig) {
     // Convert the JSON string to configuration elements.
     // Convert the JSON string to configuration elements.
     ConstElementPtr config;
     ConstElementPtr config;
     ASSERT_NO_THROW(config = parseDHCP6(config_str));
     ASSERT_NO_THROW(config = parseDHCP6(config_str));
+    extractConfig(config_str);
 
 
     // Pass the configuration in for parsing.
     // Pass the configuration in for parsing.
     ConstElementPtr status;
     ConstElementPtr status;
@@ -4141,6 +4182,7 @@ TEST_F(Dhcp6ParserTest, reservations) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -4297,6 +4339,7 @@ TEST_F(Dhcp6ParserTest, reservationWithOptionDefinition) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
     checkResult(x, 0);
@@ -4453,15 +4496,17 @@ TEST_F(Dhcp6ParserTest, reservationBogus) {
 /// rfc4649 = remote-id, rfc4580 = subscriber-id).
 /// rfc4649 = remote-id, rfc4580 = subscriber-id).
 TEST_F(Dhcp6ParserTest, macSources1) {
 TEST_F(Dhcp6ParserTest, macSources1) {
 
 
+    string config = "{ " + genIfaceConfig() + ","
+        "\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [  ], "
+        "\"valid-lifetime\": 4000 }";
+
     ConstElementPtr json;
     ConstElementPtr json;
-    ASSERT_NO_THROW(json =
-        parseDHCP6("{ " + genIfaceConfig() + ","
-                   "\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\" ],"
-                   "\"preferred-lifetime\": 3000,"
-                   "\"rebind-timer\": 2000, "
-                   "\"renew-timer\": 1000, "
-                   "\"subnet6\": [  ], "
-                   "\"valid-lifetime\": 4000 }"));
+    ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -4480,16 +4525,18 @@ TEST_F(Dhcp6ParserTest, macSources1) {
 /// MAC/Hardware sources. This uses specific method names.
 /// MAC/Hardware sources. This uses specific method names.
 TEST_F(Dhcp6ParserTest, macSources2) {
 TEST_F(Dhcp6ParserTest, macSources2) {
 
 
+    string config = "{ " + genIfaceConfig() + ","
+        "\"mac-sources\": [ \"client-link-addr-option\", \"remote-id\", "
+        "                   \"subscriber-id\"],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [  ], "
+        "\"valid-lifetime\": 4000 }";
+
     ConstElementPtr json;
     ConstElementPtr json;
-    ASSERT_NO_THROW(json =
-        parseDHCP6("{ " + genIfaceConfig() + ","
-                   "\"mac-sources\": [ \"client-link-addr-option\", \"remote-id\", "
-                   "                   \"subscriber-id\"],"
-                   "\"preferred-lifetime\": 3000,"
-                   "\"rebind-timer\": 2000, "
-                   "\"renew-timer\": 1000, "
-                   "\"subnet6\": [  ], "
-                   "\"valid-lifetime\": 4000 }"));
+    ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -4582,6 +4629,7 @@ TEST_F(Dhcp6ParserTest, hostReservationPerSubnet) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(HR_CONFIG));
     ASSERT_NO_THROW(json = parseDHCP6(HR_CONFIG));
+    extractConfig(HR_CONFIG);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -4659,15 +4707,17 @@ TEST_F(Dhcp6ParserTest, rsooNumbers) {
 /// Relay Supplied options (specified as names).
 /// Relay Supplied options (specified as names).
 TEST_F(Dhcp6ParserTest, rsooNames) {
 TEST_F(Dhcp6ParserTest, rsooNames) {
 
 
+    string config = "{ " + genIfaceConfig() + ","
+        "\"relay-supplied-options\": [ \"dns-servers\", \"remote-id\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [  ], "
+        "\"valid-lifetime\": 4000 }";
+
     ConstElementPtr json;
     ConstElementPtr json;
-    ASSERT_NO_THROW(json =
-        parseDHCP6("{ " + genIfaceConfig() + ","
-                   "\"relay-supplied-options\": [ \"dns-servers\", \"remote-id\" ],"
-                   "\"preferred-lifetime\": 3000,"
-                   "\"rebind-timer\": 2000, "
-                   "\"renew-timer\": 1000, "
-                   "\"subnet6\": [  ], "
-                   "\"valid-lifetime\": 4000 }"));
+    ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -4753,6 +4803,7 @@ TEST_F(Dhcp6ParserTest, declineTimerDefault) {
         "}";
         "}";
     ConstElementPtr config;
     ConstElementPtr config;
     ASSERT_NO_THROW(config = parseDHCP6(config_txt));
     ASSERT_NO_THROW(config = parseDHCP6(config_txt));
+    extractConfig(config_txt);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
@@ -4775,6 +4826,7 @@ TEST_F(Dhcp6ParserTest, dhcp4o6portDefault) {
         "}";
         "}";
     ConstElementPtr config;
     ConstElementPtr config;
     ASSERT_NO_THROW(config = parseDHCP6(config_txt));
     ASSERT_NO_THROW(config = parseDHCP6(config_txt));
+    extractConfig(config_txt);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
@@ -4797,6 +4849,7 @@ TEST_F(Dhcp6ParserTest, declineTimer) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -4851,6 +4904,7 @@ TEST_F(Dhcp6ParserTest, expiredLeasesProcessing) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -4929,6 +4983,7 @@ TEST_F(Dhcp6ParserTest, validClientClassDictionary) {
 
 
     ConstElementPtr json;
     ConstElementPtr json;
     ASSERT_NO_THROW(json = parseDHCP6(config));
     ASSERT_NO_THROW(json = parseDHCP6(config));
+    extractConfig(config);
 
 
     ConstElementPtr status;
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
     EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
@@ -4969,19 +5024,13 @@ TEST_F(Dhcp6ParserTest, invalidClientClassDictionary) {
         " } ] \n"
         " } ] \n"
         "} \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);
     EXPECT_THROW(parseDHCP6(config), Dhcp6ParseError);
 }
 }
 
 
 // Test verifies that regular configuration does not provide any user context
 // Test verifies that regular configuration does not provide any user context
 // in the address pool.
 // in the address pool.
 TEST_F(Dhcp6ParserTest, poolUserContextMissing) {
 TEST_F(Dhcp6ParserTest, poolUserContextMissing) {
+    extractConfig(PARSER_CONFIGS[0]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[0]), 0, 0, Lease::TYPE_NA, pool);
     getPool(string(PARSER_CONFIGS[0]), 0, 0, Lease::TYPE_NA, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -4991,6 +5040,7 @@ TEST_F(Dhcp6ParserTest, poolUserContextMissing) {
 // Test verifies that it's possible to specify empty user context in the
 // Test verifies that it's possible to specify empty user context in the
 // address pool.
 // address pool.
 TEST_F(Dhcp6ParserTest, poolUserContextEmpty) {
 TEST_F(Dhcp6ParserTest, poolUserContextEmpty) {
+    extractConfig(PARSER_CONFIGS[1]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[1]), 0, 0, Lease::TYPE_NA, pool);
     getPool(string(PARSER_CONFIGS[1]), 0, 0, Lease::TYPE_NA, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -5005,6 +5055,7 @@ TEST_F(Dhcp6ParserTest, poolUserContextEmpty) {
 // Test verifies that it's possible to specify parameters in the user context
 // Test verifies that it's possible to specify parameters in the user context
 // in the address pool.
 // in the address pool.
 TEST_F(Dhcp6ParserTest, poolUserContextlw4over6) {
 TEST_F(Dhcp6ParserTest, poolUserContextlw4over6) {
+    extractConfig(PARSER_CONFIGS[2]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[2]), 0, 0, Lease::TYPE_NA, pool);
     getPool(string(PARSER_CONFIGS[2]), 0, 0, Lease::TYPE_NA, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -5044,6 +5095,7 @@ TEST_F(Dhcp6ParserTest, poolUserContextlw4over6) {
 // Test verifies that it's possible to specify parameters in the user context
 // Test verifies that it's possible to specify parameters in the user context
 // in the min-max address pool.
 // in the min-max address pool.
 TEST_F(Dhcp6ParserTest, poolMinMaxUserContext) {
 TEST_F(Dhcp6ParserTest, poolMinMaxUserContext) {
+    extractConfig(PARSER_CONFIGS[3]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[3]), 0, 0, Lease::TYPE_NA, pool);
     getPool(string(PARSER_CONFIGS[3]), 0, 0, Lease::TYPE_NA, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -5083,6 +5135,7 @@ TEST_F(Dhcp6ParserTest, poolMinMaxUserContext) {
 // Test verifies that regular configuration does not provide any user context
 // Test verifies that regular configuration does not provide any user context
 // in the address pool.
 // in the address pool.
 TEST_F(Dhcp6ParserTest, pdPoolUserContextMissing) {
 TEST_F(Dhcp6ParserTest, pdPoolUserContextMissing) {
+    extractConfig(PARSER_CONFIGS[4]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[4]), 0, 0, Lease::TYPE_PD, pool);
     getPool(string(PARSER_CONFIGS[4]), 0, 0, Lease::TYPE_PD, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -5092,6 +5145,7 @@ TEST_F(Dhcp6ParserTest, pdPoolUserContextMissing) {
 // Test verifies that it's possible to specify empty user context in the
 // Test verifies that it's possible to specify empty user context in the
 // address pool.
 // address pool.
 TEST_F(Dhcp6ParserTest, pdPoolUserContextEmpty) {
 TEST_F(Dhcp6ParserTest, pdPoolUserContextEmpty) {
+    extractConfig(PARSER_CONFIGS[5]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[5]), 0, 0, Lease::TYPE_PD, pool);
     getPool(string(PARSER_CONFIGS[5]), 0, 0, Lease::TYPE_PD, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);
@@ -5106,6 +5160,7 @@ TEST_F(Dhcp6ParserTest, pdPoolUserContextEmpty) {
 // Test verifies that it's possible to specify parameters in the user context
 // Test verifies that it's possible to specify parameters in the user context
 // in the address pool.
 // in the address pool.
 TEST_F(Dhcp6ParserTest, pdPoolUserContextlw4over6) {
 TEST_F(Dhcp6ParserTest, pdPoolUserContextlw4over6) {
+    extractConfig(PARSER_CONFIGS[6]);
     PoolPtr pool;
     PoolPtr pool;
     getPool(string(PARSER_CONFIGS[6]), 0, 0, Lease::TYPE_PD, pool);
     getPool(string(PARSER_CONFIGS[6]), 0, 0, Lease::TYPE_PD, pool);
     ASSERT_TRUE(pool);
     ASSERT_TRUE(pool);

+ 1 - 0
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -66,6 +66,7 @@ Dhcpv6SrvTest::Dhcpv6SrvTest()
     subnet_->addPool(pool_);
     subnet_->addPool(pool_);
 
 
     isc::dhcp::CfgMgr::instance().clear();
     isc::dhcp::CfgMgr::instance().clear();
+    CfgMgr::instance().setFamily(AF_INET6);
     isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_);
     isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_);
     isc::dhcp::CfgMgr::instance().commit();
     isc::dhcp::CfgMgr::instance().commit();
 
 

File diff suppressed because it is too large
+ 6105 - 0
src/bin/dhcp6/tests/get_config_unittest.cc


+ 348 - 0
src/bin/dhcp6/tests/get_config_unittest.cc.skel

@@ -0,0 +1,348 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <cc/cfg_to_element.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcp6/tests/dhcp6_test_utils.h>
+#include <dhcp6/tests/get_config_unittest.h>
+#include <dhcp6/dhcp6_srv.h>
+#include <dhcp6/json_config_parser.h>
+#include <dhcp6/simple_parser6.h>
+
+#include <boost/algorithm/string.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <list>
+
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @name How to fill configurations
+///
+/// Copy get_config_unittest.cc.skel into get_config_unittest.cc
+///
+/// For the extracted configurations define the EXTRACT_CONFIG and
+/// recompile this file. Run dhcp6_unittests on Dhcp6ParserTest
+/// redirecting the standard error to a temporary file, e.g. by
+/// @code
+///    ./dhcp6_unittests --gtest_filter="Dhcp6Parser*" > /dev/null 2> x
+/// @endcode
+///
+/// Update EXTRACTED_CONFIGS with the file content
+///
+/// When configurations have been extracted the corresponding unparsed
+/// configurations must be generated. To do that define GENERATE_ACTION
+/// and recompile this file. Run dhcp6_unittests on Dhcp6GetConfigTest
+/// redirecting the standard error to a temporary file, e.g. by
+/// @code
+///    ./dhcp6_unittests --gtest_filter="Dhcp6GetConfig*" > /dev/null 2> u
+/// @endcode
+///
+/// Update UNPARSED_CONFIGS with the file content, recompile this file
+/// without EXTRACT_CONFIG and GENERATE_ACTION.
+///
+/// @note Check for failures at each step!
+/// @note The tests of this file do not check if configs returned
+/// by @ref isc::dhcp::CfgToElement::ToElement() are complete.
+/// This has to be done manually.
+///
+///@{
+/// @brief extracted configurations
+const char* EXTRACTED_CONFIGS[] = {
+    // "to be replaced"
+};
+
+/// @brief unparsed configurations
+const char* UNPARSED_CONFIGS[] = {
+    // "to be replaced"
+};
+
+/// @brief the number of configurations
+const size_t max_config_counter = sizeof(EXTRACTED_CONFIGS) / sizeof(char*);
+///@}
+
+/// @brief the extraction counter
+///
+/// < 0 means do not extract, >= 0 means extract on extractConfig() calls
+/// and increment
+#ifdef EXTRACT_CONFIG
+int extract_count = 0;
+#else
+int extract_count = -1;
+#endif
+
+/// @brief the generate action
+/// false means do nothing, true means unparse extracted configurations
+#ifdef GENERATE_ACTION
+const bool generate_action = true;
+#else
+const bool generate_action = false;
+static_assert(max_config_counter == sizeof(UNPARSED_CONFIGS) / sizeof(char*),
+              "unparsed configurations must be generated");
+#endif
+
+/// @brief format and output a configuration
+void
+outputFormatted(const std::string& config) {
+    // pretty print it
+    ConstElementPtr json = parseJSON(config);
+    std::string prettier = prettyPrint(json, 4, 4);
+    // get it as a line array
+    std::list<std::string> lines;
+    boost::split(lines, prettier, boost::is_any_of("\n"));
+    // add escapes using again JSON
+    std::list<std::string> escapes;
+    while (!lines.empty()) {
+        const std::string& line = lines.front();
+        ConstElementPtr escaping = Element::create(line + "\n");
+        escapes.push_back(escaping->str());
+        lines.pop_front();
+    }
+    // output them on std::cerr
+    while (!escapes.empty()) {
+        std::cerr << "\n" << escapes.front();
+        escapes.pop_front();
+    }
+}
+
+};
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @ref isc::dhcp::test::extractConfig in the header
+void
+extractConfig(const std::string& config) {
+    // skip when disable
+    if (extract_count < 0) {
+        return;
+    }
+    // mark beginning
+    if (extract_count == 0) {
+        // header (note there is no trailer)
+        std::cerr << "/// put this after const char* EXTRACTED_CONFIGS[] = {\n";
+    } else {
+        // end of previous configuration
+        std::cerr << ",\n";
+    }
+    std::cerr << "    // CONFIGURATION " << extract_count;
+    try {
+        outputFormatted(config);
+    } catch (...) {
+        // mark error
+        std::cerr << "\n//// got an error\n";
+    }
+    ++extract_count;
+}
+
+};
+};
+};
+
+namespace {
+
+/// Test fixture class (code from Dhcp6ParserTest)
+class Dhcp6GetConfigTest : public ::testing::TestWithParam<size_t> {
+public:
+    Dhcp6GetConfigTest() : rcode_(-1), srv_(0) {
+        // srv_(0) means to not open any sockets. We don't want to
+        // deal with sockets here, just check if configuration handling
+        // is sane.
+
+        // Reset configuration for each test.
+        resetConfiguration();
+    }
+
+    ~Dhcp6GetConfigTest() {
+        // Reset configuration database after each test.
+        resetConfiguration();
+    };
+
+    /// @brief Parse and Execute configuration
+    ///
+    /// Parses a configuration and executes a configuration of the server.
+    /// If the operation fails, the current test will register a failure.
+    ///
+    /// @param config Configuration to parse
+    /// @param operation Operation being performed.  In the case of an error,
+    ///        the error text will include the string "unable to <operation>.".
+    ///
+    /// @return true if the configuration succeeded, false if not.
+    bool
+    executeConfiguration(const std::string& config, const char* operation) {
+        // clear config manager
+        CfgMgr::instance().clear();
+
+        // enable fake network interfaces
+        IfaceMgrTestConfig test_config(true);
+
+        // try JSON parser
+        ConstElementPtr json;
+        try {
+            json = parseJSON(config);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "invalid JSON for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << config << "\n";
+            return (false);
+        }
+
+        // try DHCP6 parser
+        try {
+            json = parseDHCP6(config, true);
+        } catch (...) {
+            ADD_FAILURE() << "parsing failed for " << operation
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // try DHCP6 configure
+        ConstElementPtr status;
+        try {
+            status = configureDhcp6Server(srv_, json);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " failed with " << ex.what()
+                          << " on\n" << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // The status object must not be NULL
+        if (!status) {
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned null on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+
+        // Returned value should be 0 (configuration success)
+        comment_ = parseAnswer(rcode_, status);
+        if (rcode_ != 0) {
+            string reason = "";
+            if (comment_) {
+                reason = string(" (") + comment_->stringValue() + string(")");
+            }
+            ADD_FAILURE() << "configure for " << operation
+                          << " returned error code "
+                          << rcode_ << reason << " on\n"
+                          << prettyPrint(json) << "\n";
+            return (false);
+        }
+        return (true);
+    }
+
+    /// @brief Reset configuration database.
+    ///
+    /// This function resets configuration data base by
+    /// removing all subnets and option-data. Reset must
+    /// be performed after each test to make sure that
+    /// contents of the database do not affect result of
+    /// subsequent tests.
+    void resetConfiguration() {
+        string config = "{"
+            "\"interfaces-config\": { \"interfaces\": [ \"*\" ] },"
+            "\"preferred-lifetime\": 3000,"
+            "\"rebind-timer\": 2000, "
+            "\"renew-timer\": 1000, "
+            "\"valid-lifetime\": 4000, "
+            "\"subnet6\": [ ], "
+            "\"dhcp-ddns\": { \"enable-updates\" : false }, "
+            "\"option-def\": [ ], "
+            "\"option-data\": [ ] }";
+        EXPECT_TRUE(executeConfiguration(config, "reset configuration"));
+        CfgMgr::instance().clear();
+        CfgMgr::instance().setFamily(AF_INET6);
+    }
+
+    int rcode_;          ///< Return code (see @ref isc::config::parseAnswer)
+    Dhcpv6Srv srv_;      ///< Instance of the Dhcp6Srv used during tests
+    ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
+};
+
+/// Test a configuration
+TEST_P(Dhcp6GetConfigTest, run) {
+    // configurations have not been extracted yet
+    if (max_config_counter == 0) {
+        return;
+    }
+
+    // get the index of configurations to test
+    size_t config_counter = GetParam();
+
+    // emit unparsed header if wanted
+    if ((config_counter == 0) && generate_action) {
+        std::cerr << "///put this after const char* UNPARSED_CONFIGS[] = {\n";
+    }
+
+    // get the extracted configuration
+    std::string config = EXTRACTED_CONFIGS[config_counter];
+    std::ostringstream ss;
+    ss << "extracted config #" << config_counter;
+
+    // execute the extracted configuration
+    ASSERT_TRUE(executeConfiguration(config, ss.str().c_str()));
+
+    // unparse it
+    ConstSrvConfigPtr extracted = CfgMgr::instance().getStagingCfg();
+    ConstElementPtr unparsed;
+    ASSERT_NO_THROW(unparsed = extracted->toElement());
+    ConstElementPtr dhcp;
+    ASSERT_NO_THROW(dhcp = unparsed->get("Dhcp6"));
+    ASSERT_TRUE(dhcp);
+
+    // dump if wanted else check
+    std::string expected;
+    if (generate_action) {
+        if (config_counter > 0) {
+            std::cerr << ",\n";
+        }
+        std::cerr << "    // CONFIGURATION " << config_counter;
+        ASSERT_NO_THROW(expected = prettyPrint(dhcp));
+        ASSERT_NO_THROW(outputFormatted(dhcp->str()));
+    } else {
+        expected = UNPARSED_CONFIGS[config_counter];
+        ConstElementPtr json;
+        ASSERT_NO_THROW(json = parseDHCP6(expected, true));
+        EXPECT_TRUE(isEquivalent(dhcp, json));
+        std::string current = prettyPrint(dhcp, 4, 4) + "\n";
+        EXPECT_EQ(expected, current);
+        if (expected != current) {
+            expected = current;
+        }
+    }
+
+    // execute the dhcp configuration
+    ss.str("");
+    ss << "unparsed config #" << config_counter;
+    EXPECT_TRUE(executeConfiguration(expected, ss.str().c_str()));
+
+    // is it a fixed point?
+    ConstSrvConfigPtr extracted2 = CfgMgr::instance().getStagingCfg();
+    ConstElementPtr unparsed2;
+    ASSERT_NO_THROW(unparsed2 = extracted2->toElement());
+    ASSERT_TRUE(unparsed2);
+    EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
+}
+
+/// Define the parametrized test loop
+INSTANTIATE_TEST_CASE_P(Dhcp6GetConfigTest, Dhcp6GetConfigTest,
+                        ::testing::Range(0UL, max_config_counter));
+
+};

+ 27 - 0
src/bin/dhcp6/tests/get_config_unittest.h

@@ -0,0 +1,27 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef GET_CONFIG_UNITTEST_H
+#define GET_CONFIG_UNITTEST_H
+
+#include <string.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Extract a configuration as a C++ source for JSON on std::cerr
+///
+/// This function should be called when a configuration in an unit test
+/// is interesting and should be extracted. Do nothing when extract_count
+/// is negative.
+void extractConfig(const std::string& config);
+
+};
+};
+};
+
+#endif // GET_CONFIG_UNITTEST_H

+ 2 - 3
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
 // 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
 // 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
 // This test checks if the parameters in option-data are assigned default values
 // if not explicitly specified.
 // if not explicitly specified.
-TEST_F(SimpleParser6Test, optionDataDefaults4) {
+TEST_F(SimpleParser6Test, optionDataDefaults6) {
     ElementPtr global = parseJSON("{ \"renew-timer\": 1,"
     ElementPtr global = parseJSON("{ \"renew-timer\": 1,"
                                   "  \"rebind-timer\": 2,"
                                   "  \"rebind-timer\": 2,"
                                   "  \"preferred-lifetime\": 3,"
                                   "  \"preferred-lifetime\": 3,"
@@ -179,7 +179,6 @@ TEST_F(SimpleParser6Test, optionDataDefaults4) {
     // we should have appropriate default value set. See
     // we should have appropriate default value set. See
     // SimpleParser4::OPTION4_DEFAULTS for a list of default values.
     // SimpleParser4::OPTION4_DEFAULTS for a list of default values.
     checkStringValue(option, "space", "dhcp6");
     checkStringValue(option, "space", "dhcp6");
-    checkStringValue(option, "encapsulate", "");
     checkBoolValue(option, "csv-format", true);
     checkBoolValue(option, "csv-format", true);
 }
 }
 
 

+ 2 - 2
src/lib/cc/cfg_to_element.h

@@ -30,9 +30,9 @@ struct CfgToElement {
     /// Destructor
     /// Destructor
     virtual ~CfgToElement() { }
     virtual ~CfgToElement() { }
 
 
-    /// @brief Unparse a configuration objet
+    /// @brief Unparse a configuration object
     ///
     ///
-    /// Returns an element which must parse into the same objet, i.e.
+    /// Returns an element which must parse into the same object, i.e.
     /// @code
     /// @code
     /// for all valid config C parse(parse(C)->toElement()) == parse(C)
     /// for all valid config C parse(parse(C)->toElement()) == parse(C)
     /// @endcode
     /// @endcode

+ 236 - 0
src/lib/cc/data.cc

@@ -11,6 +11,7 @@
 #include <cstring>
 #include <cstring>
 #include <cassert>
 #include <cassert>
 #include <climits>
 #include <climits>
+#include <list>
 #include <map>
 #include <map>
 #include <cstdio>
 #include <cstdio>
 #include <iostream>
 #include <iostream>
@@ -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<ElementPtr> 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<std::string, ConstElementPtr> 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<ConstElementPtr> 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<std::string, ConstElementPtr> 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<ElementPtr> 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<std::string, ConstElementPtr> 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) {
 void Element::preprocess(std::istream& in, std::stringstream& out) {
 
 
     std::string line;
     std::string line;

+ 160 - 116
src/lib/cc/data.h

@@ -23,7 +23,7 @@ typedef boost::shared_ptr<Element> ElementPtr;
 typedef boost::shared_ptr<const Element> ConstElementPtr;
 typedef boost::shared_ptr<const Element> ConstElementPtr;
 
 
 ///
 ///
-/// \brief A standard Data module exception that is thrown if a function
+/// @brief A standard Data module exception that is thrown if a function
 /// is called for an Element that has a wrong type (e.g. int_value on a
 /// is called for an Element that has a wrong type (e.g. int_value on a
 /// ListElement)
 /// ListElement)
 ///
 ///
@@ -34,7 +34,7 @@ public:
 };
 };
 
 
 ///
 ///
-/// \brief A standard Data module exception that is thrown if a parse
+/// @brief A standard Data module exception that is thrown if a parse
 /// error is encountered when constructing an Element from a string
 /// error is encountered when constructing an Element from a string
 ///
 ///
 // i'd like to use Exception here but we need one that is derived from
 // i'd like to use Exception here but we need one that is derived from
@@ -47,32 +47,32 @@ public:
 };
 };
 
 
 ///
 ///
-/// \brief The \c Element class represents a piece of data, used by
+/// @brief The @c Element class represents a piece of data, used by
 /// the command channel and configuration parts.
 /// the command channel and configuration parts.
 ///
 ///
-/// An \c Element can contain simple types (int, real, string, bool and
+/// An @c Element can contain simple types (int, real, string, bool and
 /// None), and composite types (list and string->element maps)
 /// None), and composite types (list and string->element maps)
 ///
 ///
 /// Elements should in calling functions usually be referenced through
 /// Elements should in calling functions usually be referenced through
-/// an \c ElementPtr, which can be created using the factory functions
-/// \c Element::create() and \c Element::fromJSON()
+/// an @c ElementPtr, which can be created using the factory functions
+/// @c Element::create() and @c Element::fromJSON()
 ///
 ///
 /// Notes to developers: Element is a base class, implemented by a
 /// Notes to developers: Element is a base class, implemented by a
 /// specific subclass for each type (IntElement, BoolElement, etc).
 /// specific subclass for each type (IntElement, BoolElement, etc).
 /// Element does define all functions for all types, and defaults to
 /// Element does define all functions for all types, and defaults to
-/// raising a \c TypeError for functions that are not supported for
+/// raising a @c TypeError for functions that are not supported for
 /// the type in question.
 /// the type in question.
 ///
 ///
 class Element {
 class Element {
 
 
 public:
 public:
-    /// \brief Represents the position of the data element within a
+    /// @brief Represents the position of the data element within a
     /// configuration string.
     /// configuration string.
     ///
     ///
     /// Position comprises a file name, line number and an offset within this
     /// Position comprises a file name, line number and an offset within this
     /// line where the element value starts. For example, if the JSON string is
     /// line where the element value starts. For example, if the JSON string is
     ///
     ///
-    /// \code
+    /// @code
     /// { "foo": "some string",
     /// { "foo": "some string",
     ///   "bar": 123 }
     ///   "bar": 123 }
     /// \endcode
     /// \endcode
@@ -90,27 +90,27 @@ public:
         uint32_t line_;    ///< Line number.
         uint32_t line_;    ///< Line number.
         uint32_t pos_;     ///< Position within the line.
         uint32_t pos_;     ///< Position within the line.
 
 
-        /// \brief Default constructor.
+        /// @brief Default constructor.
         Position() : file_(""), line_(0), pos_(0) {
         Position() : file_(""), line_(0), pos_(0) {
         }
         }
 
 
-        /// \brief Constructor.
+        /// @brief Constructor.
         ///
         ///
-        /// \param file File name.
-        /// \param line Line number.
-        /// \param pos Position within the line.
+        /// @param file File name.
+        /// @param line Line number.
+        /// @param pos Position within the line.
         Position(const std::string& file, const uint32_t line,
         Position(const std::string& file, const uint32_t line,
                  const uint32_t pos)
                  const uint32_t pos)
             : file_(file), line_(line), pos_(pos) {
             : file_(file), line_(line), pos_(pos) {
         }
         }
 
 
-        /// \brief Returns the position in the textual format.
+        /// @brief Returns the position in the textual format.
         ///
         ///
         /// The returned position has the following format: file:line:pos.
         /// The returned position has the following format: file:line:pos.
         std::string str() const;
         std::string str() const;
     };
     };
 
 
-    /// \brief Returns @c Position object with line_ and pos_ set to 0, and
+    /// @brief Returns @c Position object with line_ and pos_ set to 0, and
     /// with an empty file name.
     /// with an empty file name.
     ///
     ///
     /// The object containing two zeros is a default for most of the
     /// The object containing two zeros is a default for most of the
@@ -128,15 +128,15 @@ private:
     // function getType?
     // function getType?
     int type_;
     int type_;
 
 
-    /// \brief Position of the element in the configuration string.
+    /// @brief Position of the element in the configuration string.
     Position position_;
     Position position_;
 
 
 protected:
 protected:
 
 
-    /// \brief Constructor.
+    /// @brief Constructor.
     ///
     ///
-    /// \param t Element type.
-    /// \param pos Structure holding position of the value of the data element.
+    /// @param t Element type.
+    /// @param pos Structure holding position of the value of the data element.
     /// It comprises the line number and the position within this line. The values
     /// It comprises the line number and the position within this line. The values
     /// held in this structure are used for error logging purposes.
     /// held in this structure are used for error logging purposes.
     Element(int t, const Position& pos = ZERO_POSITION())
     Element(int t, const Position& pos = ZERO_POSITION())
@@ -152,10 +152,10 @@ public:
     // base class; make dtor virtual
     // base class; make dtor virtual
     virtual ~Element() {};
     virtual ~Element() {};
 
 
-    /// \return the type of this element
+    /// @return the type of this element
     int getType() const { return (type_); }
     int getType() const { return (type_); }
 
 
-    /// \brief Returns position where the data element's value starts in a
+    /// @brief Returns position where the data element's value starts in a
     /// configuration string.
     /// configuration string.
     ///
     ///
     /// @warning The returned reference is valid as long as the object which
     /// @warning The returned reference is valid as long as the object which
@@ -168,17 +168,17 @@ public:
     ///
     ///
     /// The resulting string will contain the Element in JSON format.
     /// The resulting string will contain the Element in JSON format.
     ///
     ///
-    /// \return std::string containing the string representation
+    /// @return std::string containing the string representation
     std::string str() const;
     std::string str() const;
 
 
     /// Returns the wireformat for the Element and all its child
     /// Returns the wireformat for the Element and all its child
     /// elements.
     /// elements.
     ///
     ///
-    /// \return std::string containing the element in wire format
+    /// @return std::string containing the element in wire format
     std::string toWire() const;
     std::string toWire() const;
     void toWire(std::ostream& out) const;
     void toWire(std::ostream& out) const;
 
 
-    /// \brief Add the position to a TypeError message
+    /// @brief Add the position to a TypeError message
     /// should be used in place of isc_throw(TypeError, error)
     /// should be used in place of isc_throw(TypeError, error)
 #define throwTypeError(error)                   \
 #define throwTypeError(error)                   \
     {                                           \
     {                                           \
@@ -191,18 +191,18 @@ public:
         isc_throw(TypeError, msg_);             \
         isc_throw(TypeError, msg_);             \
     }
     }
 
 
-    /// \name pure virtuals, every derived class must implement these
+    /// @name pure virtuals, every derived class must implement these
 
 
-    /// \return true if the other ElementPtr has the same type and value
+    /// @return true if the other ElementPtr has the same type and value
     virtual bool equals(const Element& other) const = 0;
     virtual bool equals(const Element& other) const = 0;
 
 
     /// Converts the Element to JSON format and appends it to
     /// Converts the Element to JSON format and appends it to
     /// the given stringstream.
     /// the given stringstream.
     virtual void toJSON(std::ostream& ss) const = 0;
     virtual void toJSON(std::ostream& ss) const = 0;
 
 
-    /// \name Type-specific getters
+    /// @name Type-specific getters
     ///
     ///
-    /// \brief These functions only
+    /// @brief These functions only
     /// work on their corresponding Element type. For all other
     /// work on their corresponding Element type. For all other
     /// types, a TypeError is thrown.
     /// types, a TypeError is thrown.
     /// If you want an exception-safe getter method, use
     /// If you want an exception-safe getter method, use
@@ -226,9 +226,9 @@ public:
     };
     };
     //@}
     //@}
 
 
-    /// \name Exception-safe getters
+    /// @name Exception-safe getters
     ///
     ///
-    /// \brief The getValue() functions return false if the given reference
+    /// @brief The getValue() functions return false if the given reference
     /// is of another type than the element contains
     /// is of another type than the element contains
     /// By default it always returns false; the derived classes
     /// By default it always returns false; the derived classes
     /// override the function for their type, copying their
     /// override the function for their type, copying their
@@ -244,9 +244,9 @@ public:
     //@}
     //@}
 
 
     ///
     ///
-    /// \name Exception-safe setters.
+    /// @name Exception-safe setters.
     ///
     ///
-    /// \brief Return false if the Element is not
+    /// @brief Return false if the Element is not
     /// the right type. Set the value and return true if the Elements
     /// the right type. Set the value and return true if the Elements
     /// is of the correct type
     /// is of the correct type
     ///
     ///
@@ -267,35 +267,35 @@ public:
 
 
     // Other functions for specific subtypes
     // Other functions for specific subtypes
 
 
-    /// \name ListElement functions
+    /// @name ListElement functions
     ///
     ///
-    /// \brief If the Element on which these functions are called are not
+    /// @brief If the Element on which these functions are called are not
     /// an instance of ListElement, a TypeError exception is thrown.
     /// an instance of ListElement, a TypeError exception is thrown.
     //@{
     //@{
     /// Returns the ElementPtr at the given index. If the index is out
     /// Returns the ElementPtr at the given index. If the index is out
     /// of bounds, this function throws an std::out_of_range exception.
     /// of bounds, this function throws an std::out_of_range exception.
-    /// \param i The position of the ElementPtr to return
+    /// @param i The position of the ElementPtr to return
     virtual ConstElementPtr get(const int i) const;
     virtual ConstElementPtr get(const int i) const;
 
 
-    /// \brief returns element as non-const pointer
+    /// @brief returns element as non-const pointer
     ///
     ///
-    /// \param i The position of the ElementPtr to retrieve
-    /// \return specified element pointer
+    /// @param i The position of the ElementPtr to retrieve
+    /// @return specified element pointer
     virtual ElementPtr getNonConst(const int i) const;
     virtual ElementPtr getNonConst(const int i) const;
 
 
     /// Sets the ElementPtr at the given index. If the index is out
     /// Sets the ElementPtr at the given index. If the index is out
     /// of bounds, this function throws an std::out_of_range exception.
     /// of bounds, this function throws an std::out_of_range exception.
-    /// \param i The position of the ElementPtr to set
-    /// \param element The ElementPtr to set at the position
+    /// @param i The position of the ElementPtr to set
+    /// @param element The ElementPtr to set at the position
     virtual void set(const size_t i, ElementPtr element);
     virtual void set(const size_t i, ElementPtr element);
 
 
     /// Adds an ElementPtr to the list
     /// Adds an ElementPtr to the list
-    /// \param element The ElementPtr to add
+    /// @param element The ElementPtr to add
     virtual void add(ElementPtr element);
     virtual void add(ElementPtr element);
 
 
     /// Removes the element at the given position. If the index is out
     /// Removes the element at the given position. If the index is out
     /// of nothing happens.
     /// of nothing happens.
-    /// \param i The index of the element to remove.
+    /// @param i The index of the element to remove.
     virtual void remove(const int i);
     virtual void remove(const int i);
 
 
     /// Returns the number of elements in the list.
     /// Returns the number of elements in the list.
@@ -306,28 +306,28 @@ public:
     //@}
     //@}
 
 
 
 
-    /// \name MapElement functions
+    /// @name MapElement functions
     ///
     ///
-    /// \brief If the Element on which these functions are called are not
+    /// @brief If the Element on which these functions are called are not
     /// an instance of MapElement, a TypeError exception is thrown.
     /// an instance of MapElement, a TypeError exception is thrown.
     //@{
     //@{
     /// Returns the ElementPtr at the given key
     /// Returns the ElementPtr at the given key
-    /// \param name The key of the Element to return
-    /// \return The ElementPtr at the given key, or null if not present
+    /// @param name The key of the Element to return
+    /// @return The ElementPtr at the given key, or null if not present
     virtual ConstElementPtr get(const std::string& name) const;
     virtual ConstElementPtr get(const std::string& name) const;
 
 
     /// Sets the ElementPtr at the given key
     /// Sets the ElementPtr at the given key
-    /// \param name The key of the Element to set
-    /// \param element The ElementPtr to set at the given key.
+    /// @param name The key of the Element to set
+    /// @param element The ElementPtr to set at the given key.
     virtual void set(const std::string& name, ConstElementPtr element);
     virtual void set(const std::string& name, ConstElementPtr element);
 
 
     /// Remove the ElementPtr at the given key
     /// Remove the ElementPtr at the given key
-    /// \param name The key of the Element to remove
+    /// @param name The key of the Element to remove
     virtual void remove(const std::string& name);
     virtual void remove(const std::string& name);
 
 
     /// Checks if there is data at the given key
     /// Checks if there is data at the given key
-    /// \param name The key of the Element to remove
-    /// \return true if there is data at the key, false if not.
+    /// @param name The key of the Element to remove
+    /// @return true if there is data at the key, false if not.
     virtual bool contains(const std::string& name) const;
     virtual bool contains(const std::string& name) const;
 
 
     /// Recursively finds any data at the given identifier. The
     /// Recursively finds any data at the given identifier. The
@@ -339,30 +339,30 @@ public:
     /// Another Element at key "bar", the identifier for that last
     /// Another Element at key "bar", the identifier for that last
     /// element from the first is "foo/bar".
     /// element from the first is "foo/bar".
     ///
     ///
-    /// \param identifier The identifier of the element to find
-    /// \return The ElementPtr at the given identifier. Returns a
+    /// @param identifier The identifier of the element to find
+    /// @return The ElementPtr at the given identifier. Returns a
     /// null ElementPtr if it is not found, which can be checked with
     /// null ElementPtr if it is not found, which can be checked with
     /// Element::is_null(ElementPtr e).
     /// Element::is_null(ElementPtr e).
     virtual ConstElementPtr find(const std::string& identifier) const;
     virtual ConstElementPtr find(const std::string& identifier) const;
 
 
-    /// See \c Element::find()
-    /// \param identifier The identifier of the element to find
-    /// \param t Reference to store the resulting ElementPtr, if found.
-    /// \return true if the element was found, false if not.
+    /// See @c Element::find()
+    /// @param identifier The identifier of the element to find
+    /// @param t Reference to store the resulting ElementPtr, if found.
+    /// @return true if the element was found, false if not.
     virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
     virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
     //@}
     //@}
 
 
 
 
-    /// \name Factory functions
+    /// @name Factory functions
 
 
     // TODO: should we move all factory functions to a different class
     // TODO: should we move all factory functions to a different class
     // so as not to burden the Element base with too many functions?
     // so as not to burden the Element base with too many functions?
     // and/or perhaps even to a separate header?
     // and/or perhaps even to a separate header?
 
 
-    /// \name Direct factory functions
-    /// \brief These functions simply wrap the given data directly
+    /// @name Direct factory functions
+    /// @brief These functions simply wrap the given data directly
     /// in an Element object, and return a reference to it, in the form
     /// in an Element object, and return a reference to it, in the form
-    /// of an \c ElementPtr.
+    /// of an @c ElementPtr.
     /// These factory functions are exception-free (unless there is
     /// These factory functions are exception-free (unless there is
     /// no memory available, in which case bad_alloc is raised by the
     /// no memory available, in which case bad_alloc is raised by the
     /// underlying system).
     /// underlying system).
@@ -390,42 +390,42 @@ public:
     static ElementPtr create(const char *s,
     static ElementPtr create(const char *s,
                              const Position& pos = ZERO_POSITION());
                              const Position& pos = ZERO_POSITION());
 
 
-    /// \brief Creates an empty ListElement type ElementPtr.
+    /// @brief Creates an empty ListElement type ElementPtr.
     ///
     ///
-    /// \param pos A structure holding position of the data element value
+    /// @param pos A structure holding position of the data element value
     /// in the configuration string. It is used for error logging purposes.
     /// in the configuration string. It is used for error logging purposes.
     static ElementPtr createList(const Position& pos = ZERO_POSITION());
     static ElementPtr createList(const Position& pos = ZERO_POSITION());
 
 
-    /// \brief Creates an empty MapElement type ElementPtr.
+    /// @brief Creates an empty MapElement type ElementPtr.
     ///
     ///
-    /// \param pos A structure holding position of the data element value
+    /// @param pos A structure holding position of the data element value
     /// in the configuration string. It is used for error logging purposes.
     /// in the configuration string. It is used for error logging purposes.
     static ElementPtr createMap(const Position& pos = ZERO_POSITION());
     static ElementPtr createMap(const Position& pos = ZERO_POSITION());
     //@}
     //@}
 
 
 
 
-    /// \name Compound factory functions
+    /// @name Compound factory functions
 
 
-    /// \brief These functions will parse the given string (JSON)
+    /// @brief These functions will parse the given string (JSON)
     /// representation  of a compound element. If there is a parse
     /// representation  of a compound element. If there is a parse
     /// error, an exception of the type isc::data::JSONError is thrown.
     /// error, an exception of the type isc::data::JSONError is thrown.
 
 
     //@{
     //@{
     /// Creates an Element from the given JSON string
     /// Creates an Element from the given JSON string
-    /// \param in The string to parse the element from
-    /// \param preproc specified whether preprocessing (e.g. comment removal)
+    /// @param in The string to parse the element from
+    /// @param preproc specified whether preprocessing (e.g. comment removal)
     ///                should be performed
     ///                should be performed
-    /// \return An ElementPtr that contains the element(s) specified
+    /// @return An ElementPtr that contains the element(s) specified
     /// in the given string.
     /// in the given string.
     static ElementPtr fromJSON(const std::string& in, bool preproc = false);
     static ElementPtr fromJSON(const std::string& in, bool preproc = false);
 
 
     /// Creates an Element from the given input stream containing JSON
     /// Creates an Element from the given input stream containing JSON
     /// formatted data.
     /// formatted data.
     ///
     ///
-    /// \param in The string to parse the element from
-    /// \param preproc specified whether preprocessing (e.g. comment removal)
+    /// @param in The string to parse the element from
+    /// @param preproc specified whether preprocessing (e.g. comment removal)
     ///                should be performed
     ///                should be performed
-    /// \return An ElementPtr that contains the element(s) specified
+    /// @return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     /// in the given input stream.
     static ElementPtr fromJSON(std::istream& in, bool preproc = false)
     static ElementPtr fromJSON(std::istream& in, bool preproc = false)
         throw(JSONError);
         throw(JSONError);
@@ -433,11 +433,11 @@ public:
     /// Creates an Element from the given input stream containing JSON
     /// Creates an Element from the given input stream containing JSON
     /// formatted data.
     /// formatted data.
     ///
     ///
-    /// \param in The string to parse the element from
-    /// \param file_name specified input file name (used in error reporting)
-    /// \param preproc specified whether preprocessing (e.g. comment removal)
+    /// @param in The string to parse the element from
+    /// @param file_name specified input file name (used in error reporting)
+    /// @param preproc specified whether preprocessing (e.g. comment removal)
     ///                should be performed
     ///                should be performed
-    /// \return An ElementPtr that contains the element(s) specified
+    /// @return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     /// in the given input stream.
     static ElementPtr fromJSON(std::istream& in, const std::string& file_name,
     static ElementPtr fromJSON(std::istream& in, const std::string& file_name,
                                bool preproc = false)
                                bool preproc = false)
@@ -446,13 +446,13 @@ public:
     /// Creates an Element from the given input stream, where we keep
     /// Creates an Element from the given input stream, where we keep
     /// track of the location in the stream for error reporting.
     /// track of the location in the stream for error reporting.
     ///
     ///
-    /// \param in The string to parse the element from.
-    /// \param file The input file name.
-    /// \param line A reference to the int where the function keeps
+    /// @param in The string to parse the element from.
+    /// @param file The input file name.
+    /// @param line A reference to the int where the function keeps
     /// track of the current line.
     /// track of the current line.
-    /// \param pos A reference to the int where the function keeps
+    /// @param pos A reference to the int where the function keeps
     /// track of the current position within the current line.
     /// track of the current position within the current line.
-    /// \return An ElementPtr that contains the element(s) specified
+    /// @return An ElementPtr that contains the element(s) specified
     /// in the given input stream.
     /// in the given input stream.
     // make this one private?
     // make this one private?
     static ElementPtr fromJSON(std::istream& in, const std::string& file,
     static ElementPtr fromJSON(std::istream& in, const std::string& file,
@@ -470,23 +470,23 @@ public:
                                    bool preproc = false);
                                    bool preproc = false);
     //@}
     //@}
 
 
-    /// \name Type name conversion functions
+    /// @name Type name conversion functions
 
 
     /// Returns the name of the given type as a string
     /// Returns the name of the given type as a string
     ///
     ///
-    /// \param type The type to return the name of
-    /// \return The name of the type, or "unknown" if the type
+    /// @param type The type to return the name of
+    /// @return The name of the type, or "unknown" if the type
     ///         is not known.
     ///         is not known.
     static std::string typeToName(Element::types type);
     static std::string typeToName(Element::types type);
 
 
     /// Converts the string to the corresponding type
     /// Converts the string to the corresponding type
     /// Throws a TypeError if the name is unknown.
     /// Throws a TypeError if the name is unknown.
     ///
     ///
-    /// \param type_name The name to get the type of
-    /// \return the corresponding type value
+    /// @param type_name The name to get the type of
+    /// @return the corresponding type value
     static Element::types nameToType(const std::string& type_name);
     static Element::types nameToType(const std::string& type_name);
 
 
-    /// \brief input text preprocessor
+    /// @brief input text preprocessor
     ///
     ///
     /// This method performs preprocessing of the input stream (which is
     /// This method performs preprocessing of the input stream (which is
     /// expected to contain a text version of to be parsed JSON). For now the
     /// expected to contain a text version of to be parsed JSON). For now the
@@ -502,7 +502,7 @@ public:
     /// @param out output stream (filtered content will be written here)
     /// @param out output stream (filtered content will be written here)
     static void preprocess(std::istream& in, std::stringstream& out);
     static void preprocess(std::istream& in, std::stringstream& out);
 
 
-    /// \name Wire format factory functions
+    /// @name Wire format factory functions
 
 
     /// These function pparse the wireformat at the given stringstream
     /// These function pparse the wireformat at the given stringstream
     /// (of the given length). If there is a parse error an exception
     /// (of the given length). If there is a parse error an exception
@@ -514,17 +514,17 @@ public:
     /// Since the wire format is JSON, this is the same as
     /// Since the wire format is JSON, this is the same as
     /// fromJSON, and could be removed.
     /// fromJSON, and could be removed.
     ///
     ///
-    /// \param in The input stringstream.
-    /// \param length The length of the wireformat data in the stream
-    /// \return ElementPtr with the data that is parsed.
+    /// @param in The input stringstream.
+    /// @param length The length of the wireformat data in the stream
+    /// @return ElementPtr with the data that is parsed.
     static ElementPtr fromWire(std::stringstream& in, int length);
     static ElementPtr fromWire(std::stringstream& in, int length);
 
 
     /// Creates an Element from the wire format in the given string
     /// Creates an Element from the wire format in the given string
     /// Since the wire format is JSON, this is the same as
     /// Since the wire format is JSON, this is the same as
     /// fromJSON, and could be removed.
     /// fromJSON, and could be removed.
     ///
     ///
-    /// \param s The input string
-    /// \return ElementPtr with the data that is parsed.
+    /// @param s The input string
+    /// @return ElementPtr with the data that is parsed.
     static ElementPtr fromWire(const std::string& s);
     static ElementPtr fromWire(const std::string& s);
     //@}
     //@}
 };
 };
@@ -698,12 +698,12 @@ public:
 };
 };
 
 
 /// Checks whether the given ElementPtr is a NULL pointer
 /// Checks whether the given ElementPtr is a NULL pointer
-/// \param p The ElementPtr to check
-/// \return true if it is NULL, false if not.
+/// @param p The ElementPtr to check
+/// @return true if it is NULL, false if not.
 bool isNull(ConstElementPtr p);
 bool isNull(ConstElementPtr p);
 
 
 ///
 ///
-/// \brief Remove all values from the first ElementPtr that are
+/// @brief Remove all values from the first ElementPtr that are
 /// equal in the second. Both ElementPtrs MUST be MapElements
 /// equal in the second. Both ElementPtrs MUST be MapElements
 /// The use for this function is to end up with a MapElement that
 /// The use for this function is to end up with a MapElement that
 /// only contains new and changed values (for ModuleCCSession and
 /// only contains new and changed values (for ModuleCCSession and
@@ -711,14 +711,14 @@ bool isNull(ConstElementPtr p);
 /// Raises a TypeError if a or b are not MapElements
 /// Raises a TypeError if a or b are not MapElements
 void removeIdentical(ElementPtr a, ConstElementPtr b);
 void removeIdentical(ElementPtr a, ConstElementPtr b);
 
 
-/// \brief Create a new ElementPtr from the first ElementPtr, removing all
+/// @brief Create a new ElementPtr from the first ElementPtr, removing all
 /// values that are equal in the second. Both ElementPtrs MUST be MapElements.
 /// values that are equal in the second. Both ElementPtrs MUST be MapElements.
 /// The returned ElementPtr will be a MapElement that only contains new and
 /// The returned ElementPtr will be a MapElement that only contains new and
 /// changed values (for ModuleCCSession and configuration update handlers).
 /// changed values (for ModuleCCSession and configuration update handlers).
 /// Raises a TypeError if a or b are not MapElements
 /// Raises a TypeError if a or b are not MapElements
 ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b);
 ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b);
 
 
-/// \brief Merges the data from other into element.
+/// @brief Merges the data from other into element.
 /// (on the first level). Both elements must be
 /// (on the first level). Both elements must be
 /// MapElements.
 /// MapElements.
 /// Every string,value pair in other is copied into element
 /// Every string,value pair in other is copied into element
@@ -732,34 +732,78 @@ ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b);
 /// Raises a TypeError if either ElementPtr is not a MapElement
 /// Raises a TypeError if either ElementPtr is not a MapElement
 void merge(ElementPtr element, ConstElementPtr other);
 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.
+/// @brief Insert Element::Position as a string into stream.
 ///
 ///
-/// This operator converts the \c Element::Position into a string and
-/// inserts it into the output stream \c out.
+/// This operator converts the @c Element::Position into a string and
+/// inserts it into the output stream @c out.
 ///
 ///
-/// \param out A \c std::ostream object on which the insertion operation is
+/// @param out A @c std::ostream object on which the insertion operation is
 /// performed.
 /// performed.
-/// \param pos The \c Element::Position structure to insert.
-/// \return A reference to the same \c std::ostream object referenced by
-/// parameter \c out after the insertion operation.
+/// @param pos The @c Element::Position structure to insert.
+/// @return A reference to the same @c std::ostream object referenced by
+/// parameter @c out after the insertion operation.
 std::ostream& operator<<(std::ostream& out, const Element::Position& pos);
 std::ostream& operator<<(std::ostream& out, const Element::Position& pos);
 
 
 ///
 ///
-/// \brief Insert the Element as a string into stream.
+/// @brief Insert the Element as a string into stream.
 ///
 ///
-/// This method converts the \c ElementPtr into a string with
-/// \c Element::str() and inserts it into the
-/// output stream \c out.
+/// This method converts the @c ElementPtr into a string with
+/// @c Element::str() and inserts it into the
+/// output stream @c out.
 ///
 ///
 /// This function overloads the global operator<< to behave as described in
 /// This function overloads the global operator<< to behave as described in
-/// ostream::operator<< but applied to \c ElementPtr objects.
+/// ostream::operator<< but applied to @c ElementPtr objects.
 ///
 ///
-/// \param out A \c std::ostream object on which the insertion operation is
+/// @param out A @c std::ostream object on which the insertion operation is
 /// performed.
 /// performed.
-/// \param e The \c ElementPtr object to insert.
-/// \return A reference to the same \c std::ostream object referenced by
-/// parameter \c out after the insertion operation.
+/// @param e The @c ElementPtr object to insert.
+/// @return A reference to the same @c std::ostream object referenced by
+/// parameter @c out after the insertion operation.
 std::ostream& operator<<(std::ostream& out, const Element& e);
 std::ostream& operator<<(std::ostream& out, const Element& e);
 
 
 bool operator==(const Element& a, const Element& b);
 bool operator==(const Element& a, const Element& b);

+ 1 - 1
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);
     ConstElementPtr elem = parent->get(name);
     if (!elem) {
     if (!elem) {
-        return (data::Element::ZERO_POSITION());
+        return (parent->getPosition());
     }
     }
     return (elem->getPosition());
     return (elem->getPosition());
 }
 }

+ 39 - 3
src/lib/cc/simple_parser.h

@@ -104,9 +104,9 @@ class SimpleParser {
 
 
     /// @brief Utility method that returns position of an element
     /// @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 name position of that element will be returned
     /// @param parent parent element (optional)
     /// @param parent parent element (optional)
@@ -203,6 +203,42 @@ protected:
                       << "' (" << getPosition(name, scope) << ")");
                       << "' (" << 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<uint32_t>(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<uint16_t>(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<uint8_t>(scope, name));
+    }
 };
 };
 
 
 };
 };

+ 192 - 4
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6,10 +6,12 @@
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
+#include <boost/pointer_cast.hpp>
 #include <boost/assign/std/vector.hpp>
 #include <boost/assign/std/vector.hpp>
 #include <climits>
 #include <climits>
 
 
 #include <cc/data.h>
 #include <cc/data.h>
+#include <util/unittests/check_valgrind.h>
 
 
 using namespace isc::data;
 using namespace isc::data;
 
 
@@ -100,7 +102,7 @@ TEST(Element, from_and_to_json) {
 
 
     BOOST_FOREACH(const std::string& s, sv) {
     BOOST_FOREACH(const std::string& s, sv) {
         // Test two types of fromJSON(): with string and istream.
         // 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()
             // test << operator, which uses Element::str()
             if (i == 0) {
             if (i == 0) {
                 el = Element::fromJSON(s);
                 el = Element::fromJSON(s);
@@ -555,6 +557,35 @@ TEST(Element, escape) {
     EXPECT_NO_THROW(Element::fromJSON("\"\\\"\\\"\""));
     EXPECT_NO_THROW(Element::fromJSON("\"\\\"\\\"\""));
     // A whitespace test
     // A whitespace test
     EXPECT_NO_THROW(Element::fromJSON("\"  \n  \r \t \f  \n \n    \t\""));
     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
 // 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"));
     el->set(long_maptag, Element::create("bar"));
     EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
     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) {
 TEST(Element, to_and_from_wire) {
@@ -857,8 +894,9 @@ TEST(Element, constRemoveIdentical) {
     c = Element::fromJSON("{ \"a\": 1 }");
     c = Element::fromJSON("{ \"a\": 1 }");
     EXPECT_EQ(*removeIdentical(a, b), *c);
     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) {
 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<Element>(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
 // This test checks whether it is possible to ignore comments. It also checks
 // that the comments are ignored only when told to.
 // that the comments are ignored only when told to.
 TEST(Element, preprocessor) {
 TEST(Element, preprocessor) {
@@ -999,6 +1183,10 @@ TEST(Element, preprocessor) {
     EXPECT_THROW(Element::fromJSON(dbl_head_comment), JSONError);
     EXPECT_THROW(Element::fromJSON(dbl_head_comment), JSONError);
     EXPECT_THROW(Element::fromJSON(dbl_mid_comment), JSONError);
     EXPECT_THROW(Element::fromJSON(dbl_mid_comment), JSONError);
     EXPECT_THROW(Element::fromJSON(dbl_tail_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) {
 TEST(Element, getPosition) {

+ 3 - 8
src/lib/cc/tests/simple_parser_unittest.cc

@@ -58,14 +58,9 @@ public:
 
 
 class SimpleParserClassTest : public SimpleParser {
 class SimpleParserClassTest : public SimpleParser {
 public:
 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<uint8_t>(scope, name));
-    }
+
+    /// Make getUint8 public
+    using SimpleParser::getUint8;
 
 
     /// @brief Instantiation of getAndConvert
     /// @brief Instantiation of getAndConvert
     ///
     ///

+ 2 - 1
src/lib/dhcpsrv/Makefile.am

@@ -85,10 +85,11 @@ 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 += alloc_engine_log.cc alloc_engine_log.h
 libkea_dhcpsrv_la_SOURCES += base_host_data_source.h
 libkea_dhcpsrv_la_SOURCES += base_host_data_source.h
 libkea_dhcpsrv_la_SOURCES += callout_handle_store.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_db_access.cc cfg_db_access.h
 libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h
 libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h
 libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h
 libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h
+libkea_dhcpsrv_la_SOURCES += cfg_hosts_util.cc cfg_hosts_util.h
 libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h
 libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h
 libkea_dhcpsrv_la_SOURCES += cfg_expiration.cc cfg_expiration.h
 libkea_dhcpsrv_la_SOURCES += cfg_expiration.cc cfg_expiration.h
 libkea_dhcpsrv_la_SOURCES += cfg_host_operations.cc cfg_host_operations.h
 libkea_dhcpsrv_la_SOURCES += cfg_host_operations.cc cfg_host_operations.h

+ 76 - 1
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
 // 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
 // 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
 /// @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 };
 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
 /// @brief calculates the first IPv6 address in a IPv6 prefix
 ///
 ///
 /// Note: This is a private function. Do not use it directly.
 /// 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<int>(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<int>((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) {
 uint64_t prefixesInRange(const uint8_t pool_len, const uint8_t delegated_len) {
     if (delegated_len < pool_len) {
     if (delegated_len < pool_len) {
         return (0);
         return (0);

+ 13 - 1
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
 // 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
 // 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,
 uint64_t addrsInRange(const isc::asiolink::IOAddress& min,
                       const isc::asiolink::IOAddress& max);
                       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.
 /// @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
 /// Note that if the answer is bigger than uint64_t can hold, it will return

+ 49 - 0
src/lib/dhcpsrv/cfg_4o6.cc

@@ -0,0 +1,49 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <dhcp/option.h>
+#include <dhcpsrv/cfg_4o6.h>
+#include <string>
+#include <string.h>
+#include <sstream>
+#include <vector>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+ElementPtr
+Cfg4o6::toElement() const {
+    ElementPtr result = Element::createMap();
+    // Set 4o6-interface
+    result->set("4o6-interface", Element::create(iface4o6_));
+    // Set 4o6-subnet
+    if (!subnet4o6_.first.isV6Zero() || (subnet4o6_.second != 128u)) {
+        std::ostringstream oss;
+        oss << subnet4o6_.first << "/"
+            << static_cast<unsigned>(subnet4o6_.second);
+        result->set("4o6-subnet", Element::create(oss.str()));
+    } else {
+        result->set("4o6-subnet", Element::create(std::string()));
+    }
+    // Set 4o6-interface-id
+    if (interface_id_) {
+        std::vector<uint8_t> bin = interface_id_->toBinary();
+        std::string iid;
+        iid.resize(bin.size());
+        if (!bin.empty()) {
+            std::memcpy(&iid[0], &bin[0], bin.size());
+        }
+        result->set("4o6-interface-id", Element::create(iid));
+    } else {
+        result->set("4o6-interface-id", Element::create(std::string()));
+    }
+    return (result);
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace

+ 9 - 3
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,8 +7,9 @@
 #ifndef CFG_4OVER6_H
 #ifndef CFG_4OVER6_H
 #define CFG_4OVER6_H
 #define CFG_4OVER6_H
 
 
-#include <string>
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
+#include <cc/cfg_to_element.h>
+#include <string>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -17,7 +18,7 @@ namespace dhcp {
 ///
 ///
 /// DHCP4o6 is completely optional. If it is not enabled, this structure
 /// DHCP4o6 is completely optional. If it is not enabled, this structure
 /// does not contain any information.
 /// does not contain any information.
-struct Cfg4o6 {
+struct Cfg4o6 : public isc::data::CfgToElement {
 
 
     /// the default constructor.
     /// the default constructor.
     ///
     ///
@@ -78,6 +79,11 @@ struct Cfg4o6 {
         enabled_ = true;
         enabled_ = true;
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// Specifies if 4o6 is enabled on this subnet.
     /// Specifies if 4o6 is enabled on this subnet.

+ 64 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,7 +9,13 @@
 #include <dhcpsrv/host_data_source_factory.h>
 #include <dhcpsrv/host_data_source_factory.h>
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
 #include <sstream>
 #include <sstream>
+#include <vector>
+
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -59,7 +65,63 @@ CfgDbAccess::getAccessString(const std::string& access_string) const {
     return (s.str());
     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<std::string> 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<int64_t>(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::dhcp namespace
 } // end of isc namespace
 } // end of isc namespace

+ 39 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,6 +7,7 @@
 #ifndef CFG_DBACCESS_H
 #ifndef CFG_DBACCESS_H
 #define CFG_DBACCESS_H
 #define CFG_DBACCESS_H
 
 
+#include <cc/cfg_to_element.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <string>
 #include <string>
 
 
@@ -63,7 +64,14 @@ public:
     /// according to the configuration specified.
     /// according to the configuration specified.
     void createManagers() const;
     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.
     /// @brief Returns lease or host database access string.
     ///
     ///
@@ -88,6 +96,35 @@ typedef boost::shared_ptr<CfgDbAccess> CfgDbAccessPtr;
 /// @brief A pointer to the const @c CfgDbAccess.
 /// @brief A pointer to the const @c CfgDbAccess.
 typedef boost::shared_ptr<const CfgDbAccess> ConstCfgDbAccessPtr;
 typedef boost::shared_ptr<const CfgDbAccess> 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_));
+    }
+};
+
 }
 }
 }
 }
 
 

+ 37 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,8 +9,11 @@
 #include <util/encode/hex.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 #include <util/strutil.h>
 #include <iostream>
 #include <iostream>
+#include <string>
+#include <string.h>
 
 
 using namespace isc;
 using namespace isc;
+using namespace isc::data;
 using namespace isc::util::encode;
 using namespace isc::util::encode;
 using namespace isc::util::str;
 using namespace isc::util::str;
 
 
@@ -71,6 +74,39 @@ CfgDUID::create(const std::string& duid_file_path) const {
     return (factory.get());
     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<long long>(time_)));
+    // Set the enterprise id
+    result->set("enterprise-id",
+                Element::create(static_cast<long long>(enterprise_id_)));
+    // Set the persistence flag
+    result->set("persist", Element::create(persist_));
+    return (result);
+}
 
 
 }
 }
 }
 }

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,7 @@
 #define CFG_DUID_H
 #define CFG_DUID_H
 
 
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
+#include <cc/cfg_to_element.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <stdint.h>
 #include <stdint.h>
 #include <vector>
 #include <vector>
@@ -25,7 +26,7 @@ namespace dhcp {
 /// generate. It also allows for overriding entire default DUID or parts of
 /// generate. It also allows for overriding entire default DUID or parts of
 /// it via configuration file. This class holds the DUID configuration
 /// it via configuration file. This class holds the DUID configuration
 /// specified in the server configuration file.
 /// specified in the server configuration file.
-class CfgDUID {
+class CfgDUID : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Constructor.
     /// @brief Constructor.
@@ -114,6 +115,11 @@ public:
     /// @return Pointer to an instance of new DUID.
     /// @return Pointer to an instance of new DUID.
     DuidPtr create(const std::string& duid_file_path) const;
     DuidPtr create(const std::string& duid_file_path) const;
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief DUID type.
     /// @brief DUID type.

+ 33 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,8 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <limits>
 #include <limits>
 
 
+using namespace isc::data;
+
 namespace isc {
 namespace isc {
 namespace dhcp {
 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<long long>
+                                (reclaim_timer_wait_time_)));
+    // Set flush-reclaimed-timer-wait-time
+    result->set("flush-reclaimed-timer-wait-time",
+                Element::create(static_cast<long long>
+                                (flush_reclaimed_timer_wait_time_)));
+    // Set hold-reclaimed-time
+    result->set("hold-reclaimed-time",
+                Element::create(static_cast<long long>
+                                (hold_reclaimed_time_)));
+    // Set max-reclaim-leases
+    result->set("max-reclaim-leases",
+                Element::create(static_cast<long long>
+                                (max_reclaim_leases_)));
+    // Set max-reclaim-time
+    result->set("max-reclaim-time",
+                Element::create(static_cast<long long>
+                                (max_reclaim_time_)));
+    // Set unwarned-reclaim-cycles
+    result->set("unwarned-reclaim-cycles",
+                Element::create(static_cast<long long>
+                                (unwarned_reclaim_cycles_)));
+    return (result);
+}
+
 }
 }
 }
 }

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,7 @@
 #define CFG_EXPIRATION_H
 #define CFG_EXPIRATION_H
 
 
 #include <asiolink/interval_timer.h>
 #include <asiolink/interval_timer.h>
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/timer_mgr.h>
 #include <dhcpsrv/timer_mgr.h>
 #include <boost/bind.hpp>
 #include <boost/bind.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
@@ -57,7 +58,7 @@ namespace dhcp {
 /// The @c CfgExpiration class provides a collection of accessors and
 /// The @c CfgExpiration class provides a collection of accessors and
 /// modifiers to manage the data. Each accessor checks if the given value
 /// modifiers to manage the data. Each accessor checks if the given value
 /// is in range allowed for this value.
 /// is in range allowed for this value.
-class CfgExpiration {
+class CfgExpiration : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @name Default values.
     /// @name Default values.
@@ -223,6 +224,11 @@ public:
                      void (Instance::*delete_fun)(const uint32_t),
                      void (Instance::*delete_fun)(const uint32_t),
                      Instance* instance_ptr) const;
                      Instance* instance_ptr) const;
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Checks if the value being set by one of the modifiers is
     /// @brief Checks if the value being set by one of the modifiers is

+ 15 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,6 +7,9 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <dhcpsrv/cfg_host_operations.h>
 #include <dhcpsrv/cfg_host_operations.h>
 #include <algorithm>
 #include <algorithm>
+#include <string>
+
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -52,5 +55,16 @@ CfgHostOperations::clearIdentifierTypes() {
     identifier_types_.clear();
     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);
+}
+
 }
 }
 }
 }

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,6 +7,7 @@
 #ifndef CFG_HOST_OPERATIONS_H
 #ifndef CFG_HOST_OPERATIONS_H
 #define CFG_HOST_OPERATIONS_H
 #define CFG_HOST_OPERATIONS_H
 
 
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/host.h>
 #include <dhcpsrv/host.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <list>
 #include <list>
@@ -39,7 +40,7 @@ ConstCfgHostOperationsPtr;
 /// An administrator selects which identifiers the server should
 /// An administrator selects which identifiers the server should
 /// use and in which order to search for host reservations to
 /// use and in which order to search for host reservations to
 /// optimize performance of the server.
 /// optimize performance of the server.
-class CfgHostOperations {
+class CfgHostOperations : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Type of the container holding ordered list of identifiers.
     /// @brief Type of the container holding ordered list of identifiers.
@@ -77,6 +78,11 @@ public:
     /// @brief Removes existing identifier types.
     /// @brief Removes existing identifier types.
     void clearIdentifierTypes();
     void clearIdentifierTypes();
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Holds ordered collection of identifiers to be used by the
     /// @brief Holds ordered collection of identifiers to be used by the

+ 146 - 1
src/lib/dhcpsrv/cfg_hosts.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6,11 +6,17 @@
 
 
 #include <config.h>
 #include <config.h>
 #include <dhcpsrv/cfg_hosts.h>
 #include <dhcpsrv/cfg_hosts.h>
+#include <dhcpsrv/cfg_hosts_util.h>
 #include <dhcpsrv/hosts_log.h>
 #include <dhcpsrv/hosts_log.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
+#include <util/encode/hex.h>
 #include <ostream>
 #include <ostream>
+#include <string>
+#include <vector>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -667,5 +673,144 @@ CfgHosts::add6(const HostPtr& host) {
     }
     }
 }
 }
 
 
+ElementPtr
+CfgHosts::toElement() const {
+    uint16_t family = CfgMgr::instance().getFamily();
+    if (family == AF_INET) {
+        return (toElement4());
+    } else if (family == AF_INET6) {
+        return (toElement6());
+    } else {
+        isc_throw(ToElementError, "CfgHosts::toElement: unknown "
+                  "address family: " << family);
+    }
+}
+
+ElementPtr
+CfgHosts::toElement4() const {
+    CfgHostsList result;
+    // Iterate using arbitrary the index 0
+    const HostContainerIndex0& idx = hosts_.get<0>();
+    for (HostContainerIndex0::const_iterator host = idx.begin();
+         host != idx.end(); ++host) {
+        // Get the subnet ID
+        SubnetID subnet_id = (*host)->getIPv4SubnetID();
+        // Prepare the map
+        ElementPtr map = Element::createMap();
+        // Set the identifier
+        Host::IdentifierType id_type = (*host)->getIdentifierType();
+        if (id_type == Host::IDENT_HWADDR) {
+            HWAddrPtr hwaddr = (*host)->getHWAddress();
+            map->set("hw-address", Element::create(hwaddr->toText(false)));
+        } else if (id_type == Host::IDENT_DUID) {
+            DuidPtr duid = (*host)->getDuid();
+            map->set("duid", Element::create(duid->toText()));
+        } else if (id_type == Host::IDENT_CIRCUIT_ID) {
+            const std::vector<uint8_t>& bin = (*host)->getIdentifier();
+            std::string circuit_id = util::encode::encodeHex(bin);
+            map->set("circuit-id", Element::create(circuit_id));
+        } else if (id_type == Host::IDENT_CLIENT_ID) {
+            const std::vector<uint8_t>& bin = (*host)->getIdentifier();
+            std::string client_id = util::encode::encodeHex(bin);
+            map->set("client-id", Element::create(client_id));
+        } else {
+            isc_throw(ToElementError, "invalid DUID type: " << id_type);
+        }
+        // Set the reservation
+        const IOAddress& address = (*host)->getIPv4Reservation();
+        map->set("ip-address", Element::create(address.toText()));
+        // Set the hostname
+        const std::string& hostname = (*host)->getHostname();
+        map->set("hostname", Element::create(hostname));
+        // Set next-server
+        const IOAddress& next_server = (*host)->getNextServer();
+        map->set("next-server", Element::create(next_server.toText()));
+        // Set server-hostname
+        const std::string& server_hostname = (*host)->getServerHostname();
+        map->set("server-hostname", Element::create(server_hostname));
+        // Set boot-file-name
+        const std::string& boot_file_name = (*host)->getBootFileName();
+        map->set("boot-file-name", Element::create(boot_file_name));
+        // Set client-classes
+        const ClientClasses& cclasses = (*host)->getClientClasses4();
+        ElementPtr classes = Element::createList();
+        for (ClientClasses::const_iterator cclass = cclasses.cbegin();
+             cclass != cclasses.end(); ++cclass) {
+            classes->add(Element::create(*cclass));
+        }
+        map->set("client-classes", classes);
+        // Set option-data
+        ConstCfgOptionPtr opts = (*host)->getCfgOption4();
+        map->set("option-data", opts->toElement());
+        // Push the map on the list
+        result.add(subnet_id, map);
+    }
+    return (result.externalize());
+}
+
+ElementPtr
+CfgHosts::toElement6() const {
+    CfgHostsList result;
+    // Iterate using arbitrary the index 0
+    const HostContainerIndex0& idx = hosts_.get<0>();
+    for (HostContainerIndex0::const_iterator host = idx.begin();
+         host != idx.end(); ++host) {
+        // Get the subnet ID
+        SubnetID subnet_id = (*host)->getIPv6SubnetID();
+        // Prepare the map
+        ElementPtr map = Element::createMap();
+        // Set the identifier
+        Host::IdentifierType id_type = (*host)->getIdentifierType();
+        if (id_type == Host::IDENT_HWADDR) {
+            HWAddrPtr hwaddr = (*host)->getHWAddress();
+            map->set("hw-address", Element::create(hwaddr->toText(false)));
+        } else if (id_type == Host::IDENT_DUID) {
+            DuidPtr duid = (*host)->getDuid();
+            map->set("duid", Element::create(duid->toText()));
+        } else if (id_type == Host::IDENT_CIRCUIT_ID) {
+            isc_throw(ToElementError, "unexpected circuit-id DUID type");
+        } else if (id_type == Host::IDENT_CLIENT_ID) {
+            isc_throw(ToElementError, "unexpected client-id DUID type");
+        } else {
+            isc_throw(ToElementError, "invalid DUID type: " << id_type);
+        }
+        // Set reservations (ip-addresses)
+        IPv6ResrvRange na_resv =
+            (*host)->getIPv6Reservations(IPv6Resrv::TYPE_NA);
+        ElementPtr resvs = Element::createList();
+        for (IPv6ResrvIterator resv = na_resv.first;
+             resv != na_resv.second; ++resv) {
+            resvs->add(Element::create(resv->second.toText()));
+        }
+        map->set("ip-addresses", resvs);
+        // Set reservations (prefixes)
+        IPv6ResrvRange pd_resv =
+                (*host)->getIPv6Reservations(IPv6Resrv::TYPE_PD);
+        resvs = Element::createList();
+        for (IPv6ResrvIterator resv = pd_resv.first;
+             resv != pd_resv.second; ++resv) {
+            resvs->add(Element::create(resv->second.toText()));
+        }
+        map->set("prefixes", resvs);
+        // Set the hostname
+        const std::string& hostname = (*host)->getHostname();
+        map->set("hostname", Element::create(hostname));
+        // Set client-classes
+        const ClientClasses& cclasses = (*host)->getClientClasses6();
+        ElementPtr classes = Element::createList();
+        for (ClientClasses::const_iterator cclass = cclasses.cbegin();
+             cclass != cclasses.end(); ++cclass) {
+            classes->add(Element::create(*cclass));
+        }
+        map->set("client-classes", classes);
+        // Set option-data
+        ConstCfgOptionPtr opts = (*host)->getCfgOption6();
+        map->set("option-data", opts->toElement());
+        // Push the map on the list
+        result.add(subnet_id, map);
+    }
+    return (result.externalize());
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 32 - 2
src/lib/dhcpsrv/cfg_hosts.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,7 @@
 #define CFG_HOSTS_H
 #define CFG_HOSTS_H
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
+#include <cc/cfg_to_element.h>
 #include <dhcp/duid.h>
 #include <dhcp/duid.h>
 #include <dhcp/hwaddr.h>
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/base_host_data_source.h>
 #include <dhcpsrv/base_host_data_source.h>
@@ -35,7 +36,8 @@ namespace dhcp {
 /// when the new configuration is applied for the server. The reservations
 /// when the new configuration is applied for the server. The reservations
 /// are retrieved by the @c HostMgr class when the server is allocating or
 /// are retrieved by the @c HostMgr class when the server is allocating or
 /// renewing an address or prefix for the particular client.
 /// renewing an address or prefix for the particular client.
-class CfgHosts : public BaseHostDataSource, public WritableHostDataSource {
+class CfgHosts : public BaseHostDataSource, public WritableHostDataSource,
+                 public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Destructor.
     /// @brief Destructor.
@@ -329,6 +331,24 @@ public:
         return (std::string("configuration file"));
         return (std::string("configuration file"));
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// host reservation lists are not autonomous so they are
+    /// not returned directly but with the subnet where they are
+    /// declared as:
+    /// @code
+    /// [
+    ///   { "id": 123, "reservations": [ <resv1>, <resv2> ] },
+    ///   { "id": 456, "reservations": [ <resv3 ] },
+    ///   ...
+    /// ]
+    /// @endcode
+    ///
+    /// @ref isc::dhcp::CfgHostsList can be used to handle this
+    ///
+    /// @return a pointer to unparsed configuration
+    isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Returns @c Host objects for the specific identifier and type.
     /// @brief Returns @c Host objects for the specific identifier and type.
@@ -491,6 +511,16 @@ private:
     /// - IPv6 address
     /// - IPv6 address
     /// - IPv6 prefix
     /// - IPv6 prefix
     HostContainer6 hosts6_;
     HostContainer6 hosts6_;
+
+    /// @brief Unparse a configuration object (DHCPv4 reservations)
+    ///
+    /// @return a pointer to unparsed configuration
+    isc::data::ElementPtr toElement4() const;
+
+    /// @brief Unparse a configuration object (DHCPv6 reservations)
+    ///
+    /// @return a pointer to unparsed configuration
+    isc::data::ElementPtr toElement6() const;
 };
 };
 
 
 /// @name Pointers to the @c CfgHosts objects.
 /// @name Pointers to the @c CfgHosts objects.

+ 94 - 0
src/lib/dhcpsrv/cfg_hosts_util.cc

@@ -0,0 +1,94 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <cc/data.h>
+#include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/cfg_hosts_util.h>
+#include <exceptions/exceptions.h>
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+void CfgHostsList::internalize(ConstElementPtr list) {
+    if (!list) {
+        isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                  "argument is NULL");
+    }
+    if (list->getType() != Element::list) {
+        isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                  "argument is not a list Element");
+    }
+    for (size_t i = 0; i < list->size(); ++i) {
+        ConstElementPtr item = list->get(i);
+        if (!item) {
+            isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                      "null pointer from the list at " << i);
+        }
+        if (item->getType() != Element::map) {
+            isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                      "not a map from the list at " << i);
+        }
+        if (item->size() != 2) {
+            isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                      "bad map size from the list at " << i);
+        }
+        ConstElementPtr id = item->get("id");
+        if (!id) {
+            isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                      "no id from a map at " << i);
+        }
+        if (id->getType() != Element::integer) {
+            isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                      "not integer id from a map at " <<i);
+        }
+        SubnetID subnet_id = static_cast<SubnetID>(id->intValue());
+        ConstElementPtr resvs = item->get("reservations");
+        if (!resvs) {
+            isc_throw(BadValue, "internal error: CfgHostsList::internalize: "
+                      "no reservations for subnet ID " << subnet_id);
+        }
+        map_.insert(std::make_pair(subnet_id, 
+                                   boost::const_pointer_cast<Element>(resvs)));
+    }
+}
+
+ElementPtr CfgHostsList::externalize() const {
+    ElementPtr result = Element::createList();
+    for (CfgHostsMap::const_iterator item = map_.begin();
+         item != map_.end(); ++item) {
+        ElementPtr pair = Element::createMap();
+        pair->set("id", Element::create(static_cast<int64_t>(item->first)));
+        pair->set("reservations", item->second);
+        result->add(pair);
+    }
+    return (result);
+}
+
+void CfgHostsList::add(SubnetID id, isc::data::ElementPtr resv) {
+    CfgHostsMap::iterator item = map_.find(id);
+    if (item != map_.end()) {
+        item->second->add(resv);
+    } else {
+        ElementPtr resvs = Element::createList();
+        resvs->add(resv);
+        map_.insert(std::make_pair(id, resvs));
+    }
+}
+
+ConstElementPtr CfgHostsList::get(SubnetID id) const {
+    CfgHostsMap::const_iterator item = map_.find(id);
+    if (item != map_.end()) {
+        return (item->second);
+    } else {
+        return (Element::createList());
+    }
+}
+
+}
+}

+ 53 - 0
src/lib/dhcpsrv/cfg_hosts_util.h

@@ -0,0 +1,53 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef CFG_HOSTS_UTIL_H
+#define CFG_HOSTS_UTIL_H
+
+#include <cc/data.h>
+#include <dhcpsrv/subnet_id.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Utility class to represent host reservation configurations
+/// internally as a map keyed by subnet IDs, externally as a list Element.
+class CfgHostsList {
+public:
+
+    /// The type of the internal map
+    typedef std::map<SubnetID, isc::data::ElementPtr> CfgHostsMap;
+
+    /// @brief Internalize a list Element
+    ///
+    /// This method gets a list Element and builds the internal map from it.
+    ///
+    /// @param list the list Element
+    void internalize(isc::data::ConstElementPtr list);
+
+    /// @brief Externalize the map to a list Element
+    ///
+    /// @return a list Element representing all host reservations
+    isc::data::ElementPtr externalize() const;
+
+    /// @brief Add a host reservation to the map
+    void add(SubnetID id, isc::data::ElementPtr resv);
+
+    /// @brief Return the host reservations for a subnet ID
+    ///
+    /// @param id the subnet ID
+    /// @return a list Element with host reservations
+    isc::data::ConstElementPtr get(SubnetID id) const;
+
+private:
+    /// @brief The internal map
+    CfgHostsMap map_;
+};
+
+}
+}
+
+#endif // CFG_HOSTS_UTIL_H

+ 31 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -14,6 +14,7 @@
 #include <algorithm>
 #include <algorithm>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -398,5 +399,34 @@ CfgIface::useSocketType(const uint16_t family,
     useSocketType(family, textToSocketType(socket_type_name));
     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::dhcp namespace
 } // end of isc namespace
 } // end of isc namespace

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,6 +9,7 @@
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
+#include <cc/cfg_to_element.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <map>
 #include <map>
 #include <set>
 #include <set>
@@ -125,7 +126,7 @@ public:
 /// to which it is bound. It is allowed to select multiple addresses on the
 /// to which it is bound. It is allowed to select multiple addresses on the
 /// particular interface explicitly, e.g. "eth0/192.168.8.1",
 /// particular interface explicitly, e.g. "eth0/192.168.8.1",
 /// "eth0/192.168.8.2".
 /// "eth0/192.168.8.2".
-class CfgIface {
+class CfgIface : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Socket type used by the DHCPv4 server.
     /// @brief Socket type used by the DHCPv4 server.
@@ -251,6 +252,11 @@ public:
         return (!equals(other));
         return (!equals(other));
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Checks if multiple IPv4 addresses has been activated on any
     /// @brief Checks if multiple IPv4 addresses has been activated on any

+ 48 - 21
src/lib/dhcpsrv/cfg_mac_source.cc

@@ -9,6 +9,32 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <dhcp/hwaddr.h>
 #include <dhcp/hwaddr.h>
 
 
+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 isc {
 namespace dhcp {
 namespace dhcp {
 
 
@@ -19,26 +45,7 @@ CfgMACSource::CfgMACSource() {
 }
 }
 
 
 uint32_t CfgMACSource::MACSourceFromText(const std::string& name) {
 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) {
         if (name.compare(sources[i].name) == 0) {
             return (sources[i].type);
             return (sources[i].type);
         }
         }
@@ -51,12 +58,32 @@ void CfgMACSource::add(uint32_t source) {
     for (CfgMACSources::const_iterator it = mac_sources_.begin();
     for (CfgMACSources::const_iterator it = mac_sources_.begin();
          it != mac_sources_.end(); ++it) {
          it != mac_sources_.end(); ++it) {
         if (*it == source) {
         if (*it == source) {
-            isc_throw(InvalidParameter, "mac-source paramter " << source
+            isc_throw(InvalidParameter, "mac-source parameter " << source
                       << "' specified twice.");
                       << "' specified twice.");
         }
         }
     }
     }
     mac_sources_.push_back(source);
     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));
+    }
+    // @todo check if the list is empty (including a new unit test)
+    return (result);
+}
+
 };
 };
 };
 };

+ 7 - 1
src/lib/dhcpsrv/cfg_mac_source.h

@@ -7,6 +7,7 @@
 #ifndef CFG_MAC_SOURCE_H
 #ifndef CFG_MAC_SOURCE_H
 #define CFG_MAC_SOURCE_H
 #define CFG_MAC_SOURCE_H
 
 
+#include <cc/cfg_to_element.h>
 #include <stdint.h>
 #include <stdint.h>
 #include <vector>
 #include <vector>
 #include <string>
 #include <string>
@@ -21,7 +22,7 @@ typedef std::vector<uint32_t> CfgMACSources;
 ///
 ///
 /// It's a simple wrapper around a vector of uint32_t, with each entry
 /// It's a simple wrapper around a vector of uint32_t, with each entry
 /// holding one MAC source.
 /// holding one MAC source.
-class CfgMACSource {
+class CfgMACSource : public isc::data::CfgToElement {
 
 
  public:
  public:
     /// @brief Default constructor.
     /// @brief Default constructor.
@@ -68,6 +69,11 @@ class CfgMACSource {
         mac_sources_.clear();
         mac_sources_.clear();
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
  protected:
  protected:
     /// @brief Actual MAC sources storage
     /// @brief Actual MAC sources storage
     CfgMACSources mac_sources_;
     CfgMACSources mac_sources_;

+ 90 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,7 +8,12 @@
 #include <dhcp/option_space.h>
 #include <dhcp/option_space.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
+#include <util/encode/hex.h>
 #include <string>
 #include <string>
+#include <sstream>
+#include <vector>
+
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -183,5 +188,89 @@ CfgOption::getAll(const uint32_t vendor_id) const {
     return (vendor_options_.getItems(vendor_id));
     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<std::string>& names = options_.getOptionSpaceNames();
+    for (std::list<std::string>::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<uint8_t> 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<uint32_t>& ids = vendor_options_.getOptionSpaceNames();
+    for (std::list<uint32_t>::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<uint8_t> 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::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,6 +9,7 @@
 
 
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/option_space_container.h>
 #include <dhcp/option_space_container.h>
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/key_from_key.h>
 #include <dhcpsrv/key_from_key.h>
 #include <boost/multi_index_container.hpp>
 #include <boost/multi_index_container.hpp>
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/hashed_index.hpp>
@@ -210,7 +211,7 @@ typedef OptionContainer::nth_index<2>::type OptionContainerPersistIndex;
 /// options is useful when the client requests stateless configuration from
 /// options is useful when the client requests stateless configuration from
 /// the DHCP server and no subnet is selected for this client. This client
 /// the DHCP server and no subnet is selected for this client. This client
 /// will only receive global options.
 /// will only receive global options.
-class CfgOption {
+class CfgOption : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief default constructor
     /// @brief default constructor
@@ -394,6 +395,11 @@ public:
     /// @return List comprising option space names for vendor options.
     /// @return List comprising option space names for vendor options.
     std::list<std::string> getVendorIdsSpaceNames() const;
     std::list<std::string> getVendorIdsSpaceNames() const;
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Appends encapsulated options to the options in an option space.
     /// @brief Appends encapsulated options to the options in an option space.

+ 58 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6,9 +6,13 @@
 
 
 #include <config.h>
 #include <config.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/libdhcp++.h>
+#include <dhcp/option_data_types.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_space.h>
 #include <dhcp/option_space.h>
 #include <dhcpsrv/cfg_option_def.h>
 #include <dhcpsrv/cfg_option_def.h>
+#include <sstream>
+
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -148,5 +152,58 @@ CfgOptionDef::get(const std::string& option_space,
     return (OptionDefinitionPtr());
     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<std::string>& names =
+        option_definitions_.getOptionSpaceNames();
+    for (std::list<std::string>::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::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,6 +9,7 @@
 
 
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_space_container.h>
 #include <dhcp/option_space_container.h>
+#include <cc/cfg_to_element.h>
 #include <string>
 #include <string>
 
 
 namespace isc {
 namespace isc {
@@ -26,7 +27,7 @@ namespace dhcp {
 /// following names: "dhcp4" and "dhcp6" are reserved, though. They are
 /// following names: "dhcp4" and "dhcp6" are reserved, though. They are
 /// names of option spaces used for standard top-level DHCPv4 and DHCPv6
 /// names of option spaces used for standard top-level DHCPv4 and DHCPv6
 /// options respectively.
 /// options respectively.
-class CfgOptionDef {
+class CfgOptionDef : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Copies this configuration to a new configuration.
     /// @brief Copies this configuration to a new configuration.
@@ -117,6 +118,11 @@ public:
         return (option_definitions_);
         return (option_definitions_);
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief A collection of option definitions.
     /// @brief A collection of option definitions.

+ 15 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -6,6 +6,9 @@
 
 
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcpsrv/cfg_rsoo.h>
 #include <dhcpsrv/cfg_rsoo.h>
+#include <boost/lexical_cast.hpp>
+
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 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<uint16_t>::const_iterator opt = rsoo_options_.cbegin();
+         opt != rsoo_options_.cend(); ++opt) {
+        const std::string& code = boost::lexical_cast<std::string>(*opt);
+        result->add(Element::create(code));
+    }
+    return (result);
+}
 
 
 }
 }
 }
 }

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,6 +7,7 @@
 #ifndef CFG_RSOO_H
 #ifndef CFG_RSOO_H
 #define CFG_RSOO_H
 #define CFG_RSOO_H
 
 
+#include <cc/cfg_to_element.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <stdint.h>
 #include <stdint.h>
 #include <set>
 #include <set>
@@ -21,7 +22,7 @@ namespace dhcp {
 /// 65 is officially RSSO-enabled. The list may be extended in the future
 /// 65 is officially RSSO-enabled. The list may be extended in the future
 /// and this class allows for specifying any future RSOO-enabled options.
 /// and this class allows for specifying any future RSOO-enabled options.
 /// The administrator may also use existing options as RSOO-enabled.
 /// The administrator may also use existing options as RSOO-enabled.
-class CfgRSOO {
+class CfgRSOO : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Constructor.
     /// @brief Constructor.
@@ -46,6 +47,11 @@ public:
     /// @param code option to be enabled in RSOO
     /// @param code option to be enabled in RSOO
     void enable(const uint16_t code);
     void enable(const uint16_t code);
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Contains a set of options that are allowed in RSOO option
     /// @brief Contains a set of options that are allowed in RSOO option

+ 111 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -13,8 +13,10 @@
 #include <dhcpsrv/addr_utilities.h>
 #include <dhcpsrv/addr_utilities.h>
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <stats/stats_mgr.h>
 #include <stats/stats_mgr.h>
+#include <sstream>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -273,5 +275,113 @@ 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<long long>(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<long long>
+                                 ((*subnet)->getT1().get())));
+        // Set rebind-timer
+        map->set("rebind-timer",
+                 Element::create(static_cast<long long>
+                                 ((*subnet)->getT2().get())));
+        // Set valid-lifetime
+        map->set("valid-lifetime",
+                 Element::create(static_cast<long long>
+                                 ((*subnet)->getValid().get())));
+        // Set pools
+        const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_V4);
+        ElementPtr pool_list = Element::createList();
+        for (PoolCollection::const_iterator pool = pools.cbegin();
+             pool != pools.cend(); ++pool) {
+            // Prepare the map for a pool (@todo move this code to pool.cc)
+            ElementPtr pool_map = Element::createMap();
+            // Set pool
+            const IOAddress& first = (*pool)->getFirstAddress();
+            const IOAddress& last = (*pool)->getLastAddress();
+            std::string range = first.toText() + "-" + last.toText();
+            // Try to output a prefix (vs a range)
+            int prefix_len = prefixLengthFromRange(first, last);
+            if (prefix_len >= 0) {
+                std::ostringstream oss;
+                oss << first.toText() << "/" << prefix_len;
+                range = oss.str();
+            }
+            pool_map->set("pool", Element::create(range));
+            // Set user-context
+            ConstElementPtr context = (*pool)->getContext();
+            if (!isNull(context)) {
+                pool_map->set("user-context", context);
+            }
+            // Set pool options (not yet supported)
+            // Push on the pool list
+            pool_list->add(pool_map);
+        }
+        map->set("pools", pool_list);
+        // Set host reservation-mode
+        Subnet::HRMode hrmode = (*subnet)->getHostReservationMode();
+        std::string mode;
+        switch (hrmode) {
+        case Subnet::HR_DISABLED:
+            mode = "disabled";
+            break;
+        case Subnet::HR_OUT_OF_POOL:
+            mode = "out-of-pool";
+            break;
+        case Subnet::HR_ALL:
+            mode = "all";
+            break;
+        default:
+            isc_throw(ToElementError,
+                      "invalid host reservation mode: " << hrmode);
+        }
+        map->set("reservation-mode", Element::create(mode));
+        // Set match-client-id
+        map->set("match-client-id",
+                 Element::create((*subnet)->getMatchClientId()));
+        // Set next-server
+        map->set("next-server",
+                 Element::create((*subnet)->getSiaddr().toText()));
+        // Set DHCP4o6
+        const Cfg4o6& d4o6 = (*subnet)->get4o6();
+        isc::data::merge(map, d4o6.toElement());
+        // Set client-class
+        const ClientClasses& cclasses = (*subnet)->getClientClasses();
+        if (cclasses.size() > 1) {
+            isc_throw(ToElementError, "client-class has too many items: "
+                      << cclasses.size());
+        } else if (!cclasses.empty()) {
+            map->set("client-class", Element::create(*cclasses.cbegin()));
+        }
+        // Set options
+        ConstCfgOptionPtr opts = (*subnet)->getCfgOption();
+        map->set("option-data", opts->toElement());
+        // Not supported: interface-id
+        // Not supported: rapid-commit
+        // Push on the list
+        result->add(map);
+    }
+    return (result);
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,7 @@
 #define CFG_SUBNETS4_H
 #define CFG_SUBNETS4_H
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/subnet_selector.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
@@ -25,7 +26,7 @@ namespace dhcp {
 ///
 ///
 /// See @c CfgSubnets4::selectSubnet documentation for more details on how the
 /// See @c CfgSubnets4::selectSubnet documentation for more details on how the
 /// subnet is selected for the client.
 /// subnet is selected for the client.
-class CfgSubnets4 {
+class CfgSubnets4 : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Adds new subnet to the configuration.
     /// @brief Adds new subnet to the configuration.
@@ -183,6 +184,11 @@ public:
     /// configuration and also subnet-ids may change.
     /// configuration and also subnet-ids may change.
     void removeStatistics();
     void removeStatistics();
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Checks that the IPv4 subnet with the given id already exists.
     /// @brief Checks that the IPv4 subnet with the given id already exists.

+ 180 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,9 +9,13 @@
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/addr_utilities.h>
 #include <stats/stats_mgr.h>
 #include <stats/stats_mgr.h>
+#include <string.h>
+#include <sstream>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 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<long long>(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<uint8_t> 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<long long>
+                                 ((*subnet)->getT1().get())));
+        // Set rebind-timer
+        map->set("rebind-timer",
+                 Element::create(static_cast<long long>
+                                 ((*subnet)->getT2().get())));
+        // Set preferred-lifetime
+        map->set("preferred-lifetime",
+                 Element::create(static_cast<long long>
+                                 ((*subnet)->getPreferred().get())));
+        // Set valid-lifetime
+        map->set("valid-lifetime",
+                 Element::create(static_cast<long long>
+                                 ((*subnet)->getValid().get())));
+        // Set rapid-commit
+        bool rapid_commit = (*subnet)->getRapidCommit();
+        map->set("rapid-commit", Element::create(rapid_commit));
+        // Set pools
+        const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_NA);
+        ElementPtr pool_list = Element::createList();
+        for (PoolCollection::const_iterator pool = pools.cbegin();
+             pool != pools.cend(); ++pool) {
+            // Prepare the map for a pool (@todo move this code to pool.cc)
+            ElementPtr pool_map = Element::createMap();
+            // Set pool
+            const IOAddress& first = (*pool)->getFirstAddress();
+            const IOAddress& last = (*pool)->getLastAddress();
+            std::string range = first.toText() + "-" + last.toText();
+            // Try to output a prefix (vs a range)
+            int prefix_len = prefixLengthFromRange(first, last);
+            if (prefix_len >= 0) {
+                std::ostringstream oss;
+                oss << first.toText() << "/" << prefix_len;
+                range = oss.str();
+            }
+            pool_map->set("pool", Element::create(range));
+            // Set user-context
+            ConstElementPtr context = (*pool)->getContext();
+            if (!isNull(context)) {
+                pool_map->set("user-context", context);
+            }
+            // Set pool options
+            ConstCfgOptionPtr opts = (*pool)->getCfgOption();
+            pool_map->set("option-data", opts->toElement());
+            // Push on the pool list
+            pool_list->add(pool_map);
+        }
+        map->set("pools", pool_list);
+        // Set pd-pools
+        const PoolCollection& pdpools = (*subnet)->getPools(Lease::TYPE_PD);
+        ElementPtr pdpool_list = Element::createList();
+        for (PoolCollection::const_iterator pool = pdpools.cbegin();
+             pool != pdpools.cend(); ++pool) {
+            // Get it as a Pool6 (@todo move this code to pool.cc)
+            const Pool6* pdpool = dynamic_cast<Pool6*>(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<int>(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<int>(xlen)));
+            } else {
+                pool_map->set("excluded-prefix",
+                              Element::create(std::string("::")));
+                pool_map->set("excluded-prefix-len", Element::create(0));
+            }
+            // Set user-context
+            ConstElementPtr context = pdpool->getContext();
+            if (!isNull(context)) {
+                pool_map->set("user-context", context);
+            }
+            // Set pool options
+            ConstCfgOptionPtr opts = pdpool->getCfgOption();
+            pool_map->set("option-data", opts->toElement());
+            // Push on the pool list
+            pdpool_list->add(pool_map);
+        }
+        map->set("pd-pools", pdpool_list);
+        // Set host reservation-mode
+        Subnet::HRMode hrmode = (*subnet)->getHostReservationMode();
+        std::string mode;
+        switch (hrmode) {
+        case Subnet::HR_DISABLED:
+            mode = "disabled";
+            break;
+        case Subnet::HR_OUT_OF_POOL:
+            mode = "out-of-pool";
+            break;
+        case Subnet::HR_ALL:
+            mode = "all";
+            break;
+        default:
+            isc_throw(ToElementError,
+                      "invalid host reservation mode: " << hrmode);
+        }
+        map->set("reservation-mode", Element::create(mode));
+        // Set client-class
+        const ClientClasses& cclasses = (*subnet)->getClientClasses();
+        if (cclasses.size() > 1) {
+            isc_throw(ToElementError, "client-class has too many items: "
+                      << cclasses.size());
+        } else if (!cclasses.empty()) {
+            map->set("client-class", Element::create(*cclasses.cbegin()));
+        }
+        // Set options
+        ConstCfgOptionPtr opts = (*subnet)->getCfgOption();
+        map->set("option-data", opts->toElement());
+        // Push on the list
+        result->add(map);
+    }
+    return (result);
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 8 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,6 +9,7 @@
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/subnet_selector.h>
 #include <util/optional_value.h>
 #include <util/optional_value.h>
@@ -26,7 +27,7 @@ namespace dhcp {
 ///
 ///
 /// See @c CfgSubnets6::selectSubnet documentation for more details on how the subnet
 /// See @c CfgSubnets6::selectSubnet documentation for more details on how the subnet
 /// is selected for the client.
 /// is selected for the client.
-class CfgSubnets6 {
+class CfgSubnets6 : public isc::data::CfgToElement {
 public:
 public:
 
 
     /// @brief Adds new subnet to the configuration.
     /// @brief Adds new subnet to the configuration.
@@ -141,6 +142,11 @@ public:
     /// configuration and also subnet-ids may change.
     /// configuration and also subnet-ids may change.
     void removeStatistics();
     void removeStatistics();
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Selects a subnet using the interface name.
     /// @brief Selects a subnet using the interface name.

+ 2 - 1
src/lib/dhcpsrv/cfgmgr.cc

@@ -167,7 +167,8 @@ CfgMgr::getStagingCfg() {
 }
 }
 
 
 CfgMgr::CfgMgr()
 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
     // 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
     // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
     // See AM_CPPFLAGS definition in Makefile.am
     // See AM_CPPFLAGS definition in Makefile.am

+ 13 - 0
src/lib/dhcpsrv/cfgmgr.h

@@ -248,6 +248,16 @@ public:
         return (default_logger_name_);
         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:
 protected:
@@ -299,6 +309,9 @@ private:
 
 
     /// @brief Default logger name.
     /// @brief Default logger name.
     std::string default_logger_name_;
     std::string default_logger_name_;
+
+    /// @brief Address family.
+    uint16_t family_;
 };
 };
 
 
 } // namespace isc::dhcp
 } // namespace isc::dhcp

+ 52 - 2
src/lib/dhcpsrv/client_class_def.cc

@@ -1,12 +1,15 @@
-// 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
 // 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
 // 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/.
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-#include "client_class_def.h"
+#include <dhcpsrv/client_class_def.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
 
 
+using namespace isc::data;
+
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
@@ -74,6 +77,16 @@ ClientClassDef::setMatchExpr(const ExpressionPtr& match_expr) {
     match_expr_ = match_expr;
     match_expr_ = match_expr;
 }
 }
 
 
+std::string
+ClientClassDef::getTest() const {
+    return (test_);
+}
+
+void
+ClientClassDef::setTest(const std::string& test) {
+    test_ = test;
+}
+
 const CfgOptionPtr&
 const CfgOptionPtr&
 ClientClassDef::getCfgOption() const {
 ClientClassDef::getCfgOption() const {
     return (cfg_option_);
     return (cfg_option_);
@@ -98,6 +111,31 @@ ClientClassDef::equals(const ClientClassDef& other) const {
             (filename_ == other.filename_));
             (filename_ == other.filename_));
 }
 }
 
 
+ElementPtr
+ClientClassDef:: toElement() const {
+    uint16_t family = CfgMgr::instance().getFamily();
+    ElementPtr result = Element::createMap();
+    // Set name
+    result->set("name", Element::create(name_));
+    // Set original match expression (empty string won't parse)
+    if (!test_.empty()) {
+        result->set("test", Element::create(test_));
+    }
+    // Set option-data
+    result->set("option-data", cfg_option_->toElement());
+    if (family != AF_INET) {
+        // Other parameters are DHCPv4 specific
+        return (result);
+    }
+    // Set next-server
+    result->set("next-server", Element::create(next_server_.toText()));
+    // Set server-hostname
+    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) {
 std::ostream& operator<<(std::ostream& os, const ClientClassDef& x) {
     os << "ClientClassDef:" << x.getName();
     os << "ClientClassDef:" << x.getName();
     return (os);
     return (os);
@@ -123,11 +161,13 @@ ClientClassDictionary::~ClientClassDictionary() {
 void
 void
 ClientClassDictionary::addClass(const std::string& name,
 ClientClassDictionary::addClass(const std::string& name,
                                 const ExpressionPtr& match_expr,
                                 const ExpressionPtr& match_expr,
+                                const std::string& test,
                                 const CfgOptionPtr& cfg_option,
                                 const CfgOptionPtr& cfg_option,
                                 asiolink::IOAddress next_server,
                                 asiolink::IOAddress next_server,
                                 const std::string& sname,
                                 const std::string& sname,
                                 const std::string& filename) {
                                 const std::string& filename) {
     ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
     ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
+    cclass->setTest(test);
     cclass->setNextServer(next_server);
     cclass->setNextServer(next_server);
     cclass->setSname(sname);
     cclass->setSname(sname);
     cclass->setFilename(filename);
     cclass->setFilename(filename);
@@ -191,6 +231,16 @@ ClientClassDictionary::equals(const ClientClassDictionary& other) const {
     return (true);
     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::dhcp
 } // namespace isc
 } // namespace isc

+ 28 - 4
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,6 +7,7 @@
 #ifndef CLIENT_CLASS_DEF_H
 #ifndef CLIENT_CLASS_DEF_H
 #define CLIENT_CLASS_DEF_H
 #define CLIENT_CLASS_DEF_H
 
 
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
 #include <eval/token.h>
 #include <eval/token.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
@@ -37,7 +38,7 @@ public:
 };
 };
 
 
 /// @brief Embodies a single client class definition
 /// @brief Embodies a single client class definition
-class ClientClassDef {
+class ClientClassDef : public isc::data::CfgToElement {
 public:
 public:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
@@ -70,6 +71,14 @@ public:
     /// @param match_expr the expression to assign the class
     /// @param match_expr the expression to assign the class
     void setMatchExpr(const ExpressionPtr& match_expr);
     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
     /// @brief Fetches the class's option collection
     const CfgOptionPtr& getCfgOption() const;
     const CfgOptionPtr& getCfgOption() const;
 
 
@@ -145,6 +154,11 @@ public:
         return (filename_);
         return (filename_);
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
     /// @brief Unique text identifier by which this class is known.
     /// @brief Unique text identifier by which this class is known.
     std::string name_;
     std::string name_;
@@ -153,6 +167,10 @@ private:
     /// this class.
     /// this class.
     ExpressionPtr match_expr_;
     ExpressionPtr match_expr_;
 
 
+    /// @brief The original expression which determines membership in
+    /// this class.
+    std::string test_;
+
     /// @brief The option data configuration for this class
     /// @brief The option data configuration for this class
     CfgOptionPtr cfg_option_;
     CfgOptionPtr cfg_option_;
 
 
@@ -188,7 +206,7 @@ typedef boost::shared_ptr<ClientClassDefMap> ClientClassDefMapPtr;
 typedef std::pair<std::string, ClientClassDefPtr> ClientClassMapPair;
 typedef std::pair<std::string, ClientClassDefPtr> ClientClassMapPair;
 
 
 /// @brief Maintains a list of ClientClassDef's
 /// @brief Maintains a list of ClientClassDef's
-class ClientClassDictionary {
+class ClientClassDictionary : public isc::data::CfgToElement {
 
 
 public:
 public:
     /// @brief Constructor
     /// @brief Constructor
@@ -203,6 +221,7 @@ public:
     ///
     ///
     /// @param name Name to assign to this class
     /// @param name Name to assign to this class
     /// @param match_expr Expression the class will use to determine membership
     /// @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 options Collection of options members should be given
     /// @param next_server next-server value for this class (optional)
     /// @param next_server next-server value for this class (optional)
     /// @param sname server-name 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
     /// dictionary.  See @ref dhcp::ClientClassDef::ClientClassDef() for
     /// others.
     /// others.
     void addClass(const std::string& name, const ExpressionPtr& match_expr,
     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"),
                   asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"),
                   const std::string& sname = std::string(),
                   const std::string& sname = std::string(),
                   const std::string& filename = std::string());
                   const std::string& filename = std::string());
@@ -271,6 +290,11 @@ public:
         return (!equals(other));
         return (!equals(other));
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Map of the class definitions
     /// @brief Map of the class definitions

+ 38 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -15,6 +15,8 @@
 #include <string>
 #include <string>
 
 
 using namespace std;
 using namespace std;
+using namespace isc::asiolink;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -217,6 +219,41 @@ D2ClientConfig::toText() const {
     return (stream.str());
     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<long long>(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<long long>(sender_port_)));
+    // Set max-queue-size
+    result->set("max-queue-size", Element::create(static_cast<long long>(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&
 std::ostream&
 operator<<(std::ostream& os, const D2ClientConfig& config) {
 operator<<(std::ostream& os, const D2ClientConfig& config) {
     os << config.toText();
     os << config.toText();

+ 9 - 2
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
 // 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
 // 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
 /// This file defines the classes Kea uses to manage configuration needed to
 /// act as a client of the kea-dhcp-ddns module (aka D2).
 /// act as a client of the kea-dhcp-ddns module (aka D2).
 ///
 ///
+
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
+#include <cc/cfg_to_element.h>
 #include <dhcp_ddns/ncr_io.h>
 #include <dhcp_ddns/ncr_io.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
@@ -46,7 +48,7 @@ public:
 /// parameters associated with DHCP-DDNS and acting as a client of D2.
 /// parameters associated with DHCP-DDNS and acting as a client of D2.
 /// Instances of this class may be constructed through configuration parsing.
 /// Instances of this class may be constructed through configuration parsing.
 ///
 ///
-class D2ClientConfig {
+class D2ClientConfig : public isc::data::CfgToElement {
 public:
 public:
     /// @brief Default configuration constants.
     /// @brief Default configuration constants.
     /// @todo For now these are hard-coded as configuration layer cannot
     /// @todo For now these are hard-coded as configuration layer cannot
@@ -230,6 +232,11 @@ public:
     /// "unknown" if not.
     /// "unknown" if not.
     static std::string replaceClientNameModeToString(const ReplaceClientNameMode& mode);
     static std::string replaceClientNameModeToString(const ReplaceClientNameMode& mode);
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 protected:
 protected:
     /// @brief Validates member values.
     /// @brief Validates member values.
     ///
     ///

+ 62 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -10,6 +10,7 @@
 #include <log/logger_name.h>
 #include <log/logger_name.h>
 
 
 using namespace isc::log;
 using namespace isc::log;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -22,6 +23,22 @@ LoggingDestination::equals(const LoggingDestination& other) const {
             flush_ == other.flush_);
             flush_ == other.flush_);
 }
 }
 
 
+ElementPtr
+LoggingDestination::toElement() const {
+    ElementPtr result = Element::createMap();
+
+    // Set output
+    result->set("output", Element::create(output_));
+    // Set maxver
+    result->set("maxver", Element::create(maxver_));
+    // Set maxsize
+    result->set("maxsize", Element::create(static_cast<long long>(maxsize_)));
+    // Set flush
+    result->set("flush", Element::create(flush_));
+
+    return(result);
+}
+
 LoggingInfo::LoggingInfo()
 LoggingInfo::LoggingInfo()
     : name_("kea"), severity_(isc::log::INFO), debuglevel_(0) {
     : name_("kea"), severity_(isc::log::INFO), debuglevel_(0) {
     // If configuration Manager is in the verbose mode, we need to modify the
     // If configuration Manager is in the verbose mode, we need to modify the
@@ -137,5 +154,49 @@ LoggingInfo::toSpec() const {
     return (spec);
     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<LoggingDestination>::const_iterator dest =
+             destinations_.cbegin();
+         dest != destinations_.cend(); ++dest) {
+        options->add(dest->toElement());
+    }
+    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::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 14 - 3
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,6 +9,7 @@
 
 
 #include <log/logger_level.h>
 #include <log/logger_level.h>
 #include <log/logger_specification.h>
 #include <log/logger_specification.h>
+#include <cc/cfg_to_element.h>
 #include <stdint.h>
 #include <stdint.h>
 #include <vector>
 #include <vector>
 
 
@@ -18,7 +19,7 @@ namespace dhcp {
 /// @brief Defines single logging destination
 /// @brief Defines single logging destination
 ///
 ///
 /// This structure is used to keep log4cplus configuration parameters.
 /// This structure is used to keep log4cplus configuration parameters.
-struct LoggingDestination {
+struct LoggingDestination : public isc::data::CfgToElement {
 
 
     /// @brief defines logging destination output
     /// @brief defines logging destination output
     ///
     ///
@@ -46,6 +47,11 @@ struct LoggingDestination {
     LoggingDestination()
     LoggingDestination()
         : output_("stdout"), maxver_(1), maxsize_(204800), flush_(true) {
         : output_("stdout"), maxver_(1), maxsize_(204800), flush_(true) {
     }
     }
+
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
 };
 };
 
 
 /// @brief structure that describes one logging entry
 /// @brief structure that describes one logging entry
@@ -65,7 +71,7 @@ struct LoggingDestination {
 ///            "severity": "WARN",
 ///            "severity": "WARN",
 ///            "debuglevel": 99
 ///            "debuglevel": 99
 ///        },
 ///        },
-struct LoggingInfo {
+struct LoggingInfo : public isc::data::CfgToElement {
 
 
     /// @brief logging name
     /// @brief logging name
     std::string name_;
     std::string name_;
@@ -116,6 +122,11 @@ struct LoggingInfo {
 
 
     /// @brief Converts logger configuration to a spec.
     /// @brief Converts logger configuration to a spec.
     isc::log::LoggerSpecification toSpec() const;
     isc::log::LoggerSpecification toSpec() const;
+
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
 };
 };
 
 
 /// @brief storage for logging information in log4cplus format
 /// @brief storage for logging information in log4cplus format

+ 59 - 56
src/lib/dhcpsrv/parsers/client_class_def_parser.cc

@@ -62,88 +62,91 @@ void
 ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
 ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
                             ConstElementPtr class_def_cfg,
                             ConstElementPtr class_def_cfg,
                             uint16_t family) {
                             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 {
         try {
             next_server = IOAddress(next_server_txt);
             next_server = IOAddress(next_server_txt);
         } catch (const IOError& ex) {
         } 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) {
         if (next_server.getFamily() != AF_INET) {
             isc_throw(DhcpConfigError, "Invalid next-server value: '"
             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()) {
         if (next_server.isV4Bcast()) {
             isc_throw(DhcpConfigError, "Invalid next-server value: '"
             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) {
         if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
             isc_throw(DhcpConfigError, "server-hostname must be at most "
             isc_throw(DhcpConfigError, "server-hostname must be at most "
                       << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
                       << 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) {
         if (filename.length() > Pkt4::MAX_FILE_LEN) {
             isc_throw(DhcpConfigError, "boot-file-name must be at most "
             isc_throw(DhcpConfigError, "boot-file-name must be at most "
                       << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
                       << 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) {
     } catch (const std::exception& ex) {
         isc_throw(DhcpConfigError, ex.what()
         isc_throw(DhcpConfigError, ex.what()
                   << " (" << class_def_cfg->getPosition() << ")");
                   << " (" << class_def_cfg->getPosition() << ")");

+ 1 - 1
src/lib/dhcpsrv/parsers/dbaccess_parser.h

@@ -43,7 +43,7 @@ public:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// @param db_type Specifies database type (lease or hosts)
     /// @param db_type Specifies database type (lease or hosts)
-    DbAccessParser(DBType db_type);
+    explicit DbAccessParser(DBType db_type);
 
 
     /// The destructor.
     /// The destructor.
     virtual ~DbAccessParser()
     virtual ~DbAccessParser()

+ 33 - 76
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -295,10 +295,7 @@ OptionDataParser::extractSpace(ConstElementPtr parent) const {
         }
         }
 
 
     } catch (std::exception& ex) {
     } 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() << " ("
         isc_throw(DhcpConfigError, ex.what() << " ("
                   << getPosition("space", parent) << ")");
                   << getPosition("space", parent) << ")");
     }
     }
@@ -445,12 +442,15 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
         // Option definition has been found so let's use it to create
         // Option definition has been found so let's use it to create
         // an instance of our option.
         // an instance of our option.
         try {
         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(), data_tokens) :
                 def->optionFactory(universe, def->getCode(), binary);
                 def->optionFactory(universe, def->getCode(), binary);
             desc.option_ = option;
             desc.option_ = option;
             desc.persistent_ = false;
             desc.persistent_ = false;
+            if (use_csv) {
+                desc.formatted_value_ = data_param;
+            }
         } catch (const isc::Exception& ex) {
         } catch (const isc::Exception& ex) {
             isc_throw(DhcpConfigError, "option data does not match"
             isc_throw(DhcpConfigError, "option data does not match"
                       << " option definition (space: " << space_param
                       << " option definition (space: " << space_param
@@ -479,7 +479,8 @@ void OptionDataListParser::parse(const CfgOptionPtr& cfg,
     BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
     BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
         std::pair<OptionDescriptor, std::string> option =
         std::pair<OptionDescriptor, std::string> option =
             option_parser.parse(data);
             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();
         cfg->encapsulate();
     }
     }
 }
 }
@@ -600,57 +601,31 @@ RelayInfoParser::RelayInfoParser(const Option::Universe& family)
     : family_(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<IOAddress,
+            buildIOAddress>(scope, name, "address"));
+}
+
 void
 void
 RelayInfoParser::parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
 RelayInfoParser::parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
                        ConstElementPtr relay_info) {
                        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
     // Ok, we're done with parsing. Let's store the result in the structure
@@ -914,17 +889,10 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) {
     try {
     try {
         std::string hr_mode = getString(params, "reservation-mode");
         std::string hr_mode = getString(params, "reservation-mode");
         subnet_->setHostReservationMode(hrModeFromText(hr_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()
                   " of reservation-mode parameter: " << ex.what()
-                  << "(" << pos << ")");
+                  << "(" << getPosition("reservation-mode", params) << ")");
     }
     }
 
 
     // Try setting up client class.
     // Try setting up client class.
@@ -943,17 +911,6 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) {
 
 
 //**************************** D2ClientConfigParser **********************
 //**************************** D2ClientConfigParser **********************
 
 
-uint32_t
-D2ClientConfigParser::getUint32(ConstElementPtr scope,
-                                const std::string& name) {
-    return (getIntType<uint32_t>(scope, name));
-}
-
-// Can't use a constructor as a function
-namespace {
-IOAddress buildIOAddress(const std::string& str) { return (IOAddress(str)); }
-};
-
 IOAddress
 IOAddress
 D2ClientConfigParser::getIOAddress(ConstElementPtr scope,
 D2ClientConfigParser::getIOAddress(ConstElementPtr scope,
                                    const std::string& name) {
                                    const std::string& name) {

+ 16 - 15
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -365,7 +365,7 @@ public:
     /// @brief Constructor.
     /// @brief Constructor.
     ///
     ///
     /// @param address_family Address family: @c AF_INET or @c AF_INET6.
     /// @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
     /// @brief Parses ElementPtr containing option definition
     ///
     ///
@@ -477,7 +477,7 @@ public:
     /// @brief Constructor.
     /// @brief Constructor.
     ///
     ///
     /// @param address_family Address family: @c AF_INET or AF_INET6
     /// @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
     /// @brief Parses a list of options, instantiates them and stores in cfg
     ///
     ///
@@ -620,7 +620,7 @@ public:
 
 
     /// @brief constructor
     /// @brief constructor
     /// @param family specifies protocol family (IPv4 or IPv6)
     /// @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
     /// @brief parses the actual relay parameters
     ///
     ///
@@ -632,7 +632,18 @@ public:
     void parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
     void parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
                isc::data::ConstElementPtr relay_info);
                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)
     /// Protocol family (IPv4 or IPv6)
     Option::Universe family_;
     Option::Universe family_;
 };
 };
@@ -664,7 +675,7 @@ public:
     /// @brief constructor
     /// @brief constructor
     ///
     ///
     /// @param family address family: @c AF_INET or @c AF_INET6
     /// @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)
     /// @brief virtual destructor (does nothing)
     virtual ~SubnetConfigParser() { }
     virtual ~SubnetConfigParser() { }
@@ -776,16 +787,6 @@ public:
 
 
 private:
 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
     /// @brief Returns a value converted to IOAddress
     ///
     ///
     /// Instantiation of getAndConvert() to IOAddress
     /// Instantiation of getAndConvert() to IOAddress

+ 47 - 92
src/lib/dhcpsrv/parsers/duid_config_parser.cc

@@ -24,107 +24,62 @@ namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
 void
 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<DUID::DUIDType>(numeric_type));
-}
+        cfg->setType(static_cast<DUID::DUIDType>(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<uint16_t>("htype", htype);
-    cfg->setHType(static_cast<uint16_t>(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<uint32_t>("time", new_time);
-    cfg->setTime(static_cast<uint32_t>(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<uint32_t>("enterprise-id", enterprise_id);
-    cfg->setEnterpriseId(static_cast<uint32_t>(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<typename NumericType>
-void
-DUIDConfigParser::checkRange(const std::string& parameter_name,
-                             const int64_t parameter_value) const {
-    if ((parameter_value < 0) ||
-        (parameter_value > std::numeric_limits<NumericType>::max())) {
-        isc_throw(DhcpConfigError, "out of range value '" << parameter_value
-                  << "' specified for parameter '" << parameter_name
-                  << "'; expected value in range of [0.."
-                  << std::numeric_limits<NumericType>::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::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 0 - 51
src/lib/dhcpsrv/parsers/duid_config_parser.h

@@ -33,57 +33,6 @@ public:
     ///
     ///
     /// @throw DhcpConfigError If the configuration is invalid.
     /// @throw DhcpConfigError If the configuration is invalid.
     void parse(const CfgDUIDPtr& cfg, isc::data::ConstElementPtr duid_configuration);
     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<typename NumericType>
-    void checkRange(const std::string& parameter_name,
-                    const int64_t parameter_value) const;
 };
 };
 
 
 }
 }

+ 33 - 30
src/lib/dhcpsrv/parsers/expiration_config_parser.cc

@@ -20,43 +20,46 @@ void
 ExpirationConfigParser::parse(ConstElementPtr expiration_config) {
 ExpirationConfigParser::parse(ConstElementPtr expiration_config) {
     CfgExpirationPtr cfg = CfgMgr::instance().getStagingCfg()->getCfgExpiration();
     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) << ")");
     }
     }
 }
 }
 
 

+ 2 - 2
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -29,7 +29,7 @@ public:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// @param protocol AF_INET for DHCPv4 and AF_INET6 for DHCPv6.
     /// @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".
     /// @brief Parses content of the "interfaces-config".
     ///
     ///

+ 2 - 2
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
 // 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
 // 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
     /// This may be useful for "prefix/len" style definition for
     /// addresses, but is mostly useful for prefix pools.
     /// addresses, but is mostly useful for prefix pools.
     /// @return prefix length (1-128)
     /// @return prefix length (1-128)
-    uint8_t getLength() {
+    uint8_t getLength() const {
         return (prefix_len_);
         return (prefix_len_);
     }
     }
 
 

+ 116 - 0
src/lib/dhcpsrv/srv_config.cc

@@ -8,6 +8,7 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/srv_config.h>
 #include <dhcpsrv/srv_config.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/cfg_hosts_util.h>
 #include <log/logger_manager.h>
 #include <log/logger_manager.h>
 #include <log/logger_specification.h>
 #include <log/logger_specification.h>
 #include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_*
 #include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_*
@@ -15,6 +16,7 @@
 #include <sstream>
 #include <sstream>
 
 
 using namespace isc::log;
 using namespace isc::log;
+using namespace isc::data;
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -200,5 +202,119 @@ 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<long long>(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<int>(dhcp4o6_port_)));
+    // Set dhcp-ddns
+    dhcp->set("dhcp-ddns", d2_client_config_->toElement());
+    // Set interfaces-config
+    dhcp->set("interfaces-config", cfg_iface_->toElement());
+    // Set option-def
+    dhcp->set("option-def", cfg_option_def_->toElement());
+    // Set option-data
+    dhcp->set("option-data", cfg_option_->toElement());
+    // Set subnets
+    ConstElementPtr subnets;
+    if (family == AF_INET) {
+        subnets = cfg_subnets4_->toElement();
+        dhcp->set("subnet4", subnets);
+    } else {
+        subnets = cfg_subnets6_->toElement();
+        dhcp->set("subnet6", subnets);
+    }
+    // Insert reservations
+    CfgHostsList resv_list;
+    resv_list.internalize(cfg_hosts_->toElement());
+    const std::vector<ElementPtr>& sn_list = subnets->listValue();
+    for (std::vector<ElementPtr>::const_iterator subnet = sn_list.begin();
+         subnet != sn_list.end(); ++subnet) {
+        ConstElementPtr id = (*subnet)->get("id");
+        if (isNull(id)) {
+            isc_throw(ToElementError, "subnet has no id");
+        }
+        SubnetID subnet_id = id->intValue();
+        ConstElementPtr resvs = resv_list.get(subnet_id);
+        (*subnet)->set("reservations", resvs);
+    }
+    // Set expired-leases-processing
+    ConstElementPtr expired = cfg_expiration_->toElement();
+    dhcp->set("expired-leases-processing", expired);
+    if (family == AF_INET6) {
+        // Set server-id (DHCPv6)
+        dhcp->set("server-id", cfg_duid_->toElement());
+
+        // Set relay-supplied-options (DHCPv6)
+        dhcp->set("relay-supplied-options", cfg_rsoo_->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_);
+    // @todo accept empty map
+    ConstElementPtr hosts_database = host_db.toElement();
+    if (hosts_database->size() > 0) {
+        dhcp->set("hosts-database", hosts_database);
+    }
+    // Set host-reservation-identifiers
+    ConstElementPtr host_ids;
+    if (family == AF_INET) {
+        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();
+    // @todo accept empty list
+    if (!client_classes->empty()) {
+        dhcp->set("client-classes", client_classes);
+    }
+    // Set hooks-libraries
+    ConstElementPtr hooks_libs = hooks_config_.toElement();
+    dhcp->set("hooks-libraries", hooks_libs);
+    // Set DhcpX
+    result->set(family == AF_INET ? "Dhcp4" : "Dhcp6", dhcp);
+
+    // Logging global map (skip if empty)
+    if (!logging_info_.empty()) {
+        ElementPtr logging = Element::createMap();
+        // Set loggers list
+        ElementPtr loggers = Element::createList();
+        for (LoggingInfoStorage::const_iterator logger =
+                 logging_info_.cbegin();
+             logger != logging_info_.cend(); ++logger) {
+            loggers->add(logger->toElement());
+        }
+        logging->set("loggers", loggers);
+        result->set("Logging", logging);
+    }
+
+    return (result);
+}
+
 }
 }
 }
 }

+ 10 - 5
src/lib/dhcpsrv/srv_config.h

@@ -7,6 +7,7 @@
 #ifndef DHCPSRV_CONFIG_H
 #ifndef DHCPSRV_CONFIG_H
 #define DHCPSRV_CONFIG_H
 #define DHCPSRV_CONFIG_H
 
 
+#include <cc/cfg_to_element.h>
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_duid.h>
 #include <dhcpsrv/cfg_duid.h>
 #include <dhcpsrv/cfg_expiration.h>
 #include <dhcpsrv/cfg_expiration.h>
@@ -37,7 +38,7 @@ class CfgMgr;
 /// @brief Specifies current DHCP configuration
 /// @brief Specifies current DHCP configuration
 ///
 ///
 /// @todo Migrate all other configuration parameters from cfgmgr.h here
 /// @todo Migrate all other configuration parameters from cfgmgr.h here
-class SrvConfig {
+class SrvConfig : public isc::data::CfgToElement {
 public:
 public:
     /// @name Constants for selection of parameters returned by @c getConfigSummary
     /// @name Constants for selection of parameters returned by @c getConfigSummary
     ///
     ///
@@ -507,8 +508,7 @@ public:
     /// this socket is bound and connected to this port and port + 1
     /// this socket is bound and connected to this port and port + 1
     ///
     ///
     /// @param port port and port + 1 to use
     /// @param port port and port + 1 to use
-    void setDhcp4o6Port(uint32_t port) {
-        /// @todo: Port is supposed to be uint16_t, not uint32_t
+    void setDhcp4o6Port(uint16_t port) {
         dhcp4o6_port_ = port;
         dhcp4o6_port_ = port;
     }
     }
 
 
@@ -516,7 +516,7 @@ public:
     ///
     ///
     /// See @ref setDhcp4o6Port for brief discussion.
     /// See @ref setDhcp4o6Port for brief discussion.
     /// @return value of DHCP4o6 IPC port
     /// @return value of DHCP4o6 IPC port
-    uint32_t getDhcp4o6Port() {
+    uint16_t getDhcp4o6Port() {
         return (dhcp4o6_port_);
         return (dhcp4o6_port_);
     }
     }
 
 
@@ -536,6 +536,11 @@ public:
         d2_client_config_ = d2_client_config;
         d2_client_config_ = d2_client_config;
     }
     }
 
 
+    /// @brief Unparse a configuration object
+    ///
+    /// @return a pointer to unparsed configuration
+    virtual isc::data::ElementPtr toElement() const;
+
 private:
 private:
 
 
     /// @brief Sequence number identifying the configuration.
     /// @brief Sequence number identifying the configuration.
@@ -624,7 +629,7 @@ private:
     ///
     ///
     /// DHCPv4-over-DHCPv6 uses a UDP socket for interserver communication,
     /// DHCPv4-over-DHCPv6 uses a UDP socket for interserver communication,
     /// this socket is bound and connected to this port and port + 1
     /// this socket is bound and connected to this port and port + 1
-    uint32_t dhcp4o6_port_;
+    uint16_t dhcp4o6_port_;
 
 
     D2ClientConfigPtr d2_client_config_;
     D2ClientConfigPtr d2_client_config_;
 };
 };

+ 11 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -310,6 +310,16 @@ public:
     void
     void
     allowClientClass(const isc::dhcp::ClientClass& class_name);
     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.
     /// @brief Specifies what type of Host Reservations are supported.
     ///
     ///
     /// Host reservations may be either in-pool (they reserve an address that
     /// Host reservations may be either in-pool (they reserve an address that

+ 1 - 0
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/config/libkea-cfgclient.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.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/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/asiolink/libkea-asiolink.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la

+ 90 - 1
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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -244,6 +244,95 @@ TEST(AddrUtilitiesTest, addrsInRange6) {
                  isc::BadValue);
                  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.
 // Checks if prefixInRange returns valid number of prefixes in specified range.
 TEST(AddrUtilitiesTest, prefixesInRange) {
 TEST(AddrUtilitiesTest, prefixesInRange) {
 
 

+ 22 - 1
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
 // 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
 // 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/.
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 #include <config.h>
 #include <config.h>
+#include <cc/data.h>
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/host_data_source_factory.h>
 #include <dhcpsrv/host_data_source_factory.h>
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/testutils/mysql_schema.h>
 #include <dhcpsrv/testutils/mysql_schema.h>
+#include <testutils/test_to_element.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 using namespace isc;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test;
 using namespace isc::dhcp::test;
+using namespace isc::test;
 
 
 namespace {
 namespace {
 
 
@@ -23,7 +26,11 @@ namespace {
 TEST(CfgDbAccessTest, defaults) {
 TEST(CfgDbAccessTest, defaults) {
     CfgDbAccess cfg;
     CfgDbAccess cfg;
     EXPECT_EQ("type=memfile", cfg.getLeaseDbAccessString());
     EXPECT_EQ("type=memfile", cfg.getLeaseDbAccessString());
+    std::string expected = "{ \"type\": \"memfile\" }";
+    runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg));
+
     EXPECT_TRUE(cfg.getHostDbAccessString().empty());
     EXPECT_TRUE(cfg.getHostDbAccessString().empty());
+    runToElementTest<CfgHostDbAccess>("{ }", CfgHostDbAccess(cfg));
 }
 }
 
 
 // This test verifies that it is possible to set the lease database
 // 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"));
     ASSERT_NO_THROW(cfg.setLeaseDbAccessString("type=mysql"));
     EXPECT_EQ("type=mysql", cfg.getLeaseDbAccessString());
     EXPECT_EQ("type=mysql", cfg.getLeaseDbAccessString());
 
 
+    // Check unparse
+    std::string expected = "{ \"type\": \"mysql\" }";
+    runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg));
+
     // Append additional parameter.
     // Append additional parameter.
     ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4"));
     ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4"));
     EXPECT_EQ("type=mysql universe=4", cfg.getLeaseDbAccessString());
     EXPECT_EQ("type=mysql universe=4", cfg.getLeaseDbAccessString());
 
 
+    // Additional parameters are not in lease_db_access_
+    runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg));
+
     // If access string is empty, no parameters will be appended.
     // If access string is empty, no parameters will be appended.
     ASSERT_NO_THROW(cfg.setLeaseDbAccessString(""));
     ASSERT_NO_THROW(cfg.setLeaseDbAccessString(""));
     EXPECT_TRUE(cfg.getLeaseDbAccessString().empty());
     EXPECT_TRUE(cfg.getLeaseDbAccessString().empty());
@@ -50,10 +64,17 @@ TEST(CfgDbAccessTest, setHostDbAccessString) {
     ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql"));
     ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql"));
     EXPECT_EQ("type=mysql", cfg.getHostDbAccessString());
     EXPECT_EQ("type=mysql", cfg.getHostDbAccessString());
 
 
+    // Check unparse
+    std::string expected = "{ \"type\": \"mysql\" }";
+    runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg));
+
     // Append additional parameter.
     // Append additional parameter.
     ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4"));
     ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4"));
     EXPECT_EQ("type=mysql universe=4", cfg.getHostDbAccessString());
     EXPECT_EQ("type=mysql universe=4", cfg.getHostDbAccessString());
 
 
+    // Additional parameters are not in host_db_access_
+    runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg));
+
     // If access string is empty, no parameters will be appended.
     // If access string is empty, no parameters will be appended.
     ASSERT_NO_THROW(cfg.setHostDbAccessString(""));
     ASSERT_NO_THROW(cfg.setHostDbAccessString(""));
     EXPECT_TRUE(cfg.getHostDbAccessString().empty());
     EXPECT_TRUE(cfg.getHostDbAccessString().empty());

+ 0 - 0
src/lib/dhcpsrv/tests/cfg_duid_unittest.cc


Some files were not shown because too many files changed in this diff