]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#226] Added getOccupancyRate
authorFrancis Dupont <fdupont@isc.org>
Fri, 8 Aug 2025 10:33:16 +0000 (12:33 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 20 Aug 2025 15:39:15 +0000 (17:39 +0200)
src/lib/dhcpsrv/allocator.h
src/lib/dhcpsrv/flq_allocator.cc
src/lib/dhcpsrv/flq_allocator.h
src/lib/dhcpsrv/tests/flq_allocator_unittest.cc

index ac94ab992a95f76583c81657f6f5b24d06038454..94e89a4fe323f6862bcb263715070747ef8fbde9 100644 (file)
@@ -140,6 +140,44 @@ public:
                                    hint_prefix_length));
     }
 
+    /// @brief Returns the occupancy rate (v4 addresses).
+    ///
+    /// The method counts the total number and the number of not free
+    /// addresses in the suitable pools of the subnet, and returns the
+    /// occupancy rate. If the total number of addresses is over UMAX64
+    /// or the address is not from one of these pools, or by default
+    /// the 0. rate is returned.
+    ///
+    /// @param addr the address.
+    /// @param client_classes list of classes client belongs to.
+    /// @param count_me the address is still marked as free.
+    virtual double
+    getOccupancyRate(const asiolink::IOAddress& addr,
+                     const ClientClasses& client_classes,
+                     const bool count_me) const {
+        return (0.);
+    }
+
+    /// @brief Returns the occupancy rate (v6 prefixes).
+    ///
+    /// The method counts the total number and the number of not free
+    /// prefixes in the suitable pools of the subnet, and returns the
+    /// occupancy rate. If the total number of prefixes is over UMAX64
+    /// or the prefix is not from one of these pools, or by default
+    /// the 0. rate is returned.
+    ///
+    /// @param pref the prefix.
+    /// @param plen the prefix length.
+    /// @param client_classes list of classes client belongs to.
+    /// @param count_me the prefix is still marked as free.
+    virtual double
+    getOccupancyRate(const asiolink::IOAddress& pref,
+                     const uint8_t plen,
+                     const ClientClasses& client_classes,
+                     const bool count_me) const {
+        return (0.);
+    }
+
     /// @brief Check if the pool matches the selection criteria relative to the
     /// provided hint prefix length.
     ///
index 8acf832dc24ca62abc527ed1d17f7d772b2676f6..d6c0ba4f56bc9f468f2105ebff37d6adb813d820 100644 (file)
@@ -13,6 +13,7 @@
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/subnet.h>
 #include <util/stopwatch.h>
+#include <limits>
 #include <unordered_set>
 
 using namespace isc::asiolink;
@@ -134,6 +135,107 @@ FreeLeaseQueueAllocator::pickPrefixInternal(const ClientClasses& client_classes,
     return (IOAddress::IPV6_ZERO_ADDRESS());
 }
 
+double
+FreeLeaseQueueAllocator::getOccupancyRate(const IOAddress& addr,
+                                          const ClientClasses& client_classes,
+                                          const bool count_me) const {
+    // Sanity.
+    if (!addr.isV4()) {
+        return (0.);
+    }
+    auto subnet = subnet_.lock();
+    uint128_t total(0);
+    uint128_t busy(0);
+    bool found(false);
+
+    for (auto const& pool : subnet->getPools(Lease::TYPE_V4)) {
+        if (!pool->clientSupported(client_classes)) {
+            continue;
+        }
+        uint128_t capacity = pool->getCapacity();
+        total += capacity;
+        if (total >= std::numeric_limits<uint64_t>::max()) {
+            return (0.);
+        }
+        auto pool_state = boost::dynamic_pointer_cast<PoolFreeLeaseQueueAllocationState>(pool->getAllocationState());
+        if (!pool_state) {
+            continue;
+        }
+        uint128_t free_cnt = pool_state->getFreeLeaseCount();
+        if (!found && pool->inRange(addr)) {
+            found = true;
+            if (count_me && (free_cnt > 0)) {
+                --free_cnt;
+            }
+        }
+        if (free_cnt > capacity) {
+            free_cnt = capacity;
+        }
+        busy += capacity - free_cnt;
+    }
+    if (!found) {
+        return (0.);
+    }
+    // Should not happen...
+    if (total == 0) {
+        return (0.);
+    }
+    return (static_cast<double>(busy) / static_cast<double>(total));
+}
+
+double
+FreeLeaseQueueAllocator::getOccupancyRate(const IOAddress& pref,
+                                          const uint8_t plen,
+                                          const ClientClasses& client_classes,
+                                          const bool count_me) const {
+    // Sanity.
+    if (!pref.isV6()) {
+        return (0.);
+    }
+    auto subnet = subnet_.lock();
+    uint128_t total(0);
+    uint128_t busy(0);
+    bool found(false);
+
+    for (auto const& pool : subnet->getPools(Lease::TYPE_PD)) {
+        if (!pool->clientSupported(client_classes)) {
+            continue;
+        }
+        auto const& pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
+        if (!pool6 || (pool6->getLength() > plen)) {
+            continue;
+        }
+        uint128_t capacity = pool->getCapacity();
+        total += capacity;
+        if (total >= std::numeric_limits<uint64_t>::max()) {
+            return (0.);
+        }
+        auto pool_state = boost::dynamic_pointer_cast<PoolFreeLeaseQueueAllocationState>(pool->getAllocationState());
+        if (!pool_state) {
+            continue;
+        }
+        uint128_t free_cnt = pool_state->getFreeLeaseCount();
+        if (!found && pool->inRange(pref)) {
+            found = true;
+            if (count_me && (free_cnt > 0)) {
+                --free_cnt;
+            }
+        }
+        if (free_cnt > capacity) {
+            free_cnt = capacity;
+        }
+        busy += capacity - free_cnt;
+    }
+    if (!found) {
+        return (0.);
+    }
+    // Should not happen...
+    if (total == 0) {
+        return (0.);
+    }
+    return (static_cast<double>(busy) / static_cast<double>(total));
+}
+
 void
 FreeLeaseQueueAllocator::initAfterConfigureInternal() {
     auto subnet = subnet_.lock();
index 026ccc3fa918b4e3cdd9add53f607ddc4350e9cd..dbe8d6d48c34b9454401f8a7b0efd2477c19943d 100644 (file)
@@ -51,6 +51,40 @@ public:
         return ("flq");
     }
 
+    /// @brief Returns the occupancy rate (v4 addresses).
+    ///
+    /// The method counts the total number and the number of not free
+    /// addresses in the suitable pools of the subnet, and returns the
+    /// occupancy rate. If the total number of addresses is over UMAX64
+    /// or the address is not from one of these pools, or by default
+    /// the 0. rate is returned.
+    ///
+    /// @param addr the address.
+    /// @param client_classes list of classes client belongs to.
+    /// @param count_me the address is still marked as free.
+    virtual double
+    getOccupancyRate(const asiolink::IOAddress& addr,
+                     const ClientClasses& client_classes,
+                     const bool count_me) const;
+
+    /// @brief Returns the occupancy rate (v6 prefixes).
+    ///
+    /// The method counts the total number and the number of not free
+    /// prefixes in the suitable pools of the subnet, and returns the
+    /// occupancy rate. If the total number of prefixes is over UMAX64
+    /// or the prefix is not from one of these pools, or by default
+    /// the 0. rate is returned.
+    ///
+    /// @param pref the prefix.
+    /// @param plen the prefix length.
+    /// @param client_classes list of classes client belongs to.
+    /// @param count_me the prefix is still marked as free.
+    virtual double
+    getOccupancyRate(const asiolink::IOAddress& pref,
+                     const uint8_t plen,
+                     const ClientClasses& client_classes,
+                     const bool count_me) const;
+
 private:
 
     /// @brief Performs allocator initialization after server's reconfiguration.
index c7b52333a10d4b7964f2087936c17103ac6a256e..36f5f68564314a474e4509ab69529b2708d8a113 100644 (file)
@@ -65,6 +65,13 @@ TEST_F(FreeLeaseQueueAllocatorTest4, populateFreeAddressLeases) {
     ASSERT_TRUE(pool_state);
     EXPECT_FALSE(pool_state->exhausted());
 
+    double r = alloc.getOccupancyRate(IOAddress("192.0.2.101"), cc_, false);
+    EXPECT_EQ(.5, r);
+    r = alloc.getOccupancyRate(IOAddress("192.0.2.101"), cc_, true);
+    EXPECT_EQ(.6, r);
+    r = alloc.getOccupancyRate(IOAddress("192.0.2.1"), cc_, false);
+    EXPECT_EQ(0., r);
+
     std::set<IOAddress> addresses;
     for (auto i = 0; i < 5; ++i) {
         auto lease = pool_state->offerFreeLease();
@@ -124,6 +131,9 @@ TEST_F(FreeLeaseQueueAllocatorTest4, singlePoolWithAllocations) {
     IOAddress candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
     EXPECT_TRUE(candidate.isV4Zero());
 
+    double r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(1., r);
+
     auto i = 0;
     for (auto const& address_lease : leases) {
         if (i % 2) {
@@ -132,6 +142,9 @@ TEST_F(FreeLeaseQueueAllocatorTest4, singlePoolWithAllocations) {
         ++i;
     }
 
+    r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(.5, r);
+
     for (auto j = 0; j < 5; ++j) {
         candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
@@ -142,6 +155,9 @@ TEST_F(FreeLeaseQueueAllocatorTest4, singlePoolWithAllocations) {
 
     candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
     EXPECT_TRUE(candidate.isV4Zero());
+
+    r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(1., r);
 }
 
 // Test allocating IPv4 addresses and re-allocating these that are
@@ -170,6 +186,9 @@ TEST_F(FreeLeaseQueueAllocatorTest4, singlePoolWithReclamations) {
     IOAddress candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
     EXPECT_TRUE(candidate.isV4Zero());
 
+    double r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(1., r);
+
     auto i = 0;
     for (auto const& address_lease : leases) {
         if (i % 2) {
@@ -179,6 +198,9 @@ TEST_F(FreeLeaseQueueAllocatorTest4, singlePoolWithReclamations) {
         }
         ++i;
     }
+    r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(.5, r);
+
     for (auto j = 0; j < 5; ++j) {
         candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, candidate));
@@ -187,9 +209,11 @@ TEST_F(FreeLeaseQueueAllocatorTest4, singlePoolWithReclamations) {
         lease->state_ = Lease::STATE_DEFAULT;
         EXPECT_NO_THROW(lease_mgr.updateLease4(lease));
     }
-
     candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
     EXPECT_TRUE(candidate.isV4Zero());
+
+    r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(1., r);
 }
 
 // Test allocating DHCPv4 leases for many pools in a subnet.
@@ -213,6 +237,9 @@ TEST_F(FreeLeaseQueueAllocatorTest4, manyPools) {
 
     auto& lease_mgr = LeaseMgrFactory::instance();
 
+    double r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(0., r);
+
     std::set<IOAddress> addresses_set;
     std::vector<IOAddress> addresses_vector;
     std::vector<PoolPtr> pools_vector;
@@ -232,6 +259,9 @@ TEST_F(FreeLeaseQueueAllocatorTest4, manyPools) {
     // Make sure that unique addresses have been returned.
     EXPECT_EQ(total, addresses_set.size());
 
+    r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(1., r);
+
     // Verify that the addresses are returned in the random order.
     // Count how many times we found consecutive addresses. It should
     // be 0 or close to 0.
@@ -263,12 +293,16 @@ TEST_F(FreeLeaseQueueAllocatorTest4, manyPools) {
 // Test that the allocator returns a zero address when there are no pools
 // in a subnet.
 TEST_F(FreeLeaseQueueAllocatorTest4, noPools) {
-   FreeLeaseQueueAllocator alloc(Lease::TYPE_V4, subnet_);
+    FreeLeaseQueueAllocator alloc(Lease::TYPE_V4, subnet_);
 
-   subnet_->delPools(Lease::TYPE_V4);
+    subnet_->delPools(Lease::TYPE_V4);
 
-   IOAddress candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
-   EXPECT_TRUE(candidate.isV4Zero());
+    IOAddress candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
+    EXPECT_TRUE(candidate.isV4Zero());
+
+    // rate is 0. because of the address can't be found, not from 0./0....
+    double r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+    EXPECT_EQ(0., r);
 }
 
 // Test that the allocator respects client class guards.
@@ -304,6 +338,8 @@ TEST_F(FreeLeaseQueueAllocatorTest4, clientClasses) {
 
    // Simulate client's request belonging to the class bar.
    cc_.insert("bar");
+   double r = alloc.getOccupancyRate(IOAddress("192.0.2.120"), cc_, false);
+   EXPECT_EQ(0., r);
    for (auto i = 0; i < 20; ++i) {
        // Allocate random addresses and make sure they belong to the
        // pools associated with the class bar.
@@ -315,11 +351,16 @@ TEST_F(FreeLeaseQueueAllocatorTest4, clientClasses) {
    }
    EXPECT_EQ(20, addresses_set.size());
 
+   r = alloc.getOccupancyRate(IOAddress("192.0.2.120"), cc_, false);
+   EXPECT_EQ(1., r);
+
    addresses_set.clear();
 
    // Simulate the case that the client also belongs to the class foo.
    // All pools should now be available.
    cc_.insert("foo");
+   r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+   EXPECT_EQ(.5, r);
    for (auto i = 0; i < 20; ++i) {
        IOAddress candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
        addresses_set.insert(candidate);
@@ -327,12 +368,16 @@ TEST_F(FreeLeaseQueueAllocatorTest4, clientClasses) {
        EXPECT_TRUE(subnet_->inRange(candidate));
    }
    EXPECT_EQ(20, addresses_set.size());
+   r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+   EXPECT_EQ(1., r);
 
    // When the client does not belong to any client class the allocator
    // can't offer any address to the client.
    cc_.clear();
    IOAddress candidate = alloc.pickAddress(cc_, clientid_, IOAddress("0.0.0.0"));
    EXPECT_TRUE(candidate.isV4Zero());
+   r = alloc.getOccupancyRate(IOAddress("192.0.2.100"), cc_, false);
+   EXPECT_EQ(0., r);
 }
 
 /// @brief Test fixture class for the DHCPv6 Free Lease Queue allocator.
@@ -382,6 +427,10 @@ TEST_F(FreeLeaseQueueAllocatorTest6, populateFreeAddressLeases) {
 
     EXPECT_NO_THROW(alloc.initAfterConfigure());
 
+    // Address getOccupancyRate is for IPv4 only.
+    double r = alloc.getOccupancyRate(IOAddress("2001:db8:1::10"), cc_, false);
+    EXPECT_EQ(0., r);
+
     auto pool_state = boost::dynamic_pointer_cast<PoolFreeLeaseQueueAllocationState>(pool_->getAllocationState());
     ASSERT_TRUE(pool_state);
     EXPECT_FALSE(pool_state->exhausted());
@@ -461,7 +510,7 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePoolWithAllocations) {
     }
 
     for (auto j = 0; j < 8; ++j) {
-       candidate = alloc.pickAddress(cc_, duid_, IOAddress("::"));
+        candidate = alloc.pickAddress(cc_, duid_, IOAddress("::"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate, cc_));
         auto lease = createLease6(Lease::TYPE_NA, candidate, i);
@@ -509,7 +558,7 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePoolWithReclamations) {
     }
 
     for (auto j = 0; j < 8; ++j) {
-       candidate = alloc.pickAddress(cc_, duid_, IOAddress("::"));
+        candidate = alloc.pickAddress(cc_, duid_, IOAddress("::"));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate));
         EXPECT_TRUE(subnet_->inPool(Lease::TYPE_NA, candidate, cc_));
         auto lease = lease_mgr.getLease6(Lease::TYPE_NA, candidate);
@@ -686,6 +735,10 @@ TEST_F(FreeLeaseQueueAllocatorTest6, populateFreePrefixDelegationLeases) {
     ASSERT_TRUE(pool_state);
     EXPECT_FALSE(pool_state->exhausted());
 
+    double r = alloc.getOccupancyRate(IOAddress("2001:db8:2::"),
+                                      128, cc_, false);
+    EXPECT_EQ(5. / 256., r);
+
     std::set<IOAddress> addresses;
     for (auto i = 0; i < 256; ++i) {
         auto lease = pool_state->offerFreeLease();
@@ -721,9 +774,13 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPool) {
     }
     // The pool comprises 65536 prefixes. All should be returned.
     EXPECT_EQ(65536, prefixes.size());
+
+    double r = alloc.getOccupancyRate(IOAddress("2001:db8:1:2::"),
+                                      128, cc_, false);
+    EXPECT_EQ(1., r);
 }
 
-// Test allocating IPv6 addresses and re-allocating these that are
+// Test allocating delegated prefixes and re-allocating these that are
 // deleted (released).
 TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithAllocations) {
     // Remove the default pool because it is too large for this test case.
@@ -757,6 +814,8 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithAllocations) {
 
     IOAddress candidate = alloc.pickPrefix(cc_, pool, duid_, Allocator::PREFIX_LEN_HIGHER, IOAddress("::"), 0);
     EXPECT_TRUE(candidate.isV6Zero());
+    double r = alloc.getOccupancyRate(IOAddress("3000::"), 128, cc_, false);
+    EXPECT_EQ(1., r);
 
     auto i = 0;
     for (auto const& address_lease : leases) {
@@ -765,6 +824,10 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithAllocations) {
         }
         ++i;
     }
+    r = alloc.getOccupancyRate(IOAddress("3000::"), 128, cc_, false);
+    EXPECT_EQ(.5, r);
+    r = alloc.getOccupancyRate(IOAddress("3000::"), 128, cc_, true);
+    EXPECT_EQ(129. / 256., r);
 
     for (auto j = 0; j < 128; ++j) {
         candidate = alloc.pickPrefix(cc_, pool, duid_, Allocator::PREFIX_LEN_HIGHER, IOAddress("::"), 0);
@@ -776,9 +839,12 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithAllocations) {
 
     candidate = alloc.pickPrefix(cc_, pool, duid_, Allocator::PREFIX_LEN_HIGHER, IOAddress("::"), 0);
     EXPECT_TRUE(candidate.isV6Zero());
+
+    r = alloc.getOccupancyRate(IOAddress("3000::"), 128, cc_, false);
+    EXPECT_EQ(1., r);
 }
 
-// Test allocating IPv6 addresses and re-allocating these that are
+// Test allocating delegated prefixes and re-allocating these that are
 // reclaimed.
 TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithReclamations) {
     // Remove the default pool because it is too large for this test case.
@@ -812,6 +878,8 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithReclamations) {
 
     IOAddress candidate = alloc.pickPrefix(cc_, pool, duid_, Allocator::PREFIX_LEN_HIGHER, IOAddress("::"), 0);
     EXPECT_TRUE(candidate.isV6Zero());
+    double r = alloc.getOccupancyRate(IOAddress("3000::"), 128, cc_, false);
+    EXPECT_EQ(1., r);
 
     auto i = 0;
     for (auto const& address_lease : leases) {
@@ -822,6 +890,8 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithReclamations) {
         }
         ++i;
     }
+    r = alloc.getOccupancyRate(IOAddress("3000::"), 128, cc_, false);
+    EXPECT_EQ(.5, r);
 
     for (auto j = 0; j < 128; ++j) {
         candidate = alloc.pickPrefix(cc_, pool, duid_, Allocator::PREFIX_LEN_HIGHER, IOAddress("::"), 0);
@@ -834,8 +904,10 @@ TEST_F(FreeLeaseQueueAllocatorTest6, singlePdPoolWithReclamations) {
 
     candidate = alloc.pickPrefix(cc_, pool, duid_, Allocator::PREFIX_LEN_HIGHER, IOAddress("::"), 0);
     EXPECT_TRUE(candidate.isV6Zero());
-}
 
+        r = alloc.getOccupancyRate(IOAddress("3000::"), 128, cc_, false);
+    EXPECT_EQ(1., r);
+}
 
 // Test allocating delegated prefixes from multiple pools.
 TEST_F(FreeLeaseQueueAllocatorTest6, manyPdPools) {
@@ -869,6 +941,9 @@ TEST_F(FreeLeaseQueueAllocatorTest6, manyPdPools) {
     }
     // Make sure that unique prefixes have been returned.
     EXPECT_EQ(total, prefixes.size());
+
+    double r = alloc.getOccupancyRate(IOAddress("3001::"), 128, cc_, false);
+    EXPECT_EQ(1., r);
 }
 
 // Test allocating delegated prefixes from multiple pools.
@@ -890,7 +965,6 @@ TEST_F(FreeLeaseQueueAllocatorTest6, manyPdPoolsPreferLower) {
     ASSERT_NO_THROW(alloc.initAfterConfigure());
     auto& lease_mgr = LeaseMgrFactory::instance();
 
-
     Pool6Ptr pool;
 
     std::set<IOAddress> prefixes;
@@ -904,6 +978,14 @@ TEST_F(FreeLeaseQueueAllocatorTest6, manyPdPoolsPreferLower) {
     }
     // Make sure that unique prefixes have been returned.
     EXPECT_EQ(total, prefixes.size());
+
+    double r = alloc.getOccupancyRate(IOAddress("2001:db8:1:2::"),
+                                      120, cc_, false);
+    EXPECT_EQ(1., r);
+    r = alloc.getOccupancyRate(IOAddress("2001:db8:1:2::"), 128, cc_, false);
+    EXPECT_EQ(65536. / 68096., r);
+    r = alloc.getOccupancyRate(IOAddress("2001:db8:1:2::"), 64, cc_, false);
+    EXPECT_EQ(0., r);
 }
 
 // Test allocating delegated prefixes from multiple pools.
@@ -938,6 +1020,8 @@ TEST_F(FreeLeaseQueueAllocatorTest6, manyPdPoolsPreferEqual) {
     }
     // Make sure that unique prefixes have been returned.
     EXPECT_EQ(total, prefixes.size());
+    double r = alloc.getOccupancyRate(IOAddress("3001::"), 128, cc_, false);
+    EXPECT_EQ(2560. / 68096., r);
 }
 
 // Test allocating delegated prefixes from multiple pools.
@@ -972,6 +1056,8 @@ TEST_F(FreeLeaseQueueAllocatorTest6, manyPdPoolsPreferHigher) {
     }
     // Make sure that unique prefixes have been returned.
     EXPECT_EQ(total, prefixes.size());
+    double r = alloc.getOccupancyRate(IOAddress("3001::"), 128, cc_, false);
+    EXPECT_EQ(2560. / 68096., r);
 }
 
 // Test that the allocator respects client class guards.
@@ -1008,8 +1094,16 @@ TEST_F(FreeLeaseQueueAllocatorTest6, pdPoolsClientClasses) {
 
     candidate = alloc.pickPrefix(cc_, pool, duid_, Allocator::PREFIX_LEN_HIGHER, IOAddress("::"), 64);
     EXPECT_TRUE(candidate.isV6Zero());
-}
 
+    double r = alloc.getOccupancyRate(IOAddress("3000:1::"), 128, cc_, false);
+    EXPECT_EQ(1., r);
+    cc_.insert("foo");
+    r = alloc.getOccupancyRate(IOAddress("3000:1::"), 128, cc_, false);
+    EXPECT_EQ(256. / 65792., r);
+    cc_.clear();
+    r = alloc.getOccupancyRate(IOAddress("3000:1::"), 128, cc_, false);
+    EXPECT_EQ(0., r);
+}
 
 } // end of isc::dhcp::test namespace
 } // end of isc::dhcp namespace