From: Marcin Siodelski Date: Tue, 8 Sep 2015 14:25:06 +0000 (+0200) Subject: [3973] Implemented first version of the lease reclamation routine. X-Git-Tag: trac4065_base^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c8837f49d0bd0ccb235fd2636033ba483e4cd170;p=thirdparty%2Fkea.git [3973] Implemented first version of the lease reclamation routine. --- diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index fa66d2e35a..8afc1182ac 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -14,8 +14,12 @@ #include +#include +#include #include #include +#include +#include #include #include #include @@ -34,6 +38,7 @@ using namespace isc::asiolink; using namespace isc::dhcp; +using namespace isc::dhcp_ddns; using namespace isc::hooks; using namespace isc::stats; @@ -1272,6 +1277,66 @@ AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases return (updated_leases); } +void +AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout, + const bool remove_lease) { + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + + Lease6Collection leases; + lease_mgr.getExpiredLeases6(leases, max_leases); + + for (Lease6Collection::const_iterator lease_it = leases.begin(); + lease_it != leases.end(); ++lease_it) { + + /// @todo execute a lease6_expire hook here + + /// @todo perform DNS update here + queueNameChangeRequest(*lease_it, *(*lease_it)->duid_); + + // Reclaim the lease - depending on the configuration, set the + // expired-reclaimed state or simply remove it. + if (remove_lease) { + LeaseMgrFactory::instance().deleteLease((*lease_it)->addr_); + + } else { + (*lease_it)->state_ = Lease::STATE_EXPIRED_RECLAIMED; + LeaseMgrFactory::instance().updateLease6(*lease_it); + } + } +} + +template +void +AllocEngine::queueNameChangeRequest(const LeasePtrType& lease, + const IdentifierType& identifier) const { + + if (lease->hostname_.empty() || !lease->fqdn_fwd_ || !lease->fqdn_rev_) { + return; + } + + if (!CfgMgr::instance().getD2ClientMgr().ddnsEnabled()) { + return; + } + + std::vector hostname_wire; + try { + OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true); + + } catch (const std::exception& ex) { + + } + + isc::dhcp_ddns::D2Dhcid dhcid(identifier, hostname_wire); + NameChangeRequestPtr ncr; + ncr.reset(new NameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, + lease->fqdn_fwd_, lease->fqdn_rev_, + lease->hostname_, + lease->addr_.toText(), + dhcid, 0, lease->valid_lft_)); + + CfgMgr::instance().getD2ClientMgr().sendRequest(ncr); +} + } // end of isc::dhcp namespace } // end of isc namespace diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h index cd8d7774c2..0bf1db7894 100644 --- a/src/lib/dhcpsrv/alloc_engine.h +++ b/src/lib/dhcpsrv/alloc_engine.h @@ -490,6 +490,21 @@ public: /// @return Returns renewed lease. Lease6Collection renewLeases6(ClientContext6& ctx); + /// @brief Reclaims expired leases. + /// + /// This method retrieves a collection of expired leases and reclaims them. + /// See http://kea.isc.org/wiki/LeaseExpirationDesign#LeasesReclamationRoutine + /// for the details. + /// + /// @param max_leases Maximum number of leases to be reclaimed. + /// @param timeout Maximum amount of time that the reclaimation routine + /// may be processing expired leases, expressed in seconds. + /// @param remove_lease A boolean value indicating if the lease should + /// be removed when it is reclaimed (if true) or it should be left in the + /// database in the "expired-reclaimed" state (if false). + void reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout, + const bool remove_lease); + /// @brief Attempts to find appropriate host reservation. /// /// Attempts to find appropriate host reservation in HostMgr. If found, it @@ -663,6 +678,10 @@ private: /// @param lease IPv6 lease to be extended. void extendLease6(ClientContext6& ctx, Lease6Ptr lease); + template + void queueNameChangeRequest(const LeasePtrType& lease, + const IdentifierType& identifier) const; + public: /// @brief Context information for the DHCPv4 lease allocation. diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index baad1d2ed6..210aece60a 100644 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/src/lib/dhcpsrv/tests/Makefile.am @@ -56,6 +56,7 @@ TESTS += libdhcpsrv_unittests libdhcpsrv_unittests_SOURCES = run_unittests.cc libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc libdhcpsrv_unittests_SOURCES += alloc_engine_utils.cc alloc_engine_utils.h +libdhcpsrv_unittests_SOURCES += alloc_engine_expiration_unittest.cc libdhcpsrv_unittests_SOURCES += alloc_engine_hooks_unittest.cc libdhcpsrv_unittests_SOURCES += alloc_engine4_unittest.cc libdhcpsrv_unittests_SOURCES += alloc_engine6_unittest.cc diff --git a/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc new file mode 100644 index 0000000000..b8d8dc8636 --- /dev/null +++ b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc @@ -0,0 +1,641 @@ +// Copyright (C) 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 +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace isc::dhcp_ddns; + +namespace { + +/// @brief Number of leases to be initialized for each test. +/// +/// This value is expected by some of the tests to be multiple +/// of 10. +const unsigned int TEST_LEASES_NUM = 100; + +/// @brief Structure wrapping a lower limit within the collection +/// of leases. +/// +/// We're using this structure rather than a size_t value directly +/// to make API of the test fixture class more readable, i.e. the +/// struct name indicates the purpose of the value being held. +struct LowerBound { + /// @brief Constructor. + /// + /// @param lower_bound Interger value wrapped by the structure. + explicit LowerBound(const size_t lower_bound) + : lower_bound_(lower_bound) { }; + + /// @brief Operator returning the size_t value wrapped. + operator size_t() const { + return (lower_bound_); + } + + /// @brief Value wrapped in the structure. + size_t lower_bound_; +}; + +/// @brief Structure wrapping an upper limit within the collection +/// of leases. +/// +/// We're using this structure rather than a size_t value directly +/// to make API of the test fixture class more readable, i.e. the +/// struct name indicates the purpose of the value being held. +struct UpperBound { + /// @brief Constructor. + /// + /// @param lower_bound Interger value wrapped by the structure. + explicit UpperBound(const size_t upper_bound) + : upper_bound_(upper_bound) { }; + + /// @brief Operator returning the size_t value wrapped. + operator size_t() const { + return (upper_bound_); + } + + /// @brief Value wrapped in the structure. + size_t upper_bound_; +}; + +/// @brief Test fixture class for the lease reclamation routines in the +/// @c AllocEngine. +/// +/// This class implements infrastructure for testing leases reclamation +/// routines. The lease reclamation routine has the following +/// characteristic: +/// - it processes multiple leases, +/// - leases are processed in certain order, +/// - number of processed leases may be limited by the parameters, +/// - maxium duration of the lease reclamation routine may be limited, +/// - reclaimed leases may be marked as reclaimed or deleted, +/// - DNS records for some of the leases must be removed when the lease +/// is reclaimed and DNS updates are enabled, +/// - hooks must be invoked (if installed) for each reclaimed lease +/// +/// The typical test requires many leases to be initialized and stored +/// in the lease database for the test. The test fixture class creates +/// these leases upon construction. It is possible to modify these +/// leases to test various properties of the lease reclamation routine +/// as listed above. For example: some of the leases may be marked +/// as expired or hostname may be cleared for some of the leases to +/// check that DNS updates are not generated for them. +/// +/// The tests are built around the +/// @c ExpirationAllocEngine6Test::testLeases methods. These methods +/// verify that the certain operations have been performed by the +/// lease reclamation routine on selected leases. The leases for which +/// certain conditions should be met are selected using the "index +/// algorithms". Various index algorithms are implemented in the +/// test fixture class as static functions and the algorithm is +/// selected by passing function pointer to the @c testLeases method. +/// +/// Examples of index algorithms are: +/// - evenLeaseIndex(index) - picks even index numbers, +/// - oddLeaseIndex(index) - picks odd index numbers, +/// - allLeasesIndexes(index) - picks all index number. +/// +/// For example, the test may use the @c evenLeaseIndex algorithm +/// to mark leases with even indexes as expired and then test whether +/// leases with even indexes have been successfully reclaimed. +/// +/// The "lease algorithm" verifies if the given lease fulfils the +/// specific conditions after reclamation. These are the examples of +/// the lease algorithms: +/// - leaseExists - lease still exists in the database, +/// - leaseDoesntExist - lease removed from the database, +/// - leaseReclaimed - lease exists but has reclaimed status, +/// - leaseNotReclaimed - lease exists and is not in the reclaimed status, +/// - dnsUpdateGeneratedForLease - DNS updates generated for lease, +/// - dnsUpdateNotGeneratedForLease - DNS updates not generated for lease +/// +/// The combination of index algorithm and lease algorithm allows for +/// verifying that the whole sets of leases in the lease database fulfil +/// certain conditions. For example, it is possible to verify that +/// after lease reclamation leases with even indexes have state set to +/// "expired-reclaimed". +/// +/// See @c ExpirationAllocEngine6Test::testLeases for further details. +class ExpirationAllocEngine6Test : public AllocEngine6Test { +public: + + /// @brief Type definition for the lease algorithm. + typedef boost::function LeaseAlgorithmFun; + /// @brief type definition for the lease index algorithm. + typedef boost::function IndexAlgorithmFun; + + /// @brief Class constructor. + /// + /// This constructor initializes @c TEST_LEASES_NUM leases and + /// stores them in the lease manager. + ExpirationAllocEngine6Test(); + + /// @brief Class destructor. + /// + /// It stops the D2 client, if it is running. + virtual ~ExpirationAllocEngine6Test(); + + /// @brief Creates collection of leases for a test. + /// + /// It is called internally at the construction time. + void createLeases(); + + /// @brief Starts D2 client. + void enableDDNS() const; + + /// @brief No-op error handler for the D2 client. + static void d2ErrorHandler(const dhcp_ddns::NameChangeSender::Result, + dhcp_ddns::NameChangeRequestPtr&) { + } + + /// @brief Marks a lease as expired. + /// + /// @param lease_index Lease index. Must be between 0 and + /// @c TEST_LEASES_NUM. + /// @param secs Offset of the expiration time since now. For example + /// a value of 2 would set the lease expiration time to 2 seconds ago. + void expire(const unsigned int lease_index, const time_t secs); + + /// @brief Test selected leases using the specified algorithms. + /// + /// This function picks leases from the range of 0 thru + /// @c TEST_LEASES_NUM and selects the ones to be verified using the + /// specified index algorithm. Selected leases are tested using + /// the specified lease algorithm. + /// + /// @param lease_algorithm Pointer to the lease algorithm function. + /// @param index_algorithm Pointer to the index algorithm function. + bool testLeases(const LeaseAlgorithmFun& lease_algorithm, + const IndexAlgorithmFun& index_algorithm) const; + + + /// @brief Test selected leases using the specified algorithms. + /// + /// This function picks leases from the range of @c lower_bound + /// thru @c upper_bound and selects the ones to be verified using the + /// specified index algorithm. Selected leases are tested using the + /// specified lease algorithm. + /// + /// @param lease_algorithm Pointer to the lease algorithm function. + /// @param index_algorithm Pointer to the index algorithm function. + /// @param lower_bound First index in the range. + /// @param upper_bound Last + 1 index in the range. + bool testLeases(const LeaseAlgorithmFun& lease_algorithm, + const IndexAlgorithmFun& index_algorithm, + const LowerBound& lower_bound, + const UpperBound& upper_bound) const; + + /// @brief Index algorithm selecting even indexes. + /// + /// @param index Lease index. + /// @return true if index is an even number. + static bool evenLeaseIndex(const size_t index) { + return (index % 2); + } + + /// @brief Index algorithm selecting odd indexes. + /// + /// @param index Lease index. + /// @return true if index is an odd number. + static bool oddLeaseIndex(const size_t index) { + return (!evenLeaseIndex(index)); + } + + /// @brief Index algorithm selecting all indexes. + /// + /// @param index Lease index. + /// @return true if the index is in the range of [0 .. TEST_LEASES_NUM). + static bool allLeaseIndexes(const size_t index) { + return (index < TEST_LEASES_NUM); + } + + /// @brief Lease algorithm checking if lease exists. + /// + /// @param lease Pointer to lease. + /// @return true if lease pointer is non-null. + static bool leaseExists(const Lease6Ptr& lease) { + return (static_cast(lease)); + } + + /// @brief Lease algorithm checking if lease doesn't exist. + /// + /// @param lease Pointer to lease. + /// @return true if lease pointer is null. + static bool leaseDoesntExist(const Lease6Ptr& lease) { + return (static_cast(!lease)); + } + + /// @brief Lease algorithm checking if lease state is expired-reclaimed. + /// + /// @param lease Pointer to lease. + /// @return true if lease state is "expired-reclaimed". + static bool leaseReclaimed(const Lease6Ptr& lease) { + return (lease && lease->stateExpiredReclaimed()); + } + + /// @brief Lease algorithm checking if lease state is not + /// expired-reclaimed. + /// + /// @param lease Pointer to lease. + /// @return true if lease state is not "expired-reclaimed". + static bool leaseNotReclaimed(const Lease6Ptr& lease) { + return (lease && !lease->stateExpiredReclaimed()); + } + + /// @brief Lease algorithm checking if removal name change request + /// has been generated for lease. + /// + /// @param lease Pointer to lease. + /// @return true if NCR has been generated for the lease. + static bool dnsUpdateGeneratedForLease(const Lease6Ptr& lease); + + /// @brief Lease algorithm checking if removal name change request + /// hasn't been generated for lease. + /// + /// @param lease Pointer to lease. + /// @return true if NCR has not been generated for the lease. + static bool dnsUpdateNotGeneratedForLease(const Lease6Ptr& lease); + + /// @brief Collection of leases created at construction time. + Lease6Collection leases_; + +}; + +ExpirationAllocEngine6Test::ExpirationAllocEngine6Test() + : AllocEngine6Test() { + createLeases(); +} + +ExpirationAllocEngine6Test::~ExpirationAllocEngine6Test() { + // Stop D2 client if running and remove all queued name change + // requests. + D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr(); + if (mgr.amSending()) { + mgr.stopSender(); + mgr.clearQueue(); + } +} + +void +ExpirationAllocEngine6Test::createLeases() { + // Create TEST_LEASES_NUM leases. + for (int i = 0; i < TEST_LEASES_NUM; ++i) { + // DUID + std::ostringstream duid_s; + duid_s << "01020304050607" << std::setw(4) << std::setfill('0') << i; + DuidPtr duid(new DUID(DUID::fromText(duid_s.str()).getDuid())); + + // Address. + std::ostringstream address_s; + address_s << "2001:db8:1::" << std::setw(4) << std::setfill('0') << i; + IOAddress address(address_s.str()); + + // Hostname. + std::ostringstream hostname_s; + hostname_s << "host" << std::setw(4) << std::setfill('0') << i + << ".example.org"; + + // Create lease. + Lease6Ptr lease(new Lease6(Lease::TYPE_NA, address, duid, 1, 50, 60, 10, 20, + SubnetID(1), true, true, hostname_s.str())); + leases_.push_back(lease); + LeaseMgrFactory::instance().addLease(lease); + } +} + +void +ExpirationAllocEngine6Test::enableDDNS() const { + // Start DDNS and assign no-op error handler. + D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr(); + D2ClientConfigPtr cfg(new D2ClientConfig()); + cfg->enableUpdates(true); + mgr.setD2ClientConfig(cfg); + mgr.startSender(boost::bind(&ExpirationAllocEngine6Test::d2ErrorHandler, _1, _2)); +} + +void +ExpirationAllocEngine6Test::expire(const unsigned int lease_index, + const time_t secs) { + ASSERT_GT(leases_.size(), lease_index); + // We set the expiration time indirectly by modifying the cltt value. + leases_[lease_index]->cltt_ = time(NULL) - secs - leases_[lease_index]->valid_lft_; + ASSERT_NO_THROW(LeaseMgrFactory::instance().updateLease6(leases_[lease_index])); +} + +bool +ExpirationAllocEngine6Test::testLeases(const LeaseAlgorithmFun& lease_algorithm, + const IndexAlgorithmFun& index_algorithm) const { + // No limits are specified, so test all leases in the range of + // 0 .. TEST_LEASES_NUM. + return (testLeases(lease_algorithm, index_algorithm, LowerBound(0), + UpperBound(TEST_LEASES_NUM))); +} + +bool +ExpirationAllocEngine6Test::testLeases(const LeaseAlgorithmFun& lease_algorithm, + const IndexAlgorithmFun& index_algorithm, + const LowerBound& lower_bound, + const UpperBound& upper_bound) const { + // Select leases between the lower_bound and upper_bound. + for (size_t i = lower_bound; i < upper_bound; ++i) { + // Get the lease from the lease database. + Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(leases_[i]->type_, + leases_[i]->addr_); + // index_algorithm(i) checks if the lease should be checked. + // If so, check if the lease_algorithm indicates that the + // lease fulfils a given condition, e.g. is present in the + // database. If not, return false. + if (index_algorithm(i) && !lease_algorithm(lease)) { + return (false); + } + } + // All leases checked, so return true. + return (true); +} + +bool +ExpirationAllocEngine6Test::dnsUpdateGeneratedForLease(const Lease6Ptr& lease) { + try { + // Iterate over the generated name change requests and try + // to find the match with our lease (using IP address). If + D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr(); + for (size_t i = 0; i < mgr.getQueueSize(); ++i) { + const NameChangeRequestPtr& ncr = mgr.peekAt(i); + // If match found, return true. + if (ncr->getIpAddress() == lease->addr_.toText()) { + return (true); + } + } + } catch (...) { + // If error occurred, treat it as no match. + return (false); + } + + // All leases checked - no match. + return (false); +} + +bool +ExpirationAllocEngine6Test::dnsUpdateNotGeneratedForLease(const Lease6Ptr& lease) { + try { + // Iterate over the generated name change requests and try + // to find the match with our lease (using IP address). If + D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr(); + for (size_t i = 0; i < mgr.getQueueSize(); ++i) { + const NameChangeRequestPtr& ncr = mgr.peekAt(i); + // If match found, we treat it as if the test fails + // because we expected no NCR. + if (ncr->getIpAddress() == lease->addr_.toText()) { + return (false); + } + } + } catch (...) { + return (false); + } + + // No match found, so we're good. + return (true); +} + +// This test verifies that the leases can be reclaimed without being removed +// from the database. In such case, the leases' state is set to +// "expired-reclaimed". +TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeases6UpdateState) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 100, false))); + ASSERT_TRUE(engine); + + for (int i = 0; i < TEST_LEASES_NUM; ++i) { + // Mark leases with even indexes as expired. + if (evenLeaseIndex(i)) { + // The higher the index, the more expired the lease. + expire(i, 10 + i); + } + } + + // Run leases reclamation routine on all leases. This should result + // in setting "expired-reclaimed" state for all leases with even + // indexes. + ASSERT_NO_THROW(engine->reclaimExpiredLeases6(0, 0, false)); + + // Leases with even indexes should be marked as reclaimed. + EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex)); + // Leases with odd indexes shouldn't be marked as reclaimed. + EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex)); + +} + +// This test verifies that the reclaimed leases are deleted when requested. +TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeases6Delete) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 100, false))); + ASSERT_TRUE(engine); + + for (int i = 0; i < TEST_LEASES_NUM; ++i) { + // Mark leases with even indexes as expired. + if (evenLeaseIndex(i)) { + // The higher the index, the more expired the lease. + expire(i, 10 + i); + } + } + + // Run leases reclamation routine on all leases. This should result + // in removal of all leases with even indexes. + ASSERT_NO_THROW(engine->reclaimExpiredLeases6(0, 0, true)); + + // Leases with odd indexes should be retained and their state + // shouldn't be "expired-reclaimed". + EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex)); + // Leases with even indexes should have been removed. + EXPECT_TRUE(testLeases(&leaseDoesntExist, &evenLeaseIndex)); +} + +// This test verifies that it is possible to specify the limit for the +// number of reclaimed leases. +TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeases6Limit) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 100, false))); + ASSERT_TRUE(engine); + + for (int i = 0; i < TEST_LEASES_NUM; ++i) { + // Mark all leaes as expired. The higher the index the less + // expired the lease. + expire(i, 1000 - i); + } + + // We will be performing lease reclamation on lease groups of 10. + // Hence, it is convenient if the number of test leases is a + // multiple of 10. + const size_t reclamation_group_size = 10; + BOOST_STATIC_ASSERT(TEST_LEASES_NUM % reclamation_group_size == 0); + + // Leases will be reclaimed in groups of 10. + for (int i = reclamation_group_size; i < TEST_LEASES_NUM; + i += reclamation_group_size) { + + // Reclaim 10 most expired leases out of TEST_LEASES_NUM. Since + // leases are ordered from the most expired to the least expired + // this should reclaim leases between 0 and 9, then 10 and 19 etc. + ASSERT_NO_THROW(engine->reclaimExpiredLeases6(reclamation_group_size, 0, + false)); + + // Check that leases having all indexes between 0 and 9, 19, 29 etc. + // have been reclaimed. + EXPECT_TRUE(testLeases(&leaseReclaimed, &allLeaseIndexes, + LowerBound(0), UpperBound(i))) + << "check failed for i = " << i; + + // Check that all remaining leases haven't been reclaimed. + EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes, + LowerBound(i), UpperBound(TEST_LEASES_NUM))) + << "check failed for i = " << i; + } +} + +// This test verifies that DNS updates are generated for the leases +// for which the DNS records exist. +TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeases6WithDDNS) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 100, false))); + ASSERT_TRUE(engine); + + // DNS must be started for the D2 client to accept NCRs. + ASSERT_NO_THROW(enableDDNS()); + + for (int i = 0; i < TEST_LEASES_NUM; ++i) { + // Expire all leases with even indexes. + if (evenLeaseIndex(i)) { + // The higher the index, the more expired the lease. + expire(i, 10 + i); + } + } + + // Reclaim all expired leases. + ASSERT_NO_THROW(engine->reclaimExpiredLeases6(0, 0, false)); + + // Leases with odd indexes shouldn't be reclaimed. + EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex)); + // Leases with even indexes should be reclaimed. + EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex)); + // DNS updates (removal NCRs) should be generated for leases with even + // indexes. + EXPECT_TRUE(testLeases(&dnsUpdateGeneratedForLease, &evenLeaseIndex)); + // DNS updates (removal NCRs) shouldn't be generated for leases with + // odd indexes. + EXPECT_TRUE(testLeases(&dnsUpdateNotGeneratedForLease, &oddLeaseIndex)); +} + +// This test verifies that it is DNS updates are generated only for the +// reclaimed expired leases. In this case we limit the number of leases +// reclaimed during a single call to reclamation routine. +TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeases6WithDDNSAndLimit) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, + 100, false))); + ASSERT_TRUE(engine); + + // DNS must be started for the D2 client to accept NCRs. + ASSERT_NO_THROW(enableDDNS()); + + for (int i = 0; i < TEST_LEASES_NUM; ++i) { + // Expire only leases with even indexes. + if (evenLeaseIndex(i)) { + // The higher the index, the more expired the lease. + expire(i, 10 + i); + } + } + + const size_t reclamation_group_size = 10; + BOOST_STATIC_ASSERT(TEST_LEASES_NUM % reclamation_group_size == 0); + + // Leases will be reclaimed in groups of 10 + for (int i = 10; i < TEST_LEASES_NUM; i += reclamation_group_size) { + // Reclaim 10 most expired leases. Note that the leases with the higher + // index are more expired. For example, if the TEST_LEASES_NUM is equal + // to 100, the most expired lease will be 98, then 96, 94 etc. + ASSERT_NO_THROW(engine->reclaimExpiredLeases6(reclamation_group_size, 0, + false)); + + // After the first iteration the lower bound is 80, because there will + // be 10 the most expired leases in this group: 80, 82, 84, 86, 88, 90, + // 92, 94, 96, 98. For subsequent iterations accordingly. + int reclaimed_lower_bound = TEST_LEASES_NUM - 2 * i; + // At some point the lower bound will hit the negative value, which + // must be corrected to 0. + if (reclaimed_lower_bound < 0) { + reclaimed_lower_bound = 0; + } + + // Leases between the lower bound calculated above and the upper bound + // of all leases, and having even indexes should have been reclaimed. + EXPECT_TRUE(testLeases(&leaseReclaimed, &evenLeaseIndex, + LowerBound(reclaimed_lower_bound), + UpperBound(TEST_LEASES_NUM))) + << "check failed for i = " << i; + + // For the same leases we should have generated DNS updates (removal NCRs). + EXPECT_TRUE(testLeases(&dnsUpdateGeneratedForLease, &evenLeaseIndex, + LowerBound(reclaimed_lower_bound), + UpperBound(TEST_LEASES_NUM))) + << "check failed for i = " << i; + + // Leases with odd indexes (falling between the reclaimed ones) shouldn't + // have been reclaimed, because they are not expired. + EXPECT_TRUE(testLeases(&leaseNotReclaimed, &oddLeaseIndex, + LowerBound(reclaimed_lower_bound), + UpperBound(TEST_LEASES_NUM))) + << "check failed for i = " << i; + + EXPECT_TRUE(testLeases(&dnsUpdateNotGeneratedForLease, &oddLeaseIndex, + LowerBound(reclaimed_lower_bound), + UpperBound(TEST_LEASES_NUM))) + << "check failed for i = " << i; + + + // At early stages of iterations, there should be conitnuous group of leases + // (expired and not expired) which haven't been reclaimed. + if (reclaimed_lower_bound > 0) { + EXPECT_TRUE(testLeases(&leaseNotReclaimed, &allLeaseIndexes, + LowerBound(0), + UpperBound(reclaimed_lower_bound))) + << "check failed for i = " << i; + + EXPECT_TRUE(testLeases(&dnsUpdateNotGeneratedForLease, &oddLeaseIndex, + LowerBound(0), UpperBound(reclaimed_lower_bound))); + + } + } +} + + + +}; // end of anonymous namespace