Commit 6d3b534e authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3965] Implemented functions to retrieve expired leases.

parent 86bf7774
......@@ -291,7 +291,6 @@ public:
virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
uint32_t iaid, SubnetID subnet_id) const = 0;
/// @brief returns zero or one IPv6 lease for a given duid+iaid+subnet_id
///
/// This function is mostly intended to be used in unit-tests during the
......@@ -318,6 +317,33 @@ public:
Lease6Ptr getLease6(Lease::Type type, const DUID& duid,
uint32_t iaid, SubnetID subnet_id) const;
/// @brief Returns a collection of expired DHCPv6 leases.
///
/// This method returns at most @c max_leases expired leases. The leases
/// returned haven't been reclaimed, i.e. the database query must exclude
/// reclaimed leases from the results returned.
///
/// @param [out] expired_leases A container to which expired leases returned
/// by the database backend are added.
/// @param max_leases A maximum number of leases to be returned. If this
/// value is set to 0, all expired (but not reclaimed) leases are returned.
virtual void getExpiredLeases6(Lease6Collection& expired_leases,
const size_t max_leases) const = 0;
/// @brief Returns a collection of expired DHCPv4 leases.
///
/// This method returns at most @c max_leases expired leases. The leases
/// returned haven't been reclaimed, i.e. the database query must exclude
/// reclaimed leases from the results returned.
///
/// @param [out] expired_leases A container to which expired leases returned
/// by the database backend are added.
/// @param max_leases A maximum number of leases to be returned. If this
/// value is set to 0, all expired (but not reclaimed) leases are returned.
virtual void getExpiredLeases4(Lease4Collection& expired_leases,
const size_t max_leases) const = 0;
/// @brief Updates IPv4 lease.
///
/// @param lease4 The lease to be updated.
......
......@@ -514,12 +514,60 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type,
return (collection);
}
void
Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
const size_t max_leases) const {
// Obtain the index which segragates leases by state and time.
const Lease6StorageExpirationIndex& index = storage6_.get<ExpirationIndexTag>();
// Retrieve leases which are not reclaimed and which haven't expired. The
// 'less-than' operator will be used for both components of the index. So,
// for the 'state' 'false' is less than 'true'. Also the leases with
// expiration time lower than current time will be returned.
Lease6StorageExpirationIndex::const_iterator ub =
index.upper_bound(boost::make_tuple(false, time_t(NULL)));
// Copy only the number of leases indicated by the max_leases parameter.
for (Lease6StorageExpirationIndex::const_iterator lease = index.begin();
(lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) <
max_leases));
++lease) {
expired_leases.push_back(Lease6Ptr(new Lease6(**lease)));
}
}
void
Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
const size_t max_leases) const {
// Obtain the index which segragates leases by state and time.
const Lease4StorageExpirationIndex& index = storage4_.get<ExpirationIndexTag>();
// Retrieve leases which are not reclaimed and which haven't expired. The
// 'less-than' operator will be used for both components of the index. So,
// for the 'state' 'false' is less than 'true'. Also the leases with
// expiration time lower than current time will be returned.
Lease4StorageExpirationIndex::const_iterator ub =
index.upper_bound(boost::make_tuple(false, time_t(NULL)));
// Copy only the number of leases indicated by the max_leases parameter.
for (Lease4StorageExpirationIndex::const_iterator lease = index.begin();
(lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) <
max_leases));
++lease) {
expired_leases.push_back(Lease4Ptr(new Lease4(**lease)));
}
}
void
Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
// Obtain 'by address' index.
Lease4StorageAddressIndex& index = storage4_.get<AddressIndexTag>();
// Lease must exist if it is to be updated.
Lease4StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_);
if (lease_it == storage4_.end()) {
isc_throw(NoSuchLease, "failed to update the lease with address "
<< lease->addr_ << " - no such lease");
......@@ -532,7 +580,8 @@ Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
lease_file4_->append(*lease);
}
**lease_it = *lease;
// Use replace() to re-index leases.
index.replace(lease_it, Lease4Ptr(new Lease4(*lease)));
}
void
......@@ -540,8 +589,12 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
if (lease_it == storage6_.end()) {
// Obtain 'by address' index.
Lease6StorageAddressIndex& index = storage6_.get<AddressIndexTag>();
// Lease must exist if it is to be updated.
Lease6StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_);
if (lease_it == index.end()) {
isc_throw(NoSuchLease, "failed to update the lease with address "
<< lease->addr_ << " - no such lease");
}
......@@ -553,7 +606,8 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
lease_file6_->append(*lease);
}
**lease_it = *lease;
// Use replace() to re-index leases.
index.replace(lease_it, Lease6Ptr(new Lease6(*lease)));
}
bool
......@@ -871,6 +925,5 @@ void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file)
}
}
} // end of namespace isc::dhcp
} // end of namespace isc
......@@ -259,6 +259,33 @@ public:
uint32_t iaid,
SubnetID subnet_id) const;
/// @brief Returns a collection of expired DHCPv6 leases.
///
/// This method returns at most @c max_leases expired leases. The leases
/// returned haven't been reclaimed, i.e. the database query must exclude
/// reclaimed leases from the results returned.
///
/// @param [out] expired_leases A container to which expired leases returned
/// by the database backend are added.
/// @param max_leases A maximum number of leases to be returned. If this
/// value is set to 0, all expired (but not reclaimed) leases are returned.
virtual void getExpiredLeases6(Lease6Collection& expired_leases,
const size_t max_leases) const;
/// @brief Returns a collection of expired DHCPv4 leases.
///
/// This method returns at most @c max_leases expired leases. The leases
/// returned haven't been reclaimed, i.e. the database query must exclude
/// reclaimed leases from the results returned.
///
/// @param [out] expired_leases A container to which expired leases returned
/// by the database backend are added.
/// @param max_leases A maximum number of leases to be returned. If this
/// value is set to 0, all expired (but not reclaimed) leases are returned.
virtual void getExpiredLeases4(Lease4Collection& expired_leases,
const size_t max_leases) const;
/// @brief Updates IPv4 lease.
///
/// @warning This function does not validate the pointer to the lease.
......
......@@ -12,8 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef INMEMORY_LEASE_STORAGE_H
#define INMEMORY_LEASE_STORAGE_H
#ifndef MEMFILE_LEASE_STORAGE_H
#define MEMFILE_LEASE_STORAGE_H
#include <asiolink/io_address.h>
#include <dhcpsrv/lease.h>
......@@ -31,6 +31,17 @@
namespace isc {
namespace dhcp {
/// @brief Tag for indexes by address.
struct AddressIndexTag { };
/// @brief Tag for indexes by expiration time.
struct ExpirationIndexTag { };
/// @name Multi index containers holding DHCPv4 and DHCPv6 leases.
///
//@{
/// @brief A multi index container holding DHCPv6 leases.
///
/// The leases in the container may be accessed using different indexes:
......@@ -44,6 +55,7 @@ typedef boost::multi_index_container<
// This index sorts leases by IPv6 addresses represented as
// IOAddress objects.
boost::multi_index::ordered_unique<
boost::multi_index::tag<AddressIndexTag>,
boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
>,
......@@ -62,6 +74,24 @@ typedef boost::multi_index_container<
boost::multi_index::member<Lease6, uint32_t, &Lease6::iaid_>,
boost::multi_index::member<Lease6, Lease::Type, &Lease6::type_>
>
>,
// Specification of the third index starts here.
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ExpirationIndexTag>,
// This is a composite index that will be used to search for
// the expired leases. Depending on the value of the first component
// of the search key, the reclaimed or not reclaimed leases will can
// be searched.
boost::multi_index::composite_key<
Lease6,
// The boolean value specifying if lease is reclaimed or not.
boost::multi_index::const_mem_fun<Lease, bool,
&Lease::stateExpiredReclaimed>,
// Lease expiration time.
boost::multi_index::const_mem_fun<Lease, int64_t,
&Lease::getExpirationTime>
>
>
>
> Lease6Storage; // Specify the type name of this container.
......@@ -82,6 +112,7 @@ typedef boost::multi_index_container<
// This index sorts leases by IPv4 addresses represented as
// IOAddress objects.
boost::multi_index::ordered_unique<
boost::multi_index::tag<AddressIndexTag>,
// The IPv4 address are held in addr_ members that belong to
// Lease class.
boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
......@@ -141,11 +172,48 @@ typedef boost::multi_index_container<
// The subnet id is accessed through the subnet_id_ member.
boost::multi_index::member<Lease, SubnetID, &Lease::subnet_id_>
>
>,
// Specification of the fifth index starts here.
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ExpirationIndexTag>,
// This is a composite index that will be used to search for
// the expired leases. Depending on the value of the first component
// of the search key, the reclaimed or not reclaimed leases will can
// be searched.
boost::multi_index::composite_key<
Lease4,
// The boolean value specifying if lease is reclaimed or not.
boost::multi_index::const_mem_fun<Lease, bool,
&Lease::stateExpiredReclaimed>,
// Lease expiration time.
boost::multi_index::const_mem_fun<Lease, int64_t,
&Lease::getExpirationTime>
>
>
>
> Lease4Storage; // Specify the type name for this container.
//@}
/// @name Indexes used by the multi index containers
///
//@{
/// @brief DHCPv6 lease storage index by address.
typedef Lease6Storage::index<AddressIndexTag>::type Lease6StorageAddressIndex;
/// @brief DHCPv6 lease storage index by expiration time.
typedef Lease6Storage::index<ExpirationIndexTag>::type Lease6StorageExpirationIndex;
/// @brief DHCPv4 lease storage index by address.
typedef Lease4Storage::index<AddressIndexTag>::type Lease4StorageAddressIndex;
/// @brief DHCPv4 lease storage index by exiration time.
typedef Lease4Storage::index<ExpirationIndexTag>::type Lease4StorageExpirationIndex;
//@}
} // end of isc::dhcp namespace
} // end of isc namespace
#endif // INMEMORY_LEASE_STORAGE_H
#endif // MEMFILE_LEASE_STORAGE_H
......@@ -1657,6 +1657,246 @@ GenericLeaseMgrTest::testVersion(int major, int minor) {
EXPECT_EQ(minor, lmptr_->getVersion().second);
}
void
GenericLeaseMgrTest::testGetExpiredLeases4() {
// Get the leases to be used for the test.
vector<Lease4Ptr> leases = createLeases4();
// Make sure we have at least 6 leases there.
ASSERT_GE(leases.size(), 6);
// Use the same current time for all leases.
time_t current_time = time_t(NULL);
// Add them to the database
for (size_t i = 0; i < leases.size(); ++i) {
// Mark every other lease as expired.
if (i % 2 == 0) {
// Set client last transmission time to the value older than the
// valid lifetime to make it expired. The expiration time also
// depends on the lease index, so as we can later check that the
// leases are ordered by the expiration time.
leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
} else {
// Set current time as cltt for remaining leases. These leases are
// not expired.
leases[i]->cltt_ = current_time;
}
ASSERT_TRUE(lmptr_->addLease(leases[i]));
}
// Retrieve expired leases.
Lease4Collection expired_leases;
lmptr_->getExpiredLeases4(expired_leases, 1000);
// Leases with even indexes should be returned as expired.
ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
// The expired leases should be returned from the most to least expired.
// This matches the reverse order to which they have been added.
for (Lease4Collection::reverse_iterator lease = expired_leases.rbegin();
lease != expired_leases.rend(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.rbegin(), lease));
// Multiple current index by two, because only leases with even indexes
// should have been returned.
EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
}
// Update current time for the next test.
current_time = time_t(NULL);
// Also, remove expired leases collected during the previous test.
expired_leases.clear();
// This time let's reverse the expiration time and see if they will be returned
// in the correct order.
for (int i = 0; i < leases.size(); ++i) {
// Update the time of expired leases with even indexes.
if (i % 2 == 0) {
leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
} else {
// Make sure remaining leases remain unexpired.
leases[i]->cltt_ = current_time + 100;
}
ASSERT_NO_THROW(lmptr_->updateLease4(leases[i]));
}
// Retrieve expired leases again. The limit of 0 means return all expired
// leases.
lmptr_->getExpiredLeases4(expired_leases, 0);
// The same leases should be returned.
ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
// This time leases should be returned in the non-reverse order.
for (Lease4Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
}
// Remember expired leases returned.
std::vector<Lease4Ptr> saved_expired_leases = expired_leases;
// Remove expired leases again.
expired_leases.clear();
// Limit the number of leases to be returned to 2.
lmptr_->getExpiredLeases4(expired_leases, 2);
// Make sure we have exactly 2 leases returned.
ASSERT_EQ(2, expired_leases.size());
// Test that most expired leases have been returned.
for (Lease4Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
}
// Mark every other expired lease as reclaimed.
for (int i = 0; i < saved_expired_leases.size(); ++i) {
if (i % 2 != 0) {
saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
}
ASSERT_NO_THROW(lmptr_->updateLease4(saved_expired_leases[i]));
}
expired_leases.clear();
// This the returned leases should exclude reclaimed ones. So the number
// of returned leases should be roughly half of the expired leases.
lmptr_->getExpiredLeases4(expired_leases, 0);
ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2),
expired_leases.size());
// Make sure that returned leases are those that are not reclaimed, i.e.
// those that have even index.
for (Lease4Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
}
}
void
GenericLeaseMgrTest::testGetExpiredLeases6() {
// Get the leases to be used for the test.
vector<Lease6Ptr> leases = createLeases6();
// Make sure we have at least 6 leases there.
ASSERT_GE(leases.size(), 6);
// Use the same current time for all leases.
time_t current_time = time_t(NULL);
// Add them to the database
for (size_t i = 0; i < leases.size(); ++i) {
// Mark every other lease as expired.
if (i % 2 == 0) {
// Set client last transmission time to the value older than the
// valid lifetime to make it expired. The expiration time also
// depends on the lease index, so as we can later check that the
// leases are ordered by the expiration time.
leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
} else {
// Set current time as cltt for remaining leases. These leases are
// not expired.
leases[i]->cltt_ = current_time;
}
ASSERT_TRUE(lmptr_->addLease(leases[i]));
}
// Retrieve expired leases.
Lease6Collection expired_leases;
lmptr_->getExpiredLeases6(expired_leases, 1000);
// Leases with even indexes should be returned as expired.
ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
// The expired leases should be returned from the most to least expired.
// This matches the reverse order to which they have been added.
for (Lease6Collection::reverse_iterator lease = expired_leases.rbegin();
lease != expired_leases.rend(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.rbegin(), lease));
// Multiple current index by two, because only leases with even indexes
// should have been returned.
EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
}
// Update current time for the next test.
current_time = time_t(NULL);
// Also, remove expired leases collected during the previous test.
expired_leases.clear();
// This time let's reverse the expiration time and see if they will be returned
// in the correct order.
for (int i = 0; i < leases.size(); ++i) {
// Update the time of expired leases with even indexes.
if (i % 2 == 0) {
leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
} else {
// Make sure remaining leases remain unexpired.
leases[i]->cltt_ = current_time + 100;
}
ASSERT_NO_THROW(lmptr_->updateLease6(leases[i]));
}
// Retrieve expired leases again. The limit of 0 means return all expired
// leases.
lmptr_->getExpiredLeases6(expired_leases, 0);
// The same leases should be returned.
ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
// This time leases should be returned in the non-reverse order.
for (Lease6Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
}
// Remember expired leases returned.
std::vector<Lease6Ptr> saved_expired_leases = expired_leases;
// Remove expired leases again.
expired_leases.clear();
// Limit the number of leases to be returned to 2.
lmptr_->getExpiredLeases6(expired_leases, 2);
// Make sure we have exactly 2 leases returned.
ASSERT_EQ(2, expired_leases.size());
// Test that most expired leases have been returned.
for (Lease6Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
}
// Mark every other expired lease as reclaimed.
for (int i = 0; i < saved_expired_leases.size(); ++i) {
if (i % 2 != 0) {
saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
}
ASSERT_NO_THROW(lmptr_->updateLease6(saved_expired_leases[i]));
}
expired_leases.clear();
// This the returned leases should exclude reclaimed ones. So the number
// of returned leases should be roughly half of the expired leases.
lmptr_->getExpiredLeases6(expired_leases, 0);
ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2),
expired_leases.size());
// Make sure that returned leases are those that are not reclaimed, i.e.
// those that have even index.
for (Lease6Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
}
}
}; // namespace test
}; // namespace dhcp
......
......@@ -265,6 +265,26 @@ public:
/// @param minor Expected minor version to be reported.
void testVersion(int major, int minor);
/// @brief Checks that the expired DHCPv4 leases can be retrieved.
///
/// This test checks the following:
/// - all expired and not reclaimed leases are retured
/// - number of leases returned can be limited
/// - leases are returned in the order from the most expired to the
/// least expired
/// - reclaimed leases are not returned.
void testGetExpiredLeases4();
/// @brief Checks that the expired DHCPv6 leases can be retrieved.
///
/// This test checks the following:
/// - all expired and not reclaimed leases are retured
/// - number of leases returned can be limited
/// - leases are returned in the order from the most expired to the
/// least expired
/// - reclaimed leases are not returned.
void testGetExpiredLeases6();
/// @brief String forms of IPv4 addresses
std::vector<std::string> straddress4_;
......
// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -173,6 +173,23 @@ public:
return (leases6_);
}
/// @brief Returns expired DHCPv6 leases.
///
/// This method is not implemented.
virtual void getExpiredLeases6(Lease6Collection&, const size_t) const {
isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases6 is not"
" implemented");
}
/// @brief Returns expired DHCPv4 leases.
///
/// This method is not implemented.
virtual void getExpiredLeases4(Lease4Collection&, const size_t) const {
isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases4 is not"
" implemented");
}