From 99da64ffd570aeaae236f4af432a47c720304c8b Mon Sep 17 00:00:00 2001 From: Marcin Siodelski Date: Tue, 26 Jun 2018 10:27:07 +0200 Subject: [PATCH] [5651] Implemented fetching IPv6 leases with paging. --- src/lib/dhcpsrv/cql_lease_mgr.cc | 89 ++++++++++++++++++- src/lib/dhcpsrv/cql_lease_mgr.h | 28 ++++++ src/lib/dhcpsrv/dhcpsrv_messages.mes | 16 ++++ src/lib/dhcpsrv/lease_mgr.h | 28 ++++++ src/lib/dhcpsrv/memfile_lease_mgr.cc | 26 ++++++ src/lib/dhcpsrv/memfile_lease_mgr.h | 28 ++++++ src/lib/dhcpsrv/mysql_lease_mgr.cc | 59 ++++++++++++ src/lib/dhcpsrv/mysql_lease_mgr.h | 32 ++++++- src/lib/dhcpsrv/pgsql_lease_mgr.cc | 56 ++++++++++++ src/lib/dhcpsrv/pgsql_lease_mgr.h | 30 +++++++ .../dhcpsrv/tests/cql_lease_mgr_unittest.cc | 5 ++ .../tests/generic_lease_mgr_unittest.cc | 51 ++++++++++- .../tests/generic_lease_mgr_unittest.h | 3 + src/lib/dhcpsrv/tests/lease_mgr_unittest.cc | 29 ++++++ .../tests/memfile_lease_mgr_unittest.cc | 6 ++ .../dhcpsrv/tests/mysql_lease_mgr_unittest.cc | 5 ++ .../dhcpsrv/tests/pgsql_lease_mgr_unittest.cc | 5 ++ 17 files changed, 491 insertions(+), 5 deletions(-) diff --git a/src/lib/dhcpsrv/cql_lease_mgr.cc b/src/lib/dhcpsrv/cql_lease_mgr.cc index 377efc3ddd..a63e73a7b8 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.cc +++ b/src/lib/dhcpsrv/cql_lease_mgr.cc @@ -879,6 +879,9 @@ public: static constexpr StatementTag GET_LEASE6_ADDR = "GET_LEASE6_ADDR"; static constexpr StatementTag GET_LEASE6_DUID_IAID = "GET_LEASE6_DUID_IAID"; static constexpr StatementTag GET_LEASE6_DUID_IAID_SUBID = "GET_LEASE6_DUID_IAID_SUBID"; + static constexpr StatementTag GET_LEASE6_LIMIT = "GET_LEASE6_LIMIT"; + static constexpr StatementTag GET_LEASE6_PAGE = "GET_LEASE6_PAGE"; + static constexpr StatementTag GET_LEASE6_RANGE = "GET_LEASE6_RANGE"; // @} private: @@ -917,6 +920,9 @@ constexpr StatementTag CqlLease6Exchange::GET_LEASE6_EXPIRE; constexpr StatementTag CqlLease6Exchange::GET_LEASE6_ADDR; constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID; constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID_SUBID; +constexpr StatementTag CqlLease6Exchange::GET_LEASE6_LIMIT; +constexpr StatementTag CqlLease6Exchange::GET_LEASE6_PAGE; +constexpr StatementTag CqlLease6Exchange::GET_LEASE6_RANGE; StatementMap CqlLease6Exchange::tagged_statements_ = { @@ -1011,6 +1017,40 @@ StatementMap CqlLease6Exchange::tagged_statements_ = { "AND subnet_id = ? " "ALLOW FILTERING "}}, + // Get range of IPv6 leases from first lease with a limit (paging) + {GET_LEASE6_LIMIT, + {GET_LEASE6_LIMIT, + "SELECT " + "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, " + "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, " + "hwaddr_source, state " + "FROM lease6 " + "LIMIT ? " + "ALLOW FILTERING "}}, + + // Get range of IPv6 leases from address with a limit (paging) + {GET_LEASE6_PAGE, + {GET_LEASE6_PAGE, + "SELECT " + "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, " + "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, " + "hwaddr_source, state " + "FROM lease6 " + "WHERE TOKEN(address) > TOKEN(?) " + "LIMIT ? " + "ALLOW FILTERING "}}, + + // Get range of IPv6 leases between two addresses + {GET_LEASE6_RANGE, + {GET_LEASE6_RANGE, + "SELECT " + "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, " + "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, " + "hwaddr_source, state " + "FROM lease6 " + "WHERE address >= ? " + "AND address <= ? " + "ALLOW FILTERING "}}, }; CqlLease6Exchange::CqlLease6Exchange(const CqlConnection &connection) @@ -1427,9 +1467,6 @@ CqlLease6Exchange::retrieve() { void CqlLease6Exchange::getLeaseCollection(StatementTag &statement_tag, AnyArray &data, Lease6Collection &result) { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4) - .arg(statement_tag); - AnyArray collection = executeSelect(connection_, data, statement_tag); // Transfer Lease6 objects to result. @@ -2289,6 +2326,52 @@ CqlLeaseMgr::getLeases6() const { isc_throw(NotImplemented, "getLeases6() is not implemented"); } +Lease6Collection +CqlLeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + if (page_size.page_size_ == 0) { + isc_throw(OutOfRange, "page size of retrieved leases must not be 0"); + } + + if (page_size.page_size_ > std::numeric_limits::max()) { + isc_throw(OutOfRange, "page size of retrieved leases must not be greater than " + << std::numeric_limits::max()); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_PAGE6) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + AnyArray data; + + std::string lb_address_data; + if (!lower_bound_address.isV6Zero()) { + lb_address_data = lower_bound_address.toText(); + if (lb_address_data.size() > ADDRESS6_TEXT_MAX_LEN) { + isc_throw(BadValue, + "CqlLeaseMgr::getLeases6(lower_bound_address, page_size): " + "address " + << lb_address_data << " of length " << lb_address_data.size() + << " exceeds maximum allowed length of " + << ADDRESS6_TEXT_MAX_LEN); + } + data.add(&lb_address_data); + } + + cass_int32_t page_size_data = static_cast(page_size.page_size_); + data.add(&page_size_data); + + // Get the leases. + Lease6Collection result; + std::unique_ptr exchange6(new CqlLease6Exchange(dbconn_)); + exchange6->getLeaseCollection(lower_bound_address.isV6Zero() ? + CqlLease6Exchange::GET_LEASE6_LIMIT : + CqlLease6Exchange::GET_LEASE6_PAGE, + data, result); + + return (result); +} + void CqlLeaseMgr::getExpiredLeases4(Lease4Collection &expired_leases, const size_t max_leases) const { diff --git a/src/lib/dhcpsrv/cql_lease_mgr.h b/src/lib/dhcpsrv/cql_lease_mgr.h index 6dc17f4265..ab4ffbaf24 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.h +++ b/src/lib/dhcpsrv/cql_lease_mgr.h @@ -333,6 +333,34 @@ public: /// this backend. virtual Lease6Collection getLeases6() const override; + /// @brief Returns range of IPv6 leases using paging. + /// + /// This method implements paged browsing of the lease database. The first + /// parameter specifies a page size. The second parameter is optional and + /// specifies the starting address of the range. This address is excluded + /// from the returned range. The IPv6 zero address (default) denotes that + /// the first page should be returned. There is no guarantee about the + /// order of returned leases. + /// + /// The typical usage of this method is as follows: + /// - Get the first page of leases by specifying IPv6 zero address as the + /// beginning of the range. + /// - Last address of the returned range should be used as a starting + /// address for the next page in the subsequent call. + /// - If the number of leases returned is lower than the page size, it + /// indicates that the last page has been retrieved. + /// - If there are no leases returned it indicates that the previous page + /// was the last page. + /// + /// @param lower_bound_address IPv4 address used as lower bound for the + /// returned range. + /// @param page_size maximum size of the page returned. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection + getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const override; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 576be84cd1..9b26dd7135 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -287,6 +287,10 @@ with addresses in the specified range. A debug message issued when the server is attempting to obtain a page of leases beginning with the specified address. +% DHCPSRV_CQL_GET_PAGE6 obtaining at most %1 IPv6 leases starting from address %2 +A debug message issued when the server is attempting to obtain a page +of leases beginning with the specified address. + % DHCPSRV_CQL_GET_SUBID4 obtaining IPv4 leases for subnet ID %1 A debug message issued when the server is attempting to obtain all IPv4 leases for a given subnet identifier from the Cassandra database. @@ -493,6 +497,10 @@ with addresses in the specified range. A debug message issued when the server is attempting to obtain a page of leases beginning with the specified address. +% DHCPSRV_MEMFILE_GET_PAGE6 obtaining at most %1 IPv6 leases starting from address %2 +A debug message issued when the server is attempting to obtain a page +of leases beginning with the specified address. + % DHCPSRV_MEMFILE_GET6 obtaining all IPv6 leases A debug message issued when the server is attempting to obtain all IPv6 leases from the memory file database. @@ -753,6 +761,10 @@ with addresses in the specified range. A debug message issued when the server is attempting to obtain a page of leases beginning with the specified address. +% DHCPSRV_MYSQL_GET_PAGE6 obtaining at most %1 IPv6 leases starting from address %2 +A debug message issued when the server is attempting to obtain a page +of leases beginning with the specified address. + % DHCPSRV_MYSQL_GET_ADDR6 obtaining IPv6 lease for address %1, lease type %2 A debug message issued when the server is attempting to obtain an IPv6 lease from the MySQL database for the specified address. @@ -967,6 +979,10 @@ lease from the PostgreSQL database for a client with the specified IAID A debug message issued when the server is attempting to obtain a page of leases beginning with the specified address. +% DHCPSRV_PGSQL_GET_PAGE6 obtaining at most %1 IPv6 leases starting from address %2 +A debug message issued when the server is attempting to obtain a page +of leases beginning with the specified address. + % DHCPSRV_PGSQL_GET_SUBID4 obtaining IPv4 leases for subnet ID %1 A debug message issued when the server is attempting to obtain all IPv4 leases for a given subnet identifier from the PostgreSQL database. diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index 0e5ca9c256..2cb8ec9afc 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -488,6 +488,34 @@ public: /// @return Lease collection (may be empty if no IPv6 lease found). virtual Lease6Collection getLeases6() const = 0; + /// @brief Returns range of IPv6 leases using paging. + /// + /// This method implements paged browsing of the lease database. The first + /// parameter specifies a page size. The second parameter is optional and + /// specifies the starting address of the range. This address is excluded + /// from the returned range. The IPv6 zero address (default) denotes that + /// the first page should be returned. There is no guarantee about the + /// order of returned leases. + /// + /// The typical usage of this method is as follows: + /// - Get the first page of leases by specifying IPv6 zero address as the + /// beginning of the range. + /// - Last address of the returned range should be used as a starting + /// address for the next page in the subsequent call. + /// - If the number of leases returned is lower than the page size, it + /// indicates that the last page has been retrieved. + /// - If there are no leases returned it indicates that the previous page + /// was the last page. + /// + /// @param lower_bound_address IPv4 address used as lower bound for the + /// returned range. + /// @param page_size maximum size of the page returned. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection + getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const = 0; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index 5fb3c239a5..d0ada4d158 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -1035,6 +1035,32 @@ Memfile_LeaseMgr::getLeases6() const { return (collection); } +Lease6Collection +Memfile_LeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_PAGE6) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + Lease6Collection collection; + const Lease6StorageAddressIndex& idx = storage6_.get(); + Lease6StorageAddressIndex::const_iterator lb = idx.lower_bound(lower_bound_address); + + // Exclude the lower bound address specified by the caller. + if ((lb != idx.end()) && ((*lb)->addr_ == lower_bound_address)) { + ++lb; + } + + // Return all other leases being within the page size. + for (auto lease = lb; + (lease != idx.end()) && (std::distance(lb, lease) < page_size.page_size_); + ++lease) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } + + return (collection); +} + void Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, const size_t max_leases) const { diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index efb6917589..c80aa15b78 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -327,6 +327,34 @@ public: /// @return Lease collection (may be empty if no IPv6 lease found). virtual Lease6Collection getLeases6() const; + /// @brief Returns range of IPv6 leases using paging. + /// + /// This method implements paged browsing of the lease database. The first + /// parameter specifies a page size. The second parameter is optional and + /// specifies the starting address of the range. This address is excluded + /// from the returned range. The IPv6 zero address (default) denotes that + /// the first page should be returned. There is no guarantee about the + /// order of returned leases. + /// + /// The typical usage of this method is as follows: + /// - Get the first page of leases by specifying IPv6 zero address as the + /// beginning of the range. + /// - Last address of the returned range should be used as a starting + /// address for the next page in the subsequent call. + /// - If the number of leases returned is lower than the page size, it + /// indicates that the last page has been retrieved. + /// - If there are no leases returned it indicates that the previous page + /// was the last page. + /// + /// @param lower_bound_address IPv4 address used as lower bound for the + /// returned range. + /// @param page_size maximum size of the page returned. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection + getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 76ae230aef..f0a4b5cb86 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -206,6 +206,26 @@ tagged_statements = { { "FROM lease6 " "WHERE duid = ? AND iaid = ? AND subnet_id = ? " "AND lease_type = ?"}, + {MySqlLeaseMgr::GET_LEASE6_PAGE, + "SELECT address, duid, valid_lifetime, " + "expire, subnet_id, pref_lifetime, " + "lease_type, iaid, prefix_len, " + "fqdn_fwd, fqdn_rev, hostname, " + "hwaddr, hwtype, hwaddr_source, " + "state " + "FROM lease6 " + "WHERE address > ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE6_RANGE, + "SELECT address, duid, valid_lifetime, " + "expire, subnet_id, pref_lifetime, " + "lease_type, iaid, prefix_len, " + "fqdn_fwd, fqdn_rev, hostname, " + "hwaddr, hwtype, hwaddr_source, " + "state " + "FROM lease6 " + "WHERE address >= ? AND address <= ?"}, {MySqlLeaseMgr::GET_LEASE6_SUBID, "SELECT address, duid, valid_lifetime, " "expire, subnet_id, pref_lifetime, " @@ -2140,6 +2160,45 @@ MySqlLeaseMgr::getLeases6() const { return (result); } +Lease6Collection +MySqlLeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_PAGE6) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + // Prepare WHERE clause + MYSQL_BIND inbind[2]; + memset(inbind, 0, sizeof(inbind)); + + // In IPv6 we compare addresses represented as strings. The IPv6 zero address + // is ::, so it is greater than any other address. In this special case, we + // just use 0 for comparison which should be lower than any real IPv6 address. + std::string lb_address_data = "0"; + if (!lower_bound_address.isV6Zero()) { + lb_address_data = lower_bound_address.toText(); + } + + // Bind lower bound address + unsigned long lb_address_data_size = lb_address_data.size(); + inbind[0].buffer_type = MYSQL_TYPE_STRING; + inbind[0].buffer = const_cast(lb_address_data.c_str()); + inbind[0].buffer_length = lb_address_data_size; + inbind[0].length = &lb_address_data_size; + + // Bind page size value + size_t* ps = const_cast(&page_size.page_size_); + inbind[1].buffer_type = MYSQL_TYPE_LONG; + inbind[1].buffer = reinterpret_cast(ps); + inbind[1].is_unsigned = MLM_TRUE; + + // Get the leases + Lease6Collection result; + getLeaseCollection(GET_LEASE6_PAGE, inbind, result); + + return (result); +} + void MySqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, const size_t max_leases) const { diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index 5f320ad922..24f70f7630 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -325,6 +325,34 @@ public: /// @return Lease collection (may be empty if no IPv6 lease found). virtual Lease6Collection getLeases6() const; + /// @brief Returns range of IPv6 leases using paging. + /// + /// This method implements paged browsing of the lease database. The first + /// parameter specifies a page size. The second parameter is optional and + /// specifies the starting address of the range. This address is excluded + /// from the returned range. The IPv6 zero address (default) denotes that + /// the first page should be returned. There is no guarantee about the + /// order of returned leases. + /// + /// The typical usage of this method is as follows: + /// - Get the first page of leases by specifying IPv6 zero address as the + /// beginning of the range. + /// - Last address of the returned range should be used as a starting + /// address for the next page in the subsequent call. + /// - If the number of leases returned is lower than the page size, it + /// indicates that the last page has been retrieved. + /// - If there are no leases returned it indicates that the previous page + /// was the last page. + /// + /// @param lower_bound_address IPv4 address used as lower bound for the + /// returned range. + /// @param page_size maximum size of the page returned. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection + getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases @@ -559,14 +587,16 @@ public: GET_LEASE4_CLIENTID_SUBID, // Get lease4 by client ID & subnet ID GET_LEASE4_HWADDR, // Get lease4 by HW address GET_LEASE4_HWADDR_SUBID, // Get lease4 by HW address & subnet ID - GET_LEASE4_SUBID, // Get IPv4 leases by subnet ID GET_LEASE4_PAGE, // Get page of leases beginning with an address GET_LEASE4_RANGE, // Get range of leases between addresses + GET_LEASE4_SUBID, // Get IPv4 leases by subnet ID GET_LEASE4_EXPIRE, // Get lease4 by expiration. GET_LEASE6, // Get all IPv6 leases GET_LEASE6_ADDR, // Get lease6 by address GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID + GET_LEASE6_PAGE, // Get page of leases beginning with an address + GET_LEASE6_RANGE, // Get range of leases between addresses GET_LEASE6_SUBID, // Get IPv6 leases by subnet ID GET_LEASE6_EXPIRE, // Get lease6 by expiration. INSERT_LEASE4, // Add entry to lease4 table diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index 84030634c0..02dd997d4b 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -200,6 +200,30 @@ PgSqlTaggedStatement tagged_statements[] = { "WHERE lease_type = $1 " "AND duid = $2 AND iaid = $3 AND subnet_id = $4"}, + // GET_LEASE6_PAGE + { 2, { OID_VARCHAR, OID_INT8 }, + "get_lease6_page", + "SELECT address, duid, valid_lifetime, " + "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, " + "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, " + "hwaddr, hwtype, hwaddr_source, " + "state " + "FROM lease6 " + "WHERE address > $1 " + "ORDER BY address " + "LIMIT $2"}, + + // GET_LEASE6_RANGE + { 2, { OID_VARCHAR, OID_VARCHAR }, + "get_lease6_range", + "SELECT address, duid, valid_lifetime, " + "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, " + "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, " + "hwaddr, hwtype, hwaddr_source, " + "state " + "FROM lease6 " + "WHERE address >= $1 AND address <= $2"}, + // GET_LEASE6_SUBID { 1, { OID_INT8 }, "get_lease6_subid", @@ -1491,6 +1515,38 @@ PgSqlLeaseMgr::getLeases6() const { return (result); } +Lease6Collection +PgSqlLeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_PAGE6) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + // Prepare WHERE clause + PsqlBindArray bind_array; + + // In IPv6 we compare addresses represented as strings. The IPv6 zero address + // is ::, so it is greater than any other address. In this special case, we + // just use 0 for comparison which should be lower than any real IPv6 address. + std::string lb_address_data = "0"; + if (!lower_bound_address.isV6Zero()) { + lb_address_data = lower_bound_address.toText(); + } + + // Bind lower bound address + bind_array.add(lb_address_data); + + // Bind page size value + std::string page_size_data = boost::lexical_cast(page_size.page_size_); + bind_array.add(page_size_data); + + // Get the leases + Lease6Collection result; + getLeaseCollection(GET_LEASE6_PAGE, bind_array, result); + + return (result); +} + void PgSqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, const size_t max_leases) const { diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index 7ecf6fa8c1..e0287c9ac3 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -297,6 +297,34 @@ public: /// @return Lease collection (may be empty if no IPv6 lease found). virtual Lease6Collection getLeases6() const; + /// @brief Returns range of IPv6 leases using paging. + /// + /// This method implements paged browsing of the lease database. The first + /// parameter specifies a page size. The second parameter is optional and + /// specifies the starting address of the range. This address is excluded + /// from the returned range. The IPv6 zero address (default) denotes that + /// the first page should be returned. There is no guarantee about the + /// order of returned leases. + /// + /// The typical usage of this method is as follows: + /// - Get the first page of leases by specifying IPv6 zero address as the + /// beginning of the range. + /// - Last address of the returned range should be used as a starting + /// address for the next page in the subsequent call. + /// - If the number of leases returned is lower than the page size, it + /// indicates that the last page has been retrieved. + /// - If there are no leases returned it indicates that the previous page + /// was the last page. + /// + /// @param lower_bound_address IPv4 address used as lower bound for the + /// returned range. + /// @param page_size maximum size of the page returned. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection + getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const; + /// @brief Returns a collection of expired DHCPv4 leases. /// /// This method returns at most @c max_leases expired leases. The leases @@ -535,6 +563,8 @@ public: GET_LEASE6_ADDR, // Get lease6 by address GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID + GET_LEASE6_PAGE, // Get page of IPv6 leases beginning with an address + GET_LEASE6_RANGE, // Get range of IPv6 leases between addresses GET_LEASE6_SUBID, // Get IPv6 leases by subnet ID GET_LEASE6_EXPIRE, // Get expired lease6 INSERT_LEASE4, // Add entry to lease4 table diff --git a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc index 5fa8b47757..ba95b0b7c2 100644 --- a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc @@ -697,6 +697,11 @@ TEST_F(CqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) { testGetLease6DuidIaidSubnetIdSize(); } +// Test that a range of IPv6 leases is returned with paging. +TEST_F(CqlLeaseMgrTest, getLeases6Paged) { + testGetLeases6Paged(); +} + /// @brief Lease6 update tests /// /// Checks that we are able to update a lease in the database. diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index 601938205b..8f663516ad 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -1259,7 +1259,7 @@ GenericLeaseMgrTest::testGetLeases4Paged() { Lease4Collection all_leases; IOAddress last_address = IOAddress("0.0.0.0"); - for (auto i = 0; i < 1000; ++i) { + for (auto i = 0; i < 4; ++i) { Lease4Collection page = lmptr_->getLeases4(last_address, LeasePageSize(3)); // Collect leases in a common structure. They may be out of order. @@ -1371,6 +1371,55 @@ GenericLeaseMgrTest::testGetLeases6() { ASSERT_EQ(leases.size(), returned.size()); } +void +GenericLeaseMgrTest::testGetLeases6Paged() { + // Get the leases to be used for the test and add to the database. + vector leases = createLeases6(); + for (size_t i = 0; i < leases.size(); ++i) { + EXPECT_TRUE(lmptr_->addLease(leases[i])); + } + + Lease6Collection all_leases; + + IOAddress last_address = IOAddress::IPV6_ZERO_ADDRESS(); + for (auto i = 0; i < 4; ++i) { + Lease6Collection page = lmptr_->getLeases6(last_address, LeasePageSize(3)); + + // Collect leases in a common structure. They may be out of order. + for (Lease6Ptr lease : page) { + all_leases.push_back(lease); + } + + // Empty page means there are no more leases. + if (page.empty()) { + break; + + } else { + // Record last returned address because it is going to be used + // as an argument for the next call. + last_address = page[page.size() - 1]->addr_; + } + } + + // Make sure that we got exactly the number of leases that we earlier + // stored in the database. + EXPECT_EQ(leases.size(), all_leases.size()); + + // Make sure that all leases that we stored in the lease database + // have been retrieved. + for (Lease6Ptr lease : leases) { + bool found = false; + for (Lease6Ptr returned_lease : all_leases) { + if (lease->addr_ == returned_lease->addr_) { + found = true; + break; + } + } + EXPECT_TRUE(found) << "lease for address " << lease->addr_.toText() + << " was not returned in any of the pages"; + } +} + void GenericLeaseMgrTest::testGetLeases6DuidIaid() { // Get the leases to be used for the test. diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index c4112d6a23..914bf4e5f4 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -215,6 +215,9 @@ public: /// @brief Test method which returns all IPv6 leases. void testGetLeases6(); + /// @brief Test method which returns range of IPv6 leases with paging. + void testGetLeases6Paged(); + /// @brief Basic Lease4 Checks /// /// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id), diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index 63daded660..6a88fdce47 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -242,6 +242,35 @@ public: return (Lease6Collection()); } + /// @brief Returns range of IPv6 leases using paging. + /// + /// This method implements paged browsing of the lease database. The first + /// parameter specifies a page size. The second parameter is optional and + /// specifies the starting address of the range. This address is excluded + /// from the returned range. The IPv6 zero address (default) denotes that + /// the first page should be returned. There is no guarantee about the + /// order of returned leases. + /// + /// The typical usage of this method is as follows: + /// - Get the first page of leases by specifying IPv6 zero address as the + /// beginning of the range. + /// - Last address of the returned range should be used as a starting + /// address for the next page in the subsequent call. + /// - If the number of leases returned is lower than the page size, it + /// indicates that the last page has been retrieved. + /// - If there are no leases returned it indicates that the previous page + /// was the last page. + /// + /// @param lower_bound_address IPv4 address used as lower bound for the + /// returned range. + /// @param page_size maximum size of the page returned. + /// + /// @return Lease collection (may be empty if no IPv6 lease found). + virtual Lease6Collection + getLeases6(const asiolink::IOAddress& /* lower_bound_address */, + const LeasePageSize& /* page_size */) const { + return (Lease6Collection()); + }; /// @brief Returns expired DHCPv6 leases. /// diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index 99632a7f61..59a6b972f4 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -954,6 +954,12 @@ TEST_F(MemfileLeaseMgrTest, getLeases6) { testGetLeases6(); } +// Test that a range of IPv6 leases is returned with paging. +TEST_F(MemfileLeaseMgrTest, getLeases6Paged) { + startBackend(V6); + testGetLeases6Paged(); +} + /// @brief Basic Lease6 Checks /// /// Checks that the addLease, getLease6 (by address) and deleteLease (with an diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index 7cc21eec4a..3c4e8a2fbd 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -375,6 +375,11 @@ TEST_F(MySqlLeaseMgrTest, getLeases6) { testGetLeases6(); } +// Test that a range of IPv6 leases is returned with paging. +TEST_F(MySqlLeaseMgrTest, getLeases6Paged) { + testGetLeases6Paged(); +} + /// @brief Basic Lease4 Checks /// /// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id), diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index 364c10638f..a444e0a03a 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -364,6 +364,11 @@ TEST_F(PgSqlLeaseMgrTest, getLeases6) { testGetLeases6(); } +// Test that a range of IPv6 leases is returned with paging. +TEST_F(PgSqlLeaseMgrTest, getLeases6Paged) { + testGetLeases6Paged(); +} + /// @brief Basic Lease4 Checks /// /// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id), -- 2.47.2