-// 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
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
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
/// @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.
/// @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.
/// @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,
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.
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
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
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)));
}
}
};
}
// This test verifies that an option can be deleted from the configuration.
-TEST_F(CfgOptionTest, delFromOptionSpace) {
+TEST_F(CfgOptionTest, deleteOptions) {
CfgOption cfg;
generateEncapsulatedOptions(cfg);
}
}
+// 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;