From: Marcin Siodelski Date: Thu, 4 Apr 2019 12:23:57 +0000 (+0200) Subject: [#103,!289] Added new function to delete options by id. X-Git-Tag: Kea-1.6.0-beta~275 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=32aa024fa0818d97b7a79101424ad9944d7121f4;p=thirdparty%2Fkea.git [#103,!289] Added new function to delete options by id. --- diff --git a/src/lib/dhcpsrv/cfg_option.cc b/src/lib/dhcpsrv/cfg_option.cc index 85a8d7d84c..f85914acbc 100644 --- a/src/lib/dhcpsrv/cfg_option.cc +++ b/src/lib/dhcpsrv/cfg_option.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -46,8 +46,13 @@ CfgOption::equals(const CfgOption& other) const { void CfgOption::add(const OptionPtr& option, const bool persistent, - const std::string& option_space) { - add(OptionDescriptor(option, persistent), option_space); + const std::string& option_space, + const uint64_t id) { + OptionDescriptor desc(option, persistent); + if (id > 0) { + desc.setId(id); + } + add(desc, option_space); } void @@ -362,6 +367,42 @@ CfgOption::del(const uint32_t vendor_id, const uint16_t option_code) { return (idx.erase(option_code)); } +size_t +CfgOption::del(const uint64_t id) { + // Hierarchical nature of the options configuration requires that + // we go over all options and decapsulate them before removing + // any of them. Let's walk over the existing option spaces. + for (auto space_name : getOptionSpaceNames()) { + // Get all options for the option space. + auto options = getAll(space_name); + for (auto option_it = options->begin(); option_it != options->end(); + ++option_it) { + if (!option_it->option_) { + continue; + } + + // For each option within the option space we need to dereference + // any existing sub options. + auto sub_options = option_it->option_->getOptions(); + for (auto sub = sub_options.begin(); sub != sub_options.end(); + ++sub) { + // Dereference sub option. + option_it->option_->delOption(sub->second->getType()); + } + } + } + + // Now that we got rid of dependencies between the instances of the options + // we can delete all options having a specified id. + size_t num_deleted = options_.deleteItems(id) + vendor_options_.deleteItems(id); + + // Let's encapsulate those options that remain in the configuration. + encapsulate(); + + // Return the number of deleted options. + return (num_deleted); +} + ElementPtr CfgOption::toElement() const { // option-data value is a list of maps diff --git a/src/lib/dhcpsrv/cfg_option.h b/src/lib/dhcpsrv/cfg_option.h index bd54ff59bd..9de6ae513b 100644 --- a/src/lib/dhcpsrv/cfg_option.h +++ b/src/lib/dhcpsrv/cfg_option.h @@ -327,10 +327,12 @@ public: /// @param persistent Boolean value which specifies if the option should /// be sent to the client regardless if requested (true), or nor (false) /// @param option_space Option space name. + /// @param id Optional database id to be associated with the option. /// /// @throw isc::BadValue if the option space is invalid. void add(const OptionPtr& option, const bool persistent, - const std::string& option_space); + const std::string& option_space, + const uint64_t id = 0); /// @brief A variant of the @ref CfgOption::add method which takes option /// descriptor as an argument. @@ -525,7 +527,7 @@ public: /// @return Number of deleted options. size_t del(const std::string& option_space, const uint16_t option_code); - /// @brief Delets vendor option for the specified vendor id. + /// @brief Deletes vendor option for the specified vendor id. /// /// @param vendor_id Vendor identifier. /// @param option_code Option code. @@ -533,6 +535,27 @@ public: /// @return Number of deleted options. size_t del(const uint32_t vendor_id, const uint16_t option_code); + /// @brief Deletes all options having a given database id. + /// + /// Note that there are cases when there will be multiple options + /// having the same id (typically id of 0). When configuration backend + /// is in use it sets the unique ids from the database. In cases when + /// the configuration backend is not used, the ids default to 0. + /// + /// Both regular and vendor specific options are deleted with this + /// method. + /// + /// This method internally calls @c encapsulate() after deleting + /// options having the given id. + /// + /// @param id Identifier of the options to be deleted. + /// + /// @return Number of deleted options. Note that if a single option + /// instance is encapsulated by multiple options it adds 1 to the + /// number of deleted options even though the same instance is + /// deleted from multiple higher level options. + size_t del(const uint64_t id); + /// @brief Returns a list of configured option space names. /// /// The returned option space names exclude vendor option spaces, diff --git a/src/lib/dhcpsrv/tests/cfg_option_unittest.cc b/src/lib/dhcpsrv/tests/cfg_option_unittest.cc index 21c6a0b995..cc77262c4b 100644 --- a/src/lib/dhcpsrv/tests/cfg_option_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_option_unittest.cc @@ -48,7 +48,11 @@ public: OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6, code, 1234)); option->setEncapsulatedSpace("foo"); - ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE)); + + // In order to easier identify the options by id, let's use the option + // code as the id. + ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE, + static_cast(code))); } // Create top level options encapsulating "bar" option space. @@ -56,7 +60,8 @@ public: OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6, code, 2345)); option->setEncapsulatedSpace("bar"); - ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE)); + ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE, + static_cast(code))); } // Create sub-options belonging to "foo" option space and encapsulating @@ -65,7 +70,7 @@ public: OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code, 0x01)); option->setEncapsulatedSpace("foo-subs"); - ASSERT_NO_THROW(cfg.add(option, false, "foo")); + ASSERT_NO_THROW(cfg.add(option, false, "foo", static_cast(code))); } // Create sub-options belonging to "bar" option space and encapsulating @@ -74,21 +79,23 @@ public: OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code, 0x02)); option->setEncapsulatedSpace("bar-subs"); - ASSERT_NO_THROW(cfg.add(option, false, "bar")); + ASSERT_NO_THROW(cfg.add(option, false, "bar", static_cast(code))); } // Create sub-options belonging to "foo-subs" option space. for (uint16_t code = 1; code < 10; ++code) { OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code, 0x03)); - ASSERT_NO_THROW(cfg.add(option, false, "foo-subs")); + ASSERT_NO_THROW(cfg.add(option, false, "foo-subs", + static_cast(code))); } // Create sub-options belonging to "bar-subs" option space. for (uint16_t code = 501; code < 510; ++code) { OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code, 0x04)); - ASSERT_NO_THROW(cfg.add(option, false, "bar-subs")); + ASSERT_NO_THROW(cfg.add(option, false, "bar-subs", + static_cast(code))); } } }; @@ -672,7 +679,7 @@ TEST_F(CfgOptionTest, encapsulate) { } // This test verifies that an option can be deleted from the configuration. -TEST_F(CfgOptionTest, delFromOptionSpace) { +TEST_F(CfgOptionTest, deleteOptions) { CfgOption cfg; generateEncapsulatedOptions(cfg); @@ -742,6 +749,52 @@ TEST_F(CfgOptionTest, delFromOptionSpace) { } } +// This test verifies that an option can be deleted from the configuration +// by database id. +TEST_F(CfgOptionTest, deleteOptionsById) { + CfgOption cfg; + + generateEncapsulatedOptions(cfg); + + // Append options from "foo" and "bar" space as sub-options and options + // from "foo-subs" and "bar-subs" as sub-options of "foo" and "bar" + // options. + ASSERT_NO_THROW(cfg.encapsulate()); + + // Create multiple vendor options for vendor id 123. + for (uint16_t code = 100; code < 110; ++code) { + OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF))); + ASSERT_NO_THROW(cfg.add(option, false, "vendor-123", static_cast(code))); + } + + // Delete options with id of 100. It includes both regular options and + // the vendor options. There are two options with id of 100. + EXPECT_EQ(2, cfg.del(100)); + + // Make sure that the option 100 was deleted but another option + // in the same option space was not. + EXPECT_FALSE(cfg.get("bar", 100).option_); + EXPECT_TRUE(cfg.get("bar", 101).option_); + + // Make sure that the deleted option was dereferenced from the + // top level options but that does not affect encapsulation of + // other options. + for (uint16_t option_code = 1020; option_code < 1040; ++option_code) { + auto top_level_option = cfg.get(DHCP6_OPTION_SPACE, option_code); + ASSERT_TRUE(top_level_option.option_); + EXPECT_FALSE(top_level_option.option_->getOption(100)); + + // The second level encapsulation should have been preserved. + auto second_level_option = top_level_option.option_->getOption(101); + ASSERT_TRUE(second_level_option); + EXPECT_TRUE(second_level_option->getOption(501)); + } + + // Vendor option with id of 100 should have been deleted too. + EXPECT_FALSE(cfg.get(123, 100).option_); + EXPECT_TRUE(cfg.get(123, 101).option_); +} + // This test verifies that a vendor option can be deleted from the configuration. TEST_F(CfgOptionTest, delVendorOption) { CfgOption cfg;