Commit 7e86696d authored by Marcin Siodelski's avatar Marcin Siodelski

[#99,!197] Implemented subnets merge in the CfgSubnets4.

parent 0043833d
// 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>();
......
// 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
......
// 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() {
......
// 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
......
......@@ -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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment