]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#99,!197] Implemented subnets merge in the CfgSubnets4.
authorMarcin Siodelski <marcin@isc.org>
Thu, 10 Jan 2019 11:07:14 +0000 (12:07 +0100)
committerMarcin Siodelski <marcin@isc.org>
Mon, 14 Jan 2019 12:18:47 +0000 (07:18 -0500)
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_subnets4_unittest.cc

index 754d318e9ba174d48ba3c7e0e7311ed68e1672f9..6491aec905102c670afb544e281df883c8527a7d 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
@@ -55,6 +55,75 @@ CfgSubnets4::del(const ConstSubnet4Ptr& subnet) {
         .arg(subnet->toText());
 }
 
+void
+CfgSubnets4::merge(const CfgSubnets4& other) {
+    auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
+
+    // Iterate over the subnets to be merged. They will replace the existing
+    // subnets with the same id. All new subnets will be inserted into the
+    // configuration into which we're merging.
+    auto other_subnets = other.getAll();
+    for (auto other_subnet = other_subnets->begin();
+         other_subnet != other_subnets->end();
+         ++other_subnet) {
+
+        // Check if there is a subnet with the same ID.
+        auto subnet_it = index.find((*other_subnet)->getID());
+        if (subnet_it != index.end()) {
+
+            // Subnet found.
+            auto subnet = *subnet_it;
+
+            // Continue if the merged and existing subnets are the same instance.
+            if (subnet == *other_subnet) {
+                continue;
+            }
+
+            // If the merged subnet belongs to a shared network we need to
+            // discard this shared network so as it is merged into the existing
+            // shared network or left not unassigned if the existing subnet
+            // is unassigned.
+            SharedNetwork4Ptr other_network;
+            (*other_subnet)->getSharedNetwork(other_network);
+            if (other_network) {
+                other_network->del((*other_subnet)->getID());
+            }
+
+            // Check if the subnet belongs to a shared network.
+            SharedNetwork4Ptr network;
+            subnet->getSharedNetwork(network);
+            if (network) {
+                // The subnet belongs to a shared network. The shared network
+                // instance holds a pointer to the subnet so we need to remove
+                // the existing subnet from the shared network it belongs to.
+                network->del(subnet->getID());
+
+                // The new subnet instance must be added to the existing shared
+                // network.
+                network->add(*other_subnet);
+            }
+
+            // The existing subnet may now be removed.
+            index.erase(subnet_it);
+        }
+    }
+
+    // Make another pass over the merged subnets to add them. Any existing
+    // instances with the same IDs have been removed.
+    for (auto other_subnet = other_subnets->begin();
+         other_subnet != other_subnets->end();
+         ++other_subnet) {
+
+        // Continue if the merged and existing subnets are the same instance.
+        auto subnet_it = index.find((*other_subnet)->getID());
+        if ((subnet_it != index.end()) && ((*subnet_it) == (*other_subnet))) {
+            continue;
+        }
+
+        subnets_.push_back(*other_subnet);
+    }
+}
+
 ConstSubnet4Ptr
 CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
     const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
index 38a0853fe3a834698c9cfd3d29222e813ba900d9..0d35e31b069113bc7523f33f04b128bc44868ad8 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
@@ -47,6 +47,43 @@ public:
     /// @throw isc::BadValue if such subnet doesn't exist.
     void del(const ConstSubnet4Ptr& subnet);
 
+    /// @brief Merges specified subnet configuration into this configuration.
+    ///
+    /// This method merges subnets from the @c other configuration into this
+    /// configuration. The general rule is that existing subnets are replaced
+    /// by the subnets from @c other. If there is no corresponding subnet in
+    /// this configuration the subnet from @c other configuration is inserted.
+    ///
+    /// The complexity of the merge process stems from the associations between
+    /// the subnets and shared networks. Although, the subnets in this
+    /// configuration are replaced by the subnets from @c other, the existing
+    /// shared networks should not be affected. The new subnets must be
+    /// inserted into the exsiting shared networks. The @c CfgSharedNetworks4
+    /// is responsible for merging the shared networks and this merge must
+    /// be triggered before the merge of the subnets. Therefore, this method
+    /// assumes that existing shared networks have been already merged.
+    ///
+    /// These are the rules concerning the shared network associations that
+    /// this method follows:
+    /// - If there is a subnet in this configuration and it is associated with
+    ///   a shared network, the shared network is preserved and the new subnet
+    ///   instance (replacing existing one) is associated with it. The old
+    ///   subnet instance is removed from the shared network.
+    /// - If there is a subnet in this configuration and it is not associated
+    ///   with any shared network, the new subnet instance replaces the existing
+    ///   subnet instance and its association with a shared network is discarded.
+    ///   As a result, the configuration will contain new subnet instance but
+    ///   not associated with any shared network.
+    /// - If there is no subnet with the given ID, the new subnet instance is
+    ///   inserted into the configuration and the association with a shared
+    ///   network (if present) will be preserved. As a result, the configuration
+    ///   will hold the instance of the new subnet with the shared network
+    ///   it originally belonged to.
+    ///
+    /// @param other the subnet configuration to be merged into this
+    /// configuration.
+    void merge(const CfgSubnets4& other);
+
     /// @brief Returns pointer to the collection of all IPv4 subnets.
     ///
     /// This is used in a hook (subnet4_select), where the hook is able
index 12a22db90258bc71c7df9279d4c9f3dd80f10266..7e61f2c0c0814295dddabe2ba450fd58bf8ea9f4 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
@@ -156,6 +156,17 @@ SrvConfig::equals(const SrvConfig& other) const {
     return (hooks_config_.equal(other.hooks_config_));
 }
 
+void
+SrvConfig::merge(const ConfigBase& other) {
+    ConfigBase::merge(other);
+
+    try {
+        const SrvConfig& other_srv_config = dynamic_cast<const SrvConfig&>(other);
+
+    } catch (const std::bad_cast&) {
+    }
+}
+
 void
 SrvConfig::removeStatistics() {
 
index eb9fe718bc58ef8375bfce370d3afdf8c55ce1de..16129c712c97311cc49607c4b4083c7c82e99405 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
@@ -448,6 +448,8 @@ public:
         return (!equals(other));
     }
 
+    virtual void merge(const ConfigBase& other);
+
     /// @brief Equality operator.
     ///
     /// It ignores the configuration sequence number when checking for
index 10f25af74ac3942935d4d04a7a929df60208f9ef..6c1adc0b168096b95f9ea09400018fcd1c1bc5eb 100644 (file)
@@ -14,6 +14,7 @@
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/shared_network.h>
 #include <dhcpsrv/cfg_subnets4.h>
+#include <dhcpsrv/shared_network.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_selector.h>
@@ -110,6 +111,112 @@ TEST(CfgSubnets4Test, deleteSubnet) {
     EXPECT_FALSE(cfg.getByPrefix("192.0.3.0/26"));
 }
 
+// This test verifies that the subnets configuration is properly merged.
+TEST(CfgSubnets4Test, mergeSubnets) {
+    CfgSubnets4 cfg_to;
+    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"),
+                                   26, 1, 2, 3, SubnetID(5)));
+    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.3.0"),
+                                   26, 1, 2, 3, SubnetID(8)));
+    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.4.0"),
+                                   26, 1, 2, 3, SubnetID(10)));
+    ASSERT_NO_THROW(cfg_to.add(subnet1));
+    ASSERT_NO_THROW(cfg_to.add(subnet2));
+    ASSERT_NO_THROW(cfg_to.add(subnet3));
+
+    SharedNetwork4Ptr shared_network1(new SharedNetwork4("shared-network1"));
+    ASSERT_NO_THROW(shared_network1->add(subnet1));
+
+    SharedNetwork4Ptr shared_network2(new SharedNetwork4("shared-network2"));
+    ASSERT_NO_THROW(shared_network2->add(subnet2));
+
+    CfgSubnets4 cfg_from;
+
+    ASSERT_NO_THROW(cfg_to.merge(cfg_from));
+    ASSERT_EQ(3, cfg_to.getAll()->size());
+
+    SharedNetwork4Ptr returned_network;
+
+    // The subnet1 should not be modified and should still belong
+    // to the same shared network.
+    auto returned_subnet1 = cfg_to.getByPrefix("192.0.2.0/26");
+    ASSERT_TRUE(returned_subnet1);
+    returned_subnet1->getSharedNetwork(returned_network);
+    EXPECT_TRUE(shared_network1 == returned_network);
+
+    // The subnet2 should not be modified and should still belong
+    // to the same shared network.
+    auto returned_subnet2 = cfg_to.getByPrefix("192.0.3.0/26");
+    ASSERT_TRUE(returned_subnet2);
+    returned_subnet2->getSharedNetwork(returned_network);
+    EXPECT_TRUE(shared_network2 == returned_network);
+
+    // The subnet3 should not be modified.
+    auto returned_subnet3 = cfg_to.getByPrefix("192.0.4.0/26");
+    ASSERT_TRUE(returned_subnet3);
+    returned_subnet3->getSharedNetwork(returned_network);
+    EXPECT_FALSE(returned_network);
+
+    // Fill cfg_from configuration with subnets.
+    Subnet4Ptr subnet4(new Subnet4(IOAddress("192.0.2.0"),
+                                   26, 2, 3, 4, SubnetID(5)));
+    Subnet4Ptr subnet5(new Subnet4(IOAddress("192.0.6.0"),
+                                   26, 1, 2, 3, SubnetID(32)));
+    Subnet4Ptr subnet6(new Subnet4(IOAddress("192.0.4.0"),
+                                   26, 3, 4, 5, SubnetID(10)));
+    ASSERT_NO_THROW(cfg_from.add(subnet4));
+    ASSERT_NO_THROW(cfg_from.add(subnet5));
+    ASSERT_NO_THROW(cfg_from.add(subnet6));
+
+    // First two subnets belong to shared networks.
+    SharedNetwork4Ptr shared_network3(new SharedNetwork4("shared-network3"));
+    ASSERT_NO_THROW(shared_network3->add(subnet4));
+
+    SharedNetwork4Ptr shared_network4(new SharedNetwork4("shared-network4"));
+    ASSERT_NO_THROW(shared_network4->add(subnet5));
+
+    SharedNetwork4Ptr shared_network5(new SharedNetwork4("shared-network5"));
+    ASSERT_NO_THROW(shared_network5->add(subnet6));
+
+    // Merge again. The subnet4 and subnet6 should replace the subnet1 and
+    // subnet3.
+    ASSERT_NO_THROW(cfg_to.merge(cfg_from));
+    ASSERT_EQ(4, cfg_to.getAll()->size());
+
+    returned_subnet1 = cfg_to.getByPrefix("192.0.2.0/26");
+    ASSERT_TRUE(returned_subnet1);
+    EXPECT_EQ(4, returned_subnet1->getValid());
+    // The subnet1 should be replaced by subnet4 but the shared network
+    // should not be affected.
+    returned_subnet1->getSharedNetwork(returned_network);
+    EXPECT_TRUE(shared_network1 == returned_network);
+
+    // The subnet2 should not be affected because it was not present
+    // in the cfg_from.
+    returned_subnet2 = cfg_to.getByPrefix("192.0.3.0/26");
+    ASSERT_TRUE(returned_subnet2);
+    EXPECT_EQ(3, returned_subnet2->getValid());
+    returned_subnet2->getSharedNetwork(returned_network);
+    EXPECT_TRUE(shared_network2 == returned_network);
+
+    returned_subnet3 = cfg_to.getByPrefix("192.0.4.0/26");
+    ASSERT_TRUE(returned_subnet3);
+    EXPECT_EQ(5, returned_subnet3->getValid());
+    // subnet3 should be replaced by subnet6 but the shared network
+    // should not be assigned (regardless if the subnet6 belongs to
+    // a shared network or not).
+    returned_subnet3->getSharedNetwork(returned_network);
+    EXPECT_FALSE(returned_network);
+
+    // subnet5 should be merged to the configuration.
+    auto returned_subnet5 = cfg_to.getByPrefix("192.0.6.0/26");
+    ASSERT_TRUE(returned_subnet5);
+    EXPECT_EQ(3, returned_subnet5->getValid());
+    // subnet5 shared network should be preserved.
+    returned_subnet5->getSharedNetwork(returned_network);
+    EXPECT_TRUE(shared_network4 == returned_network);
+}
+
 // This test verifies that it is possible to retrieve a subnet using an
 // IP address.
 TEST(CfgSubnets4Test, selectSubnetByCiaddr) {