" ]"
" },"
" {"
- " \"hw-address\": \"06:05:04:03:02:01\","
+ " \"circuit-id\": \"060504030201\","
" \"ip-address\": \"192.0.4.102\","
" \"hostname\": \"\""
" }"
ASSERT_TRUE(opt_ttl);
EXPECT_EQ(32, static_cast<int>(opt_ttl->getValue()));
- // The HW address used for one of the reservations in the subnet 542
+ // The circuit-id used for one of the reservations in the subnet 542
// consists of numbers from 6 to 1. So, let's just reverse the order
// of the address from the previous test.
- hwaddr->hwaddr_.assign(hwaddr_vec.rbegin(), hwaddr_vec.rend());
- host = hosts_cfg->get4(542, hwaddr);
+ std::vector<uint8_t> circuit_id(hwaddr_vec.rbegin(), hwaddr_vec.rend());
+ host = hosts_cfg->get4(542, Host::IDENT_CIRCUIT_ID, &circuit_id[0],
+ circuit_id.size());
EXPECT_TRUE(host);
EXPECT_EQ("192.0.4.102", host->getIPv4Reservation().toText());
// This reservation must not belong to other subnets.
- EXPECT_FALSE(hosts_cfg->get4(123, hwaddr));
- EXPECT_FALSE(hosts_cfg->get4(234, hwaddr));
+ EXPECT_FALSE(hosts_cfg->get4(123, Host::IDENT_CIRCUIT_ID,
+ &circuit_id[0], circuit_id.size()));
+ EXPECT_FALSE(hosts_cfg->get4(234, Host::IDENT_CIRCUIT_ID,
+ &circuit_id[0], circuit_id.size()));
// Repeat the test for the DUID based reservation in this subnet.
duid.reset(new DUID(std::vector<uint8_t>(duid_vec.rbegin(),
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <sys/socket.h>
+#include <sstream>
#include <string>
using namespace isc::asiolink;
///
/// This function returns the set of supported parameters for
/// host reservation in DHCPv4.
-const std::set<std::string>& getSupportedParams4() {
+///
+/// @param identifiers_only Indicates if the function should only
+/// return supported host identifiers (if true) or all supported
+/// parameters (if false).
+const std::set<std::string>&
+getSupportedParams4(const bool identifiers_only = false) {
+ // Holds set of host identifiers.
+ static std::set<std::string> identifiers_set;
+ // Holds set of all supported parameters, including identifiers.
static std::set<std::string> params_set;
+ // If this is first execution of this function, we need
+ // to initialize the set.
+ if (identifiers_set.empty()) {
+ identifiers_set.insert("duid");
+ identifiers_set.insert("hw-address");
+ identifiers_set.insert("circuit-id");
+ }
+ // Copy identifiers and add all other parameters.
if (params_set.empty()) {
- const char* params[] = {
- "duid", "hw-address", "hostname", "ip-address",
- "option-data", NULL
- };
- for (int i = 0; params[i] != NULL; ++i) {
- params_set.insert(std::string(params[i]));
- }
+ params_set = identifiers_set;
+ params_set.insert("hostname");
+ params_set.insert("ip-address");
+ params_set.insert("option-data");
}
- return (params_set);
+ return (identifiers_only ? identifiers_set : params_set);
}
-/// @brief Returns set of the supported parameters for DHCPv4.
+/// @brief Returns set of the supported parameters for DHCPv6.
///
/// This function returns the set of supported parameters for
/// host reservation in DHCPv6.
-const std::set<std::string>& getSupportedParams6() {
+///
+/// @param identifiers_only Indicates if the function should only
+/// return supported host identifiers (if true) or all supported
+/// parameters (if false).
+const std::set<std::string>&
+getSupportedParams6(const bool identifiers_only = false) {
+ // Holds set of host identifiers.
+ static std::set<std::string> identifiers_set;
+ // Holds set of all supported parameters, including identifiers.
static std::set<std::string> params_set;
+ // If this is first execution of this function, we need
+ // to initialize the set.
+ if (identifiers_set.empty()) {
+ identifiers_set.insert("duid");
+ identifiers_set.insert("hw-address");
+ }
+ // Copy identifiers and add all other parameters.
if (params_set.empty()) {
- const char* params[] = {
- "duid", "hw-address", "hostname", "ip-addresses", "prefixes",
- "option-data", NULL
- };
- for (int i = 0; params[i] != NULL; ++i) {
- params_set.insert(std::string(params[i]));
- }
+ params_set = identifiers_set;
+ params_set.insert("hostname");
+ params_set.insert("ip-addresses");
+ params_set.insert("prefixes");
+ params_set.insert("option-data");
}
- return (params_set);
+ return (identifiers_only ? identifiers_set : params_set);
}
}
" parameter '" << element.first << "'");
}
- if (element.first == "hw-address" || element.first == "duid") {
- if (!identifier_name.empty()) {
- isc_throw(DhcpConfigError, "the 'hw-address' and 'duid'"
- " parameters are mutually exclusive");
+ if (isIdentifierParameter(element.first)) {
+ if (!identifier.empty()) {
+ isc_throw(DhcpConfigError, "the '" << element.first
+ << "' and '" << identifier_name
+ << "' are mutually exclusive");
}
identifier = element.second->stringValue();
identifier_name = element.first;
}
try {
- // hw-address or duid is a must.
+ // Host identifier is a must.
if (identifier_name.empty()) {
- isc_throw(DhcpConfigError, "'hw-address' or 'duid' is a required"
- " parameter for host reservation");
+ // If there is no identifier specified, we have to display an
+ // error message and include the information what identifiers
+ // are supported.
+ std::ostringstream s;
+ BOOST_FOREACH(std::string param_name, getSupportedParameters(true)) {
+ if (s.tellp() != std::streampos(0)) {
+ s << ", ";
+ }
+ s << param_name;
+ }
+ isc_throw(DhcpConfigError, "one of the supported identifiers must"
+ " be specified for host reservation: "
+ << s.str());
+
}
// Create a host object from the basic parameters we already parsed.
}
}
+bool
+HostReservationParser::isSupportedParameter(const std::string& param_name) const {
+ return (getSupportedParameters(false).count(param_name) > 0);
+}
+
HostReservationParser4::HostReservationParser4(const SubnetID& subnet_id)
: HostReservationParser(subnet_id) {
}
}
bool
-HostReservationParser4::isSupportedParameter(const std::string& param_name) const {
- return (getSupportedParams4().count(param_name) > 0);
+HostReservationParser4::isIdentifierParameter(const std::string& param_name) const {
+ return (getSupportedParams4(true).count(param_name) > 0);
+}
+
+const std::set<std::string>&
+HostReservationParser4::getSupportedParameters(const bool identifiers_only) const {
+ return (getSupportedParams4(identifiers_only));
}
HostReservationParser6::HostReservationParser6(const SubnetID& subnet_id)
}
bool
-HostReservationParser6::isSupportedParameter(const std::string& param_name) const {
- return (getSupportedParams6().count(param_name) > 0);
+HostReservationParser6::isIdentifierParameter(const std::string& param_name) const {
+ return (getSupportedParams6(true).count(param_name) > 0);
+}
+
+const std::set<std::string>&
+HostReservationParser6::getSupportedParameters(const bool identifiers_only) const {
+ return (getSupportedParams6(identifiers_only));
}
} // end of namespace isc::dhcp
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// @throw DhcpConfigError When operation to add a configured host fails.
void addHost(isc::data::ConstElementPtr reservation_data);
+ /// @brief Checks if the specified parameter is a host identifier.
+ ///
+ /// @param param_name Parameter name.
+ ///
+ /// @return true if the parameter specifies host identifier, false
+ /// otherwise.
+ virtual bool isIdentifierParameter(const std::string& param_name) const = 0;
+
/// @brief Checks if the specified parameter is supported by the parser.
///
/// @param param_name Parameter name.
///
/// @return true if the parameter is supported, false otherwise.
- virtual bool isSupportedParameter(const std::string& param_name) const = 0;
+ virtual bool isSupportedParameter(const std::string& param_name) const;
+
+ /// @brief Returns set of the supported parameters.
+ ///
+ /// @param identifiers_only Indicates if the function should only
+ /// return supported host identifiers (if true) or all supported
+ /// parameters (if false).
+ ///
+ /// @return Set of supported parameter names.
+ virtual const std::set<std::string>&
+ getSupportedParameters(const bool identifiers_only) const = 0;
/// @brief Identifier of the subnet that the host is connected to.
SubnetID subnet_id_;
protected:
- /// @brief Checks if the specified parameter is supported by the parser.
+ /// @brief Checks if the specified parameter is a host identifier.
///
/// @param param_name Parameter name.
///
- /// @return true if the parameter is supported, false otherwise.
- virtual bool isSupportedParameter(const std::string& param_name) const;
+ /// @return true if the parameter specifies host identifier, false
+ /// otherwise.
+ virtual bool isIdentifierParameter(const std::string& param_name) const;
+
+ /// @brief Returns set of the supported parameters for DHCPv4.
+ ///
+ /// @param identifiers_only Indicates if the function should only
+ /// return supported host identifiers (if true) or all supported
+ /// parameters (if false).
+ ///
+ /// @return Set of supported parameter names.
+ virtual const std::set<std::string>&
+ getSupportedParameters(const bool identifiers_only) const;
+
};
/// @brief Parser for a single host reservation for DHCPv6.
protected:
- /// @brief Checks if the specified parameter is supported by the parser.
+ /// @brief Checks if the specified parameter is a host identifier.
///
/// @param param_name Parameter name.
///
- /// @return true if the parameter is supported, false otherwise.
- virtual bool isSupportedParameter(const std::string& param_name) const;
+ /// @return true if the parameter specifies host identifier, false
+ /// otherwise.
+ virtual bool isIdentifierParameter(const std::string& param_name) const;
+
+ /// @brief Returns set of the supported parameters for DHCPv6.
+ ///
+ /// @param identifiers_only Indicates if the function should only
+ /// return supported host identifiers (if true) or all supported
+ /// parameters (if false).
+ ///
+ /// @return Set of supported parameter names.
+ virtual const std::set<std::string>&
+ getSupportedParameters(const bool identifiers_only) const;
+
};
#include <gtest/gtest.h>
#include <iterator>
#include <string>
+#include <vector>
using namespace isc::asiolink;
using namespace isc::data;
EXPECT_TRUE(hosts[0]->getCfgOption6()->empty());
}
+ /// @brief This test verfies that the parser can parse a DHCPv4
+ /// reservation configuration including a specific identifier.
+ ///
+ /// @param identifier_name Identifier name.
+ /// @param identifier_type Identifier type.
+ void testIdentifier4(const std::string& identifier_name,
+ const std::string& identifier_value,
+ const Host::IdentifierType& expected_identifier_type,
+ const std::vector<uint8_t>& expected_identifier) const {
+ std::ostringstream config;
+ config << "{ \"" << identifier_name << "\": \"" << identifier_value
+ << "\","
+ << "\"ip-address\": \"192.0.2.112\","
+ << "\"hostname\": \"\" }";
+
+ ElementPtr config_element = Element::fromJSON(config.str());
+
+ HostReservationParser4 parser(SubnetID(10));
+ ASSERT_NO_THROW(parser.build(config_element));
+
+ CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+ HostCollection hosts;
+ ASSERT_NO_THROW(hosts = cfg_hosts->getAll(expected_identifier_type,
+ &expected_identifier[0],
+ expected_identifier.size()));
+
+ ASSERT_EQ(1, hosts.size());
+
+ EXPECT_EQ(10, hosts[0]->getIPv4SubnetID());
+ EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
+ EXPECT_EQ("192.0.2.112", hosts[0]->getIPv4Reservation().toText());
+ EXPECT_TRUE(hosts[0]->getHostname().empty());
+ }
+
/// @brief This test verfies that the parser returns an error when
/// configuration is invalid.
///
/// @brief DUID object used by tests.
DuidPtr duid_;
+ /// @brief Vector holding circuit id used by tests.
+ std::vector<uint8_t> circuit_id_;
};
void
const uint8_t duid_data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A };
duid_ = DuidPtr(new DUID(duid_data, sizeof(duid_data)));
+
+ const std::string circuit_id_str = "howdy";
+ circuit_id_.assign(circuit_id_str.begin(), circuit_id_str.end());
}
void
EXPECT_TRUE(hosts[0]->getHostname().empty());
}
+// This test verifies that the parser can parse a reservation entry for
+// which circuit-id is an identifier. The circuit-id is specified as
+// a string in quotes.
+TEST_F(HostReservationParserTest, dhcp4CircuitIdStringInQuotes) {
+ testIdentifier4("circuit-id", "'howdy'", Host::IDENT_CIRCUIT_ID,
+ circuit_id_);
+}
+
+// This test verifies that the parser can parse a reservation entry for
+// which circuit-id is an identifier. The circuit-id is specified in
+// hexadecimal format.
+TEST_F(HostReservationParserTest, dhcp4CircuitIdHex) {
+ testIdentifier4("circuit-id", "686F776479", Host::IDENT_CIRCUIT_ID,
+ circuit_id_);
+}
+
// This test verifies that the parser can parse the reservation entry
// when IPv4 address is specified, but hostname is not.
TEST_F(HostReservationParserTest, dhcp4NoHostname) {
ASSERT_EQ(0, std::distance(prefixes.first, prefixes.second));
}
+// This test verifies that host reservation parser for DHCPv6 rejects
+// "circuit-id" as a host identifier.
+TEST_F(HostReservationParserTest, dhcp6CircuitId) {
+ // Use DHCPv4 specific identifier 'circuit-id' with DHCPv6 parser.
+ std::string config = "{ \"circuit-id\": \"'howdy'\","
+ "\"ip-addresses\": [ \"2001:db8:1::100\", \"2001:db8:1::200\" ],"
+ "\"prefixes\": [ ],"
+ "\"hostname\": \"foo.example.com\" }";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // The parser should throw exception.
+ HostReservationParser6 parser(SubnetID(12));
+ EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
// This test verfies that the parser can parse the IPv6 reservation entry
// which lacks hostname parameter.
TEST_F(HostReservationParserTest, dhcp6NoHostname) {