: 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);
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
/// - 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.
///
};
-/// @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;
-
-//@}
-
}
}
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
#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>
// 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.
// 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()) {
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
};
+/// @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
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) {
}
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) {
}
#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>
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
/// 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_;
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
}
}
+/// @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