Parcourir la source

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

Marcin Siodelski il y a 9 ans
Parent
commit
6e22807310

+ 18 - 1
src/lib/dhcpsrv/cfg_host_reservations.cc

@@ -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);

+ 23 - 11
src/lib/dhcpsrv/cfg_host_reservations.h

@@ -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;
-
-//@}
-
 }
 }
 

+ 4 - 0
src/lib/dhcpsrv/host.h

@@ -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

+ 80 - 2
src/lib/dhcpsrv/parsers/host_reservation_parser.cc

@@ -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

+ 81 - 0
src/lib/dhcpsrv/parsers/host_reservation_parser.h

@@ -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

+ 4 - 0
src/lib/dhcpsrv/srv_config.cc

@@ -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) {
 }

+ 33 - 0
src/lib/dhcpsrv/srv_config.h

@@ -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_;
 

+ 20 - 0
src/lib/dhcpsrv/tests/cfg_host_reservations_unittest.cc

@@ -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

+ 170 - 0
src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc

@@ -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