From: Marcin Siodelski Date: Thu, 10 Jan 2019 11:07:14 +0000 (+0100) Subject: [#99,!197] Implemented subnets merge in the CfgSubnets4. X-Git-Tag: 100-implement-test-config-backend-dhcp6_base~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e86696d14a3c83a645c8c7d740d7058c939a019;p=thirdparty%2Fkea.git [#99,!197] Implemented subnets merge in the CfgSubnets4. --- diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 754d318e9b..6491aec905 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.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 @@ -55,6 +55,75 @@ CfgSubnets4::del(const ConstSubnet4Ptr& subnet) { .arg(subnet->toText()); } +void +CfgSubnets4::merge(const CfgSubnets4& other) { + auto& index = subnets_.get(); + + // 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(); diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index 38a0853fe3..0d35e31b06 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -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 diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 12a22db902..7e61f2c0c0 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.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 @@ -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(other); + + } catch (const std::bad_cast&) { + } +} + void SrvConfig::removeStatistics() { diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h index eb9fe718bc..16129c712c 100644 --- a/src/lib/dhcpsrv/srv_config.h +++ b/src/lib/dhcpsrv/srv_config.h @@ -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 diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index 10f25af74a..6c1adc0b16 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -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) {