From: Marcin Siodelski Date: Mon, 12 Oct 2015 11:52:01 +0000 (+0200) Subject: [3975] Scheduling lease expiration timers in the DHCP servers. X-Git-Tag: trac3874_base~15^2~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4517ba7bfa59415bd301161d322de73007aeb36d;p=thirdparty%2Fkea.git [3975] Scheduling lease expiration timers in the DHCP servers. The timers are scheduled in the ControlledDhcpvXSrv instances. The unit tests are located in the kea_controller_unittest.cc. --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 9eab7b3eac..7ef21b17a3 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -135,20 +135,6 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { ConstElementPtr answer = configureDhcp4Server(*srv, config); - // Start worker thread if there are any timers installed. Note that - // we also start worker thread when the reconfiguration failed, because - // in that case we continue using an old configuration and the server - // should still run installed timers. - if (TimerMgr::instance()->timersCount() > 0) { - try { - TimerMgr::instance()->startThread(); - } catch (const std::exception& ex) { - err << "Unable to start worker thread running timers: " - << ex.what() << "."; - return (isc::config::createAnswer(1, err.str())); - } - } - // Check that configuration was successful. If not, do not reopen sockets // and don't bother with DDNS stuff. try { @@ -181,6 +167,32 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { CfgMgr::instance().getStagingCfg()->getCfgIface()-> openSockets(AF_INET, srv->getPort(), getInstance()->useBroadcast()); + // Install the timers for handling leases reclamation. + try { + CfgMgr::instance().getStagingCfg()->getCfgExpiration()-> + setupTimers(&ControlledDhcpv4Srv::reclaimExpiredLeases, + &ControlledDhcpv4Srv::deleteExpiredReclaimedLeases, + server_); + + } catch (const std::exception& ex) { + err << "unable to setup timers for periodically running the" + " reclamation of the expired leases: " + << ex.what() << "."; + return (isc::config::createAnswer(1, err.str())); + } + + // Start worker thread if there are any timers installed. + if (TimerMgr::instance()->timersCount() > 0) { + try { + TimerMgr::instance()->startThread(); + } catch (const std::exception& ex) { + err << "Unable to start worker thread running timers: " + << ex.what() << "."; + return (isc::config::createAnswer(1, err.str())); + } + } + + return (answer); } @@ -230,7 +242,7 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { cleanup(); // Stop worker thread running timers, if it is running. - timer_mgr_->stopThread(); + TimerMgr::instance()->stopThread(); // Close the command socket (if it exists). CommandMgr::instance().closeCommandSocket(); @@ -262,5 +274,22 @@ void ControlledDhcpv4Srv::sessionReader(void) { } } +void +ControlledDhcpv4Srv::reclaimExpiredLeases(const size_t max_leases, + const uint16_t timeout, + const bool remove_lease) { + server_->alloc_engine_->reclaimExpiredLeases4(max_leases, timeout, + remove_lease); + // We're using the ONE_SHOT timer so there is a need to re-schedule it. + TimerMgr::instance()->setup(CfgExpiration::RECLAIM_EXPIRED_TIMER_NAME); +} + +void +ControlledDhcpv4Srv::deleteExpiredReclaimedLeases(const uint32_t secs) { + server_->alloc_engine_->deleteExpiredReclaimedLeases4(secs); + // We're using the ONE_SHOT timer so there is a need to re-schedule it. + TimerMgr::instance()->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME); +} + }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h index 6defa25a73..528c46ddf1 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.h +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h @@ -150,6 +150,32 @@ private: commandConfigReloadHandler(const std::string& command, isc::data::ConstElementPtr args); + + /// @brief Reclaims expired IPv4 leases and reschedules timer. + /// + /// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases4. + /// It reschedules the timer for leases reclamation upon completion of + /// this method. + /// + /// @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 milliseconds. + /// @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 reclaimExpiredLeases(const size_t max_leases, const uint16_t timeout, + const bool remove_lease); + + /// @brief Deletes reclaimed leases and reschedules the timer. + /// + /// This is a wrapper method for @c AllocEngine::deleteExpiredReclaimed4. + /// It reschedules the timer for leases reclamation upon completion of + /// this method. + /// + /// @param secs Minimum number of seconds after which a lease can be + /// deleted. + void deleteExpiredReclaimedLeases(const uint32_t secs); + /// @brief Static pointer to the sole instance of the DHCP server. /// /// This is required for config and command handlers to gain access to diff --git a/src/bin/dhcp4/tests/kea_controller_unittest.cc b/src/bin/dhcp4/tests/kea_controller_unittest.cc index d174d5992b..49c8282920 100644 --- a/src/bin/dhcp4/tests/kea_controller_unittest.cc +++ b/src/bin/dhcp4/tests/kea_controller_unittest.cc @@ -14,11 +14,17 @@ #include +#include #include #include +#include +#include #include #include +#include +#include #include +#include #include #include @@ -58,6 +64,7 @@ public: } ~JSONFileBackendTest() { + LeaseMgrFactory::destroy(); isc::log::setDefaultLoggingOutput(); static_cast(remove(TEST_FILE)); }; @@ -77,6 +84,21 @@ public: out.close(); } + /// @brief Runs timers for specified time. + /// + /// Internally, this method calls @c IfaceMgr::receive6 to run the + /// callbacks for the installed timers. + /// + /// @param timeout_ms Amount of time after which the method returns. + void runTimersWithTimeout(const long timeout_ms) { + isc::util::Stopwatch stopwatch; + while (stopwatch.getTotalMilliseconds() < timeout_ms) { + // Block for up to one millisecond waiting for the timers' + // callbacks to be executed. + IfaceMgr::instancePtr()->receive4(0, 1000); + } + } + /// Name of a config file used during tests static const char* TEST_FILE; }; @@ -303,4 +325,79 @@ TEST_F(JSONFileBackendTest, DISABLED_loadAllConfigs) { } } +// This test verifies that the DHCP server installs the timers for reclaiming +// and flushing expired leases. +TEST_F(JSONFileBackendTest, timers) { + // This is a basic configuration which enables timers for reclaiming + // expired leases and flushing them after 500 seconds since they expire. + // Both timers run at 1 second intervals. + string config = + "{ \"Dhcp4\": {" + "\"interfaces-config\": {" + " \"interfaces\": [ ]" + "}," + "\"lease-database\": {" + " \"type\": \"memfile\"," + " \"persist\": false" + "}," + "\"expired-leases-processing\": {" + " \"reclaim-timer-wait-time\": 1," + " \"hold-reclaimed-time\": 500," + " \"flush-reclaimed-timer-wait-time\": 1" + "}," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, \n" + "\"subnet4\": [ ]," + "\"valid-lifetime\": 4000 }" + "}"; + writeFile(config); + + // Create an instance of the server and intialize it. + boost::scoped_ptr srv; + ASSERT_NO_THROW(srv.reset(new ControlledDhcpv4Srv(0))); + ASSERT_NO_THROW(srv->init(TEST_FILE)); + + // Create an expired lease. The lease is expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). The lease will be reclaimed + // but shouldn't be flushed in the database because the reclaimed are + // held in the database 500 seconds after reclamation, according to the + // current configuration. + HWAddrPtr hwaddr_expired(new HWAddr(HWAddr::fromText("00:01:02:03:04:05"))); + Lease4Ptr lease_expired(new Lease4(IOAddress("10.0.0.1"), hwaddr_expired, + ClientIdPtr(), 60, 10, 20, + time(NULL) - 100, SubnetID(1))); + + // Create expired-reclaimed lease. The lease has expired 1000 - 60 seconds + // ago. It should be removed from the lease database when the "flush" timer + // goes off. + HWAddrPtr hwaddr_reclaimed(new HWAddr(HWAddr::fromText("01:02:03:04:05:06"))); + Lease4Ptr lease_reclaimed(new Lease4(IOAddress("10.0.0.2"), hwaddr_reclaimed, + ClientIdPtr(), 60, 10, 20, + time(NULL) - 1000, SubnetID(1))); + lease_reclaimed->state_ = Lease4::STATE_EXPIRED_RECLAIMED; + + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease_expired)); + ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_TRUE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); + + // Poll the timers for a while to make sure that each of them is executed + // at least once. + ASSERT_NO_THROW(runTimersWithTimeout(5000)); + + // Verify that the leases in the database have been processed as expected. + + // First lease should be reclaimed, but not removed. + ASSERT_NO_THROW(lease_expired = lease_mgr.getLease4(IOAddress("10.0.0.1"))); + ASSERT_TRUE(lease_expired); + EXPECT_TRUE(lease_expired->stateExpiredReclaimed()); + + // Second lease should have been removed. + EXPECT_FALSE(lease_mgr.getLease4(IOAddress("10.0.0.2"))); +} + } // End of anonymous namespace diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index 4e1cf818f2..7df0988f83 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -131,21 +131,6 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { ConstElementPtr answer = configureDhcp6Server(*srv, config); - // Start worker thread if there are any timers installed. Note that - // we also start worker thread when the reconfiguration failed, because - // in that case we continue using an old configuration and the server - // should still run installed timers. - if (TimerMgr::instance()->timersCount() > 0) { - try { - TimerMgr::instance()->startThread(); - } catch (const std::exception& ex) { - std::ostringstream err; - err << "Unable to start worker thread running timers: " - << ex.what() << "."; - return (isc::config::createAnswer(1, err.str())); - } - } - // Check that configuration was successful. If not, do not reopen sockets // and don't bother with DDNS stuff. try { @@ -178,6 +163,33 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { // of the interfaces. CfgMgr::instance().getStagingCfg()->getCfgIface()->openSockets(AF_INET6, srv->getPort()); + // Install the timers for handling leases reclamation. + try { + CfgMgr::instance().getStagingCfg()->getCfgExpiration()-> + setupTimers(&ControlledDhcpv6Srv::reclaimExpiredLeases, + &ControlledDhcpv6Srv::deleteExpiredReclaimedLeases, + server_); + + } catch (const std::exception& ex) { + std::ostringstream err; + err << "unable to setup timers for periodically running the" + " reclamation of the expired leases: " + << ex.what() << "."; + return (isc::config::createAnswer(1, err.str())); + } + + // Start worker thread if there are any timers installed. + if (TimerMgr::instance()->timersCount() > 0) { + try { + TimerMgr::instance()->startThread(); + } catch (const std::exception& ex) { + std::ostringstream err; + err << "Unable to start worker thread running timers: " + << ex.what() << "."; + return (isc::config::createAnswer(1, err.str())); + } + } + return (answer); } @@ -225,8 +237,10 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { try { cleanup(); - // Stop worker thread running timers, if it is running. + // Stop worker thread running timers, if it is running. Then + // unregister any timers. timer_mgr_->stopThread(); + timer_mgr_->unregisterTimers(); // Close the command socket (if it exists). CommandMgr::instance().closeCommandSocket(); @@ -258,5 +272,23 @@ void ControlledDhcpv6Srv::sessionReader(void) { } } +void +ControlledDhcpv6Srv::reclaimExpiredLeases(const size_t max_leases, + const uint16_t timeout, + const bool remove_lease) { + server_->alloc_engine_->reclaimExpiredLeases6(max_leases, timeout, + remove_lease); + // We're using the ONE_SHOT timer so there is a need to re-schedule it. + TimerMgr::instance()->setup(CfgExpiration::RECLAIM_EXPIRED_TIMER_NAME); +} + +void +ControlledDhcpv6Srv::deleteExpiredReclaimedLeases(const uint32_t secs) { + server_->alloc_engine_->deleteExpiredReclaimedLeases6(secs); + // We're using the ONE_SHOT timer so there is a need to re-schedule it. + TimerMgr::instance()->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME); +} + + }; // end of isc::dhcp namespace }; // end of isc namespace diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h index 7736c45f8b..bf77643cad 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.h +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h @@ -150,6 +150,31 @@ private: commandConfigReloadHandler(const std::string& command, isc::data::ConstElementPtr args); + /// @brief Reclaims expired IPv6 leases and reschedules timer. + /// + /// This is a wrapper method for @c AllocEngine::reclaimExpiredLeases6. + /// It reschedules the timer for leases reclamation upon completion of + /// this method. + /// + /// @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 milliseconds. + /// @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 reclaimExpiredLeases(const size_t max_leases, const uint16_t timeout, + const bool remove_lease); + + /// @brief Deletes reclaimed leases and reschedules the timer. + /// + /// This is a wrapper method for @c AllocEngine::deleteExpiredReclaimed6. + /// It reschedules the timer for leases reclamation upon completion of + /// this method. + /// + /// @param secs Minimum number of seconds after which a lease can be + /// deleted. + void deleteExpiredReclaimedLeases(const uint32_t secs); + /// @brief Static pointer to the sole instance of the DHCP server. /// /// This is required for config and command handlers to gain access to diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 630ea401cb..7c9e5fb42c 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -180,7 +180,7 @@ const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_"); static const char* SERVER_DUID_FILE = "kea-dhcp6-serverid"; Dhcpv6Srv::Dhcpv6Srv(uint16_t port) -:alloc_engine_(), serverid_(), port_(port), shutdown_(true) + : serverid_(), port_(port), shutdown_(true), alloc_engine_() { LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port); diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 9d2ff2d510..4bc84f8e72 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -823,12 +823,6 @@ private: /// @param query packet transmitted static void processStatsSent(const Pkt6Ptr& response); - /// @brief Allocation Engine. - /// Pointer to the allocation engine that we are currently using - /// It must be a pointer, because we will support changing engines - /// during normal operation (e.g. to use different allocators) - boost::shared_ptr alloc_engine_; - /// Server DUID (to be sent in server-identifier option) OptionPtr serverid_; @@ -841,6 +835,12 @@ protected: /// initiate server shutdown procedure. volatile bool shutdown_; + /// @brief Allocation Engine. + /// Pointer to the allocation engine that we are currently using + /// It must be a pointer, because we will support changing engines + /// during normal operation (e.g. to use different allocators) + boost::shared_ptr alloc_engine_; + /// Holds a list of @c isc::dhcp_ddns::NameChangeRequest objects, which /// are waiting for sending to kea-dhcp-ddns module. std::queue name_change_reqs_; diff --git a/src/bin/dhcp6/tests/kea_controller_unittest.cc b/src/bin/dhcp6/tests/kea_controller_unittest.cc index dc4bd9ea59..125ade3a1b 100644 --- a/src/bin/dhcp6/tests/kea_controller_unittest.cc +++ b/src/bin/dhcp6/tests/kea_controller_unittest.cc @@ -14,11 +14,17 @@ #include +#include #include #include +#include +#include #include #include +#include +#include #include +#include #include #include @@ -53,6 +59,7 @@ public: } ~JSONFileBackendTest() { + LeaseMgrFactory::destroy(); isc::log::setDefaultLoggingOutput(); static_cast(remove(TEST_FILE)); }; @@ -66,6 +73,21 @@ public: out.close(); } + /// @brief Runs timers for specified time. + /// + /// Internally, this method calls @c IfaceMgr::receive6 to run the + /// callbacks for the installed timers. + /// + /// @param timeout_ms Amount of time after which the method returns. + void runTimersWithTimeout(const long timeout_ms) { + isc::util::Stopwatch stopwatch; + while (stopwatch.getTotalMilliseconds() < timeout_ms) { + // Block for up to one millisecond waiting for the timers' + // callbacks to be executed. + IfaceMgr::instancePtr()->receive6(0, 1000); + } + } + static const char* TEST_FILE; }; @@ -245,4 +267,83 @@ TEST_F(JSONFileBackendTest, configBroken) { EXPECT_THROW(srv->init(TEST_FILE), BadValue); } +// This test verifies that the DHCP server installs the timers for reclaiming +// and flushing expired leases. +TEST_F(JSONFileBackendTest, timers) { + // This is a basic configuration which enables timers for reclaiming + // expired leases and flushing them after 500 seconds since they expire. + // Both timers run at 1 second intervals. + string config = + "{ \"Dhcp6\": {" + "\"interfaces-config\": {" + " \"interfaces\": [ ]" + "}," + "\"lease-database\": {" + " \"type\": \"memfile\"," + " \"persist\": false" + "}," + "\"expired-leases-processing\": {" + " \"reclaim-timer-wait-time\": 1," + " \"hold-reclaimed-time\": 500," + " \"flush-reclaimed-timer-wait-time\": 1" + "}," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ ]," + "\"preferred-lifetime\": 3000, " + "\"valid-lifetime\": 4000 }" + "}"; + writeFile(TEST_FILE, config); + + // Create an instance of the server and intialize it. + boost::scoped_ptr srv; + ASSERT_NO_THROW(srv.reset(new ControlledDhcpv6Srv(0))); + ASSERT_NO_THROW(srv->init(TEST_FILE)); + + // Create an expired lease. The lease is expired by 40 seconds ago + // (valid lifetime = 60, cltt = now - 100). The lease will be reclaimed + // but shouldn't be flushed in the database because the reclaimed are + // held in the database 500 seconds after reclamation, according to the + // current configuration. + DuidPtr duid_expired(new DUID(DUID::fromText("00:01:02:03:04:05:06").getDuid())); + Lease6Ptr lease_expired(new Lease6(Lease::TYPE_NA, IOAddress("3000::1"), + duid_expired, 1, 50, 60, 10, 20, SubnetID(1))); + lease_expired->cltt_ = time(NULL) - 100; + + + // Create expired-reclaimed lease. The lease has expired 1000 - 60 seconds + // ago. It should be removed from the lease database when the "flush" timer + // goes off. + DuidPtr duid_reclaimed(new DUID(DUID::fromText("01:02:03:04:05:06:07").getDuid())); + Lease6Ptr lease_reclaimed(new Lease6(Lease::TYPE_NA, IOAddress("3000::2"), + duid_reclaimed, 1, 50, 60, 10, 20, SubnetID(1))); + lease_reclaimed->cltt_ = time(NULL) - 1000; + lease_reclaimed->state_ = Lease6::STATE_EXPIRED_RECLAIMED; + + // Add leases to the database. + LeaseMgr& lease_mgr = LeaseMgrFactory().instance(); + ASSERT_NO_THROW(lease_mgr.addLease(lease_expired)); + ASSERT_NO_THROW(lease_mgr.addLease(lease_reclaimed)); + + // Make sure they have been added. + ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1"))); + ASSERT_TRUE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2"))); + + // Poll the timers for a while to make sure that each of them is executed + // at least once. + ASSERT_NO_THROW(runTimersWithTimeout(5000)); + + // Verify that the leases in the database have been processed as expected. + + // First lease should be reclaimed, but not removed. + ASSERT_NO_THROW( + lease_expired = lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::1")) + ); + ASSERT_TRUE(lease_expired); + EXPECT_TRUE(lease_expired->stateExpiredReclaimed()); + + // Second lease should have been removed. + EXPECT_FALSE(lease_mgr.getLease6(Lease::TYPE_NA, IOAddress("3000::2"))); +} + } // End of anonymous namespace diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 08cc2ffd7f..8be487a7c6 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -1405,6 +1405,29 @@ AllocEngine::reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeo .arg(stopwatch.logFormatTotalDuration()); } +void +AllocEngine::deleteExpiredReclaimedLeases6(const uint32_t secs) { + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE) + .arg(secs); + + uint64_t deleted_leases = 0; + try { + // Try to delete leases from the lease database. + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + deleted_leases = lease_mgr.deleteExpiredReclaimedLeases6(secs); + + } catch (const std::exception& ex) { + LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED) + .arg(ex.what()); + } + + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_COMPLETE) + .arg(deleted_leases); +} + + void AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout, const bool remove_lease) { @@ -1534,6 +1557,28 @@ AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeo .arg(stopwatch.logFormatTotalDuration()); } +void +AllocEngine::deleteExpiredReclaimedLeases4(const uint32_t secs) { + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE) + .arg(secs); + + uint64_t deleted_leases = 0; + try { + // Try to delete leases from the lease database. + LeaseMgr& lease_mgr = LeaseMgrFactory::instance(); + deleted_leases = lease_mgr.deleteExpiredReclaimedLeases4(secs); + + } catch (const std::exception& ex) { + LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED) + .arg(ex.what()); + } + + LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE, + ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_COMPLETE) + .arg(deleted_leases); +} + void AllocEngine::reclaimDeclined(const Lease4Ptr& lease) { diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h index 1652e8b37d..89dfbdc5c2 100644 --- a/src/lib/dhcpsrv/alloc_engine.h +++ b/src/lib/dhcpsrv/alloc_engine.h @@ -514,6 +514,13 @@ public: void reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout, const bool remove_lease); + /// @brief Deletes reclaimed leases expired more than specified amount + /// of time ago. + /// + /// @param secs Minimum number of seconds after which the lease can be + /// deleted. + void deleteExpiredReclaimedLeases6(const uint32_t secs); + /// @brief Reclaims expired IPv4 leases. /// /// This method retrieves a collection of expired leases and reclaims them. @@ -560,6 +567,14 @@ public: void reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout, const bool remove_lease); + /// @brief Deletes reclaimed leases expired more than specified amount + /// of time ago. + /// + /// @param secs Minimum number of seconds after which the lease can be + /// deleted. + void deleteExpiredReclaimedLeases4(const uint32_t secs); + + /// @brief Attempts to find appropriate host reservation. /// /// Attempts to find appropriate host reservation in HostMgr. If found, it diff --git a/src/lib/dhcpsrv/alloc_engine_messages.mes b/src/lib/dhcpsrv/alloc_engine_messages.mes index fc5ae7d901..412ff764f1 100644 --- a/src/lib/dhcpsrv/alloc_engine_messages.mes +++ b/src/lib/dhcpsrv/alloc_engine_messages.mes @@ -68,6 +68,25 @@ client sending the DHCPDISCOVER has a reservation for the specified address. The allocation engine will try to offer this address to the client. +% ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE begin deletion of reclaimed leases expired more than %1 seconds ago +This debug message is issued when the allocation engine begins +deletion of the reclaimed leases which have expired more than +a specified number of seconds ago. This operation is triggered +periodically according to the "flush-reclaimed-timer-wait-time" +parameter. The "hold-reclaimed-time" parameter defines a number +of seconds for which the leases are stored before they are +removed. + +% ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_COMPLETE successfully deleted %1 expired-reclaimed leases +This debug message is issued when the server successfully deletes +"expired-reclaimed" leases from the lease database. The number of +deleted leases is included in the log message. + +% ALLOC_ENGINE_V4_RECLAIMED_LEASES_DELETE_FAILED deletion of expired-reclaimed leases failed: %1 +This error message is issued when the deletion of "expired-reclaimed" +leases from the database failed. The error message is appended to +the log message. + % ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED failed to reclaim the lease %1: %2 This error message is logged when the allocation engine fails to reclaim an expired lease. The reason for the failure is included in the @@ -323,6 +342,25 @@ been reclaimed, because of the timeout, will be reclaimed when the next scheduled reclamation is started. The argument is the timeout value expressed in milliseconds. +% ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE begin deletion of reclaimed leases expired more than %1 seconds ago +This debug message is issued when the allocation engine begins +deletion of the reclaimed leases which have expired more than +a specified number of seconds ago. This operation is triggered +periodically according to the "flush-reclaimed-timer-wait-time" +parameter. The "hold-reclaimed-time" parameter defines a number +of seconds for which the leases are stored before they are +removed. + +% ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_COMPLETE successfully deleted %1 expired-reclaimed leases +This debug message is issued when the server successfully deletes +"expired-reclaimed" leases from the lease database. The number of +deleted leases is included in the log message. + +% ALLOC_ENGINE_V6_RECLAIMED_LEASES_DELETE_FAILED deletion of expired-reclaimed leases failed: %1 +This error message is issued when the deletion of "expired-reclaimed" +leases from the database failed. The error message is appended to +the log message. + % ALLOC_ENGINE_V6_RENEW_HR allocating leases reserved for the client %1 as a result of Renew This debug message is issued when the allocation engine tries to allocate reserved leases for the client sending a Renew message. diff --git a/src/lib/dhcpsrv/cfg_expiration.cc b/src/lib/dhcpsrv/cfg_expiration.cc index 01f8cd92dc..6cbeac475b 100644 --- a/src/lib/dhcpsrv/cfg_expiration.cc +++ b/src/lib/dhcpsrv/cfg_expiration.cc @@ -40,13 +40,22 @@ const uint16_t CfgExpiration::LIMIT_MAX_RECLAIM_TIME = 10000; const uint16_t CfgExpiration::LIMIT_UNWARNED_RECLAIM_CYCLES = std::numeric_limits::max(); -CfgExpiration::CfgExpiration() +// Timers' names +const std::string CfgExpiration::RECLAIM_EXPIRED_TIMER_NAME = + "reclaim-expired-leases"; + +const std::string CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME = + "flush-reclaimed-leases"; + +CfgExpiration::CfgExpiration(const bool test_mode) : reclaim_timer_wait_time_(DEFAULT_RECLAIM_TIMER_WAIT_TIME), - flush_reclaimed_timer_wait_time_(DEFAULT_FLUSH_RECLAIMED_TIMER_WAIT_TIME), - hold_reclaimed_time_(DEFAULT_HOLD_RECLAIMED_TIME), - max_reclaim_leases_(DEFAULT_MAX_RECLAIM_LEASES), - max_reclaim_time_(DEFAULT_MAX_RECLAIM_TIME), - unwarned_reclaim_cycles_(DEFAULT_UNWARNED_RECLAIM_CYCLES) { + flush_reclaimed_timer_wait_time_(DEFAULT_FLUSH_RECLAIMED_TIMER_WAIT_TIME), + hold_reclaimed_time_(DEFAULT_HOLD_RECLAIMED_TIME), + max_reclaim_leases_(DEFAULT_MAX_RECLAIM_LEASES), + max_reclaim_time_(DEFAULT_MAX_RECLAIM_TIME), + unwarned_reclaim_cycles_(DEFAULT_UNWARNED_RECLAIM_CYCLES), + timer_mgr_(TimerMgr::instance()), + test_mode_(test_mode) { } void @@ -102,7 +111,5 @@ CfgExpiration::rangeCheck(const int64_t value, const uint64_t max_value, } } - - } } diff --git a/src/lib/dhcpsrv/cfg_expiration.h b/src/lib/dhcpsrv/cfg_expiration.h index b79558277d..67fe6be069 100644 --- a/src/lib/dhcpsrv/cfg_expiration.h +++ b/src/lib/dhcpsrv/cfg_expiration.h @@ -15,6 +15,9 @@ #ifndef CFG_EXPIRATION_H #define CFG_EXPIRATION_H +#include +#include +#include #include #include #include @@ -111,10 +114,26 @@ public: //@} + /// @name Timers' names + //@{ + + /// @brief Name of the timer for reclaiming expired leases. + static const std::string RECLAIM_EXPIRED_TIMER_NAME; + + /// @brief Name of the timer for flushing relclaimed leases. + static const std::string FLUSH_RECLAIMED_TIMER_NAME; + + //@} + /// @brief Constructor. /// /// Sets all parameters to their defaults. - CfgExpiration(); + /// + /// @param test_mode Indicates if the instance should be created in the + /// test mode. In this mode the intervals for the timers are considered to + /// be specified in milliseconds, rather than seconds. This facilitates + /// testing execution of timers without the delays. + CfgExpiration(const bool test_mode = false); /// @brief Returns reclaim-timer-wait-time uint16_t getReclaimTimerWaitTime() const { @@ -176,6 +195,42 @@ public: /// @param unwarned_reclaim_cycles New value. void setUnwarnedReclaimCycles(const int64_t unwarned_reclaim_cycles); + /// @brief Setup timers for the reclamation of expired leases according + /// to the configuration parameters. + /// + /// This method includes the logic for setting the interval timers + /// performing the reclamation of the expired leases and the removal + /// of expired-reclaimed leases. + /// + /// The following is the sample code illustrating how to call this function + /// to setup the leases reclamation for the DHCPv4 server. + /// @code + /// CfgExpiration cfg; + /// + /// (set some cfg values here) + /// + /// AllocEnginePtr alloc_engine(new AllocEngine(...)); + /// cfg.setupTimers(&AllocEngine::reclaimExpiredLeases4, + /// &AllocEngine::deleteExpiredReclaimedLeases4, + /// alloc_engine.get()); + /// @endcode + /// + /// @param reclaim_fun Pointer to the leases reclamation routine. + /// @param delete_fun Pointer to the function which removes the + /// expired-reclaimed leases from the lease database. + /// @param instance_ptr Pointer to the instance of the object which + /// implements the lease reclamation routine. Typically it will be + /// the pointer to the @c AllocEngine. In case of unit tests it + /// will be a pointer to some test class which provides stub + /// implementation of the leases reclamation routines. + /// @tparam Instance Instance of the object in which both functions + /// are implemented. + template + void setupTimers(void (Instance::*reclaim_fun)(const size_t, const uint16_t, + const bool), + void (Instance::*delete_fun)(const uint32_t), + Instance* instance_ptr) const; + private: /// @brief Checks if the value being set by one of the modifiers is @@ -209,6 +264,11 @@ private: /// @brief unwarned-reclaim-cycles. uint16_t unwarned_reclaim_cycles_; + /// @brief Pointer to the instance of the Timer Manager. + TimerMgrPtr timer_mgr_; + + /// @brief Indicates if the instance is in the test mode. + bool test_mode_; }; /// @name Pointers to the @c CfgExpiration objects. @@ -221,6 +281,57 @@ typedef boost::shared_ptr ConstCfgExpirationPtr; //@} +template +void +CfgExpiration::setupTimers(void (Instance::*reclaim_fun)(const size_t, + const uint16_t, + const bool), + void (Instance::*delete_fun)(const uint32_t), + Instance* instance_ptr) const { + // One of the parameters passed to the leases' reclamation routine + // is a boolean value which indicates if reclaimed leases should + // be removed by the leases' reclamation routine. This is the case + // when the timer for flushing reclaimed leases is set to 0 + // (disabled). + const bool flush_timer_disabled = (getFlushReclaimedTimerWaitTime() == 0); + + // If the timer interval for the leases reclamation is non-zero + // the timer will be scheduled. + if (getReclaimTimerWaitTime() > 0) { + // In the test mode the interval is expressed in milliseconds. + // If this is not the test mode, the interval is in seconds. + const long reclaim_interval = test_mode_ ? getReclaimTimerWaitTime() : + 1000 * getReclaimTimerWaitTime(); + // Register timer for leases' reclamation routine. + timer_mgr_->registerTimer(RECLAIM_EXPIRED_TIMER_NAME, + boost::bind(reclaim_fun, instance_ptr, + getMaxReclaimLeases(), + getMaxReclaimTime(), + flush_timer_disabled), + reclaim_interval, + asiolink::IntervalTimer::ONE_SHOT); + timer_mgr_->setup(RECLAIM_EXPIRED_TIMER_NAME); + } + + // If the interval for the timer flusing expired-reclaimed leases + // is set we will schedule the timer. + if (!flush_timer_disabled) { + // The interval is specified in milliseconds if we're in the test mode. + // It is specified in seconds otherwise. + const long flush_interval = test_mode_ ? + getFlushReclaimedTimerWaitTime() : + 1000 * getFlushReclaimedTimerWaitTime(); + // Register and setup the timer. + timer_mgr_->registerTimer(FLUSH_RECLAIMED_TIMER_NAME, + boost::bind(delete_fun, instance_ptr, + getHoldReclaimedTime()), + flush_interval, + asiolink::IntervalTimer::ONE_SHOT); + timer_mgr_->setup(FLUSH_RECLAIMED_TIMER_NAME); + } +} + + } // end of isc::dhcp namespace } // end of isc namespace diff --git a/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc index da504e4a53..43aa51f36c 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc @@ -250,6 +250,20 @@ public: ASSERT_NO_THROW(updateLease(lease_index)); } + /// @brief Marks lease as expired-reclaimed. + /// + /// @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 reclaim(const uint16_t lease_index, const time_t secs) { + ASSERT_GT(leases_.size(), lease_index); + leases_[lease_index]->cltt_ = time(NULL) - secs - + leases_[lease_index]->valid_lft_; + leases_[lease_index]->state_ = Lease::STATE_EXPIRED_RECLAIMED; + ASSERT_NO_THROW(updateLease(lease_index)); + } + /// @brief Declines specified lease /// /// Sets specified lease to declined state and sets its probation-period. @@ -291,6 +305,13 @@ public: const uint16_t timeout, const bool remove_lease) = 0; + /// @brief Wrapper method for removing expired-reclaimed leases. + /// + /// @param secs The minimum amount of time, expressed in seconds, + /// for the lease to be left in the "expired-reclaimed" state + /// before it can be removed. + virtual void deleteExpiredReclaimedLeases(const uint32_t secs) = 0; + /// @brief Test selected leases using the specified algorithms. /// /// This function picks leases from the range of 0 thru @@ -905,6 +926,27 @@ public: UpperBound(TEST_LEASES_NUM))); } + /// @brief This test verifies that expired-reclaimed leases are removed + /// from the lease database. + void testDeleteExpiredReclaimedLeases() { + 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. + reclaim(i, 10 + i); + } + } + + // Run leases reclamation routine on all leases. This should result + // in removal of all leases with even indexes. + ASSERT_NO_THROW(deleteExpiredReclaimedLeases(10)); + + // Leases with odd indexes shouldn't be removed from the database. + EXPECT_TRUE(testLeases(&leaseExists, &oddLeaseIndex)); + // Leases with even indexes should have been removed. + EXPECT_TRUE(testLeases(&leaseDoesntExist, &evenLeaseIndex)); + } + /// @brief Test that declined expired leases can be removed. /// /// This method allows controlling remove_leases parameter when calling @@ -1086,6 +1128,15 @@ public: engine_->reclaimExpiredLeases6(max_leases, timeout, remove_lease); } + /// @brief Wrapper method for removing expired-reclaimed leases. + /// + /// @param secs The minimum amount of time, expressed in seconds, + /// for the lease to be left in the "expired-reclaimed" state + /// before it can be removed. + virtual void deleteExpiredReclaimedLeases(const uint32_t secs) { + engine_->deleteExpiredReclaimedLeases6(secs); + } + /// @brief Test that statistics is updated when leases are reclaimed. void testReclaimExpiredLeasesStats(); @@ -1283,6 +1334,13 @@ TEST_F(ExpirationAllocEngine6Test, reclaimExpiredLeasesShortTimeout) { testReclaimExpiredLeasesTimeout(1); } +// This test verifies that expired-reclaimed leases are removed from the +// lease database. +TEST_F(ExpirationAllocEngine6Test, deleteExpiredReclaimedLeases) { + BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 10); + testDeleteExpiredReclaimedLeases(); +} + // ******************************************************* // // DHCPv4 lease reclamation routine tests start here! @@ -1346,6 +1404,15 @@ public: engine_->reclaimExpiredLeases4(max_leases, timeout, remove_lease); } + /// @brief Wrapper method for removing expired-reclaimed leases. + /// + /// @param secs The minimum amount of time, expressed in seconds, + /// for the lease to be left in the "expired-reclaimed" state + /// before it can be removed. + virtual void deleteExpiredReclaimedLeases(const uint32_t secs) { + engine_->deleteExpiredReclaimedLeases4(secs); + } + /// @brief Lease algorithm checking if NCR has been generated from client /// identifier. /// @@ -1647,6 +1714,13 @@ TEST_F(ExpirationAllocEngine4Test, reclaimExpiredLeasesShortTimeout) { testReclaimExpiredLeasesTimeout(1); } +// This test verifies that expired-reclaimed leases are removed from the +// lease database. +TEST_F(ExpirationAllocEngine4Test, deleteExpiredReclaimedLeases) { + BOOST_STATIC_ASSERT(TEST_LEASES_NUM >= 10); + testDeleteExpiredReclaimedLeases(); +} + /// This test verifies that @ref AllocEngine::reclaimExpiredLeases4 properly /// handles declined leases that have expired in case when it is told to /// remove leases. diff --git a/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc b/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc index 5bb65afa06..c98671fdf2 100644 --- a/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_expiration_unittest.cc @@ -13,14 +13,19 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include #include +#include #include +#include #include +#include #include #include using namespace isc; using namespace isc::dhcp; +using namespace isc::util; namespace { @@ -158,4 +163,243 @@ TEST(CfgExpirationTest, getUnwarnedReclaimCycles) { &CfgExpiration::getUnwarnedReclaimCycles); } +/// @brief Implements test routines for leases reclamation. +/// +/// This class implements two routines called by the @c CfgExpiration object +/// instead of the typical routines for leases' reclamation in the +/// @c AllocEngine. These methods do not perform the actual reclamation, +/// but instead they record the number of calls to them and the parameters +/// with which they were executed. This allows for checking if the +/// @c CfgExpiration object calls the leases reclamation routine with the +/// appropriate parameteres. +class LeaseReclamationStub { +public: + + /// @brief Collection of parameters with which the @c reclaimExpiredLeases + /// method is called. + /// + /// Examination of these values allows for assesment if the @c CfgExpiration + /// calls the routine with the appropriate values. + struct RecordedParams { + /// @brief Maximum number of leases to be processed. + size_t max_leases; + + /// @brief Timeout for processing leases in milliseconds. + uint16_t timeout; + + /// @brief Boolean flag which indicates if the leases should be removed + /// when reclaimed. + bool remove_lease; + }; + + /// @brief Constructor. + /// + /// Resets recorded parameters and obtains the instance of the @c TimerMgr. + LeaseReclamationStub() + : reclaim_calls_count_(0), delete_calls_count_(0), reclaim_params_(), + secs_param_(0), timer_mgr_(TimerMgr::instance()) { + } + + /// @brief Stub implementation of the leases' reclamation routine. + /// + /// @param max_leases Maximum number of leases to be processed. + /// @param timeout Timeout for processing leases in milliseconds. + /// @remove_lease Boolean flag which indicates if the leases should be + /// removed when it is reclaimed. + void + reclaimExpiredLeases(const size_t max_leases, const uint16_t timeout, + const bool remove_lease) { + // Increase calls counter for this method. + ++reclaim_calls_count_; + // Record all parameters with which this method has been called. + reclaim_params_.max_leases = max_leases; + reclaim_params_.timeout = timeout; + reclaim_params_.remove_lease = remove_lease; + + // Leases' reclamation routine is responsible for re-scheduling + // the timer. + timer_mgr_->setup(CfgExpiration::RECLAIM_EXPIRED_TIMER_NAME); + } + + /// @brief Stub implementation of the routine which flushes + /// expired-reclaimed leases. + /// + /// @param secs Specifies the minimum amount of time, expressed in + /// seconds, that must elapse before the expired-reclaime lease is + /// deleted from the database. + void + deleteReclaimedLeases(const uint32_t secs) { + // Increase calls counter for this method. + ++delete_calls_count_; + // Record the value of the parameter. + secs_param_ = secs; + + // Routine which flushes the reclaimed leases is responsible for + // re-scheduling the timer. + timer_mgr_->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME); + } + + /// @brief Counter holding the number of calls to @c reclaimExpiredLeases. + long reclaim_calls_count_; + + /// @brief Counter holding the number of calls to @c deleteReclaimedLeases. + long delete_calls_count_; + + /// @brief Structure holding values of parameters with which the + /// @c reclaimExpiredLeases was called. + /// + /// These values are overriden on subsequent calls to this method. + RecordedParams reclaim_params_; + + /// @brief Value of the parameter with which the @c deleteReclaimedLeases + /// was called. + uint32_t secs_param_; + +private: + + /// @brief Pointer to the @c TimerMgr. + TimerMgrPtr timer_mgr_; + +}; + +/// @brief Pointer to the @c LeaseReclamationStub. +typedef boost::shared_ptr LeaseReclamationStubPtr; + +/// @brief Test fixture class for the @c CfgExpiration. +class CfgExpirationTimersTest : public ::testing::Test { +public: + + /// @brief Constructor. + /// + /// Creates instance of the test fixture class. Besides initialization + /// of the class members, it also stops the @c TimerMgr worker thread + /// and removes any registered timers. + CfgExpirationTimersTest() + : timer_mgr_(TimerMgr::instance()), + stub_(new LeaseReclamationStub()), + cfg_(true) { + cleanupTimerMgr(); + } + + /// @brief Destructor. + /// + /// It stops the @c TimerMgr worker thread and removes any registered + /// timers. + virtual ~CfgExpirationTimersTest() { + cleanupTimerMgr(); + } + + /// @brief Stop @c TimerMgr worker thread and remove the timers. + void cleanupTimerMgr() const { + timer_mgr_->stopThread(); + timer_mgr_->unregisterTimers(); + } + + /// @brief Runs timers for specified time. + /// + /// Internally, this method calls @c IfaceMgr::receive6 to run the + /// callbacks for the installed timers. + /// + /// @param timeout_ms Amount of time after which the method returns. + void runTimersWithTimeout(const long timeout_ms) { + Stopwatch stopwatch; + while (stopwatch.getTotalMilliseconds() < timeout_ms) { + // Block for up to one millisecond waiting for the timers' + // callbacks to be executed. + IfaceMgr::instancePtr()->receive6(0, 1000); + } + } + + /// @brief Setup timers according to the configuration and run them + /// for the specified amount of time. + /// + /// @param timeout_ms Timeout in milliseconds. + void setupAndRun(const long timeout_ms) { + cfg_.setupTimers(&LeaseReclamationStub::reclaimExpiredLeases, + &LeaseReclamationStub::deleteReclaimedLeases, + stub_.get()); + // Run timers. + ASSERT_NO_THROW({ + timer_mgr_->startThread(); + runTimersWithTimeout(timeout_ms); + timer_mgr_->stopThread(); + }); + } + + /// @brief Pointer to the @c TimerMgr. + TimerMgrPtr timer_mgr_; + + /// @brief Pointer to the @c LeaseReclamationStub instance. + LeaseReclamationStubPtr stub_; + + /// @brief Instance of the @c CfgExpiration class used by the tests. + CfgExpiration cfg_; +}; + +// Test that the reclamation routines are called with the appropriate parameters. +TEST_F(CfgExpirationTimersTest, reclamationParameters) { + // Set parameters to some non-default values. + cfg_.setMaxReclaimLeases(1000); + cfg_.setMaxReclaimTime(1500); + cfg_.setHoldReclaimedTime(1800); + + // Run timers for 500ms. + ASSERT_NO_FATAL_FAILURE(setupAndRun(500)); + + // Make sure we had more than one call to the reclamation routine. + ASSERT_GT(stub_->reclaim_calls_count_, 1); + // Make sure it was called with appropriate arguments. + EXPECT_EQ(1000, stub_->reclaim_params_.max_leases); + EXPECT_EQ(1500, stub_->reclaim_params_.timeout); + EXPECT_FALSE(stub_->reclaim_params_.remove_lease); + + // Make sure we had more than one call to the routine which flushes + // expired reclaimed leases. + ASSERT_GT(stub_->delete_calls_count_, 1); + // Make sure that the argument was correct. + EXPECT_EQ(1800, stub_->secs_param_); +} + +// This test verifies that if the value of "flush-reclaimed-timer-wait-time" +// configuration parameter is set to 0, the lease reclamation routine would +// delete reclaimed leases from a lease database. +TEST_F(CfgExpirationTimersTest, noLeaseAffinity) { + // Set the timer for flushing leases to 0. This effectively disables + // the timer. + cfg_.setFlushReclaimedTimerWaitTime(0); + + // Run the lease reclamation timer for a while. + ASSERT_NO_FATAL_FAILURE(setupAndRun(500)); + + // Make sure that the lease reclamation routine has been executed a + // couple of times. + ASSERT_GT(stub_->reclaim_calls_count_, 1); + EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_LEASES, + stub_->reclaim_params_.max_leases); + EXPECT_EQ(CfgExpiration::DEFAULT_MAX_RECLAIM_TIME, + stub_->reclaim_params_.timeout); + // When the "flush" timer is disabled, the lease reclamation routine is + // responsible for removal of reclaimed leases. This is controlled using + // the "remove_lease" parameter which should be set to true in this case. + EXPECT_TRUE(stub_->reclaim_params_.remove_lease); + + // The routine flushing reclaimed leases should not be run at all. + EXPECT_EQ(0, stub_->delete_calls_count_); +} + +// This test verfies that lease reclamation may be disabled. +TEST_F(CfgExpirationTimersTest, noLeaseReclamation) { + // Disable both timers. + cfg_.setReclaimTimerWaitTime(0); + cfg_.setFlushReclaimedTimerWaitTime(0); + + // Wait for 500ms. + ASSERT_NO_FATAL_FAILURE(setupAndRun(500)); + + // Make sure that neither leases' reclamation routine nor the routine + // flushing expired-reclaimed leases was run. + EXPECT_EQ(0, stub_->reclaim_calls_count_); + EXPECT_EQ(0, stub_->delete_calls_count_); +} + } // end of anonymous namespace