Parcourir la source

Merge branch 'trac3799' Add support for per subnet statistics in v6

Conflicts:
	ChangeLog
	doc/guide/dhcp6-srv.xml
	src/bin/dhcp6/tests/dhcp6_test_utils.cc
Shawn Routhier il y a 9 ans
Parent
commit
4aa4808268

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+965.	[func]		sar
+	Per IPv6 subnet statistics (subnet[id].assigned-nas, 
+	subnet[id].total-nas, subnet[id].assigned-pds, and subnet[id].total-pds)
+	has been implemented.
+	(Trac #3799, git TBD)
+
 964.	[doc]		tomek
 	User's Guide and Developer's Guide updated with statistics and
 	control channel description.

+ 63 - 3
doc/guide/dhcp6-srv.xml

@@ -2590,7 +2590,7 @@ should include options from the isc option space:
         The DHCPv6 server supports the following statistics:
       </para>
         <table frame="all" id="dhcp6-statistics">
-          <title>DHCPv4 Statistics</title>
+          <title>DHCPv6 Statistics</title>
           <tgroup cols='3'>
           <colspec colname='statistic' align='center'/>
           <colspec colname='type' align='center'/>
@@ -2774,8 +2774,68 @@ should include options from the isc option space:
               </entry>
             </row>
 
-          </tbody>
-          </tgroup>
+            <row>
+            <entry>subnet[id].total-nas</entry>
+            <entry>integer</entry>
+            <entry>
+            This statistic shows the total number of NA addresses available for
+            DHCPv6 management for a given subnet. In other words, this is the sum
+            of all addresses in all configured pools. This statistic changes only
+            during configuration changes. Note it does not take into account any
+            addresses that may be reserved due to host reservation. The
+            <emphasis>id</emphasis> is the subnet-id of a given subnet. This
+            statistic is exposed for each subnet separately. This statistic is
+            reset during a reconfiguration event.
+            </entry>
+            </row>
+
+            <row>
+            <entry>subnet[id].assigned-nas</entry>
+            <entry>integer</entry>
+            <entry>
+            This statistic shows the number of NA addresses in a given subnet that
+            are assigned. This statistic increases every time a new lease is allocated
+            (as a result of receiving a REQUEST message) and is decreased every time a
+            lease is released (a RELEASE message is received). When lease expiration
+            is implemented (planned for Kea 1.0), it will also decrease when a lease
+            is expired. The <emphasis>id</emphasis> is the subnet-id of a given
+            subnet. This statistic is exposed for each subnet separately. This
+            statistic is reset during a reconfiguration event.
+            </entry>
+            </row>
+
+            <row>
+            <entry>subnet[id].total-pds</entry>
+            <entry>integer</entry>
+            <entry>
+            This statistic shows the total number of PD prefixes available for
+            DHCPv6 management for a given subnet. In other words, this is the sum
+            of all prefixes in all configured pools. This statistic changes only
+            during configuration changes. Note it does not take into account any
+            prefixes that may be reserved due to host reservation. The
+            <emphasis>id</emphasis> is the subnet-id of a given subnet. This
+            statistic is exposed for each subnet separately. This statistic is
+            reset during a reconfiguration event.
+            </entry>
+            </row>
+
+            <row>
+            <entry>subnet[id].assigned-pds</entry>
+            <entry>integer</entry>
+            <entry>
+            This statistic shows the number of PD prefixes in a given subnet that
+            are assigned. This statistic increases every time a new lease is allocated
+            (as a result of receiving a REQUEST message) and is decreased every time a
+            lease is released (a RELEASE message is received). When lease expiration
+            is implemented (planned for Kea 1.0), it will also decrease when a lease
+            is expired. The <emphasis>id</emphasis> is the subnet-id of a given
+            subnet. This statistic is exposed for each subnet separately. This
+            statistic is reset during a reconfiguration event.
+            </entry>
+            </row>
+
+        </tbody>
+        </tgroup>
         </table>
     </section>
 

+ 10 - 0
src/bin/dhcp6/dhcp6_srv.cc

@@ -2244,6 +2244,11 @@ Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
         ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
                           "Lease released. Thank you, please come again."));
 
+        // Need to decrease statistic for assigned addresses.
+        StatsMgr::instance().addValue(
+            StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-nas"),
+            static_cast<int64_t>(-1));
+
         // Check if a lease has flags indicating that the FQDN update has
         // been performed. If so, create NameChangeRequest which removes
         // the entries.
@@ -2394,6 +2399,11 @@ Dhcpv6Srv::releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
 
         ia_rsp->addOption(createStatusCode(*query, *ia_rsp, STATUS_Success,
                           "Lease released. Thank you, please come again."));
+
+        // Need to decrease statistic for assigned prefixes.
+        StatsMgr::instance().addValue(
+            StatsMgr::generateName("subnet", lease->subnet_id_, "assigned-pds"),
+            static_cast<int64_t>(-1));
     }
 
     return (ia_rsp);

+ 4 - 0
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -965,6 +965,7 @@ TEST_F(Dhcpv6SrvTest, pdRenewReject) {
 // - returned REPLY message has server-id
 // - returned REPLY message has IA_NA that does not include an IAADDR
 // - lease is actually removed from LeaseMgr
+// - assigned-nas stats counter is properly decremented
 TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
     testReleaseBasic(Lease::TYPE_NA, IOAddress("2001:db8:1:1::cafe:babe"),
                      IOAddress("2001:db8:1:1::cafe:babe"));
@@ -979,6 +980,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
 // - returned REPLY message has server-id
 // - returned REPLY message has IA_PD that does not include an IAPREFIX
 // - lease is actually removed from LeaseMgr
+// - assigned-pds stats counter is properly decremented
 TEST_F(Dhcpv6SrvTest, pdReleaseBasic) {
     testReleaseBasic(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"),
                      IOAddress("2001:db8:1:2::"));
@@ -997,6 +999,7 @@ TEST_F(Dhcpv6SrvTest, pdReleaseBasic) {
 // - returned REPLY message has server-id
 // - returned REPLY message has IA_NA that includes STATUS-CODE
 // - No lease in LeaseMgr
+// - assigned-nas stats counter is properly not decremented
 TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     testReleaseReject(Lease::TYPE_NA, IOAddress("2001:db8:1:1::dead"));
 }
@@ -1014,6 +1017,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
 // - returned REPLY message has server-id
 // - returned REPLY message has IA_PD that includes STATUS-CODE
 // - No lease in LeaseMgr
+// - assigned-pds stats counter is properly not decremented
 TEST_F(Dhcpv6SrvTest, pdReleaseReject) {
     testReleaseReject(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"));
 }

+ 29 - 1
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -26,6 +26,7 @@
 using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
+using namespace isc::stats;
 
 namespace isc {
 namespace test {
@@ -594,6 +595,12 @@ Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
     Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, existing);
     ASSERT_TRUE(l);
 
+    // And prepopulate the stats counter
+    std::string name = StatsMgr::generateName("subnet", subnet_->getID(),
+                                              type == Lease::TYPE_NA ? "assigned-nas" :
+                                              "assigned-pds");
+    StatsMgr::instance().setValue(name, static_cast<int64_t>(1));
+
     // Let's create a RELEASE
     Pkt6Ptr rel = createMessage(DHCPV6_RELEASE, type, release_addr, prefix_len,
                                 iaid);
@@ -632,6 +639,11 @@ Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
     l = LeaseMgrFactory::instance().getLease6(type, *duid_, iaid,
                                               subnet_->getID());
     ASSERT_FALSE(l);
+
+    // We should have decremented the address counter
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(0, stat->getInteger().first);
 }
 
 void
@@ -660,6 +672,12 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
     // GenerateClientId() also sets duid_
     OptionPtr clientid = generateClientId();
 
+    // Pretend we have allocated 1 lease
+    std::string name = StatsMgr::generateName("subnet", subnet_->getID(),
+                                              type == Lease::TYPE_NA ? "assigned-nas" :
+                                              "assigned-pds");
+    StatsMgr::instance().setValue(name, static_cast<int64_t>(1));
+
     // Check that the lease is NOT in the database
     Lease6Ptr l = LeaseMgrFactory::instance().getLease6(type, addr);
     ASSERT_FALSE(l);
@@ -689,6 +707,11 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
     l = LeaseMgrFactory::instance().getLease6(type, addr);
     ASSERT_FALSE(l);
 
+    // Verify we didn't decrement the stats counter
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(1, stat->getInteger().first);
+
     // CASE 2: Lease is known and belongs to this client, but to a different IAID
     SCOPED_TRACE("CASE 2: Lease is known and belongs to this client, but to a different IAID");
 
@@ -717,6 +740,9 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
     l = LeaseMgrFactory::instance().getLease6(type, addr);
     ASSERT_TRUE(l);
 
+    // Verify we didn't decrement the stats counter
+    EXPECT_EQ(1, stat->getInteger().first);
+
     // CASE 3: Lease belongs to a client with different client-id
     SCOPED_TRACE("CASE 3: Lease belongs to a client with different client-id");
 
@@ -741,6 +767,9 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
     l = LeaseMgrFactory::instance().getLease6(type, addr);
     ASSERT_TRUE(l);
 
+    // Verify we didn't decrement the stats counter
+    EXPECT_EQ(1, stat->getInteger().first);
+
     // Finally, let's cleanup the database
     EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
 }
@@ -748,7 +777,6 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
 void
 Dhcpv6SrvTest::testReceiveStats(uint8_t pkt_type, const std::string& stat_name) {
 
-    using namespace isc::stats;
     StatsMgr& mgr = StatsMgr::instance();
     NakedDhcpv6Srv srv(0);
 

+ 29 - 1
src/lib/dhcpsrv/alloc_engine.cc

@@ -812,6 +812,13 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
         // Remove this lease from LeaseMgr
         LeaseMgrFactory::instance().deleteLease((*candidate)->addr_);
 
+        // Need to decrease statistic for assigned addresses.
+        StatsMgr::instance().addValue(
+            StatsMgr::generateName("subnet", ctx.subnet_->getID(),
+                                   ctx.type_ == Lease::TYPE_NA ? "assigned-nas" :
+                                                                 "assigned-pds"),
+            static_cast<int64_t>(-1));
+
         // In principle, we could trigger a hook here, but we will do this
         // only if we get serious complaints from actual users. We want the
         // conflict resolution procedure to really work and user libraries
@@ -868,6 +875,13 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
             // Remove this lease from LeaseMgr
             LeaseMgrFactory::instance().deleteLease((*lease)->addr_);
 
+            // Need to decrease statistic for assigned addresses.
+            StatsMgr::instance().addValue(
+                StatsMgr::generateName("subnet", ctx.subnet_->getID(),
+                                       ctx.type_ == Lease::TYPE_NA ? "assigned-nas" :
+                                                                     "assigned-pds"),
+                static_cast<int64_t>(-1));
+
             /// @todo: Probably trigger a hook here
 
             // Add this to the list of removed leases.
@@ -1024,6 +1038,15 @@ Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
         bool status = LeaseMgrFactory::instance().addLease(lease);
 
         if (status) {
+            // The lease insertion succeeded - if the lease is in the
+            // current subnet lets bump up the statistic.
+            if (ctx.subnet_->inPool(ctx.type_, addr)) {
+                StatsMgr::instance().addValue(
+                    StatsMgr::generateName("subnet", ctx.subnet_->getID(),
+                                           ctx.type_ == Lease::TYPE_NA ? "assigned-nas" :
+                                                                         "assigned-pds"),
+                    static_cast<int64_t>(1));
+            }
 
             return (lease);
         } else {
@@ -1140,6 +1163,11 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
         // Remove this lease from LeaseMgr
         LeaseMgrFactory::instance().deleteLease(lease->addr_);
 
+        // Need to decrease statistic for assigned addresses.
+        StatsMgr::instance().addValue(
+            StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-nas"),
+            static_cast<int64_t>(-1));
+
         // Add it to the removed leases list.
         ctx.old_leases_.push_back(lease);
 
@@ -1779,7 +1807,7 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr) {
         if (status) {
 
             // The lease insertion succeeded, let's bump up the statistic.
-            isc::stats::StatsMgr::instance().addValue(
+            StatsMgr::instance().addValue(
                 StatsMgr::generateName("subnet", ctx.subnet_->getID(), "assigned-addresses"),
                 static_cast<int64_t>(1));
 

+ 44 - 0
src/lib/dhcpsrv/cfg_subnets6.cc

@@ -16,6 +16,7 @@
 #include <dhcpsrv/cfg_subnets6.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/subnet_id.h>
+#include <stats/stats_mgr.h>
 
 using namespace isc::asiolink;
 
@@ -179,5 +180,48 @@ CfgSubnets6::isDuplicate(const Subnet6& subnet) const {
     return (false);
 }
 
+void
+CfgSubnets6::removeStatistics() {
+    using namespace isc::stats;
+
+    // For each v6 subnet currently configured, remove the statistics.
+    for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
+         subnet6 != subnets_.end(); ++subnet6) {
+
+        StatsMgr::instance().del(StatsMgr::generateName("subnet",
+                                                        (*subnet6)->getID(),
+                                                        "total-nas"));
+
+        StatsMgr::instance().del(StatsMgr::generateName("subnet",
+                                                        (*subnet6)->getID(),
+                                                        "assigned-nas"));
+
+        StatsMgr::instance().del(StatsMgr::generateName("subnet",
+                                                        (*subnet6)->getID(),
+                                                        "total-pds"));
+
+        StatsMgr::instance().del(StatsMgr::generateName("subnet",
+                                                        (*subnet6)->getID(),
+                                                        "assigned-pds"));
+    }
+}
+
+void
+CfgSubnets6::updateStatistics() {
+    using namespace isc::stats;
+
+    for (Subnet6Collection::const_iterator subnet = subnets_.begin();
+         subnet != subnets_.end(); ++subnet) {
+
+        StatsMgr::instance().setValue(
+            StatsMgr::generateName("subnet", (*subnet)->getID(), "total-nas"),
+            static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_NA)));
+
+        StatsMgr::instance().setValue(
+            StatsMgr::generateName("subnet", (*subnet)->getID(), "total-pds"),
+            static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_PD)));
+    }
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc

+ 18 - 1
src/lib/dhcpsrv/cfg_subnets6.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -132,6 +132,23 @@ public:
                  const ClientClasses& client_classes = ClientClasses(),
                  const bool is_relay_address = false) const;
 
+    /// @brief Updates statistics.
+    ///
+    /// This method updates statistics that are affected by the newly committed
+    /// configuration. In particular, it updates the number of available addresses
+    /// and prefixes in each subnet. Other statistics may be added in the future. In
+    /// general, these are statistics that are dependant only on configuration, so
+    /// they are not expected to change until the next reconfiguration event.
+    void updateStatistics();
+
+    /// @brief Removes statistics.
+    ///
+    /// During commitment of a new configuration, we need to get rid of the old
+    /// statistics for the old configuration. In particular, we need to remove
+    /// anything related to subnets, as there may be fewer subnets in the new
+    /// configuration and also subnet-ids may change.
+    void removeStatistics();
+
 private:
 
     /// @brief Selects a subnet using the interface name.

+ 7 - 4
src/lib/dhcpsrv/srv_config.cc

@@ -148,16 +148,19 @@ SrvConfig::equals(const SrvConfig& other) const {
 void
 SrvConfig::removeStatistics() {
 
-    // For now, this method only removes statistics for v4 subnets, but in the
-    // near future, we'll also get statistics for v6 subnets.
+    // Removes statistics for v4 and v6 subnets
     getCfgSubnets4()->removeStatistics();
+
+    getCfgSubnets6()->removeStatistics();
 }
 
 void
 SrvConfig::updateStatistics() {
-    // For now, this method only updates statistics for v4 subnets, but in the
-    // near future, we'll also get statistics for v6 subnets.
+
+    // Updates  statistics for v4 and v6 subnets
     getCfgSubnets4()->updateStatistics();
+
+    getCfgSubnets6()->updateStatistics();
 }
 
 }

+ 4 - 2
src/lib/dhcpsrv/srv_config.h

@@ -365,13 +365,15 @@ public:
     /// @brief Updates statistics.
     ///
     /// This method calls appropriate methods in child objects that update
-    /// related statistics. See @ref CfgSubnets4::updateStatistics for details.
+    /// related statistics. See @ref CfgSubnets4::updateStatistics and
+    /// @ref CfgSubnets6::updateStatistics for details.
     void updateStatistics();
 
     /// @brief Removes statistics.
     ///
     /// This method calls appropriate methods in child objects that remove
-    /// related statistics. See @ref CfgSubnets4::removeStatistics for details.
+    /// related statistics. See @ref CfgSubnets4::removeStatistics and
+    /// @ref CfgSubnets6::removeStatistics for details.
     void removeStatistics();
 
 private:

+ 100 - 11
src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc

@@ -17,10 +17,12 @@
 #include <dhcp/pkt6.h>
 #include <dhcpsrv/tests/alloc_engine_utils.h>
 #include <dhcpsrv/tests/test_utils.h>
+#include <stats/stats_mgr.h>
 
 using namespace std;
 using namespace isc::hooks;
 using namespace isc::asiolink;
+using namespace isc::stats;
 
 namespace isc {
 namespace dhcp {
@@ -51,24 +53,54 @@ TEST_F(AllocEngine6Test, constructor) {
 }
 
 // This test checks if the simple allocation (REQUEST) can succeed
+// and the stats counter is properly bumped by 1
 TEST_F(AllocEngine6Test, simpleAlloc6) {
+
     simpleAlloc6Test(pool_, IOAddress("::"), false);
+
+    // We should have bumped the address counter by 1
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas");
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(101, stat->getInteger().first);
 }
 
 // This test checks if the simple PD allocation (REQUEST) can succeed
+// and the stats counter is properly bumped by 1
 TEST_F(AllocEngine6Test, pdSimpleAlloc6) {
+
     simpleAlloc6Test(pd_pool_, IOAddress("::"), false);
+
+    // We should have bumped the address counter by 1
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-pds");
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(101, stat->getInteger().first);
 }
 
 // This test checks if the fake allocation (for SOLICIT) can succeed
+// and the stats counter isn't bumped
 TEST_F(AllocEngine6Test, fakeAlloc6) {
 
     simpleAlloc6Test(pool_, IOAddress("::"), true);
+
+    // We should not have bumped the address counter
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas");
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(100, stat->getInteger().first);
 }
 
 // This test checks if the fake PD allocation (for SOLICIT) can succeed
+// and the stats counter isn't bumped
 TEST_F(AllocEngine6Test, pdFakeAlloc6) {
     simpleAlloc6Test(pd_pool_, IOAddress("::"), true);
+
+    // We should not have bumped the address counter
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-pds");
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(100, stat->getInteger().first);
 };
 
 // This test checks if the allocation with a hint that is valid (in range,
@@ -526,6 +558,10 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
     lease->hostname_ = "myhost.example.com.";
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
+    // By default we pretend our subnet has 100 addresses
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas");
+    StatsMgr::instance().setValue(name, static_cast<int64_t>(100));
+
     // A client comes along, asking specifically for this address
     AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, addr, Lease::TYPE_NA,
                                     false, false, "", false);
@@ -556,6 +592,13 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease(lease, from_mgr);
+
+    // We should not have bumped the address counter
+    // NOTE: when we start expiring addresses and removing them from
+    // the stats this will no longer be true.
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(100, stat->getInteger().first);
 }
 
 // --- v6 host reservation ---
@@ -568,7 +611,7 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 // - Client sends SOLICIT without any hints.
 // - Client is allocated a reserved address.
 //
-// Note that DHCPv6 client can, but don't have to send any hints in its
+// Note that a DHCPv6 client can, but doesn't have to send any hints in its
 // Solicit message.
 TEST_F(AllocEngine6Test, reservedAddressInPoolSolicitNoHint) {
     // Create reservation for the client. This is in-pool reservation,
@@ -599,9 +642,18 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolRequestNoHint) {
 
     AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
 
+    // By default we pretend our subnet has 100 addresses
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas");
+    StatsMgr::instance().setValue(name, static_cast<int64_t>(100));
+
     Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), false);
     ASSERT_TRUE(lease);
     EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
+
+    // We should have bumped the address counter
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(101, stat->getInteger().first);
 }
 
 // Checks that a client gets the address reserved (in-pool case)
@@ -708,7 +760,7 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolRequestMatchingHint) {
 // This test checks the behavior of the allocation engine in the following
 // scenario:
 // - Client has no lease in the database.
-// - Client has an in-pool reservation.
+// - Client has an out-of-pool reservation.
 // - Client sends SOLICIT without any hints.
 // - Client is allocated a reserved address.
 //
@@ -724,13 +776,14 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitNoHint) {
     Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), true, false);
     ASSERT_TRUE(lease);
     EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
+
 }
 
-// Checks that a client gets the address reserved (in-pool case)
+// Checks that a client gets the address reserved (out-of-pool case)
 // This test checks the behavior of the allocation engine in the following
 // scenario:
 // - Client has no lease in the database.
-// - Client has an in-pool reservation.
+// - Client has an out-of-pool reservation.
 // - Client sends REQUEST without any hints.
 // - Client is allocated a reserved address.
 //
@@ -743,9 +796,18 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestNoHint) {
 
     AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
 
+    // By default we pretend our subnet has 100 addresses
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas");
+    StatsMgr::instance().setValue(name, static_cast<int64_t>(100));
+
     Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), false, false);
     ASSERT_TRUE(lease);
     EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
+
+    // We should not have bumped the address counter
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(100, stat->getInteger().first);
 }
 
 // Checks that a client gets the address reserved (in-pool case)
@@ -773,11 +835,11 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitValidHint) {
     EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
 }
 
-// Checks that a client gets the address reserved (in-pool case)
+// Checks that a client gets the address reserved (out-of-pool case)
 // This test checks the behavior of the allocation engine in the following
 // scenario:
 // - Client has no lease in the database.
-// - Client has an in-pool reservation.
+// - Client has an out-of-pool reservation.
 // - Client sends REQUEST with a hint that does not match reservation
 // - Client is allocated a reserved address, not the hint.
 //
@@ -798,11 +860,11 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestValidHint) {
     EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
 }
 
-// Checks that a client gets the address reserved (in-pool case)
+// Checks that a client gets the address reserved (out-of-pool case)
 // This test checks the behavior of the allocation engine in the following
 // scenario:
 // - Client has no lease in the database.
-// - Client has an in-pool reservation.
+// - Client has an out-of-pool reservation.
 // - Client sends SOLICIT with a hint that does matches reservation
 // - Client is allocated a reserved address, not the hint.
 //
@@ -823,11 +885,11 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitMatchingHint) {
     EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
 }
 
-// Checks that a client gets the address reserved (in-pool case)
+// Checks that a client gets the address reserved (out-of-pool case)
 // This test checks the behavior of the allocation engine in the following
 // scenario:
 // - Client has no lease in the database.
-// - Client has an in-pool reservation.
+// - Client has an out-of-pool reservation.
 // - Client sends REQUEST with a hint that does not match reservation
 // - Client is allocated a reserved address, not the hint.
 //
@@ -854,6 +916,7 @@ TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestMatchingHint) {
 // - client tries to get address A:
 //    Check that his existing lease for lease A is removed
 //    Check that he is assigned a new lease for B
+// - verify that the number of assigned address behaves as expected
 TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) {
     AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
 
@@ -861,12 +924,21 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) {
     Lease6Ptr lease1 = simpleAlloc6Test(pool_, IOAddress("::"), false);
     ASSERT_TRUE(lease1);
 
+    // We should have bumped the address counter
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas");
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(101, stat->getInteger().first);
+
     // Just check that if the client requests again, it will get the same
     // address.
     Lease6Ptr lease2 = simpleAlloc6Test(pool_, lease1->addr_, false);
     ASSERT_TRUE(lease2);
     detailCompareLease(lease1, lease2);
 
+    // We should not have bumped the address counter again
+    EXPECT_EQ(101, stat->getInteger().first);
+
     // Now admin creates a reservation for this client. This is in-pool
     // reservation, as the pool is 2001:db8:1::10 - 2001:db8:1::20.
     createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
@@ -894,8 +966,11 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) {
 
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease(lease3, from_mgr);
-}
 
+    // Lastly check to see that the address counter is still 101 we should have
+    // have decremented it on the implied release and incremented it on the reserved
+    EXPECT_EQ(101, stat->getInteger().first);
+}
 // In the following situation:
 // - client X is assigned an address A
 // - HR is made for client Y (*other* client) to get A
@@ -909,12 +984,21 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedOther) {
     Lease6Ptr lease1 = simpleAlloc6Test(pool_, IOAddress("::"), false);
     ASSERT_TRUE(lease1);
 
+    // We should have bumped the address counter
+    string name = StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas");
+    ObservationPtr stat = StatsMgr::instance().getObservation(name);
+    ASSERT_TRUE(stat);
+    EXPECT_EQ(101, stat->getInteger().first);
+
     // Just check that if the client requests again, it will get the same
     // address.
     Lease6Ptr lease2 = simpleAlloc6Test(pool_, lease1->addr_, false);
     ASSERT_TRUE(lease2);
     detailCompareLease(lease1, lease2);
 
+    // We should not have bumped the address counter again
+    EXPECT_EQ(101, stat->getInteger().first);
+
     // Now admin creates a reservation for this client. Let's use the
     // address client X just received. Let's generate a host, but don't add it
     // to the HostMgr yet.
@@ -948,6 +1032,10 @@ TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedOther) {
 
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease(lease3, from_mgr);
+
+    // Lastly check to see that the address counter is still 101 we should have
+    // have decremented it on the implied release and incremented it on the reserved
+    EXPECT_EQ(101, stat->getInteger().first);
 }
 
 // Checks that a reserved address for client A is not assigned when
@@ -1363,6 +1451,7 @@ TEST_F(AllocEngine6Test, reservedAddressByMacInPoolRequestValidHint) {
     EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
 }
 
+
 }; // namespace test
 }; // namespace dhcp
 }; // namespace isc

+ 10 - 0
src/lib/dhcpsrv/tests/alloc_engine_utils.cc

@@ -22,6 +22,7 @@
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
+#include <stats/stats_mgr.h>
 
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/tests/alloc_engine_utils.h>
@@ -40,6 +41,7 @@
 using namespace std;
 using namespace isc::hooks;
 using namespace isc::asiolink;
+using namespace isc::stats;
 
 namespace isc {
 namespace dhcp {
@@ -82,6 +84,14 @@ AllocEngine6Test::initSubnet(const asiolink::IOAddress& subnet,
 
     cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
     cfg_mgr.commit();
+
+    // By default we pretend our subnet has 100 addresses and prefixes allocated.
+    StatsMgr::instance().setValue(
+        StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"),
+        static_cast<int64_t>(100));
+    StatsMgr::instance().setValue(
+        StatsMgr::generateName("subnet", subnet_->getID(), "assigned-pds"),
+        static_cast<int64_t>(100));
 }
 
 void

+ 91 - 4
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc

@@ -578,12 +578,12 @@ TEST_F(CfgMgrTest, verbosity) {
 
 // This test verifies that once the configuration is committed, statistics
 // are updated appropriately.
-TEST_F(CfgMgrTest, commitStats) {
+TEST_F(CfgMgrTest, commitStats4) {
     CfgMgr& cfg_mgr = CfgMgr::instance();
     StatsMgr& stats_mgr = StatsMgr::instance();
 
     // Let's prepare the "old" configuration: a subnet with id 123
-    // and pretent there ware addresses assigned, so statistics are non-zero.
+    // and pretend there were addresses assigned, so statistics are non-zero.
     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
     CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
     subnets->add(subnet1);
@@ -617,12 +617,12 @@ TEST_F(CfgMgrTest, commitStats) {
 
 // This test verifies that once the configuration is cleared, the statistics
 // are removed.
-TEST_F(CfgMgrTest, clearStats) {
+TEST_F(CfgMgrTest, clearStats4) {
     CfgMgr& cfg_mgr = CfgMgr::instance();
     StatsMgr& stats_mgr = StatsMgr::instance();
 
     // Let's prepare the "old" configuration: a subnet with id 123
-    // and pretent there ware addresses assigned, so statistics are non-zero.
+    // and pretend there were addresses assigned, so statistics are non-zero.
     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
     CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
     subnets->add(subnet1);
@@ -642,6 +642,93 @@ TEST_F(CfgMgrTest, clearStats) {
     EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-addresses"));
 }
 
+// This test verifies that once the configuration is committed, statistics
+// are updated appropriately.
+TEST_F(CfgMgrTest, commitStats6) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    StatsMgr& stats_mgr = StatsMgr::instance();
+
+    // Let's prepare the "old" configuration: a subnet with id 123
+    // and pretend there were addresses assigned, so statistics are non-zero.
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 123));
+    CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
+    subnets->add(subnet1);
+    cfg_mgr.commit();
+    stats_mgr.addValue("subnet[123].total-nas", static_cast<int64_t>(256));
+    stats_mgr.setValue("subnet[123].assigned-nas", static_cast<int64_t>(150));
+
+    stats_mgr.addValue("subnet[123].total-pds", static_cast<int64_t>(256));
+    stats_mgr.setValue("subnet[123].assigned-pds", static_cast<int64_t>(150));
+
+    // Now, let's change the configuration to something new.
+
+    // There's a subnet 2001:db8:2::/48 with ID=42
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4, 42));
+
+    // Let's make pools with 128 addresses and 65536 prefixes available.
+    PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:2::"), 121)); // 128 addrs
+    PoolPtr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:3::"), 96, 112)); // 65536 prefixes
+    subnet2->addPool(pool1);
+    subnet2->addPool(pool2);
+
+    subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
+    subnets->add(subnet2);
+
+    // Let's commit it
+    cfg_mgr.commit();
+
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-nas"));
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-nas"));
+
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-pds"));
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-pds"));
+
+    ObservationPtr total_addrs;
+    EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-nas"));
+    ASSERT_TRUE(total_addrs);
+    EXPECT_EQ(128, total_addrs->getInteger().first);
+
+    EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-pds"));
+    ASSERT_TRUE(total_addrs);
+    EXPECT_EQ(65536, total_addrs->getInteger().first);
+}
+
+// This test verifies that once the configuration is cleared, the v6 statistics
+// are removed.
+TEST_F(CfgMgrTest, clearStats6) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    StatsMgr& stats_mgr = StatsMgr::instance();
+
+    // Let's prepare the "old" configuration: a subnet with id 123
+    // and pretend there were addresses assigned, so statistics are non-zero.
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 123));
+    CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
+    subnets->add(subnet1);
+    cfg_mgr.commit();
+    stats_mgr.addValue("subnet[123].total-nas", static_cast<int64_t>(256));
+    stats_mgr.setValue("subnet[123].assigned-nas", static_cast<int64_t>(150));
+
+    stats_mgr.addValue("subnet[123].total-pds", static_cast<int64_t>(256));
+    stats_mgr.setValue("subnet[123].assigned-pds", static_cast<int64_t>(150));
+
+    // The stats should be there.
+    EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-nas"));
+    EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-nas"));
+
+    EXPECT_TRUE(stats_mgr.getObservation("subnet[123].total-pds"));
+    EXPECT_TRUE(stats_mgr.getObservation("subnet[123].assigned-pds"));
+
+    // Let's remove all configurations
+    cfg_mgr.clear();
+
+    // The stats should not be there anymore.
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-nas"));
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-nas"));
+
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-pds"));
+    EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-pds"));
+}
+
 /// @todo Add unit-tests for testing:
 /// - addActiveIface() with invalid interface name
 /// - addActiveIface() with the same interface twice