Commit fb4c4a30 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[4301] Host configuration parser now supports circuit-id parameter.

parent 5adcd2c0
...@@ -3454,7 +3454,7 @@ TEST_F(Dhcp4ParserTest, reservations) { ...@@ -3454,7 +3454,7 @@ TEST_F(Dhcp4ParserTest, reservations) {
" ]" " ]"
" }," " },"
" {" " {"
" \"hw-address\": \"06:05:04:03:02:01\"," " \"circuit-id\": \"060504030201\","
" \"ip-address\": \"192.0.4.102\"," " \"ip-address\": \"192.0.4.102\","
" \"hostname\": \"\"" " \"hostname\": \"\""
" }" " }"
...@@ -3528,16 +3528,19 @@ TEST_F(Dhcp4ParserTest, reservations) { ...@@ -3528,16 +3528,19 @@ TEST_F(Dhcp4ParserTest, reservations) {
ASSERT_TRUE(opt_ttl); ASSERT_TRUE(opt_ttl);
EXPECT_EQ(32, static_cast<int>(opt_ttl->getValue())); 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 // consists of numbers from 6 to 1. So, let's just reverse the order
// of the address from the previous test. // of the address from the previous test.
hwaddr->hwaddr_.assign(hwaddr_vec.rbegin(), hwaddr_vec.rend()); std::vector<uint8_t> circuit_id(hwaddr_vec.rbegin(), hwaddr_vec.rend());
host = hosts_cfg->get4(542, hwaddr); host = hosts_cfg->get4(542, Host::IDENT_CIRCUIT_ID, &circuit_id[0],
circuit_id.size());
EXPECT_TRUE(host); EXPECT_TRUE(host);
EXPECT_EQ("192.0.4.102", host->getIPv4Reservation().toText()); EXPECT_EQ("192.0.4.102", host->getIPv4Reservation().toText());
// This reservation must not belong to other subnets. // This reservation must not belong to other subnets.
EXPECT_FALSE(hosts_cfg->get4(123, hwaddr)); EXPECT_FALSE(hosts_cfg->get4(123, Host::IDENT_CIRCUIT_ID,
EXPECT_FALSE(hosts_cfg->get4(234, hwaddr)); &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. // Repeat the test for the DUID based reservation in this subnet.
duid.reset(new DUID(std::vector<uint8_t>(duid_vec.rbegin(), duid.reset(new DUID(std::vector<uint8_t>(duid_vec.rbegin(),
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <sys/socket.h> #include <sys/socket.h>
#include <sstream>
#include <string> #include <string>
using namespace isc::asiolink; using namespace isc::asiolink;
...@@ -23,36 +24,62 @@ namespace { ...@@ -23,36 +24,62 @@ namespace {
/// ///
/// This function returns the set of supported parameters for /// This function returns the set of supported parameters for
/// host reservation in DHCPv4. /// 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; 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()) { if (params_set.empty()) {
const char* params[] = { params_set = identifiers_set;
"duid", "hw-address", "hostname", "ip-address", params_set.insert("hostname");
"option-data", NULL params_set.insert("ip-address");
}; params_set.insert("option-data");
for (int i = 0; params[i] != NULL; ++i) {
params_set.insert(std::string(params[i]));
}
} }
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 /// This function returns the set of supported parameters for
/// host reservation in DHCPv6. /// 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; 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()) { if (params_set.empty()) {
const char* params[] = { params_set = identifiers_set;
"duid", "hw-address", "hostname", "ip-addresses", "prefixes", params_set.insert("hostname");
"option-data", NULL params_set.insert("ip-addresses");
}; params_set.insert("prefixes");
for (int i = 0; params[i] != NULL; ++i) { params_set.insert("option-data");
params_set.insert(std::string(params[i]));
}
} }
return (params_set); return (identifiers_only ? identifiers_set : params_set);
} }
} }
...@@ -80,10 +107,11 @@ HostReservationParser::build(isc::data::ConstElementPtr reservation_data) { ...@@ -80,10 +107,11 @@ HostReservationParser::build(isc::data::ConstElementPtr reservation_data) {
" parameter '" << element.first << "'"); " parameter '" << element.first << "'");
} }
if (element.first == "hw-address" || element.first == "duid") { if (isIdentifierParameter(element.first)) {
if (!identifier_name.empty()) { if (!identifier.empty()) {
isc_throw(DhcpConfigError, "the 'hw-address' and 'duid'" isc_throw(DhcpConfigError, "the '" << element.first
" parameters are mutually exclusive"); << "' and '" << identifier_name
<< "' are mutually exclusive");
} }
identifier = element.second->stringValue(); identifier = element.second->stringValue();
identifier_name = element.first; identifier_name = element.first;
...@@ -100,10 +128,22 @@ HostReservationParser::build(isc::data::ConstElementPtr reservation_data) { ...@@ -100,10 +128,22 @@ HostReservationParser::build(isc::data::ConstElementPtr reservation_data) {
} }
try { try {
// hw-address or duid is a must. // Host identifier is a must.
if (identifier_name.empty()) { if (identifier_name.empty()) {
isc_throw(DhcpConfigError, "'hw-address' or 'duid' is a required" // If there is no identifier specified, we have to display an
" parameter for host reservation"); // 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. // Create a host object from the basic parameters we already parsed.
...@@ -129,6 +169,11 @@ HostReservationParser::addHost(isc::data::ConstElementPtr reservation_data) { ...@@ -129,6 +169,11 @@ HostReservationParser::addHost(isc::data::ConstElementPtr reservation_data) {
} }
} }
bool
HostReservationParser::isSupportedParameter(const std::string& param_name) const {
return (getSupportedParameters(false).count(param_name) > 0);
}
HostReservationParser4::HostReservationParser4(const SubnetID& subnet_id) HostReservationParser4::HostReservationParser4(const SubnetID& subnet_id)
: HostReservationParser(subnet_id) { : HostReservationParser(subnet_id) {
} }
...@@ -168,8 +213,13 @@ HostReservationParser4::build(isc::data::ConstElementPtr reservation_data) { ...@@ -168,8 +213,13 @@ HostReservationParser4::build(isc::data::ConstElementPtr reservation_data) {
} }
bool bool
HostReservationParser4::isSupportedParameter(const std::string& param_name) const { HostReservationParser4::isIdentifierParameter(const std::string& param_name) const {
return (getSupportedParams4().count(param_name) > 0); 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) HostReservationParser6::HostReservationParser6(const SubnetID& subnet_id)
...@@ -263,8 +313,13 @@ HostReservationParser6::build(isc::data::ConstElementPtr reservation_data) { ...@@ -263,8 +313,13 @@ HostReservationParser6::build(isc::data::ConstElementPtr reservation_data) {
} }
bool bool
HostReservationParser6::isSupportedParameter(const std::string& param_name) const { HostReservationParser6::isIdentifierParameter(const std::string& param_name) const {
return (getSupportedParams6().count(param_name) > 0); 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 } // 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 // 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 // License, v. 2.0. If a copy of the MPL was not distributed with this
...@@ -48,12 +48,30 @@ protected: ...@@ -48,12 +48,30 @@ protected:
/// @throw DhcpConfigError When operation to add a configured host fails. /// @throw DhcpConfigError When operation to add a configured host fails.
void addHost(isc::data::ConstElementPtr reservation_data); 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. /// @brief Checks if the specified parameter is supported by the parser.
/// ///
/// @param param_name Parameter name. /// @param param_name Parameter name.
/// ///
/// @return true if the parameter is supported, false otherwise. /// @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. /// @brief Identifier of the subnet that the host is connected to.
SubnetID subnet_id_; SubnetID subnet_id_;
...@@ -84,12 +102,24 @@ public: ...@@ -84,12 +102,24 @@ public:
protected: 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. /// @param param_name Parameter name.
/// ///
/// @return true if the parameter is supported, false otherwise. /// @return true if the parameter specifies host identifier, false
virtual bool isSupportedParameter(const std::string& param_name) const; /// 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. /// @brief Parser for a single host reservation for DHCPv6.
...@@ -112,12 +142,24 @@ public: ...@@ -112,12 +142,24 @@ public:
protected: 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. /// @param param_name Parameter name.
/// ///
/// @return true if the parameter is supported, false otherwise. /// @return true if the parameter specifies host identifier, false
virtual bool isSupportedParameter(const std::string& param_name) const; /// 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;
}; };
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <iterator> #include <iterator>
#include <string> #include <string>
#include <vector>
using namespace isc::asiolink; using namespace isc::asiolink;
using namespace isc::data; using namespace isc::data;
...@@ -126,6 +127,40 @@ protected: ...@@ -126,6 +127,40 @@ protected:
EXPECT_TRUE(hosts[0]->getCfgOption6()->empty()); 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 /// @brief This test verfies that the parser returns an error when
/// configuration is invalid. /// configuration is invalid.
/// ///
...@@ -144,6 +179,8 @@ protected: ...@@ -144,6 +179,8 @@ protected:
/// @brief DUID object used by tests. /// @brief DUID object used by tests.
DuidPtr duid_; DuidPtr duid_;
/// @brief Vector holding circuit id used by tests.
std::vector<uint8_t> circuit_id_;
}; };
void void
...@@ -158,6 +195,9 @@ HostReservationParserTest::SetUp() { ...@@ -158,6 +195,9 @@ HostReservationParserTest::SetUp() {
const uint8_t duid_data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, const uint8_t duid_data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A }; 0x08, 0x09, 0x0A };
duid_ = DuidPtr(new DUID(duid_data, sizeof(duid_data))); 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 void
...@@ -213,6 +253,22 @@ TEST_F(HostReservationParserTest, dhcp4DUID) { ...@@ -213,6 +253,22 @@ TEST_F(HostReservationParserTest, dhcp4DUID) {
EXPECT_TRUE(hosts[0]->getHostname().empty()); 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 // This test verifies that the parser can parse the reservation entry
// when IPv4 address is specified, but hostname is not. // when IPv4 address is specified, but hostname is not.
TEST_F(HostReservationParserTest, dhcp4NoHostname) { TEST_F(HostReservationParserTest, dhcp4NoHostname) {
...@@ -447,6 +503,22 @@ TEST_F(HostReservationParserTest, dhcp6DUID) { ...@@ -447,6 +503,22 @@ TEST_F(HostReservationParserTest, dhcp6DUID) {
ASSERT_EQ(0, std::distance(prefixes.first, prefixes.second)); 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 // This test verfies that the parser can parse the IPv6 reservation entry
// which lacks hostname parameter. // which lacks hostname parameter.
TEST_F(HostReservationParserTest, dhcp6NoHostname) { TEST_F(HostReservationParserTest, dhcp6NoHostname) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment