Commit 6e228073 authored by Marcin Siodelski's avatar Marcin Siodelski

[4303] Implemented parser for 'host-reservation-identifiers' list.

parent af0439c0
......@@ -15,12 +15,29 @@ CfgHostReservations::CfgHostReservations()
: identifier_types_() {
}
CfgHostReservationsPtr
CfgHostReservations::createConfig4() {
CfgHostReservationsPtr cfg(new CfgHostReservations());
cfg->addIdentifierType("hw-address");
cfg->addIdentifierType("duid");
cfg->addIdentifierType("circuit-id");
return (cfg);
}
CfgHostReservationsPtr
CfgHostReservations::createConfig6() {
CfgHostReservationsPtr cfg(new CfgHostReservations());
cfg->addIdentifierType("hw-address");
cfg->addIdentifierType("duid");
return (cfg);
}
void
CfgHostReservations::addIdentifierType(const std::string& identifier_name) {
Host::IdentifierType identifier_type = Host::getIdentifierType(identifier_name);
if (std::find(identifier_types_.begin(), identifier_types_.end(),
identifier_type) != identifier_types_.end()) {
isc_throw(isc::BadValue, "invalid host identifier name '"
isc_throw(isc::BadValue, "duplicate host identifier '"
<< identifier_name << "'");
}
identifier_types_.push_back(identifier_type);
......
......@@ -15,6 +15,20 @@
namespace isc {
namespace dhcp {
/// @brief Forward declaration of the @ref CfgHostReservations.
class CfgHostReservations;
/// @name Pointers to the @ref CfgHostReservations objects.
//@{
/// @brief Pointer to the Non-const object.
typedef boost::shared_ptr<CfgHostReservations> CfgHostReservationsPtr;
/// @brief Pointer to the const object.
typedef boost::shared_ptr<const CfgHostReservations>
ConstCfgHostReservationsPtr;
//@}
/// @brief Represents global configuration for host reservations.
///
/// This class represents server configuration pertaining to host
......@@ -37,6 +51,15 @@ public:
/// - no identifiers selected for host reservations searches.
CfgHostReservations();
/// @name Factory functions for creating default configurations.
//@{
/// @brief Factory function for DHCPv4.
static CfgHostReservationsPtr createConfig4();
/// @brief Factory function for DHCPv6.
static CfgHostReservationsPtr createConfig6();
//@}
/// @brief Adds new identifier type to a collection of identifiers
/// to be used by the server to search for host reservations.
///
......@@ -62,17 +85,6 @@ private:
};
/// @name Pointers to the @ref CfgHostReservations objects.
//@{
/// @brief Pointer to the Non-const object.
typedef boost::shared_ptr<CfgHostReservations> CfgHostReservationsPtr;
/// @brief Pointer to the const object.
typedef boost::shared_ptr<const CfgHostReservations>
ConstCfgHostReservationsPtr;
//@}
}
}
......
......@@ -183,6 +183,10 @@ public:
IDENT_CIRCUIT_ID
};
/// @brief Constant pointing to the last identifier of the
/// @ref IdentifierType enumeration.
static const IdentifierType LAST_IDENTIFIER_TYPE = IDENT_CIRCUIT_ID;
/// @brief Constructor.
///
/// Creates a @c Host object using an identifier in a binary format. This
......
......@@ -11,6 +11,7 @@
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <algorithm>
#include <sys/socket.h>
#include <sstream>
#include <string>
......@@ -37,8 +38,8 @@ getSupportedParams4(const bool identifiers_only = false) {
// 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("duid");
identifiers_set.insert("circuit-id");
}
// Copy identifiers and add all other parameters.
......@@ -68,8 +69,8 @@ getSupportedParams6(const bool identifiers_only = false) {
// 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("duid");
}
// Copy identifiers and add all other parameters.
if (params_set.empty()) {
......@@ -317,5 +318,82 @@ HostReservationParser6::getSupportedParameters(const bool identifiers_only) cons
return (getSupportedParams6(identifiers_only));
}
HostReservationIdsParser::HostReservationIdsParser()
: staging_cfg_() {
}
void
HostReservationIdsParser::build(isc::data::ConstElementPtr ids_list) {
// Remove any default configuration.
staging_cfg_->clear();
BOOST_FOREACH(ConstElementPtr element, ids_list->listValue()) {
std::string id_name = element->stringValue();
try {
if (id_name != "auto") {
if (!isSupportedIdentifier(id_name)) {
isc_throw(isc::BadValue, "unsupported identifier '"
<< id_name << "'");
}
staging_cfg_->addIdentifierType(id_name);
} else {
// 'auto' is mutually exclusive with other values. If there
// are any values in the configuration already it means that
// some other values have already been specified.
if (!staging_cfg_->getIdentifierTypes().empty()) {
isc_throw(isc::BadValue, "if 'auto' keyword is used,"
" no other values can be specified within '"
"host-reservation-identifiers' list");
}
// Iterate over all identifier types and for those supported
// in a given context (DHCPv4 or DHCPv6) add the identifier type
// to the configuration.
for (unsigned int i = 0;
i <= static_cast<unsigned int>(Host::LAST_IDENTIFIER_TYPE);
++i) {
std::string supported_id_name =
Host::getIdentifierName(static_cast<Host::IdentifierType>(i));
if (isSupportedIdentifier(supported_id_name)) {
staging_cfg_->addIdentifierType(supported_id_name);
}
}
}
} catch (const std::exception& ex) {
// Append line number where the error occurred.
isc_throw(DhcpConfigError, ex.what() << " ("
<< element->getPosition() << ")");
}
}
// The parsed list must not be empty.
if (staging_cfg_->getIdentifierTypes().empty()) {
isc_throw(DhcpConfigError, "'host-reservation-identifiers' list must not"
" be empty (" << ids_list->getPosition() << ")");
}
}
HostReservationIdsParser4::HostReservationIdsParser4()
: HostReservationIdsParser() {
staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostReservations4();
}
bool
HostReservationIdsParser4::isSupportedIdentifier(const std::string& id_name) const {
return (getSupportedParams4(true).count(id_name) > 0);
}
HostReservationIdsParser6::HostReservationIdsParser6()
: HostReservationIdsParser() {
staging_cfg_ = CfgMgr::instance().getStagingCfg()->getCfgHostReservations6();
}
bool
HostReservationIdsParser6::isSupportedIdentifier(const std::string& id_name) const {
return (getSupportedParams6(true).count(id_name) > 0);
}
} // end of namespace isc::dhcp
} // end of namespace isc
......@@ -146,6 +146,87 @@ protected:
};
/// @brief Parser for a list of host identifiers.
///
/// This is a parent parser class for parsing "host-reservation-identifiers"
/// global configuration parmeter. The DHCPv4 and DHCPv6 specific parsers
/// derive from this class.
class HostReservationIdsParser : public DhcpConfigParser {
public:
/// @brief Constructor.
HostReservationIdsParser();
/// @brief Parses a list of host identifiers.
///
/// @param ids_list Data element pointing to an ordered list of host
/// identifier names.
///
/// @throw DhcpConfigError If specified configuration is invalid.
virtual void build(isc::data::ConstElementPtr ids_list);
/// @brief Commit, unused.
virtual void commit() { }
protected:
/// @brief Checks if specified identifier name is supported in the
/// context of the parser.
///
/// This is abstract method which must be implemented in the derived
/// parser classes for DHCPv4 and DHCPv6.
///
/// @param id_name Identifier name.
/// @return true if the specified identifier is supported, false
/// otherwise.
virtual bool isSupportedIdentifier(const std::string& id_name) const = 0;
/// @brief Pointer to the object holding configuration.
CfgHostReservationsPtr staging_cfg_;
};
/// @brief Parser for a list of host identifiers for DHCPv4.
class HostReservationIdsParser4 : public HostReservationIdsParser {
public:
/// @brief Constructor.
///
/// Initializes staging configuration pointer to the one used for DHCPv4
/// configuration.
HostReservationIdsParser4();
protected:
/// @brief Checks if specified identifier name is supported for DHCPv4.
///
/// @param id_name Identifier name.
/// @return true if the specified identifier is supported, false
/// otherwise.
virtual bool isSupportedIdentifier(const std::string& id_name) const;
};
/// @brief Parser for a list of host identifiers for DHCPv6.
class HostReservationIdsParser6 : public HostReservationIdsParser {
public:
/// @brief Constructor.
///
/// Initializes staging configuration pointer to the one used for DHCPv6
/// configuration.
HostReservationIdsParser6();
protected:
/// @brief Checks if specified identifier name is supported for DHCPv6.
///
/// @param id_name Identifier name.
/// @return true if the specified identifier is supported, false
/// otherwise.
virtual bool isSupportedIdentifier(const std::string& id_name) const;
};
}
} // end of namespace isc
......
......@@ -25,6 +25,8 @@ SrvConfig::SrvConfig()
cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
cfg_db_access_(new CfgDbAccess()),
cfg_host_reservations4_(CfgHostReservations::createConfig4()),
cfg_host_reservations6_(CfgHostReservations::createConfig6()),
class_dictionary_(new ClientClassDictionary()),
decline_timer_(0) {
}
......@@ -36,6 +38,8 @@ SrvConfig::SrvConfig(const uint32_t sequence)
cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
cfg_db_access_(new CfgDbAccess()),
cfg_host_reservations4_(CfgHostReservations::createConfig4()),
cfg_host_reservations6_(CfgHostReservations::createConfig6()),
class_dictionary_(new ClientClassDictionary()),
decline_timer_(0) {
}
......
......@@ -10,6 +10,7 @@
#include <dhcpsrv/cfg_db_access.h>
#include <dhcpsrv/cfg_duid.h>
#include <dhcpsrv/cfg_expiration.h>
#include <dhcpsrv/cfg_host_reservations.h>
#include <dhcpsrv/cfg_hosts.h>
#include <dhcpsrv/cfg_iface.h>
#include <dhcpsrv/cfg_option.h>
......@@ -291,6 +292,30 @@ public:
return (cfg_db_access_);
}
/// @brief Returns pointer to the object holding general configuration
/// for host reservations in DHCPv4.
CfgHostReservationsPtr getCfgHostReservations4() {
return (cfg_host_reservations4_);
}
/// @brief Returns const pointer to the object holding general
/// configuration for host reservations in DHCPv4
ConstCfgHostReservationsPtr getCfgHostReservations4() const {
return (cfg_host_reservations4_);
}
/// @brief Returns pointer to the object holding general configuration
/// for host reservations in DHCPv6.
CfgHostReservationsPtr getCfgHostReservations6() {
return (cfg_host_reservations6_);
}
/// @brief Returns const pointer to the object holding general
/// configuration for host reservations in DHCPv6
ConstCfgHostReservationsPtr getCfgHostReservations6() const {
return (cfg_host_reservations6_);
}
//@}
/// @brief Returns non-const reference to an array that stores
......@@ -502,6 +527,14 @@ private:
/// connection parameters.
CfgDbAccessPtr cfg_db_access_;
/// @brief Pointer to the general configuration for host reservations in
/// DHCPv4.
CfgHostReservationsPtr cfg_host_reservations4_;
/// @brief Pointer to the general configuration for host reservations in
/// DHCPv6.
CfgHostReservationsPtr cfg_host_reservations6_;
/// @brief Pointer to the control-socket information
isc::data::ConstElementPtr control_socket_;
......
......@@ -60,4 +60,24 @@ TEST(CfgHostReservationsTest, addIdentifier) {
EXPECT_TRUE(cfg.getIdentifierTypes().empty());
}
// This test verfies that the default DHCPv4 configuration is created
// as expected.
TEST(CfgHostReservationsTest, createConfig4) {
CfgHostReservationsPtr cfg = CfgHostReservations::createConfig4();
EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_HWADDR));
EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_DUID));
EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_CIRCUIT_ID));
}
// This test verfies that the default DHCPv6 configuration is created
// as expected.
TEST(CfgHostReservationsTest, createConfig6) {
CfgHostReservationsPtr cfg = CfgHostReservations::createConfig6();
EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_HWADDR));
EXPECT_TRUE(identifierAdded(*cfg, Host::IDENT_DUID));
EXPECT_FALSE(identifierAdded(*cfg, Host::IDENT_CIRCUIT_ID));
}
} // end of anonymous namespace
......@@ -783,5 +783,175 @@ TEST_F(HostReservationParserTest, mutuallyExclusiveIdentifiers6) {
}
}
/// @brief Test fixture class for @ref HostReservationIdsParser.
class HostReservationIdsParserTest : public ::testing::Test {
public:
/// @brief Constructor.
///
/// Clears current configuration.
HostReservationIdsParserTest() {
CfgMgr::instance().clear();
}
/// @brief Destructor.
///
/// Clears current configuration.
virtual ~HostReservationIdsParserTest() {
CfgMgr::instance().clear();
}
/// @brief Test verifies that invalid configuration causes an error.
///
/// @param config Configuration string.
/// @tparam ParserType @ref HostReservationIdsParser4 or
/// @ref HostReservationIdsParser6
template<typename ParserType>
void testInvalidConfig(const std::string& config) const {
ElementPtr config_element = Element::fromJSON(config);
ParserType parser;
EXPECT_THROW(parser.build(config_element), DhcpConfigError);
}
};
// Test that list of supported DHCPv4 identifiers list is correctly
// parsed.
TEST_F(HostReservationIdsParserTest, dhcp4Identifiers) {
std::string config = "[ \"circuit-id\", \"duid\", \"hw-address\" ]";
ElementPtr config_element = Element::fromJSON(config);
HostReservationIdsParser4 parser;
ASSERT_NO_THROW(parser.build(config_element));
ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
getCfgHostReservations4();
const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
ASSERT_EQ(3, ids.size());
CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
EXPECT_EQ(*id++, Host::IDENT_CIRCUIT_ID);
EXPECT_EQ(*id++, Host::IDENT_DUID);
EXPECT_EQ(*id++, Host::IDENT_HWADDR);
}
// Test that list of supported DHCPv6 identifiers list is correctly
// parsed.
TEST_F(HostReservationIdsParserTest, dhcp6Identifiers) {
std::string config = "[ \"duid\", \"hw-address\" ]";
ElementPtr config_element = Element::fromJSON(config);
HostReservationIdsParser6 parser;
ASSERT_NO_THROW(parser.build(config_element));
ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
getCfgHostReservations6();
const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
ASSERT_EQ(2, ids.size());
CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
EXPECT_EQ(*id++, Host::IDENT_DUID);
EXPECT_EQ(*id++, Host::IDENT_HWADDR);
}
// Test that invalid DHCPv4 identifier causes error.
TEST_F(HostReservationIdsParserTest, dhcp4InvalidIdentifier) {
// Create configuration including unsupported identifier.
std::string config = "[ \"unsupported-id\" ]";
testInvalidConfig<HostReservationIdsParser4>(config);
}
// Test that invalid DHCPv6 identifier causes error.
TEST_F(HostReservationIdsParserTest, dhcp6InvalidIdentifier) {
// Create configuration including unsupported identifier for DHCPv6.
// The circuit-id is only supported in DHCPv4.
std::string config = "[ \"circuit-id\" ]";
testInvalidConfig<HostReservationIdsParser6>(config);
}
// Check that all supported identifiers are used when 'auto' keyword
// is specified for DHCPv4 case.
TEST_F(HostReservationIdsParserTest, dhcp4AutoIdentifiers) {
std::string config = "[ \"auto\" ]";
ElementPtr config_element = Element::fromJSON(config);
HostReservationIdsParser4 parser;
ASSERT_NO_THROW(parser.build(config_element));
ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
getCfgHostReservations4();
const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
ASSERT_EQ(3, ids.size());
CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
EXPECT_EQ(*id++, Host::IDENT_HWADDR);
EXPECT_EQ(*id++, Host::IDENT_DUID);
EXPECT_EQ(*id++, Host::IDENT_CIRCUIT_ID);
}
// This test verifies that use of "auto" together with an explicit
// identifier causes an error. "auto" is placed before the explicit
// identifier.
TEST_F(HostReservationIdsParserTest, dhcp4AutoBeforeIdentifier) {
std::string config = "[ \"auto\", \"duid\" ]";
testInvalidConfig<HostReservationIdsParser4>(config);
}
// This test verifies that use of "auto" together with an explicit
// identifier causes an error. "auto" is placed after the explicit
// identifier.
TEST_F(HostReservationIdsParserTest, dhcp4AutoAfterIdentifier) {
std::string config = "[ \"duid\", \"auto\" ]";
testInvalidConfig<HostReservationIdsParser4>(config);
}
// Test that empty list of identifier types is not allowed.
TEST_F(HostReservationIdsParserTest, dhcp4EmptyList) {
std::string config = "[ ]";
testInvalidConfig<HostReservationIdsParser4>(config);
}
// Check that all supported identifiers are used when 'auto' keyword
// is specified for DHCPv6 case.
TEST_F(HostReservationIdsParserTest, dhcp6AutoIdentifiers) {
std::string config = "[ \"auto\" ]";
ElementPtr config_element = Element::fromJSON(config);
HostReservationIdsParser6 parser;
ASSERT_NO_THROW(parser.build(config_element));
ConstCfgHostReservationsPtr cfg = CfgMgr::instance().getStagingCfg()->
getCfgHostReservations6();
const CfgHostReservations::IdentifierTypes& ids = cfg->getIdentifierTypes();
ASSERT_EQ(2, ids.size());
CfgHostReservations::IdentifierTypes::const_iterator id = ids.begin();
EXPECT_EQ(*id++, Host::IDENT_HWADDR);
EXPECT_EQ(*id++, Host::IDENT_DUID);
}
// This test verifies that use of "auto" together with an explicit
// identifier causes an error. "auto" is placed before the explicit
// identifier.
TEST_F(HostReservationIdsParserTest, dhcp6AutoBeforeIdentifier) {
std::string config = "[ \"auto\", \"duid\" ]";
testInvalidConfig<HostReservationIdsParser6>(config);
}
// This test verifies that use of "auto" together with an explicit
// identifier causes an error. "auto" is placed after the explicit
// identifier.
TEST_F(HostReservationIdsParserTest, dhcp6AutoAfterIdentifier) {
std::string config = "[ \"duid\", \"auto\" ]";
testInvalidConfig<HostReservationIdsParser6>(config);
}
// Test that empty list of identifier types is not allowed.
TEST_F(HostReservationIdsParserTest, dhcp6EmptyList) {
std::string config = "[ ]";
testInvalidConfig<HostReservationIdsParser6>(config);
}
} // end of anonymous namespace
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