Commit fb4c4a30 authored by Marcin Siodelski's avatar Marcin Siodelski

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

parent 5adcd2c0
......@@ -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\","
" \"hostname\": \"\""
" }"
......@@ -3528,16 +3528,19 @@ TEST_F(Dhcp4ParserTest, reservations) {
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(),
......
......@@ -12,6 +12,7 @@
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <sys/socket.h>
#include <sstream>
#include <string>
using namespace isc::asiolink;
......@@ -23,36 +24,62 @@ namespace {
///
/// 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);
}
}
......@@ -80,10 +107,11 @@ HostReservationParser::build(isc::data::ConstElementPtr reservation_data) {
" 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;
......@@ -100,10 +128,22 @@ HostReservationParser::build(isc::data::ConstElementPtr reservation_data) {
}
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.
......@@ -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)
: HostReservationParser(subnet_id) {
}
......@@ -168,8 +213,13 @@ HostReservationParser4::build(isc::data::ConstElementPtr reservation_data) {
}
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)
......@@ -263,8 +313,13 @@ HostReservationParser6::build(isc::data::ConstElementPtr reservation_data) {
}
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
......@@ -48,12 +48,30 @@ protected:
/// @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_;
......@@ -84,12 +102,24 @@ public:
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.
......@@ -112,12 +142,24 @@ public:
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;
};
......
......@@ -21,6 +21,7 @@
#include <gtest/gtest.h>
#include <iterator>
#include <string>
#include <vector>
using namespace isc::asiolink;
using namespace isc::data;
......@@ -126,6 +127,40 @@ protected:
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.
///
......@@ -144,6 +179,8 @@ protected:
/// @brief DUID object used by tests.
DuidPtr duid_;
/// @brief Vector holding circuit id used by tests.
std::vector<uint8_t> circuit_id_;
};
void
......@@ -158,6 +195,9 @@ HostReservationParserTest::SetUp() {
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
......@@ -213,6 +253,22 @@ TEST_F(HostReservationParserTest, dhcp4DUID) {
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) {
......@@ -447,6 +503,22 @@ TEST_F(HostReservationParserTest, dhcp6DUID) {
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) {
......
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