Commit dac0b87d authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[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
parent ea8be8f8
......@@ -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
// 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
// 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
......@@ -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
......
......@@ -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
......
// 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
// 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;