-// 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
.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>();
-// 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
/// @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
-// 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
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() {
-// 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
return (!equals(other));
}
+ virtual void merge(const ConfigBase& other);
+
/// @brief Equality operator.
///
/// It ignores the configuration sequence number when checking for
#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>
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) {