Commit 63d6755e authored by Marcin Siodelski's avatar Marcin Siodelski

[5314] Added indexing to subnet searches.

parent 62d94947
......@@ -34,6 +34,20 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) {
subnets_.push_back(subnet);
}
ConstSubnet4Ptr
CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
const auto& index = subnets_.get<SubnetIdIndexTag>();
auto subnet_it = index.find(subnet_id);
return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
}
ConstSubnet4Ptr
CfgSubnets4::getByPrefix(const std::string& subnet_text) const {
const auto& index = subnets_.get<SubnetPrefixIndexTag>();
auto subnet_it = index.find(subnet_text);
return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
}
Subnet4Ptr
CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
......
......@@ -10,8 +10,10 @@
#include <asiolink/io_address.h>
#include <cc/cfg_to_element.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/subnet_selector.h>
#include <boost/shared_ptr.hpp>
#include <string>
namespace isc {
namespace dhcp {
......@@ -48,6 +50,40 @@ public:
return (&subnets_);
}
/// @brief Returns const pointer to a subnet identified by the specified
/// subnet identifier.
///
/// The const pointer is returned by this method to prevent a caller from
/// modifying the subnet configuration. Modifications to subnet configuration
/// is dangerous and must be done carefully. The subnets' configruation is
/// held in the multi index container and any modifications to the subnet
/// id or subnet prefix must trigger re-indexing of multi index container.
/// There is no possibility to enforce this when the non-const pointer is
/// returned.
///
/// @param subnet_id Subnet identifier.
///
/// @return Pointer to the @c Subnet4 object or null pointer if such
/// subnet doesn't exist.
ConstSubnet4Ptr getBySubnetId(const SubnetID& subnet_id) const;
/// @brief Returns const pointer to a subnet which matches the specified
/// prefix in the canonical form.
///
/// The const pointer is returned by this method to prevent a caller from
/// modifying the subnet configuration. Modifications to subnet configuration
/// is dangerous and must be done carefully. The subnets' configruation is
/// held in the multi index container and any modifications to the subnet
/// id or subnet prefix must trigger re-indexing of multi index container.
/// There is no possibility to enforce this when the non-const pointer is
/// returned.
///
/// @param subnet_prefix Subnet prefix, e.g. 10.2.3.0/24
///
/// @return Pointer to the @c Subnet4 object or null pointer if such
/// subnet doesn't exist.
ConstSubnet4Ptr getByPrefix(const std::string& subnet_prefix) const;
/// @brief Returns a pointer to the selected subnet.
///
/// This method tries to retrieve the subnet for the client using various
......
......@@ -33,6 +33,20 @@ CfgSubnets6::add(const Subnet6Ptr& subnet) {
subnets_.push_back(subnet);
}
ConstSubnet6Ptr
CfgSubnets6::getBySubnetId(const SubnetID& subnet_id) const {
const auto& index = subnets_.get<SubnetIdIndexTag>();
auto subnet_it = index.find(subnet_id);
return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
}
ConstSubnet6Ptr
CfgSubnets6::getByPrefix(const std::string& subnet_text) const {
const auto& index = subnets_.get<SubnetPrefixIndexTag>();
auto subnet_it = index.find(subnet_text);
return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet6Ptr());
}
Subnet6Ptr
CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
Subnet6Ptr subnet;
......
// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2017 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
......@@ -11,9 +11,11 @@
#include <dhcp/option.h>
#include <cc/cfg_to_element.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/subnet_selector.h>
#include <util/optional_value.h>
#include <boost/shared_ptr.hpp>
#include <string>
namespace isc {
namespace dhcp {
......@@ -49,6 +51,40 @@ public:
return (&subnets_);
}
/// @brief Returns const pointer to a subnet identified by the specified
/// subnet identifier.
///
/// The const pointer is returned by this method to prevent a caller from
/// modifying the subnet configuration. Modifications to subnet configuration
/// is dangerous and must be done carefully. The subnets' configruation is
/// held in the multi index container and any modifications to the subnet
/// id or subnet prefix must trigger re-indexing of multi index container.
/// There is no possibility to enforce this when the non-const pointer is
/// returned.
///
/// @param subnet_id Subnet identifier.
///
/// @return Pointer to the @c Subnet6 object or null pointer if such
/// subnet doesn't exist.
ConstSubnet6Ptr getBySubnetId(const SubnetID& subnet_id) const;
/// @brief Returns const pointer to a subnet which matches the specified
/// prefix in the canonical form.
///
/// The const pointer is returned by this method to prevent a caller from
/// modifying the subnet configuration. Modifications to subnet configuration
/// is dangerous and must be done carefully. The subnets' configruation is
/// held in the multi index container and any modifications to the subnet
/// id or subnet prefix must trigger re-indexing of multi index container.
/// There is no possibility to enforce this when the non-const pointer is
/// returned.
///
/// @param subnet_prefix Subnet prefix, e.g. 2001:db8:1::/64
///
/// @return Pointer to the @c Subnet6 object or null pointer if such
/// subnet doesn't exist.
ConstSubnet6Ptr getByPrefix(const std::string& subnet_prefix) const;
/// @brief Selects a subnet using parameters specified in the selector.
///
/// This method tries to retrieve the subnet for the client using various
......
......@@ -19,6 +19,11 @@
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/triplet.h>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/shared_ptr.hpp>
namespace isc {
......@@ -623,16 +628,12 @@ private:
Cfg4o6 dhcp4o6_;
};
/// @brief A pointer to a @c Subnet4 object
/// @brief A const pointer to a @c Subnet4 object.
typedef boost::shared_ptr<const Subnet4> ConstSubnet4Ptr;
/// @brief A pointer to a @c Subnet4 object.
typedef boost::shared_ptr<Subnet4> Subnet4Ptr;
/// @brief A collection of @c Subnet4 objects
///
/// That is a simple vector of pointers. It does not make much sense to
/// optimize access time (e.g. using a map), because typical search
/// pattern will use calling inRange() method on each subnet until
/// a match is found.
typedef std::vector<Subnet4Ptr> Subnet4Collection;
/// @brief A configuration holder for IPv6 subnet.
///
......@@ -731,11 +732,83 @@ private:
};
/// @brief A const pointer to a @c Subnet6 object.
typedef boost::shared_ptr<const Subnet6> ConstSubnet6Ptr;
/// @brief A pointer to a Subnet6 object
typedef boost::shared_ptr<Subnet6> Subnet6Ptr;
/// @brief A collection of Subnet6 objects
typedef std::vector<Subnet6Ptr> Subnet6Collection;
/// @name Definition of the multi index container holding subnet information
///
//@{
/// @brief Tag for the random access index.
struct SubnetRandomAccessIndexTag { };
/// @brief Tag for the index for searching by subnet identifier.
struct SubnetIdIndexTag { };
/// @brief Tag for the index for searching by subnet prefix.
struct SubnetPrefixIndexTag { };
/// @brief Multi index container holding subnets.
///
/// This multi index container can hold pointers to @ref Subnet4 or
/// @ref Subnet6 objects representing subnets. It provides indexes for
/// subnet lookups using subnet properties such as: subnet identifier
/// or subnet prefix. It also provides a random access index which
/// allows for using the container like a vector.
///
/// The random access index is used by the DHCP servers which perform
/// a full scan on subnets to find the one that matches some specific
/// criteria for subnet selection.
///
/// The remaining indexes are used for searching for a specific subnet
/// as a result of receiving a command over the control API, e.g.
/// when 'subnet-get' command is received.
///
/// @todo We should consider optimizing subnet selection by leveraging
/// the indexing capabilities of this container, e.g. searching for
/// a subnet by interface name, relay address etc.
///
/// @tparam SubnetType Type of the subnet: @ref Subnet4 or @ref Subnet6.
template<typename SubnetType>
using SubnetCollection = boost::multi_index_container<
// Multi index container holds pointers to the subnets.
boost::shared_ptr<SubnetType>,
// The following holds all indexes.
boost::multi_index::indexed_by<
// First is the random access index allowing for accessing
// objects just like we'd do with a vector.
boost::multi_index::random_access<
boost::multi_index::tag<SubnetRandomAccessIndexTag>
>,
// Second index allows for searching using subnet identifier.
boost::multi_index::ordered_unique<
boost::multi_index::tag<SubnetIdIndexTag>,
boost::multi_index::const_mem_fun<Subnet, SubnetID, &Subnet::getID>
>,
// Third index allows for searching using an output from toText function.
boost::multi_index::ordered_unique<
boost::multi_index::tag<SubnetTextIndexTag>,
boost::multi_index::const_mem_fun<Subnet, std::string, &Subnet::toText>
>
>
>;
/// @brief A collection of @c Subnet4 objects
///
/// This container provides a set of indexes which can be used to retrieve
/// subnets by various properties.
typedef SubnetCollection<Subnet4> Subnet4Collection;
/// @brief A collection of @c Subnet6 objects
///
/// This container provides a set of indexes which can be used to retrieve
/// subnets by various properties.
typedef SubnetCollection<Subnet6> Subnet6Collection;
//@}
} // end of isc::dhcp namespace
} // end of isc namespace
......
......@@ -529,6 +529,8 @@ AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start,
AllocEngine4Test::AllocEngine4Test() {
CfgMgr::instance().clear();
// This lease mgr needs to exist to before configuration commits.
factory_.create("type=memfile universe=4 persist=false");
......
......@@ -23,6 +23,57 @@ using namespace isc::test;
namespace {
// This test verifies that specific subnet can be retrieved by specifying
// subnet identifier or subnet prefix.
TEST(CfgSubnets4Test, getSpecificSubnet) {
CfgSubnets4 cfg;
// Create 3 subnets.
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"),
26, 1, 2, 3, SubnetID(5)));
Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"),
26, 1, 2, 3, SubnetID(8)));
Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"),
26, 1, 2, 3, SubnetID(10)));
// Store the subnets in a vector to make it possible to loop over
// all configured subnets.
std::vector<Subnet4Ptr> subnets;
subnets.push_back(subnet1);
subnets.push_back(subnet2);
subnets.push_back(subnet3);
// Add all subnets to the configuration.
for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) {
ASSERT_NO_THROW(cfg.add(*subnet)) << "failed to add subnet with id: "
<< (*subnet)->getID();
}
// Iterate over all subnets and make sure they can be retrieved by
// subnet identifier.
for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
ConstSubnet4Ptr subnet_returned = cfg.getBySubnetId((*subnet)->getID());
ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
<< (*subnet)->getID();
EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
}
// Repeat the previous test, but this time retrieve subnets by their
// prefixes.
for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
ConstSubnet4Ptr subnet_returned = cfg.getByPrefix((*subnet)->toText());
ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
<< (*subnet)->getID();
EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
}
// Make sure that null pointers are returned for non-existing subnets.
EXPECT_FALSE(cfg.getBySubnetId(SubnetID(123)));
EXPECT_FALSE(cfg.getByPrefix("10.20.30.0/29"));
}
// This test verifies that it is possible to retrieve a subnet using an
// IP address.
TEST(CfgSubnets4Test, selectSubnetByCiaddr) {
......
......@@ -32,6 +32,57 @@ generateInterfaceId(const std::string& text) {
return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, buffer));
}
// This test verifies that specific subnet can be retrieved by specifying
// subnet identifier or subnet prefix.
TEST(CfgSubnets6Test, getSpecificSubnet) {
CfgSubnets6 cfg;
// Create 3 subnets.
Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4,
SubnetID(5)));
Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4,
SubnetID(8)));
Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4,
SubnetID(10)));
// Store the subnets in a vector to make it possible to loop over
// all configured subnets.
std::vector<Subnet6Ptr> subnets;
subnets.push_back(subnet1);
subnets.push_back(subnet2);
subnets.push_back(subnet3);
// Add all subnets to the configuration.
for (auto subnet = subnets.cbegin(); subnet != subnets.cend(); ++subnet) {
ASSERT_NO_THROW(cfg.add(*subnet)) << "failed to add subnet with id: "
<< (*subnet)->getID();
}
// Iterate over all subnets and make sure they can be retrieved by
// subnet identifier.
for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
ConstSubnet6Ptr subnet_returned = cfg.getBySubnetId((*subnet)->getID());
ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
<< (*subnet)->getID();
EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
}
// Repeat the previous test, but this time retrieve subnets by their
// prefixes.
for (auto subnet = subnets.rbegin(); subnet != subnets.rend(); ++subnet) {
ConstSubnet6Ptr subnet_returned = cfg.getByPrefix((*subnet)->toText());
ASSERT_TRUE(subnet_returned) << "failed to return subnet with id: "
<< (*subnet)->getID();
EXPECT_EQ((*subnet)->getID(), subnet_returned->getID());
EXPECT_EQ((*subnet)->toText(), subnet_returned->toText());
}
// Make sure that null pointers are returned for non-existing subnets.
EXPECT_FALSE(cfg.getBySubnetId(SubnetID(123)));
EXPECT_FALSE(cfg.getByPrefix("3000::/16"));
}
// This test checks that the subnet can be selected using a relay agent's
// link address.
TEST(CfgSubnets6Test, selectSubnetByRelayAddress) {
......
......@@ -51,10 +51,10 @@ public:
test_subnets4_.push_back(subnet);
}
// Create IPv6 subnets.
IOAddress prefix("2001:db8:1::0");
for (int i = 0; i < TEST_SUBNETS_NUM; ++i) {
// This is a base prefix. All other prefixes will be created by
// modifying this one.
IOAddress prefix("2001:db8:1::0");
std::vector<uint8_t> prefix_bytes = prefix.toBytes();
// Modify 5th byte of the prefix, so 2001:db8:1::0 becomes
// 2001:db8:2::0 etc.
......@@ -239,7 +239,6 @@ TEST_F(SrvConfigTest, summarySubnets) {
addSubnet6(1);
EXPECT_EQ("added IPv4 subnets: 2; added IPv6 subnets: 2",
conf_.getConfigSummary(SrvConfig::CFGSEL_SUBNET));
}
// Verifies that we can get and set the client class dictionary
......
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