]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#400,!243] kea-dhcp4 now merges in option definitions
authorThomas Markwalder <tmark@isc.org>
Tue, 26 Feb 2019 13:25:54 +0000 (08:25 -0500)
committerThomas Markwalder <tmark@isc.org>
Tue, 26 Feb 2019 13:25:54 +0000 (08:25 -0500)
src/bin/dhcp4/tests/config_backend_unittest.cc
    TEST_F(Dhcp4CBTest, mergeGlobals) - enabled/revamped

src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc
    TEST(CfgOptionDefTest, merge) - new test

src/lib/dhcpsrv/cfg_option_def.*
    CfgOptionDef::merge(CfgOptionDef& other)  - new method
    which merges option definitions

Removed const from "other" parameter in in the following merge methods:

    CfgSharedNetworks4::merge(CfgSharedNetworks4& other)
    CfgSubnets4::merge(CfgSharedNetworks4Ptr networks, CfgSubnets4& other);
    SrvConfig::merge(ConfigBase& other)
    SrvConfig::merge4(SrvConfig& other)
    SrvConfig::mergeGlobals4(SrvConfig& other)
    ConfigBase::merge(ConfigBase& other)

12 files changed:
src/bin/dhcp4/tests/config_backend_unittest.cc
src/lib/dhcpsrv/cfg_option_def.cc
src/lib/dhcpsrv/cfg_option_def.h
src/lib/dhcpsrv/cfg_shared_networks.cc
src/lib/dhcpsrv/cfg_shared_networks.h
src/lib/dhcpsrv/cfg_subnets4.cc
src/lib/dhcpsrv/cfg_subnets4.h
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/srv_config.h
src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc
src/lib/process/config_base.cc
src/lib/process/config_base.h

index 4a446cc92887736005167df027ec64d3e697030a..cd620ef62375673a7456b67f0aaed4ab9d581072 100644 (file)
@@ -259,16 +259,22 @@ TEST_F(Dhcp4CBTest, mergeGlobals) {
 
 // This test verifies that externally configured option definitions
 // merged correctly into staging configuration.
-// @todo enable test when SrvConfig can merge option definitions.
-TEST_F(Dhcp4CBTest, DISABLED_mergeOptionDefs) {
+TEST_F(Dhcp4CBTest, mergeOptionDefs) {
     string base_config =
         "{ \n"
-        "    \"option-def\": [ {"
-        "        \"name\": \"one\","
-        "        \"code\": 100,"
-        "        \"type\": \"ipv4-address\","
-        "        \"space\": \"isc\""
-        "     } ],"
+        "    \"option-def\": [ { \n"
+        "        \"name\": \"one\", \n"
+        "        \"code\": 1, \n"
+        "        \"type\": \"ipv4-address\", \n"
+        "        \"space\": \"isc\" \n"
+        "     }, \n"
+        "     { \n"
+        "        \"name\": \"two\", \n"
+        "        \"code\": 2, \n"
+        "        \"type\": \"string\", \n"
+        "        \"space\": \"isc\" \n"
+        "     } \n"
+        "   ], \n"
         "    \"config-control\": { \n"
         "       \"config-databases\": [ { \n"
         "               \"type\": \"memfile\", \n"
@@ -283,33 +289,47 @@ TEST_F(Dhcp4CBTest, DISABLED_mergeOptionDefs) {
 
     extractConfig(base_config);
 
-    // Create option two and add it to first backend.
-    OptionDefinitionPtr def_two(new OptionDefinition("two", 234, "string"));
-    def_two->setOptionSpaceName("dhcp4");
-    db1_->createUpdateOptionDef4(ServerSelector::ALL(), def_two);
+    // Create option one replacement and add it to first backend.
+    OptionDefinitionPtr def;
+    def.reset(new OptionDefinition("one", 101, "uint16"));
+    def->setOptionSpaceName("isc");
+    db1_->createUpdateOptionDef4(ServerSelector::ALL(), def);
 
-    // Create option three and add it to second backend.
-    OptionDefinitionPtr def_three(new OptionDefinition("three", 235, "string"));
-    def_three->setOptionSpaceName("dhcp4");
-    db2_->createUpdateOptionDef4(ServerSelector::ALL(), def_two);
+    // Create option three and add it to first backend.
+    def.reset(new OptionDefinition("three", 3, "string"));
+    def->setOptionSpaceName("isc");
+    db1_->createUpdateOptionDef4(ServerSelector::ALL(), def);
+
+    // Create option four and add it to second backend.
+    def.reset(new OptionDefinition("four", 4, "string"));
+    def->setOptionSpaceName("isc");
+    db2_->createUpdateOptionDef4(ServerSelector::ALL(), def);
 
     // Should parse and merge without error.
     ASSERT_NO_FATAL_FAILURE(configure(base_config, CONTROL_RESULT_SUCCESS, ""));
 
     // Verify the composite staging is correct.
     SrvConfigPtr staging_cfg = CfgMgr::instance().getStagingCfg();
-
-    // Option definition from JSON should be there.
     ConstCfgOptionDefPtr option_defs = staging_cfg->getCfgOptionDef();
-    OptionDefinitionPtr found_def = option_defs->get("isc", 100);
+
+    // Definition "one" from first backend should be there.
+    OptionDefinitionPtr found_def = option_defs->get("isc", "one");
+    ASSERT_TRUE(found_def);
+    EXPECT_EQ(101, found_def->getCode());
+    EXPECT_EQ(OptionDataType::OPT_UINT16_TYPE, found_def->getType());
+
+    // Definition "two" from JSON config should be there.
+    found_def = option_defs->get("isc", "two");
     ASSERT_TRUE(found_def);
+    EXPECT_EQ(2, found_def->getCode());
 
-    // Option definition from db1 should be there.
-    found_def = option_defs->get("dhcp4", 234);
+    // Definition "three" from first backened should be there.
+    found_def = option_defs->get("isc", "three");
     ASSERT_TRUE(found_def);
+    EXPECT_EQ(3, found_def->getCode());
 
-    // Option definition from db2 should not be there.
-    found_def = option_defs->get("dhcp4", 235);
+    // Definition "four" from first backened should not be there.
+    found_def = option_defs->get("isc", "four");
     ASSERT_FALSE(found_def);
 }
 
index 42a92267677ece2ae848033711f360fd0178e7d4..6530c9e2c3892801ed20efbb5fab81b4bad11bd7 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015,2017 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
@@ -206,5 +206,36 @@ CfgOptionDef::toElement() const {
     return (result);
 }
 
+void
+CfgOptionDef::merge(CfgOptionDef& other) {
+
+    if (other.getContainer().getOptionSpaceNames().empty()) {
+        // Nothing to merge, don't waste cycles.
+        return;
+    }
+
+
+    // Iterate over this config's definitions in each space.
+    // If either a definition's name or code already exist in
+    // that space in "other", skip it.  Otherwise, add it to "other".
+    auto spaces = option_definitions_.getOptionSpaceNames();
+    for (auto space = spaces.begin(); space != spaces.end(); ++space) {
+        OptionDefContainerPtr my_defs = getAll(*space);
+        for (auto my_def = my_defs->begin(); my_def != my_defs->end(); ++my_def) {
+            if ((other.get(*space, (*my_def)->getName())) ||
+                (other.get(*space, (*my_def)->getCode()))) {
+                // Already in "other" so skip it.
+                continue;
+            }
+
+            // Not in "other" so add it.
+            other.add(*my_def, *space);
+        }
+    }
+
+    // Replace the current definitions with the merged set.
+    other.copyTo(*this);
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc
index 69e18fca977662253ee2eefc59234cdfa2a57e01..2571ab45e4cd02ca869d5f55eef125954b84d260 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015,2017 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
@@ -123,6 +123,27 @@ public:
     /// @return a pointer to unparsed configuration
     virtual isc::data::ElementPtr toElement() const;
 
+    /// @brief Merges specified option definitions configuration into this
+    /// configuration.
+    ///
+    /// This method merges the option defintiions from the @c other
+    /// configuration into this configuration.  The merged set of
+    /// definitions is created as follows:
+    ///
+    /// Iterator over the definitions in each name space in this configuration:
+    /// If either the definition's name or code are defined in @c other
+    /// then skip over the definition otherwise add it to @other.
+    ///
+    /// Replace this configuration's definitions with the defnitions
+    /// in @c other using @c copyTo().
+    ///
+    /// @warning The merge operation affects @c other.
+    /// Therefore, the caller must not rely on the data held in the @c other
+    /// object after the call to @c merge. Also, the data held in @c other must
+    /// not be modified after the call to @c merge because it may affect the
+    /// merged configuration.
+    void merge(CfgOptionDef& other);
+
 private:
 
     /// @brief A collection of option definitions.
index 3ab6e3626ebd018c4d0ab71522d16723161bf6e0..f0f02d3cc85647aeea03a069817a18c9c8e44149 100644 (file)
@@ -20,7 +20,7 @@ CfgSharedNetworks4::hasNetworkWithServerId(const IOAddress& server_id) const {
 }
 
 void
-CfgSharedNetworks4::merge(const CfgSharedNetworks4& other) {
+CfgSharedNetworks4::merge(CfgSharedNetworks4& other) {
     auto& index = networks_.get<SharedNetworkNameIndexTag>();
 
     // Iterate over the subnets to be merged. They will replace the existing
index 833b8234b9ea1e882d6343b6726b74a8206866b9..24a219d0ce58b2cee7903b1760dd01f7ad5208eb 100644 (file)
@@ -150,7 +150,7 @@ public:
     ///
     /// @param other the shared network configuration to be merged into this
     /// configuration.
-    void merge(const CfgSharedNetworks4& other);
+    void merge(CfgSharedNetworks4& other);
 };
 
 /// @brief Pointer to the configuration of IPv4 shared networks.
index fa097d329c9a97f8691cbdcf8d67481c66e09566..99e64893db9a4a23b82093a9e15d15a2118da6e6 100644 (file)
@@ -57,7 +57,7 @@ CfgSubnets4::del(const ConstSubnet4Ptr& subnet) {
 
 void
 CfgSubnets4::merge(CfgSharedNetworks4Ptr networks,
-                   const CfgSubnets4& other) {
+                   CfgSubnets4& other) {
     auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
 
     // Iterate over the subnets to be merged. They will replace the existing
index b6c1b80fb99a9d64943b947189707c76193c665c..79fa3664d394b120635200d48a4e027238af9c1a 100644 (file)
@@ -85,7 +85,7 @@ public:
     /// to the same SrvConfig instance we are merging into.
     /// @param other the subnet configuration to be merged into this
     /// configuration.
-    void merge(CfgSharedNetworks4Ptr networks, const CfgSubnets4& other);
+    void merge(CfgSharedNetworks4Ptr networks, CfgSubnets4& other);
 
     /// @brief Returns pointer to the collection of all IPv4 subnets.
     ///
index 162aedc17baa1d2d50776715b3a42f7d1d0fe804..f335fc5360a8e607e038ecdc8ceaf12f05d41f39 100644 (file)
@@ -159,10 +159,10 @@ SrvConfig::equals(const SrvConfig& other) const {
 }
 
 void
-SrvConfig::merge(const ConfigBase& other) {
+SrvConfig::merge(ConfigBase& other) {
     ConfigBase::merge(other);
     try {
-        const SrvConfig& other_srv_config = dynamic_cast<const SrvConfig&>(other);
+        SrvConfig& other_srv_config = dynamic_cast<SrvConfig&>(other);
         if (CfgMgr::instance().getFamily() == AF_INET) {
             merge4(other_srv_config);
         } else {
@@ -176,13 +176,14 @@ SrvConfig::merge(const ConfigBase& other) {
 }
 
 void
-SrvConfig::merge4(const SrvConfig& other) {
+SrvConfig::merge4(SrvConfig& other) {
     /// We merge objects in order of dependency (real or theoretical).
 
     /// Merge globals.
     mergeGlobals4(other);
 
-    /// @todo merge option defs
+    /// Merge option defs
+    cfg_option_def_->merge((*other.getCfgOptionDef()));
 
     /// @todo merge options
 
@@ -196,7 +197,7 @@ SrvConfig::merge4(const SrvConfig& other) {
 }
 
 void
-SrvConfig::mergeGlobals4(const SrvConfig& other) {
+SrvConfig::mergeGlobals4(SrvConfig& other) {
     // Iterate over the "other" globals, adding/overwriting them into
     // this config's list of globals.
     for (auto other_global : other.getConfiguredGlobals()->mapValue()) {
index 652cae0bd3e60c20723e63b881c781671172047f..7dcb361c78bb8434ab122989661ee300ecf06c18 100644 (file)
@@ -516,7 +516,7 @@ public:
     ///
     /// @param other An object holding the configuration to be merged
     /// into this configuration.
-    virtual void merge(const ConfigBase& other);
+    virtual void merge(ConfigBase& other);
 
     /// @brief Updates statistics.
     ///
@@ -662,7 +662,7 @@ private:
     ///
     /// @param other An object holding the configuration to be merged
     /// into this configuration.
-    void merge4(const SrvConfig& other);
+    void merge4(SrvConfig& other);
 
 
     /// @brief Merges the DHCPv4 globals specified in the given configuration
@@ -687,7 +687,7 @@ private:
     ///
     /// @param other An object holding the configuration to be merged
     /// into this configuration.
-    void mergeGlobals4(const SrvConfig& other);
+    void mergeGlobals4(SrvConfig& other);
 
     /// @brief Sequence number identifying the configuration.
     uint32_t sequence_;
index df7be812b3f08b2f81c848102490328b59b604f4..ec548202da617803bcacb7348b1b76c576b353c1 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017 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
@@ -250,7 +250,7 @@ TEST(CfgOptionDefTest, unparse) {
     CfgOptionDef cfg;
 
     // Add some options.
-    cfg.add(OptionDefinitionPtr(new 
+    cfg.add(OptionDefinitionPtr(new
         OptionDefinition("option-foo", 5, "uint16")), "isc");
     cfg.add(OptionDefinitionPtr(new
         OptionDefinition("option-bar", 5, "uint16", true)), "dns");
@@ -262,7 +262,7 @@ TEST(CfgOptionDefTest, unparse) {
     rec->addRecordField("uint16");
     rec->addRecordField("uint16");
     cfg.add(rec, "dns");
-    
+
     // Unparse
     std::string expected = "[\n"
         "{\n"
@@ -303,4 +303,50 @@ TEST(CfgOptionDefTest, unparse) {
     isc::test::runToElementTest<CfgOptionDef>(expected, cfg);
 }
 
+// This test verifies that configured option definitions can be merged
+// correctly.
+TEST(CfgOptionDefTest, merge) {
+    CfgOptionDef to;         // Configuration we are merging to.
+
+    // Add some options to the "to" config.
+    to.add((OptionDefinitionPtr(new OptionDefinition("one", 1, "uint16"))), "isc");
+    to.add((OptionDefinitionPtr(new OptionDefinition("two", 2, "uint16"))), "isc");
+    to.add((OptionDefinitionPtr(new OptionDefinition("three", 3, "uint16"))), "fluff");
+    to.add((OptionDefinitionPtr(new OptionDefinition("four", 4, "uint16"))), "fluff");
+
+    // Clone the "to" config and use that for merging.
+    CfgOptionDef to_clone;
+    to.copyTo(to_clone);
+
+    // Ensure they are equal before we do anything.
+    ASSERT_TRUE(to.equals(to_clone));
+
+    // Merge from an empty config.
+    CfgOptionDef empty_from;
+    ASSERT_NO_THROW(to_clone.merge(empty_from));
+
+    // Should have the same content as before.
+    ASSERT_TRUE(to.equals(to_clone));
+
+    // Construct a non-empty "from" config.
+    // Options "two" and "three" will be updated definitions and "five" will be new.
+    CfgOptionDef from;
+    from.add((OptionDefinitionPtr(new OptionDefinition("two", 22, "uint16"))), "isc");
+    from.add((OptionDefinitionPtr(new OptionDefinition("three", 3, "string"))), "fluff");
+    from.add((OptionDefinitionPtr(new OptionDefinition("five", 5, "uint16"))), "fluff");
+
+    // Now let's clone "from" config and use that manually construct the expected config.
+    CfgOptionDef expected;
+    from.copyTo(expected);
+    expected.add((OptionDefinitionPtr(new OptionDefinition("one", 1, "uint16"))), "isc");
+    expected.add((OptionDefinitionPtr(new OptionDefinition("four", 4, "uint16"))), "fluff");
+
+    // Do the merge.
+    ASSERT_NO_THROW(to_clone.merge(from));
+
+    // Verify we have the expected content.
+    ASSERT_TRUE(expected.equals(to_clone));
+}
+
+
 }
index 99f03fb2c823aea4099f2a7d46f59f106ff13e3a..47e4214414e885c614aaf6e5e5fb96c8ab4ab6b8 100644 (file)
@@ -83,7 +83,7 @@ ConfigBase::copy(ConfigBase& other) const {
 }
 
 void
-ConfigBase::merge(const ConfigBase& other) {
+ConfigBase::merge(ConfigBase& other) {
     // Merge logging info.
     if (!other.logging_info_.empty()) {
         logging_info_ = other.logging_info_;
index 8de72f2e92fa42f3249c93accfb6223ce5729b63..966a863b2b0d65ffd26f64ca043228d5c2e6a044 100644 (file)
@@ -77,7 +77,7 @@ public:
     ///
     /// @param other the other configuration to be merged into this
     /// configuration.
-    virtual void merge(const ConfigBase& other);
+    virtual void merge(ConfigBase& other);
 
     /// @brief Converts to Element representation
     ///