From: Marcin Siodelski Date: Mon, 25 Jun 2018 19:47:20 +0000 (+0200) Subject: [5651] Implemented LeaseMgr methods retrieving ranges/pages of IPv4 leases. X-Git-Tag: trac5694_base~5^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=122bb75a7ad250a80380e251ce48c326de00eae8;p=thirdparty%2Fkea.git [5651] Implemented LeaseMgr methods retrieving ranges/pages of IPv4 leases. --- diff --git a/src/lib/dhcpsrv/cql_lease_mgr.cc b/src/lib/dhcpsrv/cql_lease_mgr.cc index a225585ab2..377efc3ddd 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.cc +++ b/src/lib/dhcpsrv/cql_lease_mgr.cc @@ -217,6 +217,12 @@ public: static constexpr StatementTag GET_LEASE4_HWADDR = "GET_LEASE4_HWADDR"; // Get lease4 by HW address & subnet ID static constexpr StatementTag GET_LEASE4_HWADDR_SUBID = "GET_LEASE4_HWADDR_SUBID"; + // Get range of lease4 from first lease with a limit + static constexpr StatementTag GET_LEASE4_LIMIT = "GET_LEASE4_LIMIT"; + // Get range of lease4 from address with limit (paging) + static constexpr StatementTag GET_LEASE4_PAGE = "GET_LEASE4_PAGE"; + // Get range of lease4 between two addresses + static constexpr StatementTag GET_LEASE4_RANGE = "GET_LEASE4_RANGE"; // Get lease4 by subnet ID static constexpr StatementTag GET_LEASE4_SUBID = "GET_LEASE4_SUBID"; /// @} @@ -240,6 +246,9 @@ constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID; constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID_SUBID; constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR; constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR_SUBID; +constexpr StatementTag CqlLease4Exchange::GET_LEASE4_LIMIT; +constexpr StatementTag CqlLease4Exchange::GET_LEASE4_PAGE; +constexpr StatementTag CqlLease4Exchange::GET_LEASE4_RANGE; constexpr StatementTag CqlLease4Exchange::GET_LEASE4_SUBID; StatementMap CqlLease4Exchange::tagged_statements_{ @@ -349,6 +358,38 @@ StatementMap CqlLease4Exchange::tagged_statements_{ "AND subnet_id = ? " "ALLOW FILTERING "}}, + // Get range of lease4 from first lease with a limit (paging) + {GET_LEASE4_LIMIT, + {GET_LEASE4_LIMIT, + "SELECT " + "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "LIMIT ? " + "ALLOW FILTERING "}}, + + // Get range of lease4 from address with a limit (paging) + {GET_LEASE4_PAGE, + {GET_LEASE4_PAGE, + "SELECT " + "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE TOKEN(address) > TOKEN(?) " + "LIMIT ? " + "ALLOW FILTERING "}}, + + // Get range of lease4 between two addresses + {GET_LEASE4_RANGE, + {GET_LEASE4_RANGE, + "SELECT " + "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE address >= ? " + "AND address <= ? " + "ALLOW FILTERING "}}, + // Gets an IPv4 lease(s) with specified subnet-id {GET_LEASE4_SUBID, {GET_LEASE4_SUBID, @@ -2082,6 +2123,73 @@ CqlLeaseMgr::getLeases4() const { return (result); } +Lease4Collection +CqlLeaseMgr::getLeases4(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_PAGE4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + AnyArray data; + + cass_int32_t address_data = 0; + if (!lower_bound_address.isV4Zero()) { + address_data = static_cast(lower_bound_address.toUint32()); + data.add(&address_data); + } + + cass_int32_t page_size_data = static_cast(page_size.page_size_); + data.add(&page_size_data); + + // Get the data. + Lease4Collection result; + std::unique_ptr exchange4(new CqlLease4Exchange(dbconn_)); + exchange4->getLeaseCollection(lower_bound_address.isV4Zero() ? + CqlLease4Exchange::GET_LEASE4_LIMIT : + CqlLease4Exchange::GET_LEASE4_PAGE, + data, result); + + return (result); +} + +Lease4Collection +CqlLeaseMgr::getLeases4(const IOAddress& lower_bound_address, + const IOAddress& upper_bound_address) const { + if (upper_bound_address < lower_bound_address) { + isc_throw(InvalidRange, "upper bound address " << upper_bound_address + << " is lower than lower bound address " << lower_bound_address); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR_RANGE4) + .arg(lower_bound_address.toText()) + .arg(upper_bound_address.toText()); + + // Set up the WHERE clause value + AnyArray data; + + cass_int32_t lb_address_data = static_cast(lower_bound_address.toUint32()); + data.add(&lb_address_data); + + cass_int32_t ub_address_data = static_cast(upper_bound_address.toUint32()); + data.add(&ub_address_data); + + // Get the data. + Lease4Collection result; + std::unique_ptr exchange4(new CqlLease4Exchange(dbconn_)); + exchange4->getLeaseCollection(CqlLease4Exchange::GET_LEASE4_RANGE, data, result); + + return (result); +} + Lease6Ptr CqlLeaseMgr::getLease6(Lease::Type lease_type, const IOAddress &addr) const { std::string addr_data = addr.toText(); diff --git a/src/lib/dhcpsrv/cql_lease_mgr.h b/src/lib/dhcpsrv/cql_lease_mgr.h index 69adf64500..6dc17f4265 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.h +++ b/src/lib/dhcpsrv/cql_lease_mgr.h @@ -215,6 +215,50 @@ public: /// this backend. virtual Lease4Collection getLeases4() const override; + /// @brief Returns range of IPv4 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 IPv4 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 IPv4 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 IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const override; + + /// @brief Returns a range of IPv4 leases. + /// + /// Returned leases are ordered by IPv4 addresses. + /// + /// @param lower_bound_address IPv4 address used as a lower bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// @param upper_bound_address IPv4 address used as an upper bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// + /// @return Lease collection (may be empty if no IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const asiolink::IOAddress& upper_bound_address) const override; + /// @brief Returns existing IPv6 lease for a given IPv6 address. /// /// For a given address, we assume that there will be only one lease. diff --git a/src/lib/dhcpsrv/db_exceptions.h b/src/lib/dhcpsrv/db_exceptions.h index 46fc58bdd0..64e99f4612 100644 --- a/src/lib/dhcpsrv/db_exceptions.h +++ b/src/lib/dhcpsrv/db_exceptions.h @@ -1,4 +1,4 @@ -// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -59,6 +59,14 @@ public: isc::Exception(file, line, what) {} }; +/// @brief Upper bound address is lower than lower bound address while +/// retrieving a range of leases. +class InvalidRange : public Exception { +public: + InvalidRange(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + } // namespace isc } // namespace dhcp diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 8d6e4fa36e..576be84cd1 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -279,6 +279,14 @@ A debug message issued when the server is attempting to obtain an IPv6 lease from the Cassandra database for a client with the specified IAID (Identity Association ID), Subnet ID and DUID (DHCP Unique Identifier). +% DHCPSRV_CQL_GET_ADDR_RANGE4 obtaining all IPv4 leases with addresses in range from %1 to %2 +A debug message issued when the server is attempting to obtain leases +with addresses in the specified range. + +% DHCPSRV_CQL_GET_PAGE4 obtaining at most %1 IPv4 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. @@ -477,6 +485,14 @@ in the message. A debug message issued when the server is attempting to obtain all IPv4 leases from the memory file database. +% DHCPSRV_MEMFILE_GET_ADDR_RANGE4 obtaining all IPv4 leases with addresses in range from %1 to %2 +A debug message issued when the server is attempting to obtain leases +with addresses in the specified range. + +% DHCPSRV_MEMFILE_GET_PAGE4 obtaining at most %1 IPv4 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. @@ -729,6 +745,14 @@ leases from the MySQL database. A debug message issued when the server is attempting to obtain an IPv4 lease from the MySQL database for the specified address. +% DHCPSRV_MYSQL_GET_ADDR_RANGE4 obtaining all IPv4 leases with addresses in range from %1 to %2 +A debug message issued when the server is attempting to obtain leases +with addresses in the specified range. + +% DHCPSRV_MYSQL_GET_PAGE4 obtaining at most %1 IPv4 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. @@ -905,6 +929,10 @@ lease from the PostgreSQL database for the specified address. A debug message issued when the server is attempting to obtain an IPv6 lease from the PostgreSQL database for the specified address. +% DHCPSRV_PGSQL_GET_ADDR_RANGE4 obtaining all IPv4 leases with addresses in range from %1 to %2 +A debug message issued when the server is attempting to obtain leases +with addresses in the specified range. + % DHCPSRV_PGSQL_GET_CLIENTID obtaining IPv4 leases for client ID %1 A debug message issued when the server is attempting to obtain a set of IPv4 leases from the PostgreSQL database for a client with the specified @@ -935,6 +963,10 @@ A debug message issued when the server is attempting to obtain an IPv6 lease from the PostgreSQL database for a client with the specified IAID (Identity Association ID), Subnet ID and DUID (DHCP Unique Identifier). +% DHCPSRV_PGSQL_GET_PAGE4 obtaining at most %1 IPv4 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.cc b/src/lib/dhcpsrv/lease_mgr.cc index 1defefc259..235b3495e3 100644 --- a/src/lib/dhcpsrv/lease_mgr.cc +++ b/src/lib/dhcpsrv/lease_mgr.cc @@ -30,6 +30,19 @@ using namespace std; namespace isc { namespace dhcp { +LeasePageSize::LeasePageSize(const size_t page_size) + : page_size_(page_size) { + + if (page_size_ == 0) { + isc_throw(OutOfRange, "page size of retrieved leases must not be 0"); + } + + if (page_size_ > std::numeric_limits::max()) { + isc_throw(OutOfRange, "page size of retrieved leases must not be greater than " + << std::numeric_limits::max()); + } +} + Lease6Ptr LeaseMgr::getLease6(Lease::Type type, const DUID& duid, uint32_t iaid, SubnetID subnet_id) const { diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index a820304ae2..0e5ca9c256 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -64,6 +64,20 @@ namespace dhcp { /// @brief Pair containing major and minor versions typedef std::pair VersionPair; +/// @brief Wraps value holding size of the page with leases. +class LeasePageSize { +public: + + /// @brief Constructor. + /// + /// @param page_size page size value. + /// @throw OutOfRange if page size is 0 or greater than uint32_t numeric + /// limit. + explicit LeasePageSize(const size_t page_size); + + const size_t page_size_; ///< Holds page size. +}; + /// @brief Contains a single row of lease statistical data /// /// The contents of the row consist of a subnet ID, a lease @@ -350,6 +364,50 @@ public: /// @return Lease collection (may be empty if no IPv4 lease found). virtual Lease4Collection getLeases4() const = 0; + /// @brief Returns range of IPv4 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 IPv4 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 IPv4 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 IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const = 0; + + /// @brief Returns a range of IPv4 leases. + /// + /// Returned leases are ordered by IPv4 addresses. + /// + /// @param lower_bound_address IPv4 address used as a lower bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// @param upper_bound_address IPv4 address used as an upper bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// + /// @return Lease collection (may be empty if no IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const asiolink::IOAddress& upper_bound_address) const = 0; + /// @brief Returns existing IPv6 lease for a given IPv6 address. /// /// For a given address, we assume that there will be only one lease. diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index 449dae4564..5fb3c239a5 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -38,6 +38,7 @@ const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE"; } // end of anonymous namespace +using namespace isc::asiolink; using namespace isc::util; namespace isc { @@ -883,6 +884,59 @@ Memfile_LeaseMgr::getLeases4() const { return (collection); } +Lease4Collection +Memfile_LeaseMgr::getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_PAGE4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + Lease4Collection collection; + const Lease4StorageAddressIndex& idx = storage4_.get(); + Lease4StorageAddressIndex::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(Lease4Ptr(new Lease4(**lease))); + } + + return (collection); +} + +Lease4Collection +Memfile_LeaseMgr::getLeases4(const IOAddress& lower_bound_address, + const IOAddress& upper_bound_address) const { + // Check if the range boundaries aren't swapped. + if (upper_bound_address < lower_bound_address) { + isc_throw(InvalidRange, "upper bound address " << upper_bound_address + << " is lower than lower bound address " << lower_bound_address); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_ADDR_RANGE4) + .arg(lower_bound_address.toText()) + .arg(upper_bound_address.toText()); + + Lease4Collection collection; + const Lease4StorageAddressIndex& idx = storage4_.get(); + std::pair l = + std::make_pair(idx.lower_bound(lower_bound_address), + idx.upper_bound(upper_bound_address)); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease4Ptr(new Lease4(**lease))); + } + + return (collection); +} + Lease6Ptr Memfile_LeaseMgr::getLease6(Lease::Type type, const isc::asiolink::IOAddress& addr) const { diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index 3af59246a8..efb6917589 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -232,6 +232,50 @@ public: /// @return Lease collection (may be empty if no IPv4 lease found). virtual Lease4Collection getLeases4() const; + /// @brief Returns range of IPv4 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 IPv4 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 IPv4 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 IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const; + + /// @brief Returns a range of IPv4 leases. + /// + /// Returned leases are ordered by IPv4 addresses. + /// + /// @param lower_bound_address IPv4 address used as a lower bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// @param upper_bound_address IPv4 address used as an upper bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// + /// @return Lease collection (may be empty if no IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const asiolink::IOAddress& upper_bound_address) const; + /// @brief Returns existing IPv6 lease for a given IPv6 address. /// /// This function returns a copy of the lease. The modification in the diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 93593e9054..76ae230aef 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -25,6 +25,7 @@ #include using namespace isc; +using namespace isc::asiolink; using namespace isc::dhcp; using namespace std; @@ -137,6 +138,22 @@ tagged_statements = { { "state " "FROM lease4 " "WHERE hwaddr = ? AND subnet_id = ?"}, + {MySqlLeaseMgr::GET_LEASE4_PAGE, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state " + "FROM lease4 " + "WHERE address > ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_RANGE, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state " + "FROM lease4 " + "WHERE address >= ? AND address <= ?"}, {MySqlLeaseMgr::GET_LEASE4_SUBID, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " @@ -1902,6 +1919,71 @@ MySqlLeaseMgr::getLeases4() const { return (result); } +Lease4Collection +MySqlLeaseMgr::getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_PAGE4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + // Prepare WHERE clause + MYSQL_BIND inbind[2]; + memset(inbind, 0, sizeof(inbind)); + + // Bind lower bound address + uint32_t lb_address_data = lower_bound_address.toUint32(); + inbind[0].buffer_type = MYSQL_TYPE_LONG; + inbind[0].buffer = reinterpret_cast(&lb_address_data); + inbind[0].is_unsigned = MLM_TRUE; + + // 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 + Lease4Collection result; + getLeaseCollection(GET_LEASE4_PAGE, inbind, result); + + return (result); +} + +Lease4Collection +MySqlLeaseMgr::getLeases4(const IOAddress& lower_bound_address, + const IOAddress& upper_bound_address) const { + if (upper_bound_address < lower_bound_address) { + isc_throw(InvalidRange, "upper bound address " << upper_bound_address + << " is lower than lower bound address " << lower_bound_address); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_ADDR_RANGE4) + .arg(lower_bound_address.toText()) + .arg(upper_bound_address.toText()); + + // Prepare WHERE clause + MYSQL_BIND inbind[2]; + memset(inbind, 0, sizeof(inbind)); + + // Bind lower bound address as uint32 value + uint32_t lb_address_data = lower_bound_address.toUint32(); + inbind[0].buffer_type = MYSQL_TYPE_LONG; + inbind[0].buffer = reinterpret_cast(&lb_address_data); + inbind[0].is_unsigned = MLM_TRUE; + + // Bind upper bound address as uint32 value + uint32_t ub_address_data = upper_bound_address.toUint32(); + inbind[1].buffer_type = MYSQL_TYPE_LONG; + inbind[1].buffer = reinterpret_cast(&ub_address_data); + inbind[1].is_unsigned = MLM_TRUE; + + // Get the leases + Lease4Collection result; + getLeaseCollection(GET_LEASE4_RANGE, inbind, result); + + return (result); +} + Lease6Ptr MySqlLeaseMgr::getLease6(Lease::Type lease_type, const isc::asiolink::IOAddress& addr) const { diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index 38615124c4..5f320ad922 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -206,6 +206,50 @@ public: /// @return Lease collection (may be empty if no IPv4 lease found). virtual Lease4Collection getLeases4() const; + /// @brief Returns range of IPv4 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 IPv4 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 IPv4 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 IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const; + + /// @brief Returns a range of IPv4 leases. + /// + /// Returned leases are ordered by IPv4 addresses. + /// + /// @param lower_bound_address IPv4 address used as a lower bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// @param upper_bound_address IPv4 address used as an upper bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// + /// @return Lease collection (may be empty if no IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const asiolink::IOAddress& upper_bound_address) const; + /// @brief Returns existing IPv6 lease for a given IPv6 address. /// /// For a given address, we assume that there will be only one lease. @@ -516,6 +560,8 @@ public: 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_EXPIRE, // Get lease4 by expiration. GET_LEASE6, // Get all IPv6 leases GET_LEASE6_ADDR, // Get lease6 by address diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index f6e2a18434..84030634c0 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -21,6 +21,7 @@ #include using namespace isc; +using namespace isc::asiolink; using namespace isc::dhcp; using namespace std; @@ -111,6 +112,28 @@ PgSqlTaggedStatement tagged_statements[] = { "FROM lease4 " "WHERE hwaddr = $1 AND subnet_id = $2"}, + // GET_LEASE4_PAGE + { 2, { OID_INT8, OID_INT8 }, + "get_lease4_page", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state " + "FROM lease4 " + "WHERE address > $1 " + "ORDER BY address " + "LIMIT $2"}, + + // GET_LEASE4_RANGE + { 2, { OID_INT8, OID_INT8 }, + "get_lease4_range", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state " + "FROM lease4 " + "WHERE address >= $1 AND address <= $2"}, + // GET_LEASE4_SUBID { 1, { OID_INT8 }, "get_lease4_subid", @@ -1294,6 +1317,64 @@ PgSqlLeaseMgr::getLeases4() const { return (result); } +Lease4Collection +PgSqlLeaseMgr::getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_PAGE4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + // Prepare WHERE clause + PsqlBindArray bind_array; + + // Bind lower bound address + std::string lb_address_data = boost::lexical_cast + (lower_bound_address.toUint32()); + 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 + Lease4Collection result; + getLeaseCollection(GET_LEASE4_PAGE, bind_array, result); + + return (result); +} + +Lease4Collection +PgSqlLeaseMgr::getLeases4(const IOAddress& lower_bound_address, + const IOAddress& upper_bound_address) const { + if (upper_bound_address < lower_bound_address) { + isc_throw(InvalidRange, "upper bound address " << upper_bound_address + << " is lower than lower bound address " << lower_bound_address); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_ADDR_RANGE4) + .arg(lower_bound_address.toText()) + .arg(upper_bound_address.toText()); + + // Prepare WHERE clause + PsqlBindArray bind_array; + + // Bind lower bound address + std::string lb_address_data = boost::lexical_cast + (lower_bound_address.toUint32()); + bind_array.add(lb_address_data); + + // Bind upper bound address + std::string ub_address_data = boost::lexical_cast + (upper_bound_address.toUint32()); + bind_array.add(ub_address_data); + + // Get the leases + Lease4Collection result; + getLeaseCollection(GET_LEASE4_RANGE, bind_array, result); + + return (result); +} + Lease6Ptr PgSqlLeaseMgr::getLease6(Lease::Type lease_type, const isc::asiolink::IOAddress& addr) const { diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index 79353dfcdc..7ecf6fa8c1 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -187,6 +187,50 @@ public: /// @return Lease collection (may be empty if no IPv4 lease found). virtual Lease4Collection getLeases4() const; + /// @brief Returns range of IPv4 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 IPv4 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 IPv4 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 IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const; + + /// @brief Returns a range of IPv4 leases. + /// + /// Returned leases are ordered by IPv4 addresses. + /// + /// @param lower_bound_address IPv4 address used as a lower bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// @param upper_bound_address IPv4 address used as an upper bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// + /// @return Lease collection (may be empty if no IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& lower_bound_address, + const asiolink::IOAddress& upper_bound_address) const; + /// @brief Returns existing IPv6 lease for a given IPv6 address. /// /// For a given address, we assume that there will be only one lease. @@ -483,6 +527,8 @@ 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_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 expired lease4 GET_LEASE6, // Get all IPv6 leases diff --git a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc index 74beea413a..5fa8b47757 100644 --- a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc @@ -587,6 +587,16 @@ TEST_F(CqlLeaseMgrTest, getLeases4) { testGetLeases4(); } +// Test that a range of IPv4 leases is returned with paging. +TEST_F(CqlLeaseMgrTest, getLeases4Paged) { + testGetLeases4Paged(); +} + +// Test that a range of IPv4 leases is returmed. +TEST_F(CqlLeaseMgrTest, getLeases4Range) { + testGetLeases4Range(); +} + /// @brief Basic Lease4 Checks /// /// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id), diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index ed1208fe21..601938205b 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -15,9 +16,11 @@ #include #include +#include #include +#include #include using namespace std; @@ -1245,6 +1248,102 @@ GenericLeaseMgrTest::testGetLeases4() { ASSERT_EQ(leases.size(), returned.size()); } +void +GenericLeaseMgrTest::testGetLeases4Paged() { + // Get the leases to be used for the test and add to the database. + vector leases = createLeases4(); + for (size_t i = 0; i < leases.size(); ++i) { + EXPECT_TRUE(lmptr_->addLease(leases[i])); + } + + Lease4Collection all_leases; + + IOAddress last_address = IOAddress("0.0.0.0"); + for (auto i = 0; i < 1000; ++i) { + Lease4Collection page = lmptr_->getLeases4(last_address, LeasePageSize(3)); + + // Collect leases in a common structure. They may be out of order. + for (Lease4Ptr 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 (Lease4Ptr lease : leases) { + bool found = false; + for (Lease4Ptr 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"; + } + + boost::scoped_ptr lease_page_size; + + // The maximum allowed value for the limit is max for uint32_t. + size_t oor = static_cast(std::numeric_limits::max()) + 1; + EXPECT_THROW(lease_page_size.reset(new LeasePageSize(oor)), OutOfRange); + + // Zero page size is illegal too. + EXPECT_THROW(lease_page_size.reset(new LeasePageSize(0)), OutOfRange); +} + +void +GenericLeaseMgrTest::testGetLeases4Range() { + // Get the leases to be used for the test and add to the database. + vector leases = createLeases4(); + for (size_t i = 0; i < leases.size(); ++i) { + EXPECT_TRUE(lmptr_->addLease(leases[i])); + } + + // All addresses in the specified range should be returned. + Lease4Collection returned = lmptr_->getLeases4(IOAddress("192.0.2.2"), + IOAddress("192.0.2.6")); + EXPECT_EQ(5, returned.size()); + + // The lower bound address is below the range, so the first two addresses + // in the database should be returned. + returned = lmptr_->getLeases4(IOAddress("192.0.1.0"), IOAddress("192.0.2.1")); + EXPECT_EQ(2, returned.size()); + + // The lower bound address is the last address in the database, so only this + // address should be returned. + returned = lmptr_->getLeases4(IOAddress("192.0.2.7"), IOAddress("192.0.2.15")); + EXPECT_EQ(1, returned.size()); + + // The lower bound is below the range and the upper bound is above the range, + // so the whole range should be returned. + returned = lmptr_->getLeases4(IOAddress("192.0.1.7"), IOAddress("192.0.2.15")); + EXPECT_EQ(8, returned.size()); + + // No addresses should be returned because our desired range does not + // overlap with leases in the database. + returned = lmptr_->getLeases4(IOAddress("192.0.2.8"), IOAddress("192.0.2.15")); + EXPECT_TRUE(returned.empty()); + + // Swapping the lower bound and upper bound should cause an error. + EXPECT_THROW(lmptr_->getLeases4(IOAddress("192.0.2.8"), IOAddress("192.0.2.1")), + InvalidRange); +} + void GenericLeaseMgrTest::testGetLeases6SubnetId() { // Get the leases to be used for the test and add to the database. @@ -2860,6 +2959,17 @@ LeaseMgrDbLostCallbackTest::testDbLostCallback() { EXPECT_TRUE(callback_called_); } +void +GenericLeaseMgrTest::checkLeaseRange(const Lease4Collection& returned, + const std::vector& expected_addresses) { + ASSERT_EQ(expected_addresses.size(), returned.size()); + + for (auto a = returned.cbegin(); a != returned.cend(); ++a) { + EXPECT_EQ(expected_addresses[std::distance(returned.cbegin(), a)], + (*a)->addr_.toText()); + } +} + void GenericLeaseMgrTest::checkQueryAgainstRowSet(const LeaseStatsQueryPtr& query, const RowSet& expected_rows) { diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index 09b0be7520..c4112d6a23 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -203,6 +203,12 @@ public: /// @brief Test method which returns all IPv4 leases. void testGetLeases4(); + /// @brief Test method which returns range of IPv4 leases with paging. + void testGetLeases4Paged(); + + /// @brief Test method which returns range of IPv4 leases. + void testGetLeases4Range(); + /// @brief Test method which returns all IPv6 leases for Subnet ID. void testGetLeases6SubnetId(); @@ -426,6 +432,13 @@ public: /// @param row_set - set of rows expected to be found in the query rows void checkQueryAgainstRowSet(const LeaseStatsQueryPtr& qry, const RowSet& row_set); + /// @brief Checks if specified range of leases was returned. + /// + /// @param returned collection of leases returned. + /// @param expected_addresses ordered collection of expected addresses. + void checkLeaseRange(const Lease4Collection& returned, + const std::vector& expected_addresses); + /// @brief String forms of IPv4 addresses std::vector straddress4_; diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index 28e5c97e9b..63daded660 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -145,6 +145,54 @@ public: return (Lease4Collection()); } + /// @brief Returns range of IPv4 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 IPv4 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 IPv4 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 IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& /* lower_bound_address */, + const LeasePageSize& /* page_size */) const { + return (Lease4Collection()); + } + + /// @brief Returns a range of IPv4 leases. + /// + /// Returned leases are ordered by IPv4 addresses. + /// + /// @param lower_bound_address IPv4 address used as a lower bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// @param upper_bound_address IPv4 address used as an upper bound for the + /// returned range. The lease for this address is included in the returned + /// range if the lease exists. + /// + /// @return Lease collection (may be empty if no IPv4 lease found). + virtual Lease4Collection + getLeases4(const asiolink::IOAddress& /* lower_bound_address */, + const asiolink::IOAddress& /* upper_bound_address */) const { + return (Lease4Collection()); + } + /// @brief Returns existing IPv6 lease for a given IPv6 address. /// /// @param addr address of the searched lease diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index 84735a9e11..99632a7f61 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -930,6 +930,18 @@ TEST_F(MemfileLeaseMgrTest, getLeases4) { testGetLeases4(); } +// Test that a range of IPv4 leases is returned with paging. +TEST_F(MemfileLeaseMgrTest, getLeases4Paged) { + startBackend(V4); + testGetLeases4Paged(); +} + +// Test that a range of IPv4 leases is returmed. +TEST_F(MemfileLeaseMgrTest, getLeases4Range) { + startBackend(V4); + testGetLeases4Range(); +} + // This test checks that all IPv6 leases for a specified subnet id are returned. TEST_F(MemfileLeaseMgrTest, getLeases6SubnetId) { startBackend(V6); diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index dcacbc403e..7cc21eec4a 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -355,6 +355,16 @@ TEST_F(MySqlLeaseMgrTest, getLeases4) { testGetLeases4(); } +// Test that a range of IPv4 leases is returned with paging. +TEST_F(MySqlLeaseMgrTest, getLeases4Paged) { + testGetLeases4Paged(); +} + +// Test that a range of IPv4 leases is returmed. +TEST_F(MySqlLeaseMgrTest, getLeases4Range) { + testGetLeases4Range(); +} + // This test checks that all IPv6 leases for a specified subnet id are returned. TEST_F(MySqlLeaseMgrTest, getLeases6SubnetId) { testGetLeases6SubnetId(); diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index f0b3ec7b47..364c10638f 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -344,6 +344,16 @@ TEST_F(PgSqlLeaseMgrTest, getLeases4) { testGetLeases4(); } +// Test that a range of IPv4 leases is returned with paging. +TEST_F(PgSqlLeaseMgrTest, getLeases4Paged) { + testGetLeases4Paged(); +} + +// Test that a range of IPv4 leases is returmed. +TEST_F(PgSqlLeaseMgrTest, getLeases4Range) { + testGetLeases4Range(); +} + // This test checks that all IPv6 leases for a specified subnet id are returned. TEST_F(PgSqlLeaseMgrTest, getLeases6SubnetId) { testGetLeases6SubnetId();