Browse Source

[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
Thomas Markwalder 8 years ago
parent
commit
64c23c76ca

+ 2 - 2
src/lib/dhcpsrv/cfg_subnets4.cc

@@ -267,8 +267,8 @@ CfgSubnets4::updateStatistics() {
                                                                      TYPE_V4)));
                                                                      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();
             LeaseMgrFactory::instance().recountAddressStats4();
     }
     }
 }
 }

+ 1 - 1
src/lib/dhcpsrv/mysql_lease_mgr.cc

@@ -207,7 +207,7 @@ TaggedStatement tagged_statements[] = {
                             "WHERE address = ?"},
                             "WHERE address = ?"},
     {MySqlLeaseMgr::RECOUNT_LEASE4_STATS,
     {MySqlLeaseMgr::RECOUNT_LEASE4_STATS,
                     "SELECT subnet_id, state, count(state) as state_count "
                     "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
     // End of list sentinel
     {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
     {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
 };
 };

+ 123 - 1
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -26,7 +26,7 @@ using namespace std;
 
 
 namespace {
 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.
 /// columns.  This is coverd by tickets #3557, #4530, and PR#9.
 
 
 /// @brief Catalog of all the SQL statements currently supported.  Note
 /// @brief Catalog of all the SQL statements currently supported.  Note
@@ -201,6 +201,12 @@ PgSqlTaggedStatement tagged_statements[] = {
         "state = $13 "
         "state = $13 "
       "WHERE address = $14"},
       "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
     // End of list sentinel
     { 0,  { 0 }, NULL, NULL}
     { 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)
 PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
     : LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
     : LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
     exchange6_(new PgSqlLease6Exchange()), conn_(parameters) {
     exchange6_(new PgSqlLease6Exchange()), conn_(parameters) {

+ 11 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.h

@@ -317,6 +317,16 @@ public:
     /// @return Number of leases deleted.
     /// @return Number of leases deleted.
     virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs);
     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
     /// @brief Return backend type
     ///
     ///
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
@@ -385,6 +395,7 @@ public:
         INSERT_LEASE6,              // Add entry to lease6 table
         INSERT_LEASE6,              // Add entry to lease6 table
         UPDATE_LEASE4,              // Update a Lease4 entry
         UPDATE_LEASE4,              // Update a Lease4 entry
         UPDATE_LEASE6,              // Update a Lease6 entry
         UPDATE_LEASE6,              // Update a Lease6 entry
+        RECOUNT_LEASE4_STATS,       // Fetch IPv4 lease statistical data
         NUM_STATEMENTS              // Number of statements
         NUM_STATEMENTS              // Number of statements
     };
     };
 
 

+ 12 - 5
src/lib/dhcpsrv/srv_config.cc

@@ -7,6 +7,7 @@
 #include <config.h>
 #include <config.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/srv_config.h>
 #include <dhcpsrv/srv_config.h>
+#include <dhcpsrv/lease_mgr_factory.h>
 #include <log/logger_manager.h>
 #include <log/logger_manager.h>
 #include <log/logger_specification.h>
 #include <log/logger_specification.h>
 #include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_*
 #include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_*
@@ -164,11 +165,17 @@ SrvConfig::removeStatistics() {
 
 
 void
 void
 SrvConfig::updateStatistics() {
 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();
+    }
 }
 }
 
 
 }
 }

+ 20 - 0
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc

@@ -10,6 +10,7 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcp/tests/iface_mgr_test_config.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
 #include <stats/stats_mgr.h>
 #include <stats/stats_mgr.h>
@@ -280,6 +281,23 @@ public:
     void clear() {
     void clear() {
         CfgMgr::instance().setVerbose(false);
         CfgMgr::instance().setVerbose(false);
         CfgMgr::instance().clear();
         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)
     /// used in client classification (or just empty container for other tests)
@@ -575,6 +593,7 @@ TEST_F(CfgMgrTest, verbosity) {
 TEST_F(CfgMgrTest, commitStats4) {
 TEST_F(CfgMgrTest, commitStats4) {
     CfgMgr& cfg_mgr = CfgMgr::instance();
     CfgMgr& cfg_mgr = CfgMgr::instance();
     StatsMgr& stats_mgr = StatsMgr::instance();
     StatsMgr& stats_mgr = StatsMgr::instance();
+    startBackend(AF_INET);
 
 
     // Let's prepare the "old" configuration: a subnet with id 123
     // Let's prepare the "old" configuration: a subnet with id 123
     // and pretend there were addresses assigned, so statistics are non-zero.
     // and pretend there were addresses assigned, so statistics are non-zero.
@@ -641,6 +660,7 @@ TEST_F(CfgMgrTest, clearStats4) {
 TEST_F(CfgMgrTest, commitStats6) {
 TEST_F(CfgMgrTest, commitStats6) {
     CfgMgr& cfg_mgr = CfgMgr::instance();
     CfgMgr& cfg_mgr = CfgMgr::instance();
     StatsMgr& stats_mgr = StatsMgr::instance();
     StatsMgr& stats_mgr = StatsMgr::instance();
+    startBackend(AF_INET6);
 
 
     // Let's prepare the "old" configuration: a subnet with id 123
     // Let's prepare the "old" configuration: a subnet with id 123
     // and pretend there were addresses assigned, so statistics are non-zero.
     // and pretend there were addresses assigned, so statistics are non-zero.

+ 6 - 0
src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc

@@ -402,4 +402,10 @@ TEST_F(PgSqlLeaseMgrTest, getExpiredLeases6) {
     testGetExpiredLeases6();
     testGetExpiredLeases6();
 }
 }
 
 
+// Verifies that IPv4 lease statistics can be recalculated.
+TEST_F(PgSqlLeaseMgrTest, recountAddressStats4) {
+    testRecountAddressStats4();
+}
+
+
 }; // namespace
 }; // namespace