From: Thomas Markwalder Date: Mon, 15 Aug 2016 14:25:25 +0000 (-0400) Subject: [4294] PostgreSQL now supports IPv4 lease stats recount X-Git-Tag: trac4631_base~6^2~16 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=64c23c76ca53a29be709f4eade7d139782a0603e;p=thirdparty%2Fkea.git [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 --- diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 43b1883053..f33b40e958 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -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(); } } diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 87925d1f52..5169ef073a 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -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} }; diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index f9a5ad28eb..174237df70 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,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 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(subnet_id); + + // Fetch the lease state. + uint32_t state; + PgSqlExchange::getColumnValue(*result_set_, next_row_ , 1, state); + row.lease_state_ = static_cast(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) { diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index d316f1b9f0..9511c0e237 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -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 }; 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 #include #include +#include #include #include #include // 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/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 #include #include +#include #include #include #include @@ -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/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index 9234881863..762d37c253 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -402,4 +402,10 @@ TEST_F(PgSqlLeaseMgrTest, getExpiredLeases6) { testGetExpiredLeases6(); } +// Verifies that IPv4 lease statistics can be recalculated. +TEST_F(PgSqlLeaseMgrTest, recountAddressStats4) { + testRecountAddressStats4(); +} + + }; // namespace