]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2445] add getClassLeaseCount() for MySQL and PostgreSQL
authorAndrei Pavel <andrei@isc.org>
Wed, 29 Jun 2022 17:56:45 +0000 (20:56 +0300)
committerAndrei Pavel <andrei@isc.org>
Thu, 30 Jun 2022 12:31:05 +0000 (15:31 +0300)
src/lib/dhcpsrv/lease_mgr.h
src/lib/dhcpsrv/memfile_lease_mgr.h
src/lib/dhcpsrv/mysql_lease_mgr.cc
src/lib/dhcpsrv/mysql_lease_mgr.h
src/lib/dhcpsrv/pgsql_lease_mgr.cc
src/lib/dhcpsrv/pgsql_lease_mgr.h
src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc

index b20ab2d1004a41ff52d7865f80fbd0691eb4eed1..80716b6d2c0047bfe5aa6363e7fb7ca49ed9932b 100644 (file)
@@ -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.
index 0c1b807136ba714b70458ff89d35e59af25c7157..4298c19626ab06a5deb291f183e7e73d12c3ea07 100644 (file)
@@ -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.
     ///
index 40ca2cabe12376c6070ca14fde116f06f8b628d4..179ab7a3d7ee83718f0bca09d2220ce7eb53a02c 100644 (file)
@@ -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<uint8_t>(ltype));
+    }
+    MySqlBindingCollection out_bindings({
+        MySqlBinding::createInteger<int64_t>()
+    });
+
+    // 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<int64_t>();
+    });
+
+    return count;
+}
+
 LeaseStatsQueryPtr
 MySqlLeaseMgr::startLeaseStatsQuery4() {
     // Get a context
index cd1db818507154f3418bc894a324a32006d06d13..962aac6a736aed01febabe091a319800f98141c0 100644 (file)
@@ -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.
index 0c523879a5e1f82ab85a2f29eb28f6b7a2bec11e..bc3c102e0f1d24dfede37c9dcc748253c4a22626 100644 (file)
@@ -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
index 81d831cc844e62953ce508d15031745aebeeac02..5329baa732bb1d543fb52da2989500be78bcc878 100644 (file)
@@ -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:
index 3a8817ed3f1befe85706bcdb37396cffc357a3d8..08ea901d9ca0ee1272a23cdbdf00f0b7cb111680 100644 (file)
@@ -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.)
index 07795b9e6a992cf91530485ee9c07755f62eaaad..a0986d998210b69af49e5795df6eee398d31e6e8 100644 (file)
@@ -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();
index b5ccf0389d712c89a34fe83c359db2983389b143..9383db8b63583f8827bbb778efb5309080e237d7 100644 (file)
@@ -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.
index f07876e1d227400ab53389e5ac352e445538c272..84d8d204163fb296e628faa368389b44bda55c14 100644 (file)
@@ -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