Parcourir la source

[5014_phase2] SimpleParser implemented, 4 parsers converted

 - SimpleParser concept implemented
 - Converted 4 parsers (option data, option data list, option defintion,
   option definition list)
 - updated unit-tests
 - converted other parsers (HostReservationParser{4,6}, ClientClassDefParser)
   to use those new parsers
 - converted kea-dhcp{4,6} to use those new parsers

Conflicts:
	src/bin/dhcp6/json_config_parser.cc
	src/lib/dhcpsrv/parsers/dhcp_parsers.cc
Tomek Mrugalski il y a 8 ans
Parent
commit
f9abab4bd0

+ 37 - 19
src/bin/dhcp4/json_config_parser.cc

@@ -20,6 +20,7 @@
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/simple_parser.h>
 #include <dhcpsrv/timer_mgr.h>
 #include <config/command_mgr.h>
 #include <util/encode/hex.h>
@@ -190,8 +191,7 @@ protected:
             parser = new Pools4ListParser(config_id, pools_);
         } else if (config_id.compare("relay") == 0) {
             parser = new RelayInfoParser(config_id, relay_info_, Option::V4);
-        } else if (config_id.compare("option-data") == 0) {
-            parser = new OptionDataListParser(config_id, options_, AF_INET);
+        // option-data has been converted to SimpleParser already.
         } else if (config_id.compare("match-client-id") == 0) {
             parser = new BooleanParser(config_id, boolean_values_);
         } else if (config_id.compare("4o6-subnet") == 0) {
@@ -424,10 +424,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
         parser = new IfacesConfigParser4();
     } else if (config_id.compare("subnet4") == 0) {
         parser = new Subnets4ListConfigParser(config_id);
-    } else if (config_id.compare("option-data") == 0) {
-        parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET);
-    } else if (config_id.compare("option-def") == 0) {
-        parser  = new OptionDefListParser(config_id, globalContext());
+    // option-data and option-def have been converted to SimpleParser already.
     } else if ((config_id.compare("version") == 0) ||
                (config_id.compare("next-server") == 0)) {
         parser  = new StringParser(config_id,
@@ -538,7 +535,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     // Please do not change this order!
     ParserCollection independent_parsers;
     ParserPtr subnet_parser;
-    ParserPtr option_parser;
     ParserPtr iface_parser;
     ParserPtr leases_parser;
     ParserPtr client_classes_parser;
@@ -567,10 +563,43 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
     // the name of the failing parser can be retrieved in the "catch" clause.
     ConfigPair config_pair;
     try {
+
+        // This is a way to convert ConstElementPtr to ElementPtr.
+        // We need a config that can be edited, because we will insert
+        // default values and will insert derived values as well.
+        std::map<std::string, ConstElementPtr> values;
+        config_set->getValue(values);
+        ElementPtr mutable_cfg(new MapElement());
+        mutable_cfg->setValue(values);
+
+        // Set all default values if not specified by the user.
+        SimpleParser::setAllDefaults(mutable_cfg, false);
+
+        // We need definitions first
+        ConstElementPtr option_defs = mutable_cfg->get("option-def");
+        if (option_defs) {
+            OptionDefListParser parser(AF_INET);
+            CfgOptionDefPtr cfg_option_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
+            parser.parse(cfg_option_def, option_defs);
+        }
+
         // Make parsers grouping.
         const std::map<std::string, ConstElementPtr>& values_map =
-                                                        config_set->mapValue();
+                                                        mutable_cfg->mapValue();
         BOOST_FOREACH(config_pair, values_map) {
+
+            if (config_pair.first == "option-def") {
+                // This is converted to SimpleParser and is handled already above.
+                continue;
+            }
+
+            if (config_pair.first == "option-data") {
+                OptionDataListParser parser(AF_INET);
+                CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
+                parser.parse(cfg_option, config_pair.second);
+                continue;
+            }
+
             ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first,
                                                            config_pair.second));
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
@@ -579,8 +608,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
                 subnet_parser = parser;
             } else if (config_pair.first == "lease-database") {
                 leases_parser = parser;
-            } else if (config_pair.first == "option-data") {
-                option_parser = parser;
             } else if (config_pair.first == "interfaces-config") {
                 // The interface parser is independent from any other
                 // parser and can be run here before any other parsers.
@@ -606,15 +633,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
             }
         }
 
-        // The option values parser is the next one to be run.
-        std::map<std::string, ConstElementPtr>::const_iterator option_config =
-            values_map.find("option-data");
-        if (option_config != values_map.end()) {
-            config_pair.first = "option-data";
-            option_parser->build(option_config->second);
-            option_parser->commit();
-        }
-
         // The class definitions parser is the next one to be run.
         std::map<std::string, ConstElementPtr>::const_iterator cc_config =
             values_map.find("client-classes");

+ 2 - 2
src/bin/dhcp4/tests/host_options_unittest.cc

@@ -203,7 +203,7 @@ const char* HOST_CONFIGS[] = {
         "\"valid-lifetime\": 600,"
         "\"option-data\": [ {"
         "    \"name\": \"vivso-suboptions\","
-        "    \"data\": 4491"
+        "    \"data\": \"4491\""
         "},"
         "{"
         "    \"name\": \"tftp-servers\","
@@ -221,7 +221,7 @@ const char* HOST_CONFIGS[] = {
         "        \"ip-address\": \"10.0.0.7\","
         "        \"option-data\": [ {"
         "            \"name\": \"vivso-suboptions\","
-        "            \"data\": 4491"
+        "            \"data\": \"4491\""
         "        },"
         "        {"
         "            \"name\": \"tftp-servers\","

+ 46 - 24
src/bin/dhcp6/json_config_parser.cc

@@ -29,6 +29,7 @@
 #include <dhcpsrv/parsers/host_reservation_parser.h>
 #include <dhcpsrv/parsers/host_reservations_list_parser.h>
 #include <dhcpsrv/parsers/ifaces_config_parser.h>
+#include <dhcpsrv/parsers/simple_parser.h>
 #include <log/logger_support.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
@@ -184,10 +185,12 @@ public:
                                                              uint32_values_));
                 parser = code_parser;
             } else if (entry == "option-data") {
-                OptionDataListParserPtr option_parser(new OptionDataListParser(entry,
-                                                                               options_,
-                                                                               AF_INET6));
-                parser = option_parser;
+                OptionDataListParser opts_parser(AF_INET6);
+                opts_parser.parse(options_, param.second);
+
+                // OptionDataListParser is converted to SimpleParser already,
+                // no need to go through build/commit phases.
+                continue;
             } else if (entry == "user-context") {
                 user_context_ = param.second;
                 continue; // no parser to remember, simply store the value
@@ -425,8 +428,7 @@ protected:
             parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
         } else if (config_id.compare("pd-pools") == 0) {
             parser = new PdPoolListParser(config_id, pools_);
-        } else if (config_id.compare("option-data") == 0) {
-            parser = new OptionDataListParser(config_id, options_, AF_INET6);
+        // option-data was here, but it is now converted to SimpleParser
         } else if (config_id.compare("rapid-commit") == 0) {
             parser = new BooleanParser(config_id, boolean_values_);
         } else {
@@ -703,11 +705,9 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
         parser = new IfacesConfigParser6();
     } else if (config_id.compare("subnet6") == 0) {
         parser = new Subnets6ListConfigParser(config_id);
-    } else if (config_id.compare("option-data") == 0) {
-        parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET6);
-    } else if (config_id.compare("option-def") == 0) {
-        parser  = new OptionDefListParser(config_id, globalContext());
-    } else if (config_id.compare("version") == 0) {
+    // option-data and option-def are no longer needed here. They're now
+    //  converted to SimpleParser and are handled in configureDhcp6Server
+    }  else if (config_id.compare("version") == 0) {
         parser  = new StringParser(config_id,
                                    globalContext()->string_values_);
     } else if (config_id.compare("lease-database") == 0) {
@@ -808,7 +808,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     // Please do not change this order!
     ParserCollection independent_parsers;
     ParserPtr subnet_parser;
-    ParserPtr option_parser;
     ParserPtr iface_parser;
     ParserPtr leases_parser;
     ParserPtr client_classes_parser;
@@ -838,10 +837,42 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     ConfigPair config_pair;
     try {
 
+        // This is a way to convert ConstElementPtr to ElementPtr.
+        // We need a config that can be edited, because we will insert
+        // default values and will insert derived values as well.
+        std::map<std::string, ConstElementPtr> values;
+        config_set->getValue(values);
+        ElementPtr mutable_cfg(new MapElement());
+        mutable_cfg->setValue(values);
+
+        SimpleParser::setAllDefaults(mutable_cfg, true);
+
         // Make parsers grouping.
         const std::map<std::string, ConstElementPtr>& values_map =
-            config_set->mapValue();
+            mutable_cfg->mapValue();
+
+        // We need definitions first
+        ConstElementPtr option_defs = mutable_cfg->get("option-def");
+        if (option_defs) {
+            OptionDefListParser parser(AF_INET6);
+            CfgOptionDefPtr cfg_option_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
+            parser.parse(cfg_option_def, option_defs);
+        }
+
         BOOST_FOREACH(config_pair, values_map) {
+
+            if (config_pair.first == "option-def") {
+                // This is converted to SimpleParser and is handled already above.
+                continue;
+            }
+
+            if (config_pair.first == "option-data") {
+                OptionDataListParser parser(AF_INET6);
+                CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
+                parser.parse(cfg_option, config_pair.second);
+                continue;
+            }
+
             ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
                                                            config_pair.second));
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
@@ -850,9 +881,9 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
                 subnet_parser = parser;
             } else if (config_pair.first == "lease-database") {
                 leases_parser = parser;
-            } else if (config_pair.first == "option-data") {
+            } /* else if (config_pair.first == "option-data") {
                 option_parser = parser;
-            } else if (config_pair.first == "hooks-libraries") {
+            } */ else if (config_pair.first == "hooks-libraries") {
                 // Executing the commit will alter currently loaded hooks
                 // libraries. Check if the supplied libraries are valid,
                 // but defer the commit until after everything else has
@@ -878,15 +909,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
             }
         }
 
-        // The option values parser is the next one to be run.
-        std::map<std::string, ConstElementPtr>::const_iterator option_config =
-            values_map.find("option-data");
-        if (option_config != values_map.end()) {
-            config_pair.first = "option-data";
-            option_parser->build(option_config->second);
-            option_parser->commit();
-        }
-
         // The class definitions parser is the next one to be run.
         std::map<std::string, ConstElementPtr>::const_iterator cc_config =
             values_map.find("client-classes");

+ 2 - 0
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -2024,10 +2024,12 @@ TEST_F(Dhcpv6SrvTest, rsooOverride) {
         "    \"option-def\": [ {"
         "      \"name\": \"foo\","
         "      \"code\": 120,"
+        "      \"csv-format\": false,"
         "      \"type\": \"binary\""
         "    } ],"
         "    \"option-data\": [ {"
         "      \"code\": 120,"
+        "      \"csv-format\": false,"
         "      \"data\": \"05\""
         "    } ],"
         "    \"preferred-lifetime\": 3000,"

+ 2 - 2
src/bin/dhcp6/tests/host_unittest.cc

@@ -229,7 +229,7 @@ const char* CONFIGS[] = {
         "\"renew-timer\": 1000, "
         "\"option-data\": [ {"
         "    \"name\": \"vendor-opts\","
-        "    \"data\": 4491"
+        "    \"data\": \"4491\""
         "},"
         "{"
         "    \"name\": \"tftp-servers\","
@@ -247,7 +247,7 @@ const char* CONFIGS[] = {
         "        \"ip-addresses\": [ \"2001:db8:1::2\" ],"
         "        \"option-data\": [ {"
         "            \"name\": \"vendor-opts\","
-        "            \"data\": 4491"
+        "            \"data\": \"4491\""
         "        },"
         "        {"
         "            \"name\": \"tftp-servers\","

+ 4 - 0
src/lib/dhcpsrv/Makefile.am

@@ -32,6 +32,8 @@ EXTRA_DIST =
 EXTRA_DIST += parsers/client_class_def_parser.cc
 EXTRA_DIST += parsers/client_class_def_parser.h
 EXTRA_DIST += parsers/dhcp_config_parser.h
+EXTRA_DIST += parsers/simple_parser.cc
+EXTRA_DIST += parsers/simple_parser.h
 EXTRA_DIST += parsers/dbaccess_parser.cc
 EXTRA_DIST += parsers/dbaccess_parser.h
 EXTRA_DIST += parsers/dhcp_parsers.cc
@@ -161,6 +163,8 @@ libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/dbaccess_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/dhcp_parsers.cc
 libkea_dhcpsrv_la_SOURCES += parsers/dhcp_parsers.h
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser.cc
+libkea_dhcpsrv_la_SOURCES += parsers/simple_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/duid_config_parser.cc
 libkea_dhcpsrv_la_SOURCES += parsers/duid_config_parser.h
 libkea_dhcpsrv_la_SOURCES += parsers/expiration_config_parser.cc

+ 7 - 3
src/lib/dhcpsrv/parsers/client_class_def_parser.cc

@@ -91,12 +91,16 @@ ClientClassDefParser::build(ConstElementPtr class_def_cfg) {
                                                                 global_context_));
             parser = exp_parser;
         } else if (entry == "option-data") {
-            OptionDataListParserPtr opts_parser;
+
             uint16_t family = (global_context_->universe_ == Option::V4 ?
                                                              AF_INET : AF_INET6);
 
-            opts_parser.reset(new OptionDataListParser(entry, options_, family));
-            parser = opts_parser;
+            OptionDataListParser opts_parser(family);
+            opts_parser.parse(options_, param.second);
+
+            // OptionDataListParser is converted to SimpleParser already,
+            // no need to go through build/commit phases.
+            continue;
         } else if (entry == "next-server") {
             StringParserPtr str_parser(new StringParser(entry, string_values_));
             parser = str_parser;

+ 94 - 212
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -376,75 +376,30 @@ HooksLibrariesParser::getLibraries(isc::hooks::HookLibsCollection& libraries,
 }
 
 // **************************** OptionDataParser *************************
-OptionDataParser::OptionDataParser(const std::string&, const CfgOptionPtr& cfg,
-                                   const uint16_t address_family)
-    : boolean_values_(new BooleanStorage()),
-      string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
-      option_descriptor_(false), cfg_(cfg),
-      address_family_(address_family) {
-    // If configuration not specified, then it is a global configuration
-    // scope.
-    if (!cfg_) {
-        cfg_ = CfgMgr::instance().getStagingCfg()->getCfgOption();
-    }
+OptionDataParser::OptionDataParser(const uint16_t address_family)
+    : address_family_(address_family) {
 }
 
-void
-OptionDataParser::build(ConstElementPtr option_data_entries) {
-    BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
-        ParserPtr parser;
-        if (param.first == "name" || param.first == "data" ||
-            param.first == "space") {
-            StringParserPtr name_parser(new StringParser(param.first,
-                                        string_values_));
-            parser = name_parser;
-        } else if (param.first == "code") {
-            Uint32ParserPtr code_parser(new Uint32Parser(param.first,
-                                       uint32_values_));
-            parser = code_parser;
-        } else if (param.first == "csv-format") {
-            BooleanParserPtr value_parser(new BooleanParser(param.first,
-                                         boolean_values_));
-            parser = value_parser;
-        } else {
-            isc_throw(DhcpConfigError,
-                      "option-data parameter not supported: " << param.first
-                      << " (" << param.second->getPosition() << ")");
-        }
-
-        parser->build(param.second);
-        // Before we can create an option we need to get the data from
-        // the child parsers. The only way to do it is to invoke commit
-        // on them so as they store the values in appropriate storages
-        // that this class provided to them. Note that this will not
-        // modify values stored in the global storages so the configuration
-        // will remain consistent even parsing fails somewhere further on.
-        parser->commit();
-    }
+std::pair<OptionDescriptor, std::string>
+OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
 
     // Try to create the option instance.
-    createOption(option_data_entries);
+    std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
 
-    if (!option_descriptor_.option_) {
+    if (!opt.first.option_) {
         isc_throw(isc::InvalidOperation,
             "parser logic error: no option has been configured and"
             " thus there is nothing to commit. Has build() been called?");
     }
 
-    cfg_->add(option_descriptor_.option_, option_descriptor_.persistent_,
-              option_space_);
-}
-
-void
-OptionDataParser::commit() {
-    // Does nothing
+    return (opt);
 }
 
 OptionalValue<uint32_t>
 OptionDataParser::extractCode(ConstElementPtr parent) const {
     uint32_t code;
     try {
-        code = uint32_values_->getParam("code");
+        code = getInteger(parent, "code");
 
     } catch (const exception&) {
         // The code parameter was not found. Return an unspecified
@@ -454,14 +409,14 @@ OptionDataParser::extractCode(ConstElementPtr parent) const {
 
     if (code == 0) {
         isc_throw(DhcpConfigError, "option code must not be zero "
-                  "(" << uint32_values_->getPosition("code", parent) << ")");
+                  "(" << getPosition("code", parent) << ")");
 
     } else if (address_family_ == AF_INET &&
                code > std::numeric_limits<uint8_t>::max()) {
         isc_throw(DhcpConfigError, "invalid option code '" << code
                 << "', it must not be greater than '"
                   << static_cast<int>(std::numeric_limits<uint8_t>::max())
-                  << "' (" << uint32_values_->getPosition("code", parent)
+                  << "' (" << getPosition("code", parent)
                   << ")");
 
     } else if (address_family_ == AF_INET6 &&
@@ -469,7 +424,7 @@ OptionDataParser::extractCode(ConstElementPtr parent) const {
         isc_throw(DhcpConfigError, "invalid option code '" << code
                 << "', it must not exceed '"
                   << std::numeric_limits<uint16_t>::max()
-                  << "' (" << uint32_values_->getPosition("code", parent)
+                  << "' (" << getPosition("code", parent)
                   << ")");
 
     }
@@ -481,7 +436,7 @@ OptionalValue<std::string>
 OptionDataParser::extractName(ConstElementPtr parent) const {
     std::string name;
     try {
-        name = string_values_->getParam("name");
+        name = getString(parent, "name");
 
     } catch (...) {
         return (OptionalValue<std::string>());
@@ -490,17 +445,17 @@ OptionDataParser::extractName(ConstElementPtr parent) const {
     if (name.find(" ") != std::string::npos) {
         isc_throw(DhcpConfigError, "invalid option name '" << name
                   << "', space character is not allowed ("
-                  << string_values_->getPosition("name", parent) << ")");
+                  << getPosition("name", parent) << ")");
     }
 
     return (OptionalValue<std::string>(name, OptionalValueState(true)));
 }
 
 std::string
-OptionDataParser::extractData() const {
+OptionDataParser::extractData(ConstElementPtr parent) const {
     std::string data;
     try {
-        data = string_values_->getParam("data");
+        data = getString(parent, "data");
 
     } catch (...) {
         // The "data" parameter was not found. Return an empty value.
@@ -511,10 +466,10 @@ OptionDataParser::extractData() const {
 }
 
 OptionalValue<bool>
-OptionDataParser::extractCSVFormat() const {
+OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
     bool csv_format = true;
     try {
-        csv_format = boolean_values_->getParam("csv-format");
+        csv_format = getBoolean(parent, "csv-format");
 
     } catch (...) {
         return (OptionalValue<bool>(csv_format));
@@ -524,11 +479,11 @@ OptionDataParser::extractCSVFormat() const {
 }
 
 std::string
-OptionDataParser::extractSpace() const {
+OptionDataParser::extractSpace(ConstElementPtr parent) const {
     std::string space = address_family_ == AF_INET ?
         DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
     try {
-        space = string_values_->getParam("space");
+        space = getString(parent, "space");
 
     } catch (...) {
         return (space);
@@ -556,7 +511,7 @@ OptionDataParser::extractSpace() const {
         // should never get here. Therefore, it is ok to call getPosition for
         // the space parameter here as this parameter will always be specified.
         isc_throw(DhcpConfigError, ex.what() << " ("
-                  << string_values_->getPosition("space") << ")");
+                  << getPosition("space", parent) << ")");
     }
 
     return (space);
@@ -588,16 +543,16 @@ OptionDataParser::findOptionDefinition(const std::string& option_space,
     return (def);
 }
 
-void
+std::pair<OptionDescriptor, std::string>
 OptionDataParser::createOption(ConstElementPtr option_data) {
     const Option::Universe universe = address_family_ == AF_INET ?
         Option::V4 : Option::V6;
 
     OptionalValue<uint32_t> code_param =  extractCode(option_data);
     OptionalValue<std::string> name_param = extractName(option_data);
-    OptionalValue<bool> csv_format_param = extractCSVFormat();
-    std::string data_param = extractData();
-    std::string space_param = extractSpace();
+    OptionalValue<bool> csv_format_param = extractCSVFormat(option_data);
+    std::string data_param = extractData(option_data);
+    std::string space_param = extractSpace(option_data);
 
     // Require that option code or option name is specified.
     if (!code_param.isSpecified() && !name_param.isSpecified()) {
@@ -622,7 +577,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
                       << space_param << "." << name_param
                       << "' having code '" << code_param
                       << "' does not exist ("
-                      << string_values_->getPosition("name", option_data)
+                      << getPosition("name", option_data)
                       << ")");
 
         // If there is no option definition and the option code is not specified
@@ -631,7 +586,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
             isc_throw(DhcpConfigError, "definition for the option '"
                       << space_param << "." << name_param
                       << "' does not exist ("
-                      << string_values_->getPosition("name", option_data)
+                      << getPosition("name", option_data)
                       << ")");
         }
     }
@@ -664,12 +619,14 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
             isc_throw(DhcpConfigError, "option data is not a valid"
                       << " string of hexadecimal digits: " << data_param
                       << " ("
-                      << string_values_->getPosition("data", option_data)
+                      << getPosition("data", option_data)
                       << ")");
         }
     }
 
     OptionPtr option;
+    OptionDescriptor desc(false);
+
     if (!def) {
         // @todo We have a limited set of option definitions initalized at
         // the moment.  In the future we want to initialize option definitions
@@ -678,13 +635,9 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
         // ok to create generic option if definition does not exist.
         OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
                                     binary));
-        // The created option is stored in option_descriptor_ class member
-        // until the commit stage when it is inserted into the main storage.
-        // If an option with the same code exists in main storage already the
-        // old option is replaced.
-        option_descriptor_.option_ = option;
-        option_descriptor_.persistent_ = false;
 
+        desc.option_ = option;
+        desc.persistent_ = false;
     } else {
 
         // Option name is specified it should match the name in the definition.
@@ -693,7 +646,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
                       << name_param << "' does not match the "
                       << "option definition: '" << space_param
                       << "." << def->getName() << "' ("
-                      << string_values_->getPosition("name", option_data)
+                      << getPosition("name", option_data)
                       << ")");
         }
 
@@ -704,144 +657,65 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
                 !csv_format_param.isSpecified() || csv_format_param ?
                 def->optionFactory(universe, def->getCode(), data_tokens) :
                 def->optionFactory(universe, def->getCode(), binary);
-            OptionDescriptor desc(option, false);
-            option_descriptor_.option_ = option;
-            option_descriptor_.persistent_ = false;
-
+            desc.option_ = option;
+            desc.persistent_ = false;
         } catch (const isc::Exception& ex) {
             isc_throw(DhcpConfigError, "option data does not match"
                       << " option definition (space: " << space_param
                       << ", code: " << def->getCode() << "): "
                       << ex.what() << " ("
-                      << string_values_->getPosition("data", option_data)
+                      << getPosition("data", option_data)
                       << ")");
         }
     }
 
     // All went good, so we can set the option space name.
-    option_space_ = space_param;
+    return make_pair(desc, space_param);
 }
 
 // **************************** OptionDataListParser *************************
-OptionDataListParser::OptionDataListParser(const std::string&,
-                                           const CfgOptionPtr& cfg,
+OptionDataListParser::OptionDataListParser(//const std::string&,
+                                           //const CfgOptionPtr& cfg,
                                            const uint16_t address_family)
-    : cfg_(cfg), address_family_(address_family) {
+    : address_family_(address_family) {
 }
 
-void
-OptionDataListParser::build(ConstElementPtr option_data_list) {
-    BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
-        boost::shared_ptr<OptionDataParser>
-            parser(new OptionDataParser("option-data", cfg_, address_family_));
-
-        parser->build(option_value);
-        parsers_.push_back(parser);
-    }
-}
 
-void
-OptionDataListParser::commit() {
-    BOOST_FOREACH(ParserPtr parser, parsers_) {
-        parser->commit();
-    }
-    // Append suboptions to the top-level options
-    if (cfg_) {
-        cfg_->encapsulate();
-    } else {
-        CfgMgr::instance().getStagingCfg()->getCfgOption()->encapsulate();
+void OptionDataListParser::parse(const CfgOptionPtr& cfg,
+                                 isc::data::ConstElementPtr option_data_list) {
+    OptionDataParser option_parser(address_family_);
+    BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
+        std::pair<OptionDescriptor, std::string> option =
+            option_parser.parse(data);
+        cfg->add(option.first.option_, option.first.persistent_, option.second);
+        cfg->encapsulate();
     }
 }
 
 // ******************************** OptionDefParser ****************************
-OptionDefParser::OptionDefParser(const std::string&,
-                                 ParserContextPtr global_context)
-    : boolean_values_(new BooleanStorage()),
-      string_values_(new StringStorage()),
-      uint32_values_(new Uint32Storage()),
-      global_context_(global_context) {
+OptionDefParser::OptionDefParser(const uint16_t address_family)
+    : address_family_(address_family) {
 }
 
-void
-OptionDefParser::build(ConstElementPtr option_def) {
-    // Parse the elements that make up the option definition.
-    BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
-        std::string entry(param.first);
-        ParserPtr parser;
-        if (entry == "name" || entry == "type" || entry == "record-types"
-            || entry == "space" || entry == "encapsulate") {
-            StringParserPtr str_parser(new StringParser(entry,
-                                       string_values_));
-            parser = str_parser;
-        } else if (entry == "code") {
-            Uint32ParserPtr code_parser(new Uint32Parser(entry,
-                                        uint32_values_));
-            parser = code_parser;
-        } else if (entry == "array") {
-            BooleanParserPtr array_parser(new BooleanParser(entry,
-                                         boolean_values_));
-            parser = array_parser;
-        } else {
-            isc_throw(DhcpConfigError, "invalid parameter '" << entry
-                      << "' (" << param.second->getPosition() << ")");
-        }
+std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
+OptionDefParser::parse(ConstElementPtr option_def) {
 
-        parser->build(param.second);
-        parser->commit();
-    }
-    // Create an instance of option definition.
-    createOptionDef(option_def);
+    // Get mandatory parameters.
+    std::string name = getString(option_def, "name");
+    uint32_t code = getInteger(option_def, "code");
+    std::string type = getString(option_def, "type");
 
-    try {
-        CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->
-            add(option_definition_, option_space_name_);
-
-    } catch (const std::exception& ex) {
-        // Append position if there is a failure.
-        isc_throw(DhcpConfigError, ex.what() << " ("
-                  << option_def->getPosition() << ")");
-    }
-
-    // All definitions have been prepared. Put them as runtime options into
-    // the libdhcp++.
-    const OptionDefSpaceContainer& container =
-        CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->getContainer();
-    LibDHCP::setRuntimeOptionDefs(container);
-}
-
-void
-OptionDefParser::commit() {
-    // Do nothing.
-}
-
-void
-OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
-    // Check if mandatory parameters have been specified.
-    std::string name;
-    uint32_t code;
-    std::string type;
-    try {
-        name = string_values_->getParam("name");
-        code = uint32_values_->getParam("code");
-        type = string_values_->getParam("type");
-    } catch (const std::exception& ex) {
-        isc_throw(DhcpConfigError, ex.what() << " ("
-                  << option_def_element->getPosition() << ")");
-    }
-
-    bool array_type = boolean_values_->getOptionalParam("array", false);
-    std::string record_types =
-        string_values_->getOptionalParam("record-types", "");
-    std::string space = string_values_->getOptionalParam("space",
-              global_context_->universe_ == Option::V4 ? DHCP4_OPTION_SPACE :
-                                                         DHCP6_OPTION_SPACE);
-    std::string encapsulates =
-        string_values_->getOptionalParam("encapsulate", "");
+    // Get optional parameters. Whoever called this parser, should have
+    // called SimpleParser::setDefaults first.
+    bool array_type = getBoolean(option_def, "array");
+    std::string record_types = getString(option_def, "record-types");
+    std::string space = getString(option_def, "space");
+    std::string encapsulates = getString(option_def, "encapsulate");
 
     if (!OptionSpace::validateName(space)) {
         isc_throw(DhcpConfigError, "invalid option space name '"
                   << space << "' ("
-                  << string_values_->getPosition("space") << ")");
+                  << getPosition("space", option_def) << ")");
     }
 
     // Create option definition.
@@ -854,14 +728,14 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
             isc_throw(DhcpConfigError, "option '" << space << "."
                       << "name" << "', comprising an array of data"
                       << " fields may not encapsulate any option space ("
-                      << option_def_element->getPosition() << ")");
+                      << option_def->getPosition() << ")");
 
         } else if (encapsulates == space) {
             isc_throw(DhcpConfigError, "option must not encapsulate"
                       << " an option space it belongs to: '"
                       << space << "." << name << "' is set to"
                       << " encapsulate '" << space << "' ("
-                      << option_def_element->getPosition() << ")");
+                      << option_def->getPosition() << ")");
 
         } else {
             def.reset(new OptionDefinition(name, code, type,
@@ -888,7 +762,7 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
             isc_throw(DhcpConfigError, "invalid record type values"
                       << " specified for the option definition: "
                       << ex.what() << " ("
-                      << string_values_->getPosition("record-types") << ")");
+                      << getPosition("record-types", option_def) << ")");
         }
     }
 
@@ -897,38 +771,43 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
         def->validate();
     } catch (const std::exception& ex) {
         isc_throw(DhcpConfigError, ex.what()
-                  << " (" << option_def_element->getPosition() << ")");
+                  << " (" << option_def->getPosition() << ")");
     }
 
     // Option definition has been created successfully.
-    option_space_name_ = space;
-    option_definition_ = def;
+    return make_pair(def, space);
 }
 
 // ******************************** OptionDefListParser ************************
-OptionDefListParser::OptionDefListParser(const std::string&,
-                                         ParserContextPtr global_context)
-    : global_context_(global_context) {
+OptionDefListParser::OptionDefListParser(const uint16_t address_family)
+    : address_family_(address_family) {
 }
 
 void
-OptionDefListParser::build(ConstElementPtr option_def_list) {
+OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_list) {
     if (!option_def_list) {
         isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
                   << " option definitions is NULL ("
                   << option_def_list->getPosition() << ")");
     }
 
+    OptionDefParser parser(address_family_);
     BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
-        boost::shared_ptr<OptionDefParser>
-            parser(new OptionDefParser("single-option-def", global_context_));
-        parser->build(option_def);
+        OptionDefinitionTuple def;
+
+        def = parser.parse(option_def);
+        try {
+            storage->add(def.first, def.second);
+        } catch (const std::exception& ex) {
+            // Append position if there is a failure.
+            isc_throw(DhcpConfigError, ex.what() << " ("
+                      << option_def->getPosition() << ")");
+        }
     }
-}
 
-void
-OptionDefListParser::commit() {
-    // Do nothing.
+    // All definitions have been prepared. Put them as runtime options into
+    // the libdhcp++.
+    LibDHCP::setRuntimeOptionDefs(storage->getContainer());
 }
 
 //****************************** RelayInfoParser ********************************
@@ -1138,13 +1017,9 @@ PoolParser::build(ConstElementPtr pool_structure) {
                           " address pools");
             }
 
-            OptionDataListParserPtr option_parser(new OptionDataListParser("option-data",
-                                                                           options_,
-                                                                           address_family_));
-            option_parser->build(option_data);
-            option_parser->commit();
-            options_->copyTo(*pool->getCfgOption());;
-
+            CfgOptionPtr cfg = pool->getCfgOption();
+            OptionDataListParser option_parser(address_family_);
+            option_parser.parse(cfg, option_data);
         } catch (const std::exception& ex) {
             isc_throw(isc::dhcp::DhcpConfigError, ex.what()
                       << " (" << option_data->getPosition() << ")");
@@ -1194,6 +1069,13 @@ SubnetConfigParser::build(ConstElementPtr subnet) {
             continue;
         }
 
+        if (param.first == "option-data") {
+            uint16_t family = global_context_->universe_ == Option::V4 ? AF_INET : AF_INET6;
+            OptionDataListParser opt_parser(family);
+            opt_parser.parse(options_, param.second);
+            continue;
+        }
+
         ParserPtr parser;
         // When unsupported parameter is specified, the function called
         // below will thrown an exception. We have to catch this exception

+ 62 - 140
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -15,7 +15,9 @@
 #include <dhcpsrv/cfg_iface.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/subnet.h>
+#include <dhcpsrv/cfg_option_def.h>
 #include <dhcpsrv/parsers/dhcp_config_parser.h>
+#include <dhcpsrv/parsers/simple_parser.h>
 #include <hooks/libinfo.h>
 #include <exceptions/exceptions.h>
 #include <util/optional_value.h>
@@ -534,39 +536,30 @@ private:
 /// an option the configuration will not be accepted. If parsing
 /// is successful then an instance of an option is created and
 /// added to the storage provided by the calling class.
-class OptionDataParser : public DhcpConfigParser {
+class OptionDataParser : public SimpleParser {
 public:
     /// @brief Constructor.
     ///
-    /// @param dummy first argument is ignored, all Parser constructors
-    /// accept string as first argument.
-    /// @param [out] cfg Pointer to the configuration object where parsed option
-    /// should be stored or NULL if this is a global option.
     /// @param address_family Address family: @c AF_INET or @c AF_INET6.
-    /// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
-    OptionDataParser(const std::string& dummy, const CfgOptionPtr& cfg,
-                     const uint16_t address_family);
+    OptionDataParser(const uint16_t address_family);
 
-    /// @brief Parses the single option data.
+    /// @brief Parses ElementPtr containing option definition
     ///
-    /// This method parses the data of a single option from the configuration.
-    /// The option data includes option name, option code and data being
-    /// carried by this option. Eventually it creates the instance of the
-    /// option and adds it to the Configuration Manager.
+    /// This method parses ElementPtr containing the option defintion,
+    /// instantiates the option for it and then returns a pair
+    /// of option descritor (that holds that new option) and
+    /// a string that specifies the option space.
     ///
-    /// @param option_data_entries collection of entries that define value
-    /// for a particular option.
-    /// @throw DhcpConfigError if invalid parameter specified in
-    /// the configuration.
-    /// @throw isc::InvalidOperation if failed to set storage prior to
-    /// calling build.
-    virtual void build(isc::data::ConstElementPtr option_data_entries);
-
-    /// @brief Does nothing.
-    virtual void commit();
-
-    /// @brief virtual destructor to ensure orderly destruction of derivations.
-    virtual ~OptionDataParser(){};
+    /// Note: ElementPtr is expected to contain all fields. If your
+    /// ElementPtr does not have them, please use
+    /// @ref SimpleParser::setOptionDefaults to fill the missing fields
+    /// with default values.
+    ///
+    /// @param single_option ElementPtr containing option defintion
+    /// @return Option object wrapped in option description and an option
+    ///         space
+    std::pair<OptionDescriptor, std::string>
+    parse(isc::data::ConstElementPtr single_option);
 private:
 
     /// @brief Finds an option definition within an option space
@@ -594,22 +587,19 @@ private:
     /// options storage. If the option data parsed by \ref build function
     /// are invalid or insufficient this function emits an exception.
     ///
-    /// @warning this function does not check if options_ storage pointer
-    /// is intitialized but this check is not needed here because it is done
-    /// in the \ref build function.
-    ///
     /// @param option_data An element holding data for a single option being
     /// created.
     ///
+    /// @return created option descriptor
+    ///
     /// @throw DhcpConfigError if parameters provided in the configuration
     /// are invalid.
-    void createOption(isc::data::ConstElementPtr option_data);
+    std::pair<OptionDescriptor, std::string>
+    createOption(isc::data::ConstElementPtr option_data);
 
     /// @brief Retrieves parsed option code as an optional value.
     ///
     /// @param parent A data element holding full option data configuration.
-    /// It is used here to log a position if the element holding a code
-    /// is not specified and its position is therefore unavailable.
     ///
     /// @return Option code, possibly unspecified.
     /// @throw DhcpConfigError if option code is invalid.
@@ -619,8 +609,6 @@ private:
     /// @brief Retrieves parsed option name as an optional value.
     ///
     /// @param parent A data element holding full option data configuration.
-    /// It is used here to log a position if the element holding a name
-    /// is not specified and its position is therefore unavailable.
     ///
     /// @return Option name, possibly unspecified.
     /// @throw DhcpConfigError if option name is invalid.
@@ -630,13 +618,14 @@ private:
     /// @brief Retrieves csv-format parameter as an optional value.
     ///
     /// @return Value of the csv-format parameter, possibly unspecified.
-    util::OptionalValue<bool> extractCSVFormat() const;
+    util::OptionalValue<bool> extractCSVFormat(data::ConstElementPtr parent) const;
 
     /// @brief Retrieves option data as a string.
     ///
+    /// @param parent A data element holding full option data configuration.
     /// @return Option data as a string. It will return empty string if
     /// option data is unspecified.
-    std::string extractData() const;
+    std::string extractData(data::ConstElementPtr parent) const;
 
     /// @brief Retrieves option space name.
     ///
@@ -644,27 +633,10 @@ private:
     /// 'dhcp4' or 'dhcp6' option space name is returned, depending on
     /// the universe specified in the parser context.
     ///
+    /// @param parent A data element holding full option data configuration.
+    ///
     /// @return Option space name.
-    std::string extractSpace() const;
-
-    /// Storage for boolean values.
-    BooleanStoragePtr boolean_values_;
-
-    /// Storage for string values (e.g. option name or data).
-    StringStoragePtr string_values_;
-
-    /// Storage for uint32 values (e.g. option code).
-    Uint32StoragePtr uint32_values_;
-
-    /// Option descriptor holds newly configured option.
-    OptionDescriptor option_descriptor_;
-
-    /// Option space name where the option belongs to.
-    std::string option_space_;
-
-    /// @brief Configuration holding option being parsed or NULL if the option
-    /// is global.
-    CfgOptionPtr cfg_;
+    std::string extractSpace(data::ConstElementPtr parent) const;
 
     /// @brief Address family: @c AF_INET or @c AF_INET6.
     uint16_t address_family_;
@@ -680,97 +652,52 @@ typedef OptionDataParser *OptionDataParserFactory(const std::string&,
 /// data for a particular subnet and creates a collection of options.
 /// If parsing is successful, all these options are added to the Subnet
 /// object.
-class OptionDataListParser : public DhcpConfigParser {
+class OptionDataListParser : public SimpleParser {
 public:
     /// @brief Constructor.
     ///
-    /// @param dummy nominally would be param name, this is always ignored.
-    /// @param [out] cfg Pointer to the configuration object where options
-    /// should be stored or NULL if this is global option scope.
     /// @param address_family Address family: @c AF_INET or AF_INET6
-    OptionDataListParser(const std::string& dummy, const CfgOptionPtr& cfg,
-                         const uint16_t address_family);
+    OptionDataListParser(const uint16_t address_family);
 
-    /// @brief Parses entries that define options' data for a subnet.
+    /// @brief Parses a list of options, instantiates them and stores in cfg
     ///
-    /// This method iterates over all entries that define option data
-    /// for options within a single subnet and creates options' instances.
-    ///
-    /// @param option_data_list pointer to a list of options' data sets.
-    /// @throw DhcpConfigError if option parsing failed.
-    void build(isc::data::ConstElementPtr option_data_list);
-
-    /// @brief Commit all option values.
+    /// This method expects to get a list of options in option_data_list,
+    /// iterates over them, creates option objects, wraps them with
+    /// option descriptor and stores in specified cfg.
     ///
-    /// This function invokes commit for all option values
-    /// and append suboptions to the top-level options.
-    void commit();
-
+    /// @param cfg created options will be stored here
+    /// @param option_data_list configuration that describes the options
+    void parse(const CfgOptionPtr& cfg,
+               isc::data::ConstElementPtr option_data_list);
 private:
-
-    /// Collection of parsers;
-    ParserCollection parsers_;
-
-    /// @brief Pointer to a configuration where options are stored.
-    CfgOptionPtr cfg_;
-
     /// @brief Address family: @c AF_INET or @c AF_INET6
     uint16_t address_family_;
-
 };
 
-typedef boost::shared_ptr<OptionDataListParser> OptionDataListParserPtr;
-
+typedef std::pair<isc::dhcp::OptionDefinitionPtr, std::string> OptionDefinitionTuple;
 
 /// @brief Parser for a single option definition.
 ///
 /// This parser creates an instance of a single option definition.
-class OptionDefParser : public DhcpConfigParser {
+class OptionDefParser : public SimpleParser {
 public:
     /// @brief Constructor.
     ///
-    /// @param dummy first argument is ignored, all Parser constructors
-    /// accept string as first argument.
-    /// @param global_context is a pointer to the global context which
-    /// stores global scope parameters, options, option defintions.
-    OptionDefParser(const std::string& dummy, ParserContextPtr global_context);
+    /// @param address_family Address family: @c AF_INET or AF_INET6
+    OptionDefParser(const uint16_t address_family);
 
     /// @brief Parses an entry that describes single option definition.
     ///
     /// @param option_def a configuration entry to be parsed.
+    /// @return tuple (option definition, option space) of the parsed structure
     ///
     /// @throw DhcpConfigError if parsing was unsuccessful.
-    void build(isc::data::ConstElementPtr option_def);
-
-    /// @brief Stores the parsed option definition in a storage.
-    void commit();
+    OptionDefinitionTuple
+    parse(isc::data::ConstElementPtr option_def);
 
 private:
-
-    /// @brief Create option definition from the parsed parameters.
-    ///
-    /// @param option_def_element A data element holding the configuration
-    /// for an option definition.
-    void createOptionDef(isc::data::ConstElementPtr option_def_element);
-
-    /// Instance of option definition being created by this parser.
-    OptionDefinitionPtr option_definition_;
-
-    /// Name of the space the option definition belongs to.
-    std::string option_space_name_;
-
-    /// Storage for boolean values.
-    BooleanStoragePtr boolean_values_;
-
-    /// Storage for string values.
-    StringStoragePtr string_values_;
-
-    /// Storage for uint32 values.
-    Uint32StoragePtr uint32_values_;
-
-    /// Parsing context which contains global values, options and option
-    /// definitions.
-    ParserContextPtr global_context_;
+    /// @brief Address family: @c AF_INET or @c AF_INET6
+    uint16_t address_family_;
 };
 
 /// @brief Parser for a list of option definitions.
@@ -779,7 +706,7 @@ private:
 /// option definitions and creates instances of these definitions.
 /// If the parsing is successful, the collection of created definitions
 /// is put into the provided storage.
-class OptionDefListParser : public DhcpConfigParser {
+class OptionDefListParser : public SimpleParser {
 public:
     /// @brief Constructor.
     ///
@@ -787,29 +714,24 @@ public:
     /// accept string as first argument.
     /// @param global_context is a pointer to the global context which
     /// stores global scope parameters, options, option defintions.
-    OptionDefListParser(const std::string& dummy,
-                        ParserContextPtr global_context);
+    OptionDefListParser(const uint16_t address_family);
+                        //const std::string& dummy,
+                        //ParserContextPtr global_context);
 
-    /// @brief Parse configuration entries.
+    /// @brief Parses a list of option defintions, create them and store in cfg
     ///
-    /// This function parses configuration entries, creates instances
-    /// of option definitions and tries to add them to the Configuration
-    /// Manager.
+    /// This method iterates over def_list, which is a JSON list of option defintions,
+    /// then creates corresponding option defintions and store them in the
+    /// configuration structure.
     ///
-    /// @param option_def_list pointer to an element that holds entries
-    /// that define option definitions.
-    /// @throw DhcpConfigError if configuration parsing fails.
-    void build(isc::data::ConstElementPtr option_def_list);
+    /// @param def_list JSON list describing option definitions
+    /// @param cfg parsed option definitions will be stored here
+    void parse(CfgOptionDefPtr cfg, isc::data::ConstElementPtr def_list);
 
-    /// @brief Commits option definitions.
-    ///
-    /// Currently this function is no-op, because option definitions are
-    /// added to the Configuration Manager in the @c build method.
-    void commit();
+protected:
 
-    /// Parsing context which contains global values, options and option
-    /// definitions.
-    ParserContextPtr global_context_;
+    /// @brief Address family: @c AF_INET or @c AF_INET6
+    uint16_t address_family_;
 };
 
 /// @brief a collection of pools

+ 12 - 4
src/lib/dhcpsrv/parsers/host_reservation_parser.cc

@@ -196,8 +196,12 @@ HostReservationParser4::build(isc::data::ConstElementPtr reservation_data) {
         // surround it with try-catch.
         if (element.first == "option-data") {
             CfgOptionPtr cfg_option = host_->getCfgOption4();
-            OptionDataListParser parser(element.first, cfg_option, AF_INET);
-            parser.build(element.second);
+
+            // This parser is converted to SimpleParser already. It
+            // parses the Element structure immediately, there's no need
+            // to go through build/commit phases.
+            OptionDataListParser parser(AF_INET);
+            parser.parse(cfg_option, element.second);
 
        // Everything else should be surrounded with try-catch to append
        // position.
@@ -255,8 +259,12 @@ HostReservationParser6::build(isc::data::ConstElementPtr reservation_data) {
         // appended).
         if (element.first == "option-data") {
             CfgOptionPtr cfg_option = host_->getCfgOption6();
-            OptionDataListParser parser(element.first, cfg_option, AF_INET6);
-            parser.build(element.second);
+
+            // This parser is converted to SimpleParser already. It
+            // parses the Element structure immediately, there's no need
+            // to go through build/commit phases.
+            OptionDataListParser parser(AF_INET6);
+            parser.parse(cfg_option, element.second);
 
         } else if (element.first == "ip-addresses" || element.first == "prefixes") {
             BOOST_FOREACH(ConstElementPtr prefix_element,

+ 253 - 0
src/lib/dhcpsrv/parsers/simple_parser.cc

@@ -0,0 +1,253 @@
+#include <dhcpsrv/parsers/simple_parser.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <cc/data.h>
+#include <string>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+/// This table defines default values for option definitions in DHCPv4
+const SimpleDefaults OPTION4_DEF_DEFAULTS{
+    { "record-types", Element::string,  ""},
+    { "space",        Element::string,  "dhcp4"},
+    { "array",        Element::boolean, "false"},
+    { "encapsulate",  Element::string,  "" }
+};
+
+/// This table defines default values for option definitions in DHCPv6
+const SimpleDefaults OPTION6_DEF_DEFAULTS{
+    { "record-types", Element::string,  ""},
+    { "space",        Element::string,  "dhcp6"},
+    { "array",        Element::boolean, "false"},
+    { "encapsulate",  Element::string,  "" }
+};
+
+/// This table defines default values for options in DHCPv4
+const SimpleDefaults OPTION4_DEFAULTS({
+    { "space",        Element::string,  "dhcp4"},
+    { "csv-format",   Element::boolean, "true"},
+    { "encapsulate",  Element::string,  "" }
+});
+
+/// This table defines default values for options in DHCPv6
+const SimpleDefaults OPTION6_DEFAULTS({
+    { "space",        Element::string,  "dhcp6"},
+    { "csv-format",   Element::boolean, "true"},
+    { "encapsulate",  Element::string,  "" }
+});
+
+/// This table defines default values for both DHCPv4 and DHCPv6
+const SimpleDefaults GLOBAL_DEFAULTS = {
+    { "renew-timer",        Element::integer, "900" },
+    { "rebind-timer",       Element::integer, "1800" },
+    { "preferred-lifetime", Element::integer, "3600" },
+    { "valid-lifetime",     Element::integer, "7200" }
+};
+
+std::string
+SimpleParser::getString(isc::data::ConstElementPtr scope, const std::string& name) {
+    ConstElementPtr x = scope->get(name);
+    if (!x) {
+        isc_throw(BadValue, "Element " << name << " not found");
+    }
+    if (x->getType() != Element::string) {
+        isc_throw(BadValue, "Element " << name << " found, but is not a string");
+    }
+
+    return (x->stringValue());
+}
+
+int64_t
+SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name) {
+    ConstElementPtr x = scope->get(name);
+    if (!x) {
+        isc_throw(BadValue, "Element " << name << " not found");
+    }
+    if (x->getType() != Element::integer) {
+        isc_throw(BadValue, "Element " << name << " found, but is not an integer");
+    }
+
+    return (x->intValue());
+}
+
+bool
+SimpleParser::getBoolean(isc::data::ConstElementPtr scope, const std::string& name) {
+    ConstElementPtr x = scope->get(name);
+    if (!x) {
+        isc_throw(BadValue, "Element " << name << " not found");
+    }
+    if (x->getType() != Element::boolean) {
+        isc_throw(BadValue, "Element " << name << " found, but is not a boolean");
+    }
+
+    return (x->boolValue());
+}
+
+const data::Element::Position&
+SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) const {
+    if (!parent) {
+        return (data::Element::ZERO_POSITION());
+    }
+    ConstElementPtr elem = parent->get(name);
+    if (!elem) {
+        return (data::Element::ZERO_POSITION());
+    }
+    return (elem->getPosition());
+}
+
+size_t SimpleParser::setDefaults(isc::data::ElementPtr scope,
+                                 const SimpleDefaults& default_values) {
+    size_t cnt = 0;
+
+    // This is the position representing a default value. As the values
+    // we're inserting here are not present in whatever the config file
+    // came from, we need to make sure it's clearly labeled as default.
+    const Element::Position pos("<default-value>", 0, 0);
+
+    // Let's go over all parameters we have defaults for.
+    BOOST_FOREACH(SimpleDefault def_value, default_values) {
+
+        // Try if such a parameter is there. If it is, let's
+        // skip it, because user knows best *cough*.
+        ConstElementPtr x = scope->get(string(def_value.name_));
+        if (x) {
+            // There is such a value already, skip it.
+            continue;
+        }
+
+        // There isn't such a value defined, let's create the default
+        // value...
+        switch (def_value.type_) {
+        case Element::string: {
+            x.reset(new StringElement(def_value.value_, pos));
+            break;
+        }
+        case Element::integer: {
+            int int_value = boost::lexical_cast<int>(def_value.value_);
+            x.reset(new IntElement(int_value, pos));
+            break;
+        }
+        case Element::boolean: {
+            bool bool_value;
+            if (def_value.value_ == string("true")) {
+                bool_value = true;
+            } else if (def_value.value_ == string("false")) {
+                bool_value = false;
+            } else {
+                isc_throw(BadValue, "Internal error. Boolean value specified as "
+                          << def_value.value_ << ", expected true or false");
+            }
+            x.reset(new BoolElement(bool_value, pos));
+            break;
+        }
+        case Element::real: {
+            double dbl_value = boost::lexical_cast<double>(def_value.value_);
+            x.reset(new DoubleElement(dbl_value, pos));
+            break;
+        }
+        default:
+            // No default values for null, list or map
+            isc_throw(BadValue, "Internal error. Incorrect default value type.");
+        }
+
+        // ... and insert it into the provided Element tree.
+        scope->set(def_value.name_, x);
+        cnt++;
+    }
+
+    return (cnt);
+}
+
+size_t SimpleParser::setGlobalDefaults(isc::data::ElementPtr global) {
+    return (setDefaults(global, GLOBAL_DEFAULTS));
+}
+
+size_t SimpleParser::setOptionDefaults(isc::data::ElementPtr option, bool v6) {
+    return (setDefaults(option, v6?OPTION6_DEFAULTS : OPTION4_DEFAULTS));
+}
+
+size_t SimpleParser::setOptionListDefaults(isc::data::ElementPtr option_list, bool v6) {
+    size_t cnt = 0;
+    BOOST_FOREACH(ElementPtr single_option, option_list->listValue()) {
+        cnt += setOptionDefaults(single_option, v6);
+    }
+    return (cnt);
+}
+
+size_t SimpleParser::setOptionDefDefaults(isc::data::ElementPtr option_def, bool v6) {
+    return (setDefaults(option_def, v6? OPTION6_DEF_DEFAULTS : OPTION4_DEF_DEFAULTS));
+}
+
+size_t SimpleParser::setOptionDefListDefaults(isc::data::ElementPtr option_def_list,
+                                              bool v6) {
+    size_t cnt = 0;
+    BOOST_FOREACH(ElementPtr single_def, option_def_list->listValue()) {
+        cnt += setOptionDefDefaults(single_def, v6);
+    }
+    return (cnt);
+}
+
+
+size_t SimpleParser::setAllDefaults(isc::data::ElementPtr global, bool v6) {
+    size_t cnt = 0;
+
+    // Set global defaults first.
+    //cnt = setGlobalDefaults(global);
+
+    // Now set option defintion defaults for each specified option definition
+    ConstElementPtr option_defs = global->get("option-def");
+    if (option_defs) {
+        BOOST_FOREACH(ElementPtr single_def, option_defs->listValue()) {
+            cnt += setOptionDefDefaults(single_def, v6);
+        }
+    }
+
+    ConstElementPtr options = global->get("option-data");
+    if (options) {
+        BOOST_FOREACH(ElementPtr single_option, options->listValue()) {
+            cnt += setOptionDefaults(single_option, v6);
+        }
+        //setOptionListDefaults(options);
+    }
+
+    return (cnt);
+}
+
+size_t
+SimpleParser::deriveParams(isc::data::ConstElementPtr parent,
+                           isc::data::ElementPtr child,
+                           const ParamsList& params) {
+    if ( (parent->getType() != Element::map) ||
+         (child->getType() != Element::map)) {
+        return (0);
+    }
+
+    size_t cnt = 0;
+    BOOST_FOREACH(string param, params) {
+        ConstElementPtr x = parent->get(param);
+        if (!x) {
+            // Parent doesn't define this parameter, so there's
+            // nothing to derive from
+            continue;
+        }
+
+        if (child->get(param)) {
+            // Child defines this parameter already. There's
+            // nothing to do here.
+            continue;
+        }
+
+        // Copy the parameters to the child scope.
+        child->set(param, x);
+        cnt++;
+    }
+
+    return (cnt);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace

+ 189 - 0
src/lib/dhcpsrv/parsers/simple_parser.h

@@ -0,0 +1,189 @@
+#ifndef SIMPLE_PARSER_H
+#define SIMPLE_PARSER_H
+
+#include <cc/data.h>
+#include <string>
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// This array defines a single entry of default values
+struct SimpleDefault {
+    SimpleDefault(const char* name, isc::data::Element::types type, const char* value)
+        :name_(name), type_(type), value_(value) {}
+    std::string name_;
+    const isc::data::Element::types type_;
+    const char* value_;
+};
+
+/// This specifies all default values in a given scope (e.g. a subnet)
+typedef std::vector<SimpleDefault> SimpleDefaults;
+
+/// This defines a list of all parameters that are derived (or inherited) between
+/// contexts
+typedef std::vector<std::string> ParamsList;
+
+
+/// @brief A simple parser
+///
+/// This class is intended to be a simpler replacement for @ref DhcpConfigParser.
+/// The simplification comes from several factors:
+/// - no build/commit nonsense. There's a single step:
+///   CfgStorage parse(ConstElementPtr json)
+///   that converts JSON configuration into an object and returns it.
+/// - almost no state kept. The only state kept in most cases is whether the
+///   parsing is done in v4 or v6 context. This greatly simplifies the
+///   parsers (no contexts, no child parsers list, no separate storage for
+///   uint32, strings etc. In fact, there's so little state kept, that this
+///   parser is mostly a collection of static methods.
+/// - no optional parameters (all are mandatory). This simplifies the parser,
+///   but introduces a new step before parsing where we insert the default
+///   values into client configuration before parsing. This is actually a good
+///   thing, because we now have a clear picture of the default parameters as
+///   they're defined in a single place (the DhcpConfigParser had the defaults
+///   spread out in multiple files in multiple directories).
+class SimpleParser {
+ public:
+
+    /// @brief Derives (inherits) parameters from parent scope to a child
+    ///
+    /// This method derives parameters from the parent scope to the child,
+    /// if there are no values specified in the child scope. For example,
+    /// this method can be used to derive timers from global scope (e.g. for
+    /// the whole DHCPv6 server) to a subnet scope. This method checks
+    /// if the child scope doesn't have more specific values defined. If
+    /// it doesn't, then the value from parent scope is copied over.
+    ///
+    /// @param parent scope to copy from (e.g. global)
+    /// @param child scope to copy from (e.g. subnet)
+    /// @param params names of the parameters to copy
+    /// @return number of parameters copied
+    static size_t deriveParams(isc::data::ConstElementPtr parent,
+                               isc::data::ElementPtr child,
+                               const ParamsList& params);
+
+    /// @brief Sets the default values
+    ///
+    /// This method sets the default values for parameters that are not
+    /// defined. The list of default values is specified by default_values.
+    /// If not present, those will be inserted into the scope. If
+    /// a parameter is already present, the default value will not
+    /// be inserted.
+    ///
+    /// @param scope default values will be inserted here
+    /// @param default_values list of default values
+    /// @return number of parameters inserted
+    static size_t setDefaults(isc::data::ElementPtr scope,
+                              const SimpleDefaults& default_values);
+
+    /// @brief Sets global defaults
+    ///
+    /// This method sets global defaults. Note it does not set the
+    /// defaults for any scopes lower than global. See @ref setAllDefaults
+    /// for that.
+    ///
+    /// @param global scope to be filled in with defaults.
+    /// @return number of default values added
+    static size_t setGlobalDefaults(isc::data::ElementPtr global);
+
+    /// @brief Sets option defaults for a single option
+    ///
+    /// This method sets default values for a single option.
+    ///
+    /// @param option an option data to be filled in with defaults.
+    /// @param v6 is it v6 (true) or v4 (false) option?
+    /// @return number of default values added
+    static size_t setOptionDefaults(isc::data::ElementPtr option, bool v6);
+
+    /// @brief Sets option defaults for the whole options list
+    ///
+    /// This method sets default values for a list of options.
+    ///
+    /// @param option_list an option data to be filled in with defaults.
+    /// @param v6 is it v6 (true) or v4 (false) option?
+    /// @return number of default values added
+    static size_t setOptionListDefaults(isc::data::ElementPtr option_list, bool v6);
+
+    /// @brief Sets option defaults for a single option definition
+    ///
+    /// This method sets default values for a single option definition.
+    ///
+    /// @param option_def an option defintion to be filled in with defaults.
+    /// @param v6 is it v6 (true) or v4 (false) option defintion?
+    /// @return number of default values added
+    static size_t setOptionDefDefaults(isc::data::ElementPtr option_def, bool v6);
+
+    /// @brief Sets option defaults for the whole option definitions list
+    ///
+    /// This method sets default values for a list of option definitions.
+    ///
+    /// @param option_def_list a list of option defintions to be filled in with defaults.
+    /// @param v6 is it v6 (true) or v4 (false) option?
+    /// @return number of default values added
+    static size_t setOptionDefListDefaults(isc::data::ElementPtr option_def_list,
+                                           bool v6);
+
+    /// @brief Sets defaults for the whole configuration
+    ///
+    /// This method sets global, options and option definition defaults. Note
+    /// it does set the defaults for scopes lower than global. If you want to
+    /// set the global defaults only, see @ref setGlobalDefaults for that.
+    ///
+    /// @param global scope to be filled in with defaults.
+    /// @return number of default values added
+    static size_t setAllDefaults(isc::data::ElementPtr global, bool v6);
+
+    /// @brief Utility method that returns position of an element
+    ///
+    /// It's mostly useful for logging.
+    ///
+    /// @param name position of that element will be returned
+    /// @param parent parent element (optional)
+    /// @return position of the element specified.
+    const data::Element::Position&
+    getPosition(const std::string& name, const data::ConstElementPtr parent =
+                data::ConstElementPtr()) const;
+
+    /// @destructor
+    ///
+    /// It's really simple, isn't it?
+    virtual ~SimpleParser() {}
+
+ protected:
+
+    /// @brief Returns a string parameter from a scope
+    ///
+    /// Unconditionally returns a parameter. If the parameter is not there or
+    /// is not of appropriate type, BadValue exception is thrown.
+    ///
+    /// @param scope specified parameter will be extracted from this scope
+    /// @param name name of the parameter
+    /// @return a string value of the parameter
+    static std::string getString(isc::data::ConstElementPtr scope, const std::string& name);
+
+    /// @brief Returns an integer parameter from a scope
+    ///
+    /// Unconditionally returns a parameter. If the parameter is not there or
+    /// is not of appropriate type, BadValue exception is thrown.
+    ///
+    /// @param scope specified parameter will be extracted from this scope
+    /// @param name name of the parameter
+    /// @return an integer value of the parameter
+    static int64_t getInteger(isc::data::ConstElementPtr scope, const std::string& name);
+
+    /// @brief Returns a boolean parameter from a scope
+    ///
+    /// Unconditionally returns a parameter. If the parameter is not there or
+    /// is not of appropriate type, BadValue exception is thrown.
+    ///
+    /// @param scope specified parameter will be extracted from this scope
+    /// @param name name of the parameter
+    /// @return a boolean value of the parameter
+    static bool getBoolean(isc::data::ConstElementPtr scope, const std::string& name);
+};
+
+};
+};
+
+#endif

+ 48 - 27
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -16,6 +16,7 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/cfg_mac_source.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
+#include <dhcpsrv/parsers/simple_parser.h>
 #include <dhcpsrv/tests/test_libraries.h>
 #include <dhcpsrv/testutils/config_result_check.h>
 #include <exceptions/exceptions.h>
@@ -292,7 +293,7 @@ public:
     /// @return returns an ConstElementPtr containing the numeric result
     /// code and outcome comment.
     isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
-                                           config_set) {
+                                               config_set) {
         // Answer will hold the result.
         ConstElementPtr answer;
         if (!config_set) {
@@ -310,6 +311,16 @@ public:
             const std::map<std::string, ConstElementPtr>& values_map =
                                                       config_set->mapValue();
             BOOST_FOREACH(config_pair, values_map) {
+
+                // These are the simple parsers. No need to go through
+                // the ParserPtr hooplas with them.
+                if ( (config_pair.first == "option-data") ||
+                     (config_pair.first == "option-def")) {
+                    continue;
+                }
+                // Remaining ones are old style parsers. Need go do
+                // the build/commit dance with them.
+
                 // Create the parser based on element name.
                 ParserPtr parser(createConfigParser(config_pair.first));
                 // Options must be parsed last
@@ -322,12 +333,27 @@ public:
                 }
             }
 
+            int family = parser_context_->universe_ == Option::V4
+                ? AF_INET : AF_INET6;
+
+            // The option definition parser is the next one to be run.
+            std::map<std::string, ConstElementPtr>::const_iterator
+                                def_config = values_map.find("option-def");
+            if (def_config != values_map.end()) {
+
+                CfgOptionDefPtr cfg_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
+                OptionDefListParser def_list_parser(family);
+                def_list_parser.parse(cfg_def, def_config->second);
+            }
+
             // The option values parser is the next one to be run.
             std::map<std::string, ConstElementPtr>::const_iterator
                                 option_config = values_map.find("option-data");
             if (option_config != values_map.end()) {
-                option_parser->build(option_config->second);
-                option_parser->commit();
+                CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
+
+                OptionDataListParser option_list_parser(family);
+                option_list_parser.parse(cfg_option, option_config->second);
             }
 
             // Everything was fine. Configuration is successful.
@@ -359,17 +385,9 @@ public:
     /// @throw throws NotImplemented if element name isn't supported.
     ParserPtr createConfigParser(const std::string& config_id) {
         ParserPtr parser;
-        int family = parser_context_->universe_ == Option::V4 ?
-            AF_INET : AF_INET6;
-        if (config_id.compare("option-data") == 0) {
-            parser.reset(new OptionDataListParser(config_id, CfgOptionPtr(),
-                                                  family));
-
-        } else if (config_id.compare("option-def") == 0) {
-            parser.reset(new OptionDefListParser(config_id,
-                                                 parser_context_));
-
-        } else if (config_id.compare("hooks-libraries") == 0) {
+        // option-data and option-def converted to SimpleParser, so they
+        // are no longer here.
+        if (config_id.compare("hooks-libraries") == 0) {
             parser.reset(new HooksLibrariesParser(config_id));
             hooks_libraries_parser_ =
                 boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
@@ -392,13 +410,15 @@ public:
     ///
     /// @return retuns 0 if the configuration parsed successfully,
     /// non-zero otherwise failure.
-    int parseConfiguration(const std::string& config) {
+    int parseConfiguration(const std::string& config, bool v6 = false) {
         int rcode_ = 1;
         // Turn config into elements.
         // Test json just to make sure its valid.
         ElementPtr json = Element::fromJSON(config);
         EXPECT_TRUE(json);
         if (json) {
+            SimpleParser::setAllDefaults(json, v6);
+
             ConstElementPtr status = parseElementSet(json);
             ConstElementPtr comment = parseAnswer(rcode_, status);
             error_text_ = comment->stringValue();
@@ -566,7 +586,7 @@ TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) {
         "}";
 
     // Verify that the configuration string parses.
-    int rcode = parseConfiguration(config);
+    int rcode = parseConfiguration(config, true);
     ASSERT_EQ(0, rcode);
 
 
@@ -828,7 +848,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
         " } ]"
         "}";
     int rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_NE(0, rcode);
 
     CfgMgr::instance().clear();
@@ -844,7 +864,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
         "    \"data\": \"0\""
         " } ]"
         "}";
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_NE(0, rcode);
 
     CfgMgr::instance().clear();
@@ -859,7 +879,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
         "    \"data\": \"0\""
         " } ]"
         "}";
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     ASSERT_EQ(0, rcode);
     OptionPtr opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
     ASSERT_TRUE(opt);
@@ -875,10 +895,11 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
         "    \"name\": \"foo-name\","
         "    \"space\": \"dhcp6\","
         "    \"code\": 25000,"
+        "    \"csv-format\": false,"
         "    \"data\": \"123456\""
         " } ]"
         "}";
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
     ASSERT_TRUE(opt);
@@ -899,7 +920,7 @@ TEST_F(ParseConfigTest, optionDataNoName) {
         " } ]"
         "}";
     int rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
         Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
@@ -919,7 +940,7 @@ TEST_F(ParseConfigTest, optionDataNoCode) {
         " } ]"
         "}";
     int rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
         Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
@@ -938,7 +959,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
         " } ]"
         "}";
     int rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
         Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
@@ -955,7 +976,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
         " } ]"
         "}";
     rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
                                                                    23));
@@ -984,7 +1005,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
         "}";
 
     int rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
         Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 2345));
@@ -1010,7 +1031,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
         "}";
 
     rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
                                                                    2345));
@@ -1031,7 +1052,7 @@ TEST_F(ParseConfigTest, emptyOptionData) {
         "}";
 
     int rcode = 0;
-    ASSERT_NO_THROW(rcode = parseConfiguration(config));
+    ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
     EXPECT_EQ(0, rcode);
     const Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
         Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, D6O_DHCPV4_O_DHCPV6_SERVER));