Commit 406bb64b authored by Francis Dupont's avatar Francis Dupont Committed by Tomek Mrugalski

[fdunparse2] Some fixes, did D2, began CA

parent c1862585
......@@ -1686,6 +1686,7 @@ AC_CONFIG_FILES([Makefile
src/bin/agent/Makefile
src/bin/agent/tests/Makefile
src/bin/agent/tests/ca_process_tests.sh
src/bin/agent/tests/test_libraries.h
src/bin/d2/Makefile
src/bin/d2/tests/Makefile
src/bin/d2/tests/d2_process_tests.sh
......
......@@ -158,6 +158,34 @@ CtrlAgentCfgContext::setControlSocketInfo(const isc::data::ConstElementPtr& cont
ctrl_sockets_[static_cast<uint8_t>(type)] = control_socket;
}
ElementPtr
CtrlAgentCfgContext::toElement() const {
ElementPtr result = Element::createMap();
// Set http-host
result->set("http-host", Element::create(http_host_));
// Set http-port
result->set("http-port",
Element::create(static_cast<int64_t>(http_port_)));
// hooks-libraries
result->set("hooks-libraries", hooks_config_.toElement());
// control-sockets
ElementPtr control_sockets = Element::createMap();
// dhcp4-server
if (ctrl_sockets_[TYPE_DHCP4]) {
control_sockets->set("dhcp4-server", ctrl_sockets_[TYPE_DHCP4]);
}
// dhcp6-server
if (ctrl_sockets_[TYPE_DHCP6]) {
control_sockets->set("dhcp6-server", ctrl_sockets_[TYPE_DHCP6]);
}
// d2-server
if (ctrl_sockets_[TYPE_D2]) {
control_sockets->set("d2-server", ctrl_sockets_[TYPE_D2]);
}
result->set("control-sockets", control_sockets);
return (result);
}
} // namespace isc::agent
} // namespace isc
......@@ -116,6 +116,17 @@ public:
return (hooks_config_);
}
/// @brief Unparse a configuration objet
///
/// Returns an element which must parse into the same objet, i.e.
/// @code
/// for all valid config C parse(parse(C)->toElement()) == parse(C)
/// @endcode
///
/// @return a pointer to a configuration which can be parsed into
/// the initial configuration object
virtual isc::data::ElementPtr toElement() const;
private:
/// @brief Private copy constructor
......
......@@ -13,6 +13,8 @@
#include <boost/foreach.hpp>
using namespace isc::asiolink;
using namespace isc::data;
using namespace isc::process;
namespace isc {
......@@ -51,6 +53,50 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
D2CfgContext::~D2CfgContext() {
}
ElementPtr
D2CfgContext::toElement() const {
ElementPtr d2 = Element::createMap();
// Set ip-address
const IOAddress& ip_address = d2_params_->getIpAddress();
d2->set("ip-address", Element::create(ip_address.toText()));
// Set port
size_t port = d2_params_->getPort();
d2->set("port", Element::create(static_cast<int64_t>(port)));
// Set dns-server-timeout
size_t dns_server_timeout = d2_params_->getDnsServerTimeout();
d2->set("dns-server-timeout",
Element::create(static_cast<int64_t>(dns_server_timeout)));
// Set ncr-protocol
const dhcp_ddns::NameChangeProtocol& ncr_protocol =
d2_params_->getNcrProtocol();
d2->set("ncr-protocol",
Element::create(dhcp_ddns::ncrProtocolToString(ncr_protocol)));
// Set ncr-format
const dhcp_ddns::NameChangeFormat& ncr_format = d2_params_->getNcrFormat();
d2->set("ncr-format",
Element::create(dhcp_ddns::ncrFormatToString(ncr_format)));
// Set forward-ddns
ElementPtr forward_ddns = Element::createMap();
forward_ddns->set("ddns-domains", forward_mgr_->toElement());
d2->set("forward-ddns", forward_ddns);
// Set reverse-ddns
ElementPtr reverse_ddns = Element::createMap();
reverse_ddns->set("ddns-domains", reverse_mgr_->toElement());
d2->set("reverse-ddns", reverse_ddns);
// Set tsig-keys
ElementPtr tsig_keys = Element::createList();
for (TSIGKeyInfoMap::const_iterator key = keys_->begin();
key != keys_->end(); ++key) {
tsig_keys->add(key->second->toElement());
}
d2->set("tsig-keys", tsig_keys);
// Set DhcpDdns
ElementPtr result = Element::createMap();
result->set("DhcpDdns", d2);
return (result);
}
// *********************** D2CfgMgr *************************
const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";
......
......@@ -90,6 +90,11 @@ public:
keys_ = keys;
}
/// @brief Unparse a configuration objet
///
/// @return a pointer to a configuration
virtual isc::data::ElementPtr toElement() const;
protected:
/// @brief Copy constructor for use by derivations in clone().
D2CfgContext(const D2CfgContext& rhs);
......
......@@ -20,6 +20,7 @@
#include <string>
using namespace isc::process;
using namespace isc::data;
namespace isc {
namespace d2 {
......@@ -180,6 +181,22 @@ TSIGKeyInfo::remakeKey() {
}
}
ElementPtr
TSIGKeyInfo::toElement() const {
ElementPtr result = Element::createMap();
// Set name
result->set("name", Element::create(name_));
// Set algorithm
result->set("algorithm", Element::create(algorithm_));
// Set secret
result->set("secret", Element::create(secret_));
// Set digest-bits
result->set("digest-bits",
Element::create(static_cast<int64_t>(digestbits_)));
return (result);
}
// *********************** DnsServerInfo *************************
DnsServerInfo::DnsServerInfo(const std::string& hostname,
isc::asiolink::IOAddress ip_address, uint32_t port,
......@@ -198,6 +215,19 @@ DnsServerInfo::toText() const {
return (stream.str());
}
ElementPtr
DnsServerInfo::toElement() const {
ElementPtr result = Element::createMap();
// Set hostname
result->set("hostname", Element::create(hostname_));
// Set ip-address
result->set("ip-address", Element::create(ip_address_.toText()));
// Set port
result->set("port", Element::create(static_cast<int64_t>(port_)));
return (result);
}
std::ostream&
operator<<(std::ostream& os, const DnsServerInfo& server) {
......@@ -226,6 +256,30 @@ DdnsDomain::getKeyName() const {
return ("");
}
ElementPtr
DdnsDomain::toElement() const {
ElementPtr result = Element::createMap();
// Set name
result->set("name", Element::create(name_));
// Set servers
ElementPtr servers = Element::createList();
for (DnsServerInfoStorage::const_iterator server = servers_->begin();
server != servers_->end(); ++server) {
ElementPtr dns_server = (*server)->toElement();
servers->add(dns_server);
}
// the dns server list may not be empty
if (!servers->empty()) {
result->set("dns-servers", servers);
}
// Set key-name
if (tsig_key_info_) {
result->set("key-name", Element::create(tsig_key_info_->getName()));
}
return (result);
}
// *********************** DdnsDomainLstMgr *************************
const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";
......@@ -321,6 +375,19 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
return (true);
}
ElementPtr
DdnsDomainListMgr::toElement() const {
ElementPtr result = Element::createList();
// Iterate on ddns domains
for (DdnsDomainMap::const_iterator domain = domains_->begin();
domain != domains_->end(); ++domain) {
ElementPtr ddns_domain = domain->second->toElement();
result->add(ddns_domain);
}
return (result);
}
// *************************** PARSERS ***********************************
// *********************** TSIGKeyInfoParser *************************
......
......@@ -10,6 +10,7 @@
#include <asiolink/io_service.h>
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <cc/cfg_to_element.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dns/tsig.h>
#include <exceptions/exceptions.h>
......@@ -255,7 +256,7 @@ typedef boost::shared_ptr<D2Params> D2ParamsPtr;
/// instance of the actual key (@ref isc::dns::TSIGKey) that can be used
/// by the IO layer for signing and verifying messages.
///
class TSIGKeyInfo {
class TSIGKeyInfo : public isc::data::CfgToElement {
public:
/// @brief Defines string values for the supported TSIG algorithms
//@{
......@@ -356,6 +357,11 @@ public:
static const dns::Name& stringToAlgorithmName(const std::string&
algorithm_id);
/// @brief Unparse a configuration objet
///
/// @return a pointer to a configuration
virtual isc::data::ElementPtr toElement() const;
private:
/// @brief Creates the actual TSIG key instance member
///
......@@ -405,7 +411,7 @@ typedef boost::shared_ptr<TSIGKeyInfoMap> TSIGKeyInfoMapPtr;
/// belongs to a list of servers supporting DNS for a given domain. It will
/// be used to establish communications with the server to carry out DNS
/// updates.
class DnsServerInfo {
class DnsServerInfo : public isc::data::CfgToElement {
public:
/// @brief defines DNS standard port value
static const uint32_t STANDARD_DNS_PORT = 53;
......@@ -472,6 +478,11 @@ public:
/// @brief Returns a text representation for the server.
std::string toText() const;
/// @brief Unparse a configuration objet
///
/// @return a pointer to a configuration
virtual isc::data::ElementPtr toElement() const;
private:
/// @brief The resolvable name of the server. If not blank, then the
......@@ -510,7 +521,7 @@ typedef boost::shared_ptr<DnsServerInfoStorage> DnsServerInfoStoragePtr;
/// @todo Currently the name entry for a domain is just an std::string. It
/// may be worthwhile to change this to a dns::Name for purposes of better
/// validation and matching capabilities.
class DdnsDomain {
class DdnsDomain : public isc::data::CfgToElement {
public:
/// @brief Constructor
///
......@@ -553,6 +564,11 @@ public:
return (tsig_key_info_);
}
/// @brief Unparse a configuration objet
///
/// @return a pointer to a configuration
virtual isc::data::ElementPtr toElement() const;
private:
/// @brief The domain name of the domain.
std::string name_;
......@@ -588,7 +604,7 @@ typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr;
/// specify the wild card domain as the only forward domain. All forward DNS
/// updates would be sent to that one list of servers, regardless of the FQDN.
/// As matching capabilities evolve this class is expected to expand.
class DdnsDomainListMgr {
class DdnsDomainListMgr : public isc::data::CfgToElement {
public:
/// @brief defines the domain name for denoting the wildcard domain.
static const char* wildcard_domain_name_;
......@@ -655,6 +671,11 @@ public:
/// set the internal wild card domain pointer accordingly.
void setDomains(DdnsDomainMapPtr domains);
/// @brief Unparse a configuration objet
///
/// @return a pointer to a configuration
virtual isc::data::ElementPtr toElement() const;
private:
/// @brief An arbitrary label assigned to this manager.
std::string name_;
......@@ -697,6 +718,13 @@ public:
return (process::DCfgContextBasePtr(new DScalarContext(*this)));
}
/// @brief Unparse a configuration objet
///
/// @return a pointer to a configuration
virtual isc::data::ElementPtr toElement() const {
isc_throw(isc::NotImplemented, "DScalarContext::ElementPtr");
}
protected:
/// @brief Copy constructor
DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs) {
......
......@@ -6,7 +6,7 @@ SHTESTS += d2_process_tests.sh
noinst_SCRIPTS = d2_process_tests.sh
EXTRA_DIST = d2_process_tests.sh.in
EXTRA_DIST += testdata/d2_cfg_tests.json
EXTRA_DIST += testdata/d2_cfg_tests.json testdata/get_config.json
# test using command-line arguments, so use check-local target instead of TESTS
check-local:
......@@ -58,6 +58,7 @@ d2_unittests_SOURCES += nc_trans_unittests.cc
d2_unittests_SOURCES += d2_controller_unittests.cc
d2_unittests_SOURCES += d2_simple_parser_unittest.cc
d2_unittests_SOURCES += parser_unittest.cc parser_unittest.h
d2_unittests_SOURCES += get_config_unittest.cc
d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS)
......
// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <cc/data.h>
#include <cc/command_interpreter.h>
#include <process/testutils/d_test_stubs.h>
#include <d2/d2_config.h>
#include <d2/d2_cfg_mgr.h>
#include <d2/tests/parser_unittest.h>
#include <gtest/gtest.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "test_data_files_config.h"
using namespace isc::config;
using namespace isc::d2;
using namespace isc::data;
using namespace isc::process;
namespace {
/// @name How to generate the testdata/get_config.json file
///
/// Define GENERATE_ACTION and recompile. Run d2_unittests on
/// D2GetConfigTest redirecting the standard error to a temporary
/// file, e.g. by
/// @code
/// ./d2_unittests --gtest_filter="D2GetConfig*" > /dev/null 2> u
/// @endcode
///
/// Update testdata/get_config.json using the temporary file content,
/// recompile without GENERATE_ACTION.
/// @brief the generate action
/// false means do nothing, true means unparse extracted configurations
#ifdef GENERATE_ACTION
const bool generate_action = true;
#else
const bool generate_action = false;
#endif
/// @brief Read a file into a string
std::string
readFile(const std::string& file_path) {
std::ifstream ifs(file_path);
if (!ifs.is_open()) {
ADD_FAILURE() << "readFile cannot open " << file_path;
isc_throw(isc::Unexpected, "readFile cannot open " << file_path);
}
std::string lines;
std::string line;
while (std::getline(ifs, line)) {
lines += line + "\n";
}
ifs.close();
return (lines);
}
/// @brief Runs parser in JSON mode
ElementPtr
parseJSON(const std::string& in, bool verbose = false) {
try {
D2ParserContext ctx;
return (ctx.parseString(in, D2ParserContext::PARSER_JSON));
} catch (const std::exception& ex) {
if (verbose) {
std::cout << "EXCEPTION: " << ex.what() << std::endl;
}
throw;
}
}
/// @brief Runs parser in DHCPDDNS mode
ElementPtr
parseDHCPDDNS(const std::string& in, bool verbose = false) {
try {
D2ParserContext ctx;
return (ctx.parseString(in, D2ParserContext::PARSER_DHCPDDNS));
} catch (const std::exception& ex) {
if (verbose) {
std::cout << "EXCEPTION: " << ex.what() << std::endl;
}
throw;
}
}
}
/// Test fixture class
class D2GetConfigTest : public ConfigParseTest {
public:
D2GetConfigTest()
: rcode_(-1) {
srv_.reset(new D2CfgMgr());
// Create fresh context.
resetConfiguration();
}
~D2GetConfigTest() {
resetConfiguration();
}
/// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
/// If the operation fails, the current test will register a failure.
///
/// @param config Configuration to parse
/// @param operation Operation being performed. In the case of an error,
/// the error text will include the string "unable to <operation>.".
///
/// @return true if the configuration succeeded, false if not.
bool
executeConfiguration(const std::string& config, const char* operation) {
// try JSON parser
ConstElementPtr json;
try {
json = parseJSON(config, true);
} catch (const std::exception& ex) {
ADD_FAILURE() << "invalid JSON for " << operation
<< " failed with " << ex.what()
<< " on\n" << config << "\n";
return (false);
}
// try DHCPDDNS parser
try {
json = parseDHCPDDNS(config, true);
} catch (...) {
ADD_FAILURE() << "parsing failed for " << operation
<< " on\n" << prettyPrint(json) << "\n";
return (false);
}
// get DhcpDdns element
ConstElementPtr d2 = json->get("DhcpDdns");
if (!d2) {
ADD_FAILURE() << "cannot get DhcpDdns for " << operation
<< " on\n" << prettyPrint(json) << "\n";
return (false);
}
// try DHCPDDNS configure
ConstElementPtr status;
try {
status = srv_->parseConfig(d2);
} catch (const std::exception& ex) {
ADD_FAILURE() << "configure for " << operation
<< " failed with " << ex.what()
<< " on\n" << prettyPrint(json) << "\n";
return (false);
}
// The status object must not be NULL
if (!status) {
ADD_FAILURE() << "configure for " << operation
<< " returned null on\n"
<< prettyPrint(json) << "\n";
return (false);
}
// Returned value should be 0 (configuration success)
comment_ = parseAnswer(rcode_, status);
if (rcode_ != 0) {
string reason = "";
if (comment_) {
reason = string(" (") + comment_->stringValue() + string(")");
}
ADD_FAILURE() << "configure for " << operation
<< " returned error code "
<< rcode_ << reason << " on\n"
<< prettyPrint(json) << "\n";
return (false);
}
return (true);
}
/// @brief Reset configuration database.
///
/// This function resets configuration data base by
/// removing control sockets and domain lists. Reset must
/// be performed after each test to make sure that
/// contents of the database do not affect result of
/// subsequent tests.
void resetConfiguration() {
string config = "{ \"DhcpDdns\": {"
" \"ip-address\": \"127.0.0.1\","
" \"port\": 53001,"
" \"dns-server-timeout\": 100,"
" \"ncr-protocol\": \"UDP\","
" \"ncr-format\": \"JSON\","
" \"tsig-keys\": [ ],"
" \"forward-ddns\": { },"
" \"reverse-ddns\": { } } }";
EXPECT_TRUE(executeConfiguration(config, "reset config"));
}
boost::scoped_ptr<D2CfgMgr> srv_; ///< D2 server under test
int rcode_; ///< Return code from element parsing
ConstElementPtr comment_; ///< Reason for parse fail
};
/// Test a configuration
TEST_F(D2GetConfigTest, sample1) {
// get the sample1 configuration
std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json";
std::string config;
ASSERT_NO_THROW(config = readFile(sample1_file));
// get the expected configuration
std::string expected_file =
std::string(D2_TEST_DATA_DIR) + "/" + "get_config.json";
std::string expected;
ASSERT_NO_THROW(expected = readFile(expected_file));
// execute the sample configuration
ASSERT_TRUE(executeConfiguration(config, "sample1 config"));
// unparse it
D2CfgContextPtr context = srv_->getD2CfgContext();
ConstElementPtr unparsed;
ASSERT_NO_THROW(unparsed = context->toElement());
// dump if wanted else check
if (generate_action) {
std::cerr << "/ Generated Configuration (remove this line)\n";
ASSERT_NO_THROW(expected = prettyPrint(unparsed));
prettyPrint(unparsed, std::cerr, 0, 4);
std::cerr << "\n";
} else {
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCPDDNS(expected, true));
EXPECT_TRUE(isEquivalent(unparsed, json));
std::string current = prettyPrint(unparsed, 0, 4) + "\n";
EXPECT_EQ(expected, current);
if (expected != current) {
expected = current;
}
}
// execute the d2 configuration
EXPECT_TRUE(executeConfiguration(expected, "unparsed config"));
// is it a fixed point?
D2CfgContextPtr context2 = srv_->getD2CfgContext();
ConstElementPtr unparsed2;
ASSERT_NO_THROW(unparsed2 = context2->toElement());
ASSERT_TRUE(unparsed2);
EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
}
{
"DhcpDdns": {
"dns-server-timeout": 1000,
"forward-ddns": {
"ddns-domains": [
{
"dns-servers": [
{
"hostname": "",
"ip-address": "172.16.1.1",
"port": 53
}
],
"key-name": "d2.md5.key",
"name": "four.example.com."
},
{
"dns-servers": [
{
"hostname": "",
"ip-address": "2001:db8:1::10",
"port": 7802
}
</