Commit a8f85f25 authored by Thomas Markwalder's avatar Thomas Markwalder

[4294] Memfile and MySql now support recalulating IPv4 lease statistics

src/lib/dhcpsrv/cfg_subnets4.cc
    CfgSubnets4::removeStatistics()
    - added removal of all lease statistics per subnet, and global declined address
    stats

    CfgSubnets4::updateStatistics()
    - added call to LeaseMgr::recountAddressStats4

src/lib/dhcpsrv/lease.cc
src/lib/dhcpsrv/lease.h
    Replaces lease state constants with LeaseState enumeration.

src/lib/dhcpsrv/lease_mgr.cc
src/lib/dhcpsrv/lease_mgr.h
    struct AddressStatsRow4 - contains the content of one row of the IPv4
    lease statistical data result set

    class AddressStatsQuery4 - base class for constructing the IPv4
    lease statistical data result set for an IPv4 lease storage

    LeaseMgr::recountAddressStats4() -  new method which recalculates
    per-subnet and global stats for IPv4 leases

    LeaseMgr::startAddressStatsQuery4() - new virtual method that fetches
    the IPv4 lease statistical data result set

src/lib/dhcpsrv/lease_mgr_factory.h
src/lib/dhcpsrv/lease_mgr_factory.cc
    LeaseMgrFactory::haveInstance() - new static method which indicates
    whether or not the lease manager singleton exists

src/lib/dhcpsrv/memfile_lease_mgr.h
src/lib/dhcpsrv/memfile_lease_mgr.cc
    MemfileAddressStatsQuery4 - Derivation of AddressStatsQuery4, it constructs
    the IPv4 lease statistical data by iterating over IPv4 lease storage

    Memfile_LeaseMgr::startAddressStatsQuery4() - new virtual method which
    creates, starts, and returns a MemfileAddressStatsQuery4

src/lib/dhcpsrv/memfile_lease_storage.h
    Added an a per subnet_ID index to IPv4 storage

src/lib/dhcpsrv/mysql_lease_mgr.h
src/lib/dhcpsrv/mysql_lease_mgr.cc
    - Added RECOUNT_LEASE4_STATS query

    MySqlAddressStatsQuery4 Derivation of AddressStatsQuery4, it constructs
    the IPv4 lease statistical data by executing RECOUNT_LEASE4_STATS

    MySqlLeaseMgr::startAddressStatsQuery4() - new virtual method which
    creates, starts, and returns a MySqlAddressStatsQuery4

src/lib/dhcpsrv/tests/alloc_engine_utils.cc
    AllocEngine6Test::AllocEngine6Test()
    AllocEngine4Test::AllocEngine4Test()
    - moved lease mgr create up above configuration commit

src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc
     ~CfgMySQLDbAccessTest() - added destruction of lease manager singleton,
    otherwise subsequent tests can fail

src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h
src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
    GenericLeaseMgrTest::checkStat() - new method for comparing a stat
    GenericLeaseMgrTest::checkAddressStats4() - new method for comparing a list
    of stats
    GenericLeaseMgrTest::makeLease4() - new method for making a minimal lease
    GenericLeaseMgrTest::testRecountAddressStats4() - new method which tests
    a lease manager's ability to recalculate the IPv4 lease statistics

src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
    TEST_F(MemfileLeaseMgrTest, recountAddressStats4) - new test which tests
    Memfile_LeaseMgr's ability to recalculate IPv4 lease statistics

src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
    TEST_F(MySqlLeaseMgrTest, recountAddressStats4) - new test which tests
    MySqlLeaseMgr's ability to recalculate IPv4 lease statistics
parent 901a83c6
......@@ -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)));
// If we have subnets and a lease mgr, recount the least statistics
if (subnets_.begin() != subnets_.end() && LeaseMgrFactory::haveInstance()) {
LeaseMgrFactory::instance().recountAddressStats4();
}
}
......
......@@ -923,3 +923,6 @@ lease from the Cassandra database for the specified address.
% DHCPSRV_CQL_UPDATE_ADDR6 updating IPv6 lease for address %1
A debug message issued when the server is attempting to update IPv6
lease from the Cassandra database for the specified address.
% TOMS_UTILITY_MESSAGE %1
Handy log message that should be deleted
......@@ -15,10 +15,6 @@ using namespace std;
namespace isc {
namespace dhcp {
const uint32_t Lease::STATE_DEFAULT = 0x0;
const uint32_t Lease::STATE_DECLINED = 0x1;
const uint32_t Lease::STATE_EXPIRED_RECLAIMED = 0x2;
Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
const bool fqdn_fwd, const bool fqdn_rev,
......
......@@ -40,18 +40,18 @@ struct Lease {
/// @return text decription
static std::string typeToText(Type type);
/// @name Common lease states constants.
/// @name Enumeration of lease states
//@{
///
/// @brief A lease in the default state.
static const uint32_t STATE_DEFAULT;
/// @brief Declined lease.
static const uint32_t STATE_DECLINED;
/// @brief Expired and reclaimed lease.
static const uint32_t STATE_EXPIRED_RECLAIMED;
typedef enum {
/// @brief A lease in the default (assigned) state.
STATE_DEFAULT,
/// @brief Declined lease.
STATE_DECLINED,
/// @brief Expired and reclaimed lease.
STATE_EXPIRED_RECLAIMED,
/// @brief The number of defined lease states.
NUM_LEASE_STATES
} LeaseState;
//@}
/// @brief Returns name(s) of the basic lease state(s).
......
......@@ -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,82 @@ LeaseMgr::getLease6(Lease::Type type, const DUID& duid,
return (*col.begin());
}
void
LeaseMgr::recountAddressStats4() {
using namespace stats;
StatsMgr& stats_mgr = StatsMgr::instance();
AddressStatsQuery4Ptr query = startAddressStatsQuery4();
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 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.
AddressStatsRow4 row;
while (query->getNextRow(row)) {
switch(row.lease_state_) {
case Lease::STATE_DEFAULT:
// Set subnet level value.
stats_mgr.setValue(StatsMgr::generateName("subnet",
row.subnet_id_,
"assigned-addresses"),
row.state_count_);
break;
case 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;
default:
// Not one we're tracking.
break;
}
}
}
AddressStatsQuery4Ptr
LeaseMgr::startAddressStatsQuery4() {
return(AddressStatsQuery4Ptr());
}
std::string
LeaseMgr::getDBVersion() {
isc_throw(NotImplemented, "LeaseMgr::getDBVersion() called");
......
......@@ -146,6 +146,70 @@ public:
virtual ~SqlExchange() {};
ExchangeColumnInfoContainer parameters_; ///< Column names and types
};
/// @brief Contains a single row of IPv4 lease statistical data
///
/// The contents of the row consist of a subnet ID, a lease state,
/// and the number of leases in that state for that subnet ID.
struct AddressStatsRow4 {
/// @brief Default constructor
AddressStatsRow4() :
subnet_id_(0), lease_state_(Lease::STATE_DEFAULT), state_count_(0) {
}
/// @brief Constructor
///
/// @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
AddressStatsRow4(const SubnetID& subnet_id,
const Lease::LeaseState& lease_state,
const int64_t state_count)
: subnet_id_(subnet_id), lease_state_(lease_state),
state_count_(state_count) {
}
/// @brief The subnet ID to which this data applies
SubnetID subnet_id_;
/// @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 IPv4 statistical lease data query
///
/// LeaseMgr derivations implement this class such that it provides
/// upto date IPv4 statistical lease data organized as rows of
/// AddressStatsRow4 instances. The rows must be accessible in
/// ascending order by subnet id.
class AddressStatsQuery4 {
public:
/// @brief Default constructor
AddressStatsQuery4() {};
/// @brief virtual destructor
virtual ~AddressStatsQuery4() {};
/// @brief Executes the query
///
/// This method should conduct whatever steps are required to
/// calculate the IPv4 lease statistical data by examining the
/// IPv4 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(AddressStatsRow4& row) { return(false); };
};
/// @brief Defines a pointer to an AddressStatsQuery4.
typedef boost::shared_ptr<AddressStatsQuery4> AddressStatsQuery4Ptr;
/// @brief Abstract Lease Manager
///
/// This is an abstract API for lease database backends. It provides unified
......@@ -397,6 +461,36 @@ 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, startAddressStatsQuery4(), which
/// returns an instance of an AddressStats4Qry. The query
/// query contains a "result set" where each row is an AddressStatRow4
/// that contains a subnet id, a lease state, the number of leases 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 recountAddressStats4();
/// @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 AddressStatsQuery whose result set has been
/// populated with upto date IPv4 lease statistical data. Each row of the
/// result set is an AddressStatRow4 which ordered ascending by subnet ID.
///
/// @return A populated AddressStatsQuery4
virtual AddressStatsQuery4Ptr startAddressStatsQuery4();
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
......
......@@ -101,6 +101,11 @@ LeaseMgrFactory::destroy() {
getLeaseMgrPtr().reset();
}
bool
LeaseMgrFactory::haveInstance() {
return (getLeaseMgrPtr().get());
}
LeaseMgr&
LeaseMgrFactory::instance() {
LeaseMgr* lmptr = getLeaseMgrPtr().get();
......
......@@ -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
......
......@@ -256,6 +256,143 @@ LFCSetup::getExitStatus() const {
return (process_->getExitStatus(pid_));
}
/// @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 MemfileAddressStatsQuery4 : public AddressStatsQuery4 {
public:
/// @brief Constructor
///
/// @param storage4 A pointer to the v4 lease storage to be counted
MemfileAddressStatsQuery4(Lease4Storage& storage4);
/// @brief Destructor
virtual ~MemfileAddressStatsQuery4() {};
/// @brief Creates the IPv4 lease statistical data result set
///
/// The result is populated by iterating over the IPv4 leases in storage,
/// in ascending order by subnet ID, accumulating the lease state counts.
/// At the completion of all entries for a given subnet, the counts are
/// used to create AddressStatsRow4 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
virtual void start();
/// @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(AddressStatsRow4& row);
/// @brief Returns the number of rows in the result set
/// @todo, should this be a virtual member of the base class?
int getRowCount();
private:
/// @brief The Memfile storage containing the IPv4 leases to analyze
Lease4Storage& storage4_;
/// @brief A vector containing the "result set"
std::vector<AddressStatsRow4> rows_;
/// @brief An iterator for accessing the next row within the result set
std::vector<AddressStatsRow4>::iterator next_pos_;
};
MemfileAddressStatsQuery4::MemfileAddressStatsQuery4(Lease4Storage& storage4)
: storage4_(storage4), rows_(0), next_pos_(rows_.end()) {};
void
MemfileAddressStatsQuery4::start() {
// Get the subnet_id index
const Lease4StorageSubnetIdIndex& idx = storage4_.get<SubnetIdIndexTag>();
// 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(Lease4StorageSubnetIdIndex::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(AddressStatsRow4(cur_id,Lease::STATE_DEFAULT,
assigned));
assigned = 0;
rows_.push_back(AddressStatsRow4(cur_id, Lease::STATE_DECLINED,
declined));
declined = 0;
}
// Update current subnet id
cur_id = (*lease)->subnet_id_;
}
// Bump the appropriate accumulator
switch ((*lease)->state_) {
case Lease::STATE_DEFAULT:
++assigned;
break;
case Lease::STATE_DECLINED:
++declined;
break;
default:
// Not one we're tracking.
break;
}
}
// Make the rows for last subnet, unless there were no rows
if (idx.begin() != idx.end()) {
rows_.push_back(AddressStatsRow4(cur_id, Lease::STATE_DEFAULT,
assigned));
rows_.push_back(AddressStatsRow4(cur_id, Lease::STATE_DECLINED,
declined));
}
// Set the next row position to the beginning of the rows.
next_pos_ = rows_.begin();
}
bool
MemfileAddressStatsQuery4::getNextRow(AddressStatsRow4& row) {
if (next_pos_ == rows_.end()) {
return (false);
}
row = *next_pos_;
++next_pos_;
return (true);
}
int
MemfileAddressStatsQuery4::getRowCount() {
return (rows_.size());
}
// 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 +436,7 @@ Memfile_LeaseMgr::Memfile_LeaseMgr(const DatabaseConnection::ParameterMap& param
}
lfcSetup(conversion_needed);
}
}
Memfile_LeaseMgr::~Memfile_LeaseMgr() {
......@@ -1048,5 +1186,12 @@ void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file)
}
}
AddressStatsQuery4Ptr
Memfile_LeaseMgr::startAddressStatsQuery4() {
AddressStatsQuery4Ptr query(new MemfileAddressStatsQuery4(storage4_));
query->start();
return(query);
}
} // end of namespace isc::dhcp
} // end of namespace isc
......@@ -92,6 +92,7 @@ public:
/// @}
/// @brief Specifies universe (V4, V6)
///
/// This enumeration is used by various functions in Memfile %Lease Manager,
......@@ -594,6 +595,14 @@ public:
int getLFCExitStatus() const;
//@}
/// @brief Creates and runs the IPv4 lease stats query
///
/// It creates an instance of a MemfileAddressStatsQuery4 and then
/// invokes it's 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 AddressStatsQuery4
virtual AddressStatsQuery4Ptr startAddressStatsQuery4();
/// @name Protected methods used for %Lease File Cleanup.
/// The following methods are protected so as they can be accessed and
......
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 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
......@@ -27,6 +27,9 @@ namespace dhcp {
/// @brief Tag for indexes by address.
struct AddressIndexTag { };
/// @brief Tag for indexes by subnet id.
struct SubnetIdIndexTag { };
/// @brief Tag for indexes by DUID, IAID, lease type tuple.
struct DuidIaidTypeIndexTag { };
......@@ -135,6 +138,15 @@ typedef boost::multi_index_container<
boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
>,
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<SubnetIdIndexTag>,
// The subnet id is held in the subnet_id_ member of Lease4
// class. Note that the subnet_id_ is defined in the base
// class (Lease) so we have to point to this class rather
// than derived class: Lease4.
boost::multi_index::member<Lease, SubnetID, &Lease::subnet_id_>
>,
// Specification of the second index starts here.
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<HWAddressSubnetIdIndexTag>,
......@@ -232,6 +244,9 @@ typedef Lease6Storage::index<ExpirationIndexTag>::type Lease6StorageExpirationIn
/// @brief DHCPv4 lease storage index by address.
typedef Lease4Storage::index<AddressIndexTag>::type Lease4StorageAddressIndex;
/// @brief DHCPv4 lease storage index by subnet id.
typedef Lease4Storage::index<SubnetIdIndexTag>::type Lease4StorageSubnetIdIndex;
/// @brief DHCPv4 lease storage index by exiration time.
typedef Lease4Storage::index<ExpirationIndexTag>::type Lease4StorageExpirationIndex;
......
......@@ -205,6 +205,9 @@ TaggedStatement tagged_statements[] = {
"hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
"state = ? "
"WHERE address = ?"},
{MySqlLeaseMgr::RECOUNT_LEASE4_STATS,
"SELECT subnet_id, state, count(state) as state_count "
"FROM lease4 group by subnet_id, state"},
// End of list sentinel
{MySqlLeaseMgr::NUM_STATEMENTS, NULL}
};
......@@ -1214,6 +1217,143 @@ private:
uint32_t state_; ///< Lease state.
};
/// @brief MySql derivation of the IPv4 statistical lease data query
///
/// This class is used to recalculate IPv4 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 subnet, ordered
/// by subnet id in ascending order.
///
class MySqlAddressStatsQuery4 : public AddressStatsQuery4 {
public:
/// @brief Constructor
///
/// @param conn A open connection to the database housing the lease data
MySqlAddressStatsQuery4(MySqlConnection& conn);
/// @brief Destructor
virtual ~MySqlAddressStatsQuery4();
/// @brief Creates the IPv4 lease statistical data result set
///
/// The result set is populated by executing an SQL query against the
/// lease4 table which sums the leases per lease state per subnet id.
/// The query used is the prepared statement identified by
/// MySqlLeaseMgr::RECOUNT_LEASE4_STATS. This method creates the binds
/// the statement to the output bind array and then executes the
/// statement.
void start();
/// @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(AddressStatsRow4& row);
private:
/// @brief Analyzes the given statement outcome status
///
/// Wrapper method around the MySqlConnection:checkError() that is
/// used to generate the appropriate exception if the status indicates
/// an error.
////
/// a DbOperation error
/// @param status The MySQL statement execution outcome status
/// @param what invocation context message which will be included in
/// any exception
void checkError(int status, const char* what) const;