Browse Source

[2315] Added storage for option definitions.

Marcin Siodelski 12 years ago
parent
commit
c6981a0eed

+ 9 - 1
src/lib/dhcp/option_definition.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -42,6 +42,14 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+/// @brief Exception to be thrown when the particular option definition
+/// duplicates existing option definition.
+class DuplicateOptionDefinition : public Exception {
+public:
+    DuplicateOptionDefinition(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief Forward declaration to OptionDefinition.
 class OptionDefinition;
 

+ 47 - 4
src/lib/dhcpsrv/cfgmgr.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -21,15 +21,54 @@ using namespace isc::util;
 namespace isc {
 namespace dhcp {
 
-
-
-
 CfgMgr&
 CfgMgr::instance() {
     static CfgMgr cfg_mgr;
     return (cfg_mgr);
 }
 
+void
+CfgMgr::addOptionDef(const OptionDefinitionPtr& def,
+                     const std::string& option_space) {
+    // @todo we need better validation of the provided option space name here.
+    // This will be implemented when #2313 is merged.
+    if (option_space.empty()) {
+        isc_throw(BadValue, "option space name must not be empty");
+    } else if (!def) {
+        isc_throw(MalformedOptionDefinition, "option definition must not be NULL");
+    } else if (getOptionDef(option_space, def->getCode())) {
+        isc_throw(DuplicateOptionDefinition, "option definition already added"
+                  << " to option space " << option_space);
+    }
+    option_def_spaces_[option_space].push_back(def);
+}
+
+const OptionDefContainer&
+CfgMgr::getOptionDefs(const std::string& option_space) const {
+    const std::map<std::string, OptionDefContainer>::const_iterator& defs =
+        option_def_spaces_.find(option_space);
+    if (defs == option_def_spaces_.end()) {
+        static OptionDefContainer empty_container;
+        return (empty_container);
+    }
+    return (defs->second);
+}
+
+OptionDefinitionPtr
+CfgMgr::getOptionDef(const std::string& option_space,
+                     const uint16_t option_code) const {
+    const OptionDefContainer& defs = getOptionDefs(option_space);
+    if (defs.empty()) {
+        return (OptionDefinitionPtr());
+    }
+    const OptionDefContainerTypeIndex& idx = defs.get<1>();
+    const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
+    if (std::distance(range.first, range.second) == 0) {
+        return (OptionDefinitionPtr());
+    }
+    return (*range.first);
+}
+
 Subnet6Ptr
 CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
 
@@ -101,6 +140,10 @@ void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
     subnets4_.push_back(subnet);
 }
 
+void CfgMgr::deleteOptionDefs() {
+    option_def_spaces_.clear();
+}
+
 void CfgMgr::deleteSubnets4() {
     subnets4_.clear();
 }

+ 46 - 1
src/lib/dhcpsrv/cfgmgr.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -17,6 +17,7 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/option.h>
+#include <dhcp/option_definition.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/subnet.h>
 #include <util/buffer.h>
@@ -77,6 +78,34 @@ public:
     /// accessing it.
     static CfgMgr& instance();
 
+    /// @brief Add new option definition.
+    ///
+    /// @param def option definition to be added.
+    ///
+    /// @throw isc::dhcp::DuplicateOptionDefinition when the particular
+    /// option definition already exists.
+    void addOptionDef(const OptionDefinitionPtr& def,
+                      const std::string& option_space);
+
+    /// @brief Return option definitions for particular option space.
+    ///
+    /// @param option_space option space.
+    ///
+    /// @return collection of option definitions for a particular
+    /// option space.
+    const OptionDefContainer&
+    getOptionDefs(const std::string& option_space) const;
+
+    /// @brief Return option definition for a particular option space and code.
+    ///
+    /// @param option_space option space.
+    /// @param option_code option code.
+    ///
+    /// @return an option definition or NULL pointer if option definition
+    /// has not been found.
+    OptionDefinitionPtr getOptionDef(const std::string& option_space,
+                                     const uint16_t option_code) const;
+
     /// @brief get IPv6 subnet by address
     ///
     /// Finds a matching subnet, based on an address. This can be used
@@ -86,6 +115,8 @@ public:
     ///    (for directly connected clients)
     ///
     /// @param hint an address that belongs to a searched subnet
+    ///
+    /// @return a subnet object
     Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
 
     /// @brief get IPv6 subnet by interface-id
@@ -93,12 +124,19 @@ public:
     /// Another possibility to find a subnet is based on interface-id.
     ///
     /// @param interface_id content of interface-id option returned by a relay
+    ///
+    /// @return a subnet object
     /// @todo This method is not currently supported.
     Subnet6Ptr getSubnet6(OptionPtr interface_id);
 
     /// @brief adds an IPv6 subnet
+    ///
+    /// @param subnet new subnet to be added.
     void addSubnet6(const Subnet6Ptr& subnet);
 
+    /// @brief Delete all option definitions.
+    void deleteOptionDefs();
+
     /// @todo: Add subnet6 removal routines. Currently it is not possible
     /// to remove subnets. The only case where subnet6 removal would be
     /// needed is a dynamic server reconfiguration - a use case that is not
@@ -125,6 +163,8 @@ public:
     ///    (for directly connected clients)
     ///
     /// @param hint an address that belongs to a searched subnet
+    ///
+    /// @return a subnet object
     Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint);
 
     /// @brief adds a subnet4
@@ -169,6 +209,11 @@ protected:
     /// pattern will use calling inRange() method on each subnet until
     /// a match is found.
     Subnet4Collection subnets4_;
+
+private:
+
+    /// A map containing option definitions for different option spaces.
+    std::map<std::string, OptionDefContainer> option_def_spaces_;
 };
 
 } // namespace isc::dhcp

+ 109 - 1
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc

@@ -43,9 +43,118 @@ public:
 
     ~CfgMgrTest() {
         CfgMgr::instance().deleteSubnets6();
+        CfgMgr::instance().deleteOptionDefs();
     }
 };
 
+// This test verifies that multiple option definitions can be added
+// under different option spaces.
+TEST_F(CfgMgrTest, getOptionDefs) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    // Create a set of option definitions with codes between 100 and 109.
+    for (uint16_t code = 100; code < 110; ++code) {
+        std::ostringstream option_name;
+        // Option name is unique, e.g. option-100, option-101 etc.
+        option_name << "option-" << code;
+        OptionDefinitionPtr def(new OptionDefinition(option_name.str(), code,
+                                                     "uint16"));
+        // Add option definition to "isc" option space.
+        // Option codes are not duplicated so expect no error
+        // when adding them.
+        ASSERT_NO_THROW(cfg_mgr.addOptionDef(def, "isc"));
+    }
+
+    // Create a set of option definitions with codes between 105 and 114 and
+    // add them to the different option space.
+    for (uint16_t code = 105; code < 115; ++code) {
+        std::ostringstream option_name;
+        option_name << "option-" << code;
+        OptionDefinitionPtr def(new OptionDefinition(option_name.str(), code,
+                                                     "uint16"));
+        ASSERT_NO_THROW(cfg_mgr.addOptionDef(def, "abcde"));
+    }
+
+    // Sanity check that all 10 option definitions are there.
+    const OptionDefContainer& option_defs1 = cfg_mgr.getOptionDefs("isc");
+    ASSERT_EQ(10, option_defs1.size());
+
+    // Iterate over all option definitions and check that they have
+    // valid codes. Also, their order should be the same as they
+    // were added (codes 100-109).
+    uint16_t code = 100;
+    for (OptionDefContainer::const_iterator it = option_defs1.begin();
+         it != option_defs1.end(); ++it, ++code) {
+        OptionDefinitionPtr def(*it);
+        ASSERT_TRUE(def);
+        EXPECT_EQ(code, def->getCode());
+    }
+
+    // Sanity check that all 10 option definitions are there.
+    const OptionDefContainer& option_defs2 = cfg_mgr.getOptionDefs("abcde");
+    ASSERT_EQ(10, option_defs2.size());
+
+    // Check that the option codes are valid.
+    code = 105;
+    for (OptionDefContainer::const_iterator it = option_defs2.begin();
+         it != option_defs2.end(); ++it, ++code) {
+        OptionDefinitionPtr def(*it);
+        ASSERT_TRUE(def);
+        EXPECT_EQ(code, def->getCode());
+    }
+
+    // Let's make one more check that the empty set is returned when
+    // invalid option space is used.
+    const OptionDefContainer& option_defs3 = cfg_mgr.getOptionDefs("non-existing");
+    ASSERT_TRUE(option_defs3.empty());
+}
+
+// This test verifies that single option definition is correctly
+// returned with getOptionDef function.
+TEST_F(CfgMgrTest, getOptionDef) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    // Create a set of option definitions with codes between 100 and 109.
+    for (uint16_t code = 100; code < 110; ++code) {
+        std::ostringstream option_name;
+        // Option name is unique, e.g. option-100, option-101 etc.
+        option_name << "option-" << code;
+        OptionDefinitionPtr def(new OptionDefinition(option_name.str(), code,
+                                                     "uint16"));
+        // Add option definition to "isc" option space.
+        // Option codes are not duplicated so expect no error
+        // when adding them.
+        ASSERT_NO_THROW(cfg_mgr.addOptionDef(def, "isc"));
+    }
+
+    // Create a set of option definitions with codes between 105 and 114 and
+    // add them to the different option space.
+    for (uint16_t code = 105; code < 115; ++code) {
+        std::ostringstream option_name;
+        option_name << "option-" << code;
+        OptionDefinitionPtr def(new OptionDefinition(option_name.str(), code,
+                                                     "uint16"));
+        ASSERT_NO_THROW(cfg_mgr.addOptionDef(def, "abcde"));
+    }
+
+    // Try to get option definitions one by one using all codes
+    // that we expect to be there.
+    for (uint16_t code = 100; code < 110; ++code) {
+        OptionDefinitionPtr def = cfg_mgr.getOptionDef("isc", code);
+        ASSERT_TRUE(def);
+        EXPECT_EQ(code, def->getCode());
+    }
+
+    // Check that the option codes are valid.
+    for (uint16_t code = 105; code < 115; ++code) {
+        OptionDefinitionPtr def = cfg_mgr.getOptionDef("abcde", code);
+        ASSERT_TRUE(def);
+        EXPECT_EQ(code, def->getCode());
+    }
+
+    // Try to query the option definition from an non-existing
+    // option space and expect NULL pointer.
+    OptionDefinitionPtr def = cfg_mgr.getOptionDef("non-existing", 56);
+    ASSERT_FALSE(def);
+}
 
 // This test verifies if the configuration manager is able to hold and return
 // valid leases
@@ -79,7 +188,6 @@ TEST_F(CfgMgrTest, subnet4) {
 
 // This test verifies if the configuration manager is able to hold and return
 // valid leases
-
 TEST_F(CfgMgrTest, subnet6) {
     CfgMgr& cfg_mgr = CfgMgr::instance();