mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 14:05:33 +00:00
[3033] Added DHCP-DDNS configuration paramater parsing to b10-dhcp4
Added configuration paramters to dhcp4 and its spec file to support DHCP-DDNS. Created new classes D2ClientMgr, D2ClientConfig, and D2CientConfigParser in the libdhcpsrv. The new parameters are parsed, validated, and stored but do not yet affect behavior. That will be implemented as a seperate ticket.
This commit is contained in:
@@ -1365,6 +1365,7 @@ AC_CONFIG_FILES([compatcheck/Makefile
|
||||
src/bin/dhcp4/spec_config.h.pre
|
||||
src/bin/dhcp4/tests/Makefile
|
||||
src/bin/dhcp4/tests/marker_file.h
|
||||
src/bin/dhcp4/tests/test_data_files_config.h
|
||||
src/bin/dhcp4/tests/test_libraries.h
|
||||
src/bin/dhcp6/Makefile
|
||||
src/bin/dhcp6/spec_config.h.pre
|
||||
|
@@ -396,6 +396,8 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
|
||||
parser = new HooksLibrariesParser(config_id);
|
||||
} else if (config_id.compare("echo-client-id") == 0) {
|
||||
parser = new BooleanParser(config_id, globalContext()->boolean_values_);
|
||||
} else if (config_id.compare("dhcp-ddns") == 0) {
|
||||
parser = new D2ClientConfigParser(config_id);
|
||||
} else {
|
||||
isc_throw(NotImplemented,
|
||||
"Parser error: Global configuration parameter not supported: "
|
||||
@@ -448,7 +450,7 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
// Some of the parsers alter the state of the system in a way that can't
|
||||
// easily be undone. (Or alter it in a way such that undoing the change has
|
||||
// the same risk of failure as doing the change.)
|
||||
ParserPtr hooks_parser_;
|
||||
ParserPtr hooks_parser;
|
||||
|
||||
// The subnet parsers implement data inheritance by directly
|
||||
// accessing global storage. For this reason the global data
|
||||
@@ -489,7 +491,7 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
// Executing commit will alter currently-loaded hooks
|
||||
// libraries. Check if the supplied libraries are valid,
|
||||
// but defer the commit until everything else has committed.
|
||||
hooks_parser_ = parser;
|
||||
hooks_parser = parser;
|
||||
parser->build(config_pair.second);
|
||||
} else {
|
||||
// Those parsers should be started before other
|
||||
@@ -557,8 +559,8 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
// This occurs last as if it succeeds, there is no easy way
|
||||
// revert it. As a result, the failure to commit a subsequent
|
||||
// change causes problems when trying to roll back.
|
||||
if (hooks_parser_) {
|
||||
hooks_parser_->commit();
|
||||
if (hooks_parser) {
|
||||
hooks_parser->commit();
|
||||
}
|
||||
}
|
||||
catch (const isc::Exception& ex) {
|
||||
|
@@ -292,7 +292,110 @@
|
||||
}
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{ "item_name": "dhcp-ddns",
|
||||
"item_type": "map",
|
||||
"item_optional": false,
|
||||
"item_default": {"enable-updates": false},
|
||||
"map_item_spec": [
|
||||
{
|
||||
"item_name": "enable-updates",
|
||||
"item_type": "boolean",
|
||||
"item_optional": false,
|
||||
"item_default": False,
|
||||
"item_description" : "Enables DDNS update processing"
|
||||
},
|
||||
{
|
||||
"item_name": "server_ip",
|
||||
"item_type": "string",
|
||||
"item_optional": true,
|
||||
"item_default": "127.0.0.1",
|
||||
"item_description" : "IP address of b10-dhcp-ddns"
|
||||
},
|
||||
{
|
||||
"item_name": "server_port",
|
||||
"item_type": "integer",
|
||||
"item_optional": true,
|
||||
"item_default": 5301,
|
||||
"item_description" : "port number of b10-dhcp-ddns"
|
||||
},
|
||||
{
|
||||
"item_name": "ncr_protocol",
|
||||
"item_type": "string",
|
||||
"item_optional": true,
|
||||
"item_default": "UDP",
|
||||
"item_description" : "Socket protocol to use with b10-dhcp-ddns"
|
||||
},
|
||||
{
|
||||
"item_name": "ncr_format",
|
||||
"item_type": "string",
|
||||
"item_optional": true,
|
||||
"item_default": "JSON",
|
||||
"item_description" : "Format of the update request packet"
|
||||
},
|
||||
{
|
||||
"item_name": "remove-on-renew",
|
||||
"item_type": "boolean",
|
||||
"item_optional": true,
|
||||
"item_default": false,
|
||||
"item_description": "Should server request a DNS Remove, before a DNS Update on renewals"
|
||||
},
|
||||
{
|
||||
|
||||
"item_name": "always-include-fqdn",
|
||||
"item_type": "boolean",
|
||||
"item_optional": true,
|
||||
"item_default": False,
|
||||
"item_description": "Should server always include the FQDN option in its response"
|
||||
},
|
||||
{
|
||||
|
||||
"item_name": "allow-client-update",
|
||||
"item_type": "boolean",
|
||||
"item_optional": true,
|
||||
"item_default": False,
|
||||
"item_description": "Enable AAAA RR update delegation to the client"
|
||||
},
|
||||
{
|
||||
"item_name": "override-no-update",
|
||||
"item_type": "boolean",
|
||||
"item_optional": true,
|
||||
"item_default": false,
|
||||
"item_description": "Do update, even if client requested no updates with N flag"
|
||||
},
|
||||
{
|
||||
"item_name": "override-client-update",
|
||||
"item_type": "boolean",
|
||||
"item_optional": true,
|
||||
"item_default": true,
|
||||
"item_description": "Server performs an update even if client requested delegation"
|
||||
},
|
||||
{
|
||||
"item_name": "replace-client-name",
|
||||
"item_type": "boolean",
|
||||
"item_optional": true,
|
||||
"item_default": false,
|
||||
"item_description": "Should server replace the domain-name supplied by the client"
|
||||
},
|
||||
{
|
||||
"item_name": "generated-prefix",
|
||||
"item_type": "string",
|
||||
"item_optional": true,
|
||||
"item_default": "myhost",
|
||||
"item_description": "Prefix to use when generating the client's name"
|
||||
},
|
||||
|
||||
{
|
||||
"item_name": "qualifying-suffix",
|
||||
"item_type": "string",
|
||||
"item_optional": true,
|
||||
"item_default": "example.com",
|
||||
"item_description": "Fully qualified domain-name suffix if partial name provided by client"
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
|
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "marker_file.h"
|
||||
#include "test_libraries.h"
|
||||
#include "test_data_files_config.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
@@ -50,6 +51,22 @@ using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Prepends the given name with the DHCP4 source directory
|
||||
///
|
||||
/// @param name file name of the desired file
|
||||
/// @return string containing the absolute path of the file in the DHCP source
|
||||
/// directory.
|
||||
std::string specfile(const std::string& name) {
|
||||
return (std::string(DHCP4_SRC_DIR) + "/" + name);
|
||||
}
|
||||
|
||||
/// @brief Tests that the spec file is valid.
|
||||
/// Verifies that the BIND10 DHCP-DDNS configuration specification file
|
||||
// is valid.
|
||||
TEST(Dhcp4SpecTest, basicSpec) {
|
||||
ASSERT_NO_THROW(isc::config::moduleSpecFromFile(specfile("dhcp4.spec")));
|
||||
}
|
||||
|
||||
class Dhcp4ParserTest : public ::testing::Test {
|
||||
public:
|
||||
Dhcp4ParserTest()
|
||||
@@ -321,6 +338,7 @@ public:
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"valid-lifetime\": 4000, "
|
||||
"\"subnet4\": [ ], "
|
||||
"\"dhcp-ddns\": { \"enable-updates\" : false }, "
|
||||
"\"option-def\": [ ], "
|
||||
"\"option-data\": [ ] }";
|
||||
static_cast<void>(executeConfiguration(config,
|
||||
@@ -2299,6 +2317,117 @@ TEST_F(Dhcp4ParserTest, allInterfaces) {
|
||||
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth2"));
|
||||
}
|
||||
|
||||
// This test checks the ability of the server to parse a configuration
|
||||
// containing a full, valid dhcp-ddns (D2ClientConfig) entry.
|
||||
TEST_F(Dhcp4ParserTest, d2ClientConfig) {
|
||||
ConstElementPtr status;
|
||||
|
||||
// Verify that the D2 configuraiton can be fetched and is set to disabled.
|
||||
D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
|
||||
EXPECT_FALSE(d2_client_config->getEnableUpdates());
|
||||
|
||||
// Verify that the convenience method agrees.
|
||||
ASSERT_FALSE(CfgMgr::instance().isDhcpDdnsEnabled());
|
||||
|
||||
string config_str = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
||||
" \"subnet\": \"192.0.2.0/24\" } ],"
|
||||
" \"dhcp-ddns\" : {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-ip\" : \"192.168.2.1\", "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"UDP\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" },"
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
|
||||
// Convert the JSON string to configuration elements.
|
||||
ElementPtr config;
|
||||
ASSERT_NO_THROW(config = Element::fromJSON(config_str));
|
||||
|
||||
// Pass the configuration in for parsing.
|
||||
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
|
||||
|
||||
// check if returned status is OK
|
||||
checkResult(status, 0);
|
||||
|
||||
// Verify that DHCP-DDNS updating is enabled.
|
||||
EXPECT_TRUE(CfgMgr::instance().isDhcpDdnsEnabled());
|
||||
|
||||
// Verify that the D2 configuration can be retrieved.
|
||||
d2_client_config = CfgMgr::instance().getD2ClientConfig();
|
||||
ASSERT_TRUE(d2_client_config);
|
||||
|
||||
// Verify that the configuration values are correct.
|
||||
EXPECT_TRUE(d2_client_config->getEnableUpdates());
|
||||
EXPECT_EQ("192.168.2.1", d2_client_config->getServerIp().toText());
|
||||
EXPECT_EQ(5301, d2_client_config->getServerPort());
|
||||
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
|
||||
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
|
||||
EXPECT_TRUE(d2_client_config->getRemoveOnRenew());
|
||||
EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
|
||||
EXPECT_TRUE(d2_client_config->getAllowClientUpdate());
|
||||
EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
|
||||
EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
|
||||
EXPECT_TRUE(d2_client_config->getReplaceClientName());
|
||||
EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
|
||||
EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
|
||||
}
|
||||
|
||||
// This test checks the ability of the server to handle a configuration
|
||||
// containing an invalid dhcp-ddns (D2ClientConfig) entry.
|
||||
TEST_F(Dhcp4ParserTest, invalidD2ClientConfig) {
|
||||
ConstElementPtr status;
|
||||
|
||||
// Configuration string with an invalid D2 client config,
|
||||
// "server-ip" is missing.
|
||||
string config_str = "{ \"interfaces\": [ \"*\" ],"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet4\": [ { "
|
||||
" \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
||||
" \"subnet\": \"192.0.2.0/24\" } ],"
|
||||
" \"dhcp-ddns\" : {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"UDP\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" },"
|
||||
"\"valid-lifetime\": 4000 }";
|
||||
|
||||
// Convert the JSON string to configuration elements.
|
||||
ElementPtr config;
|
||||
ASSERT_NO_THROW(config = Element::fromJSON(config_str));
|
||||
|
||||
// Configuration should not throw, but should fail.
|
||||
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
|
||||
|
||||
// check if returned status is failed.
|
||||
checkResult(status, 1);
|
||||
|
||||
// Verify that the D2 configuraiton can be fetched and is set to disabled.
|
||||
D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
|
||||
EXPECT_FALSE(d2_client_config->getEnableUpdates());
|
||||
|
||||
// Verify that the convenience method agrees.
|
||||
ASSERT_FALSE(CfgMgr::instance().isDhcpDdnsEnabled());
|
||||
}
|
||||
|
||||
}
|
||||
|
17
src/bin/dhcp4/tests/test_data_files_config.h.in
Normal file
17
src/bin/dhcp4/tests/test_data_files_config.h.in
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
/// @brief Path to dhcp4 source dir so tests against the dhcp4.spec file
|
||||
/// can find it reliably.
|
||||
#define DHCP4_SRC_DIR "@abs_top_srcdir@/src/bin/dhcp4"
|
@@ -18,6 +18,32 @@
|
||||
namespace isc {
|
||||
namespace dhcp_ddns {
|
||||
|
||||
NameChangeProtocol stringToNcrProtocol(const std::string& protocol_str) {
|
||||
if (protocol_str == "UDP") {
|
||||
return NCR_UDP;
|
||||
}
|
||||
|
||||
if (protocol_str == "TCP") {
|
||||
return NCR_TCP;
|
||||
}
|
||||
|
||||
isc_throw(BadValue, "Invalid NameChangeRequest protocol:" << protocol_str);
|
||||
}
|
||||
|
||||
std::string ncrProtocolToString(NameChangeProtocol protocol) {
|
||||
switch (protocol) {
|
||||
case NCR_UDP:
|
||||
return ("UDP");
|
||||
case NCR_TCP:
|
||||
return ("TCP");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ("UNKNOWN");
|
||||
}
|
||||
|
||||
|
||||
//************************** NameChangeListener ***************************
|
||||
|
||||
NameChangeListener::NameChangeListener(RequestReceiveHandler&
|
||||
|
@@ -66,6 +66,31 @@
|
||||
namespace isc {
|
||||
namespace dhcp_ddns {
|
||||
|
||||
/// @brief Defines the list of socket protocols supported.
|
||||
enum NameChangeProtocol {
|
||||
NCR_UDP,
|
||||
NCR_TCP
|
||||
};
|
||||
|
||||
/// @brief Function which converts labels to NameChangeProtocol enum values.
|
||||
///
|
||||
/// @param protocol_str text to convert to an enum.
|
||||
/// Valid string values: "UDP", "TCP"
|
||||
///
|
||||
/// @return NameChangeProtocol value which maps to the given string.
|
||||
///
|
||||
/// @throw isc::BadValue if given a string value which does not map to an
|
||||
/// enum value.
|
||||
extern NameChangeProtocol stringToNcrProtocol(const std::string& protocol_str);
|
||||
|
||||
/// @brief Function which converts NameChangeProtocol enums to text labels.
|
||||
///
|
||||
/// @param protocol enum value to convert to label
|
||||
///
|
||||
/// @return std:string containing the text label if the value is valid, or
|
||||
/// "UNKNOWN" if not.
|
||||
extern std::string ncrProtocolToString(NameChangeProtocol protocol);
|
||||
|
||||
/// @brief Exception thrown if an NcrListenerError encounters a general error.
|
||||
class NcrListenerError : public isc::Exception {
|
||||
public:
|
||||
|
@@ -26,6 +26,22 @@
|
||||
namespace isc {
|
||||
namespace dhcp_ddns {
|
||||
|
||||
NameChangeFormat stringToNcrFormat(const std::string& fmt_str) {
|
||||
if (fmt_str == "JSON") {
|
||||
return FMT_JSON;
|
||||
}
|
||||
|
||||
isc_throw(BadValue, "Invalid NameChangeRequest format:" << fmt_str);
|
||||
}
|
||||
|
||||
|
||||
std::string ncrFormatToString(NameChangeFormat format) {
|
||||
if (format == FMT_JSON) {
|
||||
return ("JSON");
|
||||
}
|
||||
|
||||
return ("UNKNOWN");
|
||||
}
|
||||
|
||||
/********************************* D2Dhcid ************************************/
|
||||
|
||||
|
@@ -70,6 +70,25 @@ enum NameChangeFormat {
|
||||
FMT_JSON
|
||||
};
|
||||
|
||||
/// @brief Function which converts labels to NameChangeFormat enum values.
|
||||
///
|
||||
/// @param format_str text to convert to an enum.
|
||||
/// Valid string values: "JSON"
|
||||
///
|
||||
/// @return NameChangeFormat value which maps to the given string.
|
||||
///
|
||||
/// @throw isc::BadValue if given a string value which does not map to an
|
||||
/// enum value.
|
||||
extern NameChangeFormat stringToNcrFormat(const std::string& fmt_str);
|
||||
|
||||
/// @brief Function which converts NameChangeFormat enums to text labels.
|
||||
///
|
||||
/// @param format enum value to convert to label
|
||||
///
|
||||
/// @return std:string containing the text label if the value is valid, or
|
||||
/// "UNKNOWN" if not.
|
||||
extern std::string ncrFormatToString(NameChangeFormat format);
|
||||
|
||||
/// @brief Container class for handling the DHCID value within a
|
||||
/// NameChangeRequest. It provides conversion to and from string for JSON
|
||||
/// formatting, but stores the data internally as unsigned bytes.
|
||||
|
@@ -12,7 +12,7 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <dhcp_ddns/ncr_msg.h>
|
||||
#include <dhcp_ddns/ncr_io.h>
|
||||
#include <dhcp/duid.h>
|
||||
#include <dhcp/hwaddr.h>
|
||||
#include <util/time_utilities.h>
|
||||
@@ -608,5 +608,23 @@ TEST(NameChangeRequestTest, ipAddresses) {
|
||||
ASSERT_THROW(ncr.setIpAddress("x001:1::f3"),NcrMessageError);
|
||||
}
|
||||
|
||||
/// @brief Tests conversion of NameChangeFormat between enum and strings.
|
||||
TEST(NameChangeFormatTest, formatEnumConversion){
|
||||
ASSERT_EQ(stringToNcrFormat("JSON"), dhcp_ddns::FMT_JSON);
|
||||
ASSERT_THROW(stringToNcrFormat("bogus"), isc::BadValue);
|
||||
|
||||
ASSERT_EQ(ncrFormatToString(dhcp_ddns::FMT_JSON), "JSON");
|
||||
}
|
||||
|
||||
/// @brief Tests conversion of NameChangeProtocol between enum and strings.
|
||||
TEST(NameChangeProtocolTest, protocolEnumConversion){
|
||||
ASSERT_EQ(stringToNcrProtocol("UDP"), dhcp_ddns::NCR_UDP);
|
||||
ASSERT_EQ(stringToNcrProtocol("TCP"), dhcp_ddns::NCR_TCP);
|
||||
ASSERT_THROW(stringToNcrProtocol("bogus"), isc::BadValue);
|
||||
|
||||
ASSERT_EQ(ncrProtocolToString(dhcp_ddns::NCR_UDP), "UDP");
|
||||
ASSERT_EQ(ncrProtocolToString(dhcp_ddns::NCR_TCP), "TCP");
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
|
@@ -39,6 +39,7 @@ libb10_dhcpsrv_la_SOURCES =
|
||||
libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
|
||||
libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
|
||||
libb10_dhcpsrv_la_SOURCES += callout_handle_store.h
|
||||
libb10_dhcpsrv_la_SOURCES += d2_client.cc d2_client.h
|
||||
libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
|
||||
libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
|
||||
libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
|
||||
@@ -64,6 +65,7 @@ libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
|
||||
libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
||||
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
|
||||
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
|
||||
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
|
||||
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
|
||||
|
@@ -348,9 +348,26 @@ CfgMgr::getUnicast(const std::string& iface) const {
|
||||
return (&(*addr).second);
|
||||
}
|
||||
|
||||
void
|
||||
CfgMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
|
||||
d2_client_mgr_.setD2ClientConfig(new_config);
|
||||
}
|
||||
|
||||
bool
|
||||
CfgMgr::isDhcpDdnsEnabled() {
|
||||
return (d2_client_mgr_.isDhcpDdnsEnabled());
|
||||
}
|
||||
|
||||
const D2ClientConfigPtr&
|
||||
CfgMgr::getD2ClientConfig() const {
|
||||
return (d2_client_mgr_.getD2ClientConfig());
|
||||
}
|
||||
|
||||
|
||||
CfgMgr::CfgMgr()
|
||||
: datadir_(DHCP_DATA_DIR),
|
||||
all_ifaces_active_(false), echo_v4_client_id_(true) {
|
||||
all_ifaces_active_(false), echo_v4_client_id_(true),
|
||||
d2_client_mgr_() {
|
||||
// DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
|
||||
// Note: the definition of DHCP_DATA_DIR needs to include quotation marks
|
||||
// See AM_CPPFLAGS definition in Makefile.am
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_definition.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <dhcpsrv/d2_client.h>
|
||||
#include <dhcpsrv/option_space_container.h>
|
||||
#include <dhcpsrv/pool.h>
|
||||
#include <dhcpsrv/subnet.h>
|
||||
@@ -332,6 +333,24 @@ public:
|
||||
return (echo_v4_client_id_);
|
||||
}
|
||||
|
||||
/// @brief Updates the DHCP-DDNS client configuration to the given value.
|
||||
///
|
||||
/// @param new_config pointer to the new client configuration.
|
||||
///
|
||||
/// @throw Underlying method(s) will throw D2ClientError if given an empty
|
||||
/// pointer.
|
||||
void setD2ClientConfig(D2ClientConfigPtr& new_config);
|
||||
|
||||
/// @param Convenience method for checking if DHCP-DDNS updates are enabled.
|
||||
///
|
||||
/// @return True if the D2 configuration is enabled.
|
||||
bool isDhcpDdnsEnabled();
|
||||
|
||||
/// @brief Fetches the DHCP-DDNS configuration pointer.
|
||||
///
|
||||
/// @return a reference to the current configuration pointer.
|
||||
const D2ClientConfigPtr& getD2ClientConfig() const;
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Protected constructor.
|
||||
@@ -411,6 +430,9 @@ private:
|
||||
|
||||
/// Indicates whether v4 server should send back client-id
|
||||
bool echo_v4_client_id_;
|
||||
|
||||
/// @brief Manages the DHCP-DDNS client and its configuration.
|
||||
D2ClientMgr d2_client_mgr_;
|
||||
};
|
||||
|
||||
} // namespace isc::dhcp
|
||||
|
187
src/lib/dhcpsrv/d2_client.cc
Normal file
187
src/lib/dhcpsrv/d2_client.cc
Normal file
@@ -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 <dhcpsrv/d2_client.h>
|
||||
#include <dhcpsrv/dhcpsrv_log.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
D2ClientConfig::D2ClientConfig(const bool enable_updates,
|
||||
const isc::asiolink::IOAddress& server_ip,
|
||||
const size_t server_port,
|
||||
const dhcp_ddns::
|
||||
NameChangeProtocol& ncr_protocol,
|
||||
const dhcp_ddns::
|
||||
NameChangeFormat& ncr_format,
|
||||
const bool remove_on_renew,
|
||||
const bool always_include_fqdn,
|
||||
const bool allow_client_update,
|
||||
const bool override_no_update,
|
||||
const bool override_client_update,
|
||||
const bool replace_client_name,
|
||||
const std::string& generated_prefix,
|
||||
const std::string& qualifying_suffix)
|
||||
: enable_updates_(enable_updates),
|
||||
server_ip_(server_ip.getAddress()),
|
||||
server_port_(server_port),
|
||||
ncr_protocol_(ncr_protocol),
|
||||
ncr_format_(ncr_format),
|
||||
remove_on_renew_(remove_on_renew),
|
||||
always_include_fqdn_(always_include_fqdn),
|
||||
allow_client_update_(allow_client_update),
|
||||
override_no_update_(override_no_update),
|
||||
override_client_update_(override_client_update),
|
||||
replace_client_name_(replace_client_name),
|
||||
generated_prefix_(generated_prefix),
|
||||
qualifying_suffix_(qualifying_suffix) {
|
||||
if (ncr_format_ != dhcp_ddns::FMT_JSON) {
|
||||
isc_throw(D2ClientError, "D2ClientConfig: NCR Format:"
|
||||
<< dhcp_ddns::ncrFormatToString(ncr_format)
|
||||
<< " is not yet supported");
|
||||
}
|
||||
|
||||
if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
|
||||
isc_throw(D2ClientError, "D2ClientConfig: NCR Protocol:"
|
||||
<< dhcp_ddns::ncrProtocolToString(ncr_protocol)
|
||||
<< " is not yet supported");
|
||||
}
|
||||
|
||||
// @todo perhaps more validation we should do yet?
|
||||
// Are there any invalid combinations of options we need to test against?
|
||||
// For instance are allow_client_update and override_client_update mutually
|
||||
// exclusive?
|
||||
// Also do we care about validating contents if it's disabled?
|
||||
}
|
||||
|
||||
D2ClientConfig::D2ClientConfig()
|
||||
: enable_updates_(false),
|
||||
server_ip_(isc::asiolink::IOAddress("0.0.0.0")),
|
||||
server_port_(0),
|
||||
ncr_protocol_(dhcp_ddns::NCR_UDP),
|
||||
ncr_format_(dhcp_ddns::FMT_JSON),
|
||||
remove_on_renew_(false),
|
||||
always_include_fqdn_(false),
|
||||
allow_client_update_(false),
|
||||
override_no_update_(false),
|
||||
override_client_update_(false),
|
||||
replace_client_name_(false),
|
||||
generated_prefix_(""),
|
||||
qualifying_suffix_("") {
|
||||
}
|
||||
|
||||
D2ClientConfig::~D2ClientConfig(){};
|
||||
|
||||
bool
|
||||
D2ClientConfig::operator == (const D2ClientConfig& other) const {
|
||||
return ((enable_updates_ == other.enable_updates_) &&
|
||||
(server_ip_ == other.server_ip_) &&
|
||||
(server_port_ == other.server_port_) &&
|
||||
(ncr_protocol_ == other.ncr_protocol_) &&
|
||||
(ncr_format_ == other.ncr_format_) &&
|
||||
(remove_on_renew_ == other.remove_on_renew_) &&
|
||||
(always_include_fqdn_ == other.always_include_fqdn_) &&
|
||||
(allow_client_update_ == other.allow_client_update_) &&
|
||||
(override_no_update_ == other.override_no_update_) &&
|
||||
(override_client_update_ == other.override_client_update_) &&
|
||||
(replace_client_name_ == other.replace_client_name_) &&
|
||||
(generated_prefix_ == other.generated_prefix_) &&
|
||||
(qualifying_suffix_ == other.qualifying_suffix_));
|
||||
}
|
||||
|
||||
bool
|
||||
D2ClientConfig::operator != (const D2ClientConfig& other) const {
|
||||
return (!(*this == other));
|
||||
}
|
||||
|
||||
std::string
|
||||
D2ClientConfig::toText() const {
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "enable_updates: " << (enable_updates_ ? "yes" : "no");
|
||||
if (enable_updates_) {
|
||||
stream << ", server_ip: " << server_ip_.toText()
|
||||
<< ", server_port: " << server_port_
|
||||
<< ", ncr_protocol: " << ncr_protocol_
|
||||
<< ", ncr_format: " << ncr_format_
|
||||
<< ", remove_on_renew: " << (remove_on_renew_ ? "yes" : "no")
|
||||
<< ", always_include_fqdn: " << (always_include_fqdn_ ?
|
||||
"yes" : "no")
|
||||
<< ", allow_client_update: " << (allow_client_update_ ?
|
||||
"yes" : "no")
|
||||
<< ", override_no_update: " << (override_no_update_ ?
|
||||
"yes" : "no")
|
||||
<< ", override_client_update: " << (override_client_update_ ?
|
||||
"yes" : "no")
|
||||
<< ", replace_client_name: " << (replace_client_name_ ?
|
||||
"yes" : "no")
|
||||
<< ", generated_prefix: [" << generated_prefix_ << "]"
|
||||
<< ", qualifying_suffix: [" << qualifying_suffix_ << "]";
|
||||
}
|
||||
|
||||
return (stream.str());
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const D2ClientConfig& config) {
|
||||
os << config.toText();
|
||||
return (os);
|
||||
}
|
||||
|
||||
D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()) {
|
||||
// Default contstructor initializes with a disabled config.
|
||||
}
|
||||
|
||||
D2ClientMgr::~D2ClientMgr(){
|
||||
}
|
||||
|
||||
void
|
||||
D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
|
||||
if (!new_config) {
|
||||
isc_throw(D2ClientError,
|
||||
"D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
|
||||
}
|
||||
|
||||
// @todo When NameChangeSender is integrated, we will need to handle these
|
||||
// scenarios:
|
||||
// 1. D2 was enabled but now it is disabled
|
||||
// - destroy the sender, flush any queued
|
||||
// 2. D2 is still enabled but server params have changed
|
||||
// - preserve any queued, reconnect based on sender params
|
||||
// 3. D2 was was disabled now it is enabled.
|
||||
// - create sender
|
||||
//
|
||||
// For now we just update the configuration.
|
||||
d2_client_config_ = new_config;
|
||||
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
|
||||
.arg(!isDhcpDdnsEnabled() ? "DHCP-DDNS updates disabled" :
|
||||
"DHCP_DDNS updates enabled");
|
||||
}
|
||||
|
||||
bool
|
||||
D2ClientMgr::isDhcpDdnsEnabled() {
|
||||
return (d2_client_config_->getEnableUpdates());
|
||||
}
|
||||
|
||||
const D2ClientConfigPtr&
|
||||
D2ClientMgr::getD2ClientConfig() const {
|
||||
return (d2_client_config_);
|
||||
}
|
||||
|
||||
}; // namespace dhcp
|
||||
}; // namespace isc
|
280
src/lib/dhcpsrv/d2_client.h
Normal file
280
src/lib/dhcpsrv/d2_client.h
Normal file
@@ -0,0 +1,280 @@
|
||||
// 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.
|
||||
|
||||
#ifndef D2_CLIENT_H
|
||||
#define D2_CLIENT_H
|
||||
|
||||
/// @file d2_client.h Defines the D2ClientConfig and D2ClientMgr classes.
|
||||
/// This file defines the classes Kea uses to act as a client of the b10-
|
||||
/// dhcp-ddns module (aka D2).
|
||||
///
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp_ddns/ncr_io.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
|
||||
/// An exception that is thrown if an error occurs while configuring
|
||||
/// the D2 DHCP DDNS client.
|
||||
class D2ClientError : public isc::Exception {
|
||||
public:
|
||||
|
||||
/// @brief constructor
|
||||
///
|
||||
/// @param file name of the file, where exception occurred
|
||||
/// @param line line of the file, where exception occurred
|
||||
/// @param what text description of the issue that caused exception
|
||||
D2ClientError(const char* file, size_t line, const char* what)
|
||||
: isc::Exception(file, line, what) {}
|
||||
};
|
||||
|
||||
/// @brief Acts as a storage vault for D2 client configuration
|
||||
///
|
||||
/// A simple container class for storing and retrieving the configuration
|
||||
/// parameters associated with DHCP-DDNS and acting as a client of D2.
|
||||
/// Instances of this class may be constructed through configuration parsing.
|
||||
///
|
||||
class D2ClientConfig {
|
||||
public:
|
||||
/// @brief Constructor
|
||||
///
|
||||
/// @param enable_updates Enables DHCP-DDNS updates
|
||||
/// @param server_ip IP address of the b10-dhcp-ddns server
|
||||
/// @param server_port IP port of the b10-dhcp-ddns server
|
||||
/// @param ncr_protocol Socket protocol to use with b10-dhcp-ddns
|
||||
/// Currently only UDP is supported.
|
||||
/// @param ncr_format Format of the b10-dhcp-ddns requests.
|
||||
/// Currently only JSON format is supported.
|
||||
/// @param remove_on_renew Enables DNS Removes when renewing a lease
|
||||
/// If true, Kea should request an explicit DNS remove prior to requesting
|
||||
/// a DNS update when renewing a lease.
|
||||
/// (Note: b10-dhcp-ddns is implemented per RFC 4703 and such a remove
|
||||
/// is unnecessary).
|
||||
/// @param always_include_fdqn Enables always including the FQDN option in
|
||||
/// DHCP responses.
|
||||
/// @param allow_client_update Enables delegation of updates to clients
|
||||
/// @param override_no_update Enables updates, even if clients request no
|
||||
/// updates.
|
||||
/// @param override_client_update Perform updates, even if client requested
|
||||
/// delegation.
|
||||
/// @param replace_client_name enables replacement of the domain-name
|
||||
/// supplied by the client with a generated name.
|
||||
/// @param generated_prefix Prefix to use when generating domain-names.
|
||||
/// @param qualifying_suffix Suffix to use to qualify partial domain-names.
|
||||
///
|
||||
/// @throw D2ClientError if given an invalid protocol or format.
|
||||
D2ClientConfig(const bool enable_updates_,
|
||||
const isc::asiolink::IOAddress& server_ip_,
|
||||
const size_t server_port_,
|
||||
const dhcp_ddns::NameChangeProtocol& ncr_protocol_,
|
||||
const dhcp_ddns::NameChangeFormat& ncr_format_,
|
||||
const bool remove_on_renew_,
|
||||
const bool always_include_fqdn_,
|
||||
const bool allow_client_update_,
|
||||
const bool override_no_update_,
|
||||
const bool override_client_update_,
|
||||
const bool replace_client_name_,
|
||||
const std::string& generated_prefix_,
|
||||
const std::string& qualifying_suffix_);
|
||||
|
||||
/// @brief Default constructor
|
||||
/// The default constructor creates an instance that has updates disabled.
|
||||
D2ClientConfig();
|
||||
|
||||
/// @brief Destructor
|
||||
virtual ~D2ClientConfig();
|
||||
|
||||
/// @brief Return whether or not DHCP-DDNS updating is enabled.
|
||||
bool getEnableUpdates() const {
|
||||
return(enable_updates_);
|
||||
}
|
||||
|
||||
/// @brief Return the IP address of b10-dhcp-ddns.
|
||||
const isc::asiolink::IOAddress& getServerIp() const {
|
||||
return(server_ip_);
|
||||
}
|
||||
|
||||
/// @brief Return the IP port of b10-dhcp-ddns.
|
||||
size_t getServerPort() const {
|
||||
return(server_port_);
|
||||
}
|
||||
|
||||
/// @brief Return the socket protocol to use with b10-dhcp-ddns.
|
||||
const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const {
|
||||
return(ncr_protocol_);
|
||||
}
|
||||
|
||||
/// @brief Return the b10-dhcp-ddns request format.
|
||||
const dhcp_ddns::NameChangeFormat& getNcrFormat() const {
|
||||
return(ncr_format_);
|
||||
}
|
||||
|
||||
/// @brief Return whether or not removes should be sent for lease renewals.
|
||||
bool getRemoveOnRenew() const {
|
||||
return(remove_on_renew_);
|
||||
}
|
||||
|
||||
/// @brief Return whether or not FQDN is always included in DHCP responses.
|
||||
bool getAlwaysIncludeFqdn() const {
|
||||
return(always_include_fqdn_);
|
||||
}
|
||||
|
||||
/// @brief Return whether or not updates can be delegated to clients.
|
||||
bool getAllowClientUpdate() const {
|
||||
return(allow_client_update_);
|
||||
}
|
||||
|
||||
/// @brief Return if updates are done even if clients request no updates.
|
||||
bool getOverrideNoUpdate() const {
|
||||
return(override_no_update_);
|
||||
}
|
||||
|
||||
/// @brief Return if updates are done even when clients request delegation.
|
||||
bool getOverrideClientUpdate() const {
|
||||
return(override_client_update_);
|
||||
}
|
||||
|
||||
/// @brief Return whether or not client's domain-name is always replaced.
|
||||
bool getReplaceClientName() const {
|
||||
return(replace_client_name_);
|
||||
}
|
||||
|
||||
/// @brief Return the prefix to use when generating domain-names.
|
||||
const std::string& getGeneratedPrefix() const {
|
||||
return(generated_prefix_);
|
||||
}
|
||||
|
||||
/// @brief Return the suffix to use to qualify partial domain-names.
|
||||
const std::string& getQualifyingSuffix() const {
|
||||
return(qualifying_suffix_);
|
||||
}
|
||||
|
||||
/// @brief Compares two D2ClientConfigs for equality
|
||||
bool operator == (const D2ClientConfig& other) const;
|
||||
|
||||
/// @brief Compares two D2ClientConfigs for inequality
|
||||
bool operator != (const D2ClientConfig& other) const;
|
||||
|
||||
/// @brief Generates a string representation of the class contents.
|
||||
std::string toText() const;
|
||||
|
||||
private:
|
||||
/// @brief Indicates whether or not DHCP DDNS updating is enabled.
|
||||
bool enable_updates_;
|
||||
|
||||
/// @brief IP address of the b10-dhcp-ddns server.
|
||||
isc::asiolink::IOAddress server_ip_;
|
||||
|
||||
/// @brief IP port of the b10-dhcp-ddns server.
|
||||
size_t server_port_;
|
||||
|
||||
/// @brief The socket protocol to use with b10-dhcp-ddns.
|
||||
/// Currently only UPD is supported.
|
||||
dhcp_ddns::NameChangeProtocol ncr_protocol_;
|
||||
|
||||
/// @brief Format of the b10-dhcp-ddns requests.
|
||||
/// Currently only JSON format is supported.
|
||||
dhcp_ddns::NameChangeFormat ncr_format_;
|
||||
|
||||
/// @brief Should Kea request a DNS Remove when renewing a lease.
|
||||
/// If true, Kea should request an explicit DNS remove prior to requesting
|
||||
/// a DNS update when renewing a lease.
|
||||
/// (Note: b10-dhcp-ddns is implemented per RFC 4703 and such a remove
|
||||
/// is unnecessary).
|
||||
bool remove_on_renew_;
|
||||
|
||||
/// @brief Should Kea always include the FQDN option in its response.
|
||||
bool always_include_fqdn_;
|
||||
|
||||
/// @brief Should Kea permit the client to do updates.
|
||||
bool allow_client_update_;
|
||||
|
||||
/// @brief Should Kea perform updates, even if client requested no updates.
|
||||
/// Overrides the client request for no updates via the N flag.
|
||||
bool override_no_update_;
|
||||
|
||||
/// @brief Should Kea perform updates, even if client requested delegation.
|
||||
bool override_client_update_;
|
||||
|
||||
/// @brief Should Kea replace the domain-name supplied by the client.
|
||||
bool replace_client_name_;
|
||||
|
||||
/// @brief Prefix Kea should use when generating domain-names.
|
||||
std::string generated_prefix_;
|
||||
|
||||
/// @brief Suffix Kea should use when to qualify partial domain-names.
|
||||
std::string qualifying_suffix_;
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, const D2ClientConfig& config);
|
||||
|
||||
/// @brief Defines a pointer for D2ClientConfig instances.
|
||||
typedef boost::shared_ptr<D2ClientConfig> D2ClientConfigPtr;
|
||||
|
||||
/// @brief D2ClientMgr isolates Kea from the details of being a D2 client.
|
||||
///
|
||||
/// Provides services for managing the current D2ClientConfig and managing
|
||||
/// communications with D2. (@todo The latter will be added once communication
|
||||
/// with D2 is implemented through the integration of
|
||||
/// dhcp_ddns::NameChangeSender interface(s).
|
||||
///
|
||||
class D2ClientMgr {
|
||||
public:
|
||||
/// @brief Constructor
|
||||
///
|
||||
/// Default constructor which constructs an instance which has DHCP-DDNS
|
||||
/// updates disabled.
|
||||
D2ClientMgr();
|
||||
|
||||
/// @brief Destructor.
|
||||
~D2ClientMgr();
|
||||
|
||||
/// @brief Updates the DHCP-DDNS client configuration to the given value.
|
||||
///
|
||||
/// @param new_config pointer to the new client configuration.
|
||||
/// @throw D2ClientError if passed an empty pointer.
|
||||
void setD2ClientConfig(D2ClientConfigPtr& new_config);
|
||||
|
||||
/// @param Convenience method for checking if DHCP-DDNS updates are enabled.
|
||||
///
|
||||
/// @return True if the D2 configuration is enabled.
|
||||
bool isDhcpDdnsEnabled();
|
||||
|
||||
/// @brief Fetches the DHCP-DDNS configuration pointer.
|
||||
///
|
||||
/// @return a reference to the current configuration pointer.
|
||||
const D2ClientConfigPtr& getD2ClientConfig() const;
|
||||
|
||||
private:
|
||||
/// @brief Container class for DHCP-DDNS configuration parameters.
|
||||
D2ClientConfigPtr d2_client_config_;
|
||||
};
|
||||
|
||||
/// @brief Defines a pointer for D2ClientMgr instances.
|
||||
typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
|
||||
|
||||
|
||||
} // namespace isc
|
||||
} // namespace dhcp
|
||||
|
||||
#endif
|
@@ -1163,5 +1163,111 @@ SubnetConfigParser::getParam(const std::string& name) {
|
||||
return (Triplet<uint32_t>(value));
|
||||
}
|
||||
|
||||
//**************************** D2ClientConfigParser **********************
|
||||
D2ClientConfigParser::D2ClientConfigParser(const std::string& entry_name)
|
||||
: entry_name_(entry_name), boolean_values_(new BooleanStorage()),
|
||||
uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
|
||||
local_client_config_() {
|
||||
}
|
||||
|
||||
D2ClientConfigParser::~D2ClientConfigParser() {
|
||||
}
|
||||
|
||||
void
|
||||
D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
|
||||
BOOST_FOREACH(ConfigPair param, client_config->mapValue()) {
|
||||
ParserPtr parser(createConfigParser(param.first));
|
||||
parser->build(param.second);
|
||||
parser->commit();
|
||||
}
|
||||
|
||||
bool enable_updates = boolean_values_->getParam("enable-updates");
|
||||
if (!enable_updates) {
|
||||
// If it's not enabled, don't bother validating the rest. This
|
||||
// allows for an abbreviated config entry that only contains
|
||||
// the flag. The default constructor creates a disabled instance.
|
||||
local_client_config_.reset(new D2ClientConfig());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all parameters that are needed to create the D2ClientConfig.
|
||||
asiolink::IOAddress server_ip(string_values_->getParam("server-ip"));
|
||||
|
||||
uint32_t server_port = uint32_values_->getParam("server-port");
|
||||
|
||||
dhcp_ddns::NameChangeProtocol
|
||||
ncr_protocol = dhcp_ddns:: stringToNcrProtocol(string_values_->
|
||||
getParam("ncr-protocol"));
|
||||
|
||||
dhcp_ddns::NameChangeFormat
|
||||
ncr_format = dhcp_ddns::stringToNcrFormat(string_values_->
|
||||
getParam("ncr-format"));
|
||||
|
||||
std::string generated_prefix = string_values_->getParam("generated-prefix");
|
||||
std::string qualifying_suffix = string_values_->
|
||||
getParam("qualifying-suffix");
|
||||
|
||||
bool remove_on_renew = boolean_values_->getParam("remove-on-renew");
|
||||
bool always_include_fqdn = boolean_values_->getParam("always-include-fqdn");
|
||||
bool allow_client_update = boolean_values_->getParam("allow-client-update");
|
||||
bool override_no_update = boolean_values_->getParam("override-no-update");
|
||||
bool override_client_update = boolean_values_->
|
||||
getParam("override-client-update");
|
||||
bool replace_client_name = boolean_values_->getParam("replace-client-name");
|
||||
|
||||
// Attempt to create the new client config.
|
||||
local_client_config_.reset(new D2ClientConfig(enable_updates, server_ip,
|
||||
server_port, ncr_protocol,
|
||||
ncr_format, remove_on_renew,
|
||||
always_include_fqdn,
|
||||
allow_client_update,
|
||||
override_no_update,
|
||||
override_client_update,
|
||||
replace_client_name,
|
||||
generated_prefix,
|
||||
qualifying_suffix));
|
||||
}
|
||||
|
||||
isc::dhcp::ParserPtr
|
||||
D2ClientConfigParser::createConfigParser(const std::string& config_id) {
|
||||
DhcpConfigParser* parser = NULL;
|
||||
if (config_id.compare("server-port") == 0) {
|
||||
parser = new Uint32Parser(config_id, uint32_values_);
|
||||
} else if ((config_id.compare("server-ip") == 0) ||
|
||||
(config_id.compare("ncr-protocol") == 0) ||
|
||||
(config_id.compare("ncr-format") == 0) ||
|
||||
(config_id.compare("generated-prefix") == 0) ||
|
||||
(config_id.compare("qualifying-suffix") == 0)) {
|
||||
parser = new StringParser(config_id, string_values_);
|
||||
} else if ((config_id.compare("enable-updates") == 0) ||
|
||||
(config_id.compare("remove-on-renew") == 0) ||
|
||||
(config_id.compare("always-include-fqdn") == 0) ||
|
||||
(config_id.compare("allow-client-update") == 0) ||
|
||||
(config_id.compare("override-no-update") == 0) ||
|
||||
(config_id.compare("override-client-update") == 0) ||
|
||||
(config_id.compare("replace-client-name") == 0)) {
|
||||
parser = new BooleanParser(config_id, boolean_values_);
|
||||
} else {
|
||||
isc_throw(NotImplemented,
|
||||
"parser error: D2ClientConfig parameter not supported: "
|
||||
<< config_id);
|
||||
}
|
||||
|
||||
return (isc::dhcp::ParserPtr(parser));
|
||||
}
|
||||
|
||||
void
|
||||
D2ClientConfigParser::commit() {
|
||||
// @todo if local_client_config_ is empty then shutdown the listener...
|
||||
// @todo Should this also attempt to start a listener?
|
||||
// In keeping with Interface, Subnet, and Hooks parsers, then this
|
||||
// should initialize the listener. Failure to init it, should cause
|
||||
// rollback. This gets sticky, because who owns the listener instance?
|
||||
// Does CfgMgr maintain it or does the server class? If the latter
|
||||
// how do we get that value here?
|
||||
// I'm thinkikng D2ClientConfig could contain the listener instance
|
||||
CfgMgr::instance().setD2ClientConfig(local_client_config_);
|
||||
}
|
||||
|
||||
}; // namespace dhcp
|
||||
}; // namespace isc
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <asiolink/io_address.h>
|
||||
#include <cc/data.h>
|
||||
#include <dhcp/option_definition.h>
|
||||
#include <dhcpsrv/d2_client.h>
|
||||
#include <dhcpsrv/dhcp_config_parser.h>
|
||||
#include <dhcpsrv/option_space_container.h>
|
||||
#include <dhcpsrv/subnet.h>
|
||||
@@ -243,7 +244,7 @@ public:
|
||||
// its value. If it doesn't we insert a new element.
|
||||
storage_->setParam(param_name_, value_);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/// Pointer to the storage where committed value is stored.
|
||||
boost::shared_ptr<ValueStorage<ValueType> > storage_;
|
||||
@@ -876,6 +877,77 @@ protected:
|
||||
ParserContextPtr global_context_;
|
||||
};
|
||||
|
||||
/// @brief Parser for D2ClientConfig
|
||||
///
|
||||
/// This class parses the configuration element "dhcp-ddns" common to the
|
||||
/// spec files for both dhcp4 and dhcp6. It creates an instance of a
|
||||
/// D2ClientConfig.
|
||||
class D2ClientConfigParser : public isc::dhcp::DhcpConfigParser {
|
||||
public:
|
||||
/// @brief Constructor
|
||||
///
|
||||
/// @param entry_name is an arbitrary label assigned to this configuration
|
||||
/// definition.
|
||||
D2ClientConfigParser(const std::string& entry_name);
|
||||
|
||||
/// @brief Destructor
|
||||
virtual ~D2ClientConfigParser();
|
||||
|
||||
/// @brief Performs the parsing of the given dhcp-ddns element.
|
||||
///
|
||||
/// The results of the parsing are retained internally for use during
|
||||
/// commit.
|
||||
///
|
||||
/// @param client_config is the "dhcp-ddns" configuration to parse
|
||||
virtual void build(isc::data::ConstElementPtr client_config);
|
||||
|
||||
/// @brief Creates a parser for the given "dhcp-ddns" member element id.
|
||||
///
|
||||
/// The elements currently supported are:
|
||||
/// -# enable-updates
|
||||
/// -# server-ip
|
||||
/// -# server-port
|
||||
/// -# ncr-protocol
|
||||
/// -# ncr-format
|
||||
/// -# remove-on-renew
|
||||
/// -# always-include-fqdn
|
||||
/// -# allow-client-update
|
||||
/// -# override-no-update
|
||||
/// -# override-client-update
|
||||
/// -# replace-client-name
|
||||
/// -# generated-prefix
|
||||
/// -# qualifying-suffix
|
||||
/// (see d2::D2ClientConfig for details on each.)
|
||||
///
|
||||
/// @param config_id is the "item_name" for a specific member element of
|
||||
/// the "dns_server" specification.
|
||||
///
|
||||
/// @return returns a pointer to newly created parser.
|
||||
virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
|
||||
config_id);
|
||||
|
||||
/// @brief Instantiates a D2ClientConfig from internal data values
|
||||
/// passes to CfgMgr singleton.
|
||||
virtual void commit();
|
||||
|
||||
private:
|
||||
/// @brief Arbitrary label assigned to this parser instance.
|
||||
/// Primarily used for diagnostics.
|
||||
std::string entry_name_;
|
||||
|
||||
/// Storage for subnet-specific boolean values.
|
||||
BooleanStoragePtr boolean_values_;
|
||||
|
||||
/// Storage for subnet-specific integer values.
|
||||
Uint32StoragePtr uint32_values_;
|
||||
|
||||
/// Storage for subnet-specific string values.
|
||||
StringStoragePtr string_values_;
|
||||
|
||||
/// @brief Pointer to temporary local instance created during build.
|
||||
D2ClientConfigPtr local_client_config_ ;
|
||||
};
|
||||
|
||||
// Pointers to various parser objects.
|
||||
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
|
||||
typedef boost::shared_ptr<StringParser> StringParserPtr;
|
||||
|
@@ -70,6 +70,9 @@ specified IPv6 subnet to its database.
|
||||
A debug message issued when server is being configured to listen on all
|
||||
interfaces.
|
||||
|
||||
% DHCPSRV_CFGMGR_CFG_DHCP_DDNS Setting DHCP-DDNS configuration to: %1
|
||||
A debug message issued when the server's DHCP-DDNS settings are changed.
|
||||
|
||||
% DHCPSRV_CFGMGR_CLEAR_ACTIVE_IFACES stop listening on all interfaces
|
||||
A debug message issued when configuration manager clears the internal list
|
||||
of active interfaces. This doesn't prevent the server from listening to
|
||||
|
@@ -52,6 +52,7 @@ libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += lease_unittest.cc
|
||||
libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
|
||||
@@ -88,6 +89,7 @@ endif
|
||||
|
||||
libdhcpsrv_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
|
||||
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
||||
|
@@ -670,6 +670,48 @@ TEST_F(CfgMgrTest, echoClientId) {
|
||||
EXPECT_TRUE(cfg_mgr.echoClientId());
|
||||
}
|
||||
|
||||
// This test checks the D2ClientMgr wrapper methods.
|
||||
TEST_F(CfgMgrTest, d2ClientConfig) {
|
||||
CfgMgr& cfg_mgr = CfgMgr::instance();
|
||||
|
||||
// After CfgMgr construction, D2 configuration should be disabled.
|
||||
// Fetch it and verify this is the case.
|
||||
D2ClientConfigPtr original_config = CfgMgr::instance().getD2ClientConfig();
|
||||
ASSERT_TRUE(original_config);
|
||||
EXPECT_FALSE(original_config->getEnableUpdates());
|
||||
|
||||
// Make sure convenience method agrees.
|
||||
EXPECT_FALSE(CfgMgr::instance().isDhcpDdnsEnabled());
|
||||
|
||||
// Verify that we cannot set the configuration to an empty pointer.
|
||||
D2ClientConfigPtr new_cfg;
|
||||
ASSERT_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg), D2ClientError);
|
||||
|
||||
// Create a new, enabled configuration.
|
||||
ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
|
||||
isc::asiolink::IOAddress("127.0.0.1"), 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
|
||||
// Verify that we can assign a new, non-empty configuration.
|
||||
ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg));
|
||||
|
||||
// Verify that we can fetch the newly assigned configuration.
|
||||
D2ClientConfigPtr updated_config = CfgMgr::instance().getD2ClientConfig();
|
||||
ASSERT_TRUE(updated_config);
|
||||
EXPECT_TRUE(updated_config->getEnableUpdates());
|
||||
|
||||
// Make sure convenience method agrees with updated configuration.
|
||||
EXPECT_TRUE(CfgMgr::instance().isDhcpDdnsEnabled());
|
||||
|
||||
// Make sure the configuration we fetched is the one we assigned,
|
||||
// and not the original configuration.
|
||||
EXPECT_EQ(*new_cfg, *updated_config);
|
||||
EXPECT_NE(*original_config, *updated_config);
|
||||
}
|
||||
|
||||
|
||||
/// @todo Add unit-tests for testing:
|
||||
/// - addActiveIface() with invalid interface name
|
||||
/// - addActiveIface() with the same interface twice
|
||||
|
308
src/lib/dhcpsrv/tests/d2_client_unittest.cc
Normal file
308
src/lib/dhcpsrv/tests/d2_client_unittest.cc
Normal file
@@ -0,0 +1,308 @@
|
||||
// Copyright (C) 2012-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.h>
|
||||
#include <dhcpsrv/d2_client.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::util;
|
||||
using namespace isc;
|
||||
|
||||
namespace {
|
||||
|
||||
// brief Checks constructors and accessors of D2ClientConfig.
|
||||
TEST(D2ClientConfigTest, constructorsAndAccessors) {
|
||||
D2ClientConfigPtr d2_client_config;
|
||||
|
||||
// Verify default constructor creates a disabled instance.
|
||||
ASSERT_NO_THROW(d2_client_config.reset(new D2ClientConfig()));
|
||||
EXPECT_FALSE(d2_client_config->getEnableUpdates());
|
||||
|
||||
d2_client_config.reset();
|
||||
|
||||
bool enable_updates = true;
|
||||
isc::asiolink::IOAddress server_ip("127.0.0.1");
|
||||
size_t server_port = 477;
|
||||
dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP;
|
||||
dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON;
|
||||
bool remove_on_renew = true;
|
||||
bool always_include_fqdn = true;
|
||||
bool allow_client_update = true;
|
||||
bool override_no_update = true;
|
||||
bool override_client_update = true;
|
||||
bool replace_client_name = true;
|
||||
std::string generated_prefix = "the_prefix";
|
||||
std::string qualifying_suffix = "the.suffix.";
|
||||
|
||||
// Verify that we can construct a valid, enabled instance.
|
||||
ASSERT_NO_THROW(d2_client_config.reset(new
|
||||
D2ClientConfig(enable_updates,
|
||||
server_ip,
|
||||
server_port,
|
||||
ncr_protocol,
|
||||
ncr_format,
|
||||
remove_on_renew,
|
||||
always_include_fqdn,
|
||||
allow_client_update,
|
||||
override_no_update,
|
||||
override_client_update,
|
||||
replace_client_name,
|
||||
generated_prefix,
|
||||
qualifying_suffix)));
|
||||
|
||||
ASSERT_TRUE(d2_client_config);
|
||||
|
||||
// Verify that the accessors return the expected values.
|
||||
EXPECT_EQ(d2_client_config->getEnableUpdates(), enable_updates);
|
||||
|
||||
EXPECT_EQ(d2_client_config->getServerIp(), server_ip);
|
||||
EXPECT_EQ(d2_client_config->getServerPort(), server_port);
|
||||
EXPECT_EQ(d2_client_config->getNcrProtocol(), ncr_protocol);
|
||||
EXPECT_EQ(d2_client_config->getNcrFormat(), ncr_format);
|
||||
EXPECT_EQ(d2_client_config->getRemoveOnRenew(), remove_on_renew);
|
||||
EXPECT_EQ(d2_client_config->getAlwaysIncludeFqdn(), always_include_fqdn);
|
||||
EXPECT_EQ(d2_client_config->getAllowClientUpdate(), allow_client_update);
|
||||
EXPECT_EQ(d2_client_config->getOverrideNoUpdate(), override_no_update);
|
||||
EXPECT_EQ(d2_client_config->getOverrideClientUpdate(),
|
||||
override_client_update);
|
||||
EXPECT_EQ(d2_client_config->getReplaceClientName(), replace_client_name);
|
||||
EXPECT_EQ(d2_client_config->getGeneratedPrefix(), generated_prefix);
|
||||
EXPECT_EQ(d2_client_config->getQualifyingSuffix(), qualifying_suffix);
|
||||
|
||||
// Verify that toText called by << operator doesn't bomb.
|
||||
ASSERT_NO_THROW(std::cout << "toText test:" << std::endl <<
|
||||
*d2_client_config << std::endl);
|
||||
|
||||
// Verify that constructor does not allow use of NCR_TCP.
|
||||
// @todo obviously this becomes invalid once TCP is supported.
|
||||
ASSERT_THROW(d2_client_config.reset(new
|
||||
D2ClientConfig(enable_updates,
|
||||
server_ip,
|
||||
server_port,
|
||||
dhcp_ddns::NCR_TCP,
|
||||
ncr_format,
|
||||
remove_on_renew,
|
||||
always_include_fqdn,
|
||||
allow_client_update,
|
||||
override_no_update,
|
||||
override_client_update,
|
||||
replace_client_name,
|
||||
generated_prefix,
|
||||
qualifying_suffix)),
|
||||
D2ClientError);
|
||||
|
||||
// @todo if additional validation is added to ctor, this test needs to
|
||||
// expand accordingly.
|
||||
}
|
||||
|
||||
// Tests the equality and inequality operators of D2ClientConfig.
|
||||
TEST(D2ClientConfigTest, equalityOperator) {
|
||||
D2ClientConfigPtr ref_config;
|
||||
D2ClientConfigPtr test_config;
|
||||
|
||||
isc::asiolink::IOAddress ref_address("127.0.0.1");
|
||||
isc::asiolink::IOAddress test_address("127.0.0.2");
|
||||
|
||||
// Create an instance to use as a reference.
|
||||
ASSERT_NO_THROW(ref_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(ref_config);
|
||||
|
||||
// Check a configuration that is identical to reference configuration.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_TRUE(*ref_config == *test_config);
|
||||
EXPECT_FALSE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by enable flag.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(false,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by server ip.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
test_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by server port.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 333,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by remove_on_renew.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
false, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by always_include_fqdn.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, false, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by allow_client_update.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, false, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by override_no_update.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, false, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by override_client_update.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, false, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by replace_client_name.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, false,
|
||||
"pre-fix", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by generated_prefix.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"bogus", "suf-fix")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
|
||||
// Check a configuration that differs only by qualifying_suffix.
|
||||
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
|
||||
ref_address, 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "bogus")));
|
||||
ASSERT_TRUE(test_config);
|
||||
EXPECT_FALSE(*ref_config == *test_config);
|
||||
EXPECT_TRUE(*ref_config != *test_config);
|
||||
}
|
||||
|
||||
// This test checks the D2ClientMgr constructor.
|
||||
TEST(D2ClientMgr, constructor) {
|
||||
D2ClientMgrPtr d2_client_mgr;
|
||||
|
||||
// Verify we can construct with the default constructor.
|
||||
ASSERT_NO_THROW(d2_client_mgr.reset(new D2ClientMgr()));
|
||||
|
||||
// After construction, D2 configuration should be disabled.
|
||||
// Fetch it and verify this is the case.
|
||||
D2ClientConfigPtr original_config = d2_client_mgr->getD2ClientConfig();
|
||||
ASSERT_TRUE(original_config);
|
||||
EXPECT_FALSE(original_config->getEnableUpdates());
|
||||
|
||||
// Make sure convenience method agrees.
|
||||
EXPECT_FALSE(d2_client_mgr->isDhcpDdnsEnabled());
|
||||
}
|
||||
|
||||
// This test checks passing the D2ClientMgr a valid D2 client configuration.
|
||||
// @todo Once NameChangeSender is integrated, this test needs to expand, and
|
||||
// additional scenario tests will need to be written.
|
||||
TEST(D2ClientMgr, validConfig) {
|
||||
D2ClientMgrPtr d2_client_mgr;
|
||||
|
||||
// Construct the manager and fetch its initial configuration.
|
||||
ASSERT_NO_THROW(d2_client_mgr.reset(new D2ClientMgr()));
|
||||
D2ClientConfigPtr original_config = d2_client_mgr->getD2ClientConfig();
|
||||
ASSERT_TRUE(original_config);
|
||||
|
||||
// Verify that we cannot set the config to an empty pointer.
|
||||
D2ClientConfigPtr new_cfg;
|
||||
ASSERT_THROW(d2_client_mgr->setD2ClientConfig(new_cfg), D2ClientError);
|
||||
|
||||
// Create a new, enabled config.
|
||||
ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
|
||||
isc::asiolink::IOAddress("127.0.0.1"), 477,
|
||||
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
|
||||
true, true, true, true, true, true,
|
||||
"pre-fix", "suf-fix")));
|
||||
|
||||
// Verify that we can assign a new, non-empty configuration.
|
||||
ASSERT_NO_THROW(d2_client_mgr->setD2ClientConfig(new_cfg));
|
||||
|
||||
// Verify that we can fetch the newly assigned configuration.
|
||||
D2ClientConfigPtr updated_config = d2_client_mgr->getD2ClientConfig();
|
||||
ASSERT_TRUE(updated_config);
|
||||
EXPECT_TRUE(updated_config->getEnableUpdates());
|
||||
|
||||
// Make sure convenience method agrees with the updated configuration.
|
||||
EXPECT_TRUE(d2_client_mgr->isDhcpDdnsEnabled());
|
||||
|
||||
// Make sure the configuration we fetched is the one we assigned,
|
||||
// and not the original configuration.
|
||||
EXPECT_EQ(*new_cfg, *updated_config);
|
||||
EXPECT_NE(*original_config, *updated_config);
|
||||
}
|
||||
|
||||
|
||||
} // end of anonymous namespace
|
@@ -367,8 +367,8 @@ public:
|
||||
///
|
||||
/// Note that the method currently it only supports option-defs, option-data
|
||||
/// and hooks-libraries.
|
||||
///
|
||||
/// @param config_id is the name of the configuration element.
|
||||
///
|
||||
/// @param config_id is the name of the configuration element.
|
||||
///
|
||||
/// @return returns a shared pointer to DhcpConfigParser.
|
||||
///
|
||||
@@ -376,20 +376,21 @@ public:
|
||||
ParserPtr createConfigParser(const std::string& config_id) {
|
||||
ParserPtr parser;
|
||||
if (config_id.compare("option-data") == 0) {
|
||||
parser.reset(new OptionDataListParser(config_id,
|
||||
parser_context_->options_,
|
||||
parser.reset(new OptionDataListParser(config_id,
|
||||
parser_context_->options_,
|
||||
parser_context_,
|
||||
UtestOptionDataParser::factory));
|
||||
|
||||
} else if (config_id.compare("option-def") == 0) {
|
||||
parser.reset(new OptionDefListParser(config_id,
|
||||
parser.reset(new OptionDefListParser(config_id,
|
||||
parser_context_->option_defs_));
|
||||
|
||||
} else if (config_id.compare("hooks-libraries") == 0) {
|
||||
parser.reset(new HooksLibrariesParser(config_id));
|
||||
hooks_libraries_parser_ =
|
||||
boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
|
||||
|
||||
} else if (config_id.compare("dhcp-ddns") == 0) {
|
||||
parser.reset(new D2ClientConfigParser(config_id));
|
||||
} else {
|
||||
isc_throw(NotImplemented,
|
||||
"Parser error: configuration parameter not supported: "
|
||||
@@ -399,8 +400,8 @@ public:
|
||||
return (parser);
|
||||
}
|
||||
|
||||
/// @brief Convenience method for parsing a configuration
|
||||
///
|
||||
/// @brief Convenience method for parsing a configuration
|
||||
///
|
||||
/// Given a configuration string, convert it into Elements
|
||||
/// and parse them.
|
||||
/// @param config is the configuration string to parse
|
||||
@@ -491,6 +492,10 @@ public:
|
||||
|
||||
// Ensure no hooks libraries are loaded.
|
||||
HooksManager::unloadLibraries();
|
||||
|
||||
// Set it to minimal, disabled config
|
||||
D2ClientConfigPtr tmp(new D2ClientConfig());
|
||||
CfgMgr::instance().setD2ClientConfig(tmp);
|
||||
}
|
||||
|
||||
/// @brief Parsers used in the parsing of the configuration
|
||||
@@ -703,6 +708,226 @@ TEST_F(ParseConfigTest, invalidHooksLibrariesTest) {
|
||||
"Error text returned from parse failure is " << error_text_;
|
||||
}
|
||||
|
||||
/// @brief Checks that a valid, enabled D2 client configuration works correctly.
|
||||
TEST_F(ParseConfigTest, validD2Config) {
|
||||
|
||||
// Configuration string. This contains a set of valid libraries.
|
||||
std::string config_str =
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-ip\" : \"192.168.2.1\", "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"UDP\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" "
|
||||
" }"
|
||||
"}";
|
||||
|
||||
// Verify that the configuration string parses.
|
||||
int rcode = parseConfiguration(config_str);
|
||||
ASSERT_TRUE(rcode == 0) << error_text_;
|
||||
|
||||
// Verify that DHCP-DDNS is enabled and we can fetch the configuration.
|
||||
EXPECT_TRUE(CfgMgr::instance().isDhcpDdnsEnabled());
|
||||
D2ClientConfigPtr d2_client_config;
|
||||
ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
|
||||
ASSERT_TRUE(d2_client_config);
|
||||
|
||||
// Verify that the configuration values are as expected.
|
||||
EXPECT_TRUE(d2_client_config->getEnableUpdates());
|
||||
EXPECT_EQ("192.168.2.1", d2_client_config->getServerIp().toText());
|
||||
EXPECT_EQ(5301, d2_client_config->getServerPort());
|
||||
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
|
||||
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
|
||||
EXPECT_TRUE(d2_client_config->getRemoveOnRenew());
|
||||
EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
|
||||
EXPECT_TRUE(d2_client_config->getAllowClientUpdate());
|
||||
EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
|
||||
EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
|
||||
EXPECT_TRUE(d2_client_config->getReplaceClientName());
|
||||
EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
|
||||
EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
|
||||
}
|
||||
|
||||
/// @brief Checks that D2 client can be configured with enable flag of
|
||||
/// false only.
|
||||
TEST_F(ParseConfigTest, validDisabledD2Config) {
|
||||
|
||||
// Configuration string. This contains a set of valid libraries.
|
||||
std::string config_str =
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : false"
|
||||
" }"
|
||||
"}";
|
||||
|
||||
// Verify that the configuration string parses.
|
||||
int rcode = parseConfiguration(config_str);
|
||||
ASSERT_TRUE(rcode == 0) << error_text_;
|
||||
|
||||
// Verify that DHCP-DDNS is disabled.
|
||||
EXPECT_FALSE(CfgMgr::instance().isDhcpDdnsEnabled());
|
||||
|
||||
// Make sure fetched config agrees.
|
||||
D2ClientConfigPtr d2_client_config;
|
||||
ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
|
||||
EXPECT_TRUE(d2_client_config);
|
||||
EXPECT_FALSE(d2_client_config->getEnableUpdates());
|
||||
}
|
||||
|
||||
/// @brief Check various invalid D2 client configurations.
|
||||
TEST_F(ParseConfigTest, invalidD2Config) {
|
||||
std::string invalid_configs[] = {
|
||||
// only the enable flag of true
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true"
|
||||
" }"
|
||||
"}",
|
||||
// Missing server ip value
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true, "
|
||||
//" \"server-ip\" : \"192.168.2.1\", "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"UDP\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" "
|
||||
" }"
|
||||
"}",
|
||||
// Invalid server ip value
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-ip\" : \"x192.168.2.1\", "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"UDP\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" "
|
||||
" }"
|
||||
"}",
|
||||
// Unknown protocol
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-ip\" : \"192.168.2.1\", "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"Bogus\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" "
|
||||
" }"
|
||||
"}",
|
||||
// Unsupported protocol
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-ip\" : \"192.168.2.1\", "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"TCP\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" "
|
||||
" }"
|
||||
"}",
|
||||
// Unknown format
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-ip\" : \"192.168.2.1\", "
|
||||
" \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"UDP\", "
|
||||
" \"ncr-format\" : \"Bogus\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" "
|
||||
" }"
|
||||
"}",
|
||||
// Missig Port
|
||||
"{ \"dhcp-ddns\" :"
|
||||
" {"
|
||||
" \"enable-updates\" : true, "
|
||||
" \"server-ip\" : \"192.168.2.1\", "
|
||||
// " \"server-port\" : 5301, "
|
||||
" \"ncr-protocol\" : \"UDP\", "
|
||||
" \"ncr-format\" : \"JSON\", "
|
||||
" \"remove-on-renew\" : true, "
|
||||
" \"always-include-fqdn\" : true, "
|
||||
" \"allow-client-update\" : true, "
|
||||
" \"override-no-update\" : true, "
|
||||
" \"override-client-update\" : true, "
|
||||
" \"replace-client-name\" : true, "
|
||||
" \"generated-prefix\" : \"test.prefix\", "
|
||||
" \"qualifying-suffix\" : \"test.suffix.\" "
|
||||
" }"
|
||||
"}",
|
||||
// stop
|
||||
""
|
||||
};
|
||||
|
||||
// Fetch the original config.
|
||||
D2ClientConfigPtr original_config;
|
||||
ASSERT_NO_THROW(original_config = CfgMgr::instance().getD2ClientConfig());
|
||||
|
||||
// Iterate through the invalid configuration strings, attempting to
|
||||
// parse each one. They should fail to parse, but fail gracefully.
|
||||
D2ClientConfigPtr current_config;
|
||||
int i = 0;
|
||||
while (!invalid_configs[i].empty()) {
|
||||
// Verify that the configuration string parses without throwing.
|
||||
int rcode = parseConfiguration(invalid_configs[i]);
|
||||
|
||||
// Verify that parse result indicates a parsing error.
|
||||
ASSERT_TRUE(rcode != 0) << "Invalid config #: " << i
|
||||
<< " should not have passed!";
|
||||
|
||||
// Verify that the "official" config still matches the original config.
|
||||
ASSERT_NO_THROW(current_config =
|
||||
CfgMgr::instance().getD2ClientConfig());
|
||||
EXPECT_EQ(*original_config, *current_config);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief DHCP Configuration Parser Context test fixture.
|
||||
class ParserContextTest : public ::testing::Test {
|
||||
public:
|
||||
|
Reference in New Issue
Block a user