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
"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}
};
//@}
};
+/// @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) {
/// @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.)
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
};
#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_*
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();
+ }
}
}
#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>
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)
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.
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.