diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml
index 7c802d3841..896adb7fa0 100644
--- a/doc/guide/dhcp4-srv.xml
+++ b/doc/guide/dhcp4-srv.xml
@@ -2583,6 +2583,41 @@ temporarily override a list of interface names and listen on all interfaces.
+
+
+
+
+ MERGE ME
+
+
+
+ subnet[id].total-addresses (integer) -
+ this statistic shows the total number of addresses available for the
+ DHCPv4 management. 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 id is the
+ subnet-id of a given subnet. This statistic is exposed for each subnet
+ separately. This statistic is reset during reconfiguration event.
+
+
+
+ subnet[id].assigned-addresses (integer) -
+ this statistic shows the number of assigned addresses in a given subnet.
+ 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 proper lease expiration
+ is implemented (planned for Kea 1.0), it will also decrease when a lease
+ is expired. The id is the subnet-id of a given
+ subnet. This statistic is exposed for each subnet separately. This
+ statistic is reset during reconfiguration event.
+
+
+
+
+
+
Supported DHCP Standards
The following standards are currently supported:
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 2f8f6097e0..e68df12a6b 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -141,6 +141,7 @@ libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 8d3147e6b8..20c3ab108f 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -19,10 +19,13 @@
#include
#include
#include
+#include
+#include
#include
using namespace isc::asiolink;
using namespace isc::util;
+using namespace isc::stats;
namespace isc {
namespace dhcp {
@@ -116,6 +119,12 @@ CfgMgr::clear() {
void
CfgMgr::commit() {
+
+ // First we need to remove statistics. The new configuration can have fewer
+ // subnets. Also, it may change subnet-ids. So we need to remove them all
+ // and add it back.
+ removeStatistics();
+
ensureCurrentAllocated();
if (!configs_.back()->sequenceEquals(*configuration_)) {
configuration_ = configs_.back();
@@ -127,6 +136,9 @@ CfgMgr::commit() {
configs_.erase(configs_.begin(), it);
}
}
+
+ // Now we need to set the statistics back.
+ updateStatistics();
}
void
@@ -186,6 +198,40 @@ CfgMgr::getStagingCfg() {
return (configs_.back());
}
+void
+CfgMgr::removeStatistics() {
+ const Subnet4Collection* subnets = getCurrentCfg()->getCfgSubnets4()->getAll();
+
+ // For each subnet currently configured, remove the statistic
+ for (Subnet4Collection::const_iterator subnet = subnets->begin();
+ subnet != subnets->end(); ++subnet) {
+
+ /// @todo: Once stat contexts are implemented, we'll need to remove all
+ /// statistics from the subnet[subnet-id] context.
+ std::stringstream stat1;
+ stat1 << "subnet[" << (*subnet)->getID() << "].total-addresses";
+ StatsMgr::instance().del(stat1.str());
+
+ std::stringstream stat2;
+ stat2 << "subnet[" << (*subnet)->getID() << "].assigned-addresses";
+ isc::stats::StatsMgr::instance().del(stat2.str());
+ }
+}
+
+void
+CfgMgr::updateStatistics() {
+ const Subnet4Collection* subnets = getCurrentCfg()->getCfgSubnets4()->getAll();
+
+ for (Subnet4Collection::const_iterator subnet = subnets->begin();
+ subnet != subnets->end(); ++subnet) {
+ std::stringstream name;
+ name << "subnet[" << (*subnet)->getID() << "].total-addresses";
+
+ StatsMgr::instance().setValue(name.str(),
+ (*subnet)->getPoolCapacity(Lease::TYPE_V4));
+ }
+}
+
CfgMgr::CfgMgr()
: datadir_(DHCP_DATA_DIR), echo_v4_client_id_(true),
d2_client_mgr_(), verbose_mode_(false) {
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 77d8fd996a..4d84976af8 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -339,6 +339,23 @@ private:
/// @return true if the duplicate subnet exists.
bool isDuplicate(const Subnet6& subnet) 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
+ /// 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();
+
/// @brief Container for defined DHCPv6 option spaces.
OptionSpaceCollection spaces6_;
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 36334dd1bd..a4c9f2217f 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -139,6 +139,7 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libdhcpsrv_unittests_LDADD += $(GTEST_LDADD)
endif
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index bf007b91b2..6aec2abf49 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include
@@ -34,6 +35,7 @@ using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::util;
+using namespace isc::stats;
using namespace isc;
// don't import the entire boost namespace. It will unexpectedly hide uint8_t
@@ -574,6 +576,46 @@ TEST_F(CfgMgrTest, verbosity) {
EXPECT_FALSE(CfgMgr::instance().isVerbose());
}
+// This test verifies that once the configuration is committed, statistics
+// are updated appropriately.
+TEST_F(CfgMgrTest, commitStats) {
+ 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.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 123));
+ CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
+ subnets->add(subnet1);
+ cfg_mgr.commit();
+ stats_mgr.addValue("subnet[123].total-addresses", static_cast(256));
+ stats_mgr.setValue("subnet[123].assigned-addresses", static_cast(150));
+
+ // Now, let's change the configuration to something new.
+
+ // There's a subnet 192.1.2.0/24 with ID=42
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3, 42));
+
+ // Let's make a pool with 128 addresses available.
+ PoolPtr pool(new Pool4(IOAddress("192.1.2.0"), 25)); // 128 addrs
+ subnet2->addPool(pool);
+
+ subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
+ subnets->add(subnet2);
+
+ // Let's commit it
+ cfg_mgr.commit();
+
+ EXPECT_FALSE(stats_mgr.getObservation("subnet[123].total-addresses"));
+ EXPECT_FALSE(stats_mgr.getObservation("subnet[123].assigned-addresses"));
+
+ ObservationPtr total_addrs;
+ EXPECT_NO_THROW(total_addrs = stats_mgr.getObservation("subnet[42].total-addresses"));
+ ASSERT_TRUE(total_addrs);
+ EXPECT_EQ(128, total_addrs->getInteger().first);
+}
+
+
/// @todo Add unit-tests for testing:
/// - addActiveIface() with invalid interface name
/// - addActiveIface() with the same interface twice