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
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.
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");
lease_file4_->append(*lease);
}
- **lease_it = *lease;
+ // Use replace() to re-index leases.
+ index.replace(lease_it, Lease4Ptr(new Lease4(*lease)));
}
void
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");
}
lease_file6_->append(*lease);
}
- **lease_it = *lease;
+ // Use replace() to re-index leases.
+ index.replace(lease_it, Lease6Ptr(new Lease6(*lease)));
}
bool
}
}
-
} // end of namespace isc::dhcp
} // end of namespace isc
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.
// 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>
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:
// 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_>
>,
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.
// 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_>
// 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
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
/// @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
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");
+ }
+
/// @brief Updates IPv4 lease.
///
/// @param lease4 The lease to be updated.
testGetLeases6DuidSize();
}
+/// @brief Check that the expired DHCPv4 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(MemfileLeaseMgrTest, getExpiredLeases4) {
+ startBackend(V4);
+ testGetExpiredLeases4();
+}
+
+/// @brief Check that the expired DHCPv6 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(MemfileLeaseMgrTest, getExpiredLeases6) {
+ startBackend(V6);
+ testGetExpiredLeases6();
+}
+
/// @brief Check that getLease6 methods discriminate by lease type.
///
/// Adds six leases, two per lease type all with the same duid and iad but