From: Marcin Siodelski Date: Thu, 3 Jan 2013 17:27:14 +0000 (+0100) Subject: [2315] Added storage for option definitions. X-Git-Tag: bind10-1.0.0-rc-release~95^2~25^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c6981a0eed4cb0c68f747fa7f758e53dbfdaf5f7;p=thirdparty%2Fkea.git [2315] Added storage for option definitions. --- diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h index 9f6bef2c99..c9ffedfa8e 100644 --- a/src/lib/dhcp/option_definition.h +++ b/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; diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc index 7dc5f555d9..4d7e2cd894 100644 --- a/src/lib/dhcpsrv/cfgmgr.cc +++ b/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::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(); } diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h index ac1b3f59a4..33682752dc 100644 --- a/src/lib/dhcpsrv/cfgmgr.h +++ b/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 #include +#include #include #include #include @@ -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 option_def_spaces_; }; } // namespace isc::dhcp diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc index 7b23bcf409..ca763d0373 100644 --- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc +++ b/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();