]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#103,!289] Added new function to delete options by id.
authorMarcin Siodelski <marcin@isc.org>
Thu, 4 Apr 2019 12:23:57 +0000 (14:23 +0200)
committerMarcin Siodelski <marcin@isc.org>
Wed, 10 Apr 2019 14:57:43 +0000 (16:57 +0200)
src/lib/dhcpsrv/cfg_option.cc
src/lib/dhcpsrv/cfg_option.h
src/lib/dhcpsrv/tests/cfg_option_unittest.cc

index 85a8d7d84c2b4714fd0271773401cfadbadc3206..f85914acbcfe62e85a5bb4b0933012019f3a9378 100644 (file)
@@ -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
index bd54ff59bd826a1d15478660c2275025afe81b3b..9de6ae513b40a286b0c2b3489debaaedfe238723 100644 (file)
@@ -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,
index 21c6a0b99538ae83e8b96de48301d10ca285613d..cc77262c4b5e9665fceec74f2292617f3bedeb17 100644 (file)
@@ -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<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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<uint64_t>(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;