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

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

parent af0439c0
...@@ -15,12 +15,29 @@ CfgHostReservations::CfgHostReservations() ...@@ -15,12 +15,29 @@ CfgHostReservations::CfgHostReservations()
: identifier_types_() { : 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 void
CfgHostReservations::addIdentifierType(const std::string& identifier_name) { CfgHostReservations::addIdentifierType(const std::string& identifier_name) {
Host::IdentifierType identifier_type = Host::getIdentifierType(identifier_name); Host::IdentifierType identifier_type = Host::getIdentifierType(identifier_name);
if (std::find(identifier_types_.begin(), identifier_types_.end(), if (std::find(identifier_types_.begin(), identifier_types_.end(),
identifier_type) != 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_name << "'");
} }
identifier_types_.push_back(identifier_type); identifier_types_.push_back(identifier_type);
......
...@@ -15,6 +15,20 @@ ...@@ -15,6 +15,20 @@
namespace isc { namespace isc {
namespace dhcp { 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. /// @brief Represents global configuration for host reservations.
/// ///
/// This class represents server configuration pertaining to host /// This class represents server configuration pertaining to host
...@@ -37,6 +51,15 @@ public: ...@@ -37,6 +51,15 @@ public:
/// - no identifiers selected for host reservations searches. /// - no identifiers selected for host reservations searches.
CfgHostReservations(); 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 /// @brief Adds new identifier type to a collection of identifiers
/// to be used by the server to search for host reservations. /// to be used by the server to search for host reservations.
/// ///
...@@ -62,17 +85,6 @@ private: ...@@ -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: ...@@ -183,6 +183,10 @@ public:
IDENT_CIRCUIT_ID 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. /// @brief Constructor.
/// ///
/// Creates a @c Host object using an identifier in a binary format. This /// Creates a @c Host object using an identifier in a binary format. This
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <dhcpsrv/parsers/host_reservation_parser.h> #include <dhcpsrv/parsers/host_reservation_parser.h>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <algorithm>
#include <sys/socket.h> #include <sys/socket.h>
#include <sstream> #include <sstream>
#include <string> #include <string>
...@@ -37,8 +38,8 @@ getSupportedParams4(const bool identifiers_only = false) { ...@@ -37,8 +38,8 @@ getSupportedParams4(const bool identifiers_only = false) {
// If this is first execution of this function, we need // If this is first execution of this function, we need
// to initialize the set. // to initialize the set.
if (identifiers_set.empty()) { if (identifiers_set.empty()) {
identifiers_set.insert("duid");
identifiers_set.insert("hw-address"); identifiers_set.insert("hw-address");
identifiers_set.insert("duid");
identifiers_set.insert("circuit-id"); identifiers_set.insert("circuit-id");
} }
// Copy identifiers and add all other parameters. // Copy identifiers and add all other parameters.
...@@ -68,8 +69,8 @@ getSupportedParams6(const bool identifiers_only = false) { ...@@ -68,8 +69,8 @@ getSupportedParams6(const bool identifiers_only = false) {
// If this is first execution of this function, we need // If this is first execution of this function, we need
// to initialize the set. // to initialize the set.
if (identifiers_set.empty()) { if (identifiers_set.empty()) {
identifiers_set.insert("duid");
identifiers_set.insert("hw-address"); identifiers_set.insert("hw-address");
identifiers_set.insert("duid");
} }
// Copy identifiers and add all other parameters. // Copy identifiers and add all other parameters.
if (params_set.empty()) { if (params_set.empty()) {
...@@ -317,5 +318,82 @@ HostReservationParser6::getSupportedParameters(const bool identifiers_only) cons ...@@ -317,5 +318,82 @@ HostReservationParser6::getSupportedParameters(const bool identifiers_only) cons
return (getSupportedParams6(identifiers_only)); 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::dhcp
} // end of namespace isc } // end of namespace isc
...@@ -146,6 +146,87 @@ protected: ...@@ -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 } // end of namespace isc
......
...@@ -25,6 +25,8 @@ SrvConfig::SrvConfig() ...@@ -25,6 +25,8 @@ SrvConfig::SrvConfig()
cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()), cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()), cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
cfg_db_access_(new CfgDbAccess()), cfg_db_access_(new CfgDbAccess()),
cfg_host_reservations4_(CfgHostReservations::createConfig4()),
cfg_host_reservations6_(CfgHostReservations::createConfig6()),
class_dictionary_(new ClientClassDictionary()), class_dictionary_(new ClientClassDictionary()),
decline_timer_(0) { decline_timer_(0) {
} }
...@@ -36,6 +38,8 @@ SrvConfig::SrvConfig(const uint32_t sequence) ...@@ -36,6 +38,8 @@ SrvConfig::SrvConfig(const uint32_t sequence)
cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()), cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()), cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
cfg_db_access_(new CfgDbAccess()), cfg_db_access_(new CfgDbAccess()),
cfg_host_reservations4_(CfgHostReservations::createConfig4()),
cfg_host_reservations6_(CfgHostReservations::createConfig6()),
class_dictionary_(new ClientClassDictionary()), class_dictionary_(new ClientClassDictionary()),
decline_timer_(0) { decline_timer_(0) {
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <dhcpsrv/cfg_db_access.h> #include <dhcpsrv/cfg_db_access.h>
#include <dhcpsrv/cfg_duid.h> #include <dhcpsrv/cfg_duid.h>
#include <dhcpsrv/cfg_expiration.h> #include <dhcpsrv/cfg_expiration.h>
#include <dhcpsrv/cfg_host_reservations.h>
#include <dhcpsrv/cfg_hosts.h> #include <dhcpsrv/cfg_hosts.h>
#include <dhcpsrv/cfg_iface.h> #include <dhcpsrv/cfg_iface.h>
#include <dhcpsrv/cfg_option.h> #include <dhcpsrv/cfg_option.h>
...@@ -291,6 +292,30 @@ public: ...@@ -291,6 +292,30 @@ public:
return (cfg_db_access_); 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 /// @brief Returns non-const reference to an array that stores
...@@ -502,6 +527,14 @@ private: ...@@ -502,6 +527,14 @@ private:
/// connection parameters. /// connection parameters.
CfgDbAccessPtr cfg_db_access_; 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 /// @brief Pointer to the control-socket information
isc::data::ConstElementPtr control_socket_; isc::data::ConstElementPtr control_socket_;
......
...@@ -60,4 +60,24 @@ TEST(CfgHostReservationsTest, addIdentifier) { ...@@ -60,4 +60,24 @@ TEST(CfgHostReservationsTest, addIdentifier) {
EXPECT_TRUE(cfg.getIdentifierTypes().empty()); 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 } // end of anonymous namespace
...@@ -783,5 +783,175 @@ TEST_F(HostReservationParserTest, mutuallyExclusiveIdentifiers6) { ...@@ -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.