From: Francis Dupont Date: Thu, 30 Mar 2023 20:51:46 +0000 (+0200) Subject: [#2753] Rebased with new schemas X-Git-Tag: Kea-2.3.7~47 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d4c3a32c8a841f63d14cf7ecb33bf247c2568b99;p=thirdparty%2Fkea.git [#2753] Rebased with new schemas --- diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 643b0b74c9..f9f15d034e 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -865,6 +865,16 @@ of leases beginning with the specified address. A debug message issued when the server is attempting to obtain a page of leases beginning with the specified address. +% DHCPSRV_MYSQL_GET_RELAYID4 obtaining at most %1 IPv4 leases starting from address %2 with relay id %3 and cltt between %4 and %5 +A debug message issued when the server is attempting to obtain a page of +IPv4 leases beginning with the specified address with a relay id and client +transaction time between start and end dates. + +% DHCPSRV_MYSQL_GET_REMOTEID4 obtaining at most %1 IPv4 leases starting from address %2 with remote id %3 and cltt between %4 and %5 +A debug message issued when the server is attempting to obtain a page of +IPv4 leases beginning with the specified address with a remote id and client +transaction time between start and end dates. + % DHCPSRV_MYSQL_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 MySQL database. @@ -1097,6 +1107,16 @@ of leases beginning with the specified address. A debug message issued when the server is attempting to obtain a page of leases beginning with the specified address. +% DHCPSRV_PGSQL_GET_RELAYID4 obtaining at most %1 IPv4 leases starting from address %2 with relay id %3 and cltt between %4 and %5 +A debug message issued when the server is attempting to obtain a page of +IPv4 leases beginning with the specified address with a relay id and client +transaction time between start and end dates. + +% DHCPSRV_PGSQL_GET_REMOTEID4 obtaining at most %1 IPv4 leases starting from address %2 with remote id %3 and cltt between %4 and %5 +A debug message issued when the server is attempting to obtain a page of +IPv4 leases beginning with the specified address with a remote id and client +transaction time between start and end dates. + % 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/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 7175bec171..3e5d61f7ff 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -116,48 +116,48 @@ tagged_statements = { { "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4"}, {MySqlLeaseMgr::GET_LEASE4_ADDR, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE address = ?"}, {MySqlLeaseMgr::GET_LEASE4_CLIENTID, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE client_id = ?"}, {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE client_id = ? AND subnet_id = ?"}, {MySqlLeaseMgr::GET_LEASE4_HWADDR, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE hwaddr = ?"}, {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "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, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE address > ? " "ORDER BY address " @@ -166,27 +166,123 @@ tagged_statements = { { "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE subnet_id = ?"}, {MySqlLeaseMgr::GET_LEASE4_HOSTNAME, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE hostname = ?"}, {MySqlLeaseMgr::GET_LEASE4_EXPIRE, "SELECT address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE state != ? " "AND valid_lifetime != 4294967295 " "AND expire < ? " "ORDER BY expire ASC " "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_RELAYID, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = ? and address > ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_RELAYID_QST, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = ? and address > ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " >= ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_RELAYID_QSET, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = ? and address > ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " >= ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " <= ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_RELAYID_QET, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = ? and address > ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " <= ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_REMOTEID, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = ? and address > ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_REMOTEID_QST, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = ? and address > ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " >= ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_REMOTEID_QSET, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = ? and address > ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " >= ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " <= ? " + "ORDER BY address " + "LIMIT ?"}, + {MySqlLeaseMgr::GET_LEASE4_REMOTEID_QET, + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = ? and address > ? " + " and UNIX_TIMESTAMP(expire) - IF" + "(valid_lifetime = 4294967295, 0, valid_lifetime)" + " <= ? " + "ORDER BY address " + "LIMIT ?"}, {MySqlLeaseMgr::GET_LEASE6, "SELECT address, duid, valid_lifetime, " "expire, subnet_id, pref_lifetime, " @@ -278,8 +374,8 @@ tagged_statements = { { "INSERT INTO lease4(address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"}, + "state, user_context, relay_id, remote_id) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"}, {MySqlLeaseMgr::INSERT_LEASE6, "INSERT INTO lease6(address, duid, valid_lifetime, " "expire, subnet_id, pref_lifetime, " @@ -293,7 +389,8 @@ tagged_statements = { { "client_id = ?, valid_lifetime = ?, expire = ?, " "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, " "hostname = ?, " - "state = ?, user_context = ? " + "state = ?, user_context = ?, " + "relay_id = ?, remote_id = ? " "WHERE address = ? AND expire = ?"}, {MySqlLeaseMgr::UPDATE_LEASE6, "UPDATE lease6 SET address = ?, duid = ?, " @@ -426,7 +523,7 @@ public: class MySqlLease4Exchange : public MySqlLeaseExchange { /// @brief Set number of database columns for this lease structure - static const size_t LEASE_COLUMNS = 11; + static const size_t LEASE_COLUMNS = 13; public: @@ -439,11 +536,15 @@ public: subnet_id_(0), valid_lifetime_(0), fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0), state_(0), user_context_length_(0), - user_context_null_(MLM_FALSE) { + user_context_null_(MLM_FALSE), + relay_id_null_(MLM_FALSE), + remote_id_null_(MLM_FALSE) { memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_)); memset(client_id_buffer_, 0, sizeof(client_id_buffer_)); memset(hostname_buffer_, 0, sizeof(hostname_buffer_)); memset(user_context_, 0, sizeof(user_context_)); + memset(relay_id_buffer_, 0, sizeof(relay_id_buffer_)); + memset(remote_id_buffer_, 0, sizeof(remote_id_buffer_)); std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE); // Set the column names (for error messages) @@ -458,7 +559,9 @@ public: columns_[8] = "hostname"; columns_[9] = "state"; columns_[10] = "user_context"; - BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS); + columns_[11] = "relay_id"; + columns_[12] = "remote_id"; + BOOST_STATIC_ASSERT(12 < LEASE_COLUMNS); } /// @brief Create MYSQL_BIND objects for Lease4 Pointer @@ -639,6 +742,36 @@ public: bind_[10].buffer_type = MYSQL_TYPE_NULL; } + // relay_id: varbinary(128) + relay_id_ = lease_->relay_id_; + if (!relay_id_.empty()) { + bind_[11].buffer_type = MYSQL_TYPE_BLOB; + bind_[11].buffer = reinterpret_cast(&relay_id_[0]); + relay_id_length_ = relay_id_.size(); + bind_[11].buffer_length = relay_id_length_; + bind_[11].length = &relay_id_length_; + } else { + bind_[11].buffer_type = MYSQL_TYPE_NULL; + relay_id_null_ = MLM_TRUE; + bind_[11].buffer = NULL; + bind_[11].is_null = &relay_id_null_; + } + + // remote_id: varbinary(128) + remote_id_ = lease_->remote_id_; + if (!remote_id_.empty()) { + bind_[12].buffer_type = MYSQL_TYPE_BLOB; + bind_[12].buffer = reinterpret_cast(&remote_id_[0]); + remote_id_length_ = remote_id_.size(); + bind_[12].buffer_length = remote_id_length_; + bind_[12].length = &remote_id_length_; + } else { + bind_[12].buffer_type = MYSQL_TYPE_NULL; + remote_id_null_ = MLM_TRUE; + bind_[12].buffer = NULL; + bind_[12].is_null = &remote_id_null_; + } + // Add the error flags setErrorIndicators(bind_, error_, LEASE_COLUMNS); @@ -759,6 +892,22 @@ public: bind_[10].length = &user_context_length_; bind_[10].is_null = &user_context_null_; + // relay_id: varbinary(128) + relay_id_length_ = sizeof(relay_id_buffer_); + bind_[11].buffer_type = MYSQL_TYPE_BLOB; + bind_[11].buffer = reinterpret_cast(relay_id_buffer_); + bind_[11].buffer_length = relay_id_length_; + bind_[11].length = &relay_id_length_; + bind_[11].is_null = &relay_id_null_; + + // remote_id: varbinary(128) + remote_id_length_ = sizeof(remote_id_buffer_); + bind_[12].buffer_type = MYSQL_TYPE_BLOB; + bind_[12].buffer = reinterpret_cast(remote_id_buffer_); + bind_[12].buffer_length = remote_id_length_; + bind_[12].length = &remote_id_length_; + bind_[12].is_null = &remote_id_null_; + // Add the error flags setErrorIndicators(bind_, error_, LEASE_COLUMNS); @@ -837,6 +986,18 @@ public: lease->setContext(ctx); } + // Set relay id if it was set. + if (relay_id_null_ == MLM_FALSE) { + lease->relay_id_.assign(relay_id_buffer_, + relay_id_buffer_ + relay_id_length_); + } + + // Set remote id if it was set. + if (remote_id_null_ == MLM_FALSE) { + lease->remote_id_.assign(remote_id_buffer_, + remote_id_buffer_ + remote_id_length_); + } + return (lease); } @@ -883,6 +1044,14 @@ private: char user_context_[USER_CONTEXT_MAX_LEN]; ///< User context unsigned long user_context_length_; ///< Length of user context my_bool user_context_null_; ///< Used when user context is null + std::vector relay_id_; ///< Relay id + uint8_t relay_id_buffer_[ClientId::MAX_CLIENT_ID_LEN]; ///< Relay id buffer + unsigned long relay_id_length_; ///< Relay id length + my_bool relay_id_null_; ///< Used when Relay id is null + std::vector remote_id_; ///< Remote id + uint8_t remote_id_buffer_[ClientId::MAX_CLIENT_ID_LEN]; ///< Remote id buffer + unsigned long remote_id_length_; ///< Remote id length + my_bool remote_id_null_; ///< Used when Remote id is null }; /// @brief Exchange MySQL and Lease6 Data @@ -2487,9 +2656,9 @@ MySqlLeaseMgr::getLeases4(const IOAddress& lower_bound_address, inbind[0].is_unsigned = MLM_TRUE; // Bind page size value - size_t* ps = const_cast(&page_size.page_size_); + uint32_t ps = static_cast(page_size.page_size_); inbind[1].buffer_type = MYSQL_TYPE_LONG; - inbind[1].buffer = reinterpret_cast(ps); + inbind[1].buffer = reinterpret_cast(&ps); inbind[1].is_unsigned = MLM_TRUE; // Get the leases @@ -2784,9 +2953,9 @@ MySqlLeaseMgr::getLeases6(const IOAddress& lower_bound_address, inbind[0].length = &lb_address_data_size; // Bind page size value - size_t* ps = const_cast(&page_size.page_size_); + uint32_t ps = static_cast(page_size.page_size_); inbind[1].buffer_type = MYSQL_TYPE_LONG; - inbind[1].buffer = reinterpret_cast(ps); + inbind[1].buffer = reinterpret_cast(&ps); inbind[1].is_unsigned = MLM_TRUE; // Get the leases @@ -3461,22 +3630,249 @@ MySqlLeaseMgr::addRemoteId6(const IOAddress& /* lease_addr */, isc_throw(NotImplemented, "MySqlLeaseMgr::addRemoteId6 not implemented"); } +namespace { + +std::string +idToText(const OptionBuffer& id) { + std::stringstream tmp; + tmp << std::hex; + bool delim = false; + for (std::vector::const_iterator it = id.begin(); + it != id.end(); ++it) { + if (delim) { + tmp << ":"; + } + tmp << std::setw(2) << std::setfill('0') + << static_cast(*it); + delim = true; + } + return (tmp.str()); +} + +} // anonymous namespace + Lease4Collection -MySqlLeaseMgr::getLeases4ByRelayId(const OptionBuffer& /* relay_id */, - const IOAddress& /* lower_bound_address */, - const LeasePageSize& /* page_size */, - const time_t& /* qry_start_time = 0 */, - const time_t& /* qry_end_time = 0 */) { - isc_throw(NotImplemented, "MySqlLeaseMgr::getLeases4ByRelayId not implemented"); +MySqlLeaseMgr::getLeases4ByRelayId(const OptionBuffer& relay_id, + const IOAddress& lower_bound_address, + const LeasePageSize& page_size, + const time_t& qry_start_time /* = 0 */, + const time_t& qry_end_time /* = 0 */) { + // Expecting IPv4 address. + if (!lower_bound_address.isV4()) { + isc_throw(InvalidAddressFamily, "expected IPv4 address while " + "retrieving leases from the lease database, got " + << lower_bound_address); + } + + // Catch 2038 bug with 32 bit time_t. + if ((qry_start_time < 0) || (qry_end_time < 0)) { + isc_throw(BadValue, "negative time value"); + } + + bool have_qst = (qry_start_time > 0); + bool have_qet = (qry_end_time > 0); + + // Start time must be before end time. + if (have_qst && have_qet && (qry_start_time > qry_end_time)) { + isc_throw(BadValue, "start time must be before end time"); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_RELAYID4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()) + .arg(idToText(relay_id)) + .arg(qry_start_time) + .arg(qry_end_time); + + // Prepare WHERE clause + size_t bindings = 3; + if (have_qst) { + ++bindings; + } + if (have_qet) { + ++bindings; + } + MYSQL_BIND inbind[bindings]; + memset(inbind, 0, sizeof(inbind)); + + std::vector relay_id_data = relay_id; + unsigned long relay_id_length = relay_id.size(); + + // If the relay id happens to be empty, we have to create a + // 1 byte dummy buffer and pass it to the binding. + if (relay_id_data.empty()) { + relay_id_data.resize(1); + } + + // Bind relay id + inbind[0].buffer_type = MYSQL_TYPE_BLOB; + inbind[0].buffer = reinterpret_cast(&relay_id_data[0]); + inbind[0].buffer_length = relay_id_length; + inbind[0].length = &relay_id_length; + + // Bind lower bound address + uint32_t lb_address_data = lower_bound_address.toUint32(); + inbind[1].buffer_type = MYSQL_TYPE_LONG; + inbind[1].buffer = reinterpret_cast(&lb_address_data); + inbind[1].is_unsigned = MLM_TRUE; + + size_t index = 2; + // Bind query start time. + uint32_t start_time = static_cast(qry_start_time); + if (have_qst) { + inbind[index].buffer_type = MYSQL_TYPE_LONG; + inbind[index].buffer = reinterpret_cast(&start_time); + inbind[index].is_unsigned = MLM_TRUE; + ++index; + } + + // Bind query end time. + uint32_t end_time = static_cast(qry_end_time); + if (have_qet) { + inbind[index].buffer_type = MYSQL_TYPE_LONG; + inbind[index].buffer = reinterpret_cast(&end_time); + inbind[index].is_unsigned = MLM_TRUE; + ++index; + } + + // Bind page size value + uint32_t ps = static_cast(page_size.page_size_); + inbind[index].buffer_type = MYSQL_TYPE_LONG; + inbind[index].buffer = reinterpret_cast(&ps); + inbind[index].is_unsigned = MLM_TRUE; + + StatementIndex stindex = GET_LEASE4_RELAYID; + if (have_qst && !have_qet) { + stindex = GET_LEASE4_RELAYID_QST; + } else if (have_qst && have_qet) { + stindex = GET_LEASE4_RELAYID_QSET; + } else if (!have_qst && have_qet) { + stindex = GET_LEASE4_RELAYID_QET; + } + + // Get the leases + Lease4Collection result; + + // Get a context + MySqlLeaseContextAlloc get_context(*this); + MySqlLeaseContextPtr ctx = get_context.ctx_; + + getLeaseCollection(ctx, stindex, inbind, result); + + return (result); } Lease4Collection -MySqlLeaseMgr::getLeases4ByRemoteId(const OptionBuffer& /* remote_id */, - const IOAddress& /* lower_bound_address */, - const LeasePageSize& /* page_size */, - const time_t& /* qry_start_time = 0 */, - const time_t& /* qry_end_time = 0 */) { - isc_throw(NotImplemented, "MySqlLeaseMgr::getLeases4ByRemoteId not implemented"); +MySqlLeaseMgr::getLeases4ByRemoteId(const OptionBuffer& remote_id, + const IOAddress& lower_bound_address, + const LeasePageSize& page_size, + const time_t& qry_start_time /* = 0 */, + const time_t& qry_end_time /* = 0 */) { + // Expecting IPv4 address. + if (!lower_bound_address.isV4()) { + isc_throw(InvalidAddressFamily, "expected IPv4 address while " + "retrieving leases from the lease database, got " + << lower_bound_address); + } + + // Catch 2038 bug with 32 bit time_t. + if ((qry_start_time < 0) || (qry_end_time < 0)) { + isc_throw(BadValue, "negative time value"); + } + + bool have_qst = (qry_start_time > 0); + bool have_qet = (qry_end_time > 0); + + // Start time must be before end time. + if (have_qst && have_qet && (qry_start_time > qry_end_time)) { + isc_throw(BadValue, "start time must be before end time"); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MYSQL_GET_REMOTEID4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()) + .arg(idToText(remote_id)) + .arg(qry_start_time) + .arg(qry_end_time); + + // Prepare WHERE clause + size_t bindings = 3; + if (have_qst) { + ++bindings; + } + if (have_qet) { + ++bindings; + } + MYSQL_BIND inbind[bindings]; + memset(inbind, 0, sizeof(inbind)); + + std::vector remote_id_data = remote_id; + unsigned long remote_id_length = remote_id.size(); + + // If the remote id happens to be empty, we have to create a + // 1 byte dummy buffer and pass it to the binding. + if (remote_id_data.empty()) { + remote_id_data.resize(1); + } + + // Bind remote id + inbind[0].buffer_type = MYSQL_TYPE_BLOB; + inbind[0].buffer = reinterpret_cast(&remote_id_data[0]); + inbind[0].buffer_length = remote_id_length; + inbind[0].length = &remote_id_length; + + // Bind lower bound address + uint32_t lb_address_data = lower_bound_address.toUint32(); + inbind[1].buffer_type = MYSQL_TYPE_LONG; + inbind[1].buffer = reinterpret_cast(&lb_address_data); + inbind[1].is_unsigned = MLM_TRUE; + + size_t index = 2; + // Bind query start time. + uint32_t start_time = static_cast(qry_start_time); + if (have_qst) { + inbind[index].buffer_type = MYSQL_TYPE_LONG; + inbind[index].buffer = reinterpret_cast(&start_time); + inbind[index].is_unsigned = MLM_TRUE; + ++index; + } + + // Bind query end time. + uint32_t end_time = static_cast(qry_end_time); + if (have_qet) { + inbind[index].buffer_type = MYSQL_TYPE_LONG; + inbind[index].buffer = reinterpret_cast(&end_time); + inbind[index].is_unsigned = MLM_TRUE; + ++index; + } + + // Bind page size value + uint32_t ps = static_cast(page_size.page_size_); + inbind[index].buffer_type = MYSQL_TYPE_LONG; + inbind[index].buffer = reinterpret_cast(&ps); + inbind[index].is_unsigned = MLM_TRUE; + + StatementIndex stindex = GET_LEASE4_REMOTEID; + if (have_qst && !have_qet) { + stindex = GET_LEASE4_REMOTEID_QST; + } else if (have_qst && have_qet) { + stindex = GET_LEASE4_REMOTEID_QSET; + } else if (!have_qst && have_qet) { + stindex = GET_LEASE4_REMOTEID_QET; + } + + // Get the leases + Lease4Collection result; + + // Get a context + MySqlLeaseContextAlloc get_context(*this); + MySqlLeaseContextPtr ctx = get_context.ctx_; + + getLeaseCollection(ctx, stindex, inbind, result); + + return (result); } Lease6Collection diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index 0def8c6aa9..26a83fc675 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -705,6 +705,14 @@ public: GET_LEASE4_SUBID, // Get IPv4 leases by subnet ID GET_LEASE4_HOSTNAME, // Get IPv4 leases by hostname GET_LEASE4_EXPIRE, // Get lease4 by expiration. + GET_LEASE4_RELAYID, // Get page of lease by relay ID. + GET_LEASE4_RELAYID_QST, // Get page of leases by relay ID and query start time. + GET_LEASE4_RELAYID_QSET, // Get page of leases by relay ID and query start and end times. + GET_LEASE4_RELAYID_QET, // Get page of leases by relay ID and query end time. + GET_LEASE4_REMOTEID, // Get page of lease by remote ID. + GET_LEASE4_REMOTEID_QST, // Get page of leases by remote ID and query start time. + GET_LEASE4_REMOTEID_QSET, // Get page of leases by remote ID and query start and end times. + GET_LEASE4_REMOTEID_QET, // Get page of leases by remote ID and query end time. GET_LEASE6, // Get all IPv6 leases GET_LEASE6_ADDR, // Get lease6 by address GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index 795db37eb2..2f590f8396 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -69,7 +69,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4"}, // GET_LEASE4_ADDR @@ -78,7 +78,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE address = $1"}, @@ -88,7 +88,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE client_id = $1"}, @@ -98,7 +98,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE client_id = $1 AND subnet_id = $2"}, @@ -108,7 +108,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE hwaddr = $1"}, @@ -118,7 +118,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE hwaddr = $1 AND subnet_id = $2"}, @@ -128,7 +128,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE address > $1 " "ORDER BY address " @@ -140,7 +140,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE subnet_id = $1"}, @@ -150,7 +150,7 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE lower(hostname) = $1"}, @@ -160,12 +160,124 @@ PgSqlTaggedStatement tagged_statements[] = { "SELECT address, hwaddr, client_id, " "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, " - "state, user_context " + "state, user_context, relay_id, remote_id " "FROM lease4 " "WHERE state != $1 AND valid_lifetime != 4294967295 AND expire < $2 " "ORDER BY expire " "LIMIT $3"}, + // GET_LEASE4_RELAYID + { 3, { OID_BYTEA, OID_INT8, OID_INT8 }, + "get_lease4_relayid", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = $1 and address > $2 " + "ORDER BY address " + "LIMIT $3"}, + + // GET_LEASE4_RELAYID_QST + { 4, { OID_BYTEA, OID_INT8, OID_INT8, OID_INT8 }, + "get_lease4_relayid_qst", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = $1 and address > $2 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) >= $3 " + "ORDER BY address " + "LIMIT $4"}, + + // GET_LEASE4_RELAYID_QSET + { 5, { OID_BYTEA, OID_INT8, OID_INT8, OID_INT8, OID_INT8 }, + "get_lease4_relayid_qset", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = $1 and address > $2 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) >= $3 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) <= $4 " + "ORDER BY address " + "LIMIT $5"}, + + // GET_LEASE4_RELAYID_QET + { 4, { OID_BYTEA, OID_INT8, OID_INT8, OID_INT8 }, + "get_lease4_relayid_qet", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE relay_id = $1 and address > $2 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) <= $3 " + "ORDER BY address " + "LIMIT $4"}, + + // GET_LEASE4_REMOTEID + { 3, { OID_BYTEA, OID_INT8, OID_INT8 }, + "get_lease4_remoteid", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = $1 and address > $2 " + "ORDER BY address " + "LIMIT $3"}, + + // GET_LEASE4_REMOTEID_QST + { 4, { OID_BYTEA, OID_INT8, OID_INT8, OID_INT8 }, + "get_lease4_remoteid_qst", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = $1 and address > $2 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) >= $3 " + "ORDER BY address " + "LIMIT $4"}, + + // GET_LEASE4_REMOTEID_QSET + { 5, { OID_BYTEA, OID_INT8, OID_INT8, OID_INT8, OID_INT8 }, + "get_lease4_remoteid_qset", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = $1 and address > $2 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) >= $3 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) <= $4 " + "ORDER BY address " + "LIMIT $5"}, + + // GET_LEASE4_REMOTEID_QET + { 4, { OID_BYTEA, OID_INT8, OID_INT8, OID_INT8 }, + "get_lease4_remoteid_qet", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, " + "state, user_context, relay_id, remote_id " + "FROM lease4 " + "WHERE remote_id = $1 and address > $2 " + "and EXTRACT(EPOCH FROM expire) - (CASE valid_lifetime WHEN 4294967295 " + "THEN 0 ELSE valid_lifetime END) <= $3 " + "ORDER BY address " + "LIMIT $4"}, + // GET_LEASE6 { 0, { OID_NONE }, "get_lease6", @@ -271,13 +383,14 @@ PgSqlTaggedStatement tagged_statements[] = { "LIMIT $3"}, // INSERT_LEASE4 - { 11, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, - OID_BOOL, OID_BOOL, OID_VARCHAR, OID_INT8, OID_TEXT }, + { 13, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, + OID_BOOL, OID_BOOL, OID_VARCHAR, OID_INT8, OID_TEXT, OID_BYTEA, + OID_BYTEA }, "insert_lease4", "INSERT INTO lease4(address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, " - "state, user_context) " - "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)"}, + "state, user_context, relay_id, remote_id) " + "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)"}, // INSERT_LEASE6 { 17, { OID_VARCHAR, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, @@ -292,14 +405,15 @@ PgSqlTaggedStatement tagged_statements[] = { "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)"}, // UPDATE_LEASE4 - { 13, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, - OID_BOOL, OID_BOOL, OID_VARCHAR, OID_INT8, OID_TEXT, OID_INT8, OID_TIMESTAMP }, + { 15, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, + OID_BOOL, OID_BOOL, OID_VARCHAR, OID_INT8, OID_TEXT, OID_BYTEA, + OID_BYTEA, OID_INT8, OID_TIMESTAMP }, "update_lease4", "UPDATE lease4 SET address = $1, hwaddr = $2, " "client_id = $3, valid_lifetime = $4, expire = $5, " "subnet_id = $6, fqdn_fwd = $7, fqdn_rev = $8, hostname = $9, " - "state = $10, user_context = $11 " - "WHERE address = $12 AND expire = $13"}, + "state = $10, user_context = $11, relay_id = $12, remote_id = $13 " + "WHERE address = $14 AND expire = $15"}, // UPDATE_LEASE6 { 19, { OID_VARCHAR, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, OID_INT8, @@ -457,19 +571,24 @@ private: static const size_t HOSTNAME_COL = 8; static const size_t STATE_COL = 9; static const size_t USER_CONTEXT_COL = 10; + static const size_t RELAY_ID_COL = 11; + static const size_t REMOTE_ID_COL = 12; /// @brief Number of columns in the table holding DHCPv4 leases. - static const size_t LEASE_COLUMNS = 11; + static const size_t LEASE_COLUMNS = 13; public: /// @brief Constructor PgSqlLease4Exchange() - : lease_(), addr4_(0), client_id_length_(0) { + : lease_(), addr4_(0), client_id_length_(0), + relay_id_length_(0), remote_id_length_(0) { BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS); memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_)); memset(client_id_buffer_, 0, sizeof(client_id_buffer_)); + memset(relay_id_buffer_, 0, sizeof(relay_id_buffer_)); + memset(remote_id_buffer_, 0, sizeof(remote_id_buffer_)); // Set the column names (for error messages) columns_.push_back("address"); @@ -483,6 +602,8 @@ public: columns_.push_back("hostname"); columns_.push_back("state"); columns_.push_back("user_context"); + columns_.push_back("relay_id"); + columns_.push_back("remote_id"); } /// @brief Creates the bind array for sending Lease4 data to the database. @@ -565,6 +686,19 @@ public: user_context_ = ""; } bind_array.add(user_context_); + + if (!lease->relay_id_.empty()) { + bind_array.add(lease->relay_id_); + } else { + bind_array.addNull(); + } + + if (!lease->remote_id_.empty()) { + bind_array.add(lease->remote_id_); + } else { + bind_array.addNull(); + } + } catch (const std::exception& ex) { isc_throw(DbOperationError, "Could not create bind array from Lease4: " @@ -626,6 +760,12 @@ public: } } + convertFromBytea(r, row, RELAY_ID_COL, relay_id_buffer_, + sizeof(relay_id_buffer_), relay_id_length_); + + convertFromBytea(r, row, REMOTE_ID_COL, remote_id_buffer_, + sizeof(remote_id_buffer_), remote_id_length_); + Lease4Ptr result(boost::make_shared(addr4_, hwaddr, client_id_buffer_, client_id_length_, @@ -639,6 +779,16 @@ public: result->setContext(ctx); } + if (relay_id_length_) { + result->relay_id_.assign(relay_id_buffer_, + relay_id_buffer_ + relay_id_length_); + } + + if (remote_id_length_) { + result->remote_id_.assign(remote_id_buffer_, + remote_id_buffer_ + remote_id_length_); + } + return (result); } catch (const std::exception& ex) { isc_throw(DbOperationError, @@ -658,6 +808,10 @@ private: uint32_t addr4_; size_t client_id_length_; uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN]; + size_t relay_id_length_; + uint8_t relay_id_buffer_[ClientId::MAX_CLIENT_ID_LEN]; + size_t remote_id_length_; + uint8_t remote_id_buffer_[ClientId::MAX_CLIENT_ID_LEN]; }; /// @brief Supports exchanging IPv6 leases with PostgreSQL. @@ -2657,22 +2811,203 @@ PgSqlLeaseMgr::addRemoteId6(const IOAddress& /* lease_addr */, isc_throw(NotImplemented, "PgSqlLeaseMgr::addRemoteId6 not implemented"); } +namespace { + +std::string +idToText(const OptionBuffer& id) { + std::stringstream tmp; + tmp << std::hex; + bool delim = false; + for (std::vector::const_iterator it = id.begin(); + it != id.end(); ++it) { + if (delim) { + tmp << ":"; + } + tmp << std::setw(2) << std::setfill('0') + << static_cast(*it); + delim = true; + } + return (tmp.str()); +} + +} // anonymous namespace + Lease4Collection -PgSqlLeaseMgr::getLeases4ByRelayId(const OptionBuffer& /* relay_id */, - const IOAddress& /* lower_bound_address */, - const LeasePageSize& /* page_size */, - const time_t& /* qry_start_time = 0 */, - const time_t& /* qry_end_time = 0 */) { - isc_throw(NotImplemented, "PgSqlLeaseMgr::getLeases4ByRelayId not implemented"); +PgSqlLeaseMgr::getLeases4ByRelayId(const OptionBuffer& relay_id, + const IOAddress& lower_bound_address, + const LeasePageSize& page_size, + const time_t& qry_start_time /* = 0 */, + const time_t& qry_end_time /* = 0 */) { + // Expecting IPv4 address. + if (!lower_bound_address.isV4()) { + isc_throw(InvalidAddressFamily, "expected IPv4 address while " + "retrieving leases from the lease database, got " + << lower_bound_address); + } + + // Catch 2038 bug with 32 bit time_t. + if ((qry_start_time < 0) || (qry_end_time < 0)) { + isc_throw(BadValue, "negative time value"); + } + + bool have_qst = (qry_start_time > 0); + bool have_qet = (qry_end_time > 0); + + // Start time must be before end time. + if (have_qst && have_qet && (qry_start_time > qry_end_time)) { + isc_throw(BadValue, "start time must be before end time"); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_RELAYID4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()) + .arg(idToText(relay_id)) + .arg(qry_start_time) + .arg(qry_end_time); + + // Prepare WHERE clause + PsqlBindArray bind_array; + + // Bind relay id + if (!relay_id.empty()) { + bind_array.add(relay_id); + } else { + bind_array.add(""); + } + + // Bind lower bound address + std::string lb_address_data = + boost::lexical_cast(lower_bound_address.toUint32()); + bind_array.add(lb_address_data); + + // Bind query start time. + std::string start_time_str; + if (have_qst) { + start_time_str = boost::lexical_cast(qry_start_time); + bind_array.add(start_time_str); + } + + // Bind query end time. + std::string end_time_str; + if (have_qet) { + end_time_str = boost::lexical_cast(qry_end_time); + bind_array.add(end_time_str); + } + + // Bind page size value + std::string page_size_data = + boost::lexical_cast(page_size.page_size_); + bind_array.add(page_size_data); + + StatementIndex stindex = GET_LEASE4_RELAYID; + if (have_qst && !have_qet) { + stindex = GET_LEASE4_RELAYID_QST; + } else if (have_qst && have_qet) { + stindex = GET_LEASE4_RELAYID_QSET; + } else if (!have_qst && have_qet) { + stindex = GET_LEASE4_RELAYID_QET; + } + + // Get the leases + Lease4Collection result; + + // Get a context + PgSqlLeaseContextAlloc get_context(*this); + PgSqlLeaseContextPtr ctx = get_context.ctx_; + + getLeaseCollection(ctx, stindex, bind_array, result); + + return (result); } Lease4Collection -PgSqlLeaseMgr::getLeases4ByRemoteId(const OptionBuffer& /* remote_id */, - const IOAddress& /* lower_bound_address */, - const LeasePageSize& /* page_size */, - const time_t& /* qry_start_time = 0 */, - const time_t& /* qry_end_time = 0 */) { - isc_throw(NotImplemented, "PgSqlLeaseMgr::getLeases4ByRemoteId not implemented"); +PgSqlLeaseMgr::getLeases4ByRemoteId(const OptionBuffer& remote_id, + const IOAddress& lower_bound_address, + const LeasePageSize& page_size, + const time_t& qry_start_time /* = 0 */, + const time_t& qry_end_time /* = 0 */) { + // Expecting IPv4 address. + if (!lower_bound_address.isV4()) { + isc_throw(InvalidAddressFamily, "expected IPv4 address while " + "retrieving leases from the lease database, got " + << lower_bound_address); + } + + // Catch 2038 bug with 32 bit time_t. + if ((qry_start_time < 0) || (qry_end_time < 0)) { + isc_throw(BadValue, "negative time value"); + } + + bool have_qst = (qry_start_time > 0); + bool have_qet = (qry_end_time > 0); + + // Start time must be before end time. + if (have_qst && have_qet && (qry_start_time > qry_end_time)) { + isc_throw(BadValue, "start time must be before end time"); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_PGSQL_GET_REMOTEID4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()) + .arg(idToText(remote_id)) + .arg(qry_start_time) + .arg(qry_end_time); + + // Prepare WHERE clause + PsqlBindArray bind_array; + + // Bind remote id + if (!remote_id.empty()) { + bind_array.add(remote_id); + } else { + bind_array.add(""); + } + + // Bind lower bound address + std::string lb_address_data = + boost::lexical_cast(lower_bound_address.toUint32()); + bind_array.add(lb_address_data); + + // Bind query start time. + std::string start_time_str; + if (have_qst) { + start_time_str = boost::lexical_cast(qry_start_time); + bind_array.add(start_time_str); + } + + // Bind query end time. + std::string end_time_str; + if (have_qet) { + end_time_str = boost::lexical_cast(qry_end_time); + bind_array.add(end_time_str); + } + + // Bind page size value + std::string page_size_data = + boost::lexical_cast(page_size.page_size_); + bind_array.add(page_size_data); + + StatementIndex stindex = GET_LEASE4_REMOTEID; + if (have_qst && !have_qet) { + stindex = GET_LEASE4_REMOTEID_QST; + } else if (have_qst && have_qet) { + stindex = GET_LEASE4_REMOTEID_QSET; + } else if (!have_qst && have_qet) { + stindex = GET_LEASE4_REMOTEID_QET; + } + + // Get the leases + Lease4Collection result; + + // Get a context + PgSqlLeaseContextAlloc get_context(*this); + PgSqlLeaseContextPtr ctx = get_context.ctx_; + + getLeaseCollection(ctx, stindex, bind_array, result); + + return (result); } Lease6Collection diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.h b/src/lib/dhcpsrv/pgsql_lease_mgr.h index 0b79629a2a..9e4d7f126f 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.h +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.h @@ -681,6 +681,14 @@ public: GET_LEASE4_SUBID, // Get IPv4 leases by subnet ID GET_LEASE4_HOSTNAME, // Get IPv4 leases by hostname GET_LEASE4_EXPIRE, // Get lease4 by expiration. + GET_LEASE4_RELAYID, // Get page of lease by relay ID. + GET_LEASE4_RELAYID_QST, // Get page of leases by relay ID and query start time. + GET_LEASE4_RELAYID_QSET, // Get page of leases by relay ID and query start and end times. + GET_LEASE4_RELAYID_QET, // Get page of leases by relay ID and query end time. + GET_LEASE4_REMOTEID, // Get page of lease by remote ID. + GET_LEASE4_REMOTEID_QST, // Get page of leases by remote ID and query start time. + GET_LEASE4_REMOTEID_QSET, // Get page of leases by remote ID and query start and end times. + GET_LEASE4_REMOTEID_QET, // Get page of leases by remote ID and query end time. GET_LEASE6, // Get all IPv6 leases GET_LEASE6_ADDR, // Get lease6 by address GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index 99e5deae76..a73d0f34a7 100644 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/src/lib/dhcpsrv/tests/Makefile.am @@ -116,10 +116,12 @@ libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc libdhcpsrv_unittests_SOURCES += ncr_generator_unittest.cc if HAVE_MYSQL libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc +libdhcpsrv_unittests_SOURCES += mysql_lease_extended_info_unittest.cc libdhcpsrv_unittests_SOURCES += mysql_host_data_source_unittest.cc endif if HAVE_PGSQL libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc +libdhcpsrv_unittests_SOURCES += pgsql_lease_extended_info_unittest.cc libdhcpsrv_unittests_SOURCES += pgsql_host_data_source_unittest.cc endif libdhcpsrv_unittests_SOURCES += pool_unittest.cc diff --git a/src/lib/dhcpsrv/tests/mysql_lease_extended_info_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_extended_info_unittest.cc new file mode 100644 index 0000000000..ba34f0cadf --- /dev/null +++ b/src/lib/dhcpsrv/tests/mysql_lease_extended_info_unittest.cc @@ -0,0 +1,617 @@ +// Copyright (C) 2023 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::db; +using namespace isc::db::test; +using namespace isc::dhcp; +using namespace isc::test; +using namespace isc::util; +using namespace std; + +namespace { + +/// @brief IPv4 addresses used in the tests. +const vector ADDRESS4 = { + "192.0.2.0", "192.0.2.1", "192.0.2.2", "192.0.2.3", + "192.0.2.4", "192.0.2.5", "192.0.2.6", "192.0.2.7" +}; + +/// @brief DUIDs used in the tests. +const vector DUIDS = { + "wwwwwwww", "BBBBBBBB", "::::::::", "0123456789acdef", + "BBBBBBBB", "$$$$$$$$", "^^^^^^^^", "\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5" +}; + +/// @brief Test fixture class for extended info tests. +class MySqlExtendedInfoTest : public ::testing::Test { +public: + /// @brief Constructor. + MySqlExtendedInfoTest() { + // Ensure we have the proper schema with no transient data. + createMySQLSchema(); + + // Connect to the database. + try { + LeaseMgrFactory::create(validMySQLConnectionString()); + } catch (...) { + std::cerr << "*** ERROR: unable to open database. The test\n" + "*** environment is broken and must be fixed before\n" + "*** the MySQL tests will run correctly.\n" + "*** The reason for the problem is described in the\n" + "*** accompanying exception output.\n"; + throw; + } + + lease_mgr_ = &(LeaseMgrFactory::instance()); + leases4.clear(); + MultiThreadingMgr::instance().setMode(false); + now_ = time(0); + } + + /// @brief Destructor. + ~MySqlExtendedInfoTest() { + LeaseMgrFactory::destroy(); + // If data wipe enabled, delete transient data otherwise destroy + // the schema. + destroyMySQLSchema(); + + leases4.clear(); + MultiThreadingMgr::instance().setMode(false); + } + + /// @brief Create and set v4 leases. + /// + /// @param insert When true insert in the database. + void initLease4(bool insert = true) { + ASSERT_EQ(ADDRESS4.size(), DUIDS.size()); + for (size_t i = 0; i < ADDRESS4.size(); ++i) { + Lease4Ptr lease; + vector hwaddr_data(5, 0x08); + hwaddr_data.push_back(0x80 + i); + HWAddrPtr hwaddr(new HWAddr(hwaddr_data, HTYPE_ETHER)); + vector client_id = createFromString(DUIDS[i]); + IOAddress address(ADDRESS4[i]); + ASSERT_NO_THROW(lease.reset(new Lease4(address, hwaddr, + &client_id[0], + client_id.size(), + 1000, now_, + static_cast(i)))); + leases4.push_back(lease); + if (insert) { + EXPECT_TRUE(lease_mgr_->addLease(lease)); + } + } + ASSERT_EQ(ADDRESS4.size(), leases4.size()); + } + + /// @brief Create a vector of uint8_t from a string. + /// + /// @param content A not empty string holding the content. + /// @return A vector of uint8_t with the given content. + inline vector createFromString(const string& content) { + vector v; + v.resize(content.size()); + memmove(&v[0], &content[0], v.size()); + return (v); + } + + /// @brief Test initLease4. + void testInitLease4(); + + /// @brief Test getLease4ByRelayId. + void testGetLeases4ByRelayId(); + + /// @brief Test getLease4ByRemoteId. + void testGetLeases4ByRemoteId(); + + /// @brief Lease manager. + LeaseMgr* lease_mgr_; + + /// @brief V4 leases. + Lease4Collection leases4; + + /// @brief Current timestamp. + time_t now_; +}; + +/// @brief Verifies that the lease manager can add the v4 leases. +void +MySqlExtendedInfoTest::testInitLease4() { + initLease4(); + EXPECT_EQ(8, leases4.size()); + IOAddress zero = IOAddress::IPV4_ZERO_ADDRESS(); + Lease4Collection got; + // Use the page version as it returns leases in order. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4(zero, LeasePageSize(100))); + ASSERT_EQ(leases4.size(), got.size()); + for (size_t i = 0; i < leases4.size(); ++i) { + ConstElementPtr expected = leases4[i]->toElement(); + LeasePtr lease = got[i]; + ASSERT_TRUE(lease); + EXPECT_TRUE(expected->equals(*lease->toElement())) + << "expected: " << expected->str() << "\n" + << "got: " << lease->toElement()->str() << "\n"; + } +} + +TEST_F(MySqlExtendedInfoTest, initLease4) { + testInitLease4(); +} + +TEST_F(MySqlExtendedInfoTest, initLease4MultiThreading) { + MultiThreadingTest mt(true); + testInitLease4(); +} + +/// @brief Verifies that getLeases4ByRelayId works as expected. +void +MySqlExtendedInfoTest::testGetLeases4ByRelayId() { + // Lease manager is created with empty tables. + initLease4(false); + + // Create leases. + IOAddress addr0(ADDRESS4[0]); + IOAddress addr1(ADDRESS4[1]); + IOAddress addr2(ADDRESS4[2]); + IOAddress addr3(ADDRESS4[3]); + IOAddress addr4(ADDRESS4[4]); + IOAddress zero = IOAddress::IPV4_ZERO_ADDRESS(); + vector relay_id0 = { 0xaa, 0xbb, 0xcc }; + vector relay_id1 = { 1, 2, 3, 4 }; + vector relay_id2 = createFromString(DUIDS[2]); + string user_context_txt0 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt0 += " \"sub-options\": \"0C03AABBCC\","; + user_context_txt0 += " \"relay-id\": \"AABBCC\" } } }"; + ElementPtr user_context0; + ASSERT_NO_THROW(user_context0 = Element::fromJSON(user_context_txt0)); + string user_context_txt1 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt1 += " \"sub-options\": \"0C0401020304\","; + user_context_txt1 += " \"relay-id\": \"01020304\" } } }"; + ElementPtr user_context1; + ASSERT_NO_THROW(user_context1 = Element::fromJSON(user_context_txt1)); + + Lease4Ptr lease; + // lease0: addr0, id0, now. + lease = leases4[0]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + + // lease1: addr1, id1, now. + lease = leases4[1]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id1; + lease->setContext(user_context1); + + // lease2: addr2, id0, now - 500. + lease = leases4[2]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 500; + + // lease3: addr3, id0, now - 800. + lease = leases4[3]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 800; + + // lease4: addr4, id0, now - 100. + lease = leases4[4]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 100; + + // Add leases. + for (size_t i = 0; i < leases4.size(); ++i) { + EXPECT_TRUE(lease_mgr_->addLease(leases4[i])); + } + + Lease4Collection got; + // Unknown relay id #2: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id2, + zero, + LeasePageSize(100))); + EXPECT_EQ(0, got.size()); + + // Unknown relay id #2, now - 1000, now + 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id2, + zero, + LeasePageSize(100), + now_ - 1000, + now_ + 1000)); + EXPECT_EQ(0, got.size()); + + // Relay id #0, now - 2000, now - 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + now_ - 2000, + now_ - 1000)); + EXPECT_EQ(0, got.size()); + + // Relay id #0, now + 1000, now + 2000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + now_ + 1000, + now_ + 2000)); + EXPECT_EQ(0, got.size()); + + // Relay id #0: 3 entries (0, 2, 3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100))); + ASSERT_EQ(4, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[3]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, partial: 2 entries (0, 2). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, partial from previous: 2 entries (3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr2, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, final partial: no entries. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr4, + LeasePageSize(2))); + EXPECT_EQ(0, got.size()); + + // Relay id #0, from now - 500: 3 entries (0, 2, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + now_ - 500)); + ASSERT_EQ(3, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, to now - 200: 3 entries (2, 3). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + 0, now_ - 200)); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, from now - 500 to now - 100, partial: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, from now - 500 to now - 100, partial from 2: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr2, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, from now - 500 to now - 100, final partial. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr4, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + EXPECT_EQ(0, got.size()); +} + +TEST_F(MySqlExtendedInfoTest, getLeases4ByRelayId) { + testGetLeases4ByRelayId(); +} + +TEST_F(MySqlExtendedInfoTest, getLeases4ByRelayIdMultiThreading) { + MultiThreadingTest mt(true); + testGetLeases4ByRelayId(); +} + +/// @brief Verifies that getLeases4ByRemoteId works as expected. +void +MySqlExtendedInfoTest::testGetLeases4ByRemoteId() { + // Lease manager is created with empty tables. + initLease4(true); + + // Update leases. + IOAddress addr0(ADDRESS4[0]); + IOAddress addr1(ADDRESS4[1]); + IOAddress addr2(ADDRESS4[2]); + IOAddress addr3(ADDRESS4[3]); + IOAddress addr4(ADDRESS4[4]); + IOAddress zero = IOAddress::IPV4_ZERO_ADDRESS(); + vector remote_id0 = { 1, 2, 3, 4 }; + vector remote_id1 = { 0xaa, 0xbb, 0xcc }; + vector remote_id2 = createFromString(DUIDS[2]); + string user_context_txt0 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt0 += " \"sub-options\": \"020401020304\","; + user_context_txt0 += " \"remote-id\": \"01020304\" } } }"; + ElementPtr user_context0; + ASSERT_NO_THROW(user_context0 = Element::fromJSON(user_context_txt0)); + string user_context_txt1 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt1 += " \"sub-options\": \"0203AABBCC\","; + user_context_txt1 += " \"remote-id\": \"AABBCC\" } } }"; + ElementPtr user_context1; + ASSERT_NO_THROW(user_context1 = Element::fromJSON(user_context_txt1)); + + Lease4Ptr lease; + // lease0: addr0, id0, now. + lease = leases4[0]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + + // lease1: addr1, id1, now. + lease = leases4[1]; + lease->remote_id_ = remote_id1; + lease->setContext(user_context1); + + // lease2: addr2, id0, now - 500. + lease = leases4[2]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 500; + + // lease3: addr3, id0, now - 800. + lease = leases4[3]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 800; + + // lease4: addr4, id0, now - 100. + lease = leases4[4]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 100; + + // Update leases. + for (size_t i = 0; i < leases4.size(); ++i) { + EXPECT_NO_THROW(lease_mgr_->updateLease4(leases4[i])); + } + + Lease4Collection got; + // Unknown remote id #2: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id2, + zero, + LeasePageSize(100))); + EXPECT_EQ(0, got.size()); + + // Unknown remote id #2, now - 1000, now + 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id2, + zero, + LeasePageSize(100), + now_ - 1000, + now_ + 1000)); + EXPECT_EQ(0, got.size()); + + // Remote id #0, now - 2000, now - 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + now_ - 2000, + now_ - 1000)); + EXPECT_EQ(0, got.size()); + + // Remote id #0, now + 1000, now + 2000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + now_ + 1000, + now_ + 2000)); + EXPECT_EQ(0, got.size()); + + // Remote id #0: 3 entries (0, 2, 3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100))); + ASSERT_EQ(4, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[3]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, partial: 2 entries (0, 2). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, partial from previous: 2 entries (3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr2, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, final partial: no entries. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr4, + LeasePageSize(2))); + EXPECT_EQ(0, got.size()); + + // Remote id #0, from now - 500: 3 entries (0, 2, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + now_ - 500)); + ASSERT_EQ(3, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, to now - 200: 3 entries (2, 3). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + 0, now_ - 200)); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, from now - 500 to now - 100, partial: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, from now - 500 to now - 100, partial from 2: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr2, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, from now - 500 to now - 100, final partial. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr4, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + EXPECT_EQ(0, got.size()); +} + +TEST_F(MySqlExtendedInfoTest, getLeases4ByRemoteId) { + testGetLeases4ByRemoteId(); +} + +TEST_F(MySqlExtendedInfoTest, getLeases4ByRemoteIdMultiThreading) { + MultiThreadingTest mt(true); + testGetLeases4ByRemoteId(); +} + +} // namespace diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_extended_info_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_extended_info_unittest.cc new file mode 100644 index 0000000000..0f7d21c5a4 --- /dev/null +++ b/src/lib/dhcpsrv/tests/pgsql_lease_extended_info_unittest.cc @@ -0,0 +1,617 @@ +// Copyright (C) 2023 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace isc; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::db; +using namespace isc::db::test; +using namespace isc::dhcp; +using namespace isc::test; +using namespace isc::util; +using namespace std; + +namespace { + +/// @brief IPv4 addresses used in the tests. +const vector ADDRESS4 = { + "192.0.2.0", "192.0.2.1", "192.0.2.2", "192.0.2.3", + "192.0.2.4", "192.0.2.5", "192.0.2.6", "192.0.2.7" +}; + +/// @brief DUIDs used in the tests. +const vector DUIDS = { + "wwwwwwww", "BBBBBBBB", "::::::::", "0123456789acdef", + "BBBBBBBB", "$$$$$$$$", "^^^^^^^^", "\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5" +}; + +/// @brief Test fixture class for extended info tests. +class PgSqlExtendedInfoTest : public ::testing::Test { +public: + /// @brief Constructor. + PgSqlExtendedInfoTest() { + // Ensure we have the proper schema with no transient data. + createPgSQLSchema(); + + // Connect to the database. + try { + LeaseMgrFactory::create(validPgSQLConnectionString()); + } catch (...) { + std::cerr << "*** ERROR: unable to open database. The test\n" + "*** environment is broken and must be fixed before\n" + "*** the PostgreSQL tests will run correctly.\n" + "*** The reason for the problem is described in the\n" + "*** accompanying exception output.\n"; + throw; + } + + lease_mgr_ = &(LeaseMgrFactory::instance()); + leases4.clear(); + MultiThreadingMgr::instance().setMode(false); + now_ = time(0); + } + + /// @brief Destructor. + ~PgSqlExtendedInfoTest() { + LeaseMgrFactory::destroy(); + // If data wipe enabled, delete transient data otherwise destroy + // the schema. + destroyPgSQLSchema(); + + leases4.clear(); + MultiThreadingMgr::instance().setMode(false); + } + + /// @brief Create and set v4 leases. + /// + /// @param insert When true insert in the database. + void initLease4(bool insert = true) { + ASSERT_EQ(ADDRESS4.size(), DUIDS.size()); + for (size_t i = 0; i < ADDRESS4.size(); ++i) { + Lease4Ptr lease; + vector hwaddr_data(5, 0x08); + hwaddr_data.push_back(0x80 + i); + HWAddrPtr hwaddr(new HWAddr(hwaddr_data, HTYPE_ETHER)); + vector client_id = createFromString(DUIDS[i]); + IOAddress address(ADDRESS4[i]); + ASSERT_NO_THROW(lease.reset(new Lease4(address, hwaddr, + &client_id[0], + client_id.size(), + 1000, now_, + static_cast(i)))); + leases4.push_back(lease); + if (insert) { + EXPECT_TRUE(lease_mgr_->addLease(lease)); + } + } + ASSERT_EQ(ADDRESS4.size(), leases4.size()); + } + + /// @brief Create a vector of uint8_t from a string. + /// + /// @param content A not empty string holding the content. + /// @return A vector of uint8_t with the given content. + inline vector createFromString(const string& content) { + vector v; + v.resize(content.size()); + memmove(&v[0], &content[0], v.size()); + return (v); + } + + /// @brief Test initLease4. + void testInitLease4(); + + /// @brief Test getLease4ByRelayId. + void testGetLeases4ByRelayId(); + + /// @brief Test getLease4ByRemoteId. + void testGetLeases4ByRemoteId(); + + /// @brief Lease manager. + LeaseMgr* lease_mgr_; + + /// @brief V4 leases. + Lease4Collection leases4; + + /// @brief Current timestamp. + time_t now_; +}; + +/// @brief Verifies that the lease manager can add the v4 leases. +void +PgSqlExtendedInfoTest::testInitLease4() { + initLease4(); + EXPECT_EQ(8, leases4.size()); + IOAddress zero = IOAddress::IPV4_ZERO_ADDRESS(); + Lease4Collection got; + // Use the page version as it returns leases in order. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4(zero, LeasePageSize(100))); + ASSERT_EQ(leases4.size(), got.size()); + for (size_t i = 0; i < leases4.size(); ++i) { + ConstElementPtr expected = leases4[i]->toElement(); + LeasePtr lease = got[i]; + ASSERT_TRUE(lease); + EXPECT_TRUE(expected->equals(*lease->toElement())) + << "expected: " << expected->str() << "\n" + << "got: " << lease->toElement()->str() << "\n"; + } +} + +TEST_F(PgSqlExtendedInfoTest, initLease4) { + testInitLease4(); +} + +TEST_F(PgSqlExtendedInfoTest, initLease4MultiThreading) { + MultiThreadingTest mt(true); + testInitLease4(); +} + +/// @brief Verifies that getLeases4ByRelayId works as expected. +void +PgSqlExtendedInfoTest::testGetLeases4ByRelayId() { + // Lease manager is created with empty tables. + initLease4(false); + + // Create leases. + IOAddress addr0(ADDRESS4[0]); + IOAddress addr1(ADDRESS4[1]); + IOAddress addr2(ADDRESS4[2]); + IOAddress addr3(ADDRESS4[3]); + IOAddress addr4(ADDRESS4[4]); + IOAddress zero = IOAddress::IPV4_ZERO_ADDRESS(); + vector relay_id0 = { 0xaa, 0xbb, 0xcc }; + vector relay_id1 = { 1, 2, 3, 4 }; + vector relay_id2 = createFromString(DUIDS[2]); + string user_context_txt0 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt0 += " \"sub-options\": \"0C03AABBCC\","; + user_context_txt0 += " \"relay-id\": \"AABBCC\" } } }"; + ElementPtr user_context0; + ASSERT_NO_THROW(user_context0 = Element::fromJSON(user_context_txt0)); + string user_context_txt1 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt1 += " \"sub-options\": \"0C0401020304\","; + user_context_txt1 += " \"relay-id\": \"01020304\" } } }"; + ElementPtr user_context1; + ASSERT_NO_THROW(user_context1 = Element::fromJSON(user_context_txt1)); + + Lease4Ptr lease; + // lease0: addr0, id0, now. + lease = leases4[0]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + + // lease1: addr1, id1, now. + lease = leases4[1]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id1; + lease->setContext(user_context1); + + // lease2: addr2, id0, now - 500. + lease = leases4[2]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 500; + + // lease3: addr3, id0, now - 800. + lease = leases4[3]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 800; + + // lease4: addr4, id0, now - 100. + lease = leases4[4]; + ASSERT_TRUE(lease); + lease->relay_id_ = relay_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 100; + + // Add leases. + for (size_t i = 0; i < leases4.size(); ++i) { + EXPECT_TRUE(lease_mgr_->addLease(leases4[i])); + } + + Lease4Collection got; + // Unknown relay id #2: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id2, + zero, + LeasePageSize(100))); + EXPECT_EQ(0, got.size()); + + // Unknown relay id #2, now - 1000, now + 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id2, + zero, + LeasePageSize(100), + now_ - 1000, + now_ + 1000)); + EXPECT_EQ(0, got.size()); + + // Relay id #0, now - 2000, now - 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + now_ - 2000, + now_ - 1000)); + EXPECT_EQ(0, got.size()); + + // Relay id #0, now + 1000, now + 2000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + now_ + 1000, + now_ + 2000)); + EXPECT_EQ(0, got.size()); + + // Relay id #0: 3 entries (0, 2, 3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100))); + ASSERT_EQ(4, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[3]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, partial: 2 entries (0, 2). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, partial from previous: 2 entries (3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr2, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, final partial: no entries. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr4, + LeasePageSize(2))); + EXPECT_EQ(0, got.size()); + + // Relay id #0, from now - 500: 3 entries (0, 2, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + now_ - 500)); + ASSERT_EQ(3, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, to now - 200: 3 entries (2, 3). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(100), + 0, now_ - 200)); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, from now - 500 to now - 100, partial: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + zero, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, from now - 500 to now - 100, partial from 2: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr2, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(relay_id0, lease->relay_id_); + + // Relay id #0, from now - 500 to now - 100, final partial. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRelayId(relay_id0, + addr4, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + EXPECT_EQ(0, got.size()); +} + +TEST_F(PgSqlExtendedInfoTest, getLeases4ByRelayId) { + testGetLeases4ByRelayId(); +} + +TEST_F(PgSqlExtendedInfoTest, getLeases4ByRelayIdMultiThreading) { + MultiThreadingTest mt(true); + testGetLeases4ByRelayId(); +} + +/// @brief Verifies that getLeases4ByRemoteId works as expected. +void +PgSqlExtendedInfoTest::testGetLeases4ByRemoteId() { + // Lease manager is created with empty tables. + initLease4(true); + + // Update leases. + IOAddress addr0(ADDRESS4[0]); + IOAddress addr1(ADDRESS4[1]); + IOAddress addr2(ADDRESS4[2]); + IOAddress addr3(ADDRESS4[3]); + IOAddress addr4(ADDRESS4[4]); + IOAddress zero = IOAddress::IPV4_ZERO_ADDRESS(); + vector remote_id0 = { 1, 2, 3, 4 }; + vector remote_id1 = { 0xaa, 0xbb, 0xcc }; + vector remote_id2 = createFromString(DUIDS[2]); + string user_context_txt0 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt0 += " \"sub-options\": \"020401020304\","; + user_context_txt0 += " \"remote-id\": \"01020304\" } } }"; + ElementPtr user_context0; + ASSERT_NO_THROW(user_context0 = Element::fromJSON(user_context_txt0)); + string user_context_txt1 = "{ \"ISC\": { \"relay-agent-info\": {"; + user_context_txt1 += " \"sub-options\": \"0203AABBCC\","; + user_context_txt1 += " \"remote-id\": \"AABBCC\" } } }"; + ElementPtr user_context1; + ASSERT_NO_THROW(user_context1 = Element::fromJSON(user_context_txt1)); + + Lease4Ptr lease; + // lease0: addr0, id0, now. + lease = leases4[0]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + + // lease1: addr1, id1, now. + lease = leases4[1]; + lease->remote_id_ = remote_id1; + lease->setContext(user_context1); + + // lease2: addr2, id0, now - 500. + lease = leases4[2]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 500; + + // lease3: addr3, id0, now - 800. + lease = leases4[3]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 800; + + // lease4: addr4, id0, now - 100. + lease = leases4[4]; + lease->remote_id_ = remote_id0; + lease->setContext(user_context0); + lease->cltt_ = now_ - 100; + + // Update leases. + for (size_t i = 0; i < leases4.size(); ++i) { + EXPECT_NO_THROW(lease_mgr_->updateLease4(leases4[i])); + } + + Lease4Collection got; + // Unknown remote id #2: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id2, + zero, + LeasePageSize(100))); + EXPECT_EQ(0, got.size()); + + // Unknown remote id #2, now - 1000, now + 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id2, + zero, + LeasePageSize(100), + now_ - 1000, + now_ + 1000)); + EXPECT_EQ(0, got.size()); + + // Remote id #0, now - 2000, now - 1000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + now_ - 2000, + now_ - 1000)); + EXPECT_EQ(0, got.size()); + + // Remote id #0, now + 1000, now + 2000: nothing. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + now_ + 1000, + now_ + 2000)); + EXPECT_EQ(0, got.size()); + + // Remote id #0: 3 entries (0, 2, 3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100))); + ASSERT_EQ(4, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[3]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, partial: 2 entries (0, 2). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, partial from previous: 2 entries (3, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr2, + LeasePageSize(2))); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, final partial: no entries. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr4, + LeasePageSize(2))); + EXPECT_EQ(0, got.size()); + + // Remote id #0, from now - 500: 3 entries (0, 2, 4). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + now_ - 500)); + ASSERT_EQ(3, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[0]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[2]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, to now - 200: 3 entries (2, 3). + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(100), + 0, now_ - 200)); + ASSERT_EQ(2, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + lease = got[1]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[3]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, from now - 500 to now - 100, partial: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + zero, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[2]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, from now - 500 to now - 100, partial from 2: 1 entry. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr2, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + ASSERT_EQ(1, got.size()); + lease = got[0]; + ASSERT_TRUE(lease); + EXPECT_EQ(*lease, *leases4[4]); + EXPECT_EQ(remote_id0, lease->remote_id_); + + // Remote id #0, from now - 500 to now - 100, final partial. + EXPECT_NO_THROW(got = lease_mgr_->getLeases4ByRemoteId(remote_id0, + addr4, + LeasePageSize(1), + now_ - 500, + now_ - 100)); + EXPECT_EQ(0, got.size()); +} + +TEST_F(PgSqlExtendedInfoTest, getLeases4ByRemoteId) { + testGetLeases4ByRemoteId(); +} + +TEST_F(PgSqlExtendedInfoTest, getLeases4ByRemoteIdMultiThreading) { + MultiThreadingTest mt(true); + testGetLeases4ByRemoteId(); +} + +} // namespace