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 += d_controller.cc d_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
 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/asiolink/libb10-asiolink.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_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
 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
 This critical error message indicates that the initial application
 configuration has failed. The service will start, but will not
 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
 This debug message is issued when the dummy handler for configuration
 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
 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
 The controller has encountered a fatal error while running the
 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 <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
 #include <d2/d2_process.h>
 
 using namespace asio;
@@ -22,7 +23,7 @@ namespace isc {
 namespace d2 {
 
 D2Process::D2Process(const char* name, IOServicePtr io_service)
-    : DProcessBase(name, io_service) {
+    : DProcessBase(name, io_service, DCfgMgrBasePtr(new D2CfgMgr())) {
 };
 
 void
@@ -60,14 +61,14 @@ D2Process::shutdown() {
 
 isc::data::ConstElementPtr
 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,
               DHCP_DDNS_CONFIGURE).arg(config_set->str());
 
-    return (isc::config::createAnswer(0, "Configuration accepted."));
+    return (getCfgMgr()->parseConfig(config_set));
 }
 
 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 <cc/data.h>
+#include <d2/d_cfg_mgr.h>
+
 #include <boost/shared_ptr.hpp>
 
 #include <exceptions/exceptions.h>
@@ -60,11 +62,17 @@ public:
     /// asynchronous event handling.
     ///
     /// @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_) {
             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
@@ -159,6 +167,13 @@ public:
         io_service_->stop();
     }
 
+    /// @brief Fetches the process's configuration manager.
+    ///
+    /// @return returns a reference to the configuration manager.
+    DCfgMgrBasePtr& getCfgMgr() {
+        return (cfg_mgr_);
+    }
+
 private:
     /// @brief Text label for the process. Generally used in log statements,
     /// but otherwise can be arbitrary.
@@ -169,6 +184,9 @@ private:
 
     /// @brief Boolean flag set when shutdown has been requested.
     bool shut_down_flag_;
+
+    /// @brief  Pointer to the configuration manager.
+    DCfgMgrBasePtr cfg_mgr_;
 };
 
 /// @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 += ../d2_process.cc ../d2_process.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 += d2_unittests.cc
 d2_unittests_SOURCES += d2_process_unittests.cc
 d2_unittests_SOURCES += d_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
 
 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/cc/libb10-cc.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 endif
 
 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");
 
 DStubProcess::DStubProcess(const char* name, IOServicePtr io_service)
-    : DProcessBase(name, io_service) {
+    : DProcessBase(name, io_service, DCfgMgrBasePtr(new DStubCfgMgr())) {
 };
 
 void
@@ -130,7 +130,7 @@ DStubController::DStubController()
 
     if (getenv("B10_FROM_BUILD")) {
         setSpecFileName(std::string(getenv("B10_FROM_BUILD")) +
-            "/src/bin/d2/d2.spec");
+            "/src/bin/d2/dhcp-ddns.spec");
     } else {
         setSpecFileName(D2_SPECFILE_LOCATION);
     }
@@ -191,5 +191,102 @@ DStubController::~DStubController() {
 // Initialize controller wrapper's static instance getter member.
 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

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

@@ -21,6 +21,8 @@
 #include <config/ccsession.h>
 
 #include <d2/d_controller.h>
+#include <d2/d_cfg_mgr.h>
+
 #include <gtest/gtest.h>
 
 namespace isc {
@@ -43,7 +45,10 @@ public:
         ftProcessConfigure,
         ftControllerCommand,
         ftProcessCommand,
-        ftProcessShutdown
+        ftProcessShutdown,
+        ftElementBuild,
+        ftElementCommit,
+        ftElementUnknown
     };
 
     /// @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
 

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

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