From: Andrei Pavel Date: Wed, 29 Jun 2022 17:56:45 +0000 (+0300) Subject: [#2445] add getClassLeaseCount() for MySQL and PostgreSQL X-Git-Tag: Kea-2.2.0~94 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6ca773c9ae9c6813ea00fb1237d3817e812b3cde;p=thirdparty%2Fkea.git [#2445] add getClassLeaseCount() for MySQL and PostgreSQL --- diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index b20ab2d100..80716b6d2c 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -798,14 +798,9 @@ public: /// @param ltype type of lease for which the count is desired. Defaults to /// Lease::TYPE_V4. /// - /// @return count of leases - /// @throw NotImplemented if a derivation does not override this. + /// @return number of leases virtual size_t getClassLeaseCount(const ClientClass& client_class, - const Lease::Type& ltype = Lease::TYPE_V4) const { - // For now we throw, ultimately this should be pure virtual. - isc_throw(NotImplemented, "LeaseMgr::getClassLeaseCount " - << client_class << ":" << ltype); - } + const Lease::Type& ltype = Lease::TYPE_V4) const = 0; private: /// The IOService object, used for all ASIO operations. diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index 0c1b807136..4298c19626 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -1244,8 +1244,10 @@ public: /// @param client_class client class for which the count is desired /// @param ltype type of lease for which the count is desired. Defaults to /// Lease::TYPE_V4. + /// + /// @return number of leases virtual size_t getClassLeaseCount(const ClientClass& client_class, - const Lease::Type& ltype = Lease::TYPE_V4) const override; + const Lease::Type& ltype = Lease::TYPE_V4) const override; /// @brief Recount the leases per class for V4 leases. /// diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 40ca2cabe1..179ab7a3d7 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -332,6 +332,14 @@ tagged_statements = { { {MySqlLeaseMgr::CHECK_LEASE4_LIMITS, "SELECT checkLease4Limits(?)"}, {MySqlLeaseMgr::CHECK_LEASE6_LIMITS, "SELECT checkLease6Limits(?)"}, {MySqlLeaseMgr::IS_JSON_SUPPORTED, "SELECT isJsonSupported()"}, + {MySqlLeaseMgr::GET_LEASE4_COUNT_BY_CLASS, + "SELECT leases " + "FROM lease4_stat_by_client_class " + "WHERE client_class = ?"}, + {MySqlLeaseMgr::GET_LEASE6_COUNT_BY_CLASS, + "SELECT leases " + "FROM lease6_stat_by_client_class " + "WHERE client_class = ? AND lease_type = ?"}, } }; // tagged_statements } // namespace @@ -3157,6 +3165,36 @@ MySqlLeaseMgr::isJsonSupported() const { return json_supported; } +size_t +MySqlLeaseMgr::getClassLeaseCount(const ClientClass& client_class, + const Lease::Type& ltype /* = Lease::TYPE_V4*/) const { + // Get a context. + MySqlLeaseContextAlloc get_context(*this); + MySqlLeaseContextPtr ctx = get_context.ctx_; + + // Create bindings. + MySqlBindingCollection in_bindings({ + MySqlBinding::createString(client_class) + }); + if (ltype != Lease::TYPE_V4) { + in_bindings.push_back(MySqlBinding::createInteger(ltype)); + } + MySqlBindingCollection out_bindings({ + MySqlBinding::createInteger() + }); + + // Execute the select. + StatementIndex const stindex(ltype == Lease::TYPE_V4 ? GET_LEASE4_COUNT_BY_CLASS : + GET_LEASE6_COUNT_BY_CLASS); + size_t count(0); + ctx->conn_.selectQuery(stindex, in_bindings, out_bindings, + [&count] (MySqlBindingCollection const& result) { + count = result[0]->getInteger(); + }); + + return count; +} + LeaseStatsQueryPtr MySqlLeaseMgr::startLeaseStatsQuery4() { // Get a context diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index cd1db81850..962aac6a73 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -727,6 +727,8 @@ public: CHECK_LEASE4_LIMITS, // Check if allocated IPv4 leases are inside the set limits. CHECK_LEASE6_LIMITS, // Check if allocated IPv6 leases are inside the set limits. IS_JSON_SUPPORTED, // Checks if JSON support is enabled in the database. + GET_LEASE4_COUNT_BY_CLASS, // Fetches the IPv4 lease count for a given class. + GET_LEASE6_COUNT_BY_CLASS, // Fetches the IPv6 lease count for given class and lease type. NUM_STATEMENTS // Number of statements }; @@ -976,6 +978,16 @@ private: /// @return true if there is JSON support, false otherwise bool isJsonSupported() const override; + /// @brief Returns the class lease count for a given class and lease type. + /// + /// @param client_class client class for which the count is desired + /// @param ltype type of lease for which the count is desired. Defaults to + /// Lease::TYPE_V4. + /// + /// @return number of leases + virtual size_t getClassLeaseCount(const ClientClass& client_class, + const Lease::Type& ltype = Lease::TYPE_V4) const override; + /// @brief Check Error and Throw Exception /// /// This method invokes @ref MySqlConnection::checkError. diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index 0c523879a5..bc3c102e0f 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -374,6 +374,20 @@ PgSqlTaggedStatement tagged_statements[] = { "is_json_supported", "SELECT isJsonSupported()" }, + // GET_LEASE4_COUNT_BY_CLASS + { 1, { OID_VARCHAR }, + "get_lease4_count_by_class", + "SELECT leases " + "FROM lease4_stat_by_client_class " + "WHERE client_class = $1"}, + + // GET_LEASE6_COUNT_BY_CLASS + { 2, { OID_VARCHAR, OID_INT2 }, + "get_lease6_count_by_class", + "SELECT leases " + "FROM lease6_stat_by_client_class " + "WHERE client_class = $1 AND lease_type = $2"}, + // End of list sentinel { 0, { 0 }, NULL, NULL} }; @@ -2348,6 +2362,42 @@ PgSqlLeaseMgr::isJsonSupported() const { return json_supported; } +size_t +PgSqlLeaseMgr::getClassLeaseCount(const ClientClass& client_class, + const Lease::Type& ltype /* = Lease::TYPE_V4*/) const { + // Get a context. + PgSqlLeaseContextAlloc get_context(*this); + PgSqlLeaseContextPtr ctx(get_context.ctx_); + + // Create bindings. + PsqlBindArray bind_array; + bind_array.add(client_class); + if (ltype != Lease::TYPE_V4) { + bind_array.add(ltype); + } + + // Execute the select. + StatementIndex const stindex(ltype == Lease::TYPE_V4 ? GET_LEASE4_COUNT_BY_CLASS : + GET_LEASE6_COUNT_BY_CLASS); + PgSqlResult r(PQexecPrepared(ctx->conn_, + tagged_statements[stindex].name, + tagged_statements[stindex].nbparams, + &bind_array.values_[0], + &bind_array.lengths_[0], + &bind_array.formats_[0], 0)); + ctx->conn_.checkStatementError(r, tagged_statements[stindex]); + + int rows = PQntuples(r); + if (rows == 0) { + // No entries means 0 leases. + return 0; + } + + size_t count; + PgSqlExchange::getColumnValue(r, 0, 0, count); + return count; +} + LeaseStatsQueryPtr PgSqlLeaseMgr::startLeaseStatsQuery4() { // Get a context diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index 81d831cc84..5329baa732 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -702,6 +702,8 @@ public: CHECK_LEASE4_LIMITS, // Check if allocated IPv4 leases are inside the set limits. CHECK_LEASE6_LIMITS, // Check if allocated IPv6 leases are inside the set limits. IS_JSON_SUPPORTED, // Checks if JSON support is enabled in the database. + GET_LEASE4_COUNT_BY_CLASS, // Fetches the IPv4 lease count for a given class. + GET_LEASE6_COUNT_BY_CLASS, // Fetches the IPv6 lease count for given class and lease type. NUM_STATEMENTS // Number of statements }; @@ -950,6 +952,16 @@ private: /// @return true if there is JSON support, false otherwise bool isJsonSupported() const override; + /// @brief Returns the class lease count for a given class and lease type. + /// + /// @param client_class client class for which the count is desired + /// @param ltype type of lease for which the count is desired. Defaults to + /// Lease::TYPE_V4. + /// + /// @return number of leases + virtual size_t getClassLeaseCount(const ClientClass& client_class, + const Lease::Type& ltype = Lease::TYPE_V4) const override; + /// @brief Context RAII Allocator. class PgSqlLeaseContextAlloc { public: diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index 3a8817ed3f..08ea901d9c 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -360,13 +360,25 @@ public: isc_throw(NotImplemented, "ConcreteLeaseMgr::checkLimits6() not implemented"); } - /// @brief Checks if JSON support is enabled in the database. + /// @brief Pretends to check if JSON support is enabled in the database. /// /// @return true if there is JSON support, false otherwise bool isJsonSupported() const override { isc_throw(NotImplemented, "ConcreteLeaseMgr::isJsonSupported() not implemented"); } + /// @brief Pretends to return the class lease count for a given class and lease type. + /// + /// @param client_class client class for which the count is desired + /// @param ltype type of lease for which the count is desired. Defaults to + /// Lease::TYPE_V4. + /// + /// @return number of leases + virtual size_t getClassLeaseCount(const ClientClass& client_class, + const Lease::Type& ltype = Lease::TYPE_V4) const override { + isc_throw(NotImplemented, "ConcreteLeaseMgr::getClassLeaseCount() not implemented"); + } + /// @brief Returns backend type. /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index 07795b9e6a..a0986d9982 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -2629,21 +2629,20 @@ TEST_F(MemfileLeaseMgrTest, checkLimitsNull4) { } // brief Checks that a null user context allows allocation. -// DISABLED_ until Memfile_LeaseMgr implements checkLimits6(). -TEST_F(MemfileLeaseMgrTest, DISABLED_checkLimitsNull6) { +TEST_F(MemfileLeaseMgrTest, checkLimitsNull6) { startBackend(V6); std::string text; ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(nullptr)); EXPECT_TRUE(text.empty()); } -// Checks a few V4 lease limit checking scenarios. +// Checks a few v4 lease limit checking scenarios. TEST_F(MemfileLeaseMgrTest, checkLimits4) { startBackend(V4); testLeaseLimits4(); } -// Checks a few V4 lease limit checking scenarios. +// Checks a few v6 lease limit checking scenarios. TEST_F(MemfileLeaseMgrTest, checkLimits6) { startBackend(V6); testLeaseLimits6(); diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index b5ccf0389d..9383db8b63 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -1084,22 +1084,34 @@ TEST_F(MySqlLeaseMgrTest, isJsonSupported) { // Verifies that v4 class lease counts are correctly adjusted // when leases have class lists. -// Disabled until MySqlLeaseMgr implements LeaseMgr::getClassLeaseCount() -TEST_F(MySqlLeaseMgrTest, DISABLED_classLeaseCount4) { +TEST_F(MySqlLeaseMgrTest, classLeaseCount4) { + if (!LeaseMgrFactory::instance().isJsonSupported()) { + std::cout << "Skipped test because of lack of JSON support in the database." << std::endl; + return; + } + testClassLeaseCount4(); } // Verifies that v6 IA_NA class lease counts are correctly adjusted // when leases have class lists. -// Disabled until MySqlLeaseMgr implements LeaseMgr::getClassLeaseCount() -TEST_F(MySqlLeaseMgrTest, DISABLED_classLeaseCount6_NA) { +TEST_F(MySqlLeaseMgrTest, classLeaseCount6_NA) { + if (!LeaseMgrFactory::instance().isJsonSupported()) { + std::cout << "Skipped test because of lack of JSON support in the database." << std::endl; + return; + } + testClassLeaseCount6(Lease::TYPE_NA); } // Verifies that v6 IA_PD class lease counts are correctly adjusted // when leases have class lists. -// Disabled until MySqlLeaseMgr implements LeaseMgr::getClassLeaseCount() -TEST_F(MySqlLeaseMgrTest, DISABLED_classLeaseCount6_PD) { +TEST_F(MySqlLeaseMgrTest, classLeaseCount6_PD) { + if (!LeaseMgrFactory::instance().isJsonSupported()) { + std::cout << "Skipped test because of lack of JSON support in the database." << std::endl; + return; + } + testClassLeaseCount6(Lease::TYPE_PD); } @@ -1112,7 +1124,7 @@ TEST_F(MySqlLeaseMgrTest, checkLimitsNull) { EXPECT_TRUE(text.empty()); } -/// @brief Checks a few limit checking scenarios. +/// @brief Checks a few v4 limit checking scenarios. TEST_F(MySqlLeaseMgrTest, checkLimits4) { // Limit checking should be precluded at reconfiguration time on systems // that don't have JSON support in the database. It's fine if it throws. @@ -1129,7 +1141,7 @@ TEST_F(MySqlLeaseMgrTest, checkLimits4) { testLeaseLimits4(); } -/// @brief Checks a few limit checking scenarios. +/// @brief Checks a few v6 limit checking scenarios. TEST_F(MySqlLeaseMgrTest, checkLimits6) { // Limit checking should be precluded at reconfiguration time on systems // that don't have JSON support in the database. It's fine if it throws. diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index f07876e1d2..84d8d20416 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -1060,6 +1060,39 @@ TEST_F(PgSqlGenericBackendTest, leaseCount) { EXPECT_EQ(0, countRows(conn, "lease4")); } +// Verifies that v4 class lease counts are correctly adjusted +// when leases have class lists. +TEST_F(PgSqlLeaseMgrTest, classLeaseCount4) { + if (!LeaseMgrFactory::instance().isJsonSupported()) { + std::cout << "Skipped test because of lack of JSON support in the database." << std::endl; + return; + } + + testClassLeaseCount4(); +} + +// Verifies that v6 IA_NA class lease counts are correctly adjusted +// when leases have class lists. +TEST_F(PgSqlLeaseMgrTest, classLeaseCount6_NA) { + if (!LeaseMgrFactory::instance().isJsonSupported()) { + std::cout << "Skipped test because of lack of JSON support in the database." << std::endl; + return; + } + + testClassLeaseCount6(Lease::TYPE_NA); +} + +// Verifies that v6 IA_PD class lease counts are correctly adjusted +// when leases have class lists. +TEST_F(PgSqlLeaseMgrTest, classLeaseCount6_PD) { + if (!LeaseMgrFactory::instance().isJsonSupported()) { + std::cout << "Skipped test because of lack of JSON support in the database." << std::endl; + return; + } + + testClassLeaseCount6(Lease::TYPE_PD); +} + /// @brief Checks that no exceptions are thrown when inquiring about JSON /// support and prints an informative message. TEST_F(PgSqlLeaseMgrTest, isJsonSupported) { @@ -1078,8 +1111,8 @@ TEST_F(PgSqlLeaseMgrTest, checkLimitsNull) { EXPECT_TRUE(text.empty()); } -/// @brief Checks a few limit checking scenarios. -TEST_F(PgSqlLeaseMgrTest, checkLimits) { +/// @brief Checks a few v4 limit checking scenarios. +TEST_F(PgSqlLeaseMgrTest, checkLimits4) { // Limit checking should be precluded at reconfiguration time on systems // that don't have JSON support in the database. It's fine if it throws. if (!LeaseMgrFactory::instance().isJsonSupported()) { @@ -1094,6 +1127,18 @@ TEST_F(PgSqlLeaseMgrTest, checkLimits) { "QUERY: SELECT * FROM JSON_ARRAY_ELEMENTS(json_cast(user_context)" "->'ISC'->'limits'->'client-classes')\n" "CONTEXT: PL/pgSQL function checklease4limits(text) line 10 at FOR over SELECT rows\n"); + return; + } + + // The rest of the checks are only for databases with JSON support. + testLeaseLimits4(); +} + +/// @brief Checks a few v6 limit checking scenarios. +TEST_F(PgSqlLeaseMgrTest, checkLimits6) { + // Limit checking should be precluded at reconfiguration time on systems + // that don't have JSON support in the database. It's fine if it throws. + if (!LeaseMgrFactory::instance().isJsonSupported()) { ASSERT_THROW_MSG(LeaseMgrFactory::instance().checkLimits6( isc::data::Element::createMap()), isc::db::DbOperationError, "Statement exec failed for: check_lease6_limits, status: 7sqlstate:[ 42883 ], " @@ -1109,7 +1154,7 @@ TEST_F(PgSqlLeaseMgrTest, checkLimits) { } // The rest of the checks are only for databases with JSON support. - testLeaseLimits(); + testLeaseLimits6(); } } // namespace