Subnet6Ptr original_subnet = ctx.subnet_;
Subnet6Ptr subnet = ctx.subnet_;
- SharedNetwork6Ptr network;
- subnet->getSharedNetwork(network);
Pool6Ptr pool;
}
uint64_t total_attempts = 0;
- subnet = original_subnet;
+
+ // Need to check if the subnet belongs to a shared network. If so,
+ // we might be able to find a better subnet for lease allocation,
+ // for which it is more likely that there are some leases available.
+ // If we stick to the selected subnet, we may end up walking over
+ // the entire subnet (or more subnets) to discover that the pools
+ // have been exhausted. Using a subnet from which a lease was
+ // assigned most recently is an optimization which increases
+ // the likelyhood of starting from the subnet which pools are not
+ // exhausted.
+ SharedNetwork6Ptr network;
+ original_subnet->getSharedNetwork(network);
+ if (network) {
+ // This would try to find a subnet with the same set of classes
+ // as the current subnet, but with the more recent "usage timestamp".
+ // This timestamp is only updated for the allocations made with an
+ // allocator (unreserved lease allocations), not the static
+ // allocations or requested addresses.
+ original_subnet = network->getPreferredSubnet(original_subnet, ctx.currentIA().type_);
+ }
+
+ ctx.subnet_ = subnet = original_subnet;
while (subnet) {
/// @param subnets Container holding subnets belonging to this shared
/// network.
/// @param selected_subnet Pointer to a currently selected subnet.
+ /// @param lease_type Type of the lease for which preferred subnet should be
+ /// returned.
///
/// @return Pointer to a preferred subnet. It may be the same as @c selected_subnet
/// if no better subnet was found.
template<typename SubnetPtrType, typename SubnetCollectionType>
static SubnetPtrType getPreferredSubnet(const SubnetCollectionType& subnets,
- const SubnetPtrType& selected_subnet) {
+ const SubnetPtrType& selected_subnet,
+ const Lease::Type& lease_type) {
- Subnet4Ptr preferred_subnet = selected_subnet;
+ auto preferred_subnet = selected_subnet;
for (auto s = subnets.begin(); s != subnets.end(); ++s) {
if (((*s)->getClientClasses() == selected_subnet->getClientClasses()) &&
- ((*s)->getLastAllocatedTime() > selected_subnet->getLastAllocatedTime())) {
+ ((*s)->getLastAllocatedTime(lease_type) >
+ selected_subnet->getLastAllocatedTime(lease_type))) {
preferred_subnet = (*s);
}
}
Subnet4Ptr
SharedNetwork4::getPreferredSubnet(const Subnet4Ptr& selected_subnet) const {
- return (Impl::getPreferredSubnet<Subnet4Ptr>(subnets_, selected_subnet));
+ return (Impl::getPreferredSubnet<Subnet4Ptr>(subnets_, selected_subnet,
+ Lease::TYPE_V4));
}
ElementPtr
return (Impl::getNextSubnet(subnets_, first_subnet, current_subnet));
}
+Subnet6Ptr
+SharedNetwork6::getPreferredSubnet(const Subnet6Ptr& selected_subnet,
+ const Lease::Type& lease_type) const {
+ return (Impl::getPreferredSubnet(subnets_, selected_subnet, lease_type));
+}
+
ElementPtr
SharedNetwork6::toElement() const {
ElementPtr map = Network6::toElement();
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 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
Subnet6Ptr getNextSubnet(const Subnet6Ptr& first_subnet,
const SubnetID& current_subnet) const;
+ /// @brief Attempts to find a subnet which is more likely to include available
+ /// leases than selected subnet.
+ ///
+ /// When allocating unreserved leases from a shared network it is important to
+ /// remember from which subnet within the shared network we have been recently
+ /// handing out leases. The allocation engine can use that information to start
+ /// trying allocation of the leases from that subnet rather than from the default
+ /// subnet selected for this client. Starting from the default subnet causes a
+ /// risk of having to walk over many subnets with exhausted address pools before
+ /// getting to the subnet with available leases. This method attempts to find
+ /// such subnet by inspecting "last allocation" timestamps. The one with most
+ /// recent timestamp is selected.
+ ///
+ /// The preferred subnet must also fulfil the condition of equal client classes
+ /// with the @c selected_subnet.
+ ///
+ /// @param selected_subnet Pointer to a currently selected subnet.
+ /// @param lease_type Type of the lease for which preferred subnet should be
+ /// returned.
+ ///
+ /// @return Pointer to a preferred subnet. It may be the same as @c selected_subnet
+ /// if no better subnet was found.
+ Subnet6Ptr getPreferredSubnet(const Subnet6Ptr& selected_subnet,
+ const Lease::Type& lease_type) const;
+
/// @brief Unparses shared network object.
///
/// @return A pointer to unparsed shared network configuration.
last_allocated_ia_(lastAddrInPrefix(prefix, len)),
last_allocated_ta_(lastAddrInPrefix(prefix, len)),
last_allocated_pd_(lastAddrInPrefix(prefix, len)),
- last_allocated_time_(boost::posix_time::neg_infin) {
+ last_allocated_time_() {
if ((prefix.isV6() && len > 128) ||
(prefix.isV4() && len > 32)) {
isc_throw(BadValue,
"Invalid prefix length specified for subnet: " << len);
}
+
+ // Initialize timestamps for each lease type to negative infinity.
+ last_allocated_time_[Lease::TYPE_V4] = boost::posix_time::neg_infin;
+ last_allocated_time_[Lease::TYPE_NA] = boost::posix_time::neg_infin;
+ last_allocated_time_[Lease::TYPE_TA] = boost::posix_time::neg_infin;
+ last_allocated_time_[Lease::TYPE_PD] = boost::posix_time::neg_infin;
}
bool
}
}
+boost::posix_time::ptime
+Subnet::getLastAllocatedTime(const Lease::Type& lease_type) const {
+ auto t = last_allocated_time_.find(lease_type);
+ if (t != last_allocated_time_.end()) {
+ return (t->second);
+ }
+
+ // This shouldn't happen, because we have initialized the structure
+ // for all lease types.
+ return (boost::posix_time::neg_infin);
+}
+
+
void Subnet::setLastAllocated(Lease::Type type,
const isc::asiolink::IOAddress& addr) {
}
// Update the timestamp of last allocation.
- last_allocated_time_ = boost::posix_time::microsec_clock::universal_time();
+ last_allocated_time_[type] = boost::posix_time::microsec_clock::universal_time();
}
std::string
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/pointer_cast.hpp>
#include <boost/shared_ptr.hpp>
+#include <map>
namespace isc {
namespace dhcp {
/// @brief Returns the timestamp when the @c setLastAllocated function
/// was called.
- boost::posix_time::ptime getLastAllocatedTime() const {
- return (last_allocated_time_);
- }
+ ///
+ /// @param lease_type Lease type for which last allocation timestamp should
+ /// be returned.
+ ///
+ /// @return Time when a lease of a specified type has been allocated from
+ /// this subnet. The negative infinity time is returned if a lease type is
+ /// not recognized (which is unlikely).
+ boost::posix_time::ptime getLastAllocatedTime(const Lease::Type& lease_type) const;
/// @brief sets the last address that was tried from this pool
///
/// See @ref last_allocated_ia_ for details.
isc::asiolink::IOAddress last_allocated_pd_;
- /// @brief Timestamp indicating when an address has been last allocated
- /// from this subnet.
- boost::posix_time::ptime last_allocated_time_;
+ /// @brief Timestamp indicating when a lease of a specified type has been
+ /// last allocated from this subnet.
+ std::map<Lease::Type, boost::posix_time::ptime> last_allocated_time_;
/// @brief Name of the network interface (if connected directly)
std::string iface_;
-// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2018 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
// offer an address from the pool1_.
ctx4.query_->addClass(ClientClass("cable-modem"));
+ // Restrict access to pool2 for this client, to make sure that the
+ // server doesn't accidentally get a lease from this pool.
+ pool2_->allowClientClass("telephone");
+
AllocEngine::findReservation(ctx4);
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx4)));
ASSERT_TRUE(lease);
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2018 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
}
}
+// This test verifies that preferred subnet is returned based on the timestamp
+// when the subnet was last used and allowed client classes.
+TEST(SharedNetwork6Test, getPreferredSubnet) {
+ SharedNetwork6Ptr network(new SharedNetwork6("frog"));
+
+ // Create four subnets.
+ Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 64, 10, 20, 30,
+ 40, SubnetID(1)));
+ Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 16, 10, 20, 30, 40,
+ SubnetID(2)));
+ Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:2::"), 64, 10, 20, 30,
+ 40, SubnetID(3)));
+ Subnet6Ptr subnet4(new Subnet6(IOAddress("3000:1::"), 64, 10, 20, 30,
+ 40, SubnetID(4)));
+
+
+ // Associate first two subnets with classes.
+ subnet1->allowClientClass("class1");
+ subnet2->allowClientClass("class1");
+
+ std::vector<Subnet6Ptr> subnets;
+ subnets.push_back(subnet1);
+ subnets.push_back(subnet2);
+ subnets.push_back(subnet3);
+ subnets.push_back(subnet4);
+
+ // Subnets have unique IDs so they should successfully be added to the
+ // network.
+ for (auto i = 0; i < subnets.size(); ++i) {
+ ASSERT_NO_THROW(network->add(subnets[i]))
+ << "failed to add subnet with id " << subnets[i]->getID()
+ << " to shared network";
+ }
+
+ Subnet6Ptr preferred;
+
+ // Initially, for every subnet we sould get the same subnet as the preferred
+ // one, because none of them have been used.
+ for (auto i = 0; i < subnets.size(); ++i) {
+ preferred = network->getPreferredSubnet(subnets[i], Lease::TYPE_NA);
+ EXPECT_EQ(subnets[i]->getID(), preferred->getID());
+ preferred = network->getPreferredSubnet(subnets[i], Lease::TYPE_TA);
+ EXPECT_EQ(subnets[i]->getID(), preferred->getID());
+ preferred = network->getPreferredSubnet(subnets[i], Lease::TYPE_PD);
+ EXPECT_EQ(subnets[i]->getID(), preferred->getID());
+ }
+
+ // Allocating an address from subnet2 updates the last allocated timestamp
+ // for this subnet, which makes this subnet preferred over subnet1.
+ subnet2->setLastAllocated(Lease::TYPE_NA, IOAddress("2001:db8:1:2::"));
+ preferred = network->getPreferredSubnet(subnet1, Lease::TYPE_NA);
+ EXPECT_EQ(subnet2->getID(), preferred->getID());
+
+ // If selected is subnet2, the same is returned.
+ preferred = network->getPreferredSubnet(subnet2, Lease::TYPE_NA);
+ EXPECT_EQ(subnet2->getID(), preferred->getID());
+
+ // The preferred subnet is dependent on the lease type. For the PD
+ // we should get the same subnet as selected.
+ preferred = network->getPreferredSubnet(subnet1, Lease::TYPE_PD);
+ EXPECT_EQ(subnet1->getID(), preferred->getID());
+
+ // Although, if we pick a prefix from the subnet2, we should get the
+ // subnet2 as preferred instead.
+ subnet2->setLastAllocated(Lease::TYPE_PD, IOAddress("3000:1234::"));
+ preferred = network->getPreferredSubnet(subnet1, Lease::TYPE_PD);
+ EXPECT_EQ(subnet2->getID(), preferred->getID());
+
+ // Even though the subnet1 has been most recently used, the preferred
+ // subnet is subnet3 in this case, because of the client class
+ // mismatch.
+ preferred = network->getPreferredSubnet(subnet3, Lease::TYPE_NA);
+ EXPECT_EQ(subnet3->getID(), preferred->getID());
+
+ // Same for subnet4.
+ preferred = network->getPreferredSubnet(subnet4, Lease::TYPE_NA);
+ EXPECT_EQ(subnet4->getID(), preferred->getID());
+
+ // Allocate an address from the subnet3. This makes it preferred to
+ // subnet4.
+ subnet3->setLastAllocated(Lease::TYPE_NA, IOAddress("2001:db8:2:1234::"));
+
+ // If the selected is subnet1, the preferred subnet is subnet2, because
+ // it has the same set of classes as subnet1. The subnet3 can't be
+ // preferred here because of the client class mismatch.
+ preferred = network->getPreferredSubnet(subnet1, Lease::TYPE_NA);
+ EXPECT_EQ(subnet2->getID(), preferred->getID());
+
+ // If we select subnet4, the preferred subnet is subnet3 because
+ // it was used more recently.
+ preferred = network->getPreferredSubnet(subnet4, Lease::TYPE_NA);
+ EXPECT_EQ(subnet3->getID(), preferred->getID());
+
+ // Repeat the test for subnet3 being a selected subnet.
+ preferred = network->getPreferredSubnet(subnet3, Lease::TYPE_NA);
+ EXPECT_EQ(subnet3->getID(), preferred->getID());
+}
+
// This test verifies that unparsing shared network returns valid structure.
TEST(SharedNetwork6Test, unparse) {
SharedNetwork6Ptr network(new SharedNetwork6("frog"));