diff options
author | Thomas Markwalder <tmark@isc.org> | 2016-08-25 17:42:54 +0200 |
---|---|---|
committer | Thomas Markwalder <tmark@isc.org> | 2016-08-25 17:42:54 +0200 |
commit | 0abdcf15f85861ffcb67d50fa4ce3965d25e4a9f (patch) | |
tree | 0eebef3247e82e9947d8cbf4fe58b836f5d42468 | |
parent | [master] Fixed build issue when none of the SQL backends are enabled. (diff) | |
parent | [4294] Changed Memfile stats recount to use existing per-address index (diff) | |
download | kea-0abdcf15f85861ffcb67d50fa4ce3965d25e4a9f.tar.xz kea-0abdcf15f85861ffcb67d50fa4ce3965d25e4a9f.zip |
[master] kea-dhcp4 and kea-dhcp6 now recalculate lease stats after reconfigure
Merges in trac4294
23 files changed, 1446 insertions, 53 deletions
diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 6c6fd408d3..f1c7a8f658 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -8,6 +8,7 @@ #include <dhcp/iface_mgr.h> #include <dhcpsrv/cfg_subnets4.h> #include <dhcpsrv/dhcpsrv_log.h> +#include <dhcpsrv/lease_mgr_factory.h> #include <dhcpsrv/subnet_id.h> #include <dhcpsrv/addr_utilities.h> #include <asiolink/io_address.h> @@ -232,18 +233,21 @@ CfgSubnets4::removeStatistics() { using namespace isc::stats; // For each v4 subnet currently configured, remove the statistic. - /// @todo: May move this to CfgSubnets4 class if there will be more - /// statistics here. + StatsMgr& stats_mgr = StatsMgr::instance(); for (Subnet4Collection::const_iterator subnet4 = subnets_.begin(); subnet4 != subnets_.end(); ++subnet4) { + SubnetID subnet_id = (*subnet4)->getID(); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "total-addresses")); - StatsMgr::instance().del(StatsMgr::generateName("subnet", - (*subnet4)->getID(), - "total-addresses")); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "assigned-addresses")); - StatsMgr::instance().del(StatsMgr::generateName("subnet", - (*subnet4)->getID(), - "assigned-addresses")); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "declined-addresses")); + + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "declined-reclaimed-addresses")); } } @@ -251,14 +255,21 @@ void CfgSubnets4::updateStatistics() { using namespace isc::stats; - /// @todo: May move this to CfgSubnets4 class if there will be more - /// statistics here. - for (Subnet4Collection::const_iterator subnet = subnets_.begin(); - subnet != subnets_.end(); ++subnet) { + StatsMgr& stats_mgr = StatsMgr::instance(); + for (Subnet4Collection::const_iterator subnet4 = subnets_.begin(); + subnet4 != subnets_.end(); ++subnet4) { + SubnetID subnet_id = (*subnet4)->getID(); + + stats_mgr.setValue(StatsMgr:: + generateName("subnet", subnet_id, "total-addresses"), + static_cast<int64_t> + ((*subnet4)->getPoolCapacity(Lease:: + TYPE_V4))); + } - StatsMgr::instance().setValue( - StatsMgr::generateName("subnet", (*subnet)->getID(), "total-addresses"), - static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_V4))); + // Only recount the stats if we have subnets. + if (subnets_.begin() != subnets_.end()) { + LeaseMgrFactory::instance().recountLeaseStats4(); } } diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index ad8ed4c6c6..3136763f3c 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2016 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 @@ -7,6 +7,7 @@ #include <config.h> #include <dhcpsrv/cfg_subnets6.h> #include <dhcpsrv/dhcpsrv_log.h> +#include <dhcpsrv/lease_mgr_factory.h> #include <dhcpsrv/subnet_id.h> #include <stats/stats_mgr.h> @@ -176,25 +177,26 @@ void CfgSubnets6::removeStatistics() { using namespace isc::stats; + StatsMgr& stats_mgr = StatsMgr::instance(); // For each v6 subnet currently configured, remove the statistics. for (Subnet6Collection::const_iterator subnet6 = subnets_.begin(); subnet6 != subnets_.end(); ++subnet6) { + SubnetID subnet_id = (*subnet6)->getID(); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-nas")); - StatsMgr::instance().del(StatsMgr::generateName("subnet", - (*subnet6)->getID(), - "total-nas")); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "assigned-nas")); - StatsMgr::instance().del(StatsMgr::generateName("subnet", - (*subnet6)->getID(), - "assigned-nas")); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-pds")); - StatsMgr::instance().del(StatsMgr::generateName("subnet", - (*subnet6)->getID(), - "total-pds")); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "assigned-pds")); - StatsMgr::instance().del(StatsMgr::generateName("subnet", - (*subnet6)->getID(), - "assigned-pds")); + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "declined-addresses")); + + stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, + "declined-reclaimed-addresses")); } } @@ -202,16 +204,26 @@ void CfgSubnets6::updateStatistics() { using namespace isc::stats; - for (Subnet6Collection::const_iterator subnet = subnets_.begin(); - subnet != subnets_.end(); ++subnet) { + StatsMgr& stats_mgr = StatsMgr::instance(); + // For each v6 subnet currently configured, calculate totals + for (Subnet6Collection::const_iterator subnet6 = subnets_.begin(); + subnet6 != subnets_.end(); ++subnet6) { + SubnetID subnet_id = (*subnet6)->getID(); + + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "total-nas"), + static_cast<int64_t> + ((*subnet6)->getPoolCapacity(Lease::TYPE_NA))); - StatsMgr::instance().setValue( - StatsMgr::generateName("subnet", (*subnet)->getID(), "total-nas"), - static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_NA))); + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "total-pds"), + static_cast<int64_t> + ((*subnet6)->getPoolCapacity(Lease::TYPE_PD))); + } - StatsMgr::instance().setValue( - StatsMgr::generateName("subnet", (*subnet)->getID(), "total-pds"), - static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_PD))); + // Only recount the stats if we have subnets. + if (subnets_.begin() != subnets_.end()) { + LeaseMgrFactory::instance().recountLeaseStats6(); } } diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h index d1ab300f11..c89ad523a3 100644 --- a/src/lib/dhcpsrv/lease.h +++ b/src/lib/dhcpsrv/lease.h @@ -52,8 +52,6 @@ struct Lease { /// @brief Expired and reclaimed lease. static const uint32_t STATE_EXPIRED_RECLAIMED; - //@} - /// @brief Returns name(s) of the basic lease state(s). /// /// @param state A numeric value holding a state information. diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc index 58f95d7dc9..cd84a69839 100644 --- a/src/lib/dhcpsrv/lease_mgr.cc +++ b/src/lib/dhcpsrv/lease_mgr.cc @@ -6,8 +6,11 @@ #include <config.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/dhcpsrv_log.h> #include <dhcpsrv/lease_mgr.h> #include <exceptions/exceptions.h> +#include <stats/stats_mgr.h> #include <boost/foreach.hpp> #include <boost/algorithm/string.hpp> @@ -44,6 +47,166 @@ LeaseMgr::getLease6(Lease::Type type, const DUID& duid, return (*col.begin()); } +void +LeaseMgr::recountLeaseStats4() { + using namespace stats; + + StatsMgr& stats_mgr = StatsMgr::instance(); + + LeaseStatsQueryPtr query = startLeaseStatsQuery4(); + if (!query) { + /// NULL means not backend does not support recounting. + return; + } + + // Zero out the global stats. + int64_t zero = 0; + stats_mgr.setValue("declined-addresses", zero); + stats_mgr.setValue("declined-reclaimed-addresses", zero); + + // Clear subnet level stats. This ensures we don't end up with corner + // cases that leave stale values in place. + const Subnet4Collection* subnets = + CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); + + for (Subnet4Collection::const_iterator subnet = subnets->begin(); + subnet != subnets->end(); ++subnet) { + SubnetID subnet_id = (*subnet)->getID(); + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "assigned-addresses"), + zero); + + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "declined-addresses"), + zero); + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "declined-reclaimed-addresses"), + zero); + } + + // Get counts per state per subnet. Iterate over the result set + // updating the subnet and global values. + LeaseStatsRow row; + while (query->getNextRow(row)) { + if (row.lease_state_ == Lease::STATE_DEFAULT) { + // Set subnet level value. + stats_mgr.setValue(StatsMgr::generateName("subnet", row.subnet_id_, + "assigned-addresses"), + row.state_count_); + } else if (row.lease_state_ == Lease::STATE_DECLINED) { + // Set subnet level value. + stats_mgr.setValue(StatsMgr::generateName("subnet", row.subnet_id_, + "declined-addresses"), + row.state_count_); + + // Add to the global value. + stats_mgr.addValue("declined-addresses", row.state_count_); + } + } +} + +LeaseStatsQueryPtr +LeaseMgr::startLeaseStatsQuery4() { + return(LeaseStatsQueryPtr()); +} + +bool +LeaseStatsQuery::getNextRow(LeaseStatsRow& /*row*/) { + return (false); +} + +void +LeaseMgr::recountLeaseStats6() { + using namespace stats; + + StatsMgr& stats_mgr = StatsMgr::instance(); + + LeaseStatsQueryPtr query = startLeaseStatsQuery6(); + if (!query) { + /// NULL means not backend does not support recounting. + return; + } + + // Zero out the global stats. (Ok, so currently there's only one + // that should be cleared. "reclaimed-declined-addresses" never + // gets zeroed. @todo discuss with Tomek the rational of not + // clearing it when we clear the rest. + int64_t zero = 0; + stats_mgr.setValue("declined-addresses", zero); + stats_mgr.setValue("declined-reclaimed-addresses", zero); + + // Clear subnet level stats. This ensures we don't end up with corner + // cases that leave stale values in place. + const Subnet6Collection* subnets = + CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll(); + + for (Subnet6Collection::const_iterator subnet = subnets->begin(); + subnet != subnets->end(); ++subnet) { + SubnetID subnet_id = (*subnet)->getID(); + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "assigned-nas"), + zero); + + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "declined-addresses"), + zero); + + stats_mgr.setValue(StatsMgr:: + generateName("subnet", subnet_id, + "declined-reclaimed-addresses"), + zero); + + stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id, + "assigned-pds"), + zero); + } + + // Get counts per state per subnet. Iterate over the result set + // updating the subnet and global values. + LeaseStatsRow row; + while (query->getNextRow(row)) { + switch(row.lease_type_) { + case Lease::TYPE_NA: + if (row.lease_state_ == Lease::STATE_DEFAULT) { + // Set subnet level value. + stats_mgr.setValue(StatsMgr:: + generateName("subnet", row.subnet_id_, + "assigned-nas"), + row.state_count_); + } if (row.lease_state_ == Lease::STATE_DECLINED) { + // Set subnet level value. + stats_mgr.setValue(StatsMgr:: + generateName("subnet", row.subnet_id_, + "declined-addresses"), + row.state_count_); + + // Add to the global value. + stats_mgr.addValue("declined-addresses", row.state_count_); + } + break; + + case Lease::TYPE_PD: + if (row.lease_state_ == Lease::STATE_DEFAULT) { + // Set subnet level value. + stats_mgr.setValue(StatsMgr:: + generateName("subnet", row.subnet_id_, + "assigned-pds"), + row.state_count_); + } + break; + + default: + // We dont' support TYPE_TAs yet + break; + } + } +} + +LeaseStatsQueryPtr +LeaseMgr::startLeaseStatsQuery6() { + return(LeaseStatsQueryPtr()); +} + std::string LeaseMgr::getDBVersion() { isc_throw(NotImplemented, "LeaseMgr::getDBVersion() called"); diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index c31f2dfa12..c8b4ef4e0d 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -146,6 +146,86 @@ public: virtual ~SqlExchange() {}; ExchangeColumnInfoContainer parameters_; ///< Column names and types }; + +/// @brief Contains a single row of lease statistical data +/// +/// The contents of the row consist of a subnet ID, a lease +/// type, a lease state, and the number of leases in that state +/// for that type for that subnet ID. +struct LeaseStatsRow { + /// @brief Default constructor + LeaseStatsRow() : + subnet_id_(0), lease_type_(Lease::TYPE_NA), + lease_state_(Lease::STATE_DEFAULT), state_count_(0) { + } + + /// @brief Constructor + /// + /// Constructor which defaults the type to TYPE_NA. + /// + /// @param subnet_id The subnet id to which this data applies + /// @param lease_state The lease state counted + /// @param state_count The count of leases in the lease state + LeaseStatsRow(const SubnetID& subnet_id, const uint32_t lease_state, + const int64_t state_count) + : subnet_id_(subnet_id), lease_type_(Lease::TYPE_NA), + lease_state_(lease_state), state_count_(state_count) { + } + + /// @brief Constructor + /// + /// @param subnet_id The subnet id to which this data applies + /// @param lease_type The lease type for this state count + /// @param lease_state The lease state counted + /// @param state_count The count of leases in the lease state + LeaseStatsRow(const SubnetID& subnet_id, const Lease::Type& lease_type, + const uint32_t lease_state, const int64_t state_count) + : subnet_id_(subnet_id), lease_type_(lease_type), + lease_state_(lease_state), state_count_(state_count) { + } + + /// @brief The subnet ID to which this data applies + SubnetID subnet_id_; + /// @brief The lease_type to which the count applies + Lease::Type lease_type_; + /// @brief The lease_state to which the count applies + uint32_t lease_state_; + /// @brief state_count The count of leases in the lease state + int64_t state_count_; +}; + +/// @brief Base class for fulfilling a statistical lease data query +/// +/// LeaseMgr derivations implement this class such that it provides +/// upto date statistical lease data organized as rows of LeaseStatsRow +/// instances. The rows must be accessible in ascending order by subnet id. +class LeaseStatsQuery { +public: + /// @brief Default constructor + LeaseStatsQuery() {}; + + /// @brief virtual destructor + virtual ~LeaseStatsQuery() {}; + + /// @brief Executes the query + /// + /// This method should conduct whatever steps are required to + /// calculate the lease statistical data by examining the + /// lease data and making that results available row by row. + virtual void start() {}; + + /// @brief Fetches the next row of data + /// + /// @param[out] row Storage into which the row is fetched + /// + /// @return True if a row was fetched, false if there are no + /// more rows. + virtual bool getNextRow(LeaseStatsRow& row); +}; + +/// @brief Defines a pointer to an LeaseStatsQuery. +typedef boost::shared_ptr<LeaseStatsQuery> LeaseStatsQueryPtr; + /// @brief Abstract Lease Manager /// /// This is an abstract API for lease database backends. It provides unified @@ -397,6 +477,68 @@ public: /// @return Number of leases deleted. virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs) = 0; + /// @brief Recalculates per-subnet and global stats for IPv4 leases + /// + /// This method recalculates the following statistics: + /// per-subnet: + /// - assigned-addresses + /// - declined-addresses + /// - declined-reclaimed-addresses (reset to zero) + /// global: + /// - declined-addresses + /// - declined-reclaimed-addresses (reset to zero) + /// + /// It invokes the virtual method, startLeaseStatsQuery4(), which + /// returns an instance of an LeaseStatsQuery. The query + /// query contains a "result set" where each row is an LeaseStatRow + /// that contains a subnet id, a lease type (currently always TYPE_NA), + /// a lease state, and the number of leases of that type, in that state + /// and is ordered by subnet id. The method iterates over the + /// result set rows, setting the appropriate statistic per subnet and + /// adding to the approporate global statistic. + void recountLeaseStats4(); + + /// @brief Virtual method which creates and runs the IPv4 lease stats query + /// + /// LeaseMgr derivations implement this method such that it creates and + /// returns an instance of an LeaseStatsQuery whose result set has been + /// populated with upto date IPv4 lease statistical data. Each row of the + /// result set is an LeaseStatRow which ordered ascending by subnet ID. + /// + /// @return A populated LeaseStatsQuery + virtual LeaseStatsQueryPtr startLeaseStatsQuery4(); + + /// @brief Recalculates per-subnet and global stats for IPv6 leases + /// + /// This method recalculates the following statistics: + /// per-subnet: + /// - assigned-addresses + /// - declined-addresses + /// - declined-reclaimed-addresses (reset to zero) + /// - assigned-pds + /// global: + /// - declined-addresses + /// - declined-reclaimed-addresses (reset to zero) + /// + /// It invokes the virtual method, startLeaseStatsQuery6(), which + /// returns an instance of an LeaseStatsQuery. The query contains + /// a "result set" where each row is an LeaseStatRow that contains + /// a subnet id, a lease type, a lease state, and the number of leases + /// of that type, in that state and is ordered by subnet id. The method + /// iterates over the result set rows, setting the appropriate statistic + /// per subnet and adding to the approporate global statistic. + void recountLeaseStats6(); + + /// @brief Virtual method which creates and runs the IPv6 lease stats query + /// + /// LeaseMgr derivations implement this method such that it creates and + /// returns an instance of an LeaseStatsQuery whose result set has been + /// populated with upto date IPv6 lease statistical data. Each row of the + /// result set is an LeaseStatRow which ordered ascending by subnet ID. + /// + /// @return A populated LeaseStatsQuery + virtual LeaseStatsQueryPtr startLeaseStatsQuery6(); + /// @brief Return backend type /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/lease_mgr_factory.cc b/src/lib/dhcpsrv/lease_mgr_factory.cc index 5f2f33a4f3..a9f1507b2b 100644 --- a/src/lib/dhcpsrv/lease_mgr_factory.cc +++ b/src/lib/dhcpsrv/lease_mgr_factory.cc @@ -101,6 +101,11 @@ LeaseMgrFactory::destroy() { getLeaseMgrPtr().reset(); } +bool +LeaseMgrFactory::haveInstance() { + return (getLeaseMgrPtr().get()); +} + LeaseMgr& LeaseMgrFactory::instance() { LeaseMgr* lmptr = getLeaseMgrPtr().get(); diff --git a/src/lib/dhcpsrv/lease_mgr_factory.h b/src/lib/dhcpsrv/lease_mgr_factory.h index 279058bed8..4c6baf1270 100644 --- a/src/lib/dhcpsrv/lease_mgr_factory.h +++ b/src/lib/dhcpsrv/lease_mgr_factory.h @@ -85,7 +85,10 @@ public: /// create() to create one before calling this method. static LeaseMgr& instance(); - + /// @brief Indicates if the lease manager has been instantiated. + /// + /// @return True if the lease manager instance exists, false otherwise. + static bool haveInstance(); private: /// @brief Hold pointer to lease manager diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index 51314b4cd8..4ff78be9d1 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -256,6 +256,268 @@ LFCSetup::getExitStatus() const { return (process_->getExitStatus(pid_)); } + +/// @brief Base Memfile derivation of the statistical lease data query +/// +/// This class provides the functionality such as results storage and row +/// fetching common to fulfilling the statistical lease data query. +/// +class MemfileLeaseStatsQuery : public LeaseStatsQuery { +public: + /// @brief Constructor + /// + MemfileLeaseStatsQuery() + : rows_(0), next_pos_(rows_.end()) { + }; + + /// @brief Destructor + virtual ~MemfileLeaseStatsQuery() {}; + + /// @brief Fetches the next row in the result set + /// + /// Once the internal result set has been populated by invoking the + /// the start() method, this method is used to iterate over the + /// result set rows. Once the last row has been fetched, subsequent + /// calls will return false. + /// @param row Storage for the fetched row + /// + /// @return True if the fetch succeeded, false if there are no more + /// rows to fetch. + virtual bool getNextRow(LeaseStatsRow& row) { + if (next_pos_ == rows_.end()) { + return (false); + } + + row = *next_pos_; + ++next_pos_; + return (true); + } + + /// @brief Returns the number of rows in the result set + int getRowCount() const { + return (rows_.size()); + } + +protected: + /// @brief A vector containing the "result set" + std::vector<LeaseStatsRow> rows_; + + /// @brief An iterator for accessing the next row within the result set + std::vector<LeaseStatsRow>::iterator next_pos_; +}; + +/// @brief Memfile derivation of the IPv4 statistical lease data query +/// +/// This class is used to recalculate IPv4 lease statistics for Memfile +/// lease storage. It does so by iterating over the given storage, +/// accumulating counts of leases in each of the monitored lease states +/// for each subnet and storing these counts in an internal collection. +/// The populated result set will contain one entry per monitored state +/// per subnet. +/// +class MemfileLeaseStatsQuery4 : public MemfileLeaseStatsQuery { +public: + /// @brief Constructor + /// + /// @param storage4 A pointer to the v4 lease storage to be counted + MemfileLeaseStatsQuery4(Lease4Storage& storage4) + : MemfileLeaseStatsQuery(), storage4_(storage4) { + }; + + /// @brief Destructor + virtual ~MemfileLeaseStatsQuery4() {}; + + /// @brief Creates the IPv4 lease statistical data result set + /// + /// The result set is populated by iterating over the IPv4 leases in + /// storage, in ascending order by address, accumulating the lease state + /// counts per subnet. Note that walking the leases by address should + /// inherently group them by subnet, and while this does not gaurantee + /// ascending order of subnet id, it should be sufficient to accumulate + /// state counts per subnet. This avoids introducing an additional + /// subnet_id index. + /// At the completion of all entries for a given subnet, the counts are + /// used to create LeaseStatsRow instances which are appended to an + /// internal vector. The process results in a vector containing one entry + /// per state per subnet. + /// + /// Currently the states counted are: + /// + /// - Lease::STATE_DEFAULT (i.e. assigned) + /// - Lease::STATE_DECLINED + void start() { + const Lease4StorageAddressIndex& idx + = storage4_.get<AddressIndexTag>(); + + // Iterate over the leases in order by subnet, accumulating per + // subnet counts for each state of interest. As we finish each + // subnet, add the appropriate rows to our result set. + SubnetID cur_id = 0; + int64_t assigned = 0; + int64_t declined = 0; + for(Lease4StorageAddressIndex::const_iterator lease = idx.begin(); + lease != idx.end(); ++lease) { + // If we've hit the next subnet, add rows for the current subnet + // and wipe the accumulators + if ((*lease)->subnet_id_ != cur_id) { + if (cur_id > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DEFAULT, + assigned)); + assigned = 0; + rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DECLINED, + declined)); + declined = 0; + } + + // Update current subnet id + cur_id = (*lease)->subnet_id_; + } + + // Bump the appropriate accumulator + if ((*lease)->state_ == Lease::STATE_DEFAULT) { + ++assigned; + } else if ((*lease)->state_ == Lease::STATE_DECLINED) { + ++declined; + } + } + + // Make the rows for last subnet, unless there were no rows + if (idx.begin() != idx.end()) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DEFAULT, + assigned)); + rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DECLINED, + declined)); + } + + // Set the next row position to the beginning of the rows. + next_pos_ = rows_.begin(); + } + +private: + /// @brief The Memfile storage containing the IPv4 leases to analyze + Lease4Storage& storage4_; +}; + + +/// @brief Memfile derivation of the IPv6 statistical lease data query +/// +/// This class is used to recalculate IPv6 lease statistics for Memfile +/// lease storage. It does so by iterating over the given storage, +/// accumulating counts of leases in each of the monitored lease states +/// for each subnet and storing these counts in an internal collection. +/// The populated result set will contain one entry per monitored state +/// per subnet. +/// +class MemfileLeaseStatsQuery6 : public MemfileLeaseStatsQuery { +public: + /// @brief Constructor + /// + /// @param storage6 A pointer to the v6 lease storage to be counted + MemfileLeaseStatsQuery6(Lease6Storage& storage6) + : MemfileLeaseStatsQuery(), storage6_(storage6) { + }; + + /// @brief Destructor + virtual ~MemfileLeaseStatsQuery6() {}; + + /// @brief Creates the IPv6 lease statistical data result set + /// + /// The result set is populated by iterating over the IPv6 leases in + /// storage, in ascending order by address, accumulating the lease state + /// counts per subnet. Note that walking the leases by address should + /// inherently group them by subnet, and while this does not gaurantee + /// ascending order of subnet id, it should be sufficient to accumulate + /// state counts per subnet. This avoids introducing an additional + /// subnet_id index. + /// At the completion of all entries for a given subnet, the counts + /// are used to create LeaseStatsRow instances which are appended to an + /// internal vector. The process results in a vector containing one entry + /// per state per lease type per subnet. + /// + /// Currently the states counted are: + /// + /// - Lease::STATE_DEFAULT (i.e. assigned) + /// - Lease::STATE_DECLINED + virtual void start() { + // Get the subnet_id index + const Lease6StorageAddressIndex& idx + = storage6_.get<AddressIndexTag>(); + + // Iterate over the leases in order by subnet, accumulating per + // subnet counts for each state of interest. As we finish each + // subnet, add the appropriate rows to our result set. + SubnetID cur_id = 0; + int64_t assigned = 0; + int64_t declined = 0; + int64_t assigned_pds = 0; + + for(Lease6StorageAddressIndex::const_iterator lease = idx.begin(); + lease != idx.end(); ++lease) { + + // If we've hit the next subnet, add rows for the current subnet + // and wipe the accumulators + if ((*lease)->subnet_id_ != cur_id) { + if (cur_id > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DEFAULT, + assigned)); + assigned = 0; + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DECLINED, + declined)); + declined = 0; + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_PD, + Lease::STATE_DEFAULT, + assigned_pds)); + assigned_pds = 0; + } + + // Update current subnet id + cur_id = (*lease)->subnet_id_; + } + + // Bump the appropriate accumulator + if ((*lease)->state_ == Lease::STATE_DEFAULT) { + switch((*lease)->type_) { + case Lease::TYPE_NA: + ++assigned; + break; + case Lease::TYPE_PD: + ++assigned_pds; + break; + default: + break; + } + } else if ((*lease)->state_ == Lease::STATE_DECLINED) { + // In theory only NAs can be declined + if (((*lease)->type_) == Lease::TYPE_NA) { + ++declined; + } + } + } + + // Make the rows for last subnet, unless there were no rows + if (idx.begin() != idx.end()) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DEFAULT, + assigned)); + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DECLINED, + declined)); + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_PD, + Lease::STATE_DEFAULT, + assigned_pds)); + } + + // Set the next row position to the beginning of the rows. + next_pos_ = rows_.begin(); + } + +private: + /// @brief The Memfile storage containing the IPv6 leases to analyze + Lease6Storage& storage6_; +}; + // Explicit definition of class static constants. Values are given in the // declaration so they're not needed here. const int Memfile_LeaseMgr::MAJOR_VERSION; @@ -299,6 +561,7 @@ Memfile_LeaseMgr::Memfile_LeaseMgr(const DatabaseConnection::ParameterMap& param } lfcSetup(conversion_needed); } + } Memfile_LeaseMgr::~Memfile_LeaseMgr() { @@ -1048,5 +1311,19 @@ void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file) } } +LeaseStatsQueryPtr +Memfile_LeaseMgr::startLeaseStatsQuery4() { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery4(storage4_)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +Memfile_LeaseMgr::startLeaseStatsQuery6() { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery6(storage6_)); + query->start(); + return(query); +} + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index 1ace364cf7..bdbd985511 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -92,6 +92,7 @@ public: /// @} + /// @brief Specifies universe (V4, V6) /// /// This enumeration is used by various functions in Memfile %Lease Manager, @@ -594,6 +595,23 @@ public: int getLFCExitStatus() const; //@} + /// @brief Creates and runs the IPv4 lease stats query + /// + /// It creates an instance of a MemfileLeaseStatsQuery4 and then + /// invokes its start method in which the query constructs its + /// statistical data result set. The query object is then returned. + /// + /// @return The populated query as a pointer to an LeaseStatsQuery + virtual LeaseStatsQueryPtr startLeaseStatsQuery4(); + + /// @brief Creates and runs the IPv6 lease stats query + /// + /// It creates an instance of a MemfileLeaseStatsQuery6 and then + /// invokes its start method in which the query constructs its + /// statistical data result set. The query object is then returned. + /// + /// @return The populated query as a pointer to an LeaseStatsQuery. + virtual LeaseStatsQueryPtr startLeaseStatsQuery6(); /// @name Protected methods used for %Lease File Cleanup. /// The following methods are protected so as they can be accessed and diff --git a/src/lib/dhcpsrv/memfile_lease_storage.h b/src/lib/dhcpsrv/memfile_lease_storage.h index 566a74d7c5..3b07fad238 100644 --- a/src/lib/dhcpsrv/memfile_lease_storage.h +++ b/src/lib/dhcpsrv/memfile_lease_storage.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2016 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 diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 8edf4dfc5c..344fb272fc 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -71,6 +71,7 @@ using namespace std; /// lease object. namespace { + /// @brief Maximum length of the hostname stored in DNS. /// /// This length is restricted by the length of the domain-name carried @@ -206,7 +207,14 @@ tagged_statements = { { "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, " "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, " "state = ? " - "WHERE address = ?"} + "WHERE address = ?"}, + {MySqlLeaseMgr::RECOUNT_LEASE4_STATS, + "SELECT subnet_id, state, count(state) as state_count " + " FROM lease4 GROUP BY subnet_id, state ORDER BY subnet_id"}, + {MySqlLeaseMgr::RECOUNT_LEASE6_STATS, + "SELECT subnet_id, lease_type, state, count(state) as state_count" + " FROM lease6 GROUP BY subnet_id, lease_type, state " + " ORDER BY subnet_id" } } }; @@ -1215,8 +1223,145 @@ private: uint32_t state_; ///< Lease state. }; +/// @brief MySql derivation of the statistical lease data query +/// +/// This class is used to recalculate lease statistics for MySQL +/// lease storage. It does so by executing a query which returns a result +/// containining contain one row per monitored state per lease type per +/// subnet, ordered by subnet id in ascending order. +/// +class MySqlLeaseStatsQuery : public LeaseStatsQuery { +public: + /// @brief Constructor + /// + /// @param conn A open connection to the database housing the lease data + /// @brief statement_index Index of the query's prepared statement + /// @brief fetch_type Indicates if query supplies lease type + MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index, + const bool fetch_type) + : conn_(conn), statement_index_(statement_index), statement_(NULL), + fetch_type_(fetch_type), + // Set the number of columns in the bind array based on fetch_type + // This is the number of columns expected in the result set + bind_(fetch_type_ ? 4 : 3) { + if (statement_index_ >= MySqlLeaseMgr::NUM_STATEMENTS) { + isc_throw(BadValue, "MySqlLeaseStatsQuery" + " - invalid statement index" << statement_index_); + } + + statement_ = conn.statements_[statement_index_]; + } + + /// @brief Destructor + virtual ~MySqlLeaseStatsQuery() { + (void) mysql_stmt_free_result(statement_); + } + /// @brief Creates the IPv4 lease statistical data result set + /// + /// The result set is populated by executing a SQL query against the + /// lease(4/6) table which sums the leases per lease state per lease + /// type (v6 only) per subnet id. This method binds the statement to + /// the output bind array and then executes the statement, and fetches + /// entire result set. + void start() { + int col = 0; + // subnet_id: unsigned int + bind_[col].buffer_type = MYSQL_TYPE_LONG; + bind_[col].buffer = reinterpret_cast<char*>(&subnet_id_); + bind_[col].is_unsigned = MLM_TRUE; + ++col; + + // Fetch the lease type if we were told to do so. + if (fetch_type_) { + // lease type: uint32_t + bind_[col].buffer_type = MYSQL_TYPE_LONG; + bind_[col].buffer = reinterpret_cast<char*>(&lease_type_); + bind_[col].is_unsigned = MLM_TRUE; + ++col; + } else { + fetch_type_ = Lease::TYPE_NA; + } + // state: uint32_t + bind_[col].buffer_type = MYSQL_TYPE_LONG; + bind_[col].buffer = reinterpret_cast<char*>(&lease_state_); + bind_[col].is_unsigned = MLM_TRUE; + ++col; + + // state_count_: uint32_t + bind_[col].buffer_type = MYSQL_TYPE_LONG; + bind_[col].buffer = reinterpret_cast<char*>(&state_count_); + bind_[col].is_unsigned = MLM_TRUE; + + // Set up the MYSQL_BIND array for the data being returned + // and bind it to the statement. + int status = mysql_stmt_bind_result(statement_, &bind_[0]); + conn_.checkError(status, statement_index_, "outbound binding failed"); + + // Execute the statement + status = mysql_stmt_execute(statement_); + conn_.checkError(status, statement_index_, "unable to execute"); + + // Ensure that all the lease information is retrieved in one go to avoid + // overhead of going back and forth between client and server. + status = mysql_stmt_store_result(statement_); + conn_.checkError(status, statement_index_, "results storage failed"); + } + + + /// @brief Fetches the next row in the result set + /// + /// Once the internal result set has been populated by invoking the + /// the start() method, this method is used to iterate over the + /// result set rows. Once the last row has been fetched, subsequent + /// calls will return false. + /// + /// @param row Storage for the fetched row + /// + /// @return True if the fetch succeeded, false if there are no more + /// rows to fetch. + bool getNextRow(LeaseStatsRow& row) { + bool have_row = false; + int status = mysql_stmt_fetch(statement_); + if (status == MLM_MYSQL_FETCH_SUCCESS) { + row.subnet_id_ = static_cast<SubnetID>(subnet_id_); + row.lease_type_ = static_cast<Lease::Type>(lease_type_); + row.lease_state_ = lease_state_; + row.state_count_ = state_count_; + have_row = true; + } else if (status != MYSQL_NO_DATA) { + conn_.checkError(status, statement_index_, "getNextRow failed"); + } + + return (have_row); + } + +private: + /// @brief Database connection to use to execute the query + MySqlConnection& conn_; + + /// @brief Index of the query's prepared statement + size_t statement_index_; + + /// @brief The query's prepared statement + MYSQL_STMT *statement_; + + /// @brief Indicates if query supplies lease type + bool fetch_type_; + + /// @brief Bind array used to store the query result set; + std::vector<MYSQL_BIND> bind_; + + /// @brief Receives subnet ID when fetching a row + uint32_t subnet_id_; + /// @brief Receives the lease type when fetching a row + uint32_t lease_type_; + /// @brief Receives the lease state when fetching a row + uint32_t lease_state_; + /// @brief Receives the state count when fetching a row + uint32_t state_count_; +}; // MySqlLeaseMgr Constructor and Destructor @@ -2026,6 +2171,23 @@ MySqlLeaseMgr::getVersion() const { return (std::make_pair(major, minor)); } +LeaseStatsQueryPtr +MySqlLeaseMgr::startLeaseStatsQuery4() { + LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_, + RECOUNT_LEASE4_STATS, + false)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +MySqlLeaseMgr::startLeaseStatsQuery6() { + LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_, + RECOUNT_LEASE6_STATS, + true)); + query->start(); + return(query); +} void MySqlLeaseMgr::commit() { diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index a5824becbb..0e4b3af0b2 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -25,7 +25,6 @@ namespace dhcp { class MySqlLease4Exchange; class MySqlLease6Exchange; - /// @brief MySQL Lease Manager /// /// This class provides the \ref isc::dhcp::LeaseMgr interface to the MySQL @@ -410,6 +409,8 @@ public: INSERT_LEASE6, // Add entry to lease6 table UPDATE_LEASE4, // Update a Lease4 entry UPDATE_LEASE6, // Update a Lease6 entry + RECOUNT_LEASE4_STATS, // Fetches IPv4 address statisics + RECOUNT_LEASE6_STATS, // Fetches IPv6 address statisics NUM_STATEMENTS // Number of statements }; @@ -590,6 +591,25 @@ private: uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIndex statement_index); + /// @brief Creates and runs the IPv4 lease stats query + /// + /// It creates an instance of a MySqlLeaseStatsQuery4 and then + /// invokes its start method, which fetches its statistical data + /// result set by executing the RECOUNT_LEASE_STATS4 query. + /// The query object is then returned. + /// + /// @return The populated query as a pointer to an LeaseStatsQuery + virtual LeaseStatsQueryPtr startLeaseStatsQuery4(); + + /// @brief Creates and runs the IPv6 lease stats query + /// + /// It creates an instance of a MySqlLeaseStatsQuery6 and then + /// invokes its start method, which fetches its statistical data + /// result set by executing the RECOUNT_LEASE_STATS6 query. + /// The query object is then returned. + /// + /// @return The populated query as a pointer to an LeaseStatsQuery + virtual LeaseStatsQueryPtr startLeaseStatsQuery6(); /// @brief Check Error and Throw Exception /// diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index f9a5ad28eb..3ea7475484 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -26,7 +26,7 @@ using namespace std; namespace { -/// @todo TKM lease6 needs to accomodate hwaddr,hwtype, and hwaddr source +/// @todo TKM lease6 needs to accomodate hwaddr,hwtype, and hwaddr source /// columns. This is coverd by tickets #3557, #4530, and PR#9. /// @brief Catalog of all the SQL statements currently supported. Note @@ -201,6 +201,19 @@ PgSqlTaggedStatement tagged_statements[] = { "state = $13 " "WHERE address = $14"}, + // RECOUNT_LEASE4_STATS, + { 0, { OID_NONE }, + "recount_lease4_stats", + "SELECT subnet_id, state, count(state) as state_count " + "FROM lease4 GROUP BY subnet_id, state ORDER BY subnet_id"}, + + // RECOUNT_LEASE6_STATS, + { 0, { OID_NONE }, + "recount_lease6_stats", + "SELECT subnet_id, lease_type, state, count(state) as state_count " + "FROM lease6 GROUP BY subnet_id, lease_type, state " + "ORDER BY subnet_id"}, + // End of list sentinel { 0, { 0 }, NULL, NULL} }; @@ -681,6 +694,107 @@ private: //@} }; +/// @brief Base PgSql derivation of the statistical lease data query +/// +/// This class provides the functionality such as results storgae and row +/// fetching common to fulfilling the statistical lease data query. +/// +class PgSqlLeaseStatsQuery : public LeaseStatsQuery { +public: + /// @brief Constructor + /// + /// @param conn A open connection to the database housing the lease data + /// @param statement The lease data SQL prepared statement to execute + /// @param fetch_statement Indicates whether or not lease_type should be + /// fetched from the result set + PgSqlLeaseStatsQuery(PgSqlConnection& conn, PgSqlTaggedStatement& statement, + const bool fetch_type) + : conn_(conn), statement_(statement), result_set_(), next_row_(0), + fetch_type_(fetch_type) { + } + + /// @brief Destructor + virtual ~PgSqlLeaseStatsQuery() {}; + + /// @brief Creates the lease statistical data result set + /// + /// The result set is populated by executing a prepared SQL query + /// against the database which sums the leases per lease state per + /// subnet id. + void start() { + // The query has no parameters, so we only need it's name. + result_set_.reset(new PgSqlResult(PQexecPrepared(conn_, statement_.name, + 0, NULL, NULL, NULL, 0))); + + conn_.checkStatementError(*result_set_, statement_); + } + + /// @brief Fetches the next row in the result set + /// + /// Once the internal result set has been populated by invoking the + /// the start() method, this method is used to iterate over the + /// result set rows. Once the last row has been fetched, subsequent + /// calls will return false. + /// + /// @param row Storage for the fetched row + /// + /// @return True if the fetch succeeded, false if there are no more + /// rows to fetch. + bool getNextRow(LeaseStatsRow& row) { + // If we're past the end, punt. + if (next_row_ >= result_set_->getRows()) { + return (false); + } + + // Fetch the subnet id. + uint32_t col = 0; + uint32_t subnet_id; + PgSqlExchange::getColumnValue(*result_set_, next_row_, col, subnet_id); + row.subnet_id_ = static_cast<SubnetID>(subnet_id); + ++col; + + // Fetch the lease type if we were told to do so. + if (fetch_type_) { + uint32_t lease_type; + PgSqlExchange::getColumnValue(*result_set_, next_row_ , col, + lease_type); + row.lease_type_ = static_cast<Lease::Type>(lease_type); + ++col; + } else { + row.lease_type_ = Lease::TYPE_NA; + } + + // Fetch the lease state. + PgSqlExchange::getColumnValue(*result_set_, next_row_ , col, + row.lease_state_); + ++col; + + // Fetch the state count. + PgSqlExchange::getColumnValue(*result_set_, next_row_, col, + row.state_count_); + + // Point to the next row. + ++next_row_; + return (true); + } + +protected: + /// @brief Database connection to use to execute the query + PgSqlConnection& conn_; + + /// @brief The query's prepared statement + PgSqlTaggedStatement& statement_; + + /// @brief The result set returned by Postgres. + boost::shared_ptr<PgSqlResult> result_set_; + + /// @brief Index of the next row to fetch + uint32_t next_row_; + + /// @brief Indicates if query supplies lease type + bool fetch_type_; +}; + PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) : LeaseMgr(), exchange4_(new PgSqlLease4Exchange()), exchange6_(new PgSqlLease6Exchange()), conn_(parameters) { @@ -1222,6 +1336,26 @@ PgSqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, return (deleteLeaseCommon(statement_index, bind_array)); } +LeaseStatsQueryPtr +PgSqlLeaseMgr::startLeaseStatsQuery4() { + LeaseStatsQueryPtr query( + new PgSqlLeaseStatsQuery(conn_, + tagged_statements[RECOUNT_LEASE4_STATS], + false)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +PgSqlLeaseMgr::startLeaseStatsQuery6() { + LeaseStatsQueryPtr query( + new PgSqlLeaseStatsQuery(conn_, + tagged_statements[RECOUNT_LEASE6_STATS], + true)); + query->start(); + return(query); +} + string PgSqlLeaseMgr::getName() const { string name = ""; diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index d316f1b9f0..69924067a3 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -317,6 +317,26 @@ public: /// @return Number of leases deleted. virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs); + /// @brief Creates and runs the IPv4 lease stats query + /// + /// It creates an instance of a PgSqlLeaseStatsQuery4 and then + /// invokes its start method, which fetches its statistical data + /// result set by executing the RECOUNT_LEASE_STATS4 query. + /// The query object is then returned. + /// + /// @return The populated query as a pointer to an LeaseStatsQuery + virtual LeaseStatsQueryPtr startLeaseStatsQuery4(); + + /// @brief Creates and runs the IPv6 lease stats query + /// + /// It creates an instance of a PgSqlLeaseStatsQuery and then + /// invokes its start method, which fetches its statistical data + /// result set by executing the RECOUNT_LEASE_STATS6 query. + /// The query object is then returned. + /// + /// @return The populated query as a pointer to an LeaseStatsQuery + virtual LeaseStatsQueryPtr startLeaseStatsQuery6(); + /// @brief Return backend type /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) @@ -385,6 +405,8 @@ public: INSERT_LEASE6, // Add entry to lease6 table UPDATE_LEASE4, // Update a Lease4 entry UPDATE_LEASE6, // Update a Lease6 entry + RECOUNT_LEASE4_STATS, // Fetch IPv4 lease statistical data + RECOUNT_LEASE6_STATS, // Fetch IPv4 lease statistical data NUM_STATEMENTS // Number of statements }; diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index 84e29bd6fc..7c7830966f 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -7,6 +7,7 @@ #include <config.h> #include <dhcpsrv/cfgmgr.h> #include <dhcpsrv/srv_config.h> +#include <dhcpsrv/lease_mgr_factory.h> #include <log/logger_manager.h> #include <log/logger_specification.h> #include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_* @@ -164,11 +165,17 @@ SrvConfig::removeStatistics() { void SrvConfig::updateStatistics() { - - // Updates statistics for v4 and v6 subnets - getCfgSubnets4()->updateStatistics(); - - getCfgSubnets6()->updateStatistics(); + // Updating subnet statistics involves updating lease statistics, which + // is done by the LeaseMgr. Since servers with subnets, must have a + // LeaseMgr, we do not bother updating subnet stats for servers without + // a lease manager, such as D2. @todo We should probably examine why + // "SrvConfig" is being used by D2. + if (LeaseMgrFactory::haveInstance()) { + // Updates statistics for v4 and v6 subnets + getCfgSubnets4()->updateStatistics(); + + getCfgSubnets6()->updateStatistics(); + } } } diff --git a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc index 8f86f6a5e9..764ad60ee1 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_utils.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_utils.cc @@ -123,6 +123,9 @@ AllocEngine4Test::generateDeclinedLease(const std::string& addr, AllocEngine6Test::AllocEngine6Test() { CfgMgr::instance().clear(); + // This lease mgr needs to exist to before configuration commits. + factory_.create("type=memfile universe=6 persist=false"); + duid_ = DuidPtr(new DUID(std::vector<uint8_t>(8, 0x42))); iaid_ = 42; @@ -141,7 +144,6 @@ AllocEngine6Test::AllocEngine6Test() { initFqdn("", false, false); - factory_.create("type=memfile universe=6 persist=false"); } void @@ -525,6 +527,10 @@ AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start, } AllocEngine4Test::AllocEngine4Test() { + + // This lease mgr needs to exist to before configuration commits. + factory_.create("type=memfile universe=4 persist=false"); + // Create fresh instance of the HostMgr, and drop any previous HostMgr state. HostMgr::instance().create(); @@ -548,7 +554,6 @@ AllocEngine4Test::AllocEngine4Test() { initSubnet(IOAddress("192.0.2.100"), IOAddress("192.0.2.109")); cfg_mgr.commit(); - factory_.create("type=memfile universe=4 persist=false"); // Create a default context. Note that remaining parameters must be // assigned when needed. diff --git a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc index 03523d34b0..80e39370b1 100644 --- a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc @@ -89,6 +89,7 @@ public: /// @brief Destructor. virtual ~CfgMySQLDbAccessTest() { destroyMySQLSchema(); + LeaseMgrFactory::destroy(); } }; diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc index c9edb88768..a6de68d7dd 100644 --- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc @@ -10,6 +10,7 @@ #include <dhcp/dhcp6.h> #include <dhcp/tests/iface_mgr_test_config.h> #include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/lease_mgr_factory.h> #include <dhcpsrv/subnet_id.h> #include <dhcpsrv/parsers/dhcp_parsers.h> #include <stats/stats_mgr.h> @@ -280,6 +281,23 @@ public: void clear() { CfgMgr::instance().setVerbose(false); CfgMgr::instance().clear(); + LeaseMgrFactory::destroy(); + } + + /// @brief Creates instance of the backend. + /// + /// @param family AF_INET for v4, AF_INET6 for v6 + void startBackend(int family = AF_INET) { + try { + std::ostringstream s; + s << "type=memfile persist=false " << (family == AF_INET6 ? + "universe=6" : "universe=4"); + LeaseMgrFactory::create(s.str()); + } catch (const std::exception& ex) { + std::cerr << "*** ERROR: unable to create instance of the Memfile\n" + " lease database backend: " << ex.what() << std::endl; + throw; + } } /// used in client classification (or just empty container for other tests) @@ -575,6 +593,7 @@ TEST_F(CfgMgrTest, verbosity) { TEST_F(CfgMgrTest, commitStats4) { CfgMgr& cfg_mgr = CfgMgr::instance(); StatsMgr& stats_mgr = StatsMgr::instance(); + startBackend(AF_INET); // Let's prepare the "old" configuration: a subnet with id 123 // and pretend there were addresses assigned, so statistics are non-zero. @@ -641,6 +660,7 @@ TEST_F(CfgMgrTest, clearStats4) { TEST_F(CfgMgrTest, commitStats6) { CfgMgr& cfg_mgr = CfgMgr::instance(); StatsMgr& stats_mgr = StatsMgr::instance(); + startBackend(AF_INET6); // Let's prepare the "old" configuration: a subnet with id 123 // and pretend there were addresses assigned, so statistics are non-zero. diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index a94a3a2b5c..25d1b2bd07 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -5,11 +5,18 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include <config.h> + +#include <asiolink/io_address.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/database_connection.h> #include <dhcpsrv/tests/generic_lease_mgr_unittest.h> #include <dhcpsrv/tests/test_utils.h> -#include <dhcpsrv/database_connection.h> -#include <asiolink/io_address.h> +#include <stats/stats_mgr.h> + +#include <boost/foreach.hpp> + #include <gtest/gtest.h> + #include <sstream> using namespace std; @@ -57,6 +64,7 @@ GenericLeaseMgrTest::GenericLeaseMgrTest() /// a template leasetype6_.push_back(LEASETYPE6[i]); } + } GenericLeaseMgrTest::~GenericLeaseMgrTest() { @@ -2381,6 +2389,300 @@ GenericLeaseMgrTest::testGetDeclinedLeases6() { } } +void +GenericLeaseMgrTest::checkStat(const std::string& name, + const int64_t expected_value) { + stats::ObservationPtr obs = + stats::StatsMgr::instance().getObservation(name); + + ASSERT_TRUE(obs) << " stat: " << name << " not found "; + ASSERT_EQ(expected_value, obs->getInteger().first) + << " stat: " << name << " value wrong"; +} + +void +GenericLeaseMgrTest::checkLeaseStats(const StatValMapList& expectedStats) { + // Global accumulators + int64_t declined_addresses = 0; + int64_t declined_reclaimed_addresses = 0; + + // Iterate over all stats for each subnet + for (int subnet_idx = 0; subnet_idx < expectedStats.size(); ++subnet_idx) { + BOOST_FOREACH(StatValPair expectedStat, expectedStats[subnet_idx]) { + // Verify the per subnet value. + checkStat(stats::StatsMgr::generateName("subnet", subnet_idx+1, + expectedStat.first), + expectedStat.second); + + // Add the value to globals as needed. + if (expectedStat.first == "declined-addresses") { + declined_addresses += expectedStat.second; + } else if (expectedStat.first == "declined-reclaimed-addresses") { + declined_reclaimed_addresses += expectedStat.second; + } + } + } + + // Verify the globals. + checkStat("declined-addresses", declined_addresses); + checkStat("declined-reclaimed-addresses", declined_reclaimed_addresses); +} + +void +GenericLeaseMgrTest::makeLease4(const std::string& address, + const SubnetID& subnet_id, + const uint32_t state) { + Lease4Ptr lease(new Lease4()); + + // set the address + lease->addr_ = IOAddress(address); + + // make a MAC from the address + std::vector<uint8_t> hwaddr = lease->addr_.toBytes(); + hwaddr.push_back(0); + hwaddr.push_back(0); + + lease->hwaddr_.reset(new HWAddr(hwaddr, HTYPE_ETHER)); + lease->valid_lft_ = 86400; + lease->cltt_ = 168256; + lease->subnet_id_ = subnet_id; + lease->state_ = state; + ASSERT_TRUE(lmptr_->addLease(lease)); +} + +void +GenericLeaseMgrTest::makeLease6(const Lease::Type& type, + const std::string& address, + uint8_t prefix_len, + const SubnetID& subnet_id, + const uint32_t state) { + IOAddress addr(address); + + // make a DUID from the address + std::vector<uint8_t> bytes = addr.toBytes(); + bytes.push_back(prefix_len); + + Lease6Ptr lease(new Lease6(type, addr, DuidPtr(new DUID(bytes)), 77, + 16000, 24000, 0, 0, subnet_id, HWAddrPtr(), + prefix_len)); + lease->state_ = state; + ASSERT_TRUE(lmptr_->addLease(lease)); +} + +void +GenericLeaseMgrTest::testRecountLeaseStats4() { + using namespace stats; + + StatsMgr::instance().removeAll(); + + // Create two subnets. + int num_subnets = 2; + CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4(); + Subnet4Ptr subnet; + Pool4Ptr pool; + + subnet.reset(new Subnet4(IOAddress("192.0.1.0"), 24, 1, 2, 3, 1)); + pool.reset(new Pool4(IOAddress("192.0.1.0"), 24)); + subnet->addPool(pool); + cfg->add(subnet); + + subnet.reset(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 2)); + pool.reset(new Pool4(IOAddress("192.0.2.0"), 24)); + subnet->addPool(pool); + cfg->add(subnet); + + + ASSERT_NO_THROW(CfgMgr::instance().commit()); + + // Create the expected stats list. At this point, the only stat + // that should be non-zero is total-addresses. + StatValMapList expectedStats(num_subnets); + for (int i = 0; i < num_subnets; ++i) { + expectedStats[i]["total-addresses"] = 256; + expectedStats[i]["assigned-addresses"] = 0; + expectedStats[i]["declined-addresses"] = 0; + expectedStats[i]["declined-reclaimed-addresses"] = 0; + } + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); + + // Recount stats. We should have the same results. + ASSERT_NO_THROW(lmptr_->recountLeaseStats4()); + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); + + // Now let's insert some leases into subnet 1. + int subnet_id = 1; + + // Insert one lease in default state, i.e. assigned. + makeLease4("192.0.1.1", subnet_id); + + // Insert one lease in declined state. + makeLease4("192.0.1.2", subnet_id, Lease::STATE_DECLINED); + + // Insert one lease in the expired state. + makeLease4("192.0.1.3", subnet_id, Lease::STATE_EXPIRED_RECLAIMED); + + // Insert another lease in default state, i.e. assigned. + makeLease4("192.0.1.4", subnet_id); + + // Update the expected stats list for subnet 1. + expectedStats[subnet_id - 1]["assigned-addresses"] = 2; + expectedStats[subnet_id - 1]["declined-addresses"] = 1; + + // Now let's add leases to subnet 2. + subnet_id = 2; + + // Insert one delined lease. + makeLease4("192.0.2.2", subnet_id, Lease::STATE_DECLINED); + + // Update the expected stats. + expectedStats[subnet_id - 1]["declined-addresses"] = 1; + + // Now Recount the stats. + ASSERT_NO_THROW(lmptr_->recountLeaseStats4()); + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); + + // Delete some leases from subnet, and update the expected stats. + EXPECT_TRUE(lmptr_->deleteLease(IOAddress("192.0.1.1"))); + expectedStats[0]["assigned-addresses"] = 1; + + EXPECT_TRUE(lmptr_->deleteLease(IOAddress("192.0.1.2"))); + expectedStats[0]["declined-addresses"] = 0; + + // Recount the stats. + ASSERT_NO_THROW(lmptr_->recountLeaseStats4()); + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); +} + + +void +GenericLeaseMgrTest::testRecountLeaseStats6() { + using namespace stats; + + StatsMgr::instance().removeAll(); + + // Create two subnets. + int num_subnets = 2; + CfgSubnets6Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets6(); + Subnet6Ptr subnet; + Pool6Ptr pool; + StatValMapList expectedStats(num_subnets); + + int subnet_id = 1; + subnet.reset(new Subnet6(IOAddress("3001:1::"), 64, 1, 2, 3, 4, subnet_id)); + pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("3001:1::"), + IOAddress("3001:1::FF"))); + subnet->addPool(pool); + expectedStats[subnet_id - 1]["total-nas"] = 256; + + pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("3001:1:2::"),96,112)); + subnet->addPool(pool); + expectedStats[subnet_id - 1]["total-pds"] = 65536; + cfg->add(subnet); + + ++subnet_id; + subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4, + subnet_id)); + pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 120)); + subnet->addPool(pool); + expectedStats[subnet_id - 1]["total-nas"] = 256; + expectedStats[subnet_id - 1]["total-pds"] = 0; + cfg->add(subnet); + + ASSERT_NO_THROW(CfgMgr::instance().commit()); + + + // Create the expected stats list. At this point, the only stat + // that should be non-zero is total-nas/total-pds. + for (int i = 0; i < num_subnets; ++i) { + expectedStats[i]["assigned-nas"] = 0; + expectedStats[i]["declined-addresses"] = 0; + expectedStats[i]["declined-reclaimed-addresses"] = 0; + expectedStats[i]["assigned-pds"] = 0; + } + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); + + + // Recount stats. We should have the same results. + ASSERT_NO_THROW(lmptr_->recountLeaseStats4()); + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); + + // Now let's insert some leases into subnet 1. + subnet_id = 1; + + // Insert three assigned NAs. + makeLease6(Lease::TYPE_NA, "3001:1::1", 0, subnet_id); + makeLease6(Lease::TYPE_NA, "3001:1::2", 0, subnet_id); + makeLease6(Lease::TYPE_NA, "3001:1::3", 0, subnet_id); + expectedStats[subnet_id - 1]["assigned-nas"] = 3; + + // Insert two declined NAs. + makeLease6(Lease::TYPE_NA, "3001:1::4", 0, subnet_id, + Lease::STATE_DECLINED); + makeLease6(Lease::TYPE_NA, "3001:1::5", 0, subnet_id, + Lease::STATE_DECLINED); + expectedStats[subnet_id - 1]["declined-addresses"] = 2; + + // Insert one expired NA. + makeLease6(Lease::TYPE_NA, "3001:1::6", 0, subnet_id, + Lease::STATE_EXPIRED_RECLAIMED); + + // Insert two assigned PDs. + makeLease6(Lease::TYPE_PD, "3001:1:2:0100::", 112, subnet_id); + makeLease6(Lease::TYPE_PD, "3001:1:2:0200::", 112, subnet_id); + expectedStats[subnet_id - 1]["assigned-pds"] = 2; + + // Insert two expired PDs. + makeLease6(Lease::TYPE_PD, "3001:1:2:0300::", 112, subnet_id, + Lease::STATE_EXPIRED_RECLAIMED); + makeLease6(Lease::TYPE_PD, "3001:1:2:0400::", 112, subnet_id, + Lease::STATE_EXPIRED_RECLAIMED); + + // Now let's add leases to subnet 2. + subnet_id = 2; + + // Insert two assigned NAs. + makeLease6(Lease::TYPE_NA, "2001:db81::1", 0, subnet_id); + makeLease6(Lease::TYPE_NA, "2001:db81::2", 0, subnet_id); + expectedStats[subnet_id - 1]["assigned-nas"] = 2; + + // Insert one declined NA. + makeLease6(Lease::TYPE_NA, "2001:db81::3", 0, subnet_id, + Lease::STATE_DECLINED); + expectedStats[subnet_id - 1]["declined-addresses"] = 1; + + // Now Recount the stats. + ASSERT_NO_THROW(lmptr_->recountLeaseStats6()); + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); + + // Delete some leases and update the expected stats. + EXPECT_TRUE(lmptr_->deleteLease(IOAddress("3001:1::2"))); + expectedStats[0]["assigned-nas"] = 2; + + EXPECT_TRUE(lmptr_->deleteLease(IOAddress("2001:db81::3"))); + expectedStats[1]["declined-addresses"] = 0; + + // Recount the stats. + ASSERT_NO_THROW(lmptr_->recountLeaseStats6()); + + // Make sure stats are as expected. + ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats)); +} + + }; // namespace test }; // namespace dhcp }; // namespace isc diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index 3ef1405503..f426787833 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2016 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 @@ -15,6 +15,12 @@ namespace isc { namespace dhcp { namespace test { + +/// @brief typedefs to simplify lease statistic testing +typedef std::map<std::string, int64_t> StatValMap; +typedef std::pair<std::string, int64_t> StatValPair; +typedef std::vector<StatValMap> StatValMapList; + /// @brief Test Fixture class with utility functions for LeaseMgr backends /// /// It contains utility functions, like dummy lease creation. @@ -94,6 +100,47 @@ public: /// @return vector<Lease6Ptr> Vector of pointers to leases std::vector<Lease6Ptr> createLeases6(); + /// @brief Compares a StatsMgr statistic to an expected value + /// + /// Attempt to fetch the named statistic from the StatsMg and if + /// found, compare its observed value to the given value. + /// Fails if the stat is not found or if the values do not match. + /// + /// @param name StatsMgr name for the statistic to check + /// @param expected_value expected value of the statistic + void checkStat(const std::string& name, const int64_t expected_value); + + /// @brief Compares StatsMgr statistics against an expected list of values + /// + /// Iterates over a list of statistic names and expected values, attempting + /// to fetch each from the StatsMgr and if found, compare its observed value + /// to the expected value. Fails if any of the expected stats are not + /// found or if the values do not match. + /// + /// @param expected_stats Map of expected static names and values. + void checkLeaseStats(const StatValMapList& expected_stats); + + /// @brief Constructs a minimal IPv4 lease and adds it to the lease storage + /// + /// @param address - IPv4 address for the lease + /// @param subnet_id - subnet ID to which the lease belongs + /// @param state - the state of the lease + void makeLease4(const std::string& address, const SubnetID& subnet_id, + const uint32_t state = Lease::STATE_DEFAULT); + + /// @brief Constructs a minimal IPv6 lease and adds it to the lease storage + /// + /// The DUID is constructed from the address and prefix length. + /// + /// @param type - type of lease to create (TYPE_NA, TYPE_PD...) + /// @param address - IPv6 address/prefix for the lease + /// @param prefix_len = length of the prefix (should be 0 for TYPE_NA) + /// @param subnet_id - subnet ID to which the lease belongs + /// @param state - the state of the lease + void makeLease6(const Lease::Type& type, const std::string& address, + uint8_t prefix_len, const SubnetID& subnet_id, + const uint32_t state = Lease::STATE_DEFAULT); + /// @brief checks that addLease, getLease4(addr) and deleteLease() works void testBasicLease4(); @@ -313,6 +360,20 @@ public: /// leases can be removed. void testDeleteExpiredReclaimedLeases4(); + /// @brief Check that the IPv4 lease statistics can be recounted + /// + /// This test creates two subnets and several leases associated with + /// them, then verifies that lease statistics are recalculated correctly + /// after altering the lease states in various ways. + void testRecountLeaseStats4(); + + /// @brief Check that the IPv6 lease statistics can be recounted + /// + /// This test creates two subnets and several leases associated with + /// them, then verifies that lease statistics are recalculated correctly + /// after altering the lease states in various ways. + void testRecountLeaseStats6(); + /// @brief String forms of IPv4 addresses std::vector<std::string> straddress4_; diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index a01a54457c..f52f2c1bd3 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -1883,6 +1883,16 @@ TEST_F(MemfileLeaseMgrTest, lease6ContainerIndexUpdate) { } } +// Verifies that IPv4 lease statistics can be recalculated. +TEST_F(MemfileLeaseMgrTest, recountLeaseStats4) { + startBackend(V4); + testRecountLeaseStats4(); +} +// Verifies that IPv6 lease statistics can be recalculated. +TEST_F(MemfileLeaseMgrTest, recountLeaseStats6) { + startBackend(V6); + testRecountLeaseStats6(); +} }; // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index 33549b415a..b5433ab332 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -477,4 +477,14 @@ TEST_F(MySqlLeaseMgrTest, deleteExpiredReclaimedLeases4) { testDeleteExpiredReclaimedLeases4(); } +// Verifies that IPv4 lease statistics can be recalculated. +TEST_F(MySqlLeaseMgrTest, recountLeaseStats4) { + testRecountLeaseStats4(); +} + +// Verifies that IPv6 lease statistics can be recalculated. +TEST_F(MySqlLeaseMgrTest, recountLeaseStats6) { + testRecountLeaseStats6(); +} + }; // Of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index 9234881863..0a629d5579 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -402,4 +402,14 @@ TEST_F(PgSqlLeaseMgrTest, getExpiredLeases6) { testGetExpiredLeases6(); } +// Verifies that IPv4 lease statistics can be recalculated. +TEST_F(PgSqlLeaseMgrTest, recountLeaseStats4) { + testRecountLeaseStats4(); +} + +// Verifies that IPv6 lease statistics can be recalculated. +TEST_F(PgSqlLeaseMgrTest, recountLeaseStats6) { + testRecountLeaseStats6(); +} + }; // namespace |