Commit 7a035096 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[#6,!54] More code removed from lib/process

parent 953dd8ca
......@@ -9,6 +9,7 @@
#include <d2/d2_log.h>
#include <d2/d2_cfg_mgr.h>
#include <d2/d2_simple_parser.h>
#include <cc/command_interpreter.h>
#include <util/encode/hex.h>
#include <boost/foreach.hpp>
......@@ -106,11 +107,6 @@ const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";
const char* D2CfgMgr::IPV6_REV_ZONE_SUFFIX = "ip6.arpa.";
D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
// TSIG keys need to parse before the Domains, so we can catch Domains
// that specify undefined keys. Create the necessary parsing order now.
addToParseOrder("tsig-keys");
addToParseOrder("forward-ddns");
addToParseOrder("reverse-ddns");
}
D2CfgMgr::~D2CfgMgr() {
......@@ -246,177 +242,58 @@ D2CfgMgr::getConfigSummary(const uint32_t) {
return (getD2Params()->getConfigSummary());
}
namespace {
template <typename int_type> int_type
getInt(const std::string& name, ConstElementPtr value) {
int64_t val_int = value->intValue();
if ((val_int < std::numeric_limits<int_type>::min()) ||
(val_int > std::numeric_limits<int_type>::max())) {
isc_throw(D2CfgError, "out of range value (" << val_int
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
return (static_cast<int_type>(val_int));
void
D2CfgMgr::setCfgDefaults(ElementPtr mutable_config) {
D2SimpleParser::setAllDefaults(mutable_config);
}
isc::asiolink::IOAddress
getIOAddress(const std::string& name, ConstElementPtr value) {
std::string str = value->stringValue();
try {
return (isc::asiolink::IOAddress(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError, "invalid address (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
isc::data::ConstElementPtr
D2CfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) {
// Do a sanity check first.
if (!config_set) {
isc_throw(D2CfgError, "Mandatory config parameter not provided");
}
}
dhcp_ddns::NameChangeProtocol
getProtocol(const std::string& name, ConstElementPtr value) {
std::string str = value->stringValue();
try {
return (dhcp_ddns::stringToNcrProtocol(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError,
"invalid NameChangeRequest protocol (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
}
D2CfgContextPtr ctx = getD2CfgContext();
dhcp_ddns::NameChangeFormat
getFormat(const std::string& name, ConstElementPtr value) {
std::string str = value->stringValue();
// Set the defaults
ElementPtr cfg = boost::const_pointer_cast<Element>(config_set);
D2SimpleParser::setAllDefaults(cfg);
// And parse the configuration.
ConstElementPtr answer;
std::string excuse;
try {
return (dhcp_ddns::stringToNcrFormat(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError,
"invalid NameChangeRequest format (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
// Do the actual parsing
D2SimpleParser parser;
parser.parse(ctx, cfg, check_only);
} catch (const isc::Exception& ex) {
excuse = ex.what();
answer = isc::config::createAnswer(2, excuse);
} catch (...) {
excuse = "undefined configuration parsing error";
answer = isc::config::createAnswer(2, excuse);
}
}
} // anon
void
D2CfgMgr::parseElement(const std::string& element_id,
ConstElementPtr element) {
try {
// Get D2 specific context.
D2CfgContextPtr context = getD2CfgContext();
if ((element_id == "ip-address") ||
(element_id == "ncr-protocol") ||
(element_id == "ncr-format") ||
(element_id == "port") ||
(element_id == "dns-server-timeout")) {
// global scalar params require nothing extra be done
} else if (element_id == "user-context") {
if (element->getType() == Element::map) {
context->setContext(element);
}
} else if (element_id == "tsig-keys") {
TSIGKeyInfoListParser parser;
context->setKeys(parser.parse(element));
} else if (element_id == "forward-ddns") {
DdnsDomainListMgrParser parser;
DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
context->getKeys());
context->setForwardMgr(mgr);
} else if (element_id == "reverse-ddns") {
DdnsDomainListMgrParser parser;
DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
context->getKeys());
context->setReverseMgr(mgr);
// At this stage the answer was created only in case of exception.
if (answer) {
if (check_only) {
LOG_ERROR(d2_logger, DHCP_DDNS_CONFIG_CHECK_FAIL).arg(excuse);
} else {
// Shouldn't occur if the JSON parser is doing its job.
isc_throw(D2CfgError, "Unsupported element: "
<< element_id << element->getPosition());
LOG_ERROR(d2_logger, DHCP_DDNS_CONFIG_FAIL).arg(excuse);
}
} catch (const D2CfgError& ex) {
// Should already have a specific error and position info
throw ex;
} catch (const std::exception& ex) {
isc_throw(D2CfgError, "element: " << element_id << " : " << ex.what()
<< element->getPosition());
return (answer);
}
};
void
D2CfgMgr::setCfgDefaults(ElementPtr mutable_config) {
D2SimpleParser::setAllDefaults(mutable_config);
}
void
D2CfgMgr::buildParams(ConstElementPtr params_config) {
// Base class build creates parses and invokes build on each parser.
// This populate the context scalar stores with all of the parameters.
DCfgMgrBase::buildParams(params_config);
// Fetch the parameters in the config, performing any logical
// validation required.
asiolink::IOAddress ip_address(0);
uint32_t port = 0;
uint32_t dns_server_timeout = 0;
dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP;
dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON;
// Assumes that params_config has had defaults added
BOOST_FOREACH(isc::dhcp::ConfigPair param, params_config->mapValue()) {
std::string entry(param.first);
ConstElementPtr value(param.second);
try {
if (entry == "ip-address") {
ip_address = getIOAddress(entry, value);
if ((ip_address.toText() == "0.0.0.0") ||
(ip_address.toText() == "::")) {
isc_throw(D2CfgError, "IP address cannot be \""
<< ip_address << "\""
<< " (" << value->getPosition() << ")");
}
} else if (entry == "port") {
port = getInt<uint32_t>(entry, value);
} else if (entry == "dns-server-timeout") {
dns_server_timeout = getInt<uint32_t>(entry, value);
} else if (entry == "ncr-protocol") {
ncr_protocol = getProtocol(entry, value);
if (ncr_protocol != dhcp_ddns::NCR_UDP) {
isc_throw(D2CfgError, "ncr-protocol : "
<< dhcp_ddns::ncrProtocolToString(ncr_protocol)
<< " is not yet supported "
<< " (" << value->getPosition() << ")");
}
} else if (entry == "ncr-format") {
ncr_format = getFormat(entry, value);
if (ncr_format != dhcp_ddns::FMT_JSON) {
isc_throw(D2CfgError, "NCR Format:"
<< dhcp_ddns::ncrFormatToString(ncr_format)
<< " is not yet supported"
<< " (" << value->getPosition() << ")");
}
} else {
isc_throw(D2CfgError,
"unsupported parameter '" << entry
<< " (" << value->getPosition() << ")");
}
} catch (const isc::data::TypeError&) {
isc_throw(D2CfgError,
"invalid value type specified for parameter '" << entry
<< " (" << value->getPosition() << ")");
}
if (check_only) {
answer = isc::config::createAnswer(0, "Configuration check successful");
} else {
answer = isc::config::createAnswer(0, "Configuration applied successfully.");
}
// Attempt to create the new client config. This ought to fly as
// we already validated everything.
D2ParamsPtr params(new D2Params(ip_address, port, dns_server_timeout,
ncr_protocol, ncr_format));
getD2CfgContext()->getD2Params() = params;
return (answer);
}
}; // end of isc::dhcp namespace
}; // end of isc namespace
......@@ -264,19 +264,14 @@ public:
protected:
/// @brief Parses an element using alternate parsers
/// @brief Parses configuration of the D2.
///
/// Each element to be parsed is passed first into this method to allow
/// it to be processed by SimpleParser derivations if they've been
/// implemented. The method should return true if it has processed the
/// element or false if the element should be passed onto the original
/// DhcpConfigParser mechanisms. This method is invoked in both
/// @c DCfgMgrBase::buildParams() and DCfgMgrBase::buildAndCommit().
///
/// @param element_id name of the element as it is expected in the cfg
/// @param element value of the element as ElementPtr
virtual void parseElement(const std::string& element_id,
isc::data::ConstElementPtr element);
/// @param config Pointer to a configuration specified for D2.
/// @param check_only Boolean flag indicating if this method should
/// only verify correctness of the provided configuration.
/// @return Pointer to a result of configuration parsing.
virtual isc::data::ConstElementPtr
parse(isc::data::ConstElementPtr config, bool check_only);
/// @brief Adds default values to the given config
///
......@@ -286,26 +281,6 @@ protected:
/// @param mutable_config - configuration to which defaults should be added
virtual void setCfgDefaults(isc::data::ElementPtr mutable_config);
/// @brief Performs the parsing of the given "params" element.
///
/// Iterates over the set of parameters, creating a parser based on the
/// parameter's id and then invoking its build method passing in the
/// parameter's configuration value.
///
/// It then fetches the parameters, validating their values and if
/// valid instantiates a D2Params instance. Invalid values result in
/// a throw.
///
/// @param params_config set of scalar configuration elements to parse
///
/// @throw D2CfgError if any of the following are true:
/// -# ip_address is 0.0.0.0 or ::
/// -# port is 0
/// -# dns_server_timeout is < 1
/// -# ncr_protocol is invalid, currently only NCR_UDP is supported
/// -# ncr_format is invalid, currently only FMT_JSON is supported
virtual void buildParams(isc::data::ConstElementPtr params_config);
/// @brief Creates an new, blank D2CfgContext context
///
/// This method is used at the beginning of configuration process to
......
......@@ -41,6 +41,16 @@ has been invoked.
This is a debug message issued when the DHCP-DDNS application configure method
has been invoked.
% DHCP_DDNS_CONFIG_CHECK_FAIL Control Agent configuration check failed: %1
This error message indicates that the DHCP-DDNS had failed configuration
check. Details are provided. Additional details may be available
in earlier log entries, possibly on lower levels.
% DHCP_DDNS_CONFIG_FAIL Control Agent configuration failed: %1
This error message indicates that the DHCP-DDNS had failed configuration
attempt. Details are provided. Additional details may be available
in earlier log entries, possibly on lower levels.
% DHCP_DDNS_FAILED application experienced a fatal error: %1
This is a debug message issued when the DHCP-DDNS application encounters an
unrecoverable error from within the event loop.
......
......@@ -198,7 +198,7 @@ D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) {
.arg(config_set->str());
isc::data::ConstElementPtr answer;
answer = getCfgMgr()->parseConfig(config_set, check_only);;
answer = getCfgMgr()->simpleParseConfig(config_set, check_only);
if (check_only) {
return (answer);
}
......
......@@ -10,6 +10,48 @@
#include <boost/foreach.hpp>
using namespace isc::data;
using namespace isc::d2;
using namespace isc;
namespace {
dhcp_ddns::NameChangeProtocol
getProtocol(ConstElementPtr map, const std::string& name) {
ConstElementPtr value = map->get(name);
if (!value) {
isc_throw(D2CfgError, "Mandatory parameter " << name
<< " not found (" << map->getPosition() << ")");
}
std::string str = value->stringValue();
try {
return (dhcp_ddns::stringToNcrProtocol(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError,
"invalid NameChangeRequest protocol (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
}
dhcp_ddns::NameChangeFormat
getFormat(ConstElementPtr map, const std::string& name) {
ConstElementPtr value = map->get(name);
if (!value) {
isc_throw(D2CfgError, "Mandatory parameter " << name
<< " not found (" << map->getPosition() << ")");
}
std::string str = value->stringValue();
try {
return (dhcp_ddns::stringToNcrFormat(str));
} catch (const std::exception& ex) {
isc_throw(D2CfgError,
"invalid NameChangeRequest format (" << str
<< ") specified for parameter '" << name
<< "' (" << value->getPosition() << ")");
}
}
} // anon
namespace isc {
namespace d2 {
......@@ -150,5 +192,87 @@ D2SimpleParser::setManagerDefaults(ElementPtr global,
return (cnt);
}
void D2SimpleParser::parse(const D2CfgContextPtr& ctx,
const isc::data::ConstElementPtr& config,
bool /*check_only*/) {
// TSIG keys need to parse before the Domains, so we can catch Domains
// that specify undefined keys. Create the necessary parsing order now.
// addToParseOrder("tsig-keys");
// addToParseOrder("forward-ddns");
// addToParseOrder("reverse-ddns");
ConstElementPtr keys = config->get("tsig-keys");
if (keys) {
TSIGKeyInfoListParser parser;
ctx->setKeys(parser.parse(keys));
}
ConstElementPtr fwd = config->get("forward-ddns");
if (fwd) {
DdnsDomainListMgrParser parser;
DdnsDomainListMgrPtr mgr = parser.parse(fwd, "forward-ddns",
ctx->getKeys());
ctx->setForwardMgr(mgr);
}
ConstElementPtr rev = config->get("reverse-ddns");
if (rev) {
DdnsDomainListMgrParser parser;
DdnsDomainListMgrPtr mgr = parser.parse(rev, "reverse-ddns",
ctx->getKeys());
ctx->setReverseMgr(mgr);
}
// Fetch the parameters in the config, performing any logical
// validation required.
asiolink::IOAddress ip_address(0);
uint32_t port = 0;
uint32_t dns_server_timeout = 0;
dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP;
dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON;
ip_address = SimpleParser::getAddress(config, "ip-address");
if ((ip_address.toText() == "0.0.0.0") ||
(ip_address.toText() == "::")) {
isc_throw(D2CfgError, "IP address cannot be \""
<< ip_address << "\""
<< " (" << config->get("ip-address")->getPosition() << ")");
}
port = SimpleParser::getUint32(config, "port");
dns_server_timeout = SimpleParser::getUint32(config, "dns-server-timeout");
ncr_protocol = getProtocol(config, "ncr-protocol");
if (ncr_protocol != dhcp_ddns::NCR_UDP) {
isc_throw(D2CfgError, "ncr-protocol : "
<< dhcp_ddns::ncrProtocolToString(ncr_protocol)
<< " is not yet supported "
<< " (" << config->get("ncr-protocol")->getPosition() << ")");
}
ncr_format = getFormat(config, "ncr-format");
if (ncr_format != dhcp_ddns::FMT_JSON) {
isc_throw(D2CfgError, "NCR Format:"
<< dhcp_ddns::ncrFormatToString(ncr_format)
<< " is not yet supported"
<< " (" << config->get("ncr-format")->getPosition() << ")");
}
ConstElementPtr user = config->get("user-context");
if (user) {
ctx->setContext(user);
}
// Attempt to create the new client config. This ought to fly as
// we already validated everything.
D2ParamsPtr params(new D2Params(ip_address, port, dns_server_timeout,
ncr_protocol, ncr_format));
ctx->getD2Params() = params;
}
};
};
......@@ -8,6 +8,7 @@
#define D2_SIMPLE_PARSER_H
#include <cc/simple_parser.h>
#include <d2/d2_cfg_mgr.h>
namespace isc {
namespace d2 {
......@@ -76,6 +77,17 @@ public:
static size_t setManagerDefaults(data::ElementPtr global,
const std::string& mgr_name,
const data::SimpleDefaults& mgr_defaults);
/// @brief Parses the whole D2 configuration
///
/// @param ctx - parsed information will be stored here
/// @param config - Element tree structure that holds configuration
/// @param check_only - if true the configuration is verified only, not applied
///
/// @throw ConfigError if any issues are encountered.
void parse(const D2CfgContextPtr& ctx,
const isc::data::ConstElementPtr& config,
bool check_only);
};
};
......
......@@ -158,7 +158,7 @@ public:
// The JSON parsed ok and we've added the defaults, pass the config
// into the Element parser and check for the expected outcome.
data::ConstElementPtr answer;
answer = cfg_mgr_->parseConfig(config_set_, false);
answer = cfg_mgr_->simpleParseConfig(config_set_, false);
// Extract the result and error text from the answer.
int rcode = 0;
......@@ -585,7 +585,7 @@ TEST_F(D2CfgMgrTest, fullConfig) {
// Verify that parsing the exact same configuration a second time
// does not cause a duplicate value errors.
answer_ = cfg_mgr_->parseConfig(config_set_, false);
answer_ = cfg_mgr_->simpleParseConfig(config_set_, false);
ASSERT_TRUE(checkAnswer(0));
}
......
......@@ -183,16 +183,16 @@ TEST_F(D2ControllerTest, configUpdateTests) {
EXPECT_EQ(0, rcode);
// Use an invalid configuration to verify parsing error return.
std::string config = "{ \"bogus\": 1000 } ";
std::string config = "{ \"ip-address\": 1000 } ";
config_set = isc::data::Element::fromJSON(config);
answer = updateConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
EXPECT_EQ(2, rcode);
// Use an invalid configuration to verify checking error return.
answer = checkConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
EXPECT_EQ(2, rcode);
}
// Tests that the original configuration is retained after a SIGHUP triggered
......
......@@ -202,9 +202,18 @@ TEST_F(D2ProcessTest, configure) {
// Invoke configure() with the invalid configuration.
answer = configure(config_set_, false);
// Verify that configure result is failure, the reconfigure flag is
// false, and that the queue manager is still running.
ASSERT_TRUE(checkAnswer(answer, 1));
// Verify that configure result is a success, as extra parameters are
// ignored. the reconfigure flag is false, and that the queue manager is
// still running.
ASSERT_TRUE(checkAnswer(answer, 0));
EXPECT_TRUE(getReconfQueueFlag());
EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState());
// Finally, try with an invalid configuration.
// Create an invalid configuration set from text config.
ASSERT_TRUE(fromJSON("{ \"ip-address\": \"950 Charter St.\" } "));
answer = configure(config_set_, false);
ASSERT_TRUE(checkAnswer(answer, 2));
EXPECT_FALSE(getReconfQueueFlag());
EXPECT_EQ(D2QueueMgr::RUNNING, queue_mgr->getMgrState());
}
......
......@@ -144,7 +144,7 @@ public:
// If this configuration fails to parse most tests will fail.
ASSERT_TRUE(fromJSON(canned_config_));
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->simpleParseConfig(config_set_);
ASSERT_TRUE(checkAnswer(0));
}
......
......@@ -154,7 +154,7 @@ public:
// try DHCPDDNS configure
ConstElementPtr status;
try {
status = srv_->parseConfig(d2, false);
status = srv_->simpleParseConfig(d2, false);
} catch (const std::exception& ex) {
ADD_FAILURE() << "configure for " << operation
<< " failed with " << ex.what()
......
......@@ -137,7 +137,7 @@
#-----
,{
"description" : "D2Params.ip-address invalid value",
"logic-error" : "invalid address (bogus) specified for parameter 'ip-address' (<string>:1:39)",
"logic-error" : "Failed to convert 'bogus' to address: Failed to convert string to address 'bogus': Invalid argument(<string>:1:39)",
"data" :
{
"ip-address" : "bogus",
......@@ -315,7 +315,7 @@
#-----
,{
"description" : "D2.tsig-keys, missing key name",
"logic-error" : "element: tsig-keys : missing parameter 'name' (<string>:1:62)<string>:1:47",
"logic-error" : "missing parameter 'name' (<string>:1:62)",
"data" :
{
"forward-ddns" : {},
......@@ -420,7 +420,7 @@
#----- D2.tsig-keys, algorithm tests
,{
"description" : "D2.tsig-keys, missing algorithm",
"logic-error" : "element: tsig-keys : missing parameter 'algorithm' (<string>:1:62)<string>:1:47",
"logic-error" : "missing parameter 'algorithm' (<string>:1:62)",
"data" :
{
"forward-ddns" : {},
......@@ -665,7 +665,7 @@
#----- D2.tsig-keys, secret tests
,{
"description" : "D2.tsig-keys, missing secret",
"logic-error" : "element: tsig-keys : missing parameter 'secret' (<string>:1:62)<string>:1:47",
"logic-error" : "missing parameter 'secret' (<string>:1:62)",
"data" :
{
"forward-ddns" : {},
......
......@@ -41,8 +41,7 @@ DCfgContextBase::~DCfgContextBase() {
// *********************** DCfgMgrBase *************************
DCfgMgrBase::DCfgMgrBase(DCfgContextBasePtr context)
: parse_order_() {
DCfgMgrBase::DCfgMgrBase(DCfgContextBasePtr context) {
setContext(context);
}
......@@ -64,167 +63,6 @@ DCfgMgrBase::setContext(DCfgContextBasePtr& context) {
context_ = context;
}
isc::data::ConstElementPtr
DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set,
bool check_only) {
LOG_DEBUG(dctl_logger, isc::log::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_;
resetContext();