From: Thomas Markwalder Date: Wed, 11 Apr 2018 18:56:44 +0000 (-0400) Subject: [5585] Initial LeaseQueryStats, LeaseMgr, and MemfileLeaseMgr extensions X-Git-Tag: trac5488_base^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=501b74e2f69bb0e613317ab421ae073232e0db93;p=thirdparty%2Fkea.git [5585] Initial LeaseQueryStats, LeaseMgr, and MemfileLeaseMgr extensions MemfileLeaseMgr unit tests fail for subnet and range query variants, not yet implemented src/lib/dhcpsrv/lease_mgr.* LeaseStatsQuery - Added SelectMode enum typedef - Added instance members: first_subnet_id_, last_subnet_id_, and select_mode_ - Added constructors for single subnet and subnet range queries LeaseMgr Added virtual start query variants: - startSubnetLeaseStatsQuery4(SubnetID subnet_id); - startSubnetRangeLeaseStatsQuery4(SubnetID first, SubnetID last); - startSubnetLeaseStatsQuery6(SubnetID subnet_id); - startSubnetRangeLeaseStatsQuery6(SubnetID first, SubnetID last); src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.* GenericLeaseMgrTest - Added checkQueryAgainstRowSet() - Added testLeaseStatsQuery4() src/lib/dhcpsrv/tests/lease_mgr_unittest.cc Added LeaseStatsQuery ctor tests - TEST (LeaseStatsQueryTest, defaultCtor) - TEST (LeaseStatsQueryTest, singleSubnetCtor) - TEST (LeaseStatsQueryTest, subnetRangeCtor) src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc TEST_F(MemfileLeaseMgrTest, leaseStatsQuery4) { --- diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc index 205e565985..2ea733258d 100644 --- a/src/lib/dhcpsrv/lease_mgr.cc +++ b/src/lib/dhcpsrv/lease_mgr.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2018 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 @@ -111,11 +111,52 @@ LeaseMgr::recountLeaseStats4() { } } +LeaseStatsQuery::LeaseStatsQuery() + : first_subnet_id_(0), last_subnet_id_(0), select_mode_(ALL_SUBNETS) { +} + +LeaseStatsQuery::LeaseStatsQuery(SubnetID subnet_id) + : first_subnet_id_(subnet_id), last_subnet_id_(0), + select_mode_(SINGLE_SUBNET) { + if (first_subnet_id_ == 0) { + isc_throw(BadValue, "LeaseStatsQuery: subnet_id_ must be > 0"); + } +} + +LeaseStatsQuery::LeaseStatsQuery(SubnetID first, SubnetID last) + : first_subnet_id_(first), last_subnet_id_(last), + select_mode_(SUBNET_RANGE) { + + if (first_subnet_id_ == 0) { + isc_throw(BadValue, "LeaseStatsQuery: first_subnet_id_ must be > 0"); + } + + if (last_subnet_id_ == 0) { + isc_throw(BadValue, "LeaseStatsQuery: last_subnet_id_ must be > 0"); + } + + if (last_subnet_id_ <= first_subnet_id_) { + isc_throw(BadValue, + "LeaseStatsQuery: last_subnet_id_must be > first_subnet_id_"); + } +} + LeaseStatsQueryPtr LeaseMgr::startLeaseStatsQuery4() { return(LeaseStatsQueryPtr()); } +LeaseStatsQueryPtr +LeaseMgr::startSubnetLeaseStatsQuery4(SubnetID /* subnet_id */) { + return(LeaseStatsQueryPtr()); +} + +LeaseStatsQueryPtr +LeaseMgr::startSubnetRangeLeaseStatsQuery4(SubnetID /* first */, + SubnetID /* last */) { + return(LeaseStatsQueryPtr()); +} + bool LeaseStatsQuery::getNextRow(LeaseStatsRow& /*row*/) { return (false); @@ -218,6 +259,17 @@ LeaseMgr::startLeaseStatsQuery6() { return(LeaseStatsQueryPtr()); } +LeaseStatsQueryPtr +LeaseMgr::startSubnetLeaseStatsQuery6(SubnetID /* subnet_id */) { + return(LeaseStatsQueryPtr()); +} + +LeaseStatsQueryPtr +LeaseMgr::startSubnetRangeLeaseStatsQuery6(SubnetID /* first */, + SubnetID /* last */) { + return(LeaseStatsQueryPtr()); +} + std::string LeaseMgr::getDBVersion() { isc_throw(NotImplemented, "LeaseMgr::getDBVersion() called"); diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index f0fd431cc4..d4062e22ce 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -114,7 +114,7 @@ struct LeaseStatsRow { if (subnet_id_ == rhs.subnet_id_ && lease_type_ == rhs.lease_type_ && - lease_state_ < rhs.lease_state_) { + lease_state_ < rhs.lease_state_) { return (true); } @@ -138,8 +138,34 @@ struct LeaseStatsRow { /// instances. The rows must be accessible in ascending order by subnet id. class LeaseStatsQuery { public: + /// @brief Defines the types of selection criteria supported + typedef enum { + ALL_SUBNETS, + SINGLE_SUBNET, + SUBNET_RANGE + } SelectMode; + /// @brief Default constructor - LeaseStatsQuery() {}; + /// The query created will return statistics for all subnets + LeaseStatsQuery(); + + /// @brief Constructor to query for a single subnet's stats + /// + /// The query created will return statistics for a single subnet + /// + /// @param subnet_id id of the subnet for which stats are desired + /// @throw BadValue if sunbet_id given is 0. + LeaseStatsQuery(SubnetID subnet_id); + + /// @brief Constructor to query for the stats for a range of subnets + /// + /// The query created will return statistics for the inclusive range of + /// subnets described by first and last sunbet IDs. + /// + /// @param first first subnet in the range of subnets + /// @param last last subnet in the range of subnets + /// @throw BadValue if either value given is 0 or if last <= first. + LeaseStatsQuery(SubnetID first, SubnetID last); /// @brief virtual destructor virtual ~LeaseStatsQuery() {}; @@ -158,6 +184,27 @@ public: /// @return True if a row was fetched, false if there are no /// more rows. virtual bool getNextRow(LeaseStatsRow& row); + + /// @brief Returns the value of first subnet ID specified (or zero) + SubnetID getFirstSubnetID() const { return first_subnet_id_; }; + + /// @brief Returns the value of last subnet ID specified (or zero) + SubnetID getLastSubnetID() const { return last_subnet_id_; }; + + /// @brief Returns the selection criteria mode + /// The value returned is based upon the constructor variant used + /// and it indicates which query variant will be executed. + SelectMode getSelectMode() const { return select_mode_; }; + +private: + /// @brief First (or only) subnet_id in the selection criteria + SubnetID first_subnet_id_; + + /// @brief Last subnet_id in the selection criteria when a range is given + SubnetID last_subnet_id_; + + /// @brief Indicates the type of selection criteria specified + SelectMode select_mode_; }; /// @brief Defines a pointer to a LeaseStatsQuery. @@ -464,16 +511,42 @@ public: /// adding to the appropriate global statistic. void recountLeaseStats4(); - /// @brief Virtual method which creates and runs the IPv4 lease stats query + /// @brief Creates and runs the IPv4 lease stats query for all subnets /// /// LeaseMgr derivations implement this method such that it creates and /// returns an instance of an LeaseStatsQuery whose result set has been - /// populated with up to date IPv4 lease statistical data. Each row of the - /// result set is an LeaseStatRow which ordered ascending by subnet ID. + /// populated with up to date IPv4 lease statistical data for all subnets. + /// Each row of the result set is an LeaseStatRow which ordered ascending + /// by subnet ID. /// /// @return A populated LeaseStatsQuery virtual LeaseStatsQueryPtr startLeaseStatsQuery4(); + /// @brief Creates and runs the IPv4 lease stats query for a single subnet + /// + /// LeaseMgr derivations implement this method such that it creates and + /// returns an instance of an LeaseStatsQuery whose result set has been + /// populated with up to date IPv4 lease statistical data for a single + /// subnet. Each row of the result set is an LeaseStatRow. + /// + /// @param subnet_id id of the subnet for which stats are desired + /// @return A populated LeaseStatsQuery + virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery4(SubnetID subnet_id); + + /// @brief Creates and runs the IPv4 lease stats query for a single subnet + /// + /// LeaseMgr derivations implement this method such that it creates and + /// returns an instance of an LeaseStatsQuery whose result set has been + /// populated with up to date IPv4 lease statistical data for an inclusive + /// range of subnets. Each row of the result set is an LeaseStatRow which + /// ordered ascending by subnet ID. + /// + /// @param first first subnet in the range of subnets + /// @param last last subnet in the range of subnets + /// @return A populated LeaseStatsQuery + virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery4(SubnetID first, + SubnetID last); + /// @brief Recalculates per-subnet and global stats for IPv6 leases /// /// This method recalculates the following statistics: @@ -495,16 +568,42 @@ public: /// per subnet and adding to the appropriate global statistic. void recountLeaseStats6(); - /// @brief Virtual method which creates and runs the IPv6 lease stats query + /// @brief Creates and runs the IPv6 lease stats query for all subnets /// /// LeaseMgr derivations implement this method such that it creates and /// returns an instance of an LeaseStatsQuery whose result set has been - /// populated with up to date IPv6 lease statistical data. Each row of the - /// result set is an LeaseStatRow which ordered ascending by subnet ID. + /// populated with up to date IPv6 lease statistical data for all subnets. + /// Each row of the result set is an LeaseStatRow which ordered ascending + /// by subnet ID. /// /// @return A populated LeaseStatsQuery virtual LeaseStatsQueryPtr startLeaseStatsQuery6(); + /// @brief Creates and runs the IPv6 lease stats query for a single subnet + /// + /// LeaseMgr derivations implement this method such that it creates and + /// returns an instance of an LeaseStatsQuery whose result set has been + /// populated with up to date IPv6 lease statistical data for a single + /// subnet. Each row of the result set is an LeaseStatRow. + /// + /// @param subnet_id id of the subnet for which stats are desired + /// @return A populated LeaseStatsQuery + virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery6(SubnetID subnet_id); + + /// @brief Creates and runs the IPv6 lease stats query for a single subnet + /// + /// LeaseMgr derivations implement this method such that it creates and + /// returns an instance of an LeaseStatsQuery whose result set has been + /// populated with up to date IPv6 lease statistical data for an inclusive + /// range of subnets. Each row of the result set is an LeaseStatRow which + /// ordered ascending by subnet ID. + /// + /// @param first first subnet in the range of subnets + /// @param last last subnet in the range of subnets + /// @return A populated LeaseStatsQuery + virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery6(SubnetID first, + SubnetID last); + /// @brief Virtual method which removes specified leases. /// /// This rather dangerous method is able to remove all leases from specified diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index f0a9f0b306..41950543d7 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -2857,6 +2857,112 @@ LeaseMgrDbLostCallbackTest::testDbLostCallback() { EXPECT_TRUE(callback_called_); } +void +GenericLeaseMgrTest::checkQueryAgainstRowSet(LeaseStatsQueryPtr query, + RowSet& expected_rows) { + ASSERT_TRUE(query) << "query is null"; + + int rows_matched = 0; + LeaseStatsRow row; + while (query->getNextRow(row)) { + auto found_row = expected_rows.find(row); + if (found_row == expected_rows.end()) { + ADD_FAILURE() << "query row not in expected set" + << " id: " << row.subnet_id_ + << " type: " << row.lease_type_ + << " state: " << row.lease_state_ + << " count: " << row.state_count_; + } else { + ++rows_matched; + } + } + + ASSERT_EQ(rows_matched, expected_rows.size()) << "rows mismatched"; +} + +void +GenericLeaseMgrTest::testLeaseStatsQuery4() { + // Create three subnets. + int num_subnets = 3; + CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4(); + Subnet4Ptr subnet; + Pool4Ptr pool; + + subnet.reset(new Subnet4(IOAddress("192.0.1.0"), 24, 1, 2, 3, 1)); + pool.reset(new Pool4(IOAddress("192.0.1.0"), 24)); + subnet->addPool(pool); + cfg->add(subnet); + + subnet.reset(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 2)); + pool.reset(new Pool4(IOAddress("192.0.2.0"), 24)); + subnet->addPool(pool); + cfg->add(subnet); + + subnet.reset(new Subnet4(IOAddress("192.0.3.0"), 24, 1, 2, 3, 3)); + pool.reset(new Pool4(IOAddress("192.0.3.0"), 24)); + subnet->addPool(pool); + cfg->add(subnet); + + ASSERT_NO_THROW(CfgMgr::instance().commit()); + + // Now let's insert some leases into subnet 1. + // Two leases in the default state, i.e. assigned. + // One lease in declined state. + // One lease in the expired state. + int subnet_id = 1; + makeLease4("192.0.1.1", subnet_id); + makeLease4("192.0.1.2", subnet_id, Lease::STATE_DECLINED); + makeLease4("192.0.1.3", subnet_id, Lease::STATE_EXPIRED_RECLAIMED); + makeLease4("192.0.1.4", subnet_id); + + // Now let's add leases to subnet 2. + // One declined lease. + subnet_id = 2; + makeLease4("192.0.2.2", subnet_id, Lease::STATE_DECLINED); + + // Now add leases to subnet 3 + // Two leases in default state, i.e. assigned. + // One declined lease. + subnet_id = 3; + makeLease4("192.0.3.1", subnet_id); + makeLease4("192.0.3.2", subnet_id); + makeLease4("192.0.3.3", subnet_id, Lease::STATE_DECLINED); + + LeaseStatsQueryPtr query; + RowSet expected_rows; + + // Test a non-matching single subnet + ASSERT_NO_THROW(query = lmptr_->startSubnetLeaseStatsQuery4(777)); + checkQueryAgainstRowSet(query, expected_rows); + + // Test a single subnet + // Add expected row for Subnet 2 + expected_rows.insert(LeaseStatsRow(2, Lease::STATE_DEFAULT, 0)); + expected_rows.insert(LeaseStatsRow(2, Lease::STATE_DECLINED, 1)); + // Start the query + ASSERT_NO_THROW(query = lmptr_->startSubnetLeaseStatsQuery4(2)); + // Verify contents + checkQueryAgainstRowSet(query, expected_rows); + + // Test a range of subnets + // Add expected rows for Subnet 3 + expected_rows.insert(LeaseStatsRow(3, Lease::STATE_DEFAULT, 2)); + expected_rows.insert(LeaseStatsRow(3, Lease::STATE_DECLINED, 1)); + // Start the query + ASSERT_NO_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery4(2,3)); + // Verify contents + checkQueryAgainstRowSet(query, expected_rows); + + // Test all subnets + // Add expected rows for Subnet 1 + expected_rows.insert(LeaseStatsRow(1, Lease::STATE_DEFAULT, 2)); + expected_rows.insert(LeaseStatsRow(1, Lease::STATE_DECLINED, 1)); + // Start the query + ASSERT_NO_THROW(query = lmptr_->startLeaseStatsQuery4()); + // Verify contents + checkQueryAgainstRowSet(query, expected_rows); +} + }; // namespace test }; // namespace dhcp }; // namespace isc diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index cd62825eb3..2d78dd8440 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace isc { namespace dhcp { @@ -19,6 +20,7 @@ namespace test { typedef std::map StatValMap; typedef std::pair StatValPair; typedef std::vector StatValMapList; +typedef std::set RowSet; /// @brief Test Fixture class with utility functions for LeaseMgr backends /// @@ -394,6 +396,24 @@ public: /// attempts to delete them, one subnet at a time. void testWipeLeases6(); + /// @brief Checks operation of v4 LeaseStatsQuery variants + /// + /// It creates three subnets with leasese in various states in + /// each. It runs and verifies the returned query contents for + /// each of the v4 startLeaseQuery variants: + /// + /// - startSubnetLeaseQuery() + /// - startSubneRangetLeaseQuery() + /// - startLeaseQuery() + /// + void testLeaseStatsQuery4(); + + /// @brief Compares LeaseQueryStats content to expected set of rows + /// + /// @param qry - a started LeaseStatsQuery + /// @param row_set - set of rows expected to be found in the query rows + void checkQueryAgainstRowSet(LeaseStatsQueryPtr qry, RowSet& row_set); + /// @brief String forms of IPv4 addresses std::vector straddress4_; diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index 88ea5a064a..7f015f0028 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -368,10 +368,45 @@ TEST_F(LeaseMgrTest, getLease6) { MultipleRecords); } +// Verify LeaseStatsQuery default construction +TEST (LeaseStatsQueryTest, defaultCtor) { + LeaseStatsQueryPtr qry; + + ASSERT_NO_THROW(qry.reset(new LeaseStatsQuery())); + ASSERT_EQ(0, qry->getFirstSubnetID()); + ASSERT_EQ(0, qry->getLastSubnetID()); + ASSERT_EQ(LeaseStatsQuery::ALL_SUBNETS, qry->getSelectMode()); +} + +// Verify LeaseStatsQuery single-subnet construction +TEST (LeaseStatsQueryTest, singleSubnetCtor) { + LeaseStatsQueryPtr qry; + + ASSERT_THROW(qry.reset(new LeaseStatsQuery(0)), BadValue); + ASSERT_NO_THROW(qry.reset(new LeaseStatsQuery(77))); + ASSERT_EQ(77, qry->getFirstSubnetID()); + ASSERT_EQ(0, qry->getLastSubnetID()); + ASSERT_EQ(LeaseStatsQuery::SINGLE_SUBNET, qry->getSelectMode()); +} + +// Verify LeaseStatsQuery subnet-range construction +TEST (LeaseStatsQueryTest, subnetRangeCtor) { + LeaseStatsQueryPtr qry; + + // Either ID set to 0, or a backward range should throw + ASSERT_THROW(qry.reset(new LeaseStatsQuery(0,1)), BadValue); + ASSERT_THROW(qry.reset(new LeaseStatsQuery(1,0)), BadValue); + ASSERT_THROW(qry.reset(new LeaseStatsQuery(2,1)), BadValue); + + // Valid values should work and set mode accordingly. + ASSERT_NO_THROW(qry.reset(new LeaseStatsQuery(1,2))); + ASSERT_EQ(1, qry->getFirstSubnetID()); + ASSERT_EQ(2, qry->getLastSubnetID()); + ASSERT_EQ(LeaseStatsQuery::SUBNET_RANGE, qry->getSelectMode()); +} + // There's no point in calling any other methods in LeaseMgr, as they // are purely virtual, so we would only call ConcreteLeaseMgr methods. // Those methods are just stubs that do not return anything. - - }; // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index cdcf55eef7..09f8032197 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -1935,4 +1935,10 @@ TEST_F(MemfileLeaseMgrTest, wipeLeases6) { testWipeLeases6(); } +TEST_F(MemfileLeaseMgrTest, leaseStatsQuery4) { + startBackend(V4); + testLeaseStatsQuery4(); +} + + } // namespace