Browse Source

[2957] Interrim check-in. This adds configuration management to
D2. It introduces DCfgMgrBase, abstract class for processing updates
to configuration, DCfgContext for storing configuration; and D2
specific initial derivations of each, D2CfgMgr and D2CfgContext.
These are skeletal derivations that will be expanded to handle
DHCP-DDNS specific configuration. New files added:

src/bin/d2/d_cfg_mgr.h
src/bin/d2/d_cfg_mgr.cc
src/bin/d2/d2_cfg_mgr.h
src/bin/d2/d2_cfg_mgr.cc
src/bin/d2/tests/d_cfg_mgr_unittests.cc

Thomas Markwalder 12 years ago
parent
commit
dac0b87d5b

+ 3 - 0
src/bin/d2/Makefile.am

@@ -52,6 +52,8 @@ b10_dhcp_ddns_SOURCES += d_process.h
 b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
 b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
 b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
 b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
 b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
 b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
+b10_dhcp_ddns_SOURCES += d_cfg_mgr.cc d_cfg_mgr.h
+b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
 
 
 nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
 nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
 EXTRA_DIST += d2_messages.mes
 EXTRA_DIST += d2_messages.mes
@@ -61,6 +63,7 @@ b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la 
 
 
 b10_dhcp_ddnsdir = $(pkgdatadir)
 b10_dhcp_ddnsdir = $(pkgdatadir)
 b10_dhcp_ddns_DATA = dhcp-ddns.spec
 b10_dhcp_ddns_DATA = dhcp-ddns.spec

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

@@ -0,0 +1,58 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace d2 {
+
+// *********************** D2CfgContext  *************************
+
+D2CfgContext::D2CfgContext() {
+    // @TODO - initialize D2 specific storage
+}
+
+D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs)
+    /* @TODO copy D2 specific storage  */ {
+}
+
+D2CfgContext::~D2CfgContext() {
+}
+
+// *********************** D2CfgMgr  *************************
+
+D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
+}
+
+D2CfgMgr::~D2CfgMgr() {
+}
+
+isc::dhcp::ParserPtr
+D2CfgMgr::createConfigParser(const std::string& element_id) {
+    // @TODO This is only enough implementation for integration.
+    // This will expand to support the top level D2 elements.
+    // For now we will simply return a debug parser for everything.
+    return (isc::dhcp::ParserPtr(new isc::dhcp::DebugParser(element_id)));
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+

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

@@ -0,0 +1,112 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <d2/d_cfg_mgr.h>
+
+#include <stdint.h>
+#include <string>
+
+#ifndef D2_CFG_MGR_H
+#define D2_CFG_MGR_H
+
+namespace isc {
+namespace d2 {
+
+/// @brief  DHCP-DDNS Configuration Context
+/// Implements the storage container for configuration context.
+/// It provides a single enclosure for the storage of configuration parameters
+/// and any other context specific information that needs to be accessible
+/// during configuration parsing as well as to the application as a whole.
+/// @TODO - add in storage of D2 specific storage like domain-to-dns_server
+/// mapping.  This is the initial implementation necessary to integrate
+/// configuration management into D2.
+class D2CfgContext : public DCfgContextBase {
+public:
+    /// @brief Constructor
+    D2CfgContext();
+
+    /// @brief Destructor
+    virtual ~D2CfgContext();
+
+    /// @brief Creates a clone of this context object.
+    /// @return returns a raw pointer to the new clone.
+    virtual D2CfgContext* clone() {
+            return (new D2CfgContext(*this));
+    }
+
+protected:
+    /// @brief Copy constructor for use by derivations in clone().
+    D2CfgContext(const D2CfgContext& rhs);
+
+private:
+    /// @brief Private assignment operator to avoid potential for slicing.
+    D2CfgContext& operator=(const D2CfgContext& rhs);
+
+    /// @TODO storage for DNS domain-server mapping will be added here
+};
+
+/// @brief Pointer to a configuration context.
+typedef boost::shared_ptr<D2CfgContext> D2CfgContextPtr;
+
+/// @brief DHCP-DDNS Configuration Manager
+///
+/// Provides the mechanisms for managing the DHCP-DDNS application's
+/// configuration.  This includes services for parsing sets of configuration
+/// values, storing the parsed information in its converted form,
+/// and retrieving the information on demand.
+/// @TODO add in D2 specific parsing
+class D2CfgMgr : public DCfgMgrBase {
+public:
+    /// @brief Constructor
+    ///
+    /// @param context is a pointer to the configuration context the manager
+    D2CfgMgr();
+
+    /// @brief Destructor
+    virtual ~D2CfgMgr();
+
+    /// @brief Convenience method that returns the D2 configuration context.
+    ///
+    /// @return returns a pointer to the configuration context.
+    D2CfgContextPtr getD2CfgContext() {
+        return (boost::dynamic_pointer_cast<D2CfgContext>(getContext()));
+    }
+
+protected:
+    /// @brief Given an element_id returns an instance of the appropriate
+    /// parser.
+    /// @TODO The initial implementation simply returns a DebugParser for any
+    /// element_id value.  This is sufficient to integrate configuration
+    /// management into D2. Specific parsers will be added as the DHCP-DDNS
+    /// specific configuration is constructed.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    ///
+    /// @return returns a ParserPtr to the parser instance.
+    /// @throw throws DCfgMgrBaseError if an error occurs.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& element_id);
+};
+
+/// @brief Defines a shared pointer to D2CfgMgr.
+typedef boost::shared_ptr<D2CfgMgr> D2CfgMgrPtr;
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif // D2_CFG_MGR_H

+ 23 - 0
src/bin/d2/d2_messages.mes

@@ -26,11 +26,22 @@ to establish a session with the BIND10 control channel.
 A debug message listing the command (and possible arguments) received
 A debug message listing the command (and possible arguments) received
 from the BIND10 control system by the controller.
 from the BIND10 control system by the controller.
 
 
+% DCTL_CONFIG_COMPLETE server has completed configuration: %1
+This is an informational message announcing the successful processing of a
+new configuration. It is output during server startup, and when an updated
+configuration is committed by the administrator.  Additional information
+may be provided.
+
 % DCTL_CONFIG_LOAD_FAIL %1 configuration failed to load: %2
 % DCTL_CONFIG_LOAD_FAIL %1 configuration failed to load: %2
 This critical error message indicates that the initial application
 This critical error message indicates that the initial application
 configuration has failed. The service will start, but will not
 configuration has failed. The service will start, but will not
 process requests until the configuration has been corrected.
 process requests until the configuration has been corrected.
 
 
+% DCTL_CONFIG_START parsing new configuration: %1
+A debug message indicating that the application process has received an
+updated configuration and has passed it to its configuration manager
+for parsing.
+
 % DCTL_CONFIG_STUB %1 configuration stub handler called
 % DCTL_CONFIG_STUB %1 configuration stub handler called
 This debug message is issued when the dummy handler for configuration
 This debug message is issued when the dummy handler for configuration
 events is called.  This only happens during initial startup.
 events is called.  This only happens during initial startup.
@@ -57,6 +68,18 @@ application and will exit.
 A warning message is issued when an attempt is made to shut down the
 A warning message is issued when an attempt is made to shut down the
 the application when it is not running.
 the application when it is not running.
 
 
+% DCTL_ORDER_ERROR Configuration contains more elements than the parsing order
+A debug message which indicates that configuration being parsed includes
+element ids not specified the configuration manager's parse order list. This is
+programming logic error.
+
+% DCTL_PARSER_FAIL configuration parsing failed for configuration element: %1
+On receipt of message containing details to a change of its configuration,
+the server failed to create a parser to decode the contents of the named
+configuration element, or the creation succeeded but the parsing actions
+and committal of changes failed.  The reason for the failure is given in
+the message.
+
 % DCTL_PROCESS_FAILED %1 application execution failed: %2
 % DCTL_PROCESS_FAILED %1 application execution failed: %2
 The controller has encountered a fatal error while running the
 The controller has encountered a fatal error while running the
 application and is terminating. The reason for the failure is
 application and is terminating. The reason for the failure is

+ 7 - 6
src/bin/d2/d2_process.cc

@@ -14,6 +14,7 @@
 
 
 #include <config/ccsession.h>
 #include <config/ccsession.h>
 #include <d2/d2_log.h>
 #include <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
 #include <d2/d2_process.h>
 #include <d2/d2_process.h>
 
 
 using namespace asio;
 using namespace asio;
@@ -22,7 +23,7 @@ namespace isc {
 namespace d2 {
 namespace d2 {
 
 
 D2Process::D2Process(const char* name, IOServicePtr io_service)
 D2Process::D2Process(const char* name, IOServicePtr io_service)
-    : DProcessBase(name, io_service) {
+    : DProcessBase(name, io_service, DCfgMgrBasePtr(new D2CfgMgr())) {
 };
 };
 
 
 void
 void
@@ -60,14 +61,14 @@ D2Process::shutdown() {
 
 
 isc::data::ConstElementPtr
 isc::data::ConstElementPtr
 D2Process::configure(isc::data::ConstElementPtr config_set) {
 D2Process::configure(isc::data::ConstElementPtr config_set) {
-    // @TODO This is the initial implementation which simply accepts
-    // any content in config_set as valid.  This is sufficient to
-    // allow participation as a BIND10 module, while D2 configuration support
-    // is being developed.
+    // @TODO This is the initial implementation passes the configuration onto
+    // the D2CfgMgr.  There may be additional steps taken added to handle
+    // configuration changes but for now, assume that D2CfgMgr is handling it
+    // all. 
     LOG_DEBUG(dctl_logger, DBGLVL_TRACE_BASIC,
     LOG_DEBUG(dctl_logger, DBGLVL_TRACE_BASIC,
               DHCP_DDNS_CONFIGURE).arg(config_set->str());
               DHCP_DDNS_CONFIGURE).arg(config_set->str());
 
 
-    return (isc::config::createAnswer(0, "Configuration accepted."));
+    return (getCfgMgr()->parseConfig(config_set));
 }
 }
 
 
 isc::data::ConstElementPtr
 isc::data::ConstElementPtr

+ 187 - 0
src/bin/d2/d_cfg_mgr.cc

@@ -0,0 +1,187 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config/ccsession.h>
+#include <d2/d2_log.h>
+#include <dhcp/libdhcp++.h>
+#include <d2/d_cfg_mgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <util/encode/hex.h>
+#include <util/strutil.h>
+
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <limits>
+#include <iostream>
+#include <vector>
+#include <map>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace d2 {
+
+// *********************** DCfgContextBase  *************************
+DCfgContextBase::DCfgContextBase():
+        boolean_values_(new BooleanStorage()),
+        uint32_values_(new Uint32Storage()),
+        string_values_(new StringStorage()) {
+    }
+
+DCfgContextBase::DCfgContextBase(const DCfgContextBase& rhs):
+        boolean_values_(new BooleanStorage(*(rhs.boolean_values_))),
+        uint32_values_(new Uint32Storage(*(rhs.uint32_values_))),
+        string_values_(new StringStorage(*(rhs.string_values_))) {
+}
+
+DCfgContextBase::~DCfgContextBase() {
+}
+
+// *********************** DCfgMgrBase  *************************
+
+DCfgMgrBase::DCfgMgrBase(DCfgContextBasePtr context)
+    : parse_order_(), context_(context) {
+    if (!context_) {
+        isc_throw(DCfgMgrBaseError, "DCfgMgrBase ctor: context cannot be NULL");
+    }
+}
+
+DCfgMgrBase::~DCfgMgrBase() {
+}
+
+isc::data::ConstElementPtr
+DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
+    LOG_DEBUG(dctl_logger, DBGLVL_COMMAND,
+                DCTL_CONFIG_START).arg(config_set->str());
+
+    if (!config_set) {
+        return (isc::config::createAnswer(1,
+                                    std::string("Can't parse NULL config")));
+    }
+
+    // The parsers implement data inheritance by directly accessing
+    // configuration context. For this reason the data parsers must store
+    // the parsed data into context immediately. This may cause data
+    // inconsistency if the parsing operation fails after the context has been
+    // modified. We need to preserve the original context here
+    // so as we can rollback changes when an error occurs.
+    DCfgContextBasePtr original_context(context_->clone());
+
+    // Answer will hold the result returned to the caller.
+    ConstElementPtr answer;
+
+    // Holds the name of the element being parsed.
+    std::string element_id;
+
+    try {
+        // Grab a map of element_ids and their data values from the new
+        // configuration set.
+        const std::map<std::string, ConstElementPtr>& values_map =
+                                                        config_set->mapValue();
+
+        // Use a pre-ordered list of element ids to parse the elements in a
+        // specific order if the list (parser_order_) is not empty; otherwise
+        // elements are parsed in the order the value_map presents them.
+
+        if (parse_order_.size() > 0) {
+            // NOTE: When using ordered parsing, the parse order list MUST
+            // include every possible element id that the value_map may contain.
+            // Entries in the map that are not in the parse order, would not be
+            // parsed. For now we will flag this as a programmatic error.  One
+            // could attempt to adjust for this, by identifying such entries
+            // and parsing them either first or last but which would be correct?
+            // Better to make hold the engineer accountable.
+            if (values_map.size() > parse_order_.size()) {
+                LOG_ERROR(dctl_logger, DCTL_ORDER_ERROR);
+                return (isc::config::createAnswer(1,
+                        "Configuration contains elements not in parse order"));
+            }
+
+            // For each element_id in the parse order list, look for it in the
+            // value map.  If the element exists in the map, pass it and it's
+            // associated data in for parsing.  If there is no matching entry
+            // in the value map, then assume the element is optional and move
+            // on to next element_id.
+            std::map<std::string, ConstElementPtr>::const_iterator it;
+            BOOST_FOREACH(element_id, parse_order_) {
+                it = values_map.find(element_id);
+                if (it != values_map.end()) {
+                    buildAndCommit(element_id, it->second);
+                }
+            }
+        } else {
+            // Order doesn't matter so iterate over the value map directly.
+            // Pass each element and it's associated data in to be parsed.
+            ConfigPair config_pair;
+            BOOST_FOREACH(config_pair, values_map) {
+                element_id = config_pair.first;
+                buildAndCommit(element_id, config_pair.second);
+            }
+        }
+
+        // Everything was fine. Configuration set processed successfully.
+        LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg("");
+        answer = isc::config::createAnswer(0, "Configuration committed.");
+
+    } catch (const DCfgMgrBaseError& ex) {
+        LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(element_id).arg(ex.what());
+        answer = isc::config::createAnswer(1,
+                     string("Configuration parsing failed:") + ex.what() +
+                     " for element: " + element_id);
+
+        // An error occurred, so make sure that we restore original context.
+        context_ = original_context;
+    }
+
+    return (answer);
+}
+
+void DCfgMgrBase::buildAndCommit(std::string& element_id,
+                                 isc::data::ConstElementPtr value) {
+    // Call derivation's implementation to create the appropriate parser
+    // based on the element id.
+    // ParserPtr parser(createConfigParser(element_id));
+    ParserPtr parser = createConfigParser(element_id);
+    if (!parser) {
+        isc_throw(DCfgMgrBaseError, std::string("Could not create parser"));
+    }
+
+    try {
+        // Invoke the parser's build method passing in the value. This will
+        // "convert" the Element form of value into the actual data item(s)
+        // and store them in parser's local storage.
+        parser->build(value);
+
+        // Invoke the parser's commit method. This "writes" the the data
+        // item(s) stored locally by the parser into the context.  (Note that
+        // parsers are free to do more than update the context, but that is an
+        // nothing something we are concerned with here.)
+        parser->commit();
+    } catch (const isc::Exception& ex) {
+        isc_throw(DCfgMgrBaseError, std::string("Could not build and commit")
+                                    + ex.what());
+    } catch (...) {
+        isc_throw(DCfgMgrBaseError, "Non-ISC exception occurred");
+    }
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+

+ 314 - 0
src/bin/d2/d_cfg_mgr.h

@@ -0,0 +1,314 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/dhcp_parsers.h>
+
+#include <stdint.h>
+#include <string>
+
+#ifndef D_CFG_MGR_H
+#define D_CFG_MGR_H
+
+namespace isc {
+namespace d2 {
+
+/// @brief Exception thrown if the configuration manager encounters an error.
+class DCfgMgrBaseError : public isc::Exception {
+public:
+    DCfgMgrBaseError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Abstract class that implements a container for configuration context.
+/// It provides a single enclosure for the storage of configuration parameters
+/// and any other context specific information that needs to be accessible
+/// during configuration parsing as well as to the application as a whole.
+/// The base class supports storage for a small set of simple data types.
+/// Derivations simply add additional storage as needed.  Note that this class
+/// declares the pure virtual clone() method, its copy constructor is protected,
+/// and its copy operator is inaccessible.  Derivations must supply an
+/// implementation of clone that calls the base class copy constructor as
+/// well as performing whatever steps are necessary to copy its additional
+/// storage.  This allows the management class to perform context backup and
+/// restoration without derivation specific knowledge using logic like
+/// the following:
+///
+///    // Make a backup copy
+///    DCfgContextBasePtr backup_copy(context_->clone());
+///    :
+///    // Restore from backup
+///    context_ = backup_copy;
+///
+class DCfgContextBase {
+public:
+    /// @brief Constructor
+    DCfgContextBase();
+
+    /// @brief Destructor
+    virtual ~DCfgContextBase();
+
+    /// @brief Fetches the value for a given boolean configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter.
+    void getParam(const std::string& name, bool& value) {
+        value = boolean_values_->getParam(name);
+    }
+
+    /// @brief Fetches the value for a given uint32_t configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter.
+    void getParam(const std::string& name, uint32_t& value) {
+        value = uint32_values_->getParam(name);
+    }
+
+    /// @brief Fetches the value for a given string configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter.
+    void getParam(const std::string& name, std::string& value) {
+        value = string_values_->getParam(name);
+    }
+
+    /// @brief Fetches the Boolean Storage. Typically used for passing
+    /// into parsers.
+    ///
+    /// @return returns a pointer to the Boolean Storage.
+    isc::dhcp::BooleanStoragePtr getBooleanStorage() {
+        return (boolean_values_);
+    }
+
+    /// @brief Fetches the uint32 Storage. Typically used for passing
+    /// into parsers.
+    ///
+    /// @return returns a pointer to the uint32 Storage.
+    isc::dhcp::Uint32StoragePtr getUint32Storage() {
+        return (uint32_values_);
+    }
+
+    /// @brief Fetches the string Storage. Typically used for passing
+    /// into parsers.
+    ///
+    /// @return returns a pointer to the string Storage.
+    isc::dhcp::StringStoragePtr getStringStorage() {
+        return (string_values_);
+    }
+
+    /// @brief Creates a clone of this context object.  As mentioned in the
+    /// the class brief, derivation must supply an implementation that
+    /// initializes the base class storage as well as its own.  Typically
+    /// the derivation's clone method would return the result of passing
+    /// "*this" into its own copy constructor:
+    ///
+    /// -----------------------------------------------------------------
+    /// class DStubContext : public DCfgContextBase {
+    /// public:
+    ///  :
+    ///     // Clone calls its own copy constructor
+    ///     virtual DStubContext* clone() {
+    ///         return (new DStubContext(*this));
+    ///     }
+    ///
+    ///     // Note that the copy constructor calls the base class copy ctor
+    ///     // then initializes its additional storage.
+    ///     DStubContext(const DStubContext& rhs) : DCfgContextBase(rhs),
+    ///         extra_values_(new Uint32Storage(*(rhs.extra_values_))) {
+    ///     }
+    ///  :
+    ///    // Here's the derivation's additional storage.
+    ///    isc::dhcp::Uint32StoragePtr extra_values_;
+    ///  :
+    /// -----------------------------------------------------------------
+    ///
+    /// @return returns a raw pointer to the new clone.
+    virtual DCfgContextBase* clone() = 0;
+
+protected:
+    /// @brief Copy constructor for use by derivations in clone().
+    DCfgContextBase(const DCfgContextBase& rhs);
+
+private:
+    /// @brief Private assignment operator to avoid potential for slicing.
+    DCfgContextBase& operator=(const DCfgContextBase& rhs);
+
+    /// @brief Storage for boolean parameters.
+    isc::dhcp::BooleanStoragePtr boolean_values_;
+
+    /// @brief Storage for uint32 parameters.
+    isc::dhcp::Uint32StoragePtr uint32_values_;
+
+    /// @brief Storage for string parameters.
+    isc::dhcp::StringStoragePtr string_values_;
+};
+
+/// @brief Pointer to a configuration context.
+typedef boost::shared_ptr<DCfgContextBase> DCfgContextBasePtr;
+
+/// @brief Defines an unsorted, list of string Element IDs.
+typedef std::vector<std::string> ElementIdList;
+
+/// @brief Configuration Manager
+///
+/// DCfgMgrBase is an abstract class that provides the mechanisms for managing
+/// an application's configuration.  This includes services for parsing sets of
+/// configuration values, storing the parsed information in its converted form,
+/// and retrieving the information on demand.  It is intended to be the worker
+/// class which is handed a set of configuration values to process by upper
+/// application management layers. Typically this call chain would look like
+/// this:
+/// External configuration event:
+///  --> Controller::configHandler(new configuration)
+///      --> Controller.updateConfig(new configuration)
+///          --> Controller.Process.configure(new configuration)
+///              --> Process.CfgMgr.parseConfig(new configuration)
+///
+/// The class presents a public method for receiving new configurations,
+/// parseConfig.  This method coordinates the parsing effort as follows:
+///
+///    make backup copy of configuration context
+///    for each top level element in new configuration
+///        get derivation-specific parser for element
+///        run parser
+///        update context with parsed results
+///        break on error
+///
+///    if an error occurred
+///        restore configuration context from backup
+///
+/// After making a backup of the current context, it iterates over the top-level
+/// elements in the new configuration.  The order in which the elements are
+/// processed is either:
+///
+///    1. Natural order presented by the configuration set
+///    2. Specific order determined by a list of element ids
+///
+/// This allows a derivation to specify the order in which its elements are
+/// parsed if there are dependencies between elements.
+/// To parse a given element, its id is passed into createConfigParser,
+/// which returns an instance of the appropriate parser.  This method is
+/// abstract so the derivation's implementation determines the type of parser
+/// created. This isolates the knowledge of specific element ids and which
+/// application specific parsers to derivation.
+/// Once the parser has been created, it is used to parse the data value
+/// associated with the element id and update the context with the parsed
+/// results.
+/// In the event that an error occurs, parsing is halted and the configuration
+/// context is restored from backup.
+class DCfgMgrBase {
+public:
+    /// @brief Constructor
+    ///
+    /// @param context is a pointer to the configuration context the manager
+    /// will use for storing parsed results.
+    ///
+    /// @throw throws DCfgMgrBaseError if context is null
+    DCfgMgrBase(DCfgContextBasePtr context);
+
+    /// @brief Destructor
+    virtual ~DCfgMgrBase();
+
+    /// @brief Acts as the receiver of new configurations and coordinates
+    /// the parsing as described in the class brief.
+    ///
+    /// @param config_set is a set of configuration elements to parsed.
+    ///
+    /// @return an Element that contains the results of configuration composed
+    /// of an integer status value (0 means successful, non-zero means failure),
+    /// and a string explanation of the outcome.
+    isc::data::ConstElementPtr parseConfig(isc::data::ConstElementPtr
+                                           config_set);
+
+    /// @brief Adds a given element id to the end of the parse order list.
+    /// The order in which elements are retrieved from this is FIFO.  Elements
+    /// should be added in the order in which they are to be parsed.
+    //
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    void addToParseOrder(std::string& element_id){
+        parse_order_.push_back(element_id);
+    }
+
+    /// @brief Fetches the parse order list.
+    ///
+    /// @return returns a const reference to the list.
+    const ElementIdList& getParseOrder() const {
+        return (parse_order_);
+    }
+
+    /// @brief Fetches the configuration context.
+    ///
+    /// @return returns a pointer reference to the configuration context.
+    DCfgContextBasePtr& getContext() {
+        return (context_);
+    }
+
+protected:
+    /// @brief Given an element_id returns an instance of the appropriate
+    /// parser.  This method is abstract, isolating any direct knowledge of
+    /// element_ids and parsers to within the application-specific derivation.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    ///
+    /// @return returns a ParserPtr to the parser instance.
+    /// @throw throws DCfgMgrBaseError if an error occurs.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& element_id) = 0;
+
+private:
+
+    /// @brief Given an element_id and data value, instantiate the appropriate
+    /// parser,  parse the data value, and commit the results.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    /// @param value is the data value to be parsed and associated with
+    /// element_id.
+    ///
+    /// @throw throws DCfgMgrBaseError if an error occurs.
+    void buildAndCommit(std::string& element_id,
+                        isc::data::ConstElementPtr value);
+
+    /// @brief An FIFO list of element ids, used to dictate the element
+    /// parsing order.  If the list is empty, the natural order in the
+    /// configuration set it used.
+    ElementIdList parse_order_;
+
+    /// @brief Pointer to the configuration context instance.
+    DCfgContextBasePtr context_;
+};
+
+/// @brief Defines a shared pointer to DCfgMgrBase.
+typedef boost::shared_ptr<DCfgMgrBase> DCfgMgrBasePtr;
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif // D_CFG_MGR_H

+ 20 - 2
src/bin/d2/d_process.h

@@ -17,6 +17,8 @@
 
 
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 #include <cc/data.h>
 #include <cc/data.h>
+#include <d2/d_cfg_mgr.h>
+
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
@@ -60,11 +62,17 @@ public:
     /// asynchronous event handling.
     /// asynchronous event handling.
     ///
     ///
     /// @throw DProcessBaseError is io_service is NULL.
     /// @throw DProcessBaseError is io_service is NULL.
-    DProcessBase(const char* app_name, IOServicePtr io_service)
-        : app_name_(app_name), io_service_(io_service), shut_down_flag_(false) {
+    DProcessBase(const char* app_name, IOServicePtr io_service, 
+                 DCfgMgrBasePtr cfg_mgr)
+        : app_name_(app_name), io_service_(io_service), shut_down_flag_(false),
+        cfg_mgr_(cfg_mgr) {
         if (!io_service_) {
         if (!io_service_) {
             isc_throw (DProcessBaseError, "IO Service cannot be null");
             isc_throw (DProcessBaseError, "IO Service cannot be null");
         }
         }
+
+        if (!cfg_mgr_) {
+            isc_throw (DProcessBaseError, "CfgMgr cannot be null");
+        }
     };
     };
 
 
     /// @brief May be used after instantiation to perform initialization unique
     /// @brief May be used after instantiation to perform initialization unique
@@ -159,6 +167,13 @@ public:
         io_service_->stop();
         io_service_->stop();
     }
     }
 
 
+    /// @brief Fetches the process's configuration manager.
+    ///
+    /// @return returns a reference to the configuration manager.
+    DCfgMgrBasePtr& getCfgMgr() {
+        return (cfg_mgr_);
+    }
+
 private:
 private:
     /// @brief Text label for the process. Generally used in log statements,
     /// @brief Text label for the process. Generally used in log statements,
     /// but otherwise can be arbitrary.
     /// but otherwise can be arbitrary.
@@ -169,6 +184,9 @@ private:
 
 
     /// @brief Boolean flag set when shutdown has been requested.
     /// @brief Boolean flag set when shutdown has been requested.
     bool shut_down_flag_;
     bool shut_down_flag_;
+
+    /// @brief  Pointer to the configuration manager.
+    DCfgMgrBasePtr cfg_mgr_;
 };
 };
 
 
 /// @brief Defines a shared pointer to DProcessBase.
 /// @brief Defines a shared pointer to DProcessBase.

+ 4 - 0
src/bin/d2/tests/Makefile.am

@@ -56,11 +56,14 @@ d2_unittests_SOURCES += ../d_process.h
 d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
 d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
 d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
+d2_unittests_SOURCES += ../d_cfg_mgr.cc ../d_cfg_mgr.h
+d2_unittests_SOURCES += ../d2_cfg_mgr.cc ../d2_cfg_mgr.h
 d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
 d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
 d2_unittests_SOURCES += d2_unittests.cc
 d2_unittests_SOURCES += d2_unittests.cc
 d2_unittests_SOURCES += d2_process_unittests.cc
 d2_unittests_SOURCES += d2_process_unittests.cc
 d2_unittests_SOURCES += d_controller_unittests.cc
 d2_unittests_SOURCES += d_controller_unittests.cc
 d2_unittests_SOURCES += d2_controller_unittests.cc
 d2_unittests_SOURCES += d2_controller_unittests.cc
+d2_unittests_SOURCES += d_cfg_mgr_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -71,6 +74,7 @@ d2_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 endif
 endif
 
 
 noinst_PROGRAMS = $(TESTS)
 noinst_PROGRAMS = $(TESTS)

+ 414 - 0
src/bin/d2/tests/d_cfg_mgr_unittests.cc

@@ -0,0 +1,414 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config/ccsession.h>
+#include <config/module_spec.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <d2/d_cfg_mgr.h>
+#include <d_test_stubs.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+#include <config.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::config;
+using namespace isc::d2;
+using namespace boost::posix_time;
+
+namespace {
+
+/// @brief Test Class for verifying that configuration context cannot be null
+/// during construction.
+class DCtorTestCfgMgr : public DCfgMgrBase {
+public:
+    /// @brief Constructor - Note that is passes in an empty configuration
+    /// pointer to the base class constructor.
+    DCtorTestCfgMgr() : DCfgMgrBase(DCfgContextBasePtr()) {
+    }
+
+    /// @brief Destructor
+    virtual ~DCtorTestCfgMgr() {
+    }
+
+    /// @brief Dummy implementation as this method is abstract.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& /* element_id */) {
+        return (isc::dhcp::ParserPtr());
+    }
+};
+
+/// @brief Test fixture class for testing DCfgMgrBase class.
+/// It maintains an member instance of DStubCfgMgr and provides methods for
+/// converting JSON strings to configuration element sets, checking parse
+/// results, and accessing the configuration context.
+class DStubCfgMgrTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    DStubCfgMgrTest():cfg_mgr_(new DStubCfgMgr) {
+    }
+
+    /// @brief Destructor
+    ~DStubCfgMgrTest() {
+    }
+
+    /// @brief Converts a given JSON string into an Element set and stores the
+    /// result the member variable, config_set_.
+    ///
+    /// @param json_text contains the configuration text in JSON format to
+    /// convert.
+    /// @return returns true if the conversion is successful, false otherwise.
+    bool fromJSON(std::string& json_text) {
+        try  {
+            config_set_ = isc::data::Element::fromJSON(json_text);
+        } catch (...) {
+            // This is so we can diagnose parsing mistakes during test
+            // development.
+            std::cerr << "fromJSON failed to parse text" << json_text
+                      << std::endl;
+            return (false);
+        }
+
+        return (true);
+    }
+
+    /// @brief Compares the status in the  parse result stored in member
+    /// variable answer_ to a given value.
+    ///
+    /// @param should_be is an integer against which to compare the status.
+    ///
+    /// @return returns true if the status value is equal to the given value.
+    bool checkAnswer(int should_be) {
+        int rcode = 0;
+        isc::data::ConstElementPtr comment;
+        comment = isc::config::parseAnswer(rcode, answer_);
+        //std::cout << "checkAnswer rcode:" << rcode << " comment: "
+        //          << *comment_ << std::endl;
+        return (rcode == should_be);
+    }
+
+    /// @brief Convenience method which returns a DStubContextPtr to the
+    /// configuration context.
+    ///
+    /// @return returns a DStubContextPtr.
+    DStubContextPtr getStubContext() {
+        return (boost::dynamic_pointer_cast<DStubContext>
+                (cfg_mgr_->getContext()));
+    }
+
+    /// @brief Configuration manager instance.
+    DStubCfgMgrPtr cfg_mgr_;
+
+    /// @brief Configuration set being tested.
+    isc::data::ElementPtr config_set_;
+
+    /// @brief Results of most recent elemnt parsing.
+    isc::data::ConstElementPtr answer_;
+};
+
+///@brief Tests basic construction/destruction of configuration manager.
+/// Verifies that:
+/// 1. Proper construction succeeds.
+/// 2. Configuration context is initialized by construction.
+/// 3. Destruction works properly.
+/// 4. Construction with a null context is not allowed.
+TEST(DCfgMgrBase, construction) {
+    DCfgMgrBase *cfg_mgr = NULL;
+
+    // Verify that configuration manager constructions without error.
+    ASSERT_NO_THROW(cfg_mgr=new DStubCfgMgr());
+
+    // Verify that the context can be retrieved and is not null.
+    DCfgContextBasePtr context = cfg_mgr->getContext();
+    EXPECT_TRUE(context);
+
+    // Verify that the manager can be destructed without error.
+    EXPECT_NO_THROW(delete cfg_mgr);
+
+    // Verify that an attempt to construct a manger with a null context fails.
+    ASSERT_THROW(DCtorTestCfgMgr(), DCfgMgrBaseError);
+}
+
+///@brief Tests fundamental aspects of configuration parsing.
+/// Verifies that:
+/// 1. A correctly formed simple configuration parses without error.
+/// 2. An error building the element is handled.
+/// 3. An error committing the element is handled.
+/// 4. An unknown element error is handled.
+TEST_F(DStubCfgMgrTest, basicParseTest) {
+    // Create a simple configuration.
+    string config = "{ \"test-value\": 1000 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that we can parse a simple configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that an error building the element is caught and returns a
+    // failed parse result.
+    SimFailure::set(SimFailure::ftElementBuild);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Verify that an error committing the element is caught and returns a
+    // failed parse result.
+    SimFailure::set(SimFailure::ftElementCommit);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Verify that an unknown element error is caught and returns a failed
+    // parse result.
+    SimFailure::set(SimFailure::ftElementUnknown);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+}
+
+///@brief Tests ordered and non-ordered element parsing
+/// This test verifies that:
+/// 1. Non-ordered parsing parses elements in the order they are presented
+/// by the configuration set (as-they-come).
+/// 2. A parse order list with too few elements is detected.
+/// 3. Ordered parsing parses the elements in the order specified by the
+/// configuration manager's parse order list.
+TEST_F(DStubCfgMgrTest, parseOrderTest) {
+    // Element ids used for test.
+    std::string charlie("charlie");
+    std::string bravo("bravo");
+    std::string alpha("alpha");
+
+    // Create the test configuration with the elements in "random" order.
+
+    // NOTE that element sets produced by  isc::data::Element::fromJSON(),
+    // are in lexical order by element_id. This means that iterating over
+    // such an element set, will present the elements in lexical order. Should
+    // this change, this test will need to be modified accordingly.
+    string config = "{ \"bravo\": 2,  "
+                     " \"alpha\": 1,  "
+                     " \"charlie\": 3 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that non-ordered parsing, results in an as-they-come parse order.
+    // Create an expected parse order.
+    // (NOTE that iterating over Element sets produced by fromJSON() will
+    // present the elements in lexical order.  Should this change, the expected
+    // order list below would need to be changed accordingly).
+    ElementIdList order_expected;
+    order_expected.push_back(alpha);
+    order_expected.push_back(bravo);
+    order_expected.push_back(charlie);
+
+    // Verify that the manager has an EMPTY parse order list. (Empty list
+    // instructs the manager to parse them as-they-come.)
+    EXPECT_EQ(0, cfg_mgr_->getParseOrder().size());
+
+    // Parse the configuration, verify it parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that the parsed order matches what we expected.
+    EXPECT_TRUE(cfg_mgr_->parsed_order_ ==  order_expected);
+
+    // Clear the manager's parse order "memory".
+    cfg_mgr_->parsed_order_.clear();
+
+    // Create a parse order list that has too few entries.  Verify that
+    // when parsing the test config, it fails.
+    cfg_mgr_->addToParseOrder(charlie);
+    // Verify the parse order list is the size we expect.
+    EXPECT_EQ(1, cfg_mgr_->getParseOrder().size());
+
+    // Verify the configuration fails.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Verify that the configuration parses correctly, when the parse order
+    // is correct.  Add the needed entries to the parse order
+    cfg_mgr_->addToParseOrder(bravo);
+    cfg_mgr_->addToParseOrder(alpha);
+
+    // Verify the parse order list is the size we expect.
+    EXPECT_EQ(3, cfg_mgr_->getParseOrder().size());
+
+    // Clear the manager's parse order "memory".
+    cfg_mgr_->parsed_order_.clear();
+
+    // Verify the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that the parsed order is the order we configured.
+    EXPECT_TRUE(cfg_mgr_->getParseOrder() == cfg_mgr_->parsed_order_);
+}
+
+/// @brief Tests that element ids supported by the base class as well as those
+/// added by the derived class function properly.
+/// This test verifies that:
+/// 1. Boolean parameters can be parsed and retrieved.
+/// 2. Uint32 parameters can be parsed and retrieved.
+/// 3. String parameters can be parsed and retrieved.
+/// 4. Derivation-specific parameters can be parsed and retrieved.
+/// 5. Parsing a second configuration, updates the existing context values
+/// correctly.
+TEST_F(DStubCfgMgrTest, simpleTypesTest) {
+    // Fetch a derivation specific pointer to the context.
+    DStubContextPtr context = getStubContext();
+    ASSERT_TRUE(context);
+
+    // Create a configuration with all of the parameters.
+    string config = "{ \"bool_test\": true , "
+                    "  \"uint32_test\": 77 , "
+                    "  \"string_test\": \"hmmm chewy\" , "
+                    "  \"extra_test\": 430 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the boolean parameter was parsed correctly by retrieving
+    // its value from the context.
+    bool actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    // Verify that the uint32 parameter was parsed correctly by retrieving
+    // its value from the context.
+    uint32_t actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    // Verify that the string parameter was parsed correctly by retrieving
+    // its value from the context.
+    std::string actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    // Verify that the "extra" parameter was parsed correctly by retrieving
+    // its value from the context.
+    uint32_t actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(430, actual_extra);
+
+    // Create a configuration which "updates" all of the parameter values.
+    string config2 = "{ \"bool_test\": false , "
+                    "  \"uint32_test\": 88 , "
+                    "  \"string_test\": \"ewww yuk!\" , "
+                    "  \"extra_test\": 11 } ";
+    ASSERT_TRUE(fromJSON(config2));
+
+    // Verify that the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that the boolean parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_bool = true;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(false, actual_bool);
+
+    // Verify that the uint32 parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(88, actual_uint32);
+
+    // Verify that the string parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("ewww yuk!", actual_string);
+
+    // Verify that the "extra" parameter was updated correctly by retrieving
+    // its value from the context.
+    actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(11, actual_extra);
+}
+
+/// @brief Tests that the configuration context is preserved after failure
+/// during parsing causes a rollback.
+/// 1. Verifies configuration context rollback.
+TEST_F(DStubCfgMgrTest, rollBackTest) {
+    // Fetch a derivation specific pointer to the context.
+    DStubContextPtr context = getStubContext();
+    ASSERT_TRUE(context);
+
+    // Create a configuration with all of the parameters.
+    string config = "{ \"bool_test\": true , "
+                    "  \"uint32_test\": 77 , "
+                    "  \"string_test\": \"hmmm chewy\" , "
+                    "  \"extra_test\": 430 } ";
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that the configuration parses without error.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that all of parameters have the expected values.
+    bool actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    uint32_t actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    std::string actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    uint32_t actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(430, actual_extra);
+
+    // Create a configuration which "updates" all of the parameter values
+    // plus one unknown at the end.
+    string config2 = "{ \"bool_test\": false , "
+                    "  \"uint32_test\": 88 , "
+                    "  \"string_test\": \"ewww yuk!\" , "
+                    "  \"extra_test\": 11 , "
+                    "  \"zeta_unknown\": 33 } ";
+    ASSERT_TRUE(fromJSON(config2));
+
+    // Force a failure on the last element
+    SimFailure::set(SimFailure::ftElementUnknown);
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
+
+    // Refresh our local pointer.
+    context = getStubContext();
+
+    // Verify that all of parameters have the original values.
+    actual_bool = false;
+    EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
+    EXPECT_EQ(true, actual_bool);
+
+    actual_uint32 = 0;
+    EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
+    EXPECT_EQ(77, actual_uint32);
+
+    actual_string = "";
+    EXPECT_NO_THROW(context->getParam("string_test", actual_string));
+    EXPECT_EQ("hmmm chewy", actual_string);
+
+    actual_extra = 0;
+    EXPECT_NO_THROW(context->getExtraParam("extra_test", actual_extra));
+    EXPECT_EQ(430, actual_extra);
+}
+
+} // end of anonymous namespace

+ 99 - 2
src/bin/d2/tests/d_test_stubs.cc

@@ -28,7 +28,7 @@ SimFailure::FailureType SimFailure::failure_type_ = SimFailure::ftNoFailure;
 const char*  DStubProcess::stub_proc_command_("cool_proc_cmd");
 const char*  DStubProcess::stub_proc_command_("cool_proc_cmd");
 
 
 DStubProcess::DStubProcess(const char* name, IOServicePtr io_service)
 DStubProcess::DStubProcess(const char* name, IOServicePtr io_service)
-    : DProcessBase(name, io_service) {
+    : DProcessBase(name, io_service, DCfgMgrBasePtr(new DStubCfgMgr())) {
 };
 };
 
 
 void
 void
@@ -130,7 +130,7 @@ DStubController::DStubController()
 
 
     if (getenv("B10_FROM_BUILD")) {
     if (getenv("B10_FROM_BUILD")) {
         setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
         setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
-            "/src/bin/d2/d2.spec");
+            "/src/bin/d2/dhcp-ddns.spec");
     } else {
     } else {
         setSpecFileName(D2_SPECFILE_LOCATION);
         setSpecFileName(D2_SPECFILE_LOCATION);
     }
     }
@@ -191,5 +191,102 @@ DStubController::~DStubController() {
 // Initialize controller wrapper's static instance getter member.
 // Initialize controller wrapper's static instance getter member.
 DControllerTest::InstanceGetter DControllerTest::instanceGetter_ = NULL;
 DControllerTest::InstanceGetter DControllerTest::instanceGetter_ = NULL;
 
 
+//************************** TestParser *************************
+
+TestParser::TestParser(const std::string& param_name):param_name_(param_name) {
+}
+
+TestParser::~TestParser(){
+}
+
+void
+TestParser::build(isc::data::ConstElementPtr new_config) {
+    if (SimFailure::shouldFailOn(SimFailure::ftElementBuild)) {
+        // Simulates an error during element data parsing.
+        isc_throw (DCfgMgrBaseError, "Simulated build exception");
+    }
+
+    value_ = new_config;
+}
+
+void
+TestParser::commit() {
+    if (SimFailure::shouldFailOn(SimFailure::ftElementCommit)) {
+        // Simulates an error while committing the parsed element data.
+        throw std::runtime_error("Simulated commit exception");
+    }
+}
+
+//************************** DStubContext *************************
+
+DStubContext::DStubContext(): extra_values_(new isc::dhcp::Uint32Storage()) {
+}
+
+DStubContext::~DStubContext() {
+}
+
+void
+DStubContext::getExtraParam(const std::string& name, uint32_t& value) {
+    value = extra_values_->getParam(name);
+}
+
+isc::dhcp::Uint32StoragePtr
+DStubContext::getExtraStorage() {
+    return (extra_values_);
+}
+
+DStubContext*
+DStubContext::clone() {
+    return (new DStubContext(*this));
+}
+
+DStubContext::DStubContext(const DStubContext& rhs): DCfgContextBase(rhs),
+    extra_values_(new isc::dhcp::Uint32Storage(*(rhs.extra_values_))) {
+}
+
+//************************** DStubCfgMgr *************************
+
+DStubCfgMgr::DStubCfgMgr()
+    : DCfgMgrBase(DCfgContextBasePtr(new DStubContext())) {
+}
+
+DStubCfgMgr::~DStubCfgMgr() {
+}
+
+isc::dhcp::ParserPtr
+DStubCfgMgr::createConfigParser(const std::string& element_id) {
+    isc::dhcp::DhcpConfigParser* parser = NULL;
+    DStubContextPtr context =
+                    boost::dynamic_pointer_cast<DStubContext>(getContext());
+
+    if (element_id == "bool_test") {
+        parser = new isc::dhcp::BooleanParser(element_id,
+                                              context->getBooleanStorage());
+    } else if (element_id == "uint32_test") {
+        parser = new isc::dhcp::Uint32Parser(element_id,
+                                             context->getUint32Storage());
+    } else if (element_id == "string_test") {
+        parser = new isc::dhcp::StringParser(element_id,
+                                             context->getStringStorage());
+    } else if (element_id == "extra_test") {
+        parser = new isc::dhcp::Uint32Parser(element_id,
+                                             context->getExtraStorage());
+    } else {
+        // Fail only if SimFailure dictates we should.  This makes it easier
+        // to test parse ordering, by permitting a wide range of element ids
+        // to "succeed" without specifically supporting them.
+        if (SimFailure::shouldFailOn(SimFailure::ftElementUnknown)) {
+            isc_throw(DCfgMgrBaseError, "Configuration parameter not supported"
+                      << element_id);
+        }
+
+        parsed_order_.push_back(element_id);
+        parser = new TestParser(element_id);
+    }
+
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+
 }; // namespace isc::d2
 }; // namespace isc::d2
 }; // namespace isc
 }; // namespace isc

+ 144 - 1
src/bin/d2/tests/d_test_stubs.h

@@ -21,6 +21,8 @@
 #include <config/ccsession.h>
 #include <config/ccsession.h>
 
 
 #include <d2/d_controller.h>
 #include <d2/d_controller.h>
+#include <d2/d_cfg_mgr.h>
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 namespace isc {
 namespace isc {
@@ -43,7 +45,10 @@ public:
         ftProcessConfigure,
         ftProcessConfigure,
         ftControllerCommand,
         ftControllerCommand,
         ftProcessCommand,
         ftProcessCommand,
-        ftProcessShutdown
+        ftProcessShutdown,
+        ftElementBuild,
+        ftElementCommit,
+        ftElementUnknown
     };
     };
 
 
     /// @brief Sets the SimFailure value to the given value.
     /// @brief Sets the SimFailure value to the given value.
@@ -433,6 +438,144 @@ public:
     }
     }
 };
 };
 
 
+/// @brief Simple parser derivation for testing the basics of configuration
+/// parsing.
+class TestParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// See @ref DhcpConfigParser class for details.
+    ///
+    /// @param param_name name of the parsed parameter
+    TestParser(const std::string& param_name);
+
+    /// @brief Destructor
+    virtual ~TestParser();
+
+    /// @brief Builds parameter value.
+    ///
+    /// See @ref DhcpConfigParser class for details.
+    ///
+    /// @param new_config pointer to the new configuration
+    /// @throw throws DCfgMgrBaseError if the SimFailure is set to
+    /// ftElementBuild. This allows for the simulation of an
+    /// exception during the build portion of parsing an element.
+    virtual void build(isc::data::ConstElementPtr new_config);
+
+    /// @brief Commits the parsed value to storage.
+    ///
+    /// See @ref DhcpConfigParser class for details.
+    ///
+    /// @throw throws DCfgMgrBaseError if SimFailure is set to ftElementCommit.
+    /// This allows for the simulation of an exception during the commit
+    /// portion of parsing an element.
+    virtual void commit();
+
+private:
+    /// name of the parsed parameter
+    std::string param_name_;
+
+    /// pointer to the parsed value of the parameter
+    isc::data::ConstElementPtr value_;
+};
+
+/// @brief Test Derivation of the DCfgContextBase class.
+///
+/// This class is used to test basic functionality of configuration context.
+/// It adds an additional storage container "extra values" to mimic an
+/// application extension of configuration storage.  This permits testing that
+/// both the base class content as well as the application content is
+/// correctly copied during cloning.  This is vital to configuration backup
+/// and rollback during configuration parsing.
+class DStubContext : public DCfgContextBase {
+public:
+
+    /// @brief Constructor
+    DStubContext();
+
+    /// @brief Destructor
+    virtual ~DStubContext();
+
+    /// @brief Fetches the value for a given "extra" configuration parameter
+    /// from the context.
+    ///
+    /// @param name is the name of the parameter to retrieve.
+    /// @param value is an output parameter in which to return the retrieved
+    /// value.
+    /// @throw throws DhcpConfigError if the context does not contain the
+    /// parameter.
+    void getExtraParam(const std::string& name, uint32_t& value);
+
+    /// @brief Fetches the extra storage.
+    ///
+    /// @return returns a pointer to the extra storage.
+    isc::dhcp::Uint32StoragePtr getExtraStorage();
+
+    /// @brief Creates a clone of a DStubContext.
+    ///
+    /// @return returns a raw pointer to the new clone.
+    virtual DStubContext* clone();
+
+protected:
+    /// @brief Copy constructor
+    DStubContext(const DStubContext& rhs);
+
+private:
+    /// @brief Private assignment operator, not implemented.
+    DStubContext& operator=(const DStubContext& rhs);
+
+    /// @brief Extra storage for uint32 parameters.
+    isc::dhcp::Uint32StoragePtr extra_values_;
+};
+
+/// @brief Defines a pointer to DStubContext.
+typedef boost::shared_ptr<DStubContext> DStubContextPtr;
+
+/// @brief Test Derivation of the DCfgMgrBase class.
+///
+/// This class is used to test basic functionality of configuration management.
+/// It supports the following configuration elements:
+///
+/// "bool_test" - Boolean element, tests parsing and committing a boolean
+///               configuration parameter.
+/// "uint32_test" - Uint32 element, tests parsing and committing a uint32_t
+///               configuration parameter.
+/// "string_test" - String element, tests parsing and committing a string
+///               configuration parameter.
+/// "extra_test" - "Extra" element, tests parsing and committing an extra
+///               configuration parameter. (This is used to demonstrate
+///               derivation's addition of storage to configuration context.
+///
+/// It also keeps track of the element ids that are parsed in the order they
+/// are parsed.  This is used to test ordered and non-ordered parsing.
+class DStubCfgMgr : public DCfgMgrBase {
+public:
+    /// @brief Constructor
+    DStubCfgMgr();
+
+    /// @brief Destructor
+    virtual ~DStubCfgMgr();
+
+    /// @brief Given an element_id returns an instance of the appropriate
+    /// parser. It supports the element ids as described in the class brief.
+    ///
+    /// @param element_id is the string name of the element as it will appear
+    /// in the configuration set.
+    ///
+    /// @return returns a ParserPtr to the parser instance.
+    /// @throw throws DCfgMgrBaseError if SimFailure is ftElementUnknown.
+    virtual isc::dhcp::ParserPtr
+    createConfigParser(const std::string& element_id);
+
+    /// @brief A list for remembering the element ids in the order they were
+    /// parsed.
+    ElementIdList parsed_order_;
+};
+
+/// @brief Defines a pointer to DStubCfgMgr.
+typedef boost::shared_ptr<DStubCfgMgr> DStubCfgMgrPtr;
+
 }; // namespace isc::d2
 }; // namespace isc::d2
 }; // namespace isc
 }; // namespace isc
 
 

+ 1 - 1
src/lib/dhcpsrv/dhcp_parsers.cc

@@ -79,9 +79,9 @@ DebugParser::DebugParser(const std::string& param_name)
 
 
 void 
 void 
 DebugParser::build(ConstElementPtr new_config) {
 DebugParser::build(ConstElementPtr new_config) {
+    value_ = new_config;
     std::cout << "Build for token: [" << param_name_ << "] = ["
     std::cout << "Build for token: [" << param_name_ << "] = ["
         << value_->str() << "]" << std::endl; 
         << value_->str() << "]" << std::endl; 
-    value_ = new_config;
 }
 }
 
 
 void 
 void