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.
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
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: "
// 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
// 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
// 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) {
}
} ]
}
- }
+ },
+
+ { "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": [
{
#include "marker_file.h"
#include "test_libraries.h"
+#include "test_data_files_config.h"
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
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()
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"subnet4\": [ ], "
+ "\"dhcp-ddns\": { \"enable-updates\" : false }, "
"\"option-def\": [ ], "
"\"option-data\": [ ] }";
static_cast<void>(executeConfiguration(config,
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());
+}
}
--- /dev/null
+// 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"
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&
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:
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 ************************************/
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.
// 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>
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
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
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
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
#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>
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.
/// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
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
#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>
// 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_;
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;
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
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
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
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
--- /dev/null
+// 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
///
/// 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.
///
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: "
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
// 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
"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: