From: Marcin Siodelski Date: Thu, 20 Aug 2015 10:21:26 +0000 (+0200) Subject: [3965] Added capability to remove expired-reclaimed leases. X-Git-Tag: trac3238_base~1^2~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7c9cdc18b7fee53102dd9fe0c4fb581cabb14a4a;p=thirdparty%2Fkea.git [3965] Added capability to remove expired-reclaimed leases. --- diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index ad277909b2..8166c52c2f 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -364,6 +364,20 @@ public: /// @return true if deletion was successful, false if no such lease exists virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0; + /// @brief Deletes all expired and reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual void deleteExpiredReclaimedLeases4(const uint32_t secs) = 0; + + /// @brief Deletes all expired and reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual void deleteExpiredReclaimedLeases6(const uint32_t secs) = 0; + /// @brief Return backend type /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc index 23c22b85ba..2aa7116436 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.cc +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace { @@ -513,7 +514,7 @@ Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases, // 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))); + index.upper_bound(boost::make_tuple(false, time(NULL))); // Copy only the number of leases indicated by the max_leases parameter. for (Lease6StorageExpirationIndex::const_iterator lease = index.begin(); @@ -535,7 +536,7 @@ Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, // 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))); + index.upper_bound(boost::make_tuple(false, time(NULL))); // Copy only the number of leases indicated by the max_leases parameter. for (Lease4StorageExpirationIndex::const_iterator lease = index.begin(); @@ -645,6 +646,52 @@ Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) { } } +void +Memfile_LeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) { + deleteExpiredReclaimedLeases(secs, storage4_); +} + +void +Memfile_LeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { + deleteExpiredReclaimedLeases(secs, storage6_); +} + +template +void +Memfile_LeaseMgr::deleteExpiredReclaimedLeases(const uint32_t secs, + StorageType& storage) const { + // Obtain the index which segragates leases by state and time. + IndexType& index = storage.template get(); + + // This returns the first element which is greater than the specified + // tuple (true, time(NULL) - secs). However, the range between the + // beginnng of the index and returned element also includes all the + // elements for which the first value is false (lease state is NOT + // reclaimed), because false < true. All elements between the + // beginning of the index and the element returned, for which the + // first value is true, represent the reclaimed leases which should + // be deleted, because their expiration time + secs has occured earlier + // than current time. + typename IndexType::const_iterator upper_limit = + index.upper_bound(boost::make_tuple(true, time(NULL) - secs)); + + // Now, we have to exclude all elements of the index which represent + // leases in the state other than reclaimed - with the first value + // in the index equal to false. Note that elements in the index are + // ordered from the lower to the higher ones. So, all elements with + // the first value of false are placed before the elements with the + // value of true. Hence, we have to find the first element which + // contains value of true. The time value is the lowest possible. + typename IndexType::const_iterator lower_limit = + index.upper_bound(boost::make_tuple(true, std::numeric_limits::min())); + + // If there are some elements in this range, delete them. + if (std::distance(lower_limit, upper_limit) > 0) { + index.erase(lower_limit, upper_limit); + } +} + + std::string Memfile_LeaseMgr::getDescription() const { return (std::string("This is a dummy memfile backend implementation.\n" diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h index 6be41bd93d..6717862dbc 100644 --- a/src/lib/dhcpsrv/memfile_lease_mgr.h +++ b/src/lib/dhcpsrv/memfile_lease_mgr.h @@ -314,6 +314,46 @@ public: /// @return true if deletion was successful, false if no such lease exists virtual bool deleteLease(const isc::asiolink::IOAddress& addr); + /// @brief Deletes all expired-reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual void deleteExpiredReclaimedLeases4(const uint32_t secs); + + /// @brief Deletes all expired-reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual void deleteExpiredReclaimedLeases6(const uint32_t secs); + +private: + + /// @brief Deletes all expired-reclaimed leases. + /// + /// This private method is called by both of the public methods: + /// @c deleteExpiredReclaimedLeases4 and + /// @c deleteExpiredReclaimedLeases6 to remove all expired + /// reclaimed DHCPv4 or DHCPv6 leases respectively. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + /// @param storage Reference to the container where leases are held. + /// Some expired-reclaimed leases will be removed from this container. + /// + /// @tparam IndexType Index type to be used to search for the + /// expired-reclaimed leases, i.e. + /// @c Lease4StorageExpirationIndex or @c Lease6StorageExpirationIndex. + /// @tparam StorageType Type of storage where leases are held, i.e. + /// @c Lease4Storage or @c Lease6Storage. + template + void deleteExpiredReclaimedLeases(const uint32_t secs, + StorageType& storage) const; + +public: + /// @brief Return backend type /// /// Returns the type of the backend. diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index de41f6ed25..f8ef01fed0 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -1665,7 +1665,7 @@ GenericLeaseMgrTest::testGetExpiredLeases4() { ASSERT_GE(leases.size(), 6); // Use the same current time for all leases. - time_t current_time = time_t(NULL); + time_t current_time = time(NULL); // Add them to the database for (size_t i = 0; i < leases.size(); ++i) { @@ -1702,7 +1702,7 @@ GenericLeaseMgrTest::testGetExpiredLeases4() { } // Update current time for the next test. - current_time = time_t(NULL); + current_time = time(NULL); // Also, remove expired leases collected during the previous test. expired_leases.clear(); @@ -1785,7 +1785,7 @@ GenericLeaseMgrTest::testGetExpiredLeases6() { ASSERT_GE(leases.size(), 6); // Use the same current time for all leases. - time_t current_time = time_t(NULL); + time_t current_time = time(NULL); // Add them to the database for (size_t i = 0; i < leases.size(); ++i) { @@ -1822,7 +1822,7 @@ GenericLeaseMgrTest::testGetExpiredLeases6() { } // Update current time for the next test. - current_time = time_t(NULL); + current_time = time(NULL); // Also, remove expired leases collected during the previous test. expired_leases.clear(); @@ -1897,6 +1897,163 @@ GenericLeaseMgrTest::testGetExpiredLeases6() { } } +void +GenericLeaseMgrTest::testDeleteExpiredReclaimedLeases4() { + // Get the leases to be used for the test. + vector leases = createLeases4(); + // Make sure we have at least 6 leases there. + ASSERT_GE(leases.size(), 6); + + time_t current_time = time(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. We also substract the value + // of 10, 20, 30, 40 etc, depending on the lease index. This + // simulates different expiration times for various leases. + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - i * 10; + // Set reclaimed state. + leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED; + + } else { + // Other leases are left as not expired - client last transmission + // time set to current time. + leases[i]->cltt_ = current_time; + } + ASSERT_TRUE(lmptr_->addLease(leases[i])); + } + + // Keep reclaimed lease for 15 seconds after expiration. + const uint32_t lease_affinity_time = 15; + + // Delete expired and reclaimed leases which have expired earlier than + // 15 seconds ago. This should affect leases with index 2, 3, 4 etc. + ASSERT_NO_THROW(lmptr_->deleteExpiredReclaimedLeases4(lease_affinity_time)); + + for (size_t i = 0; i < leases.size(); ++i) { + // Obtain lease from the server. + Lease4Ptr lease = lmptr_->getLease4(leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "Lease with index " << i + << " should have been deleted, but it was not"; + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "Lease with index " << i + << " shouldn't have been deleted, but it was"; + } + } + + // Make sure we can make another attempt, when there are no more leases + // to be deleted. + ASSERT_NO_THROW(lmptr_->deleteExpiredReclaimedLeases4(lease_affinity_time)); + + for (size_t i = 0; i < leases.size(); ++i) { + // Obtain lease from the server. + Lease4Ptr lease = lmptr_->getLease4(leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "Lease with index " << i + << " should have been deleted, but it was not"; + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "Lease with index " << i + << " shouldn't have been deleted, but it was"; + } + } +} + +void +GenericLeaseMgrTest::testDeleteExpiredReclaimedLeases6() { + // Get the leases to be used for the test. + vector leases = createLeases6(); + // Make sure we have at least 6 leases there. + ASSERT_GE(leases.size(), 6); + + time_t current_time = time(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. We also substract the value + // of 10, 20, 30, 40 etc, depending on the lease index. This + // simulates different expiration times for various leases. + leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - i * 10; + // Set reclaimed state. + leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED; + + } else { + // Other leases are left as not expired - client last transmission + // time set to current time. + leases[i]->cltt_ = current_time; + } + ASSERT_TRUE(lmptr_->addLease(leases[i])); + } + + // Keep reclaimed lease for 15 seconds after expiration. + const uint32_t lease_affinity_time = 15; + + // Delete expired and reclaimed leases which have expired earlier than + // 15 seconds ago. This should affect leases with index 2, 3, 4 etc. + ASSERT_NO_THROW(lmptr_->deleteExpiredReclaimedLeases6(lease_affinity_time)); + + for (size_t i = 0; i < leases.size(); ++i) { + // Obtain lease from the server. + Lease6Ptr lease = lmptr_->getLease6(leases[i]->type_, leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "Lease with index " << i + << " should have been deleted, but it was not"; + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "Lease with index " << i + << " shouldn't have been deleted, but it was"; + } + } + + // Make sure we can make another attempt, when there are no more leases + // to be deleted. + ASSERT_NO_THROW(lmptr_->deleteExpiredReclaimedLeases6(lease_affinity_time)); + + for (size_t i = 0; i < leases.size(); ++i) { + // Obtain lease from the server. + Lease6Ptr lease = lmptr_->getLease6(leases[i]->type_, leases[i]->addr_); + + // If the lease is reclaimed and the expiration time passed more than + // 15 seconds ago, the lease should have been deleted. + if (leases[i]->stateExpiredReclaimed() && + ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) { + EXPECT_FALSE(lease) << "Lease with index " << i + << " should have been deleted, but it was not"; + + } else { + // If the lease is not reclaimed or it has expired less than + // 15 seconds ago, the lease should still be there. + EXPECT_TRUE(lease) << "Lease with index " << i + << " shouldn't have been deleted, but it was"; + } + } +} }; // namespace test }; // namespace dhcp diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index 694ecf5c1d..933e591002 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-215 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 @@ -285,6 +285,22 @@ public: /// - reclaimed leases are not returned. void testGetExpiredLeases6(); + /// @brief Checks that selected expired-reclaimed DHCPv6 leases + /// are removed. + /// + /// This creates a number of DHCPv6 leases and marks some of them + /// as expired-reclaimed. It later verifies that the expired-reclaimed + /// leases can be removed. + void testDeleteExpiredReclaimedLeases6(); + + /// @brief Checks that selected expired-reclaimed DHCPv4 leases + /// are removed. + /// + /// This creates a number of DHCPv4 leases and marks some of them + /// as expired-reclaimed. It later verifies that the expired-reclaimed + /// leases can be removed. + void testDeleteExpiredReclaimedLeases4(); + /// @brief String forms of IPv4 addresses std::vector straddress4_; diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc index 84236b58d4..01ff896c2d 100644 --- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc @@ -214,6 +214,26 @@ public: return (false); } + /// @brief Deletes all expired and reclaimed DHCPv4 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual void deleteExpiredReclaimedLeases4(const uint32_t) { + isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases4" + " is not implemented"); + } + + /// @brief Deletes all expired and reclaimed DHCPv6 leases. + /// + /// @param secs Number of seconds since expiration of leases before + /// they can be removed. Leases which have expired later than this + /// time will not be deleted. + virtual void deleteExpiredReclaimedLeases6(const uint32_t) { + isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases6" + " is not implemented"); + } + /// @brief Returns backend type. /// /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc index 9ae806c78c..1467e353db 100644 --- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc @@ -905,6 +905,18 @@ TEST_F(MemfileLeaseMgrTest, getExpiredLeases6) { testGetExpiredLeases6(); } +/// @brief Check that expired reclaimed DHCPv6 leases are removed. +TEST_F(MemfileLeaseMgrTest, deleteExpiredReclaimedLeases6) { + startBackend(V6); + testDeleteExpiredReclaimedLeases6(); +} + +/// @brief Check that expired reclaimed DHCPv4 leases are removed. +TEST_F(MemfileLeaseMgrTest, deleteExpiredReclaimedLeases4) { + startBackend(V4); + testDeleteExpiredReclaimedLeases4(); +} + /// @brief Check that getLease6 methods discriminate by lease type. /// /// Adds six leases, two per lease type all with the same duid and iad but