]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5110] Entire D2 configuration is now handled through new parsing scheme
authorThomas Markwalder <tmark@isc.org>
Tue, 31 Jan 2017 20:28:22 +0000 (15:28 -0500)
committerThomas Markwalder <tmark@isc.org>
Tue, 31 Jan 2017 20:28:22 +0000 (15:28 -0500)
src/bin/d2/d2_cfg_mgr.h
src/bin/d2/d2_cfg_mgr.cc
    "forward-ddns" and "reverse-ddns" are now parsed using SimpleParsers

src/bin/d2/d2_config.h
src/bin/d2/d2_config.cc
    TSIGKeyInfoParser converted to flat style
    DnsServerInfoParser, DnsServerInfoListParser, DdnsDomainParser,
    DdnsDomainListParser, DdnsDomainListMgrParser
    - now all derive from SimpleParser

src/bin/d2/d2_lexer.ll
    Added sub-grammar/contexts for PARSER_TSIG_KEY, PARSER_DDNS_DOMAIN,
    PARSER_DNS_SERVER:

src/bin/d2/d2_parser.yy
    Added "false" tokens for sub-contexts:
        SUB_TSIG_KEY, SUB_TSIG_KEYS, SUB_DDNS_DOMAIN,SUB_DDNS_DOMAINS,
        SUB_DNS_SERVER, SUB_DNS_SERVERS

src/bin/d2/d2_simple_parser.h
src/bin/d2/d2_simple_parser.cc
    Added Defaults:
        TSIG_KEY_DEFAULTS, DDNS_DOMAIN_MGR_DEFAULTS, DDNS_DOMAIN_DEFAULTS,
        DNS_SERVER_DEFAULTS

    D2SimpleParser::setDdnsDomainDefaults()
    D2SimpleParser::setManagerDefaults() - new methods to handle setting
    scalar defaults and defaults of child elements

    D2SimpleParser::setAllDefaults() - now sets defaults for entire D2
    config

parser_context.h
parser_context.cc
    Added parser types:
        PARSER_SUB_DHCPDDNS,
        PARSER_TSIG_KEY,
        PARSER_TSIG_KEYS,
        PARSER_DDNS_DOMAIN,
        PARSER_DDNS_DOMAINS,
        PARSER_DNS_SERVER,
        PARSER_DNS_SERVERS

src/bin/d2/tests/d2_cfg_mgr_unittests.h
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
    Moved all component SimpleParser tests to d2_simple_parser_unittest.cc/h
    Revamped D2CfgMgrTest

src/bin/d2/tests/d2_simple_parser_unittest.h
src/bin/d2/tests/d2_simple_parser_unittest.cc
    Moved component SimpleParser tests here.

src/bin/d2/tests/nc_test_utils.h
src/bin/d2/tests/nc_test_utils.cc
    Moved valid_d2_config global from lib/process/tests to here

src/bin/d2/tests/testdata/d2_cfg_tests.json
    Updated format and error messages

src/lib/process/testutils/d_test_stubs.h
src/lib/process/testutils/d_test_stubs.cc
    Removed valid_d2_config

22 files changed:
src/bin/d2/d2_cfg_mgr.cc
src/bin/d2/d2_cfg_mgr.h
src/bin/d2/d2_config.cc
src/bin/d2/d2_config.h
src/bin/d2/d2_lexer.cc
src/bin/d2/d2_lexer.ll
src/bin/d2/d2_parser.cc
src/bin/d2/d2_parser.h
src/bin/d2/d2_parser.yy
src/bin/d2/d2_simple_parser.cc
src/bin/d2/d2_simple_parser.h
src/bin/d2/parser_context.cc
src/bin/d2/parser_context.h
src/bin/d2/tests/d2_cfg_mgr_unittests.cc
src/bin/d2/tests/d2_controller_unittests.cc
src/bin/d2/tests/d2_process_unittests.cc
src/bin/d2/tests/d2_simple_parser_unittest.cc
src/bin/d2/tests/nc_test_utils.cc
src/bin/d2/tests/nc_test_utils.h
src/bin/d2/tests/testdata/d2_cfg_tests.json
src/lib/process/testutils/d_test_stubs.cc
src/lib/process/testutils/d_test_stubs.h

index d9d8920dc482655e526f548d6dd337e67824a4be..7c1a5974eff4d7fc44b5046b511da054d5784861 100644 (file)
@@ -268,6 +268,16 @@ D2CfgMgr::parseElement(const std::string& element_id,
         } else if (element_id == "tsig-keys") {
             TSIGKeyInfoListParser parser;
             getD2CfgContext()->setKeys(parser.parse(element));
+        } else if (element_id ==  "forward-ddns") {
+            DdnsDomainListMgrParser parser;
+            DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
+                                                    context->getKeys());
+            context->setForwardMgr(mgr);
+        } else if (element_id ==  "reverse-ddns") {
+            DdnsDomainListMgrParser parser;
+            DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
+                                                    context->getKeys());
+            context->setReverseMgr(mgr);
         } else {
             // not something we handle here
             return (false);
@@ -357,29 +367,14 @@ D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
     getD2CfgContext()->getD2Params() = params;
 }
 
-isc::dhcp::ParserPtr
+dhcp::ParserPtr
 D2CfgMgr::createConfigParser(const std::string& config_id,
                              const isc::data::Element::Position& pos) {
-    // Get D2 specific context.
-    D2CfgContextPtr context = getD2CfgContext();
-
-    // Create parser instance based on element_id.
-    isc::dhcp::ParserPtr parser;
-    if (config_id ==  "forward-ddns") {
-        parser.reset(new DdnsDomainListMgrParser("forward-ddns",
-                                                 context->getForwardMgr(),
-                                                 context->getKeys()));
-    } else if (config_id ==  "reverse-ddns") {
-        parser.reset(new DdnsDomainListMgrParser("reverse-ddns",
-                                                 context->getReverseMgr(),
-                                                 context->getKeys()));
-    } else {
-        isc_throw(NotImplemented,
-                  "parser error: D2CfgMgr parameter not supported : "
-                  " (" << config_id << pos << ")");
-    }
+    isc_throw(NotImplemented,
+              "parser error: D2CfgMgr parameter not supported : "
+               " (" << config_id << pos << ")");
 
-    return (parser);
+    return (dhcp::ParserPtr());
 }
 
 }; // end of isc::dhcp namespace
index 96f7b3de230c7092d274ec3c91541c1b1d5602b6..d96523f75b180af41be9777dae7f805f2107ec4b 100644 (file)
@@ -57,6 +57,12 @@ public:
         return (forward_mgr_);
     }
 
+    /// @brief Sets the forward domain list manager
+    /// @param pointer to the new forward manager
+    void setForwardMgr(DdnsDomainListMgrPtr forward_mgr) {
+        forward_mgr_ = forward_mgr;
+    }
+
     /// @brief Fetches the reverse DNS domain list manager.
     ///
     /// @return returns a pointer to the reverse manager.
@@ -64,6 +70,12 @@ public:
         return (reverse_mgr_);
     }
 
+    /// @brief Sets the reverse domain list manager
+    /// @param pointer to the new reverse manager
+    void setReverseMgr(DdnsDomainListMgrPtr reverse_mgr) {
+        reverse_mgr_ = reverse_mgr;
+    }
+
     /// @brief Fetches the map of TSIG keys.
     ///
     /// @return returns a pointer to the key map.
@@ -71,9 +83,9 @@ public:
         return (keys_);
     }
 
-    /// @brief Sets the may of TSIG keys
+    /// @brief Sets the mapy of TSIG keys
     ///
-    /// @param new of TSIG keys
+    /// @param pointer to the new TSIG key map
     void setKeys(const TSIGKeyInfoMapPtr& keys) {
         keys_ = keys;
     }
index f223b7a561c60fa03930be0ea393587a65733f5b..090db6696ce6418e5ee15fc7568432461d29b200 100644 (file)
@@ -13,7 +13,6 @@
 #include <asiolink/io_error.h>
 
 #include <boost/foreach.hpp>
-#include <boost/lexical_cast.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 
@@ -27,11 +26,13 @@ namespace d2 {
 
 // *********************** D2Params  *************************
 
-const char *D2Params::DFT_IP_ADDRESS = "127.0.0.1";
-const char *D2Params::DFT_PORT = "53001";
-const char *D2Params::DFT_DNS_SERVER_TIMEOUT = "100";
-const char *D2Params::DFT_NCR_PROTOCOL = "UDP";
-const char *D2Params::DFT_NCR_FORMAT = "JSON";
+const char*     D2Params::DFT_IP_ADDRESS = "127.0.0.1";
+const uint32_t  D2Params::DFT_PORT = 53001;
+const char*     D2Params::DFT_PORT_STR = "53001";
+const uint32_t  D2Params::DFT_DNS_SERVER_TIMEOUT = 100;
+const char*     D2Params::DFT_DNS_SERVER_TIMEOUT_STR = "100";
+const char*     D2Params::DFT_NCR_PROTOCOL = "UDP";
+const char*     D2Params::DFT_NCR_FORMAT = "JSON";
 
 D2Params::D2Params(const isc::asiolink::IOAddress& ip_address,
                    const size_t port,
@@ -48,8 +49,7 @@ D2Params::D2Params(const isc::asiolink::IOAddress& ip_address,
 
 D2Params::D2Params()
     : ip_address_(isc::asiolink::IOAddress(DFT_IP_ADDRESS)),
-     port_(boost::lexical_cast<size_t>(DFT_PORT)),
-     dns_server_timeout_(boost::lexical_cast<size_t>(DFT_DNS_SERVER_TIMEOUT)),
+     port_(DFT_PORT), dns_server_timeout_(DFT_DNS_SERVER_TIMEOUT),
      ncr_protocol_(dhcp_ddns::NCR_UDP),
      ncr_format_(dhcp_ddns::FMT_JSON) {
     validateContents();
@@ -190,6 +190,7 @@ TSIGKeyInfo::remakeKey() {
 
 // *********************** DnsServerInfo  *************************
 
+const char* DnsServerInfo::STANDARD_DNS_PORT_STR = "53";
 const char* DnsServerInfo::EMPTY_IP_STR = "0.0.0.0";
 
 DnsServerInfo::DnsServerInfo(const std::string& hostname,
@@ -337,105 +338,37 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
 // *********************** TSIGKeyInfoParser  *************************
 
 TSIGKeyInfoPtr
-TSIGKeyInfoParser::parse(isc::data::ConstElementPtr key_config) {
-    std::string name;
-    std::string algorithm;
-    uint32_t digestbits = 0;
-    std::string secret;
-
-    isc::data::ConstElementPtr name_elem;
-    isc::data::ConstElementPtr algo_elem;
-    isc::data::ConstElementPtr bits_elem;
-    isc::data::ConstElementPtr secret_elem;
-
-    BOOST_FOREACH(isc::dhcp::ConfigPair param, key_config->mapValue()) {
-        std::string entry(param.first);
-        isc::data::ConstElementPtr value(param.second);
-        try {
-            if (entry == "name") {
-                name = value->stringValue();
-                if (name.empty()) {
-                    isc_throw(D2CfgError, "tsig-key: name cannot be blank"
-                              << " (" << value->getPosition() << ")");
-                }
-                name_elem = value;
-            } else if (entry == "algorithm") {
-                algorithm = value->stringValue();
-                // Algorithm must be valid.
-                try {
-                    TSIGKeyInfo::stringToAlgorithmName(algorithm);
-                } catch (const std::exception& ex) {
-                    isc_throw(D2CfgError, "tsig-key : " << ex.what()
-                        << " (" << value->getPosition() << ")");
-                }
-                algo_elem = value;
-            } else if (entry == "digest-bits") {
-                digestbits = value->intValue();
-                bits_elem = value;
-            } else if (entry == "secret") {
-                secret = value->stringValue();
-                // Secret cannot be blank.
-                // Cryptolink lib doesn't offer any way to validate these.
-                // As long as it isn't blank we'll accept it. If the content
-                // is bad, the call to TSIGKeyInfo::remakeKey() made in
-                // the TSIGKeyInfo ctor below will throw.
-                if (secret.empty()) {
-                    isc_throw(D2CfgError, "tsig-key: secret cannot be blank ("
-                              << value->getPosition() << ")");
-                }
-                secret_elem = value;
-            } else {
-                isc_throw(D2CfgError, "tsig-key: unsupported parameter '"
-                          << entry << " (" << value->getPosition() << ")");
-            }
-        } catch (const isc::data::TypeError&) {
-            isc_throw(D2CfgError,
-                      "tsig-key: invalid value type specified for parameter '"
-                      << entry << " (" << value->getPosition() << ")");
-        }
-    }
+TSIGKeyInfoParser::parse(data::ConstElementPtr key_config) {
+    std::string name = getString(key_config, "name");
+    std::string algorithm = getString(key_config, "algorithm");
+    uint32_t digestbits = getInteger(key_config, "digest-bits");
+    std::string secret = getString(key_config, "secret");
 
-    if (!name_elem) {
-        isc_throw(D2CfgError, "tsig-key: must specify name"
-                  << " (" << key_config->getPosition() << ")");
-    }
-
-    if (!algo_elem) {
-        isc_throw(D2CfgError,
-                  "tsig-key: must specify algorithm"
-                  << " (" << key_config->getPosition() << ")");
-    }
-
-    if (!secret_elem) {
-        isc_throw(D2CfgError,
-                  "tsig-key: must specify secret"
-                  << " (" << key_config->getPosition() << ")");
+    // Algorithm must be valid.
+    try {
+        TSIGKeyInfo::stringToAlgorithmName(algorithm);
+    } catch (const std::exception& ex) {
+        isc_throw(D2CfgError, "tsig-key : " << ex.what()
+                  << " (" << getPosition("algorithm", key_config) << ")");
     }
 
     // Non-zero digest-bits must be an integral number of octets, greater
-    // than 80 and at least half of the algorithm key length.
-    if (digestbits > 0) {
-        if ((digestbits % 8) != 0) {
-            isc_throw(D2CfgError,
-                      "tsig-key: digest bits must be a multiple of 8"
-                       << " (" << bits_elem->getPosition() << ")");
+    // than 80 and at least half of the algorithm key length. It defaults
+    // to zero and JSON parsing ensures it's a multiple of 8.
+    if ((digestbits > 0) &&
+        ((digestbits < 80) ||
+         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR)
+          && (digestbits < 112)) ||
+         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR)
+          && (digestbits < 128)) ||
+         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA384_STR)
+          && (digestbits < 192)) ||
+         (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA512_STR)
+          && (digestbits < 256)))) {
+        isc_throw(D2CfgError, "tsig-key: digest-bits too small : "
+                  << " (" << getPosition("digest-bits", key_config) << ")");
         }
 
-        if ((digestbits < 80) ||
-            (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR)
-             && (digestbits < 112)) ||
-            (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR)
-             && (digestbits < 128)) ||
-            (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA384_STR)
-             && (digestbits < 192)) ||
-            (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA512_STR)
-                   && (digestbits < 256))) {
-                isc_throw(D2CfgError, "tsig-key: digest-bits too small : "
-                          << " (" << bits_elem->getPosition() << ")");
-        }
-    }
-
-
     // Everything should be valid, so create the key instance.
     // It is possible for the asiodns::dns::TSIGKey create to fail such as
     // with an invalid secret content.
@@ -453,9 +386,9 @@ TSIGKeyInfoParser::parse(isc::data::ConstElementPtr key_config) {
 // *********************** TSIGKeyInfoListParser  *************************
 
 TSIGKeyInfoMapPtr
-TSIGKeyInfoListParser::parse(isc::data::ConstElementPtr key_list) {
+TSIGKeyInfoListParser::parse(data::ConstElementPtr key_list) {
     TSIGKeyInfoMapPtr keys(new TSIGKeyInfoMap());
-    isc::data::ConstElementPtr key_config;
+    data::ConstElementPtr key_config;
     BOOST_FOREACH(key_config, key_list->listValue()) {
         TSIGKeyInfoParser key_parser;
         TSIGKeyInfoPtr key = key_parser.parse(key_config);
@@ -475,48 +408,11 @@ TSIGKeyInfoListParser::parse(isc::data::ConstElementPtr key_list) {
 
 // *********************** DnsServerInfoParser  *************************
 
-DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
-    DnsServerInfoStoragePtr servers)
-    : entry_name_(entry_name), servers_(servers), local_scalars_() {
-    if (!servers_) {
-        isc_throw(D2CfgError, "DnsServerInfoParser ctor:"
-                  " server storage cannot be null");
-    }
-}
-
-DnsServerInfoParser::~DnsServerInfoParser() {
-}
-
-void
-DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
-    isc::dhcp::ConfigPair config_pair;
-    // For each element in the server configuration:
-    // 1. Create a parser for the element.
-    // 2. Invoke the parser's build method passing in the element's
-    // configuration.
-    // 3. Invoke the parser's commit method to store the element's parsed
-    // data to the parser's local storage.
-    BOOST_FOREACH (config_pair, server_config->mapValue()) {
-        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                                       config_pair.second->
-                                                       getPosition()));
-        parser->build(config_pair.second);
-        parser->commit();
-    }
-
-    std::string hostname;
-    std::string ip_address;
-    uint32_t port = DnsServerInfo::STANDARD_DNS_PORT;
-    std::map<std::string, isc::data::Element::Position> pos;
-
-    // Fetch the server configuration's parsed scalar values from parser's
-    // local storage.  They're all optional, so no try-catch here.
-    pos["hostname"] = local_scalars_.getParam("hostname", hostname,
-                                              DCfgContextBase::OPTIONAL);
-    pos["ip-address"] = local_scalars_.getParam("ip-address", ip_address,
-                                                DCfgContextBase::OPTIONAL);
-    pos["port"] =  local_scalars_.getParam("port", port,
-                                           DCfgContextBase::OPTIONAL);
+DnsServerInfoPtr
+DnsServerInfoParser::parse(data::ConstElementPtr server_config) {
+    std::string hostname = getString(server_config, "hostname");
+    std::string ip_address = getString(server_config, "ip-address");
+    uint32_t port = getInteger(server_config, "port");
 
     // The configuration must specify one or the other.
     if (hostname.empty() == ip_address.empty()) {
@@ -525,13 +421,7 @@ DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
                   << " (" << server_config->getPosition() << ")");
     }
 
-    // Port cannot be zero.
-    if (port == 0) {
-        isc_throw(D2CfgError, "Dns Server : port cannot be 0"
-                  << " (" << pos["port"] << ")");
-    }
-
-    DnsServerInfoPtr serverInfo;
+    DnsServerInfoPtr server_info;
     if (!hostname.empty()) {
         /// @todo when resolvable hostname is supported we create the entry
         /// as follows:
@@ -548,172 +438,52 @@ DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
         /// processing.
         /// Until then we'll throw unsupported.
         isc_throw(D2CfgError, "Dns Server : hostname is not yet supported"
-                  << " (" << pos["hostname"] << ")");
+                  << " (" << getPosition("hostname", server_config) << ")");
     } else {
         try {
             // Create an IOAddress from the IP address string given and then
             // create the DnsServerInfo.
             isc::asiolink::IOAddress io_addr(ip_address);
-            serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
+            server_info.reset(new DnsServerInfo(hostname, io_addr, port));
         } catch (const isc::asiolink::IOError& ex) {
             isc_throw(D2CfgError, "Dns Server : invalid IP address : "
-                      << ip_address << " (" << pos["ip-address"] << ")");
+                      << ip_address
+                      << " (" << getPosition("ip-address", server_config) << ")");
         }
     }
 
-    // Add the new DnsServerInfo to the server storage.
-    servers_->push_back(serverInfo);
-}
-
-isc::dhcp::ParserPtr
-DnsServerInfoParser::createConfigParser(const std::string& config_id,
-                                        const isc::data::Element::
-                                        Position& pos) {
-    DhcpConfigParser* parser = NULL;
-    // Based on the configuration id of the element, create the appropriate
-    // parser. Scalars are set to use the parser's local scalar storage.
-    if ((config_id == "hostname")  ||
-        (config_id == "ip-address")) {
-        parser = new isc::dhcp::StringParser(config_id,
-                                             local_scalars_.getStringStorage());
-    } else if (config_id == "port") {
-        parser = new isc::dhcp::Uint32Parser(config_id,
-                                             local_scalars_.getUint32Storage());
-    } else {
-        isc_throw(NotImplemented,
-                  "parser error: DnsServerInfo parameter not supported: "
-                  << config_id << " (" << pos << ")");
-    }
-
-    // Return the new parser instance.
-    return (isc::dhcp::ParserPtr(parser));
-}
-
-void
-DnsServerInfoParser::commit() {
+    return(server_info);
 }
 
 // *********************** DnsServerInfoListParser  *************************
 
-DnsServerInfoListParser::DnsServerInfoListParser(const std::string& list_name,
-                                       DnsServerInfoStoragePtr servers)
-    :list_name_(list_name), servers_(servers), parsers_() {
-    if (!servers_) {
-        isc_throw(D2CfgError, "DdnsServerInfoListParser ctor:"
-                  " server storage cannot be null");
-    }
-}
-
-DnsServerInfoListParser::~DnsServerInfoListParser(){
-}
-
-void
-DnsServerInfoListParser::
-build(isc::data::ConstElementPtr server_list){
-    int i = 0;
-    isc::data::ConstElementPtr server_config;
-    // For each server element in the server list:
-    // 1. Create a parser for the server element.
-    // 2. Invoke the parser's build method passing in the server's
-    // configuration.
-    // 3. Add the parser to a local collection of parsers.
+DnsServerInfoStoragePtr
+DnsServerInfoListParser::parse(data::ConstElementPtr server_list) {
+    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
+    data::ConstElementPtr server_config;
+    DnsServerInfoParser parser;
     BOOST_FOREACH(server_config, server_list->listValue()) {
-        // Create a name for the parser based on its position in the list.
-        std::string entry_name = boost::lexical_cast<std::string>(i++);
-        isc::dhcp::ParserPtr parser(new DnsServerInfoParser(entry_name,
-                                                            servers_));
-        parser->build(server_config);
-        parsers_.push_back(parser);
-    }
-
-    // Domains must have at least one server.
-    if (parsers_.size() == 0) {
-        isc_throw (D2CfgError, "Server List must contain at least one server"
-                   << " (" << server_list->getPosition() << ")");
+        DnsServerInfoPtr server = parser.parse(server_config);
+        servers->push_back(server);
     }
-}
 
-void
-DnsServerInfoListParser::commit() {
-    // Invoke commit on each server parser.
-    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
-        parser->commit();
-    }
+    return (servers);
 }
 
 // *********************** DdnsDomainParser  *************************
 
-DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
-                                   DdnsDomainMapPtr domains,
-                                   TSIGKeyInfoMapPtr keys)
-    : entry_name_(entry_name), domains_(domains), keys_(keys),
-    local_servers_(new DnsServerInfoStorage()), local_scalars_() {
-    if (!domains_) {
-        isc_throw(D2CfgError,
-                  "DdnsDomainParser ctor, domain storage cannot be null");
-    }
-}
-
-
-DdnsDomainParser::~DdnsDomainParser() {
-}
-
-void
-DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
-    // For each element in the domain configuration:
-    // 1. Create a parser for the element.
-    // 2. Invoke the parser's build method passing in the element's
-    // configuration.
-    // 3. Invoke the parser's commit method to store the element's parsed
-    // data to the parser's local storage.
-    isc::dhcp::ConfigPair config_pair;
-    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
-        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                                       config_pair.second->
-                                                       getPosition()));
-        parser->build(config_pair.second);
-        parser->commit();
-    }
-
-    // Now construct the domain.
-    std::string name;
-    std::string key_name;
-    std::map<std::string, isc::data::Element::Position> pos;
-
-
-    // Fetch the parsed scalar values from parser's local storage.
-    // Any required that are missing will throw.
-    try {
-        pos["name"] = local_scalars_.getParam("name", name);
-        pos["key-name"] = local_scalars_.getParam("key-name", key_name,
-                                                  DCfgContextBase::OPTIONAL);
-    } catch (const std::exception& ex) {
-        isc_throw(D2CfgError, "DdnsDomain incomplete : " << ex.what()
-                  << " (" << domain_config->getPosition() << ")");
-    }
-
-    // Blank domain names are not allowed.
-    if (name.empty()) {
-        isc_throw(D2CfgError, "DndsDomain : name cannot be blank ("
-                   << pos["name"] << ")");
-    }
-
-    // Currently, the premise is that domain storage is always empty
-    // prior to parsing so always adding domains never replacing them.
-    // Duplicates are not allowed and should be flagged as a configuration
-    // error.
-    if (domains_->find(name) != domains_->end()) {
-        isc_throw(D2CfgError, "Duplicate domain specified:" << name
-                  << " (" << pos["name"] << ")");
-    }
+DdnsDomainPtr DdnsDomainParser::parse(data::ConstElementPtr domain_config,
+                                      const TSIGKeyInfoMapPtr keys) {
+    std::string name = getString(domain_config, "name");
+    std::string key_name = getString(domain_config, "key-name");
 
     // Key name is optional. If it is not blank, then find the key in the
-    /// list of defined keys.
+    // list of defined keys.
     TSIGKeyInfoPtr tsig_key_info;
     if (!key_name.empty()) {
-        if (keys_) {
-            TSIGKeyInfoMap::iterator kit = keys_->find(key_name);
-            if (kit != keys_->end()) {
+        if (keys) {
+            TSIGKeyInfoMap::iterator kit = keys->find(key_name);
+            if (kit != keys->end()) {
                 tsig_key_info = kit->second;
             }
         }
@@ -721,147 +491,75 @@ DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
         if (!tsig_key_info) {
             isc_throw(D2CfgError, "DdnsDomain : " << name
                       << " specifies an undefined key: " << key_name
-                      << " (" << pos["key-name"] << ")");
+                      << " (" << getPosition("key-name", domain_config) << ")");
         }
     }
 
-    // Instantiate the new domain and add it to domain storage.
-    DdnsDomainPtr domain(new DdnsDomain(name, local_servers_, tsig_key_info));
-
-    // Add the new domain to the domain storage.
-    (*domains_)[name] = domain;
-}
+    // Parse the list of DNS servers
+    data::ConstElementPtr servers_config;
+    try {
+        servers_config = domain_config->get("dns-servers");
+    } catch (const std::exception& ex) {
+        isc_throw(D2CfgError, "DdnsDomain : missing dns-server list"
+                      << " (" << servers_config->getPosition() << ")");
+    }
 
-isc::dhcp::ParserPtr
-DdnsDomainParser::createConfigParser(const std::string& config_id,
-                                     const isc::data::Element::Position& pos) {
-    DhcpConfigParser* parser = NULL;
-    // Based on the configuration id of the element, create the appropriate
-    // parser. Scalars are set to use the parser's local scalar storage.
-    if ((config_id == "name")  ||
-        (config_id == "key-name")) {
-        parser = new isc::dhcp::StringParser(config_id,
-                                             local_scalars_.getStringStorage());
-    } else if (config_id == "dns-servers") {
-       // Server list parser is given in our local server storage. It will pass
-       // this down to its server parsers and is where they will write their
-       // server instances upon commit.
-       parser = new DnsServerInfoListParser(config_id, local_servers_);
-    } else {
-       isc_throw(NotImplemented,
-                "parser error: DdnsDomain parameter not supported: "
-                << config_id << " (" << pos << ")");
+    DnsServerInfoListParser server_parser;
+    DnsServerInfoStoragePtr servers =  server_parser.parse(servers_config);
+    if (servers->size() == 0) {
+        isc_throw(D2CfgError, "DNS server list cannot be empty"
+                    << servers_config->getPosition());
     }
 
-    // Return the new domain parser instance.
-    return (isc::dhcp::ParserPtr(parser));
-}
+    // Instantiate the new domain and add it to domain storage.
+    DdnsDomainPtr domain(new DdnsDomain(name, servers, tsig_key_info));
 
-void
-DdnsDomainParser::commit() {
+    return(domain);
 }
 
 // *********************** DdnsDomainListParser  *************************
 
-DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
-                                           DdnsDomainMapPtr domains,
-                                           TSIGKeyInfoMapPtr keys)
-    :list_name_(list_name), domains_(domains), keys_(keys), parsers_() {
-    if (!domains_) {
-        isc_throw(D2CfgError, "DdnsDomainListParser ctor:"
-                  " domain storage cannot be null");
-    }
-}
+DdnsDomainMapPtr DdnsDomainListParser::parse(data::ConstElementPtr domain_list,
+                                             const TSIGKeyInfoMapPtr keys) {
+    DdnsDomainMapPtr domains(new DdnsDomainMap());
+    DdnsDomainParser parser;
+    data::ConstElementPtr domain_config;
+    BOOST_FOREACH(domain_config, domain_list->listValue()) {
+        DdnsDomainPtr domain = parser.parse(domain_config, keys);
 
-DdnsDomainListParser::~DdnsDomainListParser(){
-}
+        // Duplicates are not allowed
+        if (domains->find(domain->getName()) != domains->end()) {
+            isc_throw(D2CfgError, "Duplicate domain specified:"
+                      << domain->getName()
+                      << " (" << getPosition("name", domain_config) << ")");
+        }
 
-void
-DdnsDomainListParser::
-build(isc::data::ConstElementPtr domain_list){
-    // For each domain element in the domain list:
-    // 1. Create a parser for the domain element.
-    // 2. Invoke the parser's build method passing in the domain's
-    // configuration.
-    // 3. Add the parser to the local collection of parsers.
-    int i = 0;
-    isc::data::ConstElementPtr domain_config;
-    BOOST_FOREACH(domain_config, domain_list->listValue()) {
-        std::string entry_name = boost::lexical_cast<std::string>(i++);
-        isc::dhcp::ParserPtr parser(new DdnsDomainParser(entry_name,
-                                                         domains_, keys_));
-        parser->build(domain_config);
-        parsers_.push_back(parser);
+        (*domains)[domain->getName()] = domain;
     }
-}
 
-void
-DdnsDomainListParser::commit() {
-    // Invoke commit on each server parser. This will cause each one to
-    // create it's server instance and commit it to storage.
-    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
-        parser->commit();
-    }
+    return (domains);
 }
 
-
 // *********************** DdnsDomainListMgrParser  *************************
 
-DdnsDomainListMgrParser::DdnsDomainListMgrParser(const std::string& entry_name,
-                              DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys)
-    : entry_name_(entry_name), mgr_(mgr), keys_(keys),
-    local_domains_(new DdnsDomainMap()), local_scalars_() {
-}
+DdnsDomainListMgrPtr
+DdnsDomainListMgrParser::parse(data::ConstElementPtr mgr_config,
+                               const std::string& mgr_name,
+                               const TSIGKeyInfoMapPtr keys) {
+    DdnsDomainListMgrPtr mgr(new DdnsDomainListMgr(mgr_name));
 
+    // Parse the list of domains
+    data::ConstElementPtr domains_config = mgr_config->get("ddns-domains");
+    if (domains_config) {
+        DdnsDomainListParser domain_parser;
+        DdnsDomainMapPtr domains =  domain_parser.parse(domains_config, keys);
 
-DdnsDomainListMgrParser::~DdnsDomainListMgrParser() {
-}
-
-void
-DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
-    // For each element in the domain manager configuration:
-    // 1. Create a parser for the element.
-    // 2. Invoke the parser's build method passing in the element's
-    // configuration.
-    // 3. Invoke the parser's commit method to store the element's parsed
-    // data to the parser's local storage.
-    isc::dhcp::ConfigPair config_pair;
-    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
-        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
-                                                       config_pair.second->
-                                                       getPosition()));
-        parser->build(config_pair.second);
-        parser->commit();
-    }
-
-    // Add the new domain to the domain storage.
-    mgr_->setDomains(local_domains_);
-}
-
-isc::dhcp::ParserPtr
-DdnsDomainListMgrParser::createConfigParser(const std::string& config_id,
-                                            const isc::data::Element::
-                                            Position& pos) {
-    DhcpConfigParser* parser = NULL;
-    if (config_id == "ddns-domains") {
-       // Domain list parser is given our local domain storage. It will pass
-       // this down to its domain parsers and is where they will write their
-       // domain instances upon commit.
-       parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
-    } else {
-       isc_throw(NotImplemented, "parser error: "
-                 "DdnsDomainListMgr parameter not supported: " << config_id
-                 << " (" << pos << ")");
+        // Add the new domain to the domain storage.
+        mgr->setDomains(domains);
     }
 
-    // Return the new domain parser instance.
-    return (isc::dhcp::ParserPtr(parser));
-}
-
-void
-DdnsDomainListMgrParser::commit() {
+    return(mgr);
 }
 
-
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
index 60fd3973aaa43be89adfabb3563dca8fc47f95e1..e2a6b2d0902f31c35de64c0a610e54355251b7a2 100644 (file)
@@ -140,11 +140,13 @@ public:
 
     /// @brief Default configuration constants.
     //@{
-    static const char *DFT_IP_ADDRESS;
-    static const char *DFT_PORT;
-    static const char *DFT_DNS_SERVER_TIMEOUT;
-    static const char *DFT_NCR_PROTOCOL;
-    static const char *DFT_NCR_FORMAT;
+    static const char*      DFT_IP_ADDRESS;
+    static const uint32_t   DFT_PORT;
+    static const char*      DFT_PORT_STR;
+    static const uint32_t   DFT_DNS_SERVER_TIMEOUT;
+    static const char*      DFT_DNS_SERVER_TIMEOUT_STR;
+    static const char*      DFT_NCR_PROTOCOL;
+    static const char*      DFT_NCR_FORMAT;
     //@}
 
     /// @brief Constructor
@@ -420,14 +422,13 @@ typedef boost::shared_ptr<TSIGKeyInfoMap> TSIGKeyInfoMapPtr;
 /// updates.
 class DnsServerInfo {
 public:
-
     /// @brief defines DNS standard port value
     static const uint32_t STANDARD_DNS_PORT = 53;
+    static const char* STANDARD_DNS_PORT_STR;
 
     /// @brief defines an "empty" string version of an ip address.
     static const char* EMPTY_IP_STR;
 
-
     /// @brief Constructor
     ///
     /// @param hostname is the resolvable name of the server. If not blank,
@@ -732,7 +733,7 @@ typedef boost::shared_ptr<DScalarContext> DScalarContextPtr;
 ///
 /// This class parses the configuration element "tsig-key" defined in
 /// src/bin/d2/dhcp-ddns.spec and creates an instance of a TSIGKeyInfo.
-class TSIGKeyInfoParser : public  isc::data::SimpleParser { 
+class TSIGKeyInfoParser : public  data::SimpleParser { 
 public:
     /// @brief Constructor
     TSIGKeyInfoParser(){};    
@@ -748,7 +749,7 @@ public:
     /// @param key_config is the "tsig-key" configuration to parse
     ///
     /// @return pointer to the new TSIGKeyInfo instance
-    TSIGKeyInfoPtr parse(isc::data::ConstElementPtr key_config);
+    TSIGKeyInfoPtr parse(data::ConstElementPtr key_config);
 
 private:
 };
@@ -758,7 +759,7 @@ private:
 /// This class parses a list of "tsig-key" configuration elements.
 /// (see src/bin/d2/dhcp-ddns.spec). The TSIGKeyInfo instances are added
 /// to the given storage upon commit.
-class TSIGKeyInfoListParser : public isc::data::SimpleParser {
+class TSIGKeyInfoListParser : public data::SimpleParser {
 public:
     /// @brief Constructor
     TSIGKeyInfoListParser(){};
@@ -778,81 +779,36 @@ public:
     /// @param key_list_config is the list of "tsig_key" elements to parse.
     ///
     /// @return a map containing the TSIGKeyInfo instances 
-    TSIGKeyInfoMapPtr parse(isc::data::ConstElementPtr key_list_config);
+    TSIGKeyInfoMapPtr parse(data::ConstElementPtr key_list_config);
 };
 
 /// @brief Parser for  DnsServerInfo
 ///
 /// This class parses the configuration element "dns-server" defined in
 /// src/bin/d2/dhcp-ddns.spec and creates an instance of a DnsServerInfo.
-class DnsServerInfoParser : public  isc::dhcp::DhcpConfigParser {
+class DnsServerInfoParser : public  data::SimpleParser {
 public:
     /// @brief Constructor
-    ///
-    /// @param entry_name is an arbitrary label assigned to this configuration
-    /// definition. Since servers are specified in a list this value is likely
-    /// be something akin to "server:0", set during parsing.
-    /// @param servers is a pointer to the storage area to which the parser
-    /// should commit the newly created DnsServerInfo instance.
-    DnsServerInfoParser(const std::string& entry_name,
-                        DnsServerInfoStoragePtr servers);
+    DnsServerInfoParser(){};
 
     /// @brief Destructor
-    virtual ~DnsServerInfoParser();
+    virtual ~DnsServerInfoParser(){};
 
     /// @brief Performs the actual parsing of the given  "dns-server" element.
     ///
     /// Parses a configuration for the elements needed to instantiate a
     /// DnsServerInfo, validates those entries, creates a DnsServerInfo instance
-    /// then attempts to add to a list of  servers.
+    /// and returns it.
     ///
     /// @param server_config is the "dns-server" configuration to parse
     ///
+    /// @return a pointer to the newly created server instance
+    ///
     /// @throw D2CfgError if:
     /// -# hostname is not blank, hostname is not yet supported
     /// -# ip_address is invalid
     /// -# port is 0
-    virtual void build(isc::data::ConstElementPtr server_config);
-
-    /// @brief Creates a parser for the given "dns-server" member element id.
-    ///
-    /// The server elements currently supported are(see dhcp-ddns.spec):
-    ///   1. hostname
-    ///   2. ip_address
-    ///   3. port
-    ///
-    /// @param config_id is the "item_name" for a specific member element of
-    /// the "dns-server" specification.
-    /// @param pos position within the configuration text (or file) of element
-    /// to be parsed.  This is passed for error messaging.
-    ///
-    /// @return returns a pointer to newly created parser.
-    ///
-    /// @throw D2CfgError if configuration contains an unknown parameter
-    virtual isc::dhcp::ParserPtr
-    createConfigParser(const std::string& config_id,
-                       const isc::data::Element::Position& =
-                       isc::data::Element::ZERO_POSITION());
-
-    /// @brief Commits the configured DnsServerInfo
-    /// Currently this method is a NOP, as the server instance is created and
-    /// then added to the list of servers in build().
-    virtual void commit();
-
-private:
-    /// @brief Arbitrary label assigned to this parser instance.
-    /// Since servers are specified in a list this value is likely be something
-    /// akin to "server:0", set during parsing.  Primarily here for diagnostics.
-    std::string entry_name_;
-
-    /// @brief Pointer to the storage area to which the parser should commit
-    /// the newly created DnsServerInfo instance. This is given to us as a
-    /// constructor argument by an upper level.
-    DnsServerInfoStoragePtr servers_;
-
-    /// @brief Local storage area for scalar parameter values. Use to hold
-    /// data until time to commit.
-    DScalarContext local_scalars_;
+    DnsServerInfoPtr parse(data::ConstElementPtr server_config);
 };
 
 /// @brief Parser for a list of DnsServerInfos
@@ -860,213 +816,94 @@ private:
 /// This class parses a list of "dns-server" configuration elements.
 /// (see src/bin/d2/dhcp-ddns.spec). The DnsServerInfo instances are added
 /// to the given storage upon commit.
-class DnsServerInfoListParser : public isc::dhcp::DhcpConfigParser {
+class DnsServerInfoListParser : public data::SimpleParser{
 public:
-
     /// @brief Constructor
-    ///
-    /// @param list_name is an arbitrary label assigned to this parser instance.
-    /// @param servers is a pointer to the storage area to which the parser
-    /// should commit the newly created DnsServerInfo instance.
-    DnsServerInfoListParser(const std::string& list_name,
-                            DnsServerInfoStoragePtr servers);
+    DnsServerInfoListParser(){};
 
     /// @brief Destructor
-    virtual ~DnsServerInfoListParser();
+    virtual ~DnsServerInfoListParser(){};
 
     /// @brief Performs the actual parsing of the given list "dns-server"
     /// elements.
-    /// It iterates over each server entry in the list:
-    ///   1. Instantiate a DnsServerInfoParser for the entry
-    ///   2. Pass the element configuration to the parser's build method
-    ///   3. Add the parser instance to local storage
     ///
-    /// The net effect is to parse all of the server entries in the list
-    /// prepping them for commit.
+    /// Creates an empty server list
+    /// It iterates over each server entry in the list:
+    ///   1. Creates a server instance by passing the entry to @c 
+    ///   DnsSeverInfoParser::parse()
+    ///   2. Adds the server to the server list
     ///
     /// @param server_list_config is the list of "dns-server" elements to parse.
-    virtual void build(isc::data::ConstElementPtr server_list_config);
-
-    /// @brief Commits the list of DnsServerInfos
-    ///
-    /// Iterates over the internal list of DdnsServerInfoParsers, invoking
-    /// commit on each one.
-    virtual void commit();
-
-private:
-    /// @brief Arbitrary label assigned to this parser instance.
-    std::string list_name_;
-
-    /// @brief Pointer to the storage area to which the parser should commit
-    /// the list of newly created DnsServerInfo instances. This is given to us
-    /// as a constructor argument by an upper level.
-    DnsServerInfoStoragePtr servers_;
-
-    /// @brief Local storage of DnsServerInfoParser instances
-    isc::dhcp::ParserCollection parsers_;
+    /// @return A pointer to the new, populated server list
+    DnsServerInfoStoragePtr parse(data::ConstElementPtr server_list_config);
 };
 
 /// @brief Parser for  DdnsDomain
 ///
 /// This class parses the configuration element "ddns-domain" defined in
 /// src/bin/d2/dhcp-ddns.spec and creates an instance of a DdnsDomain.
-class DdnsDomainParser : public isc::dhcp::DhcpConfigParser {
+class DdnsDomainParser : public data::SimpleParser {
 public:
     /// @brief Constructor
-    ///
-    /// @param entry_name is an arbitrary label assigned to this configuration
-    /// definition. Since domains are specified in a list this value is likely
-    /// be something akin to "forward-ddns:0", set during parsing.
-    /// @param domains is a pointer to the storage area to which the parser
-    /// @param keys is a pointer to a map of the defined TSIG keys.
-    /// should commit the newly created DdnsDomain instance.
-    DdnsDomainParser(const std::string& entry_name, DdnsDomainMapPtr domains,
-                     TSIGKeyInfoMapPtr keys);
+    DdnsDomainParser(){};
 
     /// @brief Destructor
-    virtual ~DdnsDomainParser();
+    virtual ~DdnsDomainParser(){};
 
     /// @brief Performs the actual parsing of the given  "ddns-domain" element.
     ///
     /// Parses a configuration for the elements needed to instantiate a
-    /// DdnsDomain, validates those entries, creates a DdnsDomain instance
-    /// then attempts to add it to a list of domains.
+    /// DdnsDomain, validates those entries, and creates a DdnsDomain instance.
     ///
     /// @param domain_config is the "ddns-domain" configuration to parse
-    virtual void build(isc::data::ConstElementPtr domain_config);
-
-    /// @brief Creates a parser for the given "ddns-domain" member element id.
-    ///
-    /// The domain elements currently supported are(see dhcp-ddns.spec):
-    ///   1. name
-    ///   2. key_name
-    ///   3. dns_servers
+    /// @param keys map of defined TSIG keys
     ///
-    /// @param config_id is the "item_name" for a specific member element of
-    /// the "ddns-domain" specification.
-    /// @param pos position within the configuration text (or file) of element
-    /// to be parsed.  This is passed for error messaging.
-    ///
-    /// @return returns a pointer to newly created parser.
-    ///
-    /// @throw D2CfgError if configuration contains an unknown parameter
-    virtual isc::dhcp::ParserPtr
-    createConfigParser(const std::string& config_id,
-                       const isc::data::Element::Position& pos =
-                       isc::data::Element::ZERO_POSITION());
-
-    /// @brief Commits the configured DdnsDomain
-    /// Currently this method is a NOP, as the domain instance is created and
-    /// then added to the list of domains in build().
-    virtual void commit();
-
-private:
-
-    /// @brief Arbitrary label assigned to this parser instance.
-    std::string entry_name_;
-
-    /// @brief Pointer to the storage area to which the parser should commit
-    /// the newly created DdnsDomain instance. This is given to us as a
-    /// constructor argument by an upper level.
-    DdnsDomainMapPtr domains_;
-
-    /// @brief Pointer to the map of defined TSIG keys.
-    /// This map is passed into us and contains all of the TSIG keys defined
-    /// for this configuration.  It is used to validate the key name entry of
-    /// DdnsDomains that specify one.
-    TSIGKeyInfoMapPtr keys_;
-
-    /// @brief Local storage for DnsServerInfo instances. This is passed into
-    /// DnsServerInfoListParser(s), which in turn passes it into each
-    /// DnsServerInfoParser.  When the DnsServerInfoParsers "commit" they add
-    /// their server instance to this storage.
-    DnsServerInfoStoragePtr local_servers_;
-
-    /// @brief Local storage area for scalar parameter values. Use to hold
-    /// data until time to commit.
-    DScalarContext local_scalars_;
+    /// @return a pointer to the new domain instance
+    DdnsDomainPtr parse(data::ConstElementPtr domain_config,
+                        const TSIGKeyInfoMapPtr keys);
 };
 
 /// @brief Parser for a list of DdnsDomains
 ///
 /// This class parses a list of "ddns-domain" configuration elements.
-/// (see src/bin/d2/dhcp-ddns.spec). The DdnsDomain instances are added
-/// to the given storage upon commit.
-class DdnsDomainListParser : public isc::dhcp::DhcpConfigParser {
+/// (see src/bin/d2/dhcp-ddns.spec) into a map of DdnsDomains.  
+class DdnsDomainListParser : public data::SimpleParser {
 public:
 
     /// @brief Constructor
-    ///
-    /// @param list_name is an arbitrary label assigned to this parser instance.
-    /// @param domains is a pointer to the storage area to which the parser
-    /// @param keys is a pointer to a map of the defined TSIG keys.
-    /// should commit the newly created DdnsDomain instance.
-    DdnsDomainListParser(const std::string& list_name,
-                         DdnsDomainMapPtr domains, TSIGKeyInfoMapPtr keys);
+    DdnsDomainListParser(){};
 
     /// @brief Destructor
-    virtual ~DdnsDomainListParser();
+    virtual ~DdnsDomainListParser(){};
 
     /// @brief Performs the actual parsing of the given list "ddns-domain"
     /// elements.
+    /// Creates a new DdnsDomain map
     /// It iterates over each domain entry in the list:
-    ///   1. Instantiate a DdnsDomainParser for the entry
-    ///   2. Pass the element configuration to the parser's build method
-    ///   3. Add the parser instance to local storage
-    ///
-    /// The net effect is to parse all of the domain entries in the list
-    /// prepping them for commit.
+    ///   1. Creates a DdnsDomain instance by passing the entry into @c 
+    ///   DdnsDomainParser::parser()
+    ///   2. Adds the DdnsDomain instance to the domain map
     ///
     /// @param domain_list_config is the list of "ddns-domain" elements to
     /// parse.
-    virtual void build(isc::data::ConstElementPtr domain_list_config);
-
-    /// @brief Commits the list of DdnsDomains
-    ///
-    /// Iterates over the internal list of DdnsDomainParsers, invoking
-    /// commit on each one.
-    virtual void commit();
-
-private:
-    /// @brief Arbitrary label assigned to this parser instance.
-    std::string list_name_;
-
-    /// @brief Pointer to the storage area to which the parser should commit
-    /// the list of newly created DdnsDomain instances. This is given to us
-    /// as a constructor argument by an upper level.
-    DdnsDomainMapPtr domains_;
-
-    /// @brief Pointer to the map of defined TSIG keys.
-    /// This map is passed into us and contains all of the TSIG keys defined
-    /// for this configuration.  It is used to validate the key name entry of
-    /// DdnsDomains that specify one.
-    TSIGKeyInfoMapPtr keys_;
-
-    /// @brief Local storage of DdnsDomainParser instances
-    isc::dhcp::ParserCollection parsers_;
+    /// @param keys map of defined TSIG keys
+    /// @return a pointer to the newly populated domain map
+    DdnsDomainMapPtr parse(data::ConstElementPtr domain_list_config,
+                           const TSIGKeyInfoMapPtr keys);
 };
 
 /// @brief Parser for DdnsDomainListMgr
 ///
 /// This class parses the configuration elements "forward-ddns" and
 /// "reverse-ddns" as defined in src/bin/d2/dhcp-ddns.spec.  It populates the
-/// given DdnsDomainListMgr with parsed information upon commit.  Note that
-/// unlike other parsers, this parser does NOT instantiate the final object
-/// during the commit phase, it populates it.  It must pre-exist.
-class DdnsDomainListMgrParser : public isc::dhcp::DhcpConfigParser {
+/// given DdnsDomainListMgr with parsed information.  
+class DdnsDomainListMgrParser : public data::SimpleParser {
 public:
     /// @brief Constructor
-    ///
-    /// @param entry_name is an arbitrary label assigned to this configuration
-    /// definition.
-    /// @param mgr is a pointer to the DdnsDomainListMgr to populate.
-    /// @param keys is a pointer to a map of the defined TSIG keys.
-    /// @throw throws D2CfgError if mgr pointer is empty.
-    DdnsDomainListMgrParser(const std::string& entry_name,
-                     DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys);
+    DdnsDomainListMgrParser(){};
 
     /// @brief Destructor
-    virtual ~DdnsDomainListMgrParser();
+    virtual ~DdnsDomainListMgrParser(){};
 
     /// @brief Performs the actual parsing of the given manager element.
     ///
@@ -1074,58 +911,14 @@ public:
     /// DdnsDomainListMgr, validates those entries, then creates a
     /// DdnsDomainListMgr.
     ///
-    /// @param mgr_config is the manager configuration to parse
-    virtual void build(isc::data::ConstElementPtr mgr_config);
-
-    /// @brief Creates a parser for the given manager member element id.
-    ///
-    /// The manager elements currently supported are (see dhcp-ddns.spec):
-    ///     1. ddns_domains
-    ///
-    /// @param config_id is the "item_name" for a specific member element of
-    /// the manager specification.
-    /// @param pos position within the configuration text (or file) of element
-    /// to be parsed.  This is passed for error messaging.
-    ///
-    /// @return returns a pointer to newly created parser.
+    /// @param mgr_config manager configuration to parse
+    /// @param mgr_name convenience label for the manager instance
+    /// @param keys map of defined TSIG keys
     ///
-    /// @throw D2CfgError if configuration contains an unknown parameter
-    virtual isc::dhcp::ParserPtr
-    createConfigParser(const std::string& config_id,
-                       const isc::data::Element::Position& pos =
-                       isc::data::Element::ZERO_POSITION());
-
-    /// @brief Commits the configured DdsnDomainListMgr
-    /// Currently this method is a NOP, as the manager instance is created
-    /// in build().
-    virtual void commit();
-
-private:
-    /// @brief Arbitrary label assigned to this parser instance.
-    std::string entry_name_;
-
-    /// @brief Pointer to manager instance to which the parser should commit
-    /// the parsed data. This is given to us as a constructor argument by an
-    /// upper level.
-    DdnsDomainListMgrPtr mgr_;
-
-    /// @brief Pointer to the map of defined TSIG keys.
-    /// This map is passed into us and contains all of the TSIG keys defined
-    /// for this configuration.  It is used to validate the key name entry of
-    /// DdnsDomains that specify one.
-    TSIGKeyInfoMapPtr keys_;
-
-    /// @brief Local storage for DdnsDomain instances. This is passed into a
-    /// DdnsDomainListParser(s), which in turn passes it into each
-    /// DdnsDomainParser.  When the DdnsDomainParsers "commit" they add their
-    /// domain instance to this storage.
-    DdnsDomainMapPtr local_domains_;
-
-    /// @brief Local storage area for scalar parameter values. Use to hold
-    /// data until time to commit.
-    /// @todo Currently, the manager has no scalars but this is likely to
-    /// change as matching capabilities expand.
-    DScalarContext local_scalars_;
+    /// @return a pointer to the new manager instance
+    DdnsDomainListMgrPtr parse(data::ConstElementPtr mgr_config,
+                               const std::string& mgr_name,
+                               const TSIGKeyInfoMapPtr keys);
 };
 
 
index a05a38cadff802f59fd79c0e4f01ee8658674c7f..c35e6b9815409a409af07c049fe2bbd83ec25050 100644 (file)
@@ -1229,12 +1229,12 @@ int d2_parser__flex_debug = 1;
 
 static const flex_int16_t yy_rule_linenum[58] =
     {   0,
-      117,  119,  121,  126,  127,  132,  133,  134,  146,  149,
-      154,  160,  169,  179,  189,  198,  207,  216,  226,  236,
-      246,  255,  264,  274,  283,  292,  302,  311,  320,  329,
-      339,  348,  357,  366,  375,  386,  395,  404,  413,  423,
-      521,  526,  531,  536,  537,  538,  539,  540,  541,  543,
-      561,  574,  579,  583,  585,  587,  589
+      127,  129,  131,  136,  137,  142,  143,  144,  156,  159,
+      164,  170,  179,  190,  201,  210,  219,  228,  238,  248,
+      258,  267,  276,  286,  296,  306,  317,  326,  336,  346,
+      357,  366,  375,  384,  393,  406,  415,  424,  433,  443,
+      541,  546,  551,  556,  557,  558,  559,  560,  561,  563,
+      581,  594,  599,  603,  605,  607,  609
     } ;
 
 /* The intent behind this definition is that it'll catch
@@ -1610,11 +1610,21 @@ YY_DECL
             return isc::d2::D2Parser::make_TOPLEVEL_DHCPDDNS(driver.loc_);
         case D2ParserContext::PARSER_SUB_DHCPDDNS:
             return isc::d2::D2Parser::make_SUB_DHCPDDNS(driver.loc_);
+        case D2ParserContext::PARSER_TSIG_KEY:
+            return isc::d2::D2Parser::make_SUB_TSIG_KEY(driver.loc_);
+        case D2ParserContext::PARSER_TSIG_KEYS:
+            return isc::d2::D2Parser::make_SUB_TSIG_KEYS(driver.loc_);
+        case D2ParserContext::PARSER_DDNS_DOMAIN:
+            return isc::d2::D2Parser::make_SUB_DDNS_DOMAIN(driver.loc_);
+        case D2ParserContext::PARSER_DDNS_DOMAINS:
+            return isc::d2::D2Parser::make_SUB_DDNS_DOMAINS(driver.loc_);
+        case D2ParserContext::PARSER_DNS_SERVER:
+            return isc::d2::D2Parser::make_SUB_DNS_SERVER(driver.loc_);
         }
     }
 
 
-#line 1617 "d2_lexer.cc"
+#line 1627 "d2_lexer.cc"
 
        while ( /*CONSTCOND*/1 )                /* loops until end-of-file is reached */
                {
@@ -1692,17 +1702,17 @@ do_action:      /* This label is used only to access EOF actions. */
 
 case 1:
 YY_RULE_SETUP
-#line 117 "d2_lexer.ll"
+#line 127 "d2_lexer.ll"
 ;
        YY_BREAK
 case 2:
 YY_RULE_SETUP
-#line 119 "d2_lexer.ll"
+#line 129 "d2_lexer.ll"
 ;
        YY_BREAK
 case 3:
 YY_RULE_SETUP
-#line 121 "d2_lexer.ll"
+#line 131 "d2_lexer.ll"
 {
   BEGIN(COMMENT);
   comment_start_line = driver.loc_.end.line;;
@@ -1710,33 +1720,33 @@ YY_RULE_SETUP
        YY_BREAK
 case 4:
 YY_RULE_SETUP
-#line 126 "d2_lexer.ll"
+#line 136 "d2_lexer.ll"
 BEGIN(INITIAL);
        YY_BREAK
 case 5:
 YY_RULE_SETUP
-#line 127 "d2_lexer.ll"
+#line 137 "d2_lexer.ll"
 ;
        YY_BREAK
 case YY_STATE_EOF(COMMENT):
-#line 128 "d2_lexer.ll"
+#line 138 "d2_lexer.ll"
 {
     isc_throw(D2ParseError, "Comment not closed. (/* in line " << comment_start_line);
 }
        YY_BREAK
 case 6:
 YY_RULE_SETUP
-#line 132 "d2_lexer.ll"
+#line 142 "d2_lexer.ll"
 BEGIN(DIR_ENTER);
        YY_BREAK
 case 7:
 YY_RULE_SETUP
-#line 133 "d2_lexer.ll"
+#line 143 "d2_lexer.ll"
 BEGIN(DIR_INCLUDE);
        YY_BREAK
 case 8:
 YY_RULE_SETUP
-#line 134 "d2_lexer.ll"
+#line 144 "d2_lexer.ll"
 {
     // Include directive.
 
@@ -1750,19 +1760,19 @@ YY_RULE_SETUP
 case YY_STATE_EOF(DIR_ENTER):
 case YY_STATE_EOF(DIR_INCLUDE):
 case YY_STATE_EOF(DIR_EXIT):
-#line 143 "d2_lexer.ll"
+#line 153 "d2_lexer.ll"
 {
     isc_throw(D2ParseError, "Directive not closed.");
 }
        YY_BREAK
 case 9:
 YY_RULE_SETUP
-#line 146 "d2_lexer.ll"
+#line 156 "d2_lexer.ll"
 BEGIN(INITIAL);
        YY_BREAK
 case 10:
 YY_RULE_SETUP
-#line 149 "d2_lexer.ll"
+#line 159 "d2_lexer.ll"
 {
     // Ok, we found a with space. Let's ignore it and update loc variable.
     driver.loc_.step();
@@ -1771,7 +1781,7 @@ YY_RULE_SETUP
 case 11:
 /* rule 11 can match eol */
 YY_RULE_SETUP
-#line 154 "d2_lexer.ll"
+#line 164 "d2_lexer.ll"
 {
     // Newline found. Let's update the location and continue.
     driver.loc_.lines(yyleng);
@@ -1780,7 +1790,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 12:
 YY_RULE_SETUP
-#line 160 "d2_lexer.ll"
+#line 170 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::CONFIG:
@@ -1792,10 +1802,11 @@ YY_RULE_SETUP
        YY_BREAK
 case 13:
 YY_RULE_SETUP
-#line 169 "d2_lexer.ll"
+#line 179 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
+    case isc::d2::D2ParserContext::DNS_SERVER:
     case isc::d2::D2ParserContext::DNS_SERVERS:
         return isc::d2::D2Parser::make_IP_ADDRESS(driver.loc_);
     default:
@@ -1805,10 +1816,11 @@ YY_RULE_SETUP
        YY_BREAK
 case 14:
 YY_RULE_SETUP
-#line 179 "d2_lexer.ll"
+#line 190 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
+    case isc::d2::D2ParserContext::DNS_SERVER:
     case isc::d2::D2ParserContext::DNS_SERVERS:
         return isc::d2::D2Parser::make_PORT(driver.loc_);
     default:
@@ -1818,7 +1830,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 15:
 YY_RULE_SETUP
-#line 189 "d2_lexer.ll"
+#line 201 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
@@ -1830,7 +1842,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 16:
 YY_RULE_SETUP
-#line 198 "d2_lexer.ll"
+#line 210 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
@@ -1842,7 +1854,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 17:
 YY_RULE_SETUP
-#line 207 "d2_lexer.ll"
+#line 219 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
@@ -1854,7 +1866,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 18:
 YY_RULE_SETUP
-#line 216 "d2_lexer.ll"
+#line 228 "d2_lexer.ll"
 {
     /* dhcp-ddns value keywords are case insensitive */
     if (driver.ctx_ == isc::d2::D2ParserContext::NCR_PROTOCOL) {
@@ -1867,7 +1879,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 19:
 YY_RULE_SETUP
-#line 226 "d2_lexer.ll"
+#line 238 "d2_lexer.ll"
 {
     /* dhcp-ddns value keywords are case insensitive */
     if (driver.ctx_ == isc::d2::D2ParserContext::NCR_PROTOCOL) {
@@ -1880,7 +1892,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 20:
 YY_RULE_SETUP
-#line 236 "d2_lexer.ll"
+#line 248 "d2_lexer.ll"
 {
     /* dhcp-ddns value keywords are case insensitive */
     if (driver.ctx_ == isc::d2::D2ParserContext::NCR_FORMAT) {
@@ -1893,7 +1905,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 21:
 YY_RULE_SETUP
-#line 246 "d2_lexer.ll"
+#line 258 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
@@ -1905,7 +1917,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 22:
 YY_RULE_SETUP
-#line 255 "d2_lexer.ll"
+#line 267 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
@@ -1917,7 +1929,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 23:
 YY_RULE_SETUP
-#line 264 "d2_lexer.ll"
+#line 276 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::FORWARD_DDNS:
@@ -1930,9 +1942,10 @@ YY_RULE_SETUP
        YY_BREAK
 case 24:
 YY_RULE_SETUP
-#line 274 "d2_lexer.ll"
+#line 286 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::DDNS_DOMAIN:
     case isc::d2::D2ParserContext::DDNS_DOMAINS:
         return isc::d2::D2Parser::make_KEY_NAME(driver.loc_);
     default:
@@ -1942,9 +1955,10 @@ YY_RULE_SETUP
        YY_BREAK
 case 25:
 YY_RULE_SETUP
-#line 283 "d2_lexer.ll"
+#line 296 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::DDNS_DOMAIN:
     case isc::d2::D2ParserContext::DDNS_DOMAINS:
         return isc::d2::D2Parser::make_DNS_SERVERS(driver.loc_);
     default:
@@ -1954,9 +1968,10 @@ YY_RULE_SETUP
        YY_BREAK
 case 26:
 YY_RULE_SETUP
-#line 292 "d2_lexer.ll"
+#line 306 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::DNS_SERVER:
     case isc::d2::D2ParserContext::DNS_SERVERS:
         return isc::d2::D2Parser::make_HOSTNAME(driver.loc_);
     default:
@@ -1966,7 +1981,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 27:
 YY_RULE_SETUP
-#line 302 "d2_lexer.ll"
+#line 317 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
@@ -1978,9 +1993,10 @@ YY_RULE_SETUP
        YY_BREAK
 case 28:
 YY_RULE_SETUP
-#line 311 "d2_lexer.ll"
+#line 326 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
         return isc::d2::D2Parser::make_ALGORITHM(driver.loc_);
     default:
@@ -1990,9 +2006,10 @@ YY_RULE_SETUP
        YY_BREAK
 case 29:
 YY_RULE_SETUP
-#line 320 "d2_lexer.ll"
+#line 336 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
         return isc::d2::D2Parser::make_DIGEST_BITS(driver.loc_);
     default:
@@ -2002,9 +2019,10 @@ YY_RULE_SETUP
        YY_BREAK
 case 30:
 YY_RULE_SETUP
-#line 329 "d2_lexer.ll"
+#line 346 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
         return isc::d2::D2Parser::make_SECRET(driver.loc_);
     default:
@@ -2014,7 +2032,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 31:
 YY_RULE_SETUP
-#line 339 "d2_lexer.ll"
+#line 357 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::CONFIG:
@@ -2026,7 +2044,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 32:
 YY_RULE_SETUP
-#line 348 "d2_lexer.ll"
+#line 366 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::LOGGING:
@@ -2038,7 +2056,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 33:
 YY_RULE_SETUP
-#line 357 "d2_lexer.ll"
+#line 375 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::LOGGERS:
@@ -2050,7 +2068,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 34:
 YY_RULE_SETUP
-#line 366 "d2_lexer.ll"
+#line 384 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::OUTPUT_OPTIONS:
@@ -2062,11 +2080,13 @@ YY_RULE_SETUP
        YY_BREAK
 case 35:
 YY_RULE_SETUP
-#line 375 "d2_lexer.ll"
+#line 393 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::LOGGERS:
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
+    case isc::d2::D2ParserContext::DDNS_DOMAIN:
     case isc::d2::D2ParserContext::DDNS_DOMAINS:
         return isc::d2::D2Parser::make_NAME(driver.loc_);
     default:
@@ -2076,7 +2096,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 36:
 YY_RULE_SETUP
-#line 386 "d2_lexer.ll"
+#line 406 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::LOGGERS:
@@ -2088,7 +2108,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 37:
 YY_RULE_SETUP
-#line 395 "d2_lexer.ll"
+#line 415 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::LOGGERS:
@@ -2100,7 +2120,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 38:
 YY_RULE_SETUP
-#line 404 "d2_lexer.ll"
+#line 424 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::CONFIG:
@@ -2112,7 +2132,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 39:
 YY_RULE_SETUP
-#line 413 "d2_lexer.ll"
+#line 433 "d2_lexer.ll"
 {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::CONFIG:
@@ -2124,7 +2144,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 40:
 YY_RULE_SETUP
-#line 423 "d2_lexer.ll"
+#line 443 "d2_lexer.ll"
 {
     // A string has been matched. It contains the actual string and single quotes.
     // We need to get those quotes out of the way and just use its content, e.g.
@@ -2226,7 +2246,7 @@ YY_RULE_SETUP
 case 41:
 /* rule 41 can match eol */
 YY_RULE_SETUP
-#line 521 "d2_lexer.ll"
+#line 541 "d2_lexer.ll"
 {
     // Bad string with a forbidden control character inside
     driver.error(driver.loc_, "Invalid control in " + std::string(yytext));
@@ -2235,7 +2255,7 @@ YY_RULE_SETUP
 case 42:
 /* rule 42 can match eol */
 YY_RULE_SETUP
-#line 526 "d2_lexer.ll"
+#line 546 "d2_lexer.ll"
 {
     // Bad string with a bad escape inside
     driver.error(driver.loc_, "Bad escape in " + std::string(yytext));
@@ -2243,7 +2263,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 43:
 YY_RULE_SETUP
-#line 531 "d2_lexer.ll"
+#line 551 "d2_lexer.ll"
 {
     // Bad string with an open escape at the end
     driver.error(driver.loc_, "Overflow escape in " + std::string(yytext));
@@ -2251,37 +2271,37 @@ YY_RULE_SETUP
        YY_BREAK
 case 44:
 YY_RULE_SETUP
-#line 536 "d2_lexer.ll"
+#line 556 "d2_lexer.ll"
 { return isc::d2::D2Parser::make_LSQUARE_BRACKET(driver.loc_); }
        YY_BREAK
 case 45:
 YY_RULE_SETUP
-#line 537 "d2_lexer.ll"
+#line 557 "d2_lexer.ll"
 { return isc::d2::D2Parser::make_RSQUARE_BRACKET(driver.loc_); }
        YY_BREAK
 case 46:
 YY_RULE_SETUP
-#line 538 "d2_lexer.ll"
+#line 558 "d2_lexer.ll"
 { return isc::d2::D2Parser::make_LCURLY_BRACKET(driver.loc_); }
        YY_BREAK
 case 47:
 YY_RULE_SETUP
-#line 539 "d2_lexer.ll"
+#line 559 "d2_lexer.ll"
 { return isc::d2::D2Parser::make_RCURLY_BRACKET(driver.loc_); }
        YY_BREAK
 case 48:
 YY_RULE_SETUP
-#line 540 "d2_lexer.ll"
+#line 560 "d2_lexer.ll"
 { return isc::d2::D2Parser::make_COMMA(driver.loc_); }
        YY_BREAK
 case 49:
 YY_RULE_SETUP
-#line 541 "d2_lexer.ll"
+#line 561 "d2_lexer.ll"
 { return isc::d2::D2Parser::make_COLON(driver.loc_); }
        YY_BREAK
 case 50:
 YY_RULE_SETUP
-#line 543 "d2_lexer.ll"
+#line 563 "d2_lexer.ll"
 {
     // An integer was found.
     std::string tmp(yytext);
@@ -2302,7 +2322,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 51:
 YY_RULE_SETUP
-#line 561 "d2_lexer.ll"
+#line 581 "d2_lexer.ll"
 {
     // A floating point was found.
     std::string tmp(yytext);
@@ -2318,7 +2338,7 @@ YY_RULE_SETUP
        YY_BREAK
 case 52:
 YY_RULE_SETUP
-#line 574 "d2_lexer.ll"
+#line 594 "d2_lexer.ll"
 {
     string tmp(yytext);
     return isc::d2::D2Parser::make_BOOLEAN(tmp == "true", driver.loc_);
@@ -2326,33 +2346,33 @@ YY_RULE_SETUP
        YY_BREAK
 case 53:
 YY_RULE_SETUP
-#line 579 "d2_lexer.ll"
+#line 599 "d2_lexer.ll"
 {
    return isc::d2::D2Parser::make_NULL_TYPE(driver.loc_);
 }
        YY_BREAK
 case 54:
 YY_RULE_SETUP
-#line 583 "d2_lexer.ll"
+#line 603 "d2_lexer.ll"
 driver.error (driver.loc_, "JSON true reserved keyword is lower case only");
        YY_BREAK
 case 55:
 YY_RULE_SETUP
-#line 585 "d2_lexer.ll"
+#line 605 "d2_lexer.ll"
 driver.error (driver.loc_, "JSON false reserved keyword is lower case only");
        YY_BREAK
 case 56:
 YY_RULE_SETUP
-#line 587 "d2_lexer.ll"
+#line 607 "d2_lexer.ll"
 driver.error (driver.loc_, "JSON null reserved keyword is lower case only");
        YY_BREAK
 case 57:
 YY_RULE_SETUP
-#line 589 "d2_lexer.ll"
+#line 609 "d2_lexer.ll"
 driver.error (driver.loc_, "Invalid character: " + std::string(yytext));
        YY_BREAK
 case YY_STATE_EOF(INITIAL):
-#line 591 "d2_lexer.ll"
+#line 611 "d2_lexer.ll"
 {
     if (driver.states_.empty()) {
         return isc::d2::D2Parser::make_END(driver.loc_);
@@ -2378,10 +2398,10 @@ case YY_STATE_EOF(INITIAL):
        YY_BREAK
 case 58:
 YY_RULE_SETUP
-#line 614 "d2_lexer.ll"
+#line 634 "d2_lexer.ll"
 ECHO;
        YY_BREAK
-#line 2384 "d2_lexer.cc"
+#line 2404 "d2_lexer.cc"
 
        case YY_END_OF_BUFFER:
                {
@@ -3482,7 +3502,7 @@ void d2_parser_free (void * ptr )
 
 /* %ok-for-header */
 
-#line 614 "d2_lexer.ll"
+#line 634 "d2_lexer.ll"
 
 
 using namespace isc::dhcp;
index f68b7b7359e4e12fff8ff8e2ee0e618eebf881cc..045b50c9c786aa7e926551bc5f60062fd959f6e2 100644 (file)
@@ -109,6 +109,16 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
             return isc::d2::D2Parser::make_TOPLEVEL_DHCPDDNS(driver.loc_);
         case D2ParserContext::PARSER_SUB_DHCPDDNS:
             return isc::d2::D2Parser::make_SUB_DHCPDDNS(driver.loc_);
+        case D2ParserContext::PARSER_TSIG_KEY:
+            return isc::d2::D2Parser::make_SUB_TSIG_KEY(driver.loc_);
+        case D2ParserContext::PARSER_TSIG_KEYS:
+            return isc::d2::D2Parser::make_SUB_TSIG_KEYS(driver.loc_);
+        case D2ParserContext::PARSER_DDNS_DOMAIN:
+            return isc::d2::D2Parser::make_SUB_DDNS_DOMAIN(driver.loc_);
+        case D2ParserContext::PARSER_DDNS_DOMAINS:
+            return isc::d2::D2Parser::make_SUB_DDNS_DOMAINS(driver.loc_);
+        case D2ParserContext::PARSER_DNS_SERVER:
+            return isc::d2::D2Parser::make_SUB_DNS_SERVER(driver.loc_);
         }
     }
 %}
@@ -168,6 +178,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 \"ip-address\" {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
+    case isc::d2::D2ParserContext::DNS_SERVER:
     case isc::d2::D2ParserContext::DNS_SERVERS:
         return isc::d2::D2Parser::make_IP_ADDRESS(driver.loc_);
     default:
@@ -178,6 +189,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 \"port\" {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::DHCPDDNS:
+    case isc::d2::D2ParserContext::DNS_SERVER:
     case isc::d2::D2ParserContext::DNS_SERVERS:
         return isc::d2::D2Parser::make_PORT(driver.loc_);
     default:
@@ -272,6 +284,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 \"key-name\" {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::DDNS_DOMAIN:
     case isc::d2::D2ParserContext::DDNS_DOMAINS:
         return isc::d2::D2Parser::make_KEY_NAME(driver.loc_);
     default:
@@ -281,6 +294,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 \"dns-servers\" {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::DDNS_DOMAIN:
     case isc::d2::D2ParserContext::DDNS_DOMAINS:
         return isc::d2::D2Parser::make_DNS_SERVERS(driver.loc_);
     default:
@@ -290,6 +304,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 \"hostname\" {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::DNS_SERVER:
     case isc::d2::D2ParserContext::DNS_SERVERS:
         return isc::d2::D2Parser::make_HOSTNAME(driver.loc_);
     default:
@@ -309,6 +324,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 \"algorithm\" {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
         return isc::d2::D2Parser::make_ALGORITHM(driver.loc_);
     default:
@@ -318,6 +334,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 \"digest-bits\" {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
         return isc::d2::D2Parser::make_DIGEST_BITS(driver.loc_);
     default:
@@ -327,6 +344,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 \"secret\" {
     switch(driver.ctx_) {
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
         return isc::d2::D2Parser::make_SECRET(driver.loc_);
     default:
@@ -374,7 +392,9 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 \"name\" {
     switch(driver.ctx_) {
     case isc::d2::D2ParserContext::LOGGERS:
+    case isc::d2::D2ParserContext::TSIG_KEY:
     case isc::d2::D2ParserContext::TSIG_KEYS:
+    case isc::d2::D2ParserContext::DDNS_DOMAIN:
     case isc::d2::D2ParserContext::DDNS_DOMAINS:
         return isc::d2::D2Parser::make_NAME(driver.loc_);
     default:
index ad15ae214f4bdb31eac125c13a8d4173bcdd81f8..752a420772128c3fa197be6c1ff4753aa96b12bf 100644 (file)
@@ -253,24 +253,24 @@ namespace isc { namespace d2 {
   {
       switch (that.type_get ())
     {
-      case 50: // value
-      case 77: // ncr_protocol_value
+      case 62: // value
+      case 89: // ncr_protocol_value
         value.move< ElementPtr > (that.value);
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
         value.move< bool > (that.value);
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
         value.move< double > (that.value);
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
         value.move< int64_t > (that.value);
         break;
 
-      case 41: // "constant string"
+      case 47: // "constant string"
         value.move< std::string > (that.value);
         break;
 
@@ -289,24 +289,24 @@ namespace isc { namespace d2 {
     state = that.state;
       switch (that.type_get ())
     {
-      case 50: // value
-      case 77: // ncr_protocol_value
+      case 62: // value
+      case 89: // ncr_protocol_value
         value.copy< ElementPtr > (that.value);
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
         value.copy< bool > (that.value);
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
         value.copy< double > (that.value);
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
         value.copy< int64_t > (that.value);
         break;
 
-      case 41: // "constant string"
+      case 47: // "constant string"
         value.copy< std::string > (that.value);
         break;
 
@@ -346,44 +346,44 @@ namespace isc { namespace d2 {
         << yysym.location << ": ";
     switch (yytype)
     {
-            case 41: // "constant string"
+            case 47: // "constant string"
 
-#line 98 "d2_parser.yy" // lalr1.cc:636
+#line 104 "d2_parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< std::string > (); }
 #line 354 "d2_parser.cc" // lalr1.cc:636
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
 
-#line 98 "d2_parser.yy" // lalr1.cc:636
+#line 104 "d2_parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< int64_t > (); }
 #line 361 "d2_parser.cc" // lalr1.cc:636
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
 
-#line 98 "d2_parser.yy" // lalr1.cc:636
+#line 104 "d2_parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< double > (); }
 #line 368 "d2_parser.cc" // lalr1.cc:636
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
 
-#line 98 "d2_parser.yy" // lalr1.cc:636
+#line 104 "d2_parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< bool > (); }
 #line 375 "d2_parser.cc" // lalr1.cc:636
         break;
 
-      case 50: // value
+      case 62: // value
 
-#line 98 "d2_parser.yy" // lalr1.cc:636
+#line 104 "d2_parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< ElementPtr > (); }
 #line 382 "d2_parser.cc" // lalr1.cc:636
         break;
 
-      case 77: // ncr_protocol_value
+      case 89: // ncr_protocol_value
 
-#line 98 "d2_parser.yy" // lalr1.cc:636
+#line 104 "d2_parser.yy" // lalr1.cc:636
         { yyoutput << yysym.value.template as< ElementPtr > (); }
 #line 389 "d2_parser.cc" // lalr1.cc:636
         break;
@@ -585,24 +585,24 @@ namespace isc { namespace d2 {
          when using variants.  */
         switch (yyr1_[yyn])
     {
-      case 50: // value
-      case 77: // ncr_protocol_value
+      case 62: // value
+      case 89: // ncr_protocol_value
         yylhs.value.build< ElementPtr > ();
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
         yylhs.value.build< bool > ();
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
         yylhs.value.build< double > ();
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
         yylhs.value.build< int64_t > ();
         break;
 
-      case 41: // "constant string"
+      case 47: // "constant string"
         yylhs.value.build< std::string > ();
         break;
 
@@ -624,830 +624,966 @@ namespace isc { namespace d2 {
           switch (yyn)
             {
   case 2:
-#line 107 "d2_parser.yy" // lalr1.cc:859
+#line 113 "d2_parser.yy" // lalr1.cc:859
     { ctx.ctx_ = ctx.NO_KEYWORD; }
 #line 630 "d2_parser.cc" // lalr1.cc:859
     break;
 
   case 4:
-#line 108 "d2_parser.yy" // lalr1.cc:859
+#line 114 "d2_parser.yy" // lalr1.cc:859
     { ctx.ctx_ = ctx.CONFIG; }
 #line 636 "d2_parser.cc" // lalr1.cc:859
     break;
 
   case 6:
-#line 109 "d2_parser.yy" // lalr1.cc:859
+#line 115 "d2_parser.yy" // lalr1.cc:859
     { ctx.ctx_ = ctx.DHCPDDNS; }
 #line 642 "d2_parser.cc" // lalr1.cc:859
     break;
 
   case 8:
-#line 117 "d2_parser.yy" // lalr1.cc:859
-    { yylhs.value.as< ElementPtr > () = ElementPtr(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); }
+#line 116 "d2_parser.yy" // lalr1.cc:859
+    { ctx.ctx_ = ctx.TSIG_KEY; }
 #line 648 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 9:
-#line 118 "d2_parser.yy" // lalr1.cc:859
-    { yylhs.value.as< ElementPtr > () = ElementPtr(new DoubleElement(yystack_[0].value.as< double > (), ctx.loc2pos(yystack_[0].location))); }
+  case 10:
+#line 117 "d2_parser.yy" // lalr1.cc:859
+    { ctx.ctx_ = ctx.TSIG_KEYS; }
 #line 654 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 10:
-#line 119 "d2_parser.yy" // lalr1.cc:859
-    { yylhs.value.as< ElementPtr > () = ElementPtr(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); }
+  case 12:
+#line 118 "d2_parser.yy" // lalr1.cc:859
+    { ctx.ctx_ = ctx.DDNS_DOMAIN; }
 #line 660 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 11:
-#line 120 "d2_parser.yy" // lalr1.cc:859
-    { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); }
+  case 14:
+#line 119 "d2_parser.yy" // lalr1.cc:859
+    { ctx.ctx_ = ctx.DDNS_DOMAINS; }
 #line 666 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 12:
+  case 16:
+#line 120 "d2_parser.yy" // lalr1.cc:859
+    { ctx.ctx_ = ctx.DNS_SERVERS; }
+#line 672 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 18:
 #line 121 "d2_parser.yy" // lalr1.cc:859
+    { ctx.ctx_ = ctx.DNS_SERVERS; }
+#line 678 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 20:
+#line 129 "d2_parser.yy" // lalr1.cc:859
+    { yylhs.value.as< ElementPtr > () = ElementPtr(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); }
+#line 684 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 21:
+#line 130 "d2_parser.yy" // lalr1.cc:859
+    { yylhs.value.as< ElementPtr > () = ElementPtr(new DoubleElement(yystack_[0].value.as< double > (), ctx.loc2pos(yystack_[0].location))); }
+#line 690 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 22:
+#line 131 "d2_parser.yy" // lalr1.cc:859
+    { yylhs.value.as< ElementPtr > () = ElementPtr(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); }
+#line 696 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 23:
+#line 132 "d2_parser.yy" // lalr1.cc:859
+    { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); }
+#line 702 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 24:
+#line 133 "d2_parser.yy" // lalr1.cc:859
     { yylhs.value.as< ElementPtr > () = ElementPtr(new NullElement(ctx.loc2pos(yystack_[0].location))); }
-#line 672 "d2_parser.cc" // lalr1.cc:859
+#line 708 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 13:
-#line 122 "d2_parser.yy" // lalr1.cc:859
+  case 25:
+#line 134 "d2_parser.yy" // lalr1.cc:859
     { yylhs.value.as< ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
-#line 678 "d2_parser.cc" // lalr1.cc:859
+#line 714 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 14:
-#line 123 "d2_parser.yy" // lalr1.cc:859
+  case 26:
+#line 135 "d2_parser.yy" // lalr1.cc:859
     { yylhs.value.as< ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
-#line 684 "d2_parser.cc" // lalr1.cc:859
+#line 720 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 15:
-#line 126 "d2_parser.yy" // lalr1.cc:859
+  case 27:
+#line 138 "d2_parser.yy" // lalr1.cc:859
     {
     // Push back the JSON value on the stack
     ctx.stack_.push_back(yystack_[0].value.as< ElementPtr > ());
 }
-#line 693 "d2_parser.cc" // lalr1.cc:859
+#line 729 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 16:
-#line 131 "d2_parser.yy" // lalr1.cc:859
+  case 28:
+#line 143 "d2_parser.yy" // lalr1.cc:859
     {
     // This code is executed when we're about to start parsing
     // the content of the map
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.push_back(m);
 }
-#line 704 "d2_parser.cc" // lalr1.cc:859
+#line 740 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 17:
-#line 136 "d2_parser.yy" // lalr1.cc:859
+  case 29:
+#line 148 "d2_parser.yy" // lalr1.cc:859
     {
     // map parsing completed. If we ever want to do any wrap up
     // (maybe some sanity checking), this would be the best place
     // for it.
 }
-#line 714 "d2_parser.cc" // lalr1.cc:859
+#line 750 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 20:
-#line 147 "d2_parser.yy" // lalr1.cc:859
+  case 32:
+#line 159 "d2_parser.yy" // lalr1.cc:859
     {
                   // map containing a single entry
                   ctx.stack_.back()->set(yystack_[2].value.as< std::string > (), yystack_[0].value.as< ElementPtr > ());
                   }
-#line 723 "d2_parser.cc" // lalr1.cc:859
+#line 759 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 21:
-#line 151 "d2_parser.yy" // lalr1.cc:859
+  case 33:
+#line 163 "d2_parser.yy" // lalr1.cc:859
     {
                   // map consisting of a shorter map followed by
                   // comma and string:value
                   ctx.stack_.back()->set(yystack_[2].value.as< std::string > (), yystack_[0].value.as< ElementPtr > ());
                   }
-#line 733 "d2_parser.cc" // lalr1.cc:859
+#line 769 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 22:
-#line 158 "d2_parser.yy" // lalr1.cc:859
+  case 34:
+#line 170 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.push_back(l);
 }
-#line 742 "d2_parser.cc" // lalr1.cc:859
+#line 778 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 23:
-#line 161 "d2_parser.yy" // lalr1.cc:859
+  case 35:
+#line 173 "d2_parser.yy" // lalr1.cc:859
     {
     // list parsing complete. Put any sanity checking here
 }
-#line 750 "d2_parser.cc" // lalr1.cc:859
+#line 786 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 26:
-#line 169 "d2_parser.yy" // lalr1.cc:859
+  case 38:
+#line 181 "d2_parser.yy" // lalr1.cc:859
     {
                   // List consisting of a single element.
                   ctx.stack_.back()->add(yystack_[0].value.as< ElementPtr > ());
                   }
-#line 759 "d2_parser.cc" // lalr1.cc:859
+#line 795 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 27:
-#line 173 "d2_parser.yy" // lalr1.cc:859
+  case 39:
+#line 185 "d2_parser.yy" // lalr1.cc:859
     {
                   // List ending with , and a value.
                   ctx.stack_.back()->add(yystack_[0].value.as< ElementPtr > ());
                   }
-#line 768 "d2_parser.cc" // lalr1.cc:859
+#line 804 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 28:
-#line 184 "d2_parser.yy" // lalr1.cc:859
+  case 40:
+#line 196 "d2_parser.yy" // lalr1.cc:859
     {
     const std::string& where = ctx.contextName();
     const std::string& keyword = yystack_[1].value.as< std::string > ();
     error(yystack_[1].location,
           "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
 }
-#line 779 "d2_parser.cc" // lalr1.cc:859
+#line 815 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 29:
-#line 194 "d2_parser.yy" // lalr1.cc:859
+  case 41:
+#line 206 "d2_parser.yy" // lalr1.cc:859
     {
     // This code is executed when we're about to start parsing
     // the content of the map
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.push_back(m);
 }
-#line 790 "d2_parser.cc" // lalr1.cc:859
+#line 826 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 30:
-#line 199 "d2_parser.yy" // lalr1.cc:859
+  case 42:
+#line 211 "d2_parser.yy" // lalr1.cc:859
     {
     // map parsing completed. If we ever want to do any wrap up
     // (maybe some sanity checking), this would be the best place
     // for it.
 }
-#line 800 "d2_parser.cc" // lalr1.cc:859
+#line 836 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 38:
-#line 220 "d2_parser.yy" // lalr1.cc:859
+  case 50:
+#line 232 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("DhcpDdns", m);
     ctx.stack_.push_back(m);
     ctx.enter(ctx.DHCPDDNS);
 }
-#line 811 "d2_parser.cc" // lalr1.cc:859
+#line 847 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 39:
-#line 225 "d2_parser.yy" // lalr1.cc:859
+  case 51:
+#line 237 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 820 "d2_parser.cc" // lalr1.cc:859
+#line 856 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 40:
-#line 230 "d2_parser.yy" // lalr1.cc:859
+  case 52:
+#line 242 "d2_parser.yy" // lalr1.cc:859
     {
     // Parse the dhcpddns map
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.push_back(m);
 }
-#line 830 "d2_parser.cc" // lalr1.cc:859
+#line 866 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 41:
-#line 234 "d2_parser.yy" // lalr1.cc:859
+  case 53:
+#line 246 "d2_parser.yy" // lalr1.cc:859
     {
     // parsing completed
 }
-#line 838 "d2_parser.cc" // lalr1.cc:859
+#line 874 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 53:
-#line 253 "d2_parser.yy" // lalr1.cc:859
+  case 65:
+#line 265 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 846 "d2_parser.cc" // lalr1.cc:859
+#line 882 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 54:
-#line 255 "d2_parser.yy" // lalr1.cc:859
+  case 66:
+#line 267 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("ip-address", s);
     ctx.leave();
 }
-#line 856 "d2_parser.cc" // lalr1.cc:859
+#line 892 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 55:
-#line 261 "d2_parser.yy" // lalr1.cc:859
+  case 67:
+#line 273 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< int64_t > () <= 0) {
         error(yystack_[0].location, "port must be greater than zero");
-    } 
+    }
     ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("port", i);
 }
-#line 868 "d2_parser.cc" // lalr1.cc:859
+#line 904 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 56:
-#line 269 "d2_parser.yy" // lalr1.cc:859
+  case 68:
+#line 281 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< int64_t > () <= 0) {
         error(yystack_[0].location, "dns-server-timeout must be greater than zero");
-    } else { 
+    } else {
         ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location)));
         ctx.stack_.back()->set("dns-server-timeout", i);
     }
 }
-#line 881 "d2_parser.cc" // lalr1.cc:859
+#line 917 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 57:
-#line 278 "d2_parser.yy" // lalr1.cc:859
+  case 69:
+#line 290 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NCR_PROTOCOL);
 }
-#line 889 "d2_parser.cc" // lalr1.cc:859
+#line 925 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 58:
-#line 280 "d2_parser.yy" // lalr1.cc:859
+  case 70:
+#line 292 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.back()->set("ncr-protocol", yystack_[0].value.as< ElementPtr > ());
     ctx.leave();
 }
-#line 898 "d2_parser.cc" // lalr1.cc:859
+#line 934 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 59:
-#line 286 "d2_parser.yy" // lalr1.cc:859
+  case 71:
+#line 298 "d2_parser.yy" // lalr1.cc:859
     { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("UDP", ctx.loc2pos(yystack_[0].location))); }
-#line 904 "d2_parser.cc" // lalr1.cc:859
+#line 940 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 60:
-#line 287 "d2_parser.yy" // lalr1.cc:859
+  case 72:
+#line 299 "d2_parser.yy" // lalr1.cc:859
     { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("TCP", ctx.loc2pos(yystack_[0].location))); }
-#line 910 "d2_parser.cc" // lalr1.cc:859
+#line 946 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 61:
-#line 290 "d2_parser.yy" // lalr1.cc:859
+  case 73:
+#line 302 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NCR_FORMAT);
 }
-#line 918 "d2_parser.cc" // lalr1.cc:859
+#line 954 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 62:
-#line 292 "d2_parser.yy" // lalr1.cc:859
+  case 74:
+#line 304 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr json(new StringElement("JSON", ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("ncr-format", json);
     ctx.leave();
 }
-#line 928 "d2_parser.cc" // lalr1.cc:859
+#line 964 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 63:
-#line 298 "d2_parser.yy" // lalr1.cc:859
+  case 75:
+#line 310 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("forward-ddns", m);
     ctx.stack_.push_back(m);
     ctx.enter(ctx.FORWARD_DDNS);
 }
-#line 939 "d2_parser.cc" // lalr1.cc:859
+#line 975 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 64:
-#line 303 "d2_parser.yy" // lalr1.cc:859
+  case 76:
+#line 315 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 948 "d2_parser.cc" // lalr1.cc:859
+#line 984 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 65:
-#line 308 "d2_parser.yy" // lalr1.cc:859
+  case 77:
+#line 320 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("reverse-ddns", m);
     ctx.stack_.push_back(m);
     ctx.enter(ctx.REVERSE_DDNS);
 }
-#line 959 "d2_parser.cc" // lalr1.cc:859
+#line 995 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 66:
-#line 313 "d2_parser.yy" // lalr1.cc:859
+  case 78:
+#line 325 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 968 "d2_parser.cc" // lalr1.cc:859
+#line 1004 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 73:
-#line 333 "d2_parser.yy" // lalr1.cc:859
+  case 85:
+#line 344 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("ddns-domains", l);
     ctx.stack_.push_back(l);
     ctx.enter(ctx.DDNS_DOMAINS);
 }
-#line 979 "d2_parser.cc" // lalr1.cc:859
+#line 1015 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 74:
-#line 338 "d2_parser.yy" // lalr1.cc:859
+  case 86:
+#line 349 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 988 "d2_parser.cc" // lalr1.cc:859
+#line 1024 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 79:
-#line 351 "d2_parser.yy" // lalr1.cc:859
+  case 87:
+#line 354 "d2_parser.yy" // lalr1.cc:859
+    {
+    ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+    ctx.stack_.push_back(l);
+}
+#line 1033 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 88:
+#line 357 "d2_parser.yy" // lalr1.cc:859
+    {
+    // parsing completed
+}
+#line 1041 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 93:
+#line 369 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->add(m);
     ctx.stack_.push_back(m);
 }
-#line 998 "d2_parser.cc" // lalr1.cc:859
+#line 1051 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 80:
-#line 355 "d2_parser.yy" // lalr1.cc:859
+  case 94:
+#line 373 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
 }
-#line 1006 "d2_parser.cc" // lalr1.cc:859
+#line 1059 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 87:
-#line 370 "d2_parser.yy" // lalr1.cc:859
+  case 95:
+#line 377 "d2_parser.yy" // lalr1.cc:859
+    {
+    ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+    ctx.stack_.push_back(m);
+}
+#line 1068 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 96:
+#line 380 "d2_parser.yy" // lalr1.cc:859
+    {
+    // parsing completed
+}
+#line 1076 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 103:
+#line 395 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1014 "d2_parser.cc" // lalr1.cc:859
+#line 1084 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 88:
-#line 372 "d2_parser.yy" // lalr1.cc:859
+  case 104:
+#line 397 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< std::string > () == "") {
-        error(yystack_[1].location, "name cannot be blank");
-    } 
+        error(yystack_[1].location, "Ddns domain name cannot be blank");
+    }
     ElementPtr elem(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ElementPtr name(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("name", name);
     ctx.leave();
 }
-#line 1028 "d2_parser.cc" // lalr1.cc:859
+#line 1098 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 89:
-#line 382 "d2_parser.yy" // lalr1.cc:859
+  case 105:
+#line 407 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1036 "d2_parser.cc" // lalr1.cc:859
+#line 1106 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 90:
-#line 384 "d2_parser.yy" // lalr1.cc:859
+  case 106:
+#line 409 "d2_parser.yy" // lalr1.cc:859
     {
-    if (yystack_[0].value.as< std::string > () == "") {
-        error(yystack_[1].location, "key-name cannot be blank");
-    } 
     ElementPtr elem(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ElementPtr name(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("key-name", name);
     ctx.leave();
 }
-#line 1050 "d2_parser.cc" // lalr1.cc:859
+#line 1117 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 91:
-#line 397 "d2_parser.yy" // lalr1.cc:859
+  case 107:
+#line 419 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("dns-servers", l);
     ctx.stack_.push_back(l);
     ctx.enter(ctx.DNS_SERVERS);
 }
-#line 1061 "d2_parser.cc" // lalr1.cc:859
+#line 1128 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 92:
-#line 402 "d2_parser.yy" // lalr1.cc:859
+  case 108:
+#line 424 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 1070 "d2_parser.cc" // lalr1.cc:859
+#line 1137 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 109:
+#line 429 "d2_parser.yy" // lalr1.cc:859
+    {
+    ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+    ctx.stack_.push_back(l);
+}
+#line 1146 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 110:
+#line 432 "d2_parser.yy" // lalr1.cc:859
+    {
+    // parsing completed
+}
+#line 1154 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 97:
-#line 415 "d2_parser.yy" // lalr1.cc:859
+  case 113:
+#line 440 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->add(m);
     ctx.stack_.push_back(m);
 }
-#line 1080 "d2_parser.cc" // lalr1.cc:859
+#line 1164 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 98:
-#line 419 "d2_parser.yy" // lalr1.cc:859
+  case 114:
+#line 444 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
 }
-#line 1088 "d2_parser.cc" // lalr1.cc:859
+#line 1172 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 105:
-#line 433 "d2_parser.yy" // lalr1.cc:859
+  case 115:
+#line 448 "d2_parser.yy" // lalr1.cc:859
+    {
+    ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+    ctx.stack_.push_back(m);
+}
+#line 1181 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 116:
+#line 451 "d2_parser.yy" // lalr1.cc:859
+    {
+    // parsing completed
+}
+#line 1189 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 123:
+#line 465 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1096 "d2_parser.cc" // lalr1.cc:859
+#line 1197 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 106:
-#line 435 "d2_parser.yy" // lalr1.cc:859
+  case 124:
+#line 467 "d2_parser.yy" // lalr1.cc:859
     {
-    if (yystack_[0].value.as< std::string > () == "") {
-        error(yystack_[1].location, "hostname cannot be blank");
-    } 
+    if (yystack_[0].value.as< std::string > () != "") {
+        error(yystack_[1].location, "hostname is not yet supported");
+    }
     ElementPtr elem(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ElementPtr name(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("hostname", name);
     ctx.leave();
 }
-#line 1110 "d2_parser.cc" // lalr1.cc:859
+#line 1211 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 107:
-#line 445 "d2_parser.yy" // lalr1.cc:859
+  case 125:
+#line 477 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1118 "d2_parser.cc" // lalr1.cc:859
+#line 1219 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 108:
-#line 447 "d2_parser.yy" // lalr1.cc:859
+  case 126:
+#line 479 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("ip-address", s);
     ctx.leave();
 }
-#line 1128 "d2_parser.cc" // lalr1.cc:859
+#line 1229 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 109:
-#line 453 "d2_parser.yy" // lalr1.cc:859
+  case 127:
+#line 485 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< int64_t > () <= 0) {
         error(yystack_[0].location, "port must be greater than zero");
-    } 
+    }
     ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("port", i);
 }
-#line 1140 "d2_parser.cc" // lalr1.cc:859
+#line 1241 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 110:
-#line 467 "d2_parser.yy" // lalr1.cc:859
+  case 128:
+#line 499 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("tsig-keys", l);
     ctx.stack_.push_back(l);
     ctx.enter(ctx.TSIG_KEYS);
 }
-#line 1151 "d2_parser.cc" // lalr1.cc:859
+#line 1252 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 111:
-#line 472 "d2_parser.yy" // lalr1.cc:859
+  case 129:
+#line 504 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 1160 "d2_parser.cc" // lalr1.cc:859
+#line 1261 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 116:
-#line 485 "d2_parser.yy" // lalr1.cc:859
+  case 130:
+#line 509 "d2_parser.yy" // lalr1.cc:859
+    {
+    ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+    ctx.stack_.push_back(l);
+}
+#line 1270 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 131:
+#line 512 "d2_parser.yy" // lalr1.cc:859
+    {
+    // parsing completed
+}
+#line 1278 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 136:
+#line 524 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->add(m);
     ctx.stack_.push_back(m);
 }
-#line 1170 "d2_parser.cc" // lalr1.cc:859
+#line 1288 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 117:
-#line 489 "d2_parser.yy" // lalr1.cc:859
+  case 137:
+#line 528 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
 }
-#line 1178 "d2_parser.cc" // lalr1.cc:859
+#line 1296 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 125:
-#line 504 "d2_parser.yy" // lalr1.cc:859
+  case 138:
+#line 532 "d2_parser.yy" // lalr1.cc:859
+    {
+    // Parse tsig key list entry map
+    ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+    ctx.stack_.push_back(m);
+}
+#line 1306 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 139:
+#line 536 "d2_parser.yy" // lalr1.cc:859
+    {
+    // parsing completed
+}
+#line 1314 "d2_parser.cc" // lalr1.cc:859
+    break;
+
+  case 147:
+#line 552 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1186 "d2_parser.cc" // lalr1.cc:859
+#line 1322 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 126:
-#line 506 "d2_parser.yy" // lalr1.cc:859
+  case 148:
+#line 554 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< std::string > () == "") {
-        error(yystack_[1].location, "name cannot be blank");
-    } 
+        error(yystack_[1].location, "TSIG key name cannot be blank");
+    }
     ElementPtr elem(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ElementPtr name(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("name", name);
     ctx.leave();
 }
-#line 1200 "d2_parser.cc" // lalr1.cc:859
+#line 1336 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 127:
-#line 516 "d2_parser.yy" // lalr1.cc:859
+  case 149:
+#line 564 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1208 "d2_parser.cc" // lalr1.cc:859
+#line 1344 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 128:
-#line 518 "d2_parser.yy" // lalr1.cc:859
+  case 150:
+#line 566 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< std::string > () == "") {
-        error(yystack_[1].location, "algorithm cannot be blank");
-    } 
+        error(yystack_[1].location, "TSIG key algorithm cannot be blank");
+    }
     ElementPtr elem(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("algorithm", elem);
     ctx.leave();
 }
-#line 1221 "d2_parser.cc" // lalr1.cc:859
+#line 1357 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 129:
-#line 527 "d2_parser.yy" // lalr1.cc:859
+  case 151:
+#line 575 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< int64_t > () < 0 || (yystack_[0].value.as< int64_t > () > 0  && (yystack_[0].value.as< int64_t > () % 8 != 0))) {
-        error(yystack_[0].location, "digest-bits must either be zero or a positive, multiple of eight");
-    } 
+        error(yystack_[0].location, "TSIG key digest-bits must either be zero or a positive, multiple of eight");
+    }
     ElementPtr elem(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("digest-bits", elem);
 }
-#line 1233 "d2_parser.cc" // lalr1.cc:859
+#line 1369 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 130:
-#line 535 "d2_parser.yy" // lalr1.cc:859
+  case 152:
+#line 583 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1241 "d2_parser.cc" // lalr1.cc:859
+#line 1377 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 131:
-#line 537 "d2_parser.yy" // lalr1.cc:859
+  case 153:
+#line 585 "d2_parser.yy" // lalr1.cc:859
     {
     if (yystack_[0].value.as< std::string > () == "") {
-        error(yystack_[1].location, "secret cannot be blank");
-    } 
+        error(yystack_[1].location, "TSIG key secret cannot be blank");
+    }
     ElementPtr elem(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("secret", elem);
     ctx.leave();
 }
-#line 1254 "d2_parser.cc" // lalr1.cc:859
+#line 1390 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 132:
-#line 550 "d2_parser.yy" // lalr1.cc:859
+  case 154:
+#line 598 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1262 "d2_parser.cc" // lalr1.cc:859
+#line 1398 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 133:
-#line 552 "d2_parser.yy" // lalr1.cc:859
+  case 155:
+#line 600 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.back()->set("Dhcp6", yystack_[0].value.as< ElementPtr > ());
     ctx.leave();
 }
-#line 1271 "d2_parser.cc" // lalr1.cc:859
+#line 1407 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 134:
-#line 557 "d2_parser.yy" // lalr1.cc:859
+  case 156:
+#line 605 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1279 "d2_parser.cc" // lalr1.cc:859
+#line 1415 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 135:
-#line 559 "d2_parser.yy" // lalr1.cc:859
+  case 157:
+#line 607 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.back()->set("Dhcp4", yystack_[0].value.as< ElementPtr > ());
     ctx.leave();
 }
-#line 1288 "d2_parser.cc" // lalr1.cc:859
+#line 1424 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 136:
-#line 569 "d2_parser.yy" // lalr1.cc:859
+  case 158:
+#line 617 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("Logging", m);
     ctx.stack_.push_back(m);
     ctx.enter(ctx.LOGGING);
 }
-#line 1299 "d2_parser.cc" // lalr1.cc:859
+#line 1435 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 137:
-#line 574 "d2_parser.yy" // lalr1.cc:859
+  case 159:
+#line 622 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 1308 "d2_parser.cc" // lalr1.cc:859
+#line 1444 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 141:
-#line 591 "d2_parser.yy" // lalr1.cc:859
+  case 163:
+#line 639 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("loggers", l);
     ctx.stack_.push_back(l);
     ctx.enter(ctx.LOGGERS);
 }
-#line 1319 "d2_parser.cc" // lalr1.cc:859
+#line 1455 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 142:
-#line 596 "d2_parser.yy" // lalr1.cc:859
+  case 164:
+#line 644 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 1328 "d2_parser.cc" // lalr1.cc:859
+#line 1464 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 145:
-#line 608 "d2_parser.yy" // lalr1.cc:859
+  case 167:
+#line 656 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr l(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->add(l);
     ctx.stack_.push_back(l);
 }
-#line 1338 "d2_parser.cc" // lalr1.cc:859
+#line 1474 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 146:
-#line 612 "d2_parser.yy" // lalr1.cc:859
+  case 168:
+#line 660 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
 }
-#line 1346 "d2_parser.cc" // lalr1.cc:859
+#line 1482 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 154:
-#line 627 "d2_parser.yy" // lalr1.cc:859
+  case 176:
+#line 675 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1354 "d2_parser.cc" // lalr1.cc:859
+#line 1490 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 155:
-#line 629 "d2_parser.yy" // lalr1.cc:859
+  case 177:
+#line 677 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr name(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("name", name);
     ctx.leave();
 }
-#line 1364 "d2_parser.cc" // lalr1.cc:859
+#line 1500 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 156:
-#line 635 "d2_parser.yy" // lalr1.cc:859
+  case 178:
+#line 683 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr dl(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("debuglevel", dl);
 }
-#line 1373 "d2_parser.cc" // lalr1.cc:859
+#line 1509 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 157:
-#line 639 "d2_parser.yy" // lalr1.cc:859
+  case 179:
+#line 687 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1381 "d2_parser.cc" // lalr1.cc:859
+#line 1517 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 158:
-#line 641 "d2_parser.yy" // lalr1.cc:859
+  case 180:
+#line 689 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr sev(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("severity", sev);
     ctx.leave();
 }
-#line 1391 "d2_parser.cc" // lalr1.cc:859
+#line 1527 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 159:
-#line 647 "d2_parser.yy" // lalr1.cc:859
+  case 181:
+#line 695 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("output_options", l);
     ctx.stack_.push_back(l);
     ctx.enter(ctx.OUTPUT_OPTIONS);
 }
-#line 1402 "d2_parser.cc" // lalr1.cc:859
+#line 1538 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 160:
-#line 652 "d2_parser.yy" // lalr1.cc:859
+  case 182:
+#line 700 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
     ctx.leave();
 }
-#line 1411 "d2_parser.cc" // lalr1.cc:859
+#line 1547 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 163:
-#line 661 "d2_parser.yy" // lalr1.cc:859
+  case 185:
+#line 709 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->add(m);
     ctx.stack_.push_back(m);
 }
-#line 1421 "d2_parser.cc" // lalr1.cc:859
+#line 1557 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 164:
-#line 665 "d2_parser.yy" // lalr1.cc:859
+  case 186:
+#line 713 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.stack_.pop_back();
 }
-#line 1429 "d2_parser.cc" // lalr1.cc:859
+#line 1565 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 167:
-#line 673 "d2_parser.yy" // lalr1.cc:859
+  case 189:
+#line 721 "d2_parser.yy" // lalr1.cc:859
     {
     ctx.enter(ctx.NO_KEYWORD);
 }
-#line 1437 "d2_parser.cc" // lalr1.cc:859
+#line 1573 "d2_parser.cc" // lalr1.cc:859
     break;
 
-  case 168:
-#line 675 "d2_parser.yy" // lalr1.cc:859
+  case 190:
+#line 723 "d2_parser.yy" // lalr1.cc:859
     {
     ElementPtr sev(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location)));
     ctx.stack_.back()->set("output", sev);
     ctx.leave();
 }
-#line 1447 "d2_parser.cc" // lalr1.cc:859
+#line 1583 "d2_parser.cc" // lalr1.cc:859
     break;
 
 
-#line 1451 "d2_parser.cc" // lalr1.cc:859
+#line 1587 "d2_parser.cc" // lalr1.cc:859
             default:
               break;
             }
@@ -1709,239 +1845,261 @@ namespace isc { namespace d2 {
   const short int
   D2Parser::yypact_[] =
   {
-     -24,  -104,  -104,  -104,     3,    -3,    -2,     0,  -104,  -104,
+      29,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
+      11,    -2,    16,    28,    37,    97,    81,    98,    99,   100,
+    -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
     -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
-    -104,  -104,  -104,  -104,    -3,    -8,     1,     4,  -104,    42,
-      47,    58,    68,    79,  -104,  -104,  -104,  -104,    83,  -104,
-      19,  -104,  -104,  -104,  -104,  -104,  -104,    88,    89,  -104,
-    -104,  -104,  -104,  -104,  -104,    21,  -104,  -104,  -104,  -104,
-    -104,  -104,  -104,  -104,  -104,  -104,    -3,    -3,  -104,    53,
-      91,    92,    93,    94,  -104,     1,  -104,    95,    59,    60,
-      96,    99,   100,   101,   102,     4,  -104,  -104,  -104,   103,
-      -3,    -3,   104,   105,  -104,    74,  -104,  -104,    73,    90,
-     107,   110,   113,  -104,    -3,  -104,  -104,     4,    87,  -104,
-    -104,  -104,  -104,  -104,   -13,   -13,   114,  -104,    27,  -104,
-      51,  -104,  -104,  -104,  -104,    63,  -104,  -104,  -104,    66,
-    -104,   116,   106,  -104,  -104,   119,    87,  -104,   120,   -13,
-    -104,  -104,    23,  -104,   114,   115,  -104,   121,  -104,  -104,
-     123,  -104,  -104,  -104,    67,  -104,  -104,  -104,  -104,  -104,
-    -104,   118,   122,   124,    97,   126,   127,    23,  -104,  -104,
-      31,  -104,  -104,   128,   129,  -104,   108,  -104,   109,   111,
-    -104,    24,   118,  -104,    22,  -104,   122,  -104,  -104,  -104,
-    -104,  -104,   131,  -104,  -104,    69,  -104,  -104,  -104,  -104,
-    -104,  -104,  -104,  -104,  -104,  -104,    70,  -104,  -104,  -104,
-    -104,  -104,   132,   133,    98,   134,    24,  -104,   138,   139,
-     140,    22,  -104,   112,   141,  -104,   125,  -104,   130,   142,
-     135,  -104,  -104,   144,  -104,  -104,   147,  -104,  -104,    80,
-    -104,  -104,   150,   145,  -104,   137,   144,  -104,    -5,  -104,
-     147,  -104,    76,  -104,  -104,  -104,   153,  -104,  -104,    77,
-    -104,  -104,  -104,  -104,  -104,   155,   137,  -104,   156,   136,
-     157,    -5,  -104,   143,  -104,   146,  -104,   148,  -104,  -104,
-    -104,  -104
+    -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,    -2,    15,
+       2,     3,    10,   101,     4,   102,    -5,   103,  -104,   106,
+     104,   110,    96,   112,  -104,  -104,  -104,  -104,   113,  -104,
+      12,  -104,  -104,  -104,  -104,  -104,  -104,   114,   116,  -104,
+    -104,  -104,  -104,  -104,  -104,    23,  -104,  -104,  -104,  -104,
+    -104,  -104,  -104,  -104,  -104,  -104,   118,  -104,  -104,  -104,
+      24,  -104,  -104,  -104,  -104,  -104,  -104,   117,   121,  -104,
+    -104,  -104,  -104,  -104,    33,  -104,  -104,  -104,  -104,  -104,
+     119,   123,  -104,  -104,   124,  -104,  -104,    51,  -104,  -104,
+    -104,  -104,  -104,    58,  -104,  -104,    -2,    -2,  -104,    69,
+     126,   127,   128,   129,  -104,     2,  -104,   130,    87,    88,
+     133,   136,   137,   138,   139,     3,  -104,   140,   105,   141,
+     142,    10,  -104,    10,  -104,   101,   143,   144,   145,     4,
+    -104,     4,  -104,   102,   146,   107,   147,    -5,  -104,    -5,
+     103,  -104,  -104,  -104,   148,    -2,    -2,   149,   150,  -104,
+     111,  -104,  -104,    82,   134,   152,   153,   156,  -104,   115,
+    -104,   120,   122,  -104,    55,  -104,   125,   158,   135,  -104,
+      57,  -104,   151,  -104,   154,  -104,    73,  -104,    -2,  -104,
+    -104,     3,   132,  -104,  -104,  -104,  -104,  -104,   -13,   -13,
+     101,  -104,  -104,  -104,  -104,  -104,   103,  -104,  -104,  -104,
+    -104,  -104,  -104,    74,  -104,    75,  -104,  -104,  -104,  -104,
+      76,  -104,  -104,  -104,    77,   159,    90,  -104,   162,   132,
+    -104,   164,   -13,  -104,  -104,  -104,  -104,   165,  -104,   168,
+    -104,   167,   102,  -104,    91,  -104,   169,    19,   167,  -104,
+    -104,  -104,  -104,   172,  -104,  -104,    83,  -104,  -104,  -104,
+    -104,  -104,  -104,   173,   176,   155,   177,    19,  -104,   157,
+     178,  -104,   160,  -104,  -104,   179,  -104,  -104,    95,  -104,
+     161,   179,  -104,  -104,    84,  -104,  -104,   180,   161,  -104,
+     163,  -104,  -104
   };
 
   const unsigned char
   D2Parser::yydefact_[] =
   {
-       0,     2,     4,     6,     0,     0,     0,     0,     1,    22,
-      16,    12,    11,     8,     9,    10,    15,     3,    13,    14,
-      29,     5,    40,     7,    24,    18,     0,     0,    26,     0,
-      25,     0,     0,    19,   132,   134,    38,   136,     0,    37,
-       0,    31,    36,    33,    35,    34,    53,     0,     0,    57,
-      61,    63,    65,   110,    52,     0,    42,    44,    45,    46,
-      47,    48,    49,    50,    51,    23,     0,     0,    17,     0,
-       0,     0,     0,     0,    28,     0,    30,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,    41,    27,    20,     0,
-       0,     0,     0,     0,    32,     0,    55,    56,     0,     0,
-       0,     0,     0,    43,     0,   133,   135,     0,     0,    54,
-      59,    60,    58,    62,    67,    67,   112,    21,     0,   141,
-       0,   138,   140,    73,    72,     0,    68,    69,    71,     0,
-     116,     0,   113,   114,    39,     0,     0,   137,     0,     0,
-      64,    66,     0,   111,     0,     0,   139,     0,    70,   127,
-       0,   130,   125,   124,     0,   118,   120,   121,   122,   123,
-     115,     0,    75,     0,     0,     0,     0,     0,   117,   145,
-       0,   143,    79,     0,    76,    77,     0,   129,     0,     0,
-     119,     0,     0,   142,     0,    74,     0,   128,   131,   126,
-     154,   159,     0,   157,   153,     0,   147,   149,   151,   152,
-     150,   144,    89,    91,    87,    86,     0,    81,    83,    84,
-      85,    78,     0,     0,     0,     0,     0,   146,     0,     0,
-       0,     0,    80,     0,     0,   156,     0,   148,     0,     0,
-       0,    82,   155,     0,   158,    90,    93,    88,   163,     0,
-     161,    97,     0,    94,    95,     0,     0,   160,     0,    92,
-       0,   167,     0,   165,   162,   107,     0,   105,   104,     0,
-      99,   101,   102,   103,    96,     0,     0,   164,     0,     0,
-       0,     0,    98,     0,   166,     0,   109,     0,   100,   168,
-     108,   106
+       0,     2,     4,     6,     8,    10,    12,    14,    16,    18,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       1,    34,    28,    24,    23,    20,    21,    22,    27,     3,
+      25,    26,    41,     5,    52,     7,   138,     9,   130,    11,
+      95,    13,    87,    15,   115,    17,   109,    19,    36,    30,
+       0,     0,     0,   132,     0,    89,     0,     0,    38,     0,
+      37,     0,     0,    31,   154,   156,    50,   158,     0,    49,
+       0,    43,    48,    45,    47,    46,    65,     0,     0,    69,
+      73,    75,    77,   128,    64,     0,    54,    56,    57,    58,
+      59,    60,    61,    62,    63,   149,     0,   152,   147,   146,
+       0,   140,   142,   143,   144,   145,   136,     0,   133,   134,
+     105,   107,   103,   102,     0,    97,    99,   100,   101,    93,
+       0,    90,    91,   125,     0,   123,   122,     0,   117,   119,
+     120,   121,   113,     0,   111,    35,     0,     0,    29,     0,
+       0,     0,     0,     0,    40,     0,    42,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,    53,     0,     0,     0,
+       0,     0,   139,     0,   131,     0,     0,     0,     0,     0,
+      96,     0,    88,     0,     0,     0,     0,     0,   116,     0,
+       0,   110,    39,    32,     0,     0,     0,     0,     0,    44,
+       0,    67,    68,     0,     0,     0,     0,     0,    55,     0,
+     151,     0,     0,   141,     0,   135,     0,     0,     0,    98,
+       0,    92,     0,   127,     0,   118,     0,   112,     0,   155,
+     157,     0,     0,    66,    71,    72,    70,    74,    79,    79,
+     132,   150,   153,   148,   137,   106,     0,   104,    94,   126,
+     124,   114,    33,     0,   163,     0,   160,   162,    85,    84,
+       0,    80,    81,    83,     0,     0,     0,    51,     0,     0,
+     159,     0,     0,    76,    78,   129,   108,     0,   161,     0,
+      82,     0,    89,   167,     0,   165,     0,     0,     0,   164,
+      86,   176,   181,     0,   179,   175,     0,   169,   171,   173,
+     174,   172,   166,     0,     0,     0,     0,     0,   168,     0,
+       0,   178,     0,   170,   177,     0,   180,   185,     0,   183,
+       0,     0,   182,   189,     0,   187,   184,     0,     0,   186,
+       0,   188,   190
   };
 
   const signed char
   D2Parser::yypgoto_[] =
   {
-    -104,  -104,  -104,  -104,  -104,   -23,  -104,  -104,  -104,  -104,
-    -104,  -104,  -104,  -104,  -104,   -26,  -104,  -104,  -104,    33,
-    -104,  -104,  -104,  -104,    26,    78,  -104,  -104,  -104,  -104,
-    -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,    30,
-    -104,    25,  -104,  -104,  -104,  -104,   -21,  -104,  -104,   -59,
-    -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,   -83,  -104,
-    -104,  -103,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
-    -104,    29,  -104,  -104,     2,  -104,  -104,  -104,  -104,  -104,
-    -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,    34,
-    -104,  -104,  -104,    -7,  -104,  -104,   -42,  -104,  -104,  -104,
-    -104,  -104,  -104,  -104,  -104,   -69,  -104,  -104,   -87,  -104
+    -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
+    -104,   -47,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
+    -104,   -50,  -104,  -104,  -104,    40,  -104,  -104,  -104,  -104,
+     -34,    34,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
+    -104,  -104,  -104,  -104,  -104,   -41,  -104,   -72,  -104,  -104,
+    -104,  -104,   -81,  -104,    20,  -104,  -104,  -104,    21,    25,
+    -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,   -39,    22,
+    -104,  -104,  -104,    26,    18,  -104,  -104,  -104,  -104,  -104,
+    -104,  -104,  -104,  -104,   -31,  -104,    35,  -104,  -104,  -104,
+      43,    47,  -104,  -104,  -104,  -104,  -104,  -104,  -104,  -104,
+    -104,  -104,  -104,  -104,  -104,  -104,   -48,  -104,  -104,  -104,
+     -69,  -104,  -104,   -84,  -104,  -104,  -104,  -104,  -104,  -104,
+    -104,  -104,   -97,  -104,  -104,  -103,  -104
   };
 
   const short int
   D2Parser::yydefgoto_[] =
   {
-      -1,     4,     5,     6,     7,    16,    17,    18,    25,    32,
-      33,    19,    24,    29,    30,    54,    21,    26,    40,    41,
-      42,    72,    23,    27,    55,    56,    57,    77,    58,    59,
-      60,    80,   112,    61,    81,    62,    82,    63,    83,   125,
-     126,   127,   128,   138,   173,   174,   175,   184,   206,   207,
-     208,   220,   209,   218,   210,   219,   242,   243,   244,   248,
-     259,   260,   261,   270,   262,   268,   263,    64,    84,   131,
-     132,   133,   142,   154,   155,   156,   166,   157,   163,   158,
-     159,   165,    43,    70,    44,    71,    45,    73,   120,   121,
-     122,   135,   170,   171,   181,   195,   196,   197,   212,   198,
-     199,   215,   200,   213,   239,   240,   245,   252,   253,   265
+      -1,    10,    11,    12,    13,    14,    15,    16,    17,    18,
+      19,    28,    29,    30,    49,    62,    63,    31,    48,    59,
+      60,    84,    33,    50,    70,    71,    72,   142,    35,    51,
+      85,    86,    87,   147,    88,    89,    90,   150,   226,    91,
+     151,    92,   152,    93,   153,   250,   251,   252,   253,   261,
+      43,    55,   120,   121,   122,   171,    41,    54,   114,   115,
+     116,   168,   117,   166,   118,   167,    47,    57,   133,   134,
+     179,    45,    56,   127,   128,   129,   176,   130,   174,   131,
+      94,   154,    39,    53,   107,   108,   109,   163,    37,    52,
+     100,   101,   102,   160,   103,   157,   104,   105,   159,    73,
+     140,    74,   141,    75,   143,   245,   246,   247,   258,   274,
+     275,   277,   286,   287,   288,   293,   289,   290,   296,   291,
+     294,   308,   309,   310,   314,   315,   317
   };
 
   const unsigned short int
   D2Parser::yytable_[] =
   {
-      39,    28,     9,     8,    10,    20,    11,    22,   255,   256,
-     123,    34,    35,    36,     1,     2,     3,    46,    47,    48,
-      49,   257,    75,    50,    85,    51,    52,    76,    38,    86,
-      85,    53,    37,    31,   182,   134,    38,   183,    12,    13,
-      14,    15,    38,    87,    88,    38,   202,   203,    65,    39,
-      66,   149,   150,   151,   136,   204,   152,   190,   191,   137,
-     192,   193,    67,    38,    38,    38,   139,   105,   106,   139,
-     167,   140,   216,   221,   141,   168,    68,   217,   222,   266,
-     271,   117,    69,   246,   267,   272,   247,    74,   124,   124,
-     110,   111,    78,    79,    89,    90,    91,    92,    93,    95,
-      98,    96,    97,    99,   100,   101,   102,   104,    94,   144,
-     113,   107,   108,   124,   114,   109,   153,   115,   116,   119,
-     161,   130,   143,   145,   147,   169,   162,   164,   176,   172,
-     178,   179,   186,   118,   185,   214,   223,   224,   226,   177,
-     225,   153,   228,   229,   230,   129,   233,   236,   250,   187,
-     188,   238,   189,   232,   241,   194,   249,   269,   205,   273,
-     275,   277,   231,   103,   148,   211,   234,   264,   278,   180,
-     146,   235,   251,   160,   227,   201,   237,   254,   276,   274,
-       0,     0,     0,     0,   279,     0,     0,   280,     0,   281,
-     194,     0,     0,     0,     0,   205,     0,     0,     0,     0,
+      69,    58,    99,    21,   113,    22,   126,    23,   123,   124,
+     248,    20,    64,    65,    66,   145,    76,    77,    78,    79,
+     146,   125,    80,    32,    81,    82,   155,   161,   110,   111,
+      83,   156,   162,    67,    68,    34,   169,   112,    95,    96,
+      97,   170,    68,    98,    36,    24,    25,    26,    27,    68,
+      68,    68,   281,   282,   177,   283,   284,    68,   161,   178,
+     169,   180,    61,   234,   181,   238,    68,     1,     2,     3,
+       4,     5,     6,     7,     8,     9,   177,   155,   259,   262,
+     262,   241,   257,   260,   263,   264,   297,   318,    40,   182,
+     183,   298,   319,   180,   278,    69,   266,   279,   311,   224,
+     225,   312,    38,    42,   138,    46,    44,   136,   106,   119,
+     132,    99,   135,    99,   137,   139,   184,   144,   148,   113,
+     149,   113,   158,   164,   165,   172,   173,   126,   175,   126,
+     185,   186,   187,   188,   190,   191,   192,   193,   219,   220,
+     194,   195,   196,   197,   199,   201,   202,   206,   207,   208,
+     212,   214,   218,   200,   227,   213,   221,   222,   223,   228,
+     229,   230,   231,   236,   244,   265,   267,   232,   269,   233,
+     271,   242,   235,   272,   273,   280,   295,   299,   249,   249,
+     300,   302,   237,   305,   320,   189,   307,   243,   254,   198,
+     270,   276,   210,   211,   209,   215,   313,   256,   239,   255,
+     205,   240,   217,   301,   304,   216,   204,   306,   203,   292,
+     322,   268,   249,   303,   316,   321,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,   285,     0,     0,
        0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,   258,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,   258
+       0,     0,     0,     0,     0,     0,     0,   285
   };
 
   const short int
   D2Parser::yycheck_[] =
   {
-      26,    24,     5,     0,     7,     7,     9,     7,    13,    14,
-      23,    10,    11,    12,    38,    39,    40,    13,    14,    15,
-      16,    26,     3,    19,     3,    21,    22,     8,    41,     8,
-       3,    27,    31,    41,     3,     8,    41,     6,    41,    42,
-      43,    44,    41,    66,    67,    41,    24,    25,     6,    75,
-       3,    28,    29,    30,     3,    33,    33,    33,    34,     8,
-      36,    37,     4,    41,    41,    41,     3,    90,    91,     3,
-       3,     8,     3,     3,     8,     8,     8,     8,     8,     3,
-       3,   104,     3,     3,     8,     8,     6,     4,   114,   115,
-      17,    18,     4,     4,    41,     4,     4,     4,     4,     4,
-       4,    42,    42,     4,     4,     4,     4,     4,    75,     3,
-      20,     7,     7,   139,     7,    41,   142,     7,     5,    32,
-       5,     7,     6,     4,     4,     7,     5,     4,     4,     7,
-       4,     4,     3,   107,     6,     4,     4,     4,     4,    42,
-      42,   167,     4,     4,     4,   115,     5,     5,     3,    41,
-      41,     7,    41,    41,     7,   181,     6,     4,   184,     4,
-       4,     4,   221,    85,   139,   186,    41,   250,   271,   167,
-     136,    41,    35,   144,   216,   182,    41,   246,    42,   266,
-      -1,    -1,    -1,    -1,    41,    -1,    -1,    41,    -1,    41,
-     216,    -1,    -1,    -1,    -1,   221,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,   248,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      50,    48,    52,     5,    54,     7,    56,     9,    13,    14,
+      23,     0,    10,    11,    12,     3,    13,    14,    15,    16,
+       8,    26,    19,     7,    21,    22,     3,     3,    24,    25,
+      27,     8,     8,    31,    47,     7,     3,    33,    28,    29,
+      30,     8,    47,    33,     7,    47,    48,    49,    50,    47,
+      47,    47,    33,    34,     3,    36,    37,    47,     3,     8,
+       3,     3,    47,     8,     6,     8,    47,    38,    39,    40,
+      41,    42,    43,    44,    45,    46,     3,     3,     3,     3,
+       3,     8,     8,     8,     8,     8,     3,     3,     7,   136,
+     137,     8,     8,     3,     3,   145,     6,     6,     3,    17,
+      18,     6,     5,     5,     8,     5,     7,     3,     7,     7,
+       7,   161,     6,   163,     4,     3,    47,     4,     4,   169,
+       4,   171,     4,     6,     3,     6,     3,   177,     4,   179,
+       4,     4,     4,     4,     4,    48,    48,     4,   185,   186,
+       4,     4,     4,     4,     4,     4,     4,     4,     4,     4,
+       4,     4,     4,    48,    20,    48,     7,     7,    47,     7,
+       7,     5,    47,     5,    32,     6,     4,    47,     4,    47,
+       5,   218,    47,     5,     7,     6,     4,     4,   228,   229,
+       4,     4,    47,     5,     4,   145,     7,   221,   229,   155,
+     262,   272,   171,   173,   169,   177,    35,   236,    47,   230,
+     165,    47,   180,    48,    47,   179,   163,    47,   161,   278,
+      47,   259,   262,   297,   311,   318,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,   277,    -1,    -1,
       -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,   271
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,   297
   };
 
   const unsigned char
   D2Parser::yystos_[] =
   {
-       0,    38,    39,    40,    46,    47,    48,    49,     0,     5,
-       7,     9,    41,    42,    43,    44,    50,    51,    52,    56,
-       7,    61,     7,    67,    57,    53,    62,    68,    50,    58,
-      59,    41,    54,    55,    10,    11,    12,    31,    41,    60,
-      63,    64,    65,   127,   129,   131,    13,    14,    15,    16,
-      19,    21,    22,    27,    60,    69,    70,    71,    73,    74,
-      75,    78,    80,    82,   112,     6,     3,     4,     8,     3,
-     128,   130,    66,   132,     4,     3,     8,    72,     4,     4,
-      76,    79,    81,    83,   113,     3,     8,    50,    50,    41,
-       4,     4,     4,     4,    64,     4,    42,    42,     4,     4,
-       4,     4,     4,    70,     4,    50,    50,     7,     7,    41,
-      17,    18,    77,    20,     7,     7,     5,    50,    69,    32,
-     133,   134,   135,    23,    60,    84,    85,    86,    87,    84,
-       7,   114,   115,   116,     8,   136,     3,     8,    88,     3,
-       8,     8,   117,     6,     3,     4,   134,     4,    86,    28,
-      29,    30,    33,    60,   118,   119,   120,   122,   124,   125,
-     116,     5,     5,   123,     4,   126,   121,     3,     8,     7,
-     137,   138,     7,    89,    90,    91,     4,    42,     4,     4,
-     119,   139,     3,     6,    92,     6,     3,    41,    41,    41,
-      33,    34,    36,    37,    60,   140,   141,   142,   144,   145,
-     147,   138,    24,    25,    33,    60,    93,    94,    95,    97,
-      99,    91,   143,   148,     4,   146,     3,     8,    98,   100,
-      96,     3,     8,     4,     4,    42,     4,   141,     4,     4,
-       4,    94,    41,     5,    41,    41,     5,    41,     7,   149,
-     150,     7,   101,   102,   103,   151,     3,     6,   104,     6,
-       3,    35,   152,   153,   150,    13,    14,    26,    60,   105,
-     106,   107,   109,   111,   103,   154,     3,     8,   110,     4,
-     108,     3,     8,     4,   153,     4,    42,     4,   106,    41,
-      41,    41
+       0,    38,    39,    40,    41,    42,    43,    44,    45,    46,
+      52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
+       0,     5,     7,     9,    47,    48,    49,    50,    62,    63,
+      64,    68,     7,    73,     7,    79,     7,   139,     5,   133,
+       7,   107,     5,   101,     7,   122,     5,   117,    69,    65,
+      74,    80,   140,   134,   108,   102,   123,   118,    62,    70,
+      71,    47,    66,    67,    10,    11,    12,    31,    47,    72,
+      75,    76,    77,   150,   152,   154,    13,    14,    15,    16,
+      19,    21,    22,    27,    72,    81,    82,    83,    85,    86,
+      87,    90,    92,    94,   131,    28,    29,    30,    33,    72,
+     141,   142,   143,   145,   147,   148,     7,   135,   136,   137,
+      24,    25,    33,    72,   109,   110,   111,   113,   115,     7,
+     103,   104,   105,    13,    14,    26,    72,   124,   125,   126,
+     128,   130,     7,   119,   120,     6,     3,     4,     8,     3,
+     151,   153,    78,   155,     4,     3,     8,    84,     4,     4,
+      88,    91,    93,    95,   132,     3,     8,   146,     4,   149,
+     144,     3,     8,   138,     6,     3,   114,   116,   112,     3,
+       8,   106,     6,     3,   129,     4,   127,     3,     8,   121,
+       3,     6,    62,    62,    47,     4,     4,     4,     4,    76,
+       4,    48,    48,     4,     4,     4,     4,     4,    82,     4,
+      48,     4,     4,   142,   141,   137,     4,     4,     4,   110,
+     109,   105,     4,    48,     4,   125,   124,   120,     4,    62,
+      62,     7,     7,    47,    17,    18,    89,    20,     7,     7,
+       5,    47,    47,    47,     8,    47,     5,    47,     8,    47,
+      47,     8,    62,    81,    32,   156,   157,   158,    23,    72,
+      96,    97,    98,    99,    96,   135,   119,     8,   159,     3,
+       8,   100,     3,     8,     8,     6,     6,     4,   157,     4,
+      98,     5,     5,     7,   160,   161,   103,   162,     3,     6,
+       6,    33,    34,    36,    37,    72,   163,   164,   165,   167,
+     168,   170,   161,   166,   171,     4,   169,     3,     8,     4,
+       4,    48,     4,   164,    47,     5,    47,     7,   172,   173,
+     174,     3,     6,    35,   175,   176,   173,   177,     3,     8,
+       4,   176,    47
   };
 
   const unsigned char
   D2Parser::yyr1_[] =
   {
-       0,    45,    47,    46,    48,    46,    49,    46,    50,    50,
-      50,    50,    50,    50,    50,    51,    53,    52,    54,    54,
-      55,    55,    57,    56,    58,    58,    59,    59,    60,    62,
-      61,    63,    63,    64,    64,    64,    64,    64,    66,    65,
-      68,    67,    69,    69,    70,    70,    70,    70,    70,    70,
-      70,    70,    70,    72,    71,    73,    74,    76,    75,    77,
-      77,    79,    78,    81,    80,    83,    82,    84,    84,    85,
-      85,    86,    86,    88,    87,    89,    89,    90,    90,    92,
-      91,    93,    93,    94,    94,    94,    94,    96,    95,    98,
-      97,   100,    99,   101,   101,   102,   102,   104,   103,   105,
-     105,   106,   106,   106,   106,   108,   107,   110,   109,   111,
-     113,   112,   114,   114,   115,   115,   117,   116,   118,   118,
-     119,   119,   119,   119,   119,   121,   120,   123,   122,   124,
-     126,   125,   128,   127,   130,   129,   132,   131,   133,   133,
-     134,   136,   135,   137,   137,   139,   138,   140,   140,   141,
-     141,   141,   141,   141,   143,   142,   144,   146,   145,   148,
-     147,   149,   149,   151,   150,   152,   152,   154,   153
+       0,    51,    53,    52,    54,    52,    55,    52,    56,    52,
+      57,    52,    58,    52,    59,    52,    60,    52,    61,    52,
+      62,    62,    62,    62,    62,    62,    62,    63,    65,    64,
+      66,    66,    67,    67,    69,    68,    70,    70,    71,    71,
+      72,    74,    73,    75,    75,    76,    76,    76,    76,    76,
+      78,    77,    80,    79,    81,    81,    82,    82,    82,    82,
+      82,    82,    82,    82,    82,    84,    83,    85,    86,    88,
+      87,    89,    89,    91,    90,    93,    92,    95,    94,    96,
+      96,    97,    97,    98,    98,   100,    99,   102,   101,   103,
+     103,   104,   104,   106,   105,   108,   107,   109,   109,   110,
+     110,   110,   110,   112,   111,   114,   113,   116,   115,   118,
+     117,   119,   119,   121,   120,   123,   122,   124,   124,   125,
+     125,   125,   125,   127,   126,   129,   128,   130,   132,   131,
+     134,   133,   135,   135,   136,   136,   138,   137,   140,   139,
+     141,   141,   142,   142,   142,   142,   142,   144,   143,   146,
+     145,   147,   149,   148,   151,   150,   153,   152,   155,   154,
+     156,   156,   157,   159,   158,   160,   160,   162,   161,   163,
+     163,   164,   164,   164,   164,   164,   166,   165,   167,   169,
+     168,   171,   170,   172,   172,   174,   173,   175,   175,   177,
+     176
   };
 
   const unsigned char
   D2Parser::yyr2_[] =
   {
-       0,     2,     0,     3,     0,     3,     0,     3,     1,     1,
-       1,     1,     1,     1,     1,     1,     0,     4,     0,     1,
-       3,     5,     0,     4,     0,     1,     1,     3,     2,     0,
-       4,     1,     3,     1,     1,     1,     1,     1,     0,     6,
-       0,     4,     1,     3,     1,     1,     1,     1,     1,     1,
-       1,     1,     1,     0,     4,     3,     3,     0,     4,     1,
-       1,     0,     4,     0,     6,     0,     6,     0,     1,     1,
-       3,     1,     1,     0,     6,     0,     1,     1,     3,     0,
-       4,     1,     3,     1,     1,     1,     1,     0,     4,     0,
-       4,     0,     6,     0,     1,     1,     3,     0,     4,     1,
-       3,     1,     1,     1,     1,     0,     4,     0,     4,     3,
-       0,     6,     0,     1,     1,     3,     0,     4,     1,     3,
-       1,     1,     1,     1,     1,     0,     4,     0,     4,     3,
-       0,     4,     0,     4,     0,     4,     0,     6,     1,     3,
-       1,     0,     6,     1,     3,     0,     4,     1,     3,     1,
-       1,     1,     1,     1,     0,     4,     3,     0,     4,     0,
-       6,     1,     3,     0,     4,     1,     3,     0,     4
+       0,     2,     0,     3,     0,     3,     0,     3,     0,     3,
+       0,     3,     0,     3,     0,     3,     0,     3,     0,     3,
+       1,     1,     1,     1,     1,     1,     1,     1,     0,     4,
+       0,     1,     3,     5,     0,     4,     0,     1,     1,     3,
+       2,     0,     4,     1,     3,     1,     1,     1,     1,     1,
+       0,     6,     0,     4,     1,     3,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     0,     4,     3,     3,     0,
+       4,     1,     1,     0,     4,     0,     6,     0,     6,     0,
+       1,     1,     3,     1,     1,     0,     6,     0,     4,     0,
+       1,     1,     3,     0,     4,     0,     4,     1,     3,     1,
+       1,     1,     1,     0,     4,     0,     4,     0,     6,     0,
+       4,     1,     3,     0,     4,     0,     4,     1,     3,     1,
+       1,     1,     1,     0,     4,     0,     4,     3,     0,     6,
+       0,     4,     0,     1,     1,     3,     0,     4,     0,     4,
+       1,     3,     1,     1,     1,     1,     1,     0,     4,     0,
+       4,     3,     0,     4,     0,     4,     0,     4,     0,     6,
+       1,     3,     1,     0,     6,     1,     3,     0,     4,     1,
+       3,     1,     1,     1,     1,     1,     0,     4,     3,     0,
+       4,     0,     6,     1,     3,     0,     4,     1,     3,     0,
+       4
   };
 
 
@@ -1960,55 +2118,63 @@ namespace isc { namespace d2 {
   "\"algorithm\"", "\"digest-bits\"", "\"secret\"", "\"Logging\"",
   "\"loggers\"", "\"name\"", "\"output_options\"", "\"output\"",
   "\"debuglevel\"", "\"severity\"", "TOPLEVEL_JSON", "TOPLEVEL_DHCPDDNS",
-  "SUB_DHCPDDNS", "\"constant string\"", "\"integer\"",
-  "\"floating point\"", "\"boolean\"", "$accept", "start", "$@1", "$@2",
-  "$@3", "value", "sub_json", "map2", "$@4", "map_content",
-  "not_empty_map", "list_generic", "$@5", "list_content", "not_empty_list",
-  "unknown_map_entry", "syntax_map", "$@6", "global_objects",
-  "global_object", "dhcpddns_object", "$@7", "sub_dhcpddns", "$@8",
-  "dhcpddns_params", "dhcpddns_param", "ip_address", "$@9", "port",
-  "dns_server_timeout", "ncr_protocol", "$@10", "ncr_protocol_value",
-  "ncr_format", "$@11", "forward_ddns", "$@12", "reverse_ddns", "$@13",
-  "ddns_mgr_params", "not_empty_ddns_mgr_params", "ddns_mgr_param",
-  "ddns_domains", "$@14", "ddns_domain_list", "not_empty_ddns_domain_list",
-  "ddns_domain", "$@15", "ddns_domain_params", "ddns_domain_param",
-  "ddns_domain_name", "$@16", "ddns_domain_key_name", "$@17",
-  "dns_servers", "$@18", "dns_server_list", "not_empty_dns_server_list",
-  "dns_server", "$@19", "dns_server_params", "dns_server_param",
-  "dns_server_hostname", "$@20", "dns_server_ip_address", "$@21",
-  "dns_server_port", "tsig_keys", "$@22", "tsig_keys_list",
-  "not_empty_tsig_keys_list", "tsig_key", "$@23", "tsig_key_params",
-  "tsig_key_param", "tsig_key_name", "$@24", "tsig_key_algorithm", "$@25",
-  "tsig_key_digest_bits", "tsig_key_secret", "$@26", "dhcp6_json_object",
-  "$@27", "dhcp4_json_object", "$@28", "logging_object", "$@29",
-  "logging_params", "logging_param", "loggers", "$@30", "loggers_entries",
-  "logger_entry", "$@31", "logger_params", "logger_param", "name", "$@32",
-  "debuglevel", "severity", "$@33", "output_options_list", "$@34",
-  "output_options_list_content", "output_entry", "$@35", "output_params",
-  "output_param", "$@36", YY_NULLPTR
+  "SUB_DHCPDDNS", "SUB_TSIG_KEY", "SUB_TSIG_KEYS", "SUB_DDNS_DOMAIN",
+  "SUB_DDNS_DOMAINS", "SUB_DNS_SERVER", "SUB_DNS_SERVERS",
+  "\"constant string\"", "\"integer\"", "\"floating point\"",
+  "\"boolean\"", "$accept", "start", "$@1", "$@2", "$@3", "$@4", "$@5",
+  "$@6", "$@7", "$@8", "$@9", "value", "sub_json", "map2", "$@10",
+  "map_content", "not_empty_map", "list_generic", "$@11", "list_content",
+  "not_empty_list", "unknown_map_entry", "syntax_map", "$@12",
+  "global_objects", "global_object", "dhcpddns_object", "$@13",
+  "sub_dhcpddns", "$@14", "dhcpddns_params", "dhcpddns_param",
+  "ip_address", "$@15", "port", "dns_server_timeout", "ncr_protocol",
+  "$@16", "ncr_protocol_value", "ncr_format", "$@17", "forward_ddns",
+  "$@18", "reverse_ddns", "$@19", "ddns_mgr_params",
+  "not_empty_ddns_mgr_params", "ddns_mgr_param", "ddns_domains", "$@20",
+  "sub_ddns_domains", "$@21", "ddns_domain_list",
+  "not_empty_ddns_domain_list", "ddns_domain", "$@22", "sub_ddns_domain",
+  "$@23", "ddns_domain_params", "ddns_domain_param", "ddns_domain_name",
+  "$@24", "ddns_domain_key_name", "$@25", "dns_servers", "$@26",
+  "sub_dns_servers", "$@27", "dns_server_list", "dns_server", "$@28",
+  "sub_dns_server", "$@29", "dns_server_params", "dns_server_param",
+  "dns_server_hostname", "$@30", "dns_server_ip_address", "$@31",
+  "dns_server_port", "tsig_keys", "$@32", "sub_tsig_keys", "$@33",
+  "tsig_keys_list", "not_empty_tsig_keys_list", "tsig_key", "$@34",
+  "sub_tsig_key", "$@35", "tsig_key_params", "tsig_key_param",
+  "tsig_key_name", "$@36", "tsig_key_algorithm", "$@37",
+  "tsig_key_digest_bits", "tsig_key_secret", "$@38", "dhcp6_json_object",
+  "$@39", "dhcp4_json_object", "$@40", "logging_object", "$@41",
+  "logging_params", "logging_param", "loggers", "$@42", "loggers_entries",
+  "logger_entry", "$@43", "logger_params", "logger_param", "name", "$@44",
+  "debuglevel", "severity", "$@45", "output_options_list", "$@46",
+  "output_options_list_content", "output_entry", "$@47", "output_params",
+  "output_param", "$@48", YY_NULLPTR
   };
 
 #if D2_PARSER_DEBUG
   const unsigned short int
   D2Parser::yyrline_[] =
   {
-       0,   107,   107,   107,   108,   108,   109,   109,   117,   118,
-     119,   120,   121,   122,   123,   126,   131,   131,   143,   144,
-     147,   151,   158,   158,   165,   166,   169,   173,   184,   194,
-     194,   206,   207,   211,   212,   213,   214,   215,   220,   220,
-     230,   230,   238,   239,   242,   243,   244,   245,   246,   247,
-     248,   249,   250,   253,   253,   261,   269,   278,   278,   286,
-     287,   290,   290,   298,   298,   308,   308,   318,   319,   322,
-     323,   326,   327,   333,   333,   343,   344,   347,   348,   351,
-     351,   359,   360,   363,   364,   365,   366,   370,   370,   382,
-     382,   397,   397,   407,   408,   411,   412,   415,   415,   423,
-     424,   427,   428,   429,   430,   433,   433,   445,   445,   453,
-     467,   467,   477,   478,   481,   482,   485,   485,   493,   494,
-     497,   498,   499,   500,   501,   504,   504,   516,   516,   527,
-     535,   535,   550,   550,   557,   557,   569,   569,   582,   583,
-     587,   591,   591,   603,   604,   608,   608,   616,   617,   620,
-     621,   622,   623,   624,   627,   627,   635,   639,   639,   647,
-     647,   657,   658,   661,   661,   669,   670,   673,   673
+       0,   113,   113,   113,   114,   114,   115,   115,   116,   116,
+     117,   117,   118,   118,   119,   119,   120,   120,   121,   121,
+     129,   130,   131,   132,   133,   134,   135,   138,   143,   143,
+     155,   156,   159,   163,   170,   170,   177,   178,   181,   185,
+     196,   206,   206,   218,   219,   223,   224,   225,   226,   227,
+     232,   232,   242,   242,   250,   251,   254,   255,   256,   257,
+     258,   259,   260,   261,   262,   265,   265,   273,   281,   290,
+     290,   298,   299,   302,   302,   310,   310,   320,   320,   330,
+     331,   334,   335,   338,   339,   344,   344,   354,   354,   361,
+     362,   365,   366,   369,   369,   377,   377,   384,   385,   388,
+     389,   390,   391,   395,   395,   407,   407,   419,   419,   429,
+     429,   436,   437,   440,   440,   448,   448,   455,   456,   459,
+     460,   461,   462,   465,   465,   477,   477,   485,   499,   499,
+     509,   509,   516,   517,   520,   521,   524,   524,   532,   532,
+     541,   542,   545,   546,   547,   548,   549,   552,   552,   564,
+     564,   575,   583,   583,   598,   598,   605,   605,   617,   617,
+     630,   631,   635,   639,   639,   651,   652,   656,   656,   664,
+     665,   668,   669,   670,   671,   672,   675,   675,   683,   687,
+     687,   695,   695,   705,   706,   709,   709,   717,   718,   721,
+     721
   };
 
   // Print the state stack on the debug stream.
@@ -2043,8 +2209,8 @@ namespace isc { namespace d2 {
 
 #line 14 "d2_parser.yy" // lalr1.cc:1167
 } } // isc::d2
-#line 2047 "d2_parser.cc" // lalr1.cc:1167
-#line 681 "d2_parser.yy" // lalr1.cc:1168
+#line 2213 "d2_parser.cc" // lalr1.cc:1167
+#line 729 "d2_parser.yy" // lalr1.cc:1168
 
 
 void
index 11a29c190f379ca732e59929df17457c92a4f6e4..2bc6746ec430935ac6cae5ce07b22c2442cf7449 100644 (file)
@@ -380,10 +380,16 @@ namespace isc { namespace d2 {
         TOKEN_TOPLEVEL_JSON = 293,
         TOKEN_TOPLEVEL_DHCPDDNS = 294,
         TOKEN_SUB_DHCPDDNS = 295,
-        TOKEN_STRING = 296,
-        TOKEN_INTEGER = 297,
-        TOKEN_FLOAT = 298,
-        TOKEN_BOOLEAN = 299
+        TOKEN_SUB_TSIG_KEY = 296,
+        TOKEN_SUB_TSIG_KEYS = 297,
+        TOKEN_SUB_DDNS_DOMAIN = 298,
+        TOKEN_SUB_DDNS_DOMAINS = 299,
+        TOKEN_SUB_DNS_SERVER = 300,
+        TOKEN_SUB_DNS_SERVERS = 301,
+        TOKEN_STRING = 302,
+        TOKEN_INTEGER = 303,
+        TOKEN_FLOAT = 304,
+        TOKEN_BOOLEAN = 305
       };
     };
 
@@ -654,6 +660,30 @@ namespace isc { namespace d2 {
     symbol_type
     make_SUB_DHCPDDNS (const location_type& l);
 
+    static inline
+    symbol_type
+    make_SUB_TSIG_KEY (const location_type& l);
+
+    static inline
+    symbol_type
+    make_SUB_TSIG_KEYS (const location_type& l);
+
+    static inline
+    symbol_type
+    make_SUB_DDNS_DOMAIN (const location_type& l);
+
+    static inline
+    symbol_type
+    make_SUB_DDNS_DOMAINS (const location_type& l);
+
+    static inline
+    symbol_type
+    make_SUB_DNS_SERVER (const location_type& l);
+
+    static inline
+    symbol_type
+    make_SUB_DNS_SERVERS (const location_type& l);
+
     static inline
     symbol_type
     make_STRING (const std::string& v, const location_type& l);
@@ -875,12 +905,12 @@ namespace isc { namespace d2 {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 245,     ///< Last index in yytable_.
-      yynnts_ = 110,  ///< Number of nonterminal symbols.
-      yyfinal_ = 8, ///< Termination state number.
+      yylast_ = 247,     ///< Last index in yytable_.
+      yynnts_ = 127,  ///< Number of nonterminal symbols.
+      yyfinal_ = 20, ///< Termination state number.
       yyterror_ = 1,
       yyerrcode_ = 256,
-      yyntokens_ = 45  ///< Number of tokens.
+      yyntokens_ = 51  ///< Number of tokens.
     };
 
 
@@ -926,9 +956,10 @@ namespace isc { namespace d2 {
        5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
       15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
       25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
-      35,    36,    37,    38,    39,    40,    41,    42,    43,    44
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50
     };
-    const unsigned int user_token_number_max_ = 299;
+    const unsigned int user_token_number_max_ = 305;
     const token_number_type undef_token_ = 2;
 
     if (static_cast<int>(t) <= yyeof_)
@@ -961,24 +992,24 @@ namespace isc { namespace d2 {
   {
       switch (other.type_get ())
     {
-      case 50: // value
-      case 77: // ncr_protocol_value
+      case 62: // value
+      case 89: // ncr_protocol_value
         value.copy< ElementPtr > (other.value);
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
         value.copy< bool > (other.value);
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
         value.copy< double > (other.value);
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
         value.copy< int64_t > (other.value);
         break;
 
-      case 41: // "constant string"
+      case 47: // "constant string"
         value.copy< std::string > (other.value);
         break;
 
@@ -999,24 +1030,24 @@ namespace isc { namespace d2 {
     (void) v;
       switch (this->type_get ())
     {
-      case 50: // value
-      case 77: // ncr_protocol_value
+      case 62: // value
+      case 89: // ncr_protocol_value
         value.copy< ElementPtr > (v);
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
         value.copy< bool > (v);
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
         value.copy< double > (v);
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
         value.copy< int64_t > (v);
         break;
 
-      case 41: // "constant string"
+      case 47: // "constant string"
         value.copy< std::string > (v);
         break;
 
@@ -1096,24 +1127,24 @@ namespace isc { namespace d2 {
     // Type destructor.
     switch (yytype)
     {
-      case 50: // value
-      case 77: // ncr_protocol_value
+      case 62: // value
+      case 89: // ncr_protocol_value
         value.template destroy< ElementPtr > ();
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
         value.template destroy< bool > ();
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
         value.template destroy< double > ();
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
         value.template destroy< int64_t > ();
         break;
 
-      case 41: // "constant string"
+      case 47: // "constant string"
         value.template destroy< std::string > ();
         break;
 
@@ -1140,24 +1171,24 @@ namespace isc { namespace d2 {
     super_type::move(s);
       switch (this->type_get ())
     {
-      case 50: // value
-      case 77: // ncr_protocol_value
+      case 62: // value
+      case 89: // ncr_protocol_value
         value.move< ElementPtr > (s.value);
         break;
 
-      case 44: // "boolean"
+      case 50: // "boolean"
         value.move< bool > (s.value);
         break;
 
-      case 43: // "floating point"
+      case 49: // "floating point"
         value.move< double > (s.value);
         break;
 
-      case 42: // "integer"
+      case 48: // "integer"
         value.move< int64_t > (s.value);
         break;
 
-      case 41: // "constant string"
+      case 47: // "constant string"
         value.move< std::string > (s.value);
         break;
 
@@ -1220,7 +1251,8 @@ namespace isc { namespace d2 {
      265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
      275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
      285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
-     295,   296,   297,   298,   299
+     295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
+     305
     };
     return static_cast<token_type> (yytoken_number_[type]);
   }
@@ -1459,6 +1491,42 @@ namespace isc { namespace d2 {
     return symbol_type (token::TOKEN_SUB_DHCPDDNS, l);
   }
 
+  D2Parser::symbol_type
+  D2Parser::make_SUB_TSIG_KEY (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SUB_TSIG_KEY, l);
+  }
+
+  D2Parser::symbol_type
+  D2Parser::make_SUB_TSIG_KEYS (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SUB_TSIG_KEYS, l);
+  }
+
+  D2Parser::symbol_type
+  D2Parser::make_SUB_DDNS_DOMAIN (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SUB_DDNS_DOMAIN, l);
+  }
+
+  D2Parser::symbol_type
+  D2Parser::make_SUB_DDNS_DOMAINS (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SUB_DDNS_DOMAINS, l);
+  }
+
+  D2Parser::symbol_type
+  D2Parser::make_SUB_DNS_SERVER (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SUB_DNS_SERVER, l);
+  }
+
+  D2Parser::symbol_type
+  D2Parser::make_SUB_DNS_SERVERS (const location_type& l)
+  {
+    return symbol_type (token::TOKEN_SUB_DNS_SERVERS, l);
+  }
+
   D2Parser::symbol_type
   D2Parser::make_STRING (const std::string& v, const location_type& l)
   {
@@ -1486,7 +1554,7 @@ namespace isc { namespace d2 {
 
 #line 14 "d2_parser.yy" // lalr1.cc:392
 } } // isc::d2
-#line 1490 "d2_parser.h" // lalr1.cc:392
+#line 1558 "d2_parser.h" // lalr1.cc:392
 
 
 
index 5000a75042b582d05cc4374d83154e15f3b11c32..d5e85faee71cb69e64c586a7fbb376783abf1e96 100644 (file)
@@ -85,6 +85,12 @@ using namespace std;
   TOPLEVEL_JSON
   TOPLEVEL_DHCPDDNS
   SUB_DHCPDDNS
+  SUB_TSIG_KEY
+  SUB_TSIG_KEYS
+  SUB_DDNS_DOMAIN
+  SUB_DDNS_DOMAINS
+  SUB_DNS_SERVER
+  SUB_DNS_SERVERS
 ;
 
 %token <std::string> STRING "constant string"
@@ -107,6 +113,12 @@ using namespace std;
 start: TOPLEVEL_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } sub_json
      | TOPLEVEL_DHCPDDNS { ctx.ctx_ = ctx.CONFIG; } syntax_map
      | SUB_DHCPDDNS { ctx.ctx_ = ctx.DHCPDDNS; } sub_dhcpddns
+     | SUB_TSIG_KEY { ctx.ctx_ = ctx.TSIG_KEY; } sub_tsig_key
+     | SUB_TSIG_KEYS { ctx.ctx_ = ctx.TSIG_KEYS; } sub_tsig_keys
+     | SUB_DDNS_DOMAIN { ctx.ctx_ = ctx.DDNS_DOMAIN; } sub_ddns_domain
+     | SUB_DDNS_DOMAINS { ctx.ctx_ = ctx.DDNS_DOMAINS; } sub_ddns_domains
+     | SUB_DNS_SERVER { ctx.ctx_ = ctx.DNS_SERVERS; } sub_dns_server
+     | SUB_DNS_SERVERS { ctx.ctx_ = ctx.DNS_SERVERS; } sub_dns_servers
      ;
 
 // ---- generic JSON parser ---------------------------------
@@ -261,7 +273,7 @@ ip_address: IP_ADDRESS {
 port: PORT COLON INTEGER {
     if ($3 <= 0) {
         error(@3, "port must be greater than zero");
-    } 
+    }
     ElementPtr i(new IntElement($3, ctx.loc2pos(@3)));
     ctx.stack_.back()->set("port", i);
 };
@@ -269,7 +281,7 @@ port: PORT COLON INTEGER {
 dns_server_timeout: DNS_SERVER_TIMEOUT COLON INTEGER {
     if ($3 <= 0) {
         error(@3, "dns-server-timeout must be greater than zero");
-    } else { 
+    } else {
         ElementPtr i(new IntElement($3, ctx.loc2pos(@3)));
         ctx.stack_.back()->set("dns-server-timeout", i);
     }
@@ -319,17 +331,16 @@ ddns_mgr_params: %empty
                | not_empty_ddns_mgr_params
                ;
 
-not_empty_ddns_mgr_params: ddns_mgr_param 
+not_empty_ddns_mgr_params: ddns_mgr_param
                          | ddns_mgr_params COMMA ddns_mgr_param
                          ;
 
-ddns_mgr_param: ddns_domains 
+ddns_mgr_param: ddns_domains
               | unknown_map_entry
               ;
 
 
 // --- ddns-domains ----------------------------------------
-
 ddns_domains: DDNS_DOMAINS {
     ElementPtr l(new ListElement(ctx.loc2pos(@1)));
     ctx.stack_.back()->set("ddns-domains", l);
@@ -340,6 +351,13 @@ ddns_domains: DDNS_DOMAINS {
     ctx.leave();
 };
 
+sub_ddns_domains: LSQUARE_BRACKET {
+    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+    ctx.stack_.push_back(l);
+} ddns_domain_list RSQUARE_BRACKET {
+    // parsing completed
+}
+
 ddns_domain_list: %empty
               | not_empty_ddns_domain_list
               ;
@@ -356,7 +374,14 @@ ddns_domain: LCURLY_BRACKET {
     ctx.stack_.pop_back();
 };
 
-ddns_domain_params: ddns_domain_param 
+sub_ddns_domain: LCURLY_BRACKET {
+    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+    ctx.stack_.push_back(m);
+} ddns_domain_params RCURLY_BRACKET {
+    // parsing completed
+};
+
+ddns_domain_params: ddns_domain_param
                   | ddns_domain_params COMMA ddns_domain_param
                   ;
 
@@ -371,8 +396,8 @@ ddns_domain_name: NAME {
     ctx.enter(ctx.NO_KEYWORD);
 } COLON STRING {
     if ($4 == "") {
-        error(@3, "name cannot be blank");
-    } 
+        error(@3, "Ddns domain name cannot be blank");
+    }
     ElementPtr elem(new StringElement($4, ctx.loc2pos(@4)));
     ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
     ctx.stack_.back()->set("name", name);
@@ -382,9 +407,6 @@ ddns_domain_name: NAME {
 ddns_domain_key_name: KEY_NAME {
     ctx.enter(ctx.NO_KEYWORD);
 } COLON STRING {
-    if ($4 == "") {
-        error(@3, "key-name cannot be blank");
-    } 
     ElementPtr elem(new StringElement($4, ctx.loc2pos(@4)));
     ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
     ctx.stack_.back()->set("key-name", name);
@@ -404,13 +426,16 @@ dns_servers: DNS_SERVERS {
     ctx.leave();
 };
 
-dns_server_list: %empty
-              | not_empty_dns_server_list
-              ;
+sub_dns_servers: LSQUARE_BRACKET {
+    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+    ctx.stack_.push_back(l);
+} dns_server_list RSQUARE_BRACKET {
+    // parsing completed
+}
 
-not_empty_dns_server_list: dns_server
-                        | not_empty_dns_server_list COMMA dns_server
-                        ;
+dns_server_list: dns_server
+               dns_server_list COMMA dns_server
+               ;
 
 dns_server: LCURLY_BRACKET {
     ElementPtr m(new MapElement(ctx.loc2pos(@1)));
@@ -420,7 +445,14 @@ dns_server: LCURLY_BRACKET {
     ctx.stack_.pop_back();
 };
 
-dns_server_params: dns_server_param 
+sub_dns_server: LCURLY_BRACKET {
+    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+    ctx.stack_.push_back(m);
+} dns_server_params RCURLY_BRACKET {
+    // parsing completed
+};
+
+dns_server_params: dns_server_param
                | dns_server_params COMMA dns_server_param
                ;
 
@@ -433,9 +465,9 @@ dns_server_param: dns_server_hostname
 dns_server_hostname: HOSTNAME {
     ctx.enter(ctx.NO_KEYWORD);
 } COLON STRING {
-    if ($4 == "") {
-        error(@3, "hostname cannot be blank");
-    } 
+    if ($4 != "") {
+        error(@3, "hostname is not yet supported");
+    }
     ElementPtr elem(new StringElement($4, ctx.loc2pos(@4)));
     ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
     ctx.stack_.back()->set("hostname", name);
@@ -453,7 +485,7 @@ dns_server_ip_address: IP_ADDRESS {
 dns_server_port: PORT COLON INTEGER {
     if ($3 <= 0) {
         error(@3, "port must be greater than zero");
-    } 
+    }
     ElementPtr i(new IntElement($3, ctx.loc2pos(@3)));
     ctx.stack_.back()->set("port", i);
 };
@@ -474,6 +506,13 @@ tsig_keys: TSIG_KEYS {
     ctx.leave();
 };
 
+sub_tsig_keys: LSQUARE_BRACKET {
+    ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+    ctx.stack_.push_back(l);
+} tsig_keys_list RSQUARE_BRACKET {
+    // parsing completed
+}
+
 tsig_keys_list: %empty
               | not_empty_tsig_keys_list
               ;
@@ -490,7 +529,16 @@ tsig_key: LCURLY_BRACKET {
     ctx.stack_.pop_back();
 };
 
-tsig_key_params: tsig_key_param 
+sub_tsig_key: LCURLY_BRACKET {
+    // Parse tsig key list entry map
+    ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+    ctx.stack_.push_back(m);
+} tsig_key_params RCURLY_BRACKET {
+    // parsing completed
+};
+
+
+tsig_key_params: tsig_key_param
                | tsig_key_params COMMA tsig_key_param
                ;
 
@@ -505,8 +553,8 @@ tsig_key_name: NAME {
     ctx.enter(ctx.NO_KEYWORD);
 } COLON STRING {
     if ($4 == "") {
-        error(@3, "name cannot be blank");
-    } 
+        error(@3, "TSIG key name cannot be blank");
+    }
     ElementPtr elem(new StringElement($4, ctx.loc2pos(@4)));
     ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
     ctx.stack_.back()->set("name", name);
@@ -517,8 +565,8 @@ tsig_key_algorithm: ALGORITHM {
     ctx.enter(ctx.NO_KEYWORD);
 } COLON STRING {
     if ($4 == "") {
-        error(@3, "algorithm cannot be blank");
-    } 
+        error(@3, "TSIG key algorithm cannot be blank");
+    }
     ElementPtr elem(new StringElement($4, ctx.loc2pos(@4)));
     ctx.stack_.back()->set("algorithm", elem);
     ctx.leave();
@@ -526,8 +574,8 @@ tsig_key_algorithm: ALGORITHM {
 
 tsig_key_digest_bits: DIGEST_BITS COLON INTEGER {
     if ($3 < 0 || ($3 > 0  && ($3 % 8 != 0))) {
-        error(@3, "digest-bits must either be zero or a positive, multiple of eight");
-    } 
+        error(@3, "TSIG key digest-bits must either be zero or a positive, multiple of eight");
+    }
     ElementPtr elem(new IntElement($3, ctx.loc2pos(@3)));
     ctx.stack_.back()->set("digest-bits", elem);
 };
@@ -536,8 +584,8 @@ tsig_key_secret: SECRET {
     ctx.enter(ctx.NO_KEYWORD);
 } COLON STRING {
     if ($4 == "") {
-        error(@3, "secret cannot be blank");
-    } 
+        error(@3, "TSIG key secret cannot be blank");
+    }
     ElementPtr elem(new StringElement($4, ctx.loc2pos(@4)));
     ctx.stack_.back()->set("secret", elem);
     ctx.leave();
index 58327ff3b88f0cd161ccce77b524b993d8f480a7..09ce3d27bd519ca32384ede95260ba6b9120e8b7 100644 (file)
@@ -36,23 +36,110 @@ namespace d2 {
 /// used.
 const SimpleDefaults D2SimpleParser::D2_GLOBAL_DEFAULTS = {
     { "ip-address",         Element::string, D2Params::DFT_IP_ADDRESS },
-    { "port",               Element::integer, D2Params::DFT_PORT },
-    { "dns-server-timeout", Element::integer, D2Params::DFT_DNS_SERVER_TIMEOUT },
+    { "port",               Element::integer, D2Params::DFT_PORT_STR },
+    { "dns-server-timeout", Element::integer, D2Params::DFT_DNS_SERVER_TIMEOUT_STR },
     { "ncr-protocol",       Element::string, D2Params::DFT_NCR_PROTOCOL },
     { "ncr-format",         Element::string, D2Params::DFT_NCR_FORMAT }
 };
 
+const SimpleDefaults D2SimpleParser::TSIG_KEY_DEFAULTS = {
+    { "digest-bits",    Element::integer, "0" }
+};
+
+const SimpleDefaults D2SimpleParser::DDNS_DOMAIN_MGR_DEFAULTS = {
+    // none yet
+};
+
+const SimpleDefaults D2SimpleParser::DDNS_DOMAIN_DEFAULTS = {
+    { "key-name",    Element::string, "" }
+};
+
+const SimpleDefaults D2SimpleParser::DNS_SERVER_DEFAULTS = {
+    { "hostname", Element::string, "" },
+    { "port",     Element::integer, DnsServerInfo::STANDARD_DNS_PORT_STR },
+};
+
 /// @}
 
 /// ---------------------------------------------------------------------------
 /// --- end of default values -------------------------------------------------
 /// ---------------------------------------------------------------------------
 
-size_t D2SimpleParser::setAllDefaults(isc::data::ElementPtr global) {
+size_t
+D2SimpleParser::setAllDefaults(isc::data::ElementPtr global) {
     size_t cnt = 0;
-
     // Set global defaults first.
     cnt = setDefaults(global, D2_GLOBAL_DEFAULTS);
+
+    // If the key list is present, set its members' defaults
+    if (global->find("tsig-keys")) {
+        ConstElementPtr keys = global->get("tsig-keys");
+        cnt += setListDefaults(keys, TSIG_KEY_DEFAULTS);
+    } else {
+        // Not present, so add an empty list.
+        ConstElementPtr list(new ListElement());
+        global->set("tsig-keys", list);
+        cnt++;
+    }
+
+    // Set the forward domain manager defaults.
+    cnt += setManagerDefaults(global, "forward-ddns", DDNS_DOMAIN_MGR_DEFAULTS);
+
+    // Set the reverse domain manager defaults.
+    cnt += setManagerDefaults(global, "reverse-ddns", DDNS_DOMAIN_MGR_DEFAULTS);
+    return (cnt);
+}
+
+size_t
+D2SimpleParser::setDdnsDomainDefaults(ElementPtr domain,
+                                      const SimpleDefaults& domain_defaults) {
+    size_t cnt = 0;
+
+    // Set the domain's scalar defaults
+    cnt += setDefaults(domain, domain_defaults);
+    if (domain->find("dns-servers")) {
+        // Now add the defaults to its server list.
+        ConstElementPtr servers = domain->get("dns-servers");
+        cnt += setListDefaults(servers, DNS_SERVER_DEFAULTS);
+    }
+
+    return (cnt);
+}
+
+
+size_t
+D2SimpleParser::setManagerDefaults(ElementPtr global,
+                                   const std::string& mgr_name,
+                                   const SimpleDefaults& mgr_defaults) {
+    size_t cnt = 0;
+
+    if (!global->find(mgr_name)) {
+        // If it's not present, then default is an empty map
+        ConstElementPtr map(new MapElement());
+        global->set(mgr_name, map);
+        ++cnt;
+    } else {
+        // Get a writable copy of the manager element map
+        ElementPtr mgr =
+            boost::const_pointer_cast<Element>(global->get(mgr_name));
+
+        // Set the manager's scalar defaults first
+        cnt += setDefaults(mgr, mgr_defaults);
+
+        // Get the domain list and set defaults for them.
+        // The domain list may not be present ddns for this
+        // manager is disabled.
+        if (mgr->find("ddns-domains")) {
+            ConstElementPtr domains = mgr->get("ddns-domains");
+            BOOST_FOREACH(ElementPtr domain, domains->listValue()) {
+                // Set the domain's defaults.  We can't use setListDefaults()
+                // as this does not handle sub-lists or maps, like server list.
+                cnt += setDdnsDomainDefaults(domain, DDNS_DOMAIN_DEFAULTS);
+            }
+        }
+
+    }
+
     return (cnt);
 }
 
index 2f58d8eebe1244615ce285cccd6f63897f446fcb..e927c4c80c4beb38a78aa6773c80a86089cdb7d9 100644 (file)
@@ -18,7 +18,7 @@ namespace d2 {
 /// In particular, it contains all the default values and names of the
 /// parameters that are to be derived (inherited) between scopes.
 /// For the actual values, see @file d2_simple_parser.cc
-class D2SimpleParser : public isc::data::SimpleParser {
+class D2SimpleParser : public data::SimpleParser {
 public:
 
     /// @brief Sets all defaults for D2 configuration
@@ -27,10 +27,52 @@ public:
     ///
     /// @param global scope to be filled in with defaults.
     /// @return number of default values added
-    static size_t setAllDefaults(isc::data::ElementPtr global);
+    static size_t setAllDefaults(data::ElementPtr global);
 
     // see d2_simple_parser.cc for comments for those parameters
-    static const isc::data::SimpleDefaults D2_GLOBAL_DEFAULTS;
+    static const data::SimpleDefaults D2_GLOBAL_DEFAULTS;
+
+    // Defaults for tsig-keys list elements, TSIGKeyInfos
+    static const data::SimpleDefaults TSIG_KEY_DEFAULTS;
+
+    // Defaults for <forward|reverse>-ddns elements, DdnsDomainListMgrs
+    static const data::SimpleDefaults DDNS_DOMAIN_MGR_DEFAULTS;
+
+    // Defaults for ddns-domains list elements, DdnsDomains
+    static const data::SimpleDefaults DDNS_DOMAIN_DEFAULTS;
+
+    // Defaults for dns-servers list elements, DnsServerInfos
+    static const data::SimpleDefaults DNS_SERVER_DEFAULTS;
+
+    /// @brief Adds default values to a DDNS Domain element
+    ///
+    /// Adds the scalar default values to the given DDNS domain
+    /// element, and then adds the DNS Server defaults to the domain's
+    /// server list, "dns-servers".
+    ///
+    /// @param domain DDNS domain element to which defaults should be added
+    /// @param domain_defaults list of default values from which to add
+    /// @return returns the number of default values added
+    static size_t setDdnsDomainDefaults(data::ElementPtr domain,
+                                        const data::SimpleDefaults&
+                                        domain_defaults);
+
+    /// @brief Adds default values to a DDNS Domain List Manager
+    ///
+    /// This function looks for the named DDNS domain manager element within
+    /// the given element tree.  If it is found, it adds the scalar default
+    /// values to the manager element and then adds the DDNS Domain defaults
+    /// to its domain list, "ddns-domains".  If the manager element is not
+    /// found, then an empty map entry is added for it, thus defaulting the
+    /// manager to "disabled".
+    ///
+    /// @param domain DDNS domain manager element to which defaults should be
+    /// added
+    /// @param mgr_defaults list of default values from which to add
+    /// @return returns the number of default values added
+    static size_t setManagerDefaults(data::ElementPtr global,
+                                     const std::string& mgr_name,
+                                     const data::SimpleDefaults& mgr_defaults);
 };
 
 };
index 200617d2c6e9e7a9cf24b51db530b46457b921e3..394fb2464f40275fc8669d2286c817293045dbd4 100644 (file)
@@ -104,11 +104,10 @@ D2ParserContext::enter(const ParserContext& ctx)
 void
 D2ParserContext::leave()
 {
-#if 1
     if (cstack_.empty()) {
         fatal("unbalanced syntactic context");
     }
-#endif
+
     ctx_ = cstack_.back();
     cstack_.pop_back();
 }
@@ -123,6 +122,8 @@ D2ParserContext::contextName()
         return ("toplevel");
     case DHCPDDNS:
         return ("DhcpDdns");
+    case TSIG_KEY:
+        return ("tsig-key");
     case TSIG_KEYS:
         return ("tsig-keys");
     case ALGORITHM:
@@ -135,8 +136,12 @@ D2ParserContext::contextName()
         return("forward-ddns");
     case REVERSE_DDNS:
         return("reverse-ddns");
+    case DDNS_DOMAIN:
+        return("ddns-domain");
     case DDNS_DOMAINS:
         return("ddns-domains");
+    case DNS_SERVER:
+        return("dns-server");
     case DNS_SERVERS:
         return("dns-servers");
     case LOGGING:
index ede65d84c33ddc35f2324c36e592f82369da1bda..3dacb5749ef033c1f68de255f23f572a1d4f6b2d 100644 (file)
@@ -48,11 +48,29 @@ public:
         /// This parser will parse the content as generic JSON.
         PARSER_JSON,
 
-        ///< Used while parsing top level (contains DhcpDdns, Logging, others)
+        ///< Used for parsing top level (contains DhcpDdns, Logging, others)
         PARSER_DHCPDDNS,
 
-        ///< Used while parsing content of DhcpDdns.
-        PARSER_SUB_DHCPDDNS
+        ///< Used for parsing content of DhcpDdns.
+        PARSER_SUB_DHCPDDNS,
+
+        ///< Used for parsing content of a TSIG key.
+        PARSER_TSIG_KEY,
+
+        ///< Used for pasing a list of TSIG Keys.
+        PARSER_TSIG_KEYS,
+
+        ///< Used for parsing content of a DDNS Domain.
+        PARSER_DDNS_DOMAIN,
+
+        ///< Used for parsing a list a DDNS Domains.
+        PARSER_DDNS_DOMAINS,
+
+        ///< Used for parsing content of a DNS Server.
+        PARSER_DNS_SERVER,
+
+        ///< Used for pasing a list of DNS servess.
+        PARSER_DNS_SERVERS
     } ParserType;
 
     /// @brief Default constructor.
@@ -156,7 +174,10 @@ public:
         ///< Used while parsing content of DhcpDdns.
         DHCPDDNS,
 
-        ///< Used while parsing content of tsig-keys
+        ///< Used while parsing content of a tsig-key
+        TSIG_KEY,
+
+        ///< Used while parsing a list of tsig-keys
         TSIG_KEYS,
 
         ///< Used while parsing content of DhcpDdns/tsig-keys/algorithm
@@ -174,10 +195,16 @@ public:
         ///< Used while parsing content of DhcpDdns/reverse-ddns
         REVERSE_DDNS,
 
-        ///< Used while parsing content of DhcpDdns/<forward|reverse>-ddns/ddns-domains
+        ///< Used while parsing content of a ddns-domain
+        DDNS_DOMAIN,
+
+        ///< Used while parsing a list of ddns-domains
         DDNS_DOMAINS,
 
-        ///< Used while parsing content of DhcpDdns/<forward|reverse>-ddns/ddns-domains/dns-servers
+        ///< Used while parsing content of a dns-server
+        DNS_SERVER,
+
+        ///< Used while parsing content of list of dns-servers
         DNS_SERVERS,
 
         ///< Used while parsing content of Logging
index b9fe89d4872d86becf0706c27d732c7bc02ffea3..921c56bcdfb624cadf7aa05e005489d9fdf8aaa9 100644 (file)
@@ -98,277 +98,150 @@ public:
 
         return (config.str());
     }
+
     /// @brief Enumeration to select between expected configuration outcomes
     enum RunConfigMode {
-       SHOULD_PASS,
-       SHOULD_FAIL
+        NO_ERROR,
+        SYNTAX_ERROR,
+        LOGIC_ERROR
     };
 
     /// @brief Parses a configuration string and tests against a given outcome
     ///
     /// Convenience method which accepts JSON text and an expected pass or fail
-    /// outcome. It converts the text into an ElementPtr and passes that to
-    /// configuration manager's parseConfig method.  It then tests the
-    /// parse result against the expected outcome  If they do not match it
-    /// the method asserts a failure.   If they do match, it refreshes the
-    /// the D2Params pointer with the newly parsed instance.
+    /// outcome.  It uses the D2ParserContext to parse the text under the
+    /// PARSE_SUB_DHCPDDNS context, then adds the D2 defaults to the resultant
+    /// element tree. Assuming that's successful the element tree is passed
+    /// to D2CfgMgr::parseConfig() method.
     ///
     /// @param config_str the JSON configuration text to parse
-    /// @param mode indicator if the parsing should fail or not.  It defaults
+    /// @param error_type  indicates the type error expected, NONE, SYNTAX,
+    /// or LOGIC. SYNTAX errors are emitted by JSON parser, logic errors
+    /// are emitted by element parser(s).
+    /// @param exp_error exact text of the error message expected
     /// defaults to SHOULD_PASS.
     ///
-    void runConfig(std::string config_str, RunConfigMode mode=SHOULD_PASS) {
-        // We assume the config string is valid JSON.
-        ASSERT_TRUE(fromJSON(config_str));
+    /// @return AssertionSuccess if test passes, AssertionFailure otherwise
+    ::testing::AssertionResult runConfigOrFail(const std::string& json,
+                                               const RunConfigMode mode,
+                                               const std::string& exp_error) {
 
-        // Parse the configuration and verify we got the expected outcome.
-        answer_ = cfg_mgr_->parseConfig(config_set_);
-        ASSERT_TRUE(checkAnswer(mode == SHOULD_FAIL));
+        try {
+            // Invoke the JSON parser, casting the returned element tree
+            // into mutable form.
+            D2ParserContext parser_context;
+            data::ElementPtr elem =
+                boost::const_pointer_cast<Element>
+                (parser_context.parseString(json, D2ParserContext::
+                                                  PARSER_SUB_DHCPDDNS));
+
+            // If parsing succeeded when we expected a syntax error, then fail.
+            if (mode == SYNTAX_ERROR) {
+               return ::testing::AssertionFailure()
+                             << "Unexpected  JSON parsing success"
+                             << "\njson: [" << json << " ]";
+            }
 
-        // Verify that the D2 context can be retrieved and is not null.
-        D2CfgContextPtr context;
-        ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+            // JSON parsed ok, so the defaults to the element tree it produced.
+            D2SimpleParser::setAllDefaults(elem);
+            config_set_ = elem;
+        } catch (const std::exception& ex) {
+            // JSON Parsing failed
+            if (exp_error.empty()) {
+                // We did not expect an error, so fail.
+                return ::testing::AssertionFailure()
+                          << "Unexpected sytnax error:" << ex.what()
+                          << "\njson: [" << json << " ]";
+            }
 
-        // Verify that the global scalars have the proper values.
-        d2_params_ = context->getD2Params();
-        ASSERT_TRUE(d2_params_);
-    }
+            if (ex.what() != exp_error) {
+                // Expected an error not the one we got, so fail
+                return ::testing::AssertionFailure()
+                          << "Wrong syntax error detected, expected: "
+                          << exp_error << ", got: " << ex.what()
+                          << "\njson: [" << json << " ]";
+            }
 
-    /// @brief Check parse result against expected outcome and position info
-    ///
-    /// This method analyzes the given parsing result against an expected outcome
-    /// of SHOULD_PASS or SHOULD_FAIL.  If it is expected to fail, the comment
-    /// contained within the result is searched for Element::Position information
-    /// which should contain the given file name.  It does not attempt to verify
-    /// the numerical values for line number and col.
-    ///
-    /// @param answer Element set containing an integer result code and string
-    /// comment.
-    /// @param mode indicator if the parsing should fail or not.
-    /// @param file_name name of the file containing the configuration text
-    /// parsed. It defaults to "<string>" which is the value present if the
-    /// configuration text did not originate from a file. (i.e. one did not use
-    /// isc::data::Element::fromJSONFile() to read the JSON text).
-    void
-    checkAnswerWithError(isc::data::ConstElementPtr answer,
-                         RunConfigMode mode, std::string file_name="<string>") {
+            // We go the syntax error we expected, so return success
+            return ::testing::AssertionSuccess();
+        }
+
+        // The JSON parsed ok and we've added the defaults, pass the config
+        // into the Element parser and check for the expected outcome.
+        data::ConstElementPtr answer = cfg_mgr_->parseConfig(config_set_);
+
+        // Extract the result and error text from the anwser.
         int rcode = 0;
         isc::data::ConstElementPtr comment;
         comment = isc::config::parseAnswer(rcode, answer);
 
-        if (mode == SHOULD_PASS) {
-            if (rcode == 0) {
-                return;
+        if (rcode != 0) {
+            // Element Parsing failed.
+            if (exp_error.empty()) {
+                // We didn't expect it to, fail the test.
+                return ::testing::AssertionFailure()
+                              << "Unexpected logic error: " << *comment
+                              << "\njson: [" << json << " ]";
             }
 
-            FAIL() << "Parsing was expected to pass but failed : " << rcode
-                   << " comment: " << *comment;
+            if (comment->stringValue() != exp_error) {
+                // We 't expect a different error, fail the test.
+                return ::testing::AssertionFailure()
+                              << "Wrong logic error detected, expected: "
+                              << exp_error << ", got: " << *comment
+                              << "\njson: [" << json << " ]";
+            }
+        } else {
+            // Element parsing succeeded.
+            if (!exp_error.empty()) {
+                // It was supposed to fail, so fail the test.
+                return ::testing::AssertionFailure()
+                              << "Unexpected logic success, expected error:"
+                              << exp_error
+                              << "\njson: [" << json << " ]";
+            }
         }
 
-        if (rcode == 0) {
-            FAIL() << "Parsing was expected to fail but passed : "
-                   << " comment: " << *comment;
+        // Verify that the D2 context can be retrieved and is not null.
+        D2CfgContextPtr context;
+        context = cfg_mgr_->getD2CfgContext();
+        if (!context) {
+            return ::testing::AssertionFailure() << "D2CfgContext is null";
         }
 
-        // Parsing was expected to fail, test for position info.
-        if (isc::dhcp::test::errorContainsPosition(answer, file_name)) {
-            return;
+        // Verify that the global scalar container has been created.
+        d2_params_ = context->getD2Params();
+        if (!d2_params_) {
+            return ::testing::AssertionFailure() << "D2Params is null";
         }
 
-        FAIL() << "Parsing failed as expected but lacks position : " << *comment
-            << ", filename:" << file_name;
+        return ::testing::AssertionSuccess();
     }
 
+
     /// @brief Pointer the D2Params most recently parsed.
     D2ParamsPtr d2_params_;
 };
 
+/// @brief Convenience macros for invoking runOrConfig()
+#define RUN_CONFIG_OK(a) (runConfigOrFail(a, NO_ERROR, ""))
+#define SYNTAX_ERROR(a,b) ASSERT_TRUE(runConfigOrFail(a,SYNTAX_ERROR,b))
+#define LOGIC_ERROR(a,b) ASSERT_TRUE(runConfigOrFail(a,LOGIC_ERROR,b))
+
 /// @brief Tests that the spec file is valid.
 /// Verifies that the DHCP-DDNS configuration specification file
-//  is valid.
+///  is valid.
 TEST(D2SpecTest, basicSpec) {
     ASSERT_NO_THROW(isc::config::
                     moduleSpecFromFile(specfile("dhcp-ddns.spec")));
 }
 
-/// @brief Convenience function which compares the contents of the given
-/// DnsServerInfo against the given set of values.
-///
-/// It is structured in such a way that each value is checked, and output
-/// is generate for all that do not match.
-///
-/// @param server is a pointer to the server to check against.
-/// @param hostname is the value to compare against server's hostname_.
-/// @param ip_address is the string value to compare against server's
-/// ip_address_.
-/// @param port is the value to compare against server's port.
-///
-/// @return returns true if there is a match across the board, otherwise it
-/// returns false.
-bool checkServer(DnsServerInfoPtr server, const char* hostname,
-                 const char *ip_address, uint32_t port)
-{
-    // Return value, assume its a match.
-    bool result = true;
-
-    if (!server) {
-        EXPECT_TRUE(server);
-        return false;
-    }
-
-    // Check hostname.
-    if (server->getHostname() != hostname) {
-        EXPECT_EQ(hostname, server->getHostname());
-        result = false;
-    }
-
-    // Check IP address.
-    if (server->getIpAddress().toText() != ip_address) {
-        EXPECT_EQ(ip_address, server->getIpAddress().toText());
-        result = false;
-    }
-
-    // Check port.
-    if (server->getPort() !=  port) {
-        EXPECT_EQ (port, server->getPort());
-        result = false;
-    }
-
-    return (result);
-}
-
-/// @brief Convenience function which compares the contents of the given
-/// TSIGKeyInfo against the given set of values, and that the TSIGKey
-/// member points to a key.
-///
-/// @param key is a pointer to the TSIGKeyInfo instance to verify
-/// @param name is the value to compare against key's name_.
-/// @param algorithm is the string value to compare against key's algorithm.
-/// @param secret is the value to compare against key's secret.
-///
-/// @return returns true if there is a match across the board, otherwise it
-/// returns false.
-bool checkKey(TSIGKeyInfoPtr key, const std::string& name,
-              const std::string& algorithm, const std::string& secret,
-              uint32_t digestbits = 0) {
-    // Return value, assume its a match.
-    return (((key) &&
-        (key->getName() == name) &&
-        (key->getAlgorithm() == algorithm)  &&
-        (key->getDigestbits() == digestbits) &&
-        (key->getSecret() ==  secret)  &&
-        (key->getTSIGKey())));
-}
-
-/// @brief Test fixture class for testing DnsServerInfo parsing.
-class TSIGKeyInfoTest : public ConfigParseTest {
-public:
-
-    /// @brief Constructor
-    TSIGKeyInfoTest() {
-    }
-
-    /// @brief Destructor
-    ~TSIGKeyInfoTest() {
-    }
-
-    /// @brief Pointer to the current parser instance.
-    TSIGKeyInfoParser parser_;
-};
-
-/// @brief Test fixture class for testing DnsServerInfo parsing.
-class TSIGKeyInfoListTest : public ConfigParseTest {
-public:
-
-    /// @brief Constructor
-    TSIGKeyInfoListTest() {
-    }
-
-    /// @brief Destructor
-    ~TSIGKeyInfoListTest() {
-    }
-
-    /// @brief Pointer to the current parser instance.
-    TSIGKeyInfoListParser parser_;
-};
-
-
-/// @brief Test fixture class for testing DnsServerInfo parsing.
-class DnsServerInfoTest : public ConfigParseTest {
-public:
-
-    /// @brief Constructor
-    DnsServerInfoTest() {
-        reset();
-    }
-
-    /// @brief Destructor
-    ~DnsServerInfoTest() {
-    }
-
-    /// @brief Wipe out the current storage and parser and replace
-    /// them with new ones.
-    void reset() {
-        servers_.reset(new DnsServerInfoStorage());
-        parser_.reset(new DnsServerInfoParser("test", servers_));
-    }
-
-    /// @brief Storage for "committing" servers.
-    DnsServerInfoStoragePtr servers_;
-
-    /// @brief Pointer to the current parser instance.
-    isc::dhcp::ParserPtr parser_;
-};
-
-
-/// @brief Test fixture class for testing DDnsDomain parsing.
-class DdnsDomainTest : public ConfigParseTest {
-public:
-
-    /// @brief Constructor
-    DdnsDomainTest() {
-        reset();
-    }
-
-    /// @brief Destructor
-    ~DdnsDomainTest() {
-    }
-
-    /// @brief Wipe out the current storage and parser and replace
-    /// them with new ones.
-    void reset() {
-        keys_.reset(new TSIGKeyInfoMap());
-        domains_.reset(new DdnsDomainMap());
-        parser_.reset(new DdnsDomainParser("test", domains_, keys_));
-    }
-
-    /// @brief Add TSIGKeyInfos to the key map
-    ///
-    /// @param name the name of the key
-    /// @param algorithm the algorithm of the key
-    /// @param secret the secret value of the key
-    void addKey(const std::string& name, const std::string& algorithm,
-                const std::string& secret) {
-        TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));
-        (*keys_)[name]=key_info;
-    }
-
-    /// @brief Storage for "committing" domains.
-    DdnsDomainMapPtr domains_;
-
-    /// @brief Storage for TSIGKeys
-    TSIGKeyInfoMapPtr keys_;
-
-    /// @brief Pointer to the current parser instance.
-    isc::dhcp::ParserPtr parser_;
-};
-
 /// @brief Tests a basic valid configuration for D2Param.
 TEST_F(D2CfgMgrTest, validParamsEntry) {
     // Verify that ip_address can be valid v4 address.
     std::string config = makeParamsConfigString ("192.0.0.1", 777, 333,
                                            "UDP", "JSON");
-    runConfig(config);
+    RUN_CONFIG_OK(config);
 
     EXPECT_EQ(isc::asiolink::IOAddress("192.0.0.1"),
               d2_params_->getIpAddress());
@@ -379,7 +252,7 @@ TEST_F(D2CfgMgrTest, validParamsEntry) {
 
     // Verify that ip_address can be valid v6 address.
     config = makeParamsConfigString ("3001::5", 777, 333, "UDP", "JSON");
-    runConfig(config);
+    RUN_CONFIG_OK(config);
 
     // Verify that the global scalars have the proper values.
     EXPECT_EQ(isc::asiolink::IOAddress("3001::5"),
@@ -411,7 +284,7 @@ TEST_F(D2CfgMgrTest, defaultValues) {
             "\"reverse-ddns\" : {} "
             "}";
 
-    runConfig(config);
+    RUN_CONFIG_OK(config);
     ConstElementPtr deflt;
     ASSERT_NO_THROW(deflt = defaults->get("ip-address"));
     ASSERT_TRUE(deflt);
@@ -429,7 +302,7 @@ TEST_F(D2CfgMgrTest, defaultValues) {
             "\"reverse-ddns\" : {} "
             "}";
 
-    runConfig(config);
+    RUN_CONFIG_OK(config);
     ASSERT_NO_THROW(deflt = defaults->get("port"));
     ASSERT_TRUE(deflt);
     EXPECT_EQ(deflt->intValue(), d2_params_->getPort());
@@ -446,7 +319,7 @@ TEST_F(D2CfgMgrTest, defaultValues) {
             "\"reverse-ddns\" : {} "
             "}";
 
-    runConfig(config);
+    RUN_CONFIG_OK(config);
     ASSERT_NO_THROW(deflt = defaults->get("dns-server-timeout"));
     ASSERT_TRUE(deflt);
     EXPECT_EQ(deflt->intValue(), d2_params_->getDnsServerTimeout());
@@ -463,7 +336,7 @@ TEST_F(D2CfgMgrTest, defaultValues) {
             "\"reverse-ddns\" : {} "
             "}";
 
-    runConfig(config);
+    RUN_CONFIG_OK(config);
     ASSERT_NO_THROW(deflt = defaults->get("ncr-protocol"));
     ASSERT_TRUE(deflt);
     EXPECT_EQ(dhcp_ddns::stringToNcrProtocol(deflt->stringValue()),
@@ -481,7 +354,7 @@ TEST_F(D2CfgMgrTest, defaultValues) {
             "\"reverse-ddns\" : {} "
             "}";
 
-    runConfig(config);
+    RUN_CONFIG_OK(config);
     ASSERT_NO_THROW(deflt = defaults->get("ncr-format"));
     ASSERT_TRUE(deflt);
     EXPECT_EQ(dhcp_ddns::stringToNcrFormat(deflt->stringValue()),
@@ -504,7 +377,8 @@ TEST_F(D2CfgMgrTest, unsupportedTopLevelItems) {
             "\"bogus-param\" : true "
             "}";
 
-    runConfig(config, SHOULD_FAIL);
+    SYNTAX_ERROR(config, "<string>:1.181-193: got unexpected "
+                         "keyword \"bogus-param\" in DhcpDdns map.");
 
     // Check that unsupported top level objects fails.  For
     // D2 these fail as they are not in the parse order.
@@ -522,7 +396,8 @@ TEST_F(D2CfgMgrTest, unsupportedTopLevelItems) {
             "\"bogus-object-two\" : {} "
             "}";
 
-    runConfig(config, SHOULD_FAIL);
+    SYNTAX_ERROR(config, "<string>:1.139-156: got unexpected"
+                         " keyword \"bogus-object-one\" in DhcpDdns map.");
 }
 
 
@@ -538,715 +413,35 @@ TEST_F(D2CfgMgrTest, invalidEntry) {
     // Cannot use IPv4 ANY address
     std::string config = makeParamsConfigString ("0.0.0.0", 777, 333,
                                            "UDP", "JSON");
-    runConfig(config, SHOULD_FAIL);
+    LOGIC_ERROR(config, "IP address cannot be \"0.0.0.0\" (<string>:1:17)");
 
     // Cannot use IPv6 ANY address
     config = makeParamsConfigString ("::", 777, 333, "UDP", "JSON");
-    runConfig(config, SHOULD_FAIL);
+    LOGIC_ERROR(config, "IP address cannot be \"::\" (<string>:1:17)");
 
     // Cannot use port  0
     config = makeParamsConfigString ("127.0.0.1", 0, 333, "UDP", "JSON");
-    runConfig(config, SHOULD_FAIL);
+    SYNTAX_ERROR(config, "<string>:1.40: port must be greater than zero");
 
     // Cannot use dns server timeout of 0
     config = makeParamsConfigString ("127.0.0.1", 777, 0, "UDP", "JSON");
-    runConfig(config, SHOULD_FAIL);
+    SYNTAX_ERROR(config, "<string>:1.69: dns-server-timeout"
+                         " must be greater than zero");
 
     // Invalid protocol
     config = makeParamsConfigString ("127.0.0.1", 777, 333, "BOGUS", "JSON");
-    runConfig(config, SHOULD_FAIL);
+    SYNTAX_ERROR(config, "<string>:1.92-98: syntax error,"
+                         " unexpected constant string, expecting UDP or TCP");
 
     // Unsupported protocol
     config = makeParamsConfigString ("127.0.0.1", 777, 333, "TCP", "JSON");
-    runConfig(config, SHOULD_FAIL);
+    LOGIC_ERROR(config, "ncr-protocol : TCP is not yet supported"
+                        "  (<string>:1:92)");
 
     // Invalid format
     config = makeParamsConfigString ("127.0.0.1", 777, 333, "UDP", "BOGUS");
-    runConfig(config, SHOULD_FAIL);
-}
-
-/// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos.
-/// It verifies that:
-/// 1. Name cannot be blank.
-/// 2. Algorithm cannot be blank.
-/// 3. Secret cannot be blank.
-TEST_F(TSIGKeyInfoTest, invalidEntry) {
-    // Config with a blank name entry.
-    std::string config = "{"
-                         " \"name\": \"\" , "
-                         " \"algorithm\": \"HMAC-MD5\" , "
-                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
-                         "}";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that build fails on blank name.
-    EXPECT_THROW(parser_.parse(config_set_), D2CfgError);
-
-    // Config with a blank algorithm entry.
-    config = "{"
-                         " \"name\": \"d2_key_one\" , "
-                         " \"algorithm\": \"\" , "
-                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
-                         "}";
-
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that build fails on blank algorithm.
-    EXPECT_THROW(parser_.parse(config_set_), D2CfgError);
-
-    // Config with an invalid algorithm entry.
-    config = "{"
-                         " \"name\": \"d2_key_one\" , "
-                         " \"algorithm\": \"bogus\" , "
-                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
-                         "}";
-
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that build fails on blank algorithm.
-    EXPECT_THROW(parser_.parse(config_set_), D2CfgError);
-
-    // Config with a blank secret entry.
-    config = "{"
-                         " \"name\": \"d2_key_one\" , "
-                         " \"algorithm\": \"HMAC-MD5\" , "
-                         " \"secret\": \"\" "
-                         "}";
-
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that build fails blank secret
-    EXPECT_THROW(parser_.parse(config_set_), D2CfgError);
-
-    // Config with an invalid secret entry.
-    config = "{"
-                         " \"name\": \"d2_key_one\" , "
-                         " \"algorithm\": \"HMAC-MD5\" , "
-                         " \"secret\": \"bogus\" "
-                         "}";
-
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that build fails an invalid secret
-    EXPECT_THROW(parser_.parse(config_set_), D2CfgError);
-}
-
-/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
-/// when given a valid combination of entries.
-TEST_F(TSIGKeyInfoTest, validEntry) {
-    // Valid entries for TSIG key, all items are required.
-    std::string config = "{"
-                         " \"name\": \"d2_key_one\" , "
-                         " \"algorithm\": \"HMAC-MD5\" , "
-                         " \"digest-bits\": 120 , "
-                         " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
-                         "}";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that it builds and commits without throwing.
-    //ASSERT_NO_THROW(parser_->build(config_set_));
-    TSIGKeyInfoPtr key;
-    ASSERT_NO_THROW(key = parser_.parse(config_set_));
-    ASSERT_TRUE(key);
-
-    // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "d2_key_one", "HMAC-MD5",
-                         "dGhpcyBrZXkgd2lsbCBtYXRjaA==", 120));
-}
-
-/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
-/// entries is detected.
-TEST_F(TSIGKeyInfoListTest, invalidTSIGKeyList) {
-    // Construct a list of keys with an invalid key entry.
-    std::string config = "["
-
-                         " { \"name\": \"key1\" , "
-                         "   \"algorithm\": \"HMAC-MD5\" ,"
-                         " \"digest-bits\": 120 , "
-                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
-                         " },"
-                         // this entry has an invalid algorithm
-                         " { \"name\": \"key2\" , "
-                         "   \"algorithm\": \"\" ,"
-                         " \"digest-bits\": 120 , "
-                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
-                         " },"
-                         " { \"name\": \"key3\" , "
-                         "   \"algorithm\": \"HMAC-MD5\" ,"
-                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
-                         " }"
-                         " ]";
-
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that the list builds with errors.
-    EXPECT_THROW(parser_.parse(config_set_), D2CfgError);
-}
-
-/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
-/// entries is detected.
-TEST_F(TSIGKeyInfoListTest, duplicateTSIGKey) {
-    // Construct a list of keys with an invalid key entry.
-    std::string config = "["
-                         " { \"name\": \"key1\" , "
-                         "   \"algorithm\": \"HMAC-MD5\" ,"
-                         " \"digest-bits\": 120 , "
-                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
-                         " },"
-                         " { \"name\": \"key2\" , "
-                         "   \"algorithm\": \"HMAC-MD5\" ,"
-                         " \"digest-bits\": 120 , "
-                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
-                         " },"
-                         " { \"name\": \"key1\" , "
-                         "   \"algorithm\": \"HMAC-MD5\" ,"
-                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
-                         " }"
-                         " ]";
-
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that the list builds without errors.
-    EXPECT_THROW(parser_.parse(config_set_), D2CfgError);
-}
-
-/// @brief Verifies a valid list of TSIG Keys parses correctly.
-/// Also verifies that all of the supported algorithm names work.
-TEST_F(TSIGKeyInfoListTest, validTSIGKeyList) {
-    // Construct a valid list of keys.
-    std::string config = "["
-
-                         " { \"name\": \"key1\" , "
-                         "   \"algorithm\": \"HMAC-MD5\" ,"
-                         " \"digest-bits\": 80 , "
-                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
-                         " },"
-                         " { \"name\": \"key2\" , "
-                         "   \"algorithm\": \"HMAC-SHA1\" ,"
-                         " \"digest-bits\": 80 , "
-                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
-                         " },"
-                         " { \"name\": \"key3\" , "
-                         "   \"algorithm\": \"HMAC-SHA256\" ,"
-                         " \"digest-bits\": 128 , "
-                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
-                         " },"
-                         " { \"name\": \"key4\" , "
-                         "   \"algorithm\": \"HMAC-SHA224\" ,"
-                         " \"digest-bits\": 112 , "
-                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
-                         " },"
-                         " { \"name\": \"key5\" , "
-                         "   \"algorithm\": \"HMAC-SHA384\" ,"
-                         " \"digest-bits\": 192 , "
-                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
-                         " },"
-                         " { \"name\": \"key6\" , "
-                         "   \"algorithm\": \"HMAC-SHA512\" ,"
-                         " \"digest-bits\": 256 , "
-                         "   \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
-                         " }"
-                         " ]";
-
-    ASSERT_TRUE(fromJSON(config));
-    TSIGKeyInfoMapPtr keys;
-    ASSERT_NO_THROW(keys = parser_.parse(config_set_));
-
-    std::string ref_secret = "dGhpcyBrZXkgd2lsbCBtYXRjaA==";
-    // Verify the correct number of keys are present
-    int count =  keys->size();
-    ASSERT_EQ(6, count);
-
-    // Find the 1st key and retrieve it.
-    TSIGKeyInfoMap::iterator gotit = keys->find("key1");
-    ASSERT_TRUE(gotit != keys->end());
-    TSIGKeyInfoPtr& key = gotit->second;
-
-    // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::HMAC_MD5_STR,
-                         ref_secret, 80));
-
-    // Find the 2nd key and retrieve it.
-    gotit = keys->find("key2");
-    ASSERT_TRUE(gotit != keys->end());
-    key = gotit->second;
-
-    // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::HMAC_SHA1_STR,
-                         ref_secret, 80));
-
-    // Find the 3rd key and retrieve it.
-    gotit = keys->find("key3");
-    ASSERT_TRUE(gotit != keys->end());
-    key = gotit->second;
-
-    // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::HMAC_SHA256_STR,
-                         ref_secret, 128));
-
-    // Find the 4th key and retrieve it.
-    gotit = keys->find("key4");
-    ASSERT_TRUE(gotit != keys->end());
-    key = gotit->second;
-
-    // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::HMAC_SHA224_STR,
-                         ref_secret, 112));
-
-    // Find the 5th key and retrieve it.
-    gotit = keys->find("key5");
-    ASSERT_TRUE(gotit != keys->end());
-    key = gotit->second;
-
-    // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::HMAC_SHA384_STR,
-                         ref_secret, 192));
-
-    // Find the 6th key and retrieve it.
-    gotit = keys->find("key6");
-    ASSERT_TRUE(gotit != keys->end());
-    key = gotit->second;
-
-    // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::HMAC_SHA512_STR,
-                         ref_secret, 256));
-}
-
-/// @brief Tests the enforcement of data validation when parsing DnsServerInfos.
-/// It verifies that:
-/// 1. Specifying both a hostname and an ip address is not allowed.
-/// 2. Specifying both blank a hostname and blank ip address is not allowed.
-/// 3. Specifying a negative port number is not allowed.
-TEST_F(DnsServerInfoTest, invalidEntry) {
-    // Create a config in which both host and ip address are supplied.
-    // Verify that build fails.
-    std::string config = "{ \"hostname\": \"pegasus.tmark\", "
-                         "  \"ip-address\": \"127.0.0.1\" } ";
-    ASSERT_TRUE(fromJSON(config));
-    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
-
-    // Neither host nor ip address supplied
-    // Verify that builds fails.
-    config = "{ \"hostname\": \"\", "
-             "  \"ip-address\": \"\" } ";
-    ASSERT_TRUE(fromJSON(config));
-    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
-
-    // Create a config with a negative port number.
-    // Verify that build fails.
-    config = "{ \"ip-address\": \"192.168.5.6\" ,"
-             "  \"port\": -100 }";
-    ASSERT_TRUE(fromJSON(config));
-    EXPECT_THROW (parser_->build(config_set_), isc::BadValue);
-}
-
-
-/// @brief Verifies that DnsServerInfo parsing creates a proper DnsServerInfo
-/// when given a valid combination of entries.
-/// It verifies that:
-/// 1. A DnsServerInfo entry is correctly made, when given only a hostname.
-/// 2. A DnsServerInfo entry is correctly made, when given ip address and port.
-/// 3. A DnsServerInfo entry is correctly made, when given only an ip address.
-TEST_F(DnsServerInfoTest, validEntry) {
-    /// @todo When resolvable hostname is supported you'll need this test.
-    /// // Valid entries for dynamic host
-    /// std::string config = "{ \"hostname\": \"pegasus.tmark\" }";
-    /// ASSERT_TRUE(fromJSON(config));
-
-    /// // Verify that it builds and commits without throwing.
-    /// ASSERT_NO_THROW(parser_->build(config_set_));
-    /// ASSERT_NO_THROW(parser_->commit());
-
-    /// //Verify the correct number of servers are present
-    /// int count =  servers_->size();
-    /// EXPECT_EQ(1, count);
-
-    /// Verify the server exists and has the correct values.
-    /// DnsServerInfoPtr server = (*servers_)[0];
-    /// EXPECT_TRUE(checkServer(server, "pegasus.tmark",
-    ///                         DnsServerInfo::EMPTY_IP_STR,
-    ///                         DnsServerInfo::STANDARD_DNS_PORT));
-
-    /// // Start over for a new test.
-    /// reset();
-
-    // Valid entries for static ip
-    std::string config = " { \"ip-address\": \"127.0.0.1\" , "
-                         "  \"port\": 100 }";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that it builds and commits without throwing.
-    ASSERT_NO_THROW(parser_->build(config_set_));
-    ASSERT_NO_THROW(parser_->commit());
-
-    // Verify the correct number of servers are present
-    int count =  servers_->size();
-    EXPECT_EQ(1, count);
-
-    // Verify the server exists and has the correct values.
-    DnsServerInfoPtr server = (*servers_)[0];
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
-
-    // Start over for a new test.
-    reset();
-
-    // Valid entries for static ip, no port
-    config = " { \"ip-address\": \"192.168.2.5\" }";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that it builds and commits without throwing.
-    ASSERT_NO_THROW(parser_->build(config_set_));
-    ASSERT_NO_THROW(parser_->commit());
-
-    // Verify the correct number of servers are present
-    count =  servers_->size();
-    EXPECT_EQ(1, count);
-
-    // Verify the server exists and has the correct values.
-    server = (*servers_)[0];
-    EXPECT_TRUE(checkServer(server, "", "192.168.2.5",
-                            DnsServerInfo::STANDARD_DNS_PORT));
-}
-
-/// @brief Verifies that attempting to parse an invalid list of DnsServerInfo
-/// entries is detected.
-TEST_F(ConfigParseTest, invalidServerList) {
-    // Construct a list of servers with an invalid server entry.
-    std::string config = "[ { \"ip-address\": \"127.0.0.1\" }, "
-                        "{ \"ip-address\": \"\" }, "
-                        "{ \"ip-address\": \"127.0.0.2\" } ]";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Create the server storage and list parser.
-    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
-    isc::dhcp::ParserPtr parser;
-    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));
-
-    // Verify that build fails.
-    EXPECT_THROW(parser->build(config_set_), D2CfgError);
-}
-
-/// @brief Verifies that a list of DnsServerInfo entries parses correctly given
-/// a valid configuration.
-TEST_F(ConfigParseTest, validServerList) {
-    // Create a valid list of servers.
-    std::string config = "[ { \"ip-address\": \"127.0.0.1\" }, "
-                        "{ \"ip-address\": \"127.0.0.2\" }, "
-                        "{ \"ip-address\": \"127.0.0.3\" } ]";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Create the server storage and list parser.
-    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
-    isc::dhcp::ParserPtr parser;
-    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));
-
-    // Verfiy that the list builds and commits without error.
-    ASSERT_NO_THROW(parser->build(config_set_));
-    ASSERT_NO_THROW(parser->commit());
-
-    // Verify that the server storage contains the correct number of servers.
-    int count =  servers->size();
-    EXPECT_EQ(3, count);
-
-    // Verify the first server exists and has the correct values.
-    DnsServerInfoPtr server = (*servers)[0];
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.1",
-                            DnsServerInfo::STANDARD_DNS_PORT));
-
-    // Verify the second server exists and has the correct values.
-    server = (*servers)[1];
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.2",
-                            DnsServerInfo::STANDARD_DNS_PORT));
-
-    // Verify the third server exists and has the correct values.
-    server = (*servers)[2];
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.3",
-                            DnsServerInfo::STANDARD_DNS_PORT));
-}
-
-/// @brief Tests the enforcement of data validation when parsing DdnsDomains.
-/// It verifies that:
-/// 1. Domain storage cannot be null when constructing a DdnsDomainParser.
-/// 2. The name entry is not optional.
-/// 3. The server list man not be empty.
-/// 4. That a mal-formed server entry is detected.
-/// 5. That an undefined key name is detected.
-TEST_F(DdnsDomainTest, invalidDdnsDomainEntry) {
-    // Verify that attempting to construct the parser with null storage fails.
-    DdnsDomainMapPtr domains;
-    ASSERT_THROW(isc::dhcp::ParserPtr(
-                 new DdnsDomainParser("test", domains, keys_)), D2CfgError);
-
-    // Create a domain configuration without a name
-    std::string config = "{  \"key-name\": \"d2_key.tmark.org\" , "
-                         "  \"dns-servers\" : [ "
-                         "  {  \"ip-address\": \"127.0.0.1\" , "
-                         "    \"port\": 100 },"
-                         "  { \"ip-address\": \"127.0.0.2\" , "
-                         "    \"port\": 200 },"
-                         "  {  \"ip-address\": \"127.0.0.3\" , "
-                         "    \"port\": 300 } ] } ";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that the domain configuration builds fails.
-    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
-
-    // Create a domain configuration with an empty server list.
-    config = "{ \"name\": \"tmark.org\" , "
-             "  \"key-name\": \"d2_key.tmark.org\" , "
-             "  \"dns-servers\" : [ "
-             "   ] } ";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that the domain configuration build fails.
-    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
-
-    // Create a domain configuration with a mal-formed server entry.
-    config = "{ \"name\": \"tmark.org\" , "
-             "  \"key-name\": \"d2_key.tmark.org\" , "
-             "  \"dns-servers\" : [ "
-             "  {  \"ip-address\": \"127.0.0.3\" , "
-             "    \"port\": -1 } ] } ";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that the domain configuration build fails.
-    EXPECT_THROW(parser_->build(config_set_), isc::BadValue);
-
-    // Create a domain configuration without an defined key name
-    config = "{ \"name\": \"tmark.org\" , "
-             "  \"key-name\": \"d2_key.tmark.org\" , "
-             "  \"dns-servers\" : [ "
-             "  {  \"ip-address\": \"127.0.0.3\" , "
-             "    \"port\": 300 } ] } ";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that the domain configuration build fails.
-    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
-}
-
-/// @brief Verifies the basics of parsing DdnsDomains.
-/// It verifies that:
-/// 1. Valid construction of DdnsDomainParser functions.
-/// 2. Given a valid, configuration entry, DdnsDomainParser parses
-/// correctly.
-/// (It indirectly verifies the operation of DdnsDomainMap).
-TEST_F(DdnsDomainTest, ddnsDomainParsing) {
-    // Create a valid domain configuration entry containing three valid
-    // servers.
-    std::string config =
-                        "{ \"name\": \"tmark.org\" , "
-                        "  \"key-name\": \"d2_key.tmark.org\" , "
-                        "  \"dns-servers\" : [ "
-                        "  {  \"ip-address\": \"127.0.0.1\" , "
-                        "    \"port\": 100 },"
-                        "  { \"ip-address\": \"127.0.0.2\" , "
-                        "    \"port\": 200 },"
-                        "  {  \"ip-address\": \"127.0.0.3\" , "
-                        "    \"port\": 300 } ] } ";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Add a TSIG key to the test key map, so key validation will pass.
-    addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
-
-    // Verify that the domain configuration builds and commits without error.
-    ASSERT_NO_THROW(parser_->build(config_set_));
-    ASSERT_NO_THROW(parser_->commit());
-
-    // Verify that the domain storage contains the correct number of domains.
-    int count =  domains_->size();
-    EXPECT_EQ(1, count);
-
-    // Verify that the expected domain exists and can be retrieved from
-    // the storage.
-    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
-    ASSERT_TRUE(gotit != domains_->end());
-    DdnsDomainPtr& domain = gotit->second;
-
-    // Verify the name and key_name values.
-    EXPECT_EQ("tmark.org", domain->getName());
-    EXPECT_EQ("d2_key.tmark.org", domain->getKeyName());
-    ASSERT_TRUE(domain->getTSIGKeyInfo());
-    ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
-
-    // Verify that the server list exists and contains the correct number of
-    // servers.
-    const DnsServerInfoStoragePtr& servers = domain->getServers();
-    EXPECT_TRUE(servers);
-    count =  servers->size();
-    EXPECT_EQ(3, count);
-
-    // Fetch each server and verify its contents.
-    DnsServerInfoPtr server = (*servers)[0];
-    EXPECT_TRUE(server);
-
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
-
-    server = (*servers)[1];
-    EXPECT_TRUE(server);
-
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
-
-    server = (*servers)[2];
-    EXPECT_TRUE(server);
-
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
-}
-
-/// @brief Tests the fundamentals of parsing DdnsDomain lists.
-/// This test verifies that given a valid domain list configuration
-/// it will accurately parse and populate each domain in the list.
-TEST_F(DdnsDomainTest, DdnsDomainListParsing) {
-    // Create a valid domain list configuration, with two domains
-    // that have three servers each.
-    std::string config =
-                        "[ "
-                        "{ \"name\": \"tmark.org\" , "
-                        "  \"key-name\": \"d2_key.tmark.org\" , "
-                        "  \"dns-servers\" : [ "
-                        "  { \"ip-address\": \"127.0.0.1\" , "
-                        "    \"port\": 100 },"
-                        "  { \"ip-address\": \"127.0.0.2\" , "
-                        "    \"port\": 200 },"
-                        "  { \"ip-address\": \"127.0.0.3\" , "
-                        "    \"port\": 300 } ] } "
-                        ", "
-                        "{ \"name\": \"billcat.net\" , "
-                        "  \"key-name\": \"d2_key.billcat.net\" , "
-                        "  \"dns-servers\" : [ "
-                        "  { \"ip-address\": \"127.0.0.4\" , "
-                        "    \"port\": 400 },"
-                        "  { \"ip-address\": \"127.0.0.5\" , "
-                        "    \"port\": 500 },"
-                        "  { \"ip-address\": \"127.0.0.6\" , "
-                        "    \"port\": 600 } ] } "
-                        "] ";
-
-    ASSERT_TRUE(fromJSON(config));
-
-    // Add keys to key map so key validation passes.
-    addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
-    addKey("d2_key.billcat.net", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
-
-    // Create the list parser
-    isc::dhcp::ParserPtr list_parser;
-    ASSERT_NO_THROW(list_parser.reset(
-                    new DdnsDomainListParser("test", domains_, keys_)));
-
-    // Verify that the domain configuration builds and commits without error.
-    ASSERT_NO_THROW(list_parser->build(config_set_));
-    ASSERT_NO_THROW(list_parser->commit());
-
-    // Verify that the domain storage contains the correct number of domains.
-    int count =  domains_->size();
-    EXPECT_EQ(2, count);
-
-    // Verify that the first domain exists and can be retrieved.
-    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
-    ASSERT_TRUE(gotit != domains_->end());
-    DdnsDomainPtr& domain = gotit->second;
-
-    // Verify the name and key_name values of the first domain.
-    EXPECT_EQ("tmark.org", domain->getName());
-    EXPECT_EQ("d2_key.tmark.org", domain->getKeyName());
-    ASSERT_TRUE(domain->getTSIGKeyInfo());
-    ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
-
-    // Verify the each of the first domain's servers
-    DnsServerInfoStoragePtr servers = domain->getServers();
-    EXPECT_TRUE(servers);
-    count =  servers->size();
-    EXPECT_EQ(3, count);
-
-    DnsServerInfoPtr server = (*servers)[0];
-    EXPECT_TRUE(server);
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
-
-    server = (*servers)[1];
-    EXPECT_TRUE(server);
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
-
-    server = (*servers)[2];
-    EXPECT_TRUE(server);
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
-
-    // Verify second domain
-    gotit = domains_->find("billcat.net");
-    ASSERT_TRUE(gotit != domains_->end());
-    domain = gotit->second;
-
-    // Verify the name and key_name values of the second domain.
-    EXPECT_EQ("billcat.net", domain->getName());
-    EXPECT_EQ("d2_key.billcat.net", domain->getKeyName());
-    ASSERT_TRUE(domain->getTSIGKeyInfo());
-    ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
-
-    // Verify the each of second domain's servers
-    servers = domain->getServers();
-    EXPECT_TRUE(servers);
-    count =  servers->size();
-    EXPECT_EQ(3, count);
-
-    server = (*servers)[0];
-    EXPECT_TRUE(server);
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.4", 400));
-
-    server = (*servers)[1];
-    EXPECT_TRUE(server);
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.5", 500));
-
-    server = (*servers)[2];
-    EXPECT_TRUE(server);
-    EXPECT_TRUE(checkServer(server, "", "127.0.0.6", 600));
-}
-
-/// @brief Tests that a domain list configuration cannot contain duplicates.
-TEST_F(DdnsDomainTest, duplicateDomain) {
-    // Create a domain list configuration that contains two domains with
-    // the same name.
-    std::string config =
-                        "[ "
-                        "{ \"name\": \"tmark.org\" , "
-                        "  \"dns-servers\" : [ "
-                        "  { \"ip-address\": \"127.0.0.3\" , "
-                        "    \"port\": 300 } ] } "
-                        ", "
-                        "{ \"name\": \"tmark.org\" , "
-                        "  \"dns-servers\" : [ "
-                        "  { \"ip-address\": \"127.0.0.3\" , "
-                        "    \"port\": 300 } ] } "
-                        "] ";
-    ASSERT_TRUE(fromJSON(config));
-
-    // Create the list parser
-    isc::dhcp::ParserPtr list_parser;
-    ASSERT_NO_THROW(list_parser.reset(
-                    new DdnsDomainListParser("test", domains_, keys_)));
-
-    // Verify that the parse build fails.
-    EXPECT_THROW(list_parser->build(config_set_), D2CfgError);
-}
-
-/// @brief Tests construction of D2CfgMgr
-/// This test verifies that a D2CfgMgr constructs properly.
-TEST(D2CfgMgr, construction) {
-    boost::scoped_ptr<D2CfgMgr> cfg_mgr;
-
-    // Verify that configuration manager constructions without error.
-    ASSERT_NO_THROW(cfg_mgr.reset(new D2CfgMgr()));
-
-    // Verify that the context can be retrieved and is not null.
-    D2CfgContextPtr context;
-    ASSERT_NO_THROW(context = cfg_mgr->getD2CfgContext());
-    EXPECT_TRUE(context);
-
-    // Verify that the forward manager can be retrieved and is not null.
-    EXPECT_TRUE(context->getForwardMgr());
-
-    // Verify that the reverse manager can be retrieved and is not null.
-    EXPECT_TRUE(context->getReverseMgr());
-
-    // Verify that the manager can be destructed without error.
-    EXPECT_NO_THROW(cfg_mgr.reset());
+    SYNTAX_ERROR(config, "<string>:1.115-121: syntax error,"
+                         " unexpected constant string, expecting JSON");
 }
 
 /// @brief Tests the parsing of a complete, valid DHCP-DDNS configuration.
@@ -1313,11 +508,8 @@ TEST_F(D2CfgMgrTest, fullConfig) {
                         "  ] } "
                         "] } }";
 
-    ASSERT_TRUE(fromJSON(config));
-
-    // Verify that we can parse the configuration.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
-    ASSERT_TRUE(checkAnswer(0));
+    // Should parse without error.
+    RUN_CONFIG_OK(config);
 
     // Verify that the D2 context can be retrieved and is not null.
     D2CfgContextPtr context;
@@ -1424,11 +616,8 @@ TEST_F(D2CfgMgrTest, forwardMatch) {
                         "\"reverse-ddns\" : {} "
                         "}";
 
-
-    ASSERT_TRUE(fromJSON(config));
     // Verify that we can parse the configuration.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
-    ASSERT_TRUE(checkAnswer(0));
+    RUN_CONFIG_OK(config);
 
     // Verify that the D2 context can be retrieved and is not null.
     D2CfgContextPtr context;
@@ -1492,11 +681,8 @@ TEST_F(D2CfgMgrTest, matchNoWildcard) {
                         "\"reverse-ddns\" : {} "
                         " }";
 
-    ASSERT_TRUE(fromJSON(config));
-
     // Verify that we can parse the configuration.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
-    ASSERT_TRUE(checkAnswer(0));
+    RUN_CONFIG_OK(config);
 
     // Verify that the D2 context can be retrieved and is not null.
     D2CfgContextPtr context;
@@ -1534,11 +720,8 @@ TEST_F(D2CfgMgrTest, matchAll) {
                         "\"reverse-ddns\" : {} "
                         "}";
 
-    ASSERT_TRUE(fromJSON(config));
-
     // Verify that we can parse the configuration.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
-    ASSERT_TRUE(checkAnswer(0));
+    RUN_CONFIG_OK(config);
 
     // Verify that the D2 context can be retrieved and is not null.
     D2CfgContextPtr context;
@@ -1596,11 +779,8 @@ TEST_F(D2CfgMgrTest, matchReverse) {
                         "  ] } "
                         "] } }";
 
-    ASSERT_TRUE(fromJSON(config));
-
     // Verify that we can parse the configuration.
-    answer_ = cfg_mgr_->parseConfig(config_set_);
-    ASSERT_TRUE(checkAnswer(0));
+    RUN_CONFIG_OK(config);
 
     // Verify that the D2 context can be retrieved and is not null.
     D2CfgContextPtr context;
@@ -1662,15 +842,13 @@ TEST_F(D2CfgMgrTest, matchReverse) {
 ///
 /// #    Each test has:
 /// #      1. description - optional text description
-/// #      2. syntax-error - text of syntax error D2Parser is expected to emit
-/// #         (defaults to blank = no error expected)
-/// #      3. logic-error - indicator if post-parsing element logic should fail
-/// #         (defaults to false)
+/// #      2. syntax-error - error JSON parser should emit (omit if none)
+/// #      3. logic-error - error element parser(s) should emit (omit if none)
 /// #      4. data - configuration text to parse
 /// #
 ///      "description" : "<text describing test>",
-///      "syntax-error" : ""|"<exact text from parser including position>" ,
-///      "logic_error" : <true|false> ,
+///      "syntax-error" : "<exact text from JSON parser including position>" ,
+///      "logic-error" : "<exact text from element parser including position>" ,
 ///      "data" :
 ///          {
 /// #        configuration elements here
@@ -1706,20 +884,13 @@ TEST_F(D2CfgMgrTest, configPermutations) {
     // Read in each test For each test, read:
     //
     //  1. description - optional text description
-    //  2. syntax-error - error text JSON parser should emit including position,
-    //     defaults to blank (i.e. no error)
-    //  3. logic-error - bool indicator if post-JSON element processing should
-    //     fail
-    //     (defaults to false)
-    //  4. data - configuration text to parse
-    //
-    // Next attempt to parse the configuration by passing it into
-    // D2CfgMgr::parseConfig().  Then check the parsing outcome against the
-    // expected outcome as given by logic-error.
+    //  2. syntax-error or logic-error or neither
+    //  3. data - configuration text to parse
+    //  4. convert data into JSON text
+    //  5. submit JSON for parsing
     isc::data::ConstElementPtr test;
     ASSERT_TRUE(tests->get("test-list"));
     BOOST_FOREACH(test, tests->get("test-list")->listValue()) {
-
         // Grab the description.
         std::string description = "<no desc>";
         isc::data::ConstElementPtr elem = test->get("description");
@@ -1727,20 +898,19 @@ TEST_F(D2CfgMgrTest, configPermutations) {
             elem->getValue(description);
         }
 
-        // Grab the outcome flag, logic-error, defaults to false if it's
-        // not specified.
-        bool logic_error = false;
-        elem = test->get("logic-error");
-        if (elem)  {
-            elem->getValue(logic_error);
-        }
-
-        // If the JSON parsing is expected to detect an error, the test
-        // should have specified the error message text.
-        std::string expected_syntax_error = "";
+        // Grab the expected error message, if there is one.
+        std::string expected_error = "";
+        RunConfigMode mode = NO_ERROR;
         elem = test->get("syntax-error");
         if (elem) {
-            elem->getValue(expected_syntax_error);
+            elem->getValue(expected_error);
+            mode = SYNTAX_ERROR;
+        } else {
+            elem = test->get("logic-error");
+            if (elem) {
+                elem->getValue(expected_error);
+                mode = LOGIC_ERROR;
+            }
         }
 
         // Grab the test's configuration data.
@@ -1748,43 +918,13 @@ TEST_F(D2CfgMgrTest, configPermutations) {
         ASSERT_TRUE(data) << "No data for test: "
                           << " : " << test->getPosition();
 
-        // Submit the configuration text to the D2Parser.  This is somewhat
-        // artifical as normally the JSON parsing is invoked by @c
-        // DController::configFromFile().  Doing it here, explicitly, allows us
-        // test both syntax and logic errors without the weight of a full
-        // server.  Currently the assumption is that the test data is the
-        // content permitted within a DhcpDdns element only.
-        try {
-            // First we turn the test data back into JSON text
-            D2ParserContext context;
-            stringstream os;
-            data->toJSON(os);
-            // Parse the test data
-            data = context.parseString(os.str(),
-                                       D2ParserContext::PARSER_SUB_DHCPDDNS);
-        } catch (const std::exception& ex) {
-            if (expected_syntax_error.empty()) {
-                // We have an unexpected syntax error.
-                ADD_FAILURE() << description << ", "
-                              << "Unexpected syntax error:" << ex.what();
-            } else {
-                // We expected and got a syntax error, we need to make sure
-                // we got the right one.
-                EXPECT_EQ(expected_syntax_error, ex.what())
-                            << description << ", syntax error mismatch";
-
-            }
-
-            // Expected or not, skip logic testing on syntax errors.
-            continue;
-        }
-
-        // Attempt to Element parse the configuration. We verify that we get the
-        // expected outcome, and if it was supposed to fail that the explanation
-        // contains position information.
-        checkAnswerWithError(cfg_mgr_->parseConfig(data),
-                             (logic_error ? SHOULD_FAIL : SHOULD_PASS));
+        // Convert the test data back to JSON text, then submit it for parsing.
+        stringstream os;
+        data->toJSON(os);
+        EXPECT_TRUE(runConfigOrFail(os.str(), mode, expected_error))
+            << " failed for test: " << test->getPosition() << std::endl;
     }
 }
 
+
 } // end of anonymous namespace
index b22a0b48eb06736948dbc2c891d6fc3695a486a0..6795e71f29d3f8f3038781e9c6f87793f2405ebc 100644 (file)
@@ -10,6 +10,7 @@
 #include <d2/d2_controller.h>
 #include <d2/d2_process.h>
 #include <process/spec_config.h>
+#include <d2/tests/nc_test_utils.h>
 #include <process/testutils/d_test_stubs.h>
 
 #include <boost/pointer_cast.hpp>
index f5271a885e7f5dee0e936cfb68750102805e8788..c6b134115694d4a923a162e84dc82ec751026b47 100644 (file)
@@ -11,6 +11,7 @@
 #include <d2/d2_process.h>
 #include <dhcp_ddns/ncr_io.h>
 #include <process/testutils/d_test_stubs.h>
+#include <d2/tests/nc_test_utils.h>
 
 #include <boost/bind.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
index 30369f10231277efd6a92c9784f55ed5abc65255..25f34bc0dc47a33bf8ec784776441768e58f561a 100644 (file)
@@ -18,60 +18,247 @@ using namespace isc::d2;
 
 namespace {
 
-/// @brief Text fixture class for D2SimpleParser
+/// @brief Checks if specified element matches the given integer default
+///
+/// @param element defaulted element to check
+/// @param deflt SimpleDefault which supplied the default valaue
+void checkIntegerValue(const ConstElementPtr& element,
+                       const SimpleDefault& deflt)  {
+    ASSERT_TRUE(element);
+
+    // Verify it is an integer.
+    ASSERT_EQ(Element::integer, element->getType());
+
+    // Turn default value string into an int.
+    int64_t default_value = 0;
+    ASSERT_NO_THROW(default_value = boost::lexical_cast<int64_t>(deflt.value_));
+
+    // Verify it has the expected value.
+    EXPECT_EQ(default_value, element->intValue());
+}
+
+/// @brief Checks if specified element matches the given boolean default
+///
+/// @param element defaulted element to check
+/// @param deflt SimpleDefault which supplied the default valaue
+void checkBooleanValue(const ConstElementPtr& element,
+                       const SimpleDefault& deflt)  {
+    ASSERT_TRUE(element);
+
+    // Verify it is a bool.
+    ASSERT_EQ(Element::boolean, element->getType());
+
+    // Turn default value string into a bool.
+    bool default_value = false;
+    ASSERT_NO_THROW(boost::lexical_cast<bool>(deflt.value_));
+
+    // Verify it has the expected value.
+    EXPECT_EQ(default_value, element->boolValue());
+}
+
+/// @brief Checks if specified element matches the given string default
+///
+/// @param element defaulted element to check
+/// @param deflt SimpleDefault which supplied the default valaue
+void checkStringValue(const ConstElementPtr& element,
+                      const SimpleDefault& deflt)  {
+    ASSERT_TRUE(element);
+
+    // Verify it's a string
+    ASSERT_EQ(Element::string, element->getType());
+
+    // Verify it has the expected value
+        EXPECT_EQ(deflt.value_, element->stringValue());
+    }
+
+/// TSIGKeyInfo against the given set of values, and that the TSIGKey
+/// member points to a key.
+///
+/// @param key is a pointer to the TSIGKeyInfo instance to verify
+/// @param name is the value to compare against key's name_.
+/// @param algorithm is the string value to compare against key's algorithm.
+/// @param secret is the value to compare against key's secret.
+///
+/// @return returns true if there is a match across the board, otherwise it
+/// returns false.
+bool checkKey(TSIGKeyInfoPtr key, const std::string& name,
+              const std::string& algorithm, const std::string& secret,
+              uint32_t digestbits = 0) {
+    // Return value, assume its a match.
+    return (((key) &&
+        (key->getName() == name) &&
+        (key->getAlgorithm() == algorithm)  &&
+        (key->getDigestbits() == digestbits) &&
+        (key->getSecret() ==  secret)  &&
+        (key->getTSIGKey())));
+}
+
+/// @brief Convenience function which compares the contents of the given
+/// DnsServerInfo against the given set of values.
+///
+/// It is structured in such a way that each value is checked, and output
+/// is generate for all that do not match.
+///
+/// @param server is a pointer to the server to check against.
+/// @param hostname is the value to compare against server's hostname_.
+/// @param ip_address is the string value to compare against server's
+/// ip_address_.
+/// @param port is the value to compare against server's port.
+///
+/// @return returns true if there is a match across the board, otherwise it
+/// returns false.
+bool checkServer(DnsServerInfoPtr server, const char* hostname,
+                 const char *ip_address, uint32_t port)
+{
+    // Return value, assume its a match.
+    bool result = true;
+
+    if (!server) {
+        EXPECT_TRUE(server);
+        return false;
+    }
+
+    // Check hostname.
+    if (server->getHostname() != hostname) {
+        EXPECT_EQ(hostname, server->getHostname());
+        result = false;
+    }
+
+    // Check IP address.
+    if (server->getIpAddress().toText() != ip_address) {
+        EXPECT_EQ(ip_address, server->getIpAddress().toText());
+        result = false;
+    }
+
+    // Check port.
+    if (server->getPort() !=  port) {
+        EXPECT_EQ (port, server->getPort());
+        result = false;
+    }
+
+    return (result);
+}
+
+/// @brief Base class test fixture for testing JSON and element parsing
+/// for D2 configuration elements.  It combines the three phases of
+/// configuration parsing normally orchestrated by D2CfgMgr:
+/// 1. Submit the JSON text to the JSON parser
+/// 2. Add defaults to the element tree produced by the JSON parser
+/// 3. Pass the element tree into the appropriate SimpleParser derivation
+/// to parse the element tree into D2 objects.
 class D2SimpleParserTest : public ::testing::Test {
 public:
-    /// @brief Checks if specified element matches the given integer default
+    /// @brief Constructor
     ///
-    /// @param element defaulted element to check
-    /// @param deflt SimpleDefault which supplied the default valaue
-    void checkIntegerValue(const ConstElementPtr& element, const SimpleDefault& deflt)  {
-        ASSERT_TRUE(element);
-
-        // Verify it is an integer.
-        ASSERT_EQ(Element::integer, element->getType());
-
-        // Turn default value string into an int.
-        int64_t default_value = 0;
-        ASSERT_NO_THROW(default_value = boost::lexical_cast<int64_t>(deflt.value_));
+    /// @param parser_type specifices the parsing starting point at which
+    /// the JSON parser should begin. It defaults to PARSER_JSON. See @c
+    /// D2ParserContext::ParserType for all possible values.
+    D2SimpleParserTest(const D2ParserContext::ParserType&
+                       parser_type = D2ParserContext::PARSER_JSON)
+        : parser_type_(parser_type) {
+        reset();
+    }
 
-        // Verify it has the expected value.
-        EXPECT_EQ(default_value, element->intValue());
+    /// @brief Destructor
+    virtual ~D2SimpleParserTest() {
+        reset();
     }
 
-    /// @brief Checks if specified element matches the given boolean default
+    /// @brief Parses JSON text and compares the results against an expected
+    /// outcome.
     ///
-    /// @param element defaulted element to check
-    /// @param deflt SimpleDefault which supplied the default valaue
-    void checkBooleanValue(const ConstElementPtr& element, const SimpleDefault& deflt)  {
-        ASSERT_TRUE(element);
+    /// The JSON text is submitted to the D2ParserContext for parsing. Any
+    /// errors emitted here are caught and compared against the expected
+    /// error or flagged as unexpected.
+    /// Next, the virtural method, setDefaults()is invoked. his method should
+    /// be used by derivations to add default values to the element tree
+    /// produced by the JSON parser.
+    /// Lastly, it passes the element tree into the virtual method,
+    /// parseElement().  This method should be used by derivations to create
+    /// the appropriate element parser to parse the element tree into the
+    /// approprate D2 object(s).
+    ///
+    /// @param json JSON text to parse
+    /// @param exp_error exact text of the error message expected or ""
+    /// if parsing should succeed.
+    ::testing::AssertionResult parseOrFail(const std::string& json,
+                                           const std::string& exp_error) {
+        bool failed = false;
+        try {
+            // Free up objects created by previous invocation
+            reset();
+
+            // Submit JSON text to JSON parser.  We convert the result to
+            // a mutable element tree to allow defaults to be added.
+            D2ParserContext context;
+            data::ElementPtr elem = boost::const_pointer_cast<Element>
+                                    (context.parseString(json, parser_type_));
+            // Add any defaults
+            setDefaults(elem);
 
-        // Verify it is a bool.
-        ASSERT_EQ(Element::boolean, element->getType());
+            // Now pares the element tree into object(s).
+            parseElement(elem);
+        } catch (const std::exception& ex) {
+            std::string caught_error = ex.what();
+            if (exp_error.empty()) {
+                return ::testing::AssertionFailure()
+                        << "Unexpected error: " << caught_error
+                        << "\n json: [" << json << "]";
+            }
+
+            if (exp_error != caught_error) {
+                return ::testing::AssertionFailure()
+                                << "Wrong error detected, expected: "
+                                << exp_error << ", got: " << caught_error
+                                << "\n json: [" << json << "]";
+            }
+            return ::testing::AssertionSuccess();
+        }
 
-        // Turn default value string into a bool.
-        bool default_value = false;
-        ASSERT_NO_THROW(boost::lexical_cast<bool>(deflt.value_));
+        if (!exp_error.empty()) {
+            return ::testing::AssertionFailure()
+                       << "Unexpected parsing success "
+                       << exp_error << "\n json: [" << json << "]";
+        }
 
-        // Verify it has the expected value.
-        EXPECT_EQ(default_value, element->boolValue());
+        return ::testing::AssertionSuccess();
     }
 
-    /// @brief Checks if specified element matches the given string default
+
+protected:
+    /// @brief Free up objects created by element parsing
+    /// This method is invoked at the beginning of @c parseOrFail() to
+    /// ensure any D2 object(s) that were created by a prior invocation are
+    /// destroyed. This permits parsing to be conducted more than once
+    /// in the same test.
+    virtual void reset(){};
+
+    /// @brief Adds default values to the given element tree
+    ///
+    /// Derivations are expected to use the appropriate methods in
+    /// D2SimpleParser to add defaults values.
     ///
-    /// @param element defaulted element to check
-    /// @param deflt SimpleDefault which supplied the default valaue
-    void checkStringValue(const ConstElementPtr& element, const SimpleDefault& deflt)  {
-        ASSERT_TRUE(element);
+    /// @param config element tree in which defaults should be added
+    /// @return the number of default items added to the tree
+    virtual size_t setDefaults(data::ElementPtr config) { return (0); }
 
-        // Verify it's a string
-        ASSERT_EQ(Element::string, element->getType());
+    /// @brief Parses a given element tree into D2 object(s)
+    ///
+    /// Derivations are expected to create the appropriate element
+    /// parser and pass it the element tree for parsing.  Any object(s)
+    /// created should likely be saved for content verification
+    /// outside of this method.
+    ///
+    /// @param config element tree to parse
+    virtual void parseElement(data::ConstElementPtr config) {};
 
-        // Verify it has the expected value
-        EXPECT_EQ(deflt.value_, element->stringValue());
-    }
+    D2ParserContext::ParserType parser_type_;
 };
 
+/// @brief Convenience macros for calling parseOrFail
+#define PARSE_OK(a) EXPECT_TRUE((parseOrFail(a, "")))
+#define PARSE_FAIL(a,b) EXPECT_TRUE((parseOrFail(a, b)))
+
 // This test checks if global defaults are properly set for D2.
 TEST_F(D2SimpleParserTest, globalD2Defaults) {
 
@@ -81,14 +268,14 @@ TEST_F(D2SimpleParserTest, globalD2Defaults) {
     EXPECT_NO_THROW(num = D2SimpleParser::setAllDefaults(empty));
 
     // We expect 5 parameters to be inserted.
-    EXPECT_EQ(num, 5);
+    EXPECT_EQ(num, 8);
 
     // Let's go over all parameters we have defaults for.
     BOOST_FOREACH(SimpleDefault deflt, D2SimpleParser::D2_GLOBAL_DEFAULTS) {
         ConstElementPtr x;
         ASSERT_NO_THROW(x = empty->get(deflt.name_));
-        
-        EXPECT_TRUE(x); 
+
+        EXPECT_TRUE(x);
         if (x) {
             if (deflt.type_ == Element::integer) {
                 checkIntegerValue(x, deflt);
@@ -100,9 +287,857 @@ TEST_F(D2SimpleParserTest, globalD2Defaults) {
                 // add them if we need to. Like what do you if it's a map?
                 ADD_FAILURE() << "default type not supported:" << deflt.name_
                             << " ,type: " << deflt.type_;
-            } 
+            }
+        }
+    }
+}
+
+/// @brief Test fixture class for testing TSIGKeyInfo parsing.
+class TSIGKeyInfoParserTest : public D2SimpleParserTest {
+public:
+    /// @brief Constructor
+    TSIGKeyInfoParserTest()
+        : D2SimpleParserTest(D2ParserContext::PARSER_TSIG_KEY) {
+    }
+
+    /// @brief Free up the keys created by parsing
+    virtual void reset() {
+        key_.reset();
+    };
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfoParserTest() {
+        reset();
+    };
+
+    /// @brief Adds TSIG Key default values to the given TSIG Key element
+    ///
+    /// @param config TSIG Key element to which defaults should be added
+    ///
+    /// @return the number of default items added to the tree
+    size_t setDefaults(data::ElementPtr config) {
+        return (SimpleParser::setDefaults(config, D2SimpleParser::
+                TSIG_KEY_DEFAULTS));
+    }
+
+    /// @brief Attempts to parse the given element into a TSIGKeyInfo
+    ///
+    /// Assumes the given element is a Map containing the attributes for
+    /// a TSIG Key. If parsing is successful the new TSIGKeyInfo instance
+    /// is retained in the member, key_;
+    ///
+    /// @param config element to parse
+    void parseElement(data::ConstElementPtr config) {
+        TSIGKeyInfoParser parser;
+        key_ = parser.parse(config);
+    }
+
+    /// @brief Retains the TSIGKeyInfo created by a successful parsing
+    TSIGKeyInfoPtr key_;
+};
+
+
+/// @brief Test fixture class for testing TSIGKeyInfo list parsing.
+class TSIGKeyInfoListParserTest : public D2SimpleParserTest {
+public:
+    /// @brief Constructor
+    TSIGKeyInfoListParserTest()
+        : D2SimpleParserTest(D2ParserContext::PARSER_TSIG_KEYS) {
+    }
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfoListParserTest() {
+        reset();
+    }
+
+    /// @brief Free up the keys created by parsing
+    virtual void reset() {
+        keys_.reset();
+    };
+
+    /// @brief Adds TSIG Key default values to a list of TSIG Key elements
+    ///
+    /// @param config list of TSIG Key elements to which defaults should be
+    /// added
+    ///
+    /// @return the number of default items added to the tree
+    size_t setDefaults(data::ElementPtr config) {
+        return (SimpleParser::setListDefaults(config, D2SimpleParser::
+                                                      TSIG_KEY_DEFAULTS));
+    }
+
+    /// @brief Attempts to parse the given element into a list of TSIGKeyInfos
+    ///
+    /// Assumes the given element is a list containing one or more TSIG Keys
+    /// elements.  If parsing is successful the list of TSIGKeyInfo instances
+    /// is retained in the member, keys_;
+    ///
+    /// @param config element to parse
+    void parseElement(data::ConstElementPtr config) {
+        TSIGKeyInfoListParser parser;
+        keys_ = parser.parse(config);
+    }
+
+    /// @brief Retains the TSIGKeyInfos created by a successful parsing
+    TSIGKeyInfoMapPtr keys_;
+};
+
+/// @brief Test fixture class for testing DnsServerInfo parsing.
+class DnsServerInfoParserTest : public D2SimpleParserTest {
+public:
+    /// @brief Constructor
+    DnsServerInfoParserTest()
+        : D2SimpleParserTest(D2ParserContext::PARSER_DNS_SERVER) {
+    }
+
+    /// @brief Destructor
+    virtual ~DnsServerInfoParserTest() {
+        reset();
+    }
+
+    /// @brief Free up the server created by parsing
+    virtual void reset() {
+        server_.reset();
+    }
+
+    /// @brief Adds DNS Server default values to the given DNS Server element
+    ///
+    /// @param config DNS Server element to which defaults should be added
+    ///
+    /// @return the number of default items added to the tree
+    virtual size_t setDefaults(data::ElementPtr config) {
+        return (SimpleParser::setDefaults(config, D2SimpleParser::
+                                                  DNS_SERVER_DEFAULTS));
+    }
+
+    /// @brief Attempts to parse the given element into a DnsServerInfo
+    ///
+    /// Assumes the given element is a map containing the attributes for
+    /// a DNS Server. If parsing is successful the new DnsServerInfo instance
+    /// is retained in the member, server_;
+    ///
+    /// @param config element to parse
+    virtual void parseElement(data::ConstElementPtr config) {
+        DnsServerInfoParser parser;
+        server_ = parser.parse(config);
+    }
+
+    /// @brief Retains the DnsServerInfo created by a successful parsing
+    DnsServerInfoPtr server_;
+};
+
+/// @brief Test fixture class for testing DnsServerInfoList parsing.
+class DnsServerInfoListParserTest : public D2SimpleParserTest {
+public:
+    /// @brief Constructor
+    DnsServerInfoListParserTest()
+        : D2SimpleParserTest(D2ParserContext::PARSER_DNS_SERVERS) {
+    }
+
+    /// @brief Destructor
+    virtual ~DnsServerInfoListParserTest() {
+        reset();
+    }
+
+    /// @brief Free up the servers created by parsing
+    virtual void reset() {
+        servers_.reset();
+    }
+
+    /// @brief Adds DNS Server default values to a list of DNS Server elements
+    ///
+    /// @param config list of DNS Server elements to which defaults should be
+    /// added
+    ///
+    /// @return the number of default items added to the tree
+    virtual size_t setDefaults(data::ElementPtr config) {
+        return (SimpleParser::setListDefaults(config, D2SimpleParser::
+                                                      DNS_SERVER_DEFAULTS));
+    }
+
+    /// @brief Attempts to parse the given element into a list of DnsServerInfos
+    ///
+    /// Assumes the given element is a list containing one or more DNS Servers
+    /// elements.  If parsing is successful the list of DnsServerInfo instances
+    /// is retained in the member, keys_;
+    ///
+    /// @param config element to parse
+    virtual void parseElement(data::ConstElementPtr config) {
+        DnsServerInfoListParser parser;
+        servers_ = parser.parse(config);
+    }
+
+    /// @brief Retains the DnsServerInfos created by a successful parsing
+    DnsServerInfoStoragePtr servers_;
+};
+
+
+/// @brief Test fixture class for testing DDnsDomain parsing.
+class DdnsDomainParserTest : public D2SimpleParserTest {
+public:
+
+    /// @brief Constructor
+    DdnsDomainParserTest(const D2ParserContext::ParserType& parser_type
+                         = D2ParserContext::PARSER_DDNS_DOMAIN)
+        : D2SimpleParserTest(parser_type), keys_(new TSIGKeyInfoMap())  {
+    }
+
+    /// @brief Destructor
+    virtual ~DdnsDomainParserTest() {
+        reset();
+    }
+
+    /// @brief Free up the domain created by parsing
+    virtual void reset() {
+        domain_.reset();
+    }
+
+    /// @brief Add TSIGKeyInfos to the key map
+    ///
+    /// @param name the name of the key
+    /// @param algorithm the algorithm of the key
+    /// @param secret the secret value of the key
+    void addKey(const std::string& name, const std::string& algorithm,
+                const std::string& secret) {
+        TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));
+        (*keys_)[name]=key_info;
+    }
+
+    /// @brief Adds DDNS Domain values to the given DDNS Domain element
+    ///
+    /// @param config DDNS Domain element to which defaults should be added
+    ///
+    /// @return the number of default items added to the tree
+    virtual size_t setDefaults(data::ElementPtr config) {
+        return (D2SimpleParser::setDdnsDomainDefaults(config, D2SimpleParser::
+                                                      DDNS_DOMAIN_DEFAULTS));
+    }
+
+    /// @brief Attempts to parse the given element into a DdnsDomain
+    ///
+    /// Assumes the given element is a map containing the attributes for
+    /// a DDNS Domain. If parsing is successful the new DdnsDomain instance
+    /// is retained in the member, server_;
+    ///
+    /// @param config element to parse
+    virtual void parseElement(data::ConstElementPtr config) {
+        DdnsDomainParser parser;
+        domain_ = parser.parse(config, keys_);
+    }
+
+    /// @brief Retains the DdnsDomain created by a successful parsing
+    DdnsDomainPtr domain_;
+
+    /// @brief Storage for TSIGKeys, used by DdnsDomainParser to validate
+    /// domain keys
+    TSIGKeyInfoMapPtr keys_;
+};
+
+class DdnsDomainListParserTest : public DdnsDomainParserTest {
+public:
+    /// @brief Constructor
+    DdnsDomainListParserTest()
+        // We need the list context type to parse lists correctly
+        : DdnsDomainParserTest(D2ParserContext::PARSER_DDNS_DOMAINS) {
+    }
+
+    /// @brief Destructor
+    virtual ~DdnsDomainListParserTest() {
+        reset();
+    }
+
+    /// @brief Free up domains created by parsing
+    virtual void reset() {
+        domains_.reset();
+    }
+
+    /// @brief Adds DDNS Domain default values to a list of DDNS Domain elements
+    ///
+    /// @param config list of DDNS Domain elements to which defaults should be
+    /// added
+    ///
+    /// @return the number of default items added to the tree
+    virtual size_t setDefaults(data::ElementPtr config) {
+        size_t cnt = 0;
+        // We don't use SimpleParser::setListDefaults() as this does
+        // not handle sub-lists or sub-maps
+        BOOST_FOREACH(ElementPtr domain, config->listValue()) {
+            cnt += D2SimpleParser::
+                   setDdnsDomainDefaults(domain, D2SimpleParser::
+                                                 DDNS_DOMAIN_DEFAULTS);
         }
+
+        return (cnt);
     }
+
+    /// @brief Attempts to parse the given element into a list of DdnsDomains
+    ///
+    /// Assumes the given element is a list containing one or more DDNS Domains
+    /// elements.  If parsing is successful the list of DdnsDomain instances
+    /// is retained in the member, keys_;
+    ///
+    /// @param config element to parse
+    virtual void parseElement(data::ConstElementPtr config) {
+        DdnsDomainListParser parser;
+        domains_ = parser.parse(config, keys_);
+    }
+
+    /// @brief Retains the DdnsDomains created by a successful parsing
+    DdnsDomainMapPtr domains_;
+};
+
+/// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos.
+/// It verifies that:
+/// 1. Name cannot be blank.
+/// 2. Algorithm cannot be blank.
+/// 3. Secret cannot be blank.
+TEST_F(TSIGKeyInfoParserTest, invalidEntry) {
+    // Name cannot be blank.
+    std::string config = "{"
+                         " \"name\": \"\" , "
+                         " \"algorithm\": \"HMAC-MD5\" , "
+                         " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
+                         "}";
+    PARSE_FAIL(config, "<string>:1.9: TSIG key name cannot be blank");
+
+    // Algorithm cannot be be blank.
+    config = "{"
+              " \"name\": \"d2_key_one\" , "
+              " \"algorithm\": \"\" , "
+              "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
+              "}";
+    PARSE_FAIL(config, "<string>:1.38: TSIG key algorithm cannot be blank");
+
+    // Algorithm must be a valid algorithm
+    config = "{"
+              " \"name\": \"d2_key_one\" , "
+              " \"algorithm\": \"bogus\" , "
+              "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
+              "}";
+    PARSE_FAIL(config, "tsig-key : Unknown TSIG Key algorithm:"
+                       " bogus (<string>:1:40)");
+
+    // Secret cannot be blank
+    config = "{"
+              " \"name\": \"d2_key_one\" , "
+              " \"algorithm\": \"HMAC-MD5\" , "
+              " \"secret\": \"\" "
+              "}";
+    PARSE_FAIL(config, "<string>:1.62: TSIG key secret cannot be blank");
+
+    // Secret must be valid for alogorithm
+    config = "{"
+              " \"name\": \"d2_key_one\" , "
+              " \"algorithm\": \"HMAC-MD5\" , "
+              " \"digest-bits\": 120 , "
+              " \"secret\": \"bogus\" "
+              "}";
+    PARSE_FAIL(config, "Cannot make TSIGKey: Incomplete input for base64:"
+                       " bogus (<string>:1:1)");
+}
+
+
+/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
+/// when given a valid combination of entries.
+TEST_F(TSIGKeyInfoParserTest, validEntry) {
+    // Valid entries for TSIG key, all items are required.
+    std::string config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"HMAC-MD5\" , "
+                         " \"digest-bits\": 120 , "
+                         " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
+                         "}";
+    // Verify that it parses.
+    PARSE_OK(config);
+    ASSERT_TRUE(key_);
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key_, "d2_key_one", "HMAC-MD5",
+                         "dGhpcyBrZXkgd2lsbCBtYXRjaA==", 120));
+}
+
+/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
+/// entries is detected.
+TEST_F(TSIGKeyInfoListParserTest, invalidTSIGKeyList) {
+    // Construct a list of keys with an invalid key entry.
+    std::string config = "["
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"HMAC-MD5\" ,"
+                         "   \"digest-bits\": 120 , "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
+                         " },"
+                         // this entry has an invalid algorithm
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"\" ,"
+                         "   \"digest-bits\": 120 , "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
+                         " },"
+                         " { \"name\": \"key3\" , "
+                         "   \"algorithm\": \"HMAC-MD5\" ,"
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
+                         " }"
+                         " ]";
+
+    PARSE_FAIL(config, "<string>:1.151: TSIG key algorithm cannot be blank");
+}
+
+/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
+/// entries is detected.
+TEST_F(TSIGKeyInfoListParserTest, duplicateTSIGKey) {
+    // Construct a list of keys with an invalid key entry.
+    std::string config = "["
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"HMAC-MD5\" ,"
+                         " \"digest-bits\": 120 , "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"HMAC-MD5\" ,"
+                         " \"digest-bits\": 120 , "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
+                         " },"
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"HMAC-MD5\" ,"
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
+                         " }"
+                         " ]";
+
+    PARSE_FAIL(config,
+               "Duplicate TSIG key name specified : key1 (<string>:1:239)");
+}
+
+/// @brief Verifies a valid list of TSIG Keys parses correctly.
+/// Also verifies that all of the supported algorithm names work.
+TEST_F(TSIGKeyInfoListParserTest, validTSIGKeyList) {
+    // Construct a valid list of keys.
+    std::string config = "["
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"HMAC-MD5\" ,"
+                         " \"digest-bits\": 80 , "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"HMAC-SHA1\" ,"
+                         " \"digest-bits\": 80 , "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
+                         " },"
+                         " { \"name\": \"key3\" , "
+                         "   \"algorithm\": \"HMAC-SHA256\" ,"
+                         " \"digest-bits\": 128 , "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
+                         " },"
+                         " { \"name\": \"key4\" , "
+                         "   \"algorithm\": \"HMAC-SHA224\" ,"
+                         " \"digest-bits\": 112 , "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
+                         " },"
+                         " { \"name\": \"key5\" , "
+                         "   \"algorithm\": \"HMAC-SHA384\" ,"
+                         " \"digest-bits\": 192 , "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
+                         " },"
+                         " { \"name\": \"key6\" , "
+                         "   \"algorithm\": \"HMAC-SHA512\" ,"
+                         " \"digest-bits\": 256 , "
+                         "   \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
+                         " }"
+                         " ]";
+
+    PARSE_OK(config);
+    ASSERT_TRUE(keys_);
+
+    std::string ref_secret = "dGhpcyBrZXkgd2lsbCBtYXRjaA==";
+    // Verify the correct number of keys are present
+    int count =  keys_->size();
+    ASSERT_EQ(6, count);
+
+    // Find the 1st key and retrieve it.
+    TSIGKeyInfoMap::iterator gotit = keys_->find("key1");
+    ASSERT_TRUE(gotit != keys_->end());
+    TSIGKeyInfoPtr& key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::HMAC_MD5_STR,
+                         ref_secret, 80));
+
+    // Find the 2nd key and retrieve it.
+    gotit = keys_->find("key2");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::HMAC_SHA1_STR,
+                         ref_secret, 80));
+
+    // Find the 3rd key and retrieve it.
+    gotit = keys_->find("key3");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::HMAC_SHA256_STR,
+                         ref_secret, 128));
+
+    // Find the 4th key and retrieve it.
+    gotit = keys_->find("key4");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::HMAC_SHA224_STR,
+                         ref_secret, 112));
+
+    // Find the 5th key and retrieve it.
+    gotit = keys_->find("key5");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::HMAC_SHA384_STR,
+                         ref_secret, 192));
+
+    // Find the 6th key and retrieve it.
+    gotit = keys_->find("key6");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::HMAC_SHA512_STR,
+                         ref_secret, 256));
+}
+
+/// @brief Tests the enforcement of data validation when parsing DnsServerInfos.
+/// It verifies that:
+/// 1. Specifying both a hostname and an ip address is not allowed.
+/// 2. Specifying both blank a hostname and blank ip address is not allowed.
+/// 3. Specifying a negative port number is not allowed.
+
+TEST_F(DnsServerInfoParserTest, invalidEntry) {
+    // Create a config in which both host and ip address are supplied.
+    // Verify that parsing fails.
+    std::string config = "{ \"hostname\": \"pegasus.tmark\", "
+                         "  \"ip-address\": \"127.0.0.1\", "
+                         "  \"port\": 100} ";
+    PARSE_FAIL(config, "<string>:1.13: hostname is not yet supported");
+
+
+    // Neither host nor ip address supplied
+    // Verify that builds fails.
+    config = "{ \"hostname\": \"\", "
+             "  \"ip-address\": \"\", "
+             "  \"port\": 100} ";
+    PARSE_FAIL(config, "Dns Server must specify one or the other"
+                          " of hostname or IP address (<string>:1:1)");
+
+    // Create a config with a negative port number.
+    // Verify that build fails.
+    config = "{ \"hostname\": \"\", "
+             "  \"ip-address\": \"192.168.5.6\" ,"
+             "  \"port\": -100 }";
+    PARSE_FAIL(config, "<string>:1.60-63: port must be greater than zero");
+}
+
+
+/// @brief Verifies that DnsServerInfo parsing creates a proper DnsServerInfo
+/// when given a valid combination of entries.
+/// It verifies that:
+/// 1. A DnsServerInfo entry is correctly made, when given only a hostname.
+/// 2. A DnsServerInfo entry is correctly made, when given ip address and port.
+/// 3. A DnsServerInfo entry is correctly made, when given only an ip address.
+TEST_F(DnsServerInfoParserTest, validEntry) {
+    /// @todo When resolvable hostname is supported you'll need this test.
+    /// // Valid entries for dynamic host
+    /// std::string config = "{ \"hostname\": \"pegasus.tmark\" }";
+    /// ASSERT_TRUE(fromJSON(config));
+
+    /// // Verify that it builds and commits without throwing.
+    /// ASSERT_NO_THROW(parser_->build(config_set_));
+    /// ASSERT_NO_THROW(parser_->commit());
+
+    /// //Verify the correct number of servers are present
+    /// int count =  servers_->size();
+    /// EXPECT_EQ(1, count);
+
+    /// Verify the server exists and has the correct values.
+    /// DnsServerInfoPtr server = (*servers_)[0];
+    /// EXPECT_TRUE(checkServer(server, "pegasus.tmark",
+    ///                         DnsServerInfo::EMPTY_IP_STR,
+    ///                         DnsServerInfo::STANDARD_DNS_PORT));
+
+    /// // Start over for a new test.
+    /// reset();
+
+    // Valid entries for static ip
+    std::string config = " { \"hostname\" : \"\", "
+                         "  \"ip-address\": \"127.0.0.1\" , "
+                         "  \"port\": 100 }";
+    PARSE_OK(config);
+    ASSERT_TRUE(server_);
+    EXPECT_TRUE(checkServer(server_, "", "127.0.0.1", 100));
+
+    // Valid entries for static ip, no port
+    // This will fail without invoking set defaults
+    config = " { \"ip-address\": \"192.168.2.5\" }";
+    PARSE_OK(config);
+    ASSERT_TRUE(server_);
+    EXPECT_TRUE(checkServer(server_, "", "192.168.2.5",
+                            DnsServerInfo::STANDARD_DNS_PORT));
+}
+
+/// @brief Verifies that attempting to parse an invalid list of DnsServerInfo
+/// entries is detected.
+TEST_F(DnsServerInfoListParserTest, invalidServerList) {
+    // Construct a list of servers with an invalid server entry.
+    std::string config = "[ { \"ip-address\": \"127.0.0.1\" }, "
+                        "{ \"ip-address\": \"\" }, "
+                        "{ \"ip-address\": \"127.0.0.2\" } ]";
+    PARSE_FAIL(config, "Dns Server must specify one or the other"
+                          " of hostname or IP address (<string>:1:34)");
+    ASSERT_FALSE(servers_);
+}
+
+/// @brief Verifies that a list of DnsServerInfo entries parses correctly given
+/// a valid configuration.
+TEST_F(DnsServerInfoListParserTest, validServerList) {
+    // Create a valid list of servers.
+    std::string config = "[ { \"ip-address\": \"127.0.0.1\" }, "
+                        "{ \"ip-address\": \"127.0.0.2\" }, "
+                        "{ \"ip-address\": \"127.0.0.3\" } ]";
+    PARSE_OK(config);
+
+    // Verify that the server storage contains the correct number of servers.
+    ASSERT_EQ(3, servers_->size());
+
+    // Verify the first server exists and has the correct values.
+    DnsServerInfoPtr server = (*servers_)[0];
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1",
+                            DnsServerInfo::STANDARD_DNS_PORT));
+
+    // Verify the second server exists and has the correct values.
+    server = (*servers_)[1];
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.2",
+                            DnsServerInfo::STANDARD_DNS_PORT));
+
+    // Verify the third server exists and has the correct values.
+    server = (*servers_)[2];
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.3",
+                            DnsServerInfo::STANDARD_DNS_PORT));
+}
+
+/// @brief Tests the enforcement of data validation when parsing DdnsDomains.
+/// It verifies that:
+/// 1. Domain storage cannot be null when constructing a DdnsDomainParser.
+/// 2. The name entry is not optional.
+/// 3. The server list may not be empty.
+/// 4. That a mal-formed server entry is detected.
+/// 5. That an undefined key name is detected.
+TEST_F(DdnsDomainParserTest, invalidDomain) {
+    // Create a domain configuration without a name
+    std::string config = "{  \"key-name\": \"d2_key.tmark.org\" , "
+                         "  \"dns-servers\" : [ "
+                         "  {  \"ip-address\": \"127.0.0.1\" , "
+                         "    \"port\": 100 },"
+                         "  { \"ip-address\": \"127.0.0.2\" , "
+                         "    \"port\": 200 },"
+                         "  {  \"ip-address\": \"127.0.0.3\" , "
+                         "    \"port\": 300 } ] } ";
+    PARSE_FAIL(config, "String parameter name not found(<string>:1:1)");
+
+    // Create a domain configuration with an empty server list.
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key-name\": \"\" , "
+             "  \"dns-servers\" : [ "
+             "   ] } ";
+    PARSE_FAIL(config, "<string>:1.67: syntax error, unexpected ], expecting {");
+
+    // Create a domain configuration with a mal-formed server entry.
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key-name\": \"\" , "
+             "  \"dns-servers\" : [ "
+             "  {  \"ip-address\": \"127.0.0.3\" , "
+             "    \"port\": -1 } ] } ";
+    PARSE_FAIL(config, "<string>:1.109-110: port must be greater than zero");
+
+    // Create a domain configuration without an defined key name
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key-name\": \"d2_key.tmark.org\" , "
+             "  \"dns-servers\" : [ "
+             "  {  \"ip-address\": \"127.0.0.3\" , "
+             "    \"port\": 300 } ] } ";
+    PARSE_FAIL(config, "DdnsDomain : tmark.org specifies"
+                " an undefined key: d2_key.tmark.org (<string>:1:39)");
+}
+
+/// @brief Verifies the basics of parsing of a DdnsDomain.
+TEST_F(DdnsDomainParserTest, validDomain) {
+    // Add a TSIG key to the test key map, so key validation will pass.
+    addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
+
+    // Create a valid domain configuration entry containing three valid
+    // servers.
+    std::string config =
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key-name\": \"d2_key.tmark.org\" , "
+                        "  \"dns-servers\" : [ "
+                        "  {  \"ip-address\": \"127.0.0.1\" , "
+                        "    \"port\": 100 },"
+                        "  { \"ip-address\": \"127.0.0.2\" , "
+                        "    \"port\": 200 },"
+                        "  {  \"ip-address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } ";
+    PARSE_OK(config);
+
+    // Domain should exist
+    ASSERT_TRUE(domain_);
+
+    // Verify the name and key_name values.
+    EXPECT_EQ("tmark.org", domain_->getName());
+    EXPECT_EQ("d2_key.tmark.org", domain_->getKeyName());
+    ASSERT_TRUE(domain_->getTSIGKeyInfo());
+    ASSERT_TRUE(domain_->getTSIGKeyInfo()->getTSIGKey());
+
+    // Verify that the server list exists and contains the correct number of
+    // servers.
+    const DnsServerInfoStoragePtr& servers = domain_->getServers();
+    EXPECT_TRUE(servers);
+    EXPECT_EQ(3, servers->size());
+
+    // Fetch each server and verify its contents.
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
+}
+
+/// @brief Tests the fundamentals of parsing DdnsDomain lists.
+/// This test verifies that given a valid domain list configuration
+/// it will accurately parse and populate each domain in the list.
+TEST_F(DdnsDomainListParserTest, validList) {
+    // Add keys to key map so key validation passes.
+    addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
+    addKey("d2_key.billcat.net", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
+
+    // Create a valid domain list configuration, with two domains
+    // that have three servers each.
+    std::string config =
+                        "[ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key-name\": \"d2_key.tmark.org\" , "
+                        "  \"dns-servers\" : [ "
+                        "  { \"ip-address\": \"127.0.0.1\" , "
+                        "    \"port\": 100 },"
+                        "  { \"ip-address\": \"127.0.0.2\" , "
+                        "    \"port\": 200 },"
+                        "  { \"ip-address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        ", "
+                        "{ \"name\": \"billcat.net\" , "
+                        "  \"key-name\": \"d2_key.billcat.net\" , "
+                        "  \"dns-servers\" : [ "
+                        "  { \"ip-address\": \"127.0.0.4\" , "
+                        "    \"port\": 400 },"
+                        "  { \"ip-address\": \"127.0.0.5\" , "
+                        "    \"port\": 500 },"
+                        "  { \"ip-address\": \"127.0.0.6\" , "
+                        "    \"port\": 600 } ] } "
+                        "] ";
+
+    // Verify that the domain list parses without error.
+    PARSE_OK(config);
+    ASSERT_TRUE(domains_);
+    EXPECT_EQ(2, domains_->size());
+
+    // Verify that the first domain exists and can be retrieved.
+    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
+    ASSERT_TRUE(gotit != domains_->end());
+    DdnsDomainPtr& domain = gotit->second;
+
+    // Verify the name and key_name values of the first domain.
+    EXPECT_EQ("tmark.org", domain->getName());
+    EXPECT_EQ("d2_key.tmark.org", domain->getKeyName());
+
+    // Verify the TSIGKeyInfo name and that the actual key was created
+    ASSERT_TRUE(domain->getTSIGKeyInfo());
+    EXPECT_EQ(domain->getKeyName(), domain->getTSIGKeyInfo()->getName());
+    EXPECT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
+
+    // Verify the each of the first domain's servers
+    DnsServerInfoStoragePtr servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    EXPECT_EQ(3, servers->size());
+
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
+
+    // Verify second domain
+    gotit = domains_->find("billcat.net");
+    ASSERT_TRUE(gotit != domains_->end());
+    domain = gotit->second;
+
+    // Verify the name and key_name values of the second domain.
+    EXPECT_EQ("billcat.net", domain->getName());
+    EXPECT_EQ("d2_key.billcat.net", domain->getKeyName());
+    ASSERT_TRUE(domain->getTSIGKeyInfo());
+    EXPECT_EQ(domain->getKeyName(), domain->getTSIGKeyInfo()->getName());
+    EXPECT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
+
+    // Verify the each of second domain's servers
+    servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    servers->size();
+    EXPECT_EQ(3, servers->size());
+
+    server = (*servers)[0];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.4", 400));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.5", 500));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.6", 600));
+}
+
+/// @brief Tests that a domain list configuration cannot contain duplicates.
+TEST_F(DdnsDomainListParserTest, duplicateDomain) {
+    // Create a domain list configuration that contains two domains with
+    // the same name.
+    std::string config =
+                        "[ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns-servers\" : [ "
+                        "  { \"ip-address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        ", "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns-servers\" : [ "
+                        "  { \"ip-address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        "] ";
+    // Verify that the parsing fails.
+    PARSE_FAIL(config,
+               "Duplicate domain specified:tmark.org (<string>:1:113)");
 }
 
 };
index 7f39b6cbac40c4c49b3d4d37a89a4ccb3d42202c..00ade2f36c5233a66cb59ce4732881019c16d44a 100644 (file)
@@ -22,6 +22,31 @@ using namespace isc::d2;
 namespace isc {
 namespace d2 {
 
+const char* valid_d2_config = "{ "
+                        "\"ip-address\" : \"127.0.0.1\" , "
+                        "\"port\" : 5031, "
+                        "\"tsig-keys\": ["
+                        "{ \"name\": \"d2_key.tmark.org\" , "
+                        "   \"algorithm\": \"HMAC-MD5\" ,"
+                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
+                        "} ],"
+                        "\"forward-ddns\" : {"
+                        "\"ddns-domains\": [ "
+                        "{ \"name\": \"tmark.org.\" , "
+                        "  \"key-name\": \"d2_key.tmark.org\" , "
+                        "  \"dns-servers\" : [ "
+                        "  { \"ip-address\": \"127.0.0.101\" } "
+                        "] } ] }, "
+                        "\"reverse-ddns\" : {"
+                        "\"ddns-domains\": [ "
+                        "{ \"name\": \" 0.168.192.in.addr.arpa.\" , "
+                        "  \"key-name\": \"d2_key.tmark.org\" , "
+                        "  \"dns-servers\" : [ "
+                        "  { \"ip-address\": \"127.0.0.101\" , "
+                        "    \"port\": 100 } ] } "
+                        "] } }";
+
+
 const char* TEST_DNS_SERVER_IP = "127.0.0.1";
 size_t TEST_DNS_SERVER_PORT = 5301;
 
index 2bd169c70ce9e1f9791fe5d3bbd4ed48f9426fe6..808310baf76623827014fa0ae1a979fafd9a5ecd 100644 (file)
@@ -20,6 +20,7 @@
 namespace isc {
 namespace d2 {
 
+extern const char* valid_d2_config;
 extern const char* TEST_DNS_SERVER_IP;
 extern size_t TEST_DNS_SERVER_PORT;
 
index 924af245cd4852f322a3099ac48d5c684f4d7d28..9b65489a6667f426e4f41022de03c8cb5c95a4c7 100644 (file)
@@ -27,7 +27,6 @@
 # as well as validating this as the smallest config which makes writing
 # permutations easier.
 "description" : "D2 smallest, valid config",
-"logic-error" : false,
 "data" :
     {
     "forward-ddns" : {},
@@ -37,9 +36,9 @@
 }
 
 #-----
+# Map should be supplied through setDefaults
 ,{
 "description" : "D2 missing forward-ddns map",
-"logic-error" : true,
 "data" :
     {
     "reverse-ddns" : {},
@@ -48,9 +47,9 @@
 }
 
 #-----
+# Map should be supplied through setDefaults
 ,{
 "description" : "D2 missing reverse-ddns map",
-"logic-error" : true,
 "data" :
     {
     "forward-ddns" : {},
@@ -60,9 +59,9 @@
 
 
 #-----
+# Map should be supplied through setDefaults
 ,{
 "description" : "D2 missing tsig-keys list",
-"logic-error" : true,
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2Params.ip-address invalid value",
-"logic-error" : true,
+"logic-error" : "invalid address (bogus) specified for parameter 'ip-address' (<string>:1:39)",
 "data" :
     {
     "ip-address" : "bogus",
 #-----
 ,{
 "description" : "D2Params.ncr-protocol, unsupported TCP",
-"logic-error" : true,
+"logic-error" : "ncr-protocol : TCP is not yet supported  (<string>:1:41)",
 "data" :
     {
     "ncr-protocol" : "TCP",
 #-----
 ,{
 "description" : "D2.tsig-keys, missing key name",
-"logic-error" : true,
+"logic-error" : "element: tsig-keys : String parameter name not found(<string>:1:62)<string>:1:47",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, blank key name",
-"syntax-error" : "<string>:1.95: name cannot be blank",
+"syntax-error" : "<string>:1.95: TSIG key name cannot be blank",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, duplicate key name",
-"logic-error" : true,
+"logic-error" : "Duplicate TSIG key name specified : first.key (<string>:1:185)",
 "data" :
     {
     "forward-ddns" : {},
 #----- D2.tsig-keys, algorithm tests
 ,{
 "description" : "D2.tsig-keys, missing algorithm",
-"logic-error" : true,
+"logic-error" : "element: tsig-keys : String parameter algorithm not found(<string>:1:62)<string>:1:47",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, blank algorithm",
-"syntax-error" : "<string>:1.75: algorithm cannot be blank",
+"syntax-error" : "<string>:1.75: TSIG key algorithm cannot be blank",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, invalid algorithm",
-"logic-error" : true,
+"logic-error" : "tsig-key : Unknown TSIG Key algorithm: bogus (<string>:1:77)",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, invalid digest-bits",
-"syntax-error" : "<string>:1.104-105: digest-bits must either be zero or a positive, multiple of eight",
+"syntax-error" : "<string>:1.104-105: TSIG key digest-bits must either be zero or a positive, multiple of eight",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, too small truncated HMAC-MD5",
-"logic-error" : true,
+"logic-error" : "tsig-key: digest-bits too small :  (<string>:1:104)",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, too small truncated HMAC-SHA1",
-"logic-error" : true,
+"logic-error" : "tsig-key: digest-bits too small :  (<string>:1:105)",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, too small truncated HMAC-SHA224",
-"logic-error" : true,
+"logic-error" : "tsig-key: digest-bits too small :  (<string>:1:107)",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, too small truncated HMAC-SHA256",
-"logic-error" : true,
+"logic-error" : "tsig-key: digest-bits too small :  (<string>:1:107)",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, too small truncated HMAC-SHA384",
-"logic-error" : true,
+"logic-error" : "tsig-key: digest-bits too small :  (<string>:1:107)",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, too small truncated HMAC-SHA512",
-"logic-error" : true,
+"logic-error" : "tsig-key: digest-bits too small :  (<string>:1:107)",
 "data" :
     {
     "forward-ddns" : {},
 #----- D2.tsig-keys, secret tests
 ,{
 "description" : "D2.tsig-keys, missing secret",
-"logic-error" : true,
+"logic-error" : "element: tsig-keys : String parameter secret not found(<string>:1:62)<string>:1:47",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, blank secret",
-"syntax-error" : "<string>:1.118: secret cannot be blank",
+"syntax-error" : "<string>:1.118: TSIG key secret cannot be blank",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.tsig-keys, invalid secret",
-"logic-error" : true,
+"logic-error" : "Cannot make TSIGKey: Incomplete input for base64: bogus (<string>:1:62)",
 "data" :
     {
     "forward-ddns" : {},
 #------
 ,{
 "description" : "D2.forward-ddns, duplicate domain",
-"logic-error" : true,
+"logic-error" : "Duplicate domain specified:four.example.com. (<string>:1:184)",
 "data" :
     {
     "forward-ddns" :
 #-----
 ,{
 "description" : "D2.forward-ddns.dhcp-ddns, blank name",
-"syntax-error" : "<string>:1.47: name cannot be blank",
+"syntax-error" : "<string>:1.47: Ddns domain name cannot be blank",
 "data" :
     {
     "forward-ddns" :
 #------ "D2.forward-ddns.dhcp-ddns, key-name tests
 ,{
 "description" : "D2.forward-ddns, no matching key name",
-"logic-error" : true,
+"logic-error" : "DdnsDomain : four.example.com. specifies an undefined key: no.such.key (<string>:1:104)",
 "data" :
     {
     "forward-ddns" :
 #----- D2.forward-ddns.dhcp-ddns.dns-servers tests
 ,{
 "description" : "D2.forward-ddns.dhcp-ddns.dns-servers, no servers",
-"logic-error" : true,
+"syntax-error" : "<string>:1.59: syntax error, unexpected ], expecting {",
 "data" :
     {
     "forward-ddns" :
 #-----
 ,{
 "description" : "D2.forward-ddns.dhcp-ddns.dns-servers.hostname unsupported",
-"logic-error" : true,
+"syntax-error" : "<string>:1.70: hostname is not yet supported",
 "data" :
     {
     "forward-ddns" :
 #-----
 ,{
 "description" : "D2.forward-ddns.dhcp-ddns.dns-servers.ip-address invalid address ",
-"logic-error" : true,
+"logic-error" : "Dns Server : invalid IP address : bogus (<string>:1:74)",
 "data" :
     {
     "forward-ddns" :
 #------
 ,{
 "description" : "D2.reverse-ddns, duplicate domain",
-"logic-error" : true,
+"logic-error" : "Duplicate domain specified:2.0.192.in-addra.arpa. (<string>:1:211)",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.reverse-ddns.dhcp-ddns, blank name",
-"syntax-error" : "<string>:1.69: name cannot be blank",
+"syntax-error" : "<string>:1.69: Ddns domain name cannot be blank",
 "data" :
     {
     "forward-ddns" : {},
 #------ "D2.reverse-ddns.dhcp-ddns, key-name tests
 ,{
 "description" : "D2.reverse-ddns, no matching key name",
-"logic-error" : true,
+"logic-error" : "DdnsDomain : 2.0.192.in-addr.arpa. specifies an undefined key: no.such.key (<string>:1:126)",
 "data" :
     {
     "forward-ddns" : {},
 #----- D2.reverse-ddns.dhcp-ddns.dns-servers tests
 ,{
 "description" : "D2.reverse-ddns.dhcp-ddns.dns-servers, no servers",
-"logic-error" : true,
+"syntax-error" : "<string>:1.81: syntax error, unexpected ], expecting {",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.hostname unsupported",
-"logic-error" : true,
+"syntax-error" : "<string>:1.92: hostname is not yet supported",
 "data" :
     {
     "forward-ddns" : {},
 #-----
 ,{
 "description" : "D2.reverse-ddns.dhcp-ddns.dns-servers.ip-address invalid value",
-"logic-error" : true,
+"logic-error" : "Dns Server : invalid IP address : bogus (<string>:1:96)",
 "data" :
     {
     "forward-ddns" : {},
index 0874540f93bf0e63c6d19c6c6c931b8e7c047895..2c7f83aa14f3f3fb94a6f57a7abf50de38aa354b 100644 (file)
@@ -15,30 +15,6 @@ using namespace boost::asio;
 namespace isc {
 namespace process {
 
-const char* valid_d2_config = "{ "
-                        "\"ip-address\" : \"127.0.0.1\" , "
-                        "\"port\" : 5031, "
-                        "\"tsig-keys\": ["
-                        "{ \"name\": \"d2_key.tmark.org\" , "
-                        "   \"algorithm\": \"HMAC-MD5\" ,"
-                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
-                        "} ],"
-                        "\"forward-ddns\" : {"
-                        "\"ddns-domains\": [ "
-                        "{ \"name\": \"tmark.org.\" , "
-                        "  \"key-name\": \"d2_key.tmark.org\" , "
-                        "  \"dns-servers\" : [ "
-                        "  { \"ip-address\": \"127.0.0.101\" } "
-                        "] } ] }, "
-                        "\"reverse-ddns\" : {"
-                        "\"ddns-domains\": [ "
-                        "{ \"name\": \" 0.168.192.in.addr.arpa.\" , "
-                        "  \"key-name\": \"d2_key.tmark.org\" , "
-                        "  \"dns-servers\" : [ "
-                        "  { \"ip-address\": \"127.0.0.101\" , "
-                        "    \"port\": 100 } ] } "
-                        "] } }";
-
 // Initialize the static failure flag.
 SimFailure::FailureType SimFailure::failure_type_ = SimFailure::ftNoFailure;
 
index b82169dd030906af50c325bf5d026cd0b7704b93..efb03e20b425fc9b4ae3c46eefffbd019c07414b 100644 (file)
@@ -30,11 +30,6 @@ using namespace boost::posix_time;
 namespace isc {
 namespace process {
 
-/// @brief Provides a valid DHCP-DDNS configuration for testing basic
-/// parsing fundamentals.
-extern const char* valid_d2_config;
-
-
 /// @brief Class is used to set a globally accessible value that indicates
 /// a specific type of failure to simulate.  Test derivations of base classes
 /// can exercise error handling code paths by testing for specific SimFailure
@@ -893,10 +888,6 @@ private:
     asiolink::IntervalTimerPtr timer_;
 };
 
-/// @brief Defines a small but valid DHCP-DDNS compliant configuration for
-/// testing configuration parsing fundamentals.
-extern const char* valid_d2_config;
-
 }; // namespace isc::process
 }; // namespace isc