]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5437] Optimized preferred subnet selection within shared network.
authorMarcin Siodelski <marcin@isc.org>
Mon, 26 Feb 2018 12:30:52 +0000 (13:30 +0100)
committerMarcin Siodelski <marcin@isc.org>
Mon, 26 Feb 2018 12:30:52 +0000 (13:30 +0100)
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/shared_network.cc
src/lib/dhcpsrv/shared_network.h
src/lib/dhcpsrv/subnet.cc
src/lib/dhcpsrv/subnet.h
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc

index 8c9c2114fdc4545961d46200672b4bca3fee3e92..feca9f4850247b39fc8705224a0a0c8de93933d4 100644 (file)
@@ -3446,9 +3446,29 @@ AllocEngine::allocateUnreservedLease4(ClientContext4& ctx) {
     Lease4Ptr new_lease;
     AllocatorPtr allocator = getAllocator(Lease::TYPE_V4);
     Subnet4Ptr subnet = ctx.subnet_;
-    Subnet4Ptr original_subnet = 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 address
+    // pools have been exhausted. Using a subnet from which an address
+    // was assigned most recently is an optimization which increases
+    // the likelyhood of starting from the subnet which address pools
+    // are not exhausted.
     SharedNetwork4Ptr network;
-    subnet->getSharedNetwork(network);
+    ctx.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.
+        ctx.subnet_ = subnet = network->getPreferredSubnet(ctx.subnet_);
+    }
+
+    Subnet4Ptr original_subnet = subnet;
+
     uint64_t total_attempts = 0;
     while (subnet) {
 
index 54e4e55acd1ecb0794d83727c6ded0df0dbaee7d..57123b9e3648225f7a7a040ab0234405e92298e3 100644 (file)
@@ -194,6 +194,43 @@ public:
         // Got the next subnet, so return it.
         return (*subnet_it);
     }
+
+    /// @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 subnets Container holding subnets belonging to this shared
+    /// network.
+    /// @param selected_subnet Pointer to a currently selected subnet.
+    ///
+    /// @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) {
+
+        Subnet4Ptr preferred_subnet = selected_subnet;
+        for (auto s = subnets.begin(); s != subnets.end(); ++s) {
+            if (((*s)->getClientClasses() == selected_subnet->getClientClasses()) &&
+                ((*s)->getLastAllocatedTime() > selected_subnet->getLastAllocatedTime())) {
+                preferred_subnet = (*s);
+            }
+        }
+
+        return (preferred_subnet);
+    }
 };
 
 } // end of anonymous namespace
@@ -238,6 +275,11 @@ SharedNetwork4::getNextSubnet(const Subnet4Ptr& first_subnet,
     return (Impl::getNextSubnet(subnets_, first_subnet, current_subnet));
 }
 
+Subnet4Ptr
+SharedNetwork4::getPreferredSubnet(const Subnet4Ptr& selected_subnet) const {
+    return (Impl::getPreferredSubnet<Subnet4Ptr>(subnets_, selected_subnet));
+}
+
 ElementPtr
 SharedNetwork4::toElement() const {
     ElementPtr map = Network4::toElement();
@@ -290,8 +332,7 @@ SharedNetwork6::getSubnet(const SubnetID& subnet_id) const {
 Subnet6Ptr
 SharedNetwork6::getNextSubnet(const Subnet6Ptr& first_subnet,
                               const SubnetID& current_subnet) const {
-    return (Impl::getNextSubnet(subnets_, first_subnet,
-                                         current_subnet));
+    return (Impl::getNextSubnet(subnets_, first_subnet, current_subnet));
 }
 
 ElementPtr
index 31d0d43d97f4d6181c0982256b4c5d4c9060704c..b2fbd8b22312671881d8c253cd4c4edbc53fe862 100644 (file)
@@ -120,6 +120,28 @@ public:
     Subnet4Ptr getNextSubnet(const Subnet4Ptr& 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.
+    ///
+    /// @return Pointer to a preferred subnet. It may be the same as @c selected_subnet
+    /// if no better subnet was found.
+    Subnet4Ptr getPreferredSubnet(const Subnet4Ptr& selected_subnet) const;
+
     /// @brief Unparses shared network object.
     ///
     /// @return A pointer to unparsed shared network configuration.
index 86b05f16099da2ed820340a89ba8f75500088660..82365240d6c39ae25b9960e4cc10c4ca3341228d 100644 (file)
@@ -56,7 +56,8 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
       prefix_len_(len),
       last_allocated_ia_(lastAddrInPrefix(prefix, len)),
       last_allocated_ta_(lastAddrInPrefix(prefix, len)),
-      last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
+      last_allocated_pd_(lastAddrInPrefix(prefix, len)),
+      last_allocated_time_(boost::posix_time::neg_infin) {
     if ((prefix.isV6() && len > 128) ||
         (prefix.isV4() && len > 32)) {
         isc_throw(BadValue,
@@ -99,16 +100,19 @@ void Subnet::setLastAllocated(Lease::Type type,
     case Lease::TYPE_V4:
     case Lease::TYPE_NA:
         last_allocated_ia_ = addr;
-        return;
+        break;
     case Lease::TYPE_TA:
         last_allocated_ta_ = addr;
-        return;
+        break;
     case Lease::TYPE_PD:
         last_allocated_pd_ = addr;
-        return;
+        break;
     default:
         isc_throw(BadValue, "Pool type " << type << " not supported");
     }
+
+    // Update the timestamp of last allocation.
+    last_allocated_time_ = boost::posix_time::microsec_clock::universal_time();
 }
 
 std::string
index 52b059a8e1c5b7c82bd96ed89339b3097fbda930..e2133cd97609a9cc5d9711c98876ebeaec87bc08 100644 (file)
@@ -22,6 +22,7 @@
 #include <boost/multi_index/ordered_index.hpp>
 #include <boost/multi_index/random_access_index.hpp>
 #include <boost/multi_index_container.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/pointer_cast.hpp>
 #include <boost/shared_ptr.hpp>
 
@@ -81,7 +82,13 @@ public:
     /// @return address/prefix that was last tried from this subnet
     isc::asiolink::IOAddress getLastAllocated(Lease::Type type) const;
 
-    /// @brief sets the last address that was tried from this subnet
+    /// @brief Returns the timestamp when the @c setLastAllocated function
+    /// was called.
+    boost::posix_time::ptime getLastAllocatedTime() const {
+        return (last_allocated_time_);
+    }
+
+    /// @brief sets the last address that was tried from this pool
     ///
     /// This method sets the last address that was attempted to be allocated
     /// from this subnet. This is used as helper information for the next
@@ -384,6 +391,10 @@ protected:
     /// 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 Name of the network interface (if connected directly)
     std::string iface_;
 
index 3e1a37058056cfd6407e393ee4c7d9fe56c1f9c5..104f3ac8cfc3eab2d8ba7a3503e1293446a3ca65 100644 (file)
@@ -2276,14 +2276,7 @@ TEST_F(AllocEngine4Test, findReservation) {
     EXPECT_TRUE(ctx.currentHost());
     EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
 
-    // Regardless of the host reservation mode, the host should be
-    // always returned when findReservation() is called.
-    subnet_->setHostReservationMode(Network::HR_DISABLED);
-    ASSERT_NO_THROW(engine.findReservation(ctx));
-    EXPECT_TRUE(ctx.currentHost());
-    EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
-
-    // Check the third possible reservation mode.
+    // Check the out of the pool reservation mode.
     subnet_->setHostReservationMode(Network::HR_OUT_OF_POOL);
     ASSERT_NO_THROW(engine.findReservation(ctx));
     EXPECT_TRUE(ctx.currentHost());