Commit 64c23c76 authored by Thomas Markwalder's avatar Thomas Markwalder

[4294] PostgreSQL now supports IPv4 lease stats recount

src/lib/dhcpsrv/cfg_subnets4.cc
    CfgSubnets4::updateStatistics() - removed lease mgr instance check

src/lib/dhcpsrv/mysql_lease_mgr.cc
    Added "ORDER by subnet_id" to RECOUNT_LEASE4_STATS

src/lib/dhcpsrv/pgsql_lease_mgr.cc
    Added tagged statement RECOUNT_LEASE4_STATS
    PgSqlAddressStatsQuery4 - new class, PostgreSQL derivation of AddressStatsQuery4
    PgSqlLeaseMgr::startAddressStatsQuery4() - PostgreSQL impl of virtual method

src/lib/dhcpsrv/srv_config.cc -
    SrvConfig::updateStatistics() - Added LeaseMgr singleton check around
    calls subnet statistics updates

src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
    CfgMgrTest:
       ~CfgMgrTest() - now destroys LeaseMgr singleton
       startBackend(int family = AF_INET) -  new method to create memfile lease mgr

        TEST_F(CfgMgrTest, commitStats4)
        TEST_F(CfgMgrTest, commitStats6)
    - added call to startBackend()

src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc
    TEST_F(PgSqlLeaseMgrTest, recountAddressStats4) - new test
parent a8f85f25
......@@ -267,8 +267,8 @@ CfgSubnets4::updateStatistics() {
TYPE_V4)));
}
// If we have subnets and a lease mgr, recount the least statistics
if (subnets_.begin() != subnets_.end() && LeaseMgrFactory::haveInstance()) {
// Only recount the stats if we have subnets.
if (subnets_.begin() != subnets_.end()) {
LeaseMgrFactory::instance().recountAddressStats4();
}
}
......
......@@ -207,7 +207,7 @@ TaggedStatement tagged_statements[] = {
"WHERE address = ?"},
{MySqlLeaseMgr::RECOUNT_LEASE4_STATS,
"SELECT subnet_id, state, count(state) as state_count "
"FROM lease4 group by subnet_id, state"},
"FROM lease4 GROUP BY subnet_id, state ORDER BY subnet_id"},
// End of list sentinel
{MySqlLeaseMgr::NUM_STATEMENTS, NULL}
};
......
......@@ -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,12 @@ 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"},
// End of list sentinel
{ 0, { 0 }, NULL, NULL}
};
......@@ -681,6 +687,122 @@ private:
//@}
};
/// @brief PgSql 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 PgSqlAddressStatsQuery4 : public AddressStatsQuery4 {
public:
/// @brief Constructor
///
/// @param conn A open connection to the database housing the lease data
PgSqlAddressStatsQuery4(PgSqlConnection& conn);
/// @brief Destructor
virtual ~PgSqlAddressStatsQuery4() {};
/// @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
/// PgSqlLeaseMgr::RECOUNT_LEASE4_STATS. This method executes the
/// statement which creates the result set.
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 PgSqlConnection: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;
/// @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_;
};
PgSqlAddressStatsQuery4::PgSqlAddressStatsQuery4(PgSqlConnection& conn)
: conn_(conn), statement_(tagged_statements[PgSqlLeaseMgr
::RECOUNT_LEASE4_STATS]),
result_set_(), next_row_(0) {
}
void
PgSqlAddressStatsQuery4::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_);
}
bool
PgSqlAddressStatsQuery4::getNextRow(AddressStatsRow4& 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_, 0, subnet_id);
row.subnet_id_ = static_cast<SubnetID>(subnet_id);
// Fetch the lease state.
uint32_t state;
PgSqlExchange::getColumnValue(*result_set_, next_row_ , 1, state);
row.lease_state_ = static_cast<Lease::LeaseState>(state);
// Fetch the state count.
PgSqlExchange::getColumnValue(*result_set_, next_row_, 2, row.state_count_);
// Point to the next row.
++next_row_;
return (true);
}
AddressStatsQuery4Ptr
PgSqlLeaseMgr::startAddressStatsQuery4() {
AddressStatsQuery4Ptr query(new PgSqlAddressStatsQuery4(conn_));
query->start();
return(query);
}
PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
: LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
exchange6_(new PgSqlLease6Exchange()), conn_(parameters) {
......
......@@ -317,6 +317,16 @@ 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 PgSqlAddressStatsQuery4 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 AddressStatsQuery4
virtual AddressStatsQuery4Ptr startAddressStatsQuery4();
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
......@@ -385,6 +395,7 @@ 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
NUM_STATEMENTS // Number of statements
};
......
......@@ -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();
}
}
}
......
......@@ -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.
......
......@@ -402,4 +402,10 @@ TEST_F(PgSqlLeaseMgrTest, getExpiredLeases6) {
testGetExpiredLeases6();
}
// Verifies that IPv4 lease statistics can be recalculated.
TEST_F(PgSqlLeaseMgrTest, recountAddressStats4) {
testRecountAddressStats4();
}
}; // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment