]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#4466] Added new SFLQ API funcs to MySqlLeaseMgr
authorThomas Markwalder <tmark@isc.org>
Fri, 24 Apr 2026 15:38:18 +0000 (11:38 -0400)
committerThomas Markwalder <tmark@isc.org>
Mon, 4 May 2026 15:32:33 +0000 (15:32 +0000)
/src/hooks/dhcp/mysql/mysql_lb_messages.*
    MYSQL_LB_SFLQ_POOL4_GET_ALL
    MYSQL_LB_SFLQ_POOL4_GET_BY_SUBNET
    MYSQL_LB_SFLQ_POOL4_GET_BY_RANGE
    MYSQL_LB_SFLQ_POOL4_DELETE delete
    MYSQL_LB_SFLQ_POOL6_GET_ALL fetch
    MYSQL_LB_SFLQ_POOL6_GET_BY_SUBNET
    MYSQL_LB_SFLQ_POOL6_GET_BY_RANGE
    MYSQL_LB_SFLQ_POOL6_DELETE
    - new messages

/src/hooks/dhcp/mysql/mysql_lease_mgr.cc
    MySqlLeaseMgr::SFLQ_POOL4_GET_ALL
    MySqlLeaseMgr::SFLQ_POOL4_GET_BY_SUBNET
    MySqlLeaseMgr::SFLQ_POOL4_GET_BY_RANGE
    MySqlLeaseMgr::SFLQ_POOL4_DELETE
    MySqlLeaseMgr::SFLQ_POOL6_GET_ALL
    MySqlLeaseMgr::SFLQ_POOL6_GET_BY_SUBNET
    MySqlLeaseMgr::SFLQ_POOL6_GET_BY_RANGE
    MySqlLeaseMgr::SFLQ_POOL6_DELETE
    - new SQL statements

    MySqlLeaseMgr::sflqCreateFlqPool4()
    MySqlLeaseMgr::sflqPickFreeLease4()
    MySqlLeaseMgr::sflqCreateFlqPool6()
    MySqlLeaseMgr::sflqPickFreeLease6()
    - added range adddress validation

    MySqlLeaseMgr::sflqPool4GetAll()
    MySqlLeaseMgr::sflqPool4Get(*)
    MySqlLeaseMgr::sflqPool4Del()
    MySqlLeaseMgr::sflqPool6GetAll()
    MySqlLeaseMgr::sflqPool6Get(*)
    MySqlLeaseMgr::sflqPool6Del()
    MySqlLeaseMgr::sflqPoolGetCommon()
    MySqlLeaseMgr::sflqPoolDelCommon()
    - new functions

/src/hooks/dhcp/mysql/tests/mysql_lease_mgr_unittest.cc
    TEST_F(MySqlLeaseMgrTest, testSflqAPIFuncs4)
    TEST_F(MySqlLeaseMgrTest, testSflqAPIFuncs6NA)
    TEST_F(MySqlLeaseMgrTest, testSflqAPIFuncs6PD)
    TEST_F(MySqlLeaseMgrTest, testSflqAPIOverlappingPools4)
    TEST_F(MySqlLeaseMgrTest, testSflqAPIOverlappingPools6NA)
    TEST_F(MySqlLeaseMgrTest, testSflqAPIOverlappingPools6PD)
    - new tests

/src/hooks/dhcp/pgsql/tests/pgsql_lease_mgr_unittest.cc
    TEST_F(PgSqlLeaseMgrTest, testSflqAPIFuncs4)
    TEST_F(PgSqlLeaseMgrTest, testSflqAPIFuncs6NA)
    TEST_F(PgSqlLeaseMgrTest, testSflqAPIFuncs6PD)
    TEST_F(PgSqlLeaseMgrTest, testSflqAPIOverlappingPools4)
    TEST_F(PgSqlLeaseMgrTest, testSflqAPIOverlappingPools6NA)
    TEST_F(PgSqlLeaseMgrTest, testSflqAPIOverlappingPools6PD)
    - new tests - currently fail with NotImplemented

/src/lib/asiolink/addr_utilities.*
    void validateV4Range() - new funcion
    void validateV6Range() - new funcion

/src/lib/dhcpsrv/lease_mgr.*
    SflqPoolInfo::SflqPoolInfo() - new class

    LeaseMgr::sflqPool4GetAll()
    LeaseMgr::sflqPool4Get(SubnetID)
    LeaseMgr::sflqPool4Get(asiolink::IOAddress, asiolink::IOAddress)
    LeaseMgr::sflqPool4Del(asiolink::IOAddress, asiolink::IOAddress, bool)
    LeaseMgr::sflqPool6GetAll()
    LeaseMgr::sflqPool6Get(SubnetID)
    LeaseMgr::sflqPool6Get(asiolink::IOAddress, asiolink::IOAddress)
    LeaseMgr::sflqPool6Del(asiolink::IOAddress, asiolink::IOAddress, bool)
    - new functions - all throw NotImplemented

 typedef boost::shared_ptr<LeaseStatsRow> LeaseStatsRowPtr;

/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
    TEST(SflqPoolInfo, toElement) - new test

/src/lib/dhcpsrv/testutils/generic_lease_mgr_unittest.cc
    GenericLeaseMgrTest::testSflqAPIFuncs4()
    GenericLeaseMgrTest::testSflqAPIFuncs6()
    GenericLeaseMgrTest::testSflqAPIOverlappingPools4()
    GenericLeaseMgrTest::testSflqAPIOverlappingPools6()
    - new test

14 files changed:
src/hooks/dhcp/mysql/mysql_lb_messages.cc
src/hooks/dhcp/mysql/mysql_lb_messages.h
src/hooks/dhcp/mysql/mysql_lb_messages.mes
src/hooks/dhcp/mysql/mysql_lease_mgr.cc
src/hooks/dhcp/mysql/mysql_lease_mgr.h
src/hooks/dhcp/mysql/tests/mysql_lease_mgr_unittest.cc
src/hooks/dhcp/pgsql/tests/pgsql_lease_mgr_unittest.cc
src/lib/asiolink/addr_utilities.cc
src/lib/asiolink/addr_utilities.h
src/lib/dhcpsrv/lease_mgr.cc
src/lib/dhcpsrv/lease_mgr.h
src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
src/lib/dhcpsrv/testutils/generic_lease_mgr_unittest.cc
src/lib/dhcpsrv/testutils/generic_lease_mgr_unittest.h

index e3ecc2de6e48e755ec4535d0fdaa2cb42c2a88c9..13b70cb8476084dfe3d317c4fec95ffef6097a24 100644 (file)
@@ -60,6 +60,14 @@ extern const isc::log::MessageID MYSQL_LB_SFLQ_CREATE_POOL4 = "MYSQL_LB_SFLQ_CRE
 extern const isc::log::MessageID MYSQL_LB_SFLQ_CREATE_POOL6 = "MYSQL_LB_SFLQ_CREATE_POOL6";
 extern const isc::log::MessageID MYSQL_LB_SFLQ_PICK_LEASE4 = "MYSQL_LB_SFLQ_PICK_LEASE4";
 extern const isc::log::MessageID MYSQL_LB_SFLQ_PICK_LEASE6 = "MYSQL_LB_SFLQ_PICK_LEASE6";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_DELETE = "MYSQL_LB_SFLQ_POOL4_DELETE";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_GET_ALL = "MYSQL_LB_SFLQ_POOL4_GET_ALL";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_GET_BY_RANGE = "MYSQL_LB_SFLQ_POOL4_GET_BY_RANGE";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_GET_BY_SUBNET = "MYSQL_LB_SFLQ_POOL4_GET_BY_SUBNET";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_DELETE = "MYSQL_LB_SFLQ_POOL6_DELETE";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_GET_ALL = "MYSQL_LB_SFLQ_POOL6_GET_ALL";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_GET_BY_RANGE = "MYSQL_LB_SFLQ_POOL6_GET_BY_RANGE";
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_GET_BY_SUBNET = "MYSQL_LB_SFLQ_POOL6_GET_BY_SUBNET";
 extern const isc::log::MessageID MYSQL_LB_TLS_CIPHER = "MYSQL_LB_TLS_CIPHER";
 extern const isc::log::MessageID MYSQL_LB_UPDATE_ADDR4 = "MYSQL_LB_UPDATE_ADDR4";
 extern const isc::log::MessageID MYSQL_LB_UPDATE_ADDR6 = "MYSQL_LB_UPDATE_ADDR6";
@@ -129,6 +137,14 @@ const char* values[] = {
     "MYSQL_LB_SFLQ_CREATE_POOL6", "creating shared-flq pool for address range %1 - %2, type %3, delegated length: %4, subnet id %5, recreate %6, capacity %7",
     "MYSQL_LB_SFLQ_PICK_LEASE4", "picking a free lease from address range %1 - %2",
     "MYSQL_LB_SFLQ_PICK_LEASE6", "picking a free lease from address range %1 - %2",
+    "MYSQL_LB_SFLQ_POOL4_DELETE", "delete the V4 SFLQ pool with start address %1 and end address %2, forece = %3",
+    "MYSQL_LB_SFLQ_POOL4_GET_ALL", "fetch all V4 SFLQ pools",
+    "MYSQL_LB_SFLQ_POOL4_GET_BY_RANGE", "fetch all V4 SFLQ pools that overlap the range %1 and %2",
+    "MYSQL_LB_SFLQ_POOL4_GET_BY_SUBNET", "fetch all V4 SFLQ pools for subnet-id %1",
+    "MYSQL_LB_SFLQ_POOL6_DELETE", "delete the V6 SFLQ pool with start address %1 and end address %2, forece = %3",
+    "MYSQL_LB_SFLQ_POOL6_GET_ALL", "fetch all V6 SFLQ pools",
+    "MYSQL_LB_SFLQ_POOL6_GET_BY_RANGE", "fetch all V6 SFLQ pools that overlap the range %1 and %2",
+    "MYSQL_LB_SFLQ_POOL6_GET_BY_SUBNET", "fetch all V6 SFLQ pools for subnet-id %1",
     "MYSQL_LB_TLS_CIPHER", "TLS cipher: %1",
     "MYSQL_LB_UPDATE_ADDR4", "updating IPv4 lease for address %1",
     "MYSQL_LB_UPDATE_ADDR6", "updating IPv6 lease for address %1, lease type %2",
index 73d84a1d8fdc383624df7f618b47a41415cdd925..033509c9367e302049e84e3af9ac783cb043cd4a 100644 (file)
@@ -61,6 +61,14 @@ extern const isc::log::MessageID MYSQL_LB_SFLQ_CREATE_POOL4;
 extern const isc::log::MessageID MYSQL_LB_SFLQ_CREATE_POOL6;
 extern const isc::log::MessageID MYSQL_LB_SFLQ_PICK_LEASE4;
 extern const isc::log::MessageID MYSQL_LB_SFLQ_PICK_LEASE6;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_DELETE;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_GET_ALL;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_GET_BY_RANGE;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL4_GET_BY_SUBNET;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_DELETE;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_GET_ALL;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_GET_BY_RANGE;
+extern const isc::log::MessageID MYSQL_LB_SFLQ_POOL6_GET_BY_SUBNET;
 extern const isc::log::MessageID MYSQL_LB_TLS_CIPHER;
 extern const isc::log::MessageID MYSQL_LB_UPDATE_ADDR4;
 extern const isc::log::MessageID MYSQL_LB_UPDATE_ADDR6;
index 34392e18034e510f9af281b7b0d3ff6425fc5fa2..117e0ed1b4825d7406ab53d59d35945e5c87ca95 100644 (file)
@@ -339,3 +339,64 @@ Logged at debug log level 50.
 This debug message is issued when the server upgrades IPv6 lease extended info.
 The page number and started address, and the count of already updated leases
 are displayed.
+
+% MYSQL_LB_SFLQ_CREATE_POOL4 creating shared-flq pool for address range %1 - %2, subnet id %3, recreate %4, capacity %5
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end to
+(re)create the shared free lease data for the pool described in the arguments.
+
+% MYSQL_LB_SFLQ_PICK_LEASE4 picking a free lease from address range %1 - %2
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for
+a free address from the pool described in the arguments.
+
+% MYSQL_LB_SFLQ_CREATE_POOL6 creating shared-flq pool for address range %1 - %2, type %3, delegated length: %4, subnet id %5, recreate %6, capacity %7
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end to
+(re)create the shared free lease data for the pool described in the arguments.
+
+% MYSQL_LB_SFLQ_PICK_LEASE6 picking a free lease from address range %1 - %2
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for
+a free lease from the pool described in the arguments.
+
+% MYSQL_LB_SFLQ_POOL4_GET_ALL fetch all V4 SFLQ pools
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for a list of all v4 SFLQ pools.
+
+% MYSQL_LB_SFLQ_POOL4_GET_BY_SUBNET fetch all V4 SFLQ pools for subnet-id %1
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for
+a list of all v4 SFLQ pools belonging to a subnet.
+
+% MYSQL_LB_SFLQ_POOL4_GET_BY_RANGE fetch all V4 SFLQ pools that overlap the range %1 and %2
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for
+the v4 SFLQ pool that overlap the given address range.
+
+% MYSQL_LB_SFLQ_POOL4_DELETE delete the V4 SFLQ pool with start address %1 and end address %2, forece = %3
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back to delete
+the v4 SFLQ pool (and it's free lease data) that match the given start and end
+addresses.
+
+% MYSQL_LB_SFLQ_POOL6_GET_ALL fetch all V6 SFLQ pools
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for
+a list of all v6 SFLQ pools.
+
+% MYSQL_LB_SFLQ_POOL6_GET_BY_SUBNET fetch all V6 SFLQ pools for subnet-id %1
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for
+a list of all v6 SFLQ pools belonging to a subnet.
+
+% MYSQL_LB_SFLQ_POOL6_GET_BY_RANGE fetch all V6 SFLQ pools that overlap the range %1 and %2
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back end for
+the v6 SFLQ pool that overlap the given address range.
+
+% MYSQL_LB_SFLQ_POOL6_DELETE delete the V6 SFLQ pool with start address %1 and end address %2, forece = %3
+Logged at debug log level 50.
+This debug message is issued when the server asks the lease back to delete
+the v6 SFLQ pool (and it's free lease data) that match the given start and end
+addresses.
index 0c46893b20244eda1a6809c4a42f3f178e9d19a2..f15988cafa1508bc074234b267ae5f94fba67cf6 100644 (file)
@@ -88,6 +88,9 @@ namespace {
 /// @brief Maximum length of the text returned by the limit checking functions.
 const size_t LIMITS_TEXT_MAX_LEN = 512;
 
+/// @brief Maximum string length of a V6 address.
+const size_t POOL_ADDRESS6_BUF_LENGTH = 45;
+
 boost::array<TaggedStatement, MySqlLeaseMgr::NUM_STATEMENTS>
 tagged_statements = { {
     {MySqlLeaseMgr::DELETE_LEASE4,
@@ -579,6 +582,99 @@ tagged_statements = { {
                                             "?, ?, ?, ?, ?, ?, ?, ?, ?)"},
     {MySqlLeaseMgr::SFLQ_DELETE_LEASE6,
                     "SELECT sflqDeleteLease6(?, ?)"},
+
+    {MySqlLeaseMgr::SFLQ_POOL4_GET_ALL,
+                    "SELECT q.id, q.subnet_id, 3 as lease_type, "
+                    "       q.start_address, q.end_address, 128 as delegated_len, "
+                    "       q.created_ts, q.modification_ts, count(f.address) as free_leases "
+                    "    FROM flq_pool4 AS q "
+                    "    LEFT JOIN free_lease4 AS f "
+                    "    ON f.address >= q.start_address AND f.address <= q.end_address "
+                    "    GROUP BY q.id "
+                    "    ORDER BY q.subnet_id, q.start_address"},
+
+    {MySqlLeaseMgr::SFLQ_POOL4_GET_BY_SUBNET,
+                    "SELECT q.id, q.subnet_id, 3 as lease_type, "
+                    "       q.start_address, q.end_address, 128 as delegated_len, "
+                    "       q.created_ts, q.modification_ts, count(f.address) as free_leases "
+                    "    FROM flq_pool4 AS q "
+                    "    LEFT JOIN free_lease4 AS f "
+                    "    ON f.address >= q.start_address AND f.address <= q.end_address "
+                    "    WHERE q.subnet_id = ? "
+                    "    GROUP BY q.id "
+                    "    ORDER BY q.subnet_id ASC, q.start_address ASC"},
+
+    {MySqlLeaseMgr::SFLQ_POOL4_GET_BY_RANGE,
+                    "SELECT q.id, q.subnet_id, 3 as lease_type, "
+                    "       q.start_address, q.end_address, 128 as delegated_len, "
+                    "       q.created_ts, q.modification_ts, count(f.address) as free_leases "
+                    "    FROM flq_pool4 AS q "
+                    "    LEFT JOIN free_lease4 AS f "
+                    "    ON f.address >= q.start_address AND f.address <= q.end_address "
+                    //    WHERE ((q.start <= p_start AND p_start <= q.end) OR "
+                    //           (q.start <= p_end AND p_pend <= q.end) OR "
+                    //           (p_start < q.start AND q.end < p_end))
+                    "    WHERE ((q.start_address <= ? AND ? <= q.end_address) OR "
+                    "           (q.start_address <= ? AND ? <= q.end_address) OR "
+                    "           (? < q.start_address AND q.end_address < ?)) "
+                    "    GROUP BY q.id "
+                    "    ORDER BY q.subnet_id ASC, q.start_address ASC"},
+
+    {MySqlLeaseMgr::SFLQ_POOL4_DELETE,
+                    "DELETE flq_pool4, free_lease4 "
+                    "   FROM flq_pool4 "
+                    "   INNER JOIN free_lease4 "
+                    "   ON free_lease4.address >= start_address "
+                    "       AND free_lease4.address <= end_address "
+                    "   WHERE flq_pool4.start_address = ? AND flq_pool4.end_address = ?"},
+
+    {MySqlLeaseMgr::SFLQ_POOL6_GET_ALL,
+                    "SELECT q.id, q.subnet_id, q.lease_type, "
+                    "       q.start_address, q.end_address, q.delegated_len, "
+                    "       q.created_ts, q.modification_ts, count(f.address) as free_leases "
+                    "    FROM flq_pool6 AS q "
+                    "    LEFT JOIN free_lease6 AS f "
+                    "    ON f.bin_address >= inet6_aton(q.start_address) AND "
+                    "       f.bin_address <= inet6_aton(q.end_address)"
+                    "    GROUP BY q.id "
+                    "    ORDER BY q.subnet_id ASC, inet6_aton(q.start_address) ASC"},
+
+    {MySqlLeaseMgr::SFLQ_POOL6_GET_BY_SUBNET,
+                    "SELECT q.id, q.subnet_id, q.lease_type, "
+                    "       q.start_address, q.end_address, q.delegated_len, "
+                    "       q.created_ts, q.modification_ts, count(f.address) as free_leases "
+                    "    FROM flq_pool6 AS q "
+                    "    LEFT JOIN free_lease6 AS f "
+                    "    ON f.bin_address >= inet6_aton(q.start_address) AND "
+                    "       f.bin_address <= inet6_aton(q.end_address)"
+
+                    "    WHERE q.subnet_id = ? "
+                    "    GROUP BY q.id "
+                    "    ORDER BY q.subnet_id ASC, inet6_aton(q.start_address) ASC"},
+    {MySqlLeaseMgr::SFLQ_POOL6_GET_BY_RANGE,
+                    "SELECT q.id, q.subnet_id, q.lease_type,"
+                    "       q.start_address, q.end_address, q.delegated_len, "
+                    "       q.created_ts, q.modification_ts, count(f.address) as free_leases "
+                    "    FROM flq_pool6 AS q "
+                    "    LEFT JOIN free_lease6 AS f "
+                    "    ON f.bin_address >= inet6_aton(q.start_address) AND "
+                    "       f.bin_address <= inet6_aton(q.end_address)"
+                    "    WHERE ((inet6_aton(q.start_address) <= inet6_aton(?) AND "
+                    "            inet6_aton(?) <= inet6_aton(q.end_address)) OR "
+                    "           (inet6_aton(q.start_address) <= inet6_aton(?) AND "
+                    "            inet6_aton(?) <= inet6_aton(q.end_address)) OR "
+                    "           (inet6_aton(?) < inet6_aton(q.start_address) AND "
+                    "            inet6_aton(q.end_address) < inet6_aton(?))) "
+                    "    GROUP BY q.id "
+                    "    ORDER BY q.subnet_id ASC, inet6_aton(q.start_address) ASC"},
+
+    {MySqlLeaseMgr::SFLQ_POOL6_DELETE,
+                    "DELETE flq_pool6, free_lease6 "
+                    "   FROM flq_pool6 "
+                    "   INNER JOIN free_lease6 "
+                    "   ON free_lease6.address >= start_address "
+                    "       AND free_lease6.address <= end_address "
+                    "   WHERE flq_pool6.start_address = ? AND flq_pool6.end_address = ?"}
 } };  // tagged_statements
 
 }  // namespace
@@ -5041,19 +5137,24 @@ MySqlLeaseMgr::byRemoteId6size() const {
 bool
 MySqlLeaseMgr::sflqCreateFlqPool4(IOAddress start_address, IOAddress end_address,
                                   SubnetID subnet_id, bool recreate) {
-    auto capacity = addrsInRange(start_address, end_address);
-    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL, MYSQL_LB_SFLQ_CREATE_POOL4)
-        .arg(start_address.toText())
-        .arg(end_address.toText())
-        .arg(subnet_id)
-        .arg(recreate)
-        .arg(capacity);
-
-    if (capacity > SharedFlqAllocator::MAX_V4_POOL_SIZE) {
-        isc_throw(BadValue, "MySqlLeasMgr::sflqCreateFlqPool4 pool capacity "
-                            << capacity << " exceeds limit of "
+    // This is clunky but it allows us to log the capacity.
+    try {
+        validateV4Range(start_address, end_address);
+        auto capacity = addrsInRange(start_address, end_address);
+        if (capacity > SharedFlqAllocator::MAX_V4_POOL_SIZE) {
+            isc_throw(BadValue, "pool capacity " << capacity << " exceeds limit of "
                             << SharedFlqAllocator::MAX_V4_POOL_SIZE
                             << " for shared-flq allocator on V4 pool ");
+        }
+
+        LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL, MYSQL_LB_SFLQ_CREATE_POOL4)
+            .arg(start_address.toText())
+            .arg(end_address.toText())
+            .arg(subnet_id)
+            .arg(recreate)
+            .arg(capacity);
+    } catch (const std::exception& ex) {
+        isc_throw(BadValue, "MySqlLeasMgr::sflqCreateFlqPool4 " << ex.what());
     }
 
     // Get a context.
@@ -5098,6 +5199,8 @@ MySqlLeaseMgr::sflqPickFreeLease4(IOAddress start_address, IOAddress end_address
         .arg(start_address.toText())
         .arg(end_address.toText());
 
+    validateV4Range(start_address, end_address);
+
     // Get a context.
     MySqlLeaseContextAlloc get_context(*this);
     MySqlLeaseContextPtr ctx = get_context.ctx_;
@@ -5156,28 +5259,27 @@ bool
 MySqlLeaseMgr::sflqCreateFlqPool6(IOAddress start_address, IOAddress end_address,
                                   Lease::Type lease_type, uint8_t delegated_len,
                                   SubnetID subnet_id, bool recreate) {
-    uint128_t capacity;
-    if (lease_type == Lease::TYPE_PD) {
-        auto prefix_len = prefixLengthFromRange(start_address, end_address);
-        capacity = prefixesInRange(prefix_len, delegated_len);
-    } else {
-        capacity = addrsInRange(start_address, end_address);
-    }
+    // This is clunky but it allows us to log the capacity.
+    try {
+        validateV6Range(start_address, end_address);
+        uint128_t capacity;
+        if (lease_type == Lease::TYPE_PD) {
+            auto prefix_len = prefixLengthFromRange(start_address, end_address);
+            capacity = prefixesInRange(prefix_len, delegated_len);
+        } else {
+            capacity = addrsInRange(start_address, end_address);
+        }
 
-    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL, MYSQL_LB_SFLQ_CREATE_POOL6)
-        .arg(start_address.toText())
-        .arg(end_address.toText())
-        .arg(lease_type)
-        .arg(static_cast<uint16_t>(delegated_len))
-        .arg(subnet_id)
-        .arg(recreate)
-        .arg(capacity);
-
-    if (capacity > SharedFlqAllocator::MAX_V6_POOL_SIZE) {
-        isc_throw(BadValue, "MySqlLeasMgr::sflqCreateFlqPool6 pool capacity "
-                            << capacity << " exceeds limit of "
-                            << SharedFlqAllocator::MAX_V6_POOL_SIZE
-                            << " for shared-flq allocator on V6 pool ");
+        LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL, MYSQL_LB_SFLQ_CREATE_POOL6)
+            .arg(start_address.toText())
+            .arg(end_address.toText())
+            .arg(lease_type)
+            .arg(static_cast<uint16_t>(delegated_len))
+            .arg(subnet_id)
+            .arg(recreate)
+            .arg(capacity);
+    } catch (const std::exception& ex) {
+        isc_throw(BadValue, "MySqlLeasMgr::sflqCreateFlqPool6 " << ex.what());
     }
 
     // Get a context.
@@ -5224,6 +5326,8 @@ MySqlLeaseMgr::sflqPickFreeLease6(IOAddress start_address, IOAddress end_address
         .arg(start_address.toText())
         .arg(end_address.toText());
 
+    validateV6Range(start_address, end_address);
+
     // Get a context.
     MySqlLeaseContextAlloc get_context(*this);
     MySqlLeaseContextPtr ctx = get_context.ctx_;
@@ -5282,6 +5386,241 @@ MySqlLeaseMgr::sflqPickFreeLease6(IOAddress start_address, IOAddress end_address
     return (IOAddress(tmp));
 }
 
+SflqPoolInfoCollectionPtr
+MySqlLeaseMgr::sflqPool4GetAll() {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL, MYSQL_LB_SFLQ_POOL4_GET_ALL);
+
+    // No input parameters.
+    MySqlBindingCollection in_bindings = {};
+
+    return (sflqPoolGetCommon(SFLQ_POOL4_GET_ALL, in_bindings));
+}
+
+SflqPoolInfoCollectionPtr
+MySqlLeaseMgr::sflqPool4Get(SubnetID subnet_id) {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL,
+              MYSQL_LB_SFLQ_POOL4_GET_BY_SUBNET)
+              .arg(subnet_id);
+
+    MySqlBindingCollection in_bindings = {
+        MySqlBinding::createInteger<int64_t>(subnet_id)
+    };
+
+    return (sflqPoolGetCommon(SFLQ_POOL4_GET_BY_SUBNET, in_bindings));
+}
+
+SflqPoolInfoCollectionPtr
+MySqlLeaseMgr::sflqPool4Get(IOAddress start_address, IOAddress end_address) {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL,
+              MYSQL_LB_SFLQ_POOL4_GET_BY_RANGE)
+              .arg(start_address.toText())
+              .arg(end_address.toText());
+
+    validateV4Range(start_address, end_address);
+
+    MySqlBindingCollection in_bindings = {
+        // SQL needs both addresses three times.
+        MySqlBinding::createInteger<uint32_t>(start_address.toUint32()),
+        MySqlBinding::createInteger<uint32_t>(start_address.toUint32()),
+        MySqlBinding::createInteger<uint32_t>(end_address.toUint32()),
+        MySqlBinding::createInteger<uint32_t>(end_address.toUint32()),
+        MySqlBinding::createInteger<uint32_t>(start_address.toUint32()),
+        MySqlBinding::createInteger<uint32_t>(end_address.toUint32())
+    };
+
+    return (sflqPoolGetCommon(SFLQ_POOL4_GET_BY_RANGE, in_bindings));
+}
+
+bool
+MySqlLeaseMgr::sflqPool4Del(IOAddress start_address, IOAddress end_address,
+                            bool force /* = false */) {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL,
+              MYSQL_LB_SFLQ_POOL4_DELETE)
+              .arg(start_address.toText())
+              .arg(end_address.toText())
+              .arg(force ? "true" : "false");
+
+    validateV4Range(start_address, end_address);
+
+    return (sflqPoolDelCommon(start_address, end_address, force, AF_INET));
+}
+
+SflqPoolInfoCollectionPtr
+MySqlLeaseMgr::sflqPool6GetAll() {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL, MYSQL_LB_SFLQ_POOL6_GET_ALL);
+
+    // No input parameters.
+    MySqlBindingCollection in_bindings = {};
+
+    return (sflqPoolGetCommon(SFLQ_POOL6_GET_ALL, in_bindings, AF_INET6));
+}
+
+SflqPoolInfoCollectionPtr
+MySqlLeaseMgr::sflqPool6Get(SubnetID subnet_id) {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL,
+              MYSQL_LB_SFLQ_POOL6_GET_BY_SUBNET)
+              .arg(subnet_id);
+
+    MySqlBindingCollection in_bindings = {
+        MySqlBinding::createInteger<int64_t>(subnet_id)
+    };
+
+    return (sflqPoolGetCommon(SFLQ_POOL6_GET_BY_SUBNET, in_bindings, AF_INET6));
+}
+
+SflqPoolInfoCollectionPtr
+MySqlLeaseMgr::sflqPool6Get(IOAddress start_address, IOAddress end_address) {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL,
+              MYSQL_LB_SFLQ_POOL6_GET_BY_RANGE)
+              .arg(start_address.toText())
+              .arg(end_address.toText());
+
+    validateV6Range(start_address, end_address);
+
+    MySqlBindingCollection in_bindings = {
+        // SQL needs both addresses three times.
+        MySqlBinding::createString(start_address.toText()),
+        MySqlBinding::createString(start_address.toText()),
+        MySqlBinding::createString(end_address.toText()),
+        MySqlBinding::createString(end_address.toText()),
+        MySqlBinding::createString(start_address.toText()),
+        MySqlBinding::createString(end_address.toText())
+    };
+
+    return (sflqPoolGetCommon(SFLQ_POOL6_GET_BY_RANGE, in_bindings, AF_INET6));
+}
+
+bool
+MySqlLeaseMgr::sflqPool6Del(IOAddress start_address, IOAddress end_address,
+                            bool force /* = false */) {
+    LOG_DEBUG(mysql_lb_logger, MYSQL_LB_DBG_TRACE_DETAIL,
+              MYSQL_LB_SFLQ_POOL6_DELETE)
+              .arg(start_address.toText())
+              .arg(end_address.toText())
+              .arg(force ? "true" : "false");
+
+    validateV6Range(start_address, end_address);
+
+    return (sflqPoolDelCommon(start_address, end_address, force, AF_INET6));
+}
+
+SflqPoolInfoCollectionPtr
+MySqlLeaseMgr::sflqPoolGetCommon(StatementIndex stindex,
+                                 MySqlBindingCollection& where_bindings,
+                                 uint16_t family /* = AF_INET*/ ) {
+    // Get a context.
+    MySqlLeaseContextAlloc get_context(*this);
+    MySqlLeaseContextPtr ctx = get_context.ctx_;
+
+    // Create the output bindings. Start with common columns.
+    MySqlBindingCollection out_bindings = {
+        MySqlBinding::createInteger<uint64_t>(), // db pool id
+        MySqlBinding::createInteger<uint32_t>(), // subnet_id
+        MySqlBinding::createInteger<uint32_t>(), // lease_type
+    };
+
+    // Type specific start and end address.
+    if (family == AF_INET) {
+        out_bindings.push_back(MySqlBinding::createInteger<uint32_t>());
+        out_bindings.push_back(MySqlBinding::createInteger<uint32_t>());
+    } else {
+        out_bindings.push_back(MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH));
+        out_bindings.push_back(MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH));
+    }
+
+    // Add in remaining common columns.
+    out_bindings.push_back(MySqlBinding::createInteger<uint32_t>());  // delegated_len
+    out_bindings.push_back(MySqlBinding::createTimestamp());          // created_ts
+    out_bindings.push_back(MySqlBinding::createTimestamp());          // modification_ts
+    out_bindings.push_back(MySqlBinding::createInteger<uint64_t>());  // free leases
+
+    SflqPoolInfoCollectionPtr pools(new SflqPoolInfoCollection());
+
+    ctx->conn_.selectQuery(stindex, where_bindings, out_bindings,
+                           [this, &pools]
+                           (MySqlBindingCollection& out_bindings) {
+
+        SflqPoolInfoPtr info(new SflqPoolInfo());
+        // db pool id is 0, we skip it
+        info->subnet_id_ = out_bindings[1]->getInteger<uint32_t>();
+        auto lease_type_ = out_bindings[2]->getInteger<uint32_t>();
+        switch(lease_type_) {
+        case Lease::TYPE_V4:
+            info->lease_type_ = Lease::TYPE_V4;
+            break;
+        case Lease::TYPE_NA:
+            info->lease_type_ = Lease::TYPE_NA;
+            break;
+        case Lease::TYPE_PD:
+            info->lease_type_ = Lease::TYPE_PD;
+            break;
+        default:
+            isc_throw(BadValue, "invalid pool lease type returned " <<
+                      static_cast<int>(lease_type_));
+        }
+
+        if (lease_type_ == Lease::TYPE_V4) {
+            info->start_address_ = IOAddress(out_bindings[3]->getInteger<uint32_t>());
+            info->end_address_ = IOAddress(out_bindings[4]->getInteger<uint32_t>());
+        } else {
+            info->start_address_ = IOAddress(out_bindings[3]->getString());
+            info->end_address_ = IOAddress(out_bindings[4]->getString());
+        }
+
+        info->delegated_len_ = out_bindings[5]->getInteger<uint32_t>();
+        info->created_ts_ = out_bindings[6]->getTimestamp();
+        info->modified_ts_ = out_bindings[7]->getTimestamp();
+        info->free_leases_ = out_bindings[8]->getInteger<uint64_t>();
+        pools->push_back(info);
+    });
+
+    return (pools);
+}
+
+bool
+MySqlLeaseMgr::sflqPoolDelCommon(IOAddress start_address, IOAddress end_address,
+                                 bool force, uint16_t family ) {
+    // If force is false check for overlapping pools.
+    if (!force) {
+        auto pools_in_range = (family == AF_INET ? sflqPool4Get(start_address, end_address)
+                                                 : sflqPool6Get(start_address, end_address));
+        auto count = pools_in_range->size();
+        if (count == 0) {
+            // Nothing to do.
+            return (false);
+        }
+
+        if (count > 1) {
+            // Overlapping pools, warn and bail.
+            isc_throw(InvalidOperation, "Delete would affect "
+                      << count << " overlapping pools");
+        }
+    }
+
+    // Get a context.
+    MySqlLeaseContextAlloc get_context(*this);
+    MySqlLeaseContextPtr ctx = get_context.ctx_;
+
+    // Make where clause bindings.
+    MySqlBindingCollection in_bindings;
+    StatementIndex stindex;
+    if (family == AF_INET) {
+        in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(start_address.toUint32()));
+        in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(end_address.toUint32()));
+        stindex = SFLQ_POOL4_DELETE;
+    } else {
+        in_bindings.push_back(MySqlBinding::createString(start_address.toText()));
+        in_bindings.push_back(MySqlBinding::createString(end_address.toText()));
+        stindex = SFLQ_POOL6_DELETE;
+    }
+
+    ScopedMySqlTransactionPtr trans(new MySqlTransaction(ctx->conn_));
+    auto affected_rows = ctx->conn_.updateDeleteQuery(stindex, in_bindings);
+    trans->commit();
+
+    return(affected_rows > 0);
+}
+
 TrackingLeaseMgrPtr
 MySqlLeaseMgr::factory(const isc::db::DatabaseConnection::ParameterMap& parameters) {
     LOG_INFO(mysql_lb_logger, MYSQL_LB_DB)
index ee5ceeb154aed630a0c371f7346f1b4636be8352..80dff3b7ebe10c8af56b41c5d69a0f6b6dcfc3cd 100644 (file)
@@ -856,6 +856,14 @@ public:
         SFLQ_INSERT_LEASE6,          // SFLQ Alternate for inserting v6 lease
         SFLQ_UPDATE_LEASE6,          // SFLQ Alternate for updating v6 lease
         SFLQ_DELETE_LEASE6,          // SFLQ Alternate for deleting v6 lease
+        SFLQ_POOL4_GET_ALL,          // SFLQ Fetch all v4 shared flq pools
+        SFLQ_POOL4_GET_BY_SUBNET,    // SFLQ Fetch all v4 shared flq pools for a subnet
+        SFLQ_POOL4_GET_BY_RANGE,     // SFLQ Fetch v4 shared flq pools that overlap the given range
+        SFLQ_POOL4_DELETE,           // SFLQ Delete v4 shared flq pool for an address range
+        SFLQ_POOL6_GET_ALL,          // SFLQ Fetch all v6 shared flq pools
+        SFLQ_POOL6_GET_BY_SUBNET,    // SFLQ Fetch all v6 shared flq pools for a subnet
+        SFLQ_POOL6_GET_BY_RANGE,     // SFLQ Fetch v4 shared flq pool that overlap the address range
+        SFLQ_POOL6_DELETE,           // SFLQ Delete v4 shared flq pool for an address range
         NUM_STATEMENTS               // Number of statements
     };
 
@@ -1329,7 +1337,125 @@ public:
     /// @return A free V6 address/prefix or IOAddress::IPV6_ZERO_ADDRESS().
     virtual asiolink::IOAddress sflqPickFreeLease6(asiolink::IOAddress start_address,
                                                    asiolink::IOAddress end_address) override;
+    /// @brief Fetch all SFLQ V4 pools.
+    ///
+    /// @return A collection of the SFLQ V4 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool4GetAll() override;
+
+    /// @brief Fetch all SFLQ V4 pools belonging to a subnet.
+    ///
+    /// @param subnet_id id of the desired subnet.
+    ///
+    /// @return A collection of the SFLQ V4 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool4Get(SubnetID subnet_id) override;
+
+    /// @brief Fetch all SFLQ V4 pools that overlap an address range.
+    ///
+    /// Since overlapping pools are supported, this function returns all V4
+    /// SFLQ pools whose range overlaps the given range.
+    ///
+    /// @param start_address range start address
+    /// @param end_address range end address
+    ///
+    /// @return A collection of the SFLQ V4 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool4Get(asiolink::IOAddress start_address,
+                                                   asiolink::IOAddress end_address)
+                                                   override;
+
+    /// @brief Delete the SFLQ V4 pool that matches a start and end address.
+    ///
+    /// Deletes the flq_pool4 entry along with its free_lease4 data.
+    /// Fails If there are multiple pools that overalap the given range 
+    /// unless force is true.
+    ///
+    /// @param start_address start address of the pool to delete.
+    /// @param end_address end address of the pool to delete.
+    /// @param force overrides check for overlapping pools when true. Defaults
+    /// to false. 
+    ///
+    /// @return True a pool was deleted.
+    /// @throw InvalidOperation if force is false and overlapping pools are
+    /// detected.
+    virtual bool sflqPool4Del(asiolink::IOAddress start_address,
+                              asiolink::IOAddress end_address,
+                              bool force = false) override;
+
+    /// @brief Fetch all SFLQ V6 pools.
+    ///
+    /// @return A collection of the SFLQ V6 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool6GetAll() override;
+
+    /// @brief Fetch all SFLQ V6 pools belonging to a subnet.
+    ///
+    /// @param subnet_id id of the desired subnet.
+    ///
+    /// @return A collection of the SFLQ V6 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool6Get(SubnetID subnet_id) override;
+
+    /// @brief Fetch all SFLQ V6 pools that overlap an address range.
+    ///
+    /// Since overlapping pools are supported, this function returns all V4
+    /// SFLQ pools whose range overlaps the given range.
+    ///
+    /// @param start_address range start address
+    /// @param end_address range end address
+    ///
+    /// @return A collection of the SFLQ V6 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool6Get(asiolink::IOAddress start_address,
+                                                   asiolink::IOAddress end_address)
+                                                   override;
+    /// @brief Delete the SFLQ V6 pool that matches a start and end address.
+    ///
+    /// Deletes the flq_pool6 entry along with its free_lease6 data.
+    /// Fails If there are multiple pools that overalap the given range 
+    /// unless force is true.
+    ///
+    /// @param start_address start address of the pool to delete.
+    /// @param end_address end address of the pool to delete.
+    /// @param force overrides check for overlapping pools when true. Defaults
+    /// to false. 
+    ///
+    /// @return True a pool was deleted.
+    /// @throw InvalidOperation if force is false and overlapping pools are
+    /// detected.
+    virtual bool sflqPool6Del(asiolink::IOAddress start_address,
+                              asiolink::IOAddress end_address,
+                              bool force = false) override;
+
+
 private:
+    /// @brief Fetch SFLQ pools based on statement index and option where clause.
+    ///
+    /// Common function used for all variants. Requires that all selects return
+    /// values for all the columns and in the same order.
+    ///
+    /// @param stindex index of the SQL statement to execute.
+    /// @param where_bindings input bindings holding the where clause parameter
+    /// values (if any)
+    /// @param family protocl family AF_INET or AF_INET6
+    ///
+    /// @return A collection of the SFLQ V4 pools.
+    SflqPoolInfoCollectionPtr sflqPoolGetCommon(StatementIndex stindex,
+                                                db::MySqlBindingCollection& where_bindings,
+                                                uint16_t family = AF_INET);
+
+    /// @brief Delete the SFLQ pool that matches a start and end address.
+    ///
+    /// Deletes the flq_poolX entry along with its free_leaseX data.
+    /// Fails If there are multiple pools that overalap the given range 
+    /// unless force is true.
+    ///
+    /// @param start_address start address of the pool to delete.
+    /// @param end_address end address of the pool to delete.
+    /// @param force overrides check for overlapping pools when true.
+    /// @param family protocl family AF_INET or AF_INET6
+    ///
+    /// @return True a pool was deleted.
+    /// @throw InvalidOperation if force is false and overlapping pools are
+    /// detected.
+    bool sflqPoolDelCommon(asiolink::IOAddress start_address, asiolink::IOAddress end_address,
+                           bool force, uint16_t family);
+
     /// @brief Context RAII allocator.
     class MySqlLeaseContextAlloc {
     public:
index 8c41feabc446c126cb498bfd15e28038c92ee7c7..c8ff5b9efdd923d81ba2577ef012abe6c3797692 100644 (file)
@@ -1462,6 +1462,30 @@ TEST_F(MySqlLeaseMgrTest, testSflqLeaseOps6PD) {
     testSflqLeaseOps6(Lease::TYPE_PD);
 }
 
+TEST_F(MySqlLeaseMgrTest, testSflqAPIFuncs4) {
+    testSflqAPIFuncs4();
+}
+
+TEST_F(MySqlLeaseMgrTest, testSflqAPIFuncs6NA) {
+    testSflqAPIFuncs6(Lease::TYPE_NA);
+}
+
+TEST_F(MySqlLeaseMgrTest, testSflqAPIFuncs6PD) {
+    testSflqAPIFuncs6(Lease::TYPE_PD);
+}
+
+TEST_F(MySqlLeaseMgrTest, testSflqAPIOverlappingPools4) {
+    testSflqAPIOverlappingPools4();
+}
+
+TEST_F(MySqlLeaseMgrTest, testSflqAPIOverlappingPools6NA) {
+    testSflqAPIOverlappingPools6(Lease::TYPE_NA);
+}
+
+TEST_F(MySqlLeaseMgrTest, testSflqAPIOverlappingPools6PD) {
+    testSflqAPIOverlappingPools6(Lease::TYPE_PD);
+}
+
 /// @brief Test fixture class for testing @ref CfgDbAccessTest using MySQL
 /// backend.
 class CfgMySqlLbDbAccessTest : public ::testing::Test {
index f3ed7786dd81d6dd5fd636231fda739a9da7eac2..36bef93da9cb488ea2ffa44f6a19737bfb1fa902 100644 (file)
@@ -1426,6 +1426,30 @@ TEST_F(PgSqlLeaseMgrTest, testSflqLeaseOps6PD) {
     testSflqLeaseOps6(Lease::TYPE_PD);
 }
 
+TEST_F(PgSqlLeaseMgrTest, testSflqAPIFuncs4) {
+    testSflqAPIFuncs4();
+}
+
+TEST_F(PgSqlLeaseMgrTest, testSflqAPIFuncs6NA) {
+    testSflqAPIFuncs6(Lease::TYPE_NA);
+}
+
+TEST_F(PgSqlLeaseMgrTest, testSflqAPIFuncs6PD) {
+    testSflqAPIFuncs6(Lease::TYPE_PD);
+}
+
+TEST_F(PgSqlLeaseMgrTest, testSflqAPIOverlappingPools4) {
+    testSflqAPIOverlappingPools4();
+}
+
+TEST_F(PgSqlLeaseMgrTest, testSflqAPIOverlappingPools6NA) {
+    testSflqAPIOverlappingPools6(Lease::TYPE_NA);
+}
+
+TEST_F(PgSqlLeaseMgrTest, testSflqAPIOverlappingPools6PD) {
+    testSflqAPIOverlappingPools6(Lease::TYPE_PD);
+}
+
 /// @brief Test fixture class for testing @ref CfgDbAccessTest using PostgreSQL
 /// backend.
 class CfgPgSqlLbDbAccessTest : public ::testing::Test {
index 069ccefcf364fc8b813549e1500f94c7ba9b9245..99bae1917eec1a06840399d4fe853072325fc72d 100644 (file)
@@ -411,5 +411,21 @@ IOAddress offsetAddress(const IOAddress& addr, uint128_t offset) {
     return (IOAddress::fromBytes(AF_INET6, &addr_bytes[0]));
 }
 
+void validateV4Range(const IOAddress& start, const IOAddress& end) {
+    if (!start.isV4() || !end.isV4() || end < start) {
+        isc_throw (BadValue, "invalid V4 range - start_address "
+                   << start.toText() << "r, end_address " << end.toText()
+                   << ", must be V4 addresses where start <= end");
+    }
+}
+
+void validateV6Range(const IOAddress& start, const IOAddress& end) {
+    if (!start.isV6() || !end.isV6() || end < start) {
+        isc_throw (BadValue, "invalid V6 range - start_address "
+                   << start.toText() << "r, end_address " << end.toText()
+                   << ", must be V6 addresses where start <= end");
+    }
+}
+
 }
 }
index 87f356c7bb77de3360245883bd662988dd37643d..673b95da0df71c65257fc0679d6d2854adbcc867 100644 (file)
@@ -93,6 +93,22 @@ isc::util::uint128_t prefixesInRange(const uint8_t pool_len, const uint8_t deleg
 /// @return address being offset greater than the input address
 IOAddress offsetAddress(const IOAddress& addr, isc::util::uint128_t offset);
 
+/// @brief Ensures address pair are both v4 and start <= end
+///
+/// @param addr input address
+/// @param addr input address
+///
+/// @throw BadValue if either address is not v4 or start > end
+void validateV4Range(const IOAddress& start, const IOAddress& end);
+
+/// @brief Ensures address pair are both v6 and start <= end
+///
+/// @param addr input address
+/// @param addr input address
+///
+/// @throw BadValue if either address is not v6 or start > end
+void validateV6Range(const IOAddress& start, const IOAddress& end);
+
 }  // namespace asiolink
 }  // namespace isc
 
index 461b26c762edb2407498179e5d666e37e844e9c3..539f9622fe185d4c8446e70e00a3978009317673 100644 (file)
@@ -17,6 +17,7 @@
 #include <stats/stats_mgr.h>
 #include <util/encode/encode.h>
 #include <util/str.h>
+#include <util/boost_time_utils.h>
 
 #include <boost/algorithm/string.hpp>
 
@@ -1962,6 +1963,31 @@ LeaseMgr::updateStatsOnDelete(const Lease6Ptr& lease) {
     }
 }
 
+SflqPoolInfo::SflqPoolInfo():
+    lease_type_(Lease::TYPE_V4),
+    start_address_(IOAddress::IPV4_ZERO_ADDRESS()),
+    end_address_(IOAddress::IPV4_ZERO_ADDRESS()),
+    delegated_len_(128),
+    subnet_id_(0),
+    free_leases_(0),
+    created_ts_(),
+    modified_ts_() {
+}
+
+
+data::ConstElementPtr
+SflqPoolInfo::toElement() const {
+    ElementPtr info = Element::createMap();
+    info->set("lease-type", Element::create(Lease::typeToText(lease_type_)));
+    info->set("start-address", Element::create(start_address_.toText()));
+    info->set("end-address", Element::create(end_address_.toText()));
+    info->set("delegated-len", Element::create(delegated_len_));
+    info->set("subnet-id", Element::create(subnet_id_));
+    info->set("free-leases", ElementPtr(new IntElement(free_leases_)));
+    info->set("created-ts", Element::create(isc::util::ptimeToText(created_ts_)));
+    info->set("modified-ts", Element::create(isc::util::ptimeToText(modified_ts_)));
+    return(info);
+}
 
 bool
 LeaseMgr::sflqCreateFlqPool4(IOAddress, IOAddress, SubnetID, bool) {
@@ -1983,6 +2009,46 @@ LeaseMgr::sflqPickFreeLease6(IOAddress, IOAddress) {
     isc_throw(NotImplemented, "LeaseMgr::sflqPickFreeLease6() called");
 }
 
+SflqPoolInfoCollectionPtr
+LeaseMgr::sflqPool4GetAll() {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool4GetAll() called");
+}
+
+SflqPoolInfoCollectionPtr
+LeaseMgr::sflqPool4Get(SubnetID /* subnet_id */) {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool4Get(SubnetID) called");
+}
+
+SflqPoolInfoCollectionPtr
+LeaseMgr::sflqPool4Get(asiolink::IOAddress, asiolink::IOAddress) {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool4Get(IOAddress,IOAddress) called");
+}
+
+bool
+LeaseMgr::sflqPool4Del(asiolink::IOAddress, asiolink::IOAddress, bool) {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool4Del() called");
+}
+
+SflqPoolInfoCollectionPtr
+LeaseMgr::sflqPool6GetAll() {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool6GetAll() called");
+}
+
+SflqPoolInfoCollectionPtr
+LeaseMgr::sflqPool6Get(SubnetID) {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool6Get(SubnetID) called");
+}
+
+SflqPoolInfoCollectionPtr
+LeaseMgr::sflqPool6Get(asiolink::IOAddress, asiolink::IOAddress) {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool6Get(IOAdress,IOAddress) called");
+}
+
+bool
+LeaseMgr::sflqPool6Del(asiolink::IOAddress, asiolink::IOAddress, bool) {
+    isc_throw(NotImplemented, "LeaseMgr::sflqPool6Del() called");
+}
+
 bool
 LeaseMgr::useSharedFlqStatement(Lease4Ptr lease) {
     // Only check the subnet if SFLQ is in-use in this config.
index 17f82955a50a052c195c2cfe62f1305fb8426fa8..b473b0228b44ff1160ba200db110e70aab20e175 100644 (file)
@@ -235,6 +235,33 @@ typedef boost::shared_ptr<LeaseStatsQuery> LeaseStatsQueryPtr;
 /// @brief Defines a pointer to a LeaseStatsRow.
 typedef boost::shared_ptr<LeaseStatsRow> LeaseStatsRowPtr;
 
+/// @brief Describes a SFLQ pool.
+class SflqPoolInfo {
+public:
+    SflqPoolInfo();
+
+    ~SflqPoolInfo(){};
+    
+    Lease::Type lease_type_;
+    asiolink::IOAddress start_address_;
+    asiolink::IOAddress end_address_;
+    uint8_t delegated_len_;
+    SubnetID subnet_id_;
+    uint64_t free_leases_;
+    boost::posix_time::ptime created_ts_;
+    boost::posix_time::ptime modified_ts_;
+
+    data::ConstElementPtr toElement() const;
+};
+
+/// @brief A pointer to a SFLQPoolInfo instance.
+typedef boost::shared_ptr<SflqPoolInfo> SflqPoolInfoPtr;
+
+/// @brief A collection of SFLQPoolInfo structures.
+typedef std::vector<SflqPoolInfoPtr> SflqPoolInfoCollection;
+typedef boost::shared_ptr<SflqPoolInfoCollection> SflqPoolInfoCollectionPtr;
+
+
 /// @brief Abstract Lease Manager
 ///
 /// This is an abstract API for lease database backends. It provides unified
@@ -1152,6 +1179,90 @@ public:
     virtual asiolink::IOAddress sflqPickFreeLease6(asiolink::IOAddress start_address,
                                                    asiolink::IOAddress end_address);
 
+    /// @brief Fetch all SFLQ V4 pools.
+    ///
+    /// @return A collection of the SFLQ V4 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool4GetAll();
+
+    /// @brief Fetch all SFLQ V4 pools belonging to a subnet.
+    ///
+    /// @param subnet_id id of the desired subnet. 
+    ///
+    /// @return A collection of the SFLQ V4 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool4Get(SubnetID subnet_id);
+
+    /// @brief Fetch all SFLQ V4 pools that overlap an address range.
+    ///
+    /// Since overlapping pools are supported, this function returns all V4
+    /// SFLQ pools whose range overlaps the given range.
+    ///
+    /// @param start_address range start address
+    /// @param end_address range end address
+    ///
+    /// @return A collection of the SFLQ V4 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool4Get(asiolink::IOAddress start_address,
+                                                   asiolink::IOAddress end_address);
+
+    /// @brief Delete the SFLQ V4 pool that matches a start and end address.
+    ///
+    /// Deletes the flq_pool4 entry along with its free_lease4 data.
+    /// Fails If there are multiple pools that overalap the given range 
+    /// unless force is true.
+    ///
+    /// @param start_address start address of the pool to delete.
+    /// @param end_address end address of the pool to delete.
+    /// @param force overrides check for overlapping pools when true. Defaults
+    /// to false. 
+    ///
+    /// @return True a pool was deleted.
+    /// @throw InvalidOperation if force is false and overlapping pools are
+    /// detected.
+    virtual bool sflqPool4Del(asiolink::IOAddress start_address,
+                              asiolink::IOAddress end_address,
+                              bool force = false);
+
+    /// @brief Fetch all SFLQ V6 pools.
+    ///
+    /// @return A collection of the SFLQ V6 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool6GetAll();
+
+    /// @brief Fetch all SFLQ V6 pools belonging to a subnet.
+    ///
+    /// @param subnet_id id of the desired subnet. 
+    ///
+    /// @return A collection of the SFLQ V6 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool6Get(SubnetID subnet_id);
+
+    /// @brief Fetch all SFLQ V6 pools that overlap an address range.
+    ///
+    /// Since overlapping pools are supported, this function returns all V6
+    /// SFLQ pools whose range overlaps the given range.
+    ///
+    /// @param start_address range start address
+    /// @param end_address range end address
+    ///
+    /// @return A collection of the SFLQ V6 pools.
+    virtual SflqPoolInfoCollectionPtr sflqPool6Get(asiolink::IOAddress start_address,
+                                                   asiolink::IOAddress end_address);
+
+    /// @brief Delete the SFLQ V6 pool that matches a start and end address.
+    ///
+    /// Deletes the flq_pool6 entry along with its free_lease6 data.
+    /// Fails If there are multiple pools that overalap the given range 
+    /// unless force is true.
+    ///
+    /// @param start_address start address of the pool to delete.
+    /// @param end_address end address of the pool to delete.
+    /// @param force overrides check for overlapping pools when true. Defaults
+    /// to false.
+    ///
+    /// @return True a pool was deleted.
+    /// @throw InvalidOperation if force is false and overlapping pools are
+    /// detected.
+    virtual bool sflqPool6Del(asiolink::IOAddress start_address,
+                              asiolink::IOAddress end_address,
+                              bool force = false);
+
     /// @brief Determine if SFLQ alternate SQL statements should be used
     /// for a given v4 lease
     ///
index 2e8475e957b10a6c4b60f5e316c6f5d6eff2133d..9a438ad6d206ab60f50d1dcce9d38d8cf4988d37 100644 (file)
@@ -13,6 +13,7 @@
 #include <dhcpsrv/testutils/concrete_lease_mgr.h>
 #include <dhcpsrv/testutils/generic_lease_mgr_unittest.h>
 #include <testutils/gtest_utils.h>
+#include <util/boost_time_utils.h>
 
 #include <iostream>
 #include <list>
@@ -1045,6 +1046,34 @@ TEST(Lease6ExtendedInfoTest, twoSetExtendedInfoTablesEnabled) {
     EXPECT_EQ(exp_remote_id, remote_id);
 }
 
+// Verifies SfqlPoolInfo::toElement() function.
+TEST(SflqPoolInfo, toElement) {
+    SflqPoolInfo info;
+    info.lease_type_ = Lease::TYPE_V4;
+    info.start_address_ = IOAddress("1.2.3.4");
+    info.end_address_ = IOAddress("1.2.3.5");
+    info.delegated_len_ = 128;
+    info.subnet_id_ = 77;
+    info.free_leases_ = 245;
+    auto now = boost::posix_time::second_clock::local_time();
+    info.created_ts_ = now;
+    info.modified_ts_ = now;
+
+    auto info_elem = info.toElement();
+
+    ElementPtr expected = Element::createMap();
+    expected->set("lease-type", Element::create(Lease::typeToText(info.lease_type_)));
+    expected->set("start-address", Element::create(info.start_address_.toText()));
+    expected->set("end-address", Element::create(info.end_address_.toText()));
+    expected->set("delegated-len", Element::create(info.delegated_len_));
+    expected->set("subnet-id", Element::create(info.subnet_id_));
+    expected->set("free-leases", ElementPtr(new IntElement(info.free_leases_)));
+    expected->set("created-ts", Element::create(isc::util::ptimeToText(info.created_ts_)));
+    expected->set("modified-ts", Element::create(isc::util::ptimeToText(info.modified_ts_)));
+
+    EXPECT_TRUE(info_elem->equals(*expected));
+}
+
 // 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.
index d1e70d4cc047fc0734a859a46fcf3aa6c30e036f..7f07bdb68a72639f9255611cd047c979d29898a3 100644 (file)
@@ -429,6 +429,24 @@ GenericLeaseMgrTest::createLeases6() {
     return (leases);
 }
 
+void
+GenericLeaseMgrTest::checkPoolInfos(const SflqPoolInfo& lhs,
+                                      const SflqPoolInfo& rhs,
+                                      int lineno) {
+
+    ASSERT_TRUE(lhs.lease_type_ == rhs.lease_type_ &&
+                lhs.start_address_  == rhs.start_address_ &&
+                lhs.end_address_ == rhs.end_address_ &&
+                lhs.delegated_len_ == rhs.delegated_len_ &&
+                lhs.subnet_id_ == rhs.subnet_id_ &&
+                lhs.free_leases_ == rhs.free_leases_ &&
+                lhs.created_ts_ >= rhs.created_ts_ &&
+                lhs.modified_ts_ >= rhs.modified_ts_)
+        << "Pools don't match at line " << lineno << std::endl
+        << "lhs: " << *lhs.toElement() << std::endl
+        << "rhs: " << *rhs.toElement() << std::endl;
+}
+
 void
 GenericLeaseMgrTest::testGetLease4ClientId() {
     // Let's initialize a specific lease ...
@@ -6103,6 +6121,474 @@ GenericLeaseMgrTest::testSflqLeaseOps6(Lease::Type lease_type) {
     }
 }
 
+void
+GenericLeaseMgrTest::testSflqAPIFuncs4() {
+    SflqPoolInfoCollectionPtr pool_infos;
+
+    // Fetching all pools should find none.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4GetAll());
+    ASSERT_EQ(0, pool_infos->size());
+
+    auto test_start = boost::posix_time::second_clock::local_time();
+
+    // Create three test pools.
+    SflqPoolInfoCollection test_pools;
+    SflqPoolInfoPtr pi;
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("192.0.3.0");
+    pi->end_address_ = IOAddress("192.0.3.2");
+    pi->subnet_id_ = 1;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 3;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("192.0.2.0");
+    pi->end_address_ = IOAddress("192.0.2.2");
+    pi->subnet_id_ = 2;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 3;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("192.0.1.0");
+    pi->end_address_ = IOAddress("192.0.1.2");
+    pi->subnet_id_ = 1;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 3;
+    test_pools.push_back(pi);
+
+    for ( auto const& test_pool : test_pools) {
+        ASSERT_NO_THROW_LOG(
+            lmptr_->sflqCreateFlqPool4(test_pool->start_address_,
+                                       test_pool->end_address_,
+                                       test_pool->subnet_id_, false));
+    }
+
+    // Fetching all pools should find none.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4GetAll());
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(3, pool_infos->size());
+
+    // Should get them back ordered by subnet and start address.
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[2], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[2], *test_pools[1], __LINE__);
+
+    // Fetch by subnet id for subnet_id = 1.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(1));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(2, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[2], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[0], __LINE__);
+
+    // Fetch by subnet id for subnet_id = 2.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(2));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(1, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[1], __LINE__);
+
+    // Fetch by subnet id for subnet_id = 99
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(99));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(0, pool_infos->size());
+
+    // Fetch by a range that excludes them all.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(IOAddress("1.2.3.4"),
+                                                          IOAddress("1.2.3.4")));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(0, pool_infos->size());
+
+    // Fetch by a range that includes them all.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(IOAddress("192.0.0.0"),
+                                                          IOAddress("192.0.4.0")));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(3, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[2], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[2], *test_pools[1], __LINE__);
+
+    // Fetch each by exact range match.
+    for ( auto const& test_pool : test_pools) {
+        pool_infos.reset();
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(test_pool->start_address_,
+                                                              test_pool->end_address_));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(1, pool_infos->size());
+        checkPoolInfos(*(*pool_infos)[0], *test_pool, __LINE__);
+    }
+
+    // Fetch each by overlapping the pool end.
+    for ( auto const& test_pool : test_pools) {
+        pool_infos.reset();
+        auto start_address = IOAddress::increase(test_pool->start_address_);
+        auto end_address = IOAddress::increase(test_pool->end_address_);
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(start_address, end_address));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(1, pool_infos->size());
+        checkPoolInfos(*(*pool_infos)[0], *test_pool, __LINE__);
+    }
+
+    // Fetch each by overlapping pool start.
+    for ( auto const& test_pool : test_pools) {
+        pool_infos.reset();
+
+        auto start_address = IOAddress(test_pool->start_address_.toUint32() - 1);
+        auto end_address = IOAddress(test_pool->end_address_.toUint32() - 1);
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(start_address, end_address));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(1, pool_infos->size());
+        checkPoolInfos(*(*pool_infos)[0], *test_pool, __LINE__);
+    }
+
+    // No match, no delete.
+    bool deleted;
+    ASSERT_NO_THROW_LOG(deleted = lmptr_->sflqPool4Del(IOAddress("1.2.3.4"),
+                                                       IOAddress("1.2.3.4")));
+    ASSERT_FALSE(deleted);
+
+    // Delete each pool.
+    for ( auto const& test_pool : test_pools) {
+        // Delete the pool.
+        ASSERT_NO_THROW_LOG(deleted = lmptr_->sflqPool4Del(test_pool->start_address_,
+                                                              test_pool->end_address_));
+        ASSERT_TRUE(deleted);
+
+        // Verify it's no longer there.
+        pool_infos.reset();
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(test_pool->start_address_,
+                                                              test_pool->end_address_));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(0, pool_infos->size());
+    }
+}
+
+void
+GenericLeaseMgrTest::testSflqAPIFuncs6(Lease::Type lease_type) {
+    SflqPoolInfoCollectionPtr pool_infos;
+
+    // Fetching all pools should find none.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6GetAll());
+    ASSERT_EQ(0, pool_infos->size());
+
+    auto test_start = boost::posix_time::second_clock::local_time();
+
+    // Create three test pools.
+    SflqPoolInfoCollection test_pools;
+    SflqPoolInfoPtr pi;
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("3001::30");
+    pi->end_address_ = IOAddress("3001::32");
+    pi->subnet_id_ = 1;
+    pi->lease_type_ = lease_type;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 3;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("3001::20");
+    pi->end_address_ = IOAddress("3001::22");
+    pi->subnet_id_ = 2;
+    pi->lease_type_ = lease_type;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 3;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("3001::10");
+    pi->end_address_ = IOAddress("3001::12");
+    pi->subnet_id_ = 1;
+    pi->lease_type_ = lease_type;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 3;
+    test_pools.push_back(pi);
+
+    for ( auto const& test_pool : test_pools) {
+        ASSERT_NO_THROW_LOG(
+            lmptr_->sflqCreateFlqPool6(test_pool->start_address_,
+                                       test_pool->end_address_,
+                                       test_pool->lease_type_,
+                                       test_pool->delegated_len_,
+                                       test_pool->subnet_id_, false));
+    }
+
+    // Fetching all pools should find none.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6GetAll());
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(3, pool_infos->size());
+
+    // Should get them back ordered by subnet and start address.
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[2], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[2], *test_pools[1], __LINE__);
+
+    // Fetch by subnet id for subnet_id = 1.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(1));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(2, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[2], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[0], __LINE__);
+
+    // Fetch by subnet id for subnet_id = 2.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(2));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(1, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[1], __LINE__);
+
+    // Fetch by subnet id for subnet_id = 99
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(99));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(0, pool_infos->size());
+
+    // Fetch by a range that excludes them all.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(IOAddress("2001::1"),
+                                                          IOAddress("2001::2")));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(0, pool_infos->size());
+
+    // Fetch by a range that includes them all.
+    pool_infos.reset();
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(IOAddress("3001::"),
+                                                          IOAddress("3001::FF")));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(3, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[2], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[2], *test_pools[1], __LINE__);
+
+    // Fetch each by exact range match.
+    for ( auto const& test_pool : test_pools) {
+        pool_infos.reset();
+        auto start_address = test_pool->start_address_;
+        auto end_address = test_pool->end_address_;
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(start_address.toText(),
+                                                              end_address.toText()));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(1, pool_infos->size()) << start_address.toText() << " - " << end_address.toText();
+        checkPoolInfos(*(*pool_infos)[0], *test_pool, __LINE__);
+    }
+
+    // Fetch each by overlapping the pool end.
+    for ( auto const& test_pool : test_pools) {
+        pool_infos.reset();
+        auto start_address = IOAddress::increase(test_pool->start_address_);
+        auto end_address = IOAddress::increase(test_pool->end_address_);
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(start_address, end_address));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(1, pool_infos->size());
+        checkPoolInfos(*(*pool_infos)[0], *test_pool, __LINE__);
+    }
+
+    // Fetch each by overlapping pool start.
+    for ( auto const& test_pool : test_pools) {
+        pool_infos.reset();
+
+        IOAddress one("::1");
+        auto start_address = IOAddress::subtract(test_pool->start_address_, one);
+        auto end_address = IOAddress::subtract(test_pool->end_address_, one);
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(start_address, end_address));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(1, pool_infos->size());
+        checkPoolInfos(*(*pool_infos)[0], *test_pool, __LINE__);
+    }
+
+    // No match, no delete.
+    bool deleted;
+    ASSERT_NO_THROW_LOG(deleted = lmptr_->sflqPool6Del(IOAddress("2001::1"),
+                                                       IOAddress("2001::2")));
+    ASSERT_FALSE(deleted);
+
+    // Delete each pool.
+    for ( auto const& test_pool : test_pools) {
+        // Delete the pool.
+        ASSERT_NO_THROW_LOG(deleted = lmptr_->sflqPool6Del(test_pool->start_address_,
+                                                              test_pool->end_address_));
+        ASSERT_TRUE(deleted);
+
+        // Verify it's no longer there.
+        pool_infos.reset();
+        ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(test_pool->start_address_,
+                                                              test_pool->end_address_));
+        ASSERT_TRUE(pool_infos);
+        ASSERT_EQ(0, pool_infos->size());
+    }
+}
+
+void
+GenericLeaseMgrTest::testSflqAPIOverlappingPools4() {
+    SflqPoolInfoCollectionPtr pool_infos;
+
+    // Fetching all pools should find none.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4GetAll());
+    ASSERT_EQ(0, pool_infos->size());
+
+    auto test_start = boost::posix_time::second_clock::local_time();
+
+    // Create three test pools.
+    SflqPoolInfoCollection test_pools;
+    SflqPoolInfoPtr pi;
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("192.0.1.10");
+    pi->end_address_ = IOAddress("192.0.1.20");
+    pi->subnet_id_ = 1;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 11;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("192.0.1.15");
+    pi->end_address_ = IOAddress("192.0.1.25");
+    pi->subnet_id_ = 1;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 11;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("192.0.1.20");
+    pi->end_address_ = IOAddress("192.0.1.30");
+    pi->subnet_id_ = 1;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 11;
+    test_pools.push_back(pi);
+
+    for ( auto const& test_pool : test_pools) {
+        ASSERT_NO_THROW_LOG(
+            lmptr_->sflqCreateFlqPool4(test_pool->start_address_,
+                                       test_pool->end_address_,
+                                       test_pool->subnet_id_, false));
+    }
+
+    // Fetch by middle pool's range should return all three.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(test_pools[1]->start_address_,
+                                                          test_pools[1]->end_address_));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(3, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[1], __LINE__);
+    checkPoolInfos(*(*pool_infos)[2], *test_pools[2], __LINE__);
+
+    // Attempting to delete middle pool should return false.
+    ASSERT_THROW_MSG(lmptr_->sflqPool4Del(test_pools[1]->start_address_,
+                                          test_pools[1]->end_address_),
+                     InvalidOperation, "Delete would affect 3 overlapping pools");
+    bool deleted;
+    ASSERT_NO_THROW_LOG(deleted = lmptr_->sflqPool4Del(test_pools[1]->start_address_,
+                                                       test_pools[1]->end_address_, true));
+    ASSERT_TRUE(deleted);
+
+    // Fetch by middle pool's range should the remaining two.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(test_pools[1]->start_address_,
+                                                          test_pools[1]->end_address_));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(2, pool_infos->size());
+    // Deleting the middle pool affects free_lease count for the other two.
+    // Users would need to fix them by calling create with recreate = true.
+    test_pools[0]->free_leases_ = 5;
+    test_pools[2]->free_leases_ = 5;
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[2], __LINE__);
+}
+
+void
+GenericLeaseMgrTest::testSflqAPIOverlappingPools6(Lease::Type lease_type) {
+    SflqPoolInfoCollectionPtr pool_infos;
+
+    // Fetching all pools should find none.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6GetAll());
+    ASSERT_EQ(0, pool_infos->size());
+
+    auto test_start = boost::posix_time::second_clock::local_time();
+
+    // Create three test pools.
+    SflqPoolInfoCollection test_pools;
+    SflqPoolInfoPtr pi;
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("3001::10");
+    pi->end_address_ = IOAddress("3001::20");
+    pi->subnet_id_ = 1;
+    pi->lease_type_ = lease_type;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 17;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("3001::15");
+    pi->end_address_ = IOAddress("3001::25");
+    pi->subnet_id_ = 1;
+    pi->lease_type_ = lease_type;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 17;
+    test_pools.push_back(pi);
+
+    pi.reset(new SflqPoolInfo());
+    pi->start_address_ = IOAddress("3001::20");
+    pi->end_address_ = IOAddress("3001::30");
+    pi->subnet_id_ = 1;
+    pi->lease_type_ = lease_type;
+    pi->created_ts_ = test_start;
+    pi->modified_ts_ = test_start;
+    pi->free_leases_ = 17;
+    test_pools.push_back(pi);
+
+    for ( auto const& test_pool : test_pools) {
+        ASSERT_NO_THROW_LOG(
+            lmptr_->sflqCreateFlqPool6(test_pool->start_address_,
+                                       test_pool->end_address_,
+                                       test_pool->lease_type_,
+                                       test_pool->delegated_len_,
+                                       test_pool->subnet_id_, false));
+    }
+
+    // Fetch by middle pool's range should return all three.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(test_pools[1]->start_address_,
+                                                          test_pools[1]->end_address_));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(3, pool_infos->size());
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[1], __LINE__);
+    checkPoolInfos(*(*pool_infos)[2], *test_pools[2], __LINE__);
+
+    // Attempting to delete middle pool should return false.
+    ASSERT_THROW_MSG(lmptr_->sflqPool6Del(test_pools[1]->start_address_,
+                                          test_pools[1]->end_address_),
+                     InvalidOperation, "Delete would affect 3 overlapping pools");
+    bool deleted;
+    ASSERT_NO_THROW_LOG(deleted = lmptr_->sflqPool6Del(test_pools[1]->start_address_,
+                                                       test_pools[1]->end_address_, true));
+    ASSERT_TRUE(deleted);
+
+    // Fetch by middle pool's range should the remaining two.
+    ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(test_pools[1]->start_address_,
+                                                          test_pools[1]->end_address_));
+    ASSERT_TRUE(pool_infos);
+    ASSERT_EQ(2, pool_infos->size());
+    // Deleting the middle pool affects free_lease count for the other two.
+    // Users would need to fix them by calling create with recreate = true.
+    test_pools[0]->free_leases_ = 5;
+    test_pools[2]->free_leases_ = 11;
+    checkPoolInfos(*(*pool_infos)[0], *test_pools[0], __LINE__);
+    checkPoolInfos(*(*pool_infos)[1], *test_pools[2], __LINE__);
+}
 
 }  // namespace test
 }  // namespace dhcp
index ecd5e45ee4052acb063ac49792c71570a5103ae4..28e40faa2ffd9e14e3ec7380a96736e803d446ab 100644 (file)
@@ -190,6 +190,17 @@ public:
     int countLogs(TrackingLeaseMgr::CallbackType type, SubnetID subnet_id,
                   Lease::Type lease_type) const;
 
+    /// Compare two SflqPoolInfo structures for equality. 
+    ///
+    /// All members are compared for equality except the timestamps. Those
+    /// are considered correct if the lhs timestamps are greater than or
+    /// equal to their rhs counterparts.  Asserts if they are not "equal".
+    /// 
+    /// @param lhs left-side instance to compare 
+    /// @param rhs reft-side instance to compare
+    /// @param lineno source line of invocation (pass in __LINE__)
+    void checkPoolInfos(const SflqPoolInfo& lhs, const SflqPoolInfo& rhs, int lineno);
+
     /// @brief checks that addLease, getLease4(addr) and deleteLease() works
     void testBasicLease4();
 
@@ -713,6 +724,22 @@ public:
     /// @param lease_type lease type to test (TYPE_NA or TYPE_PD)
     void testSflqLeaseOps6(Lease::Type lease_type);
 
+    /// @brief Checks V4 SFLQ API sflqPool4Get*, sflqPool4Del
+    void testSflqAPIFuncs4();
+
+    /// @brief Checks V4 SFLQ API behavior with overlapping pools.
+    void testSflqAPIOverlappingPools4();
+
+    /// @brief Checks V6 SFLQ API sflqPool6Get*, sflqPool6Del
+    ///
+    /// @param lease_type lease type to test (TYPE_NA or TYPE_PD)
+    void testSflqAPIFuncs6(Lease::Type lease_type);
+
+    /// @brief Checks V6 SFLQ API behavior with overlapping pools.
+    ///
+    /// @param lease_type lease type to test (TYPE_NA or TYPE_PD)
+    void testSflqAPIOverlappingPools6(Lease::Type lease_type);
+
     /// @brief String forms of IPv4 addresses
     std::vector<std::string> straddress4_;