Browse Source

[master] Merge branch 'trac3973'

Marcin Siodelski 9 years ago
parent
commit
d24119e808

+ 232 - 0
src/lib/dhcpsrv/alloc_engine.cc

@@ -14,17 +14,24 @@
 
 #include <config.h>
 
+#include <dhcp/option_data_types.h>
+#include <dhcp_ddns/ncr_msg.h>
 #include <dhcpsrv/alloc_engine.h>
 #include <dhcpsrv/alloc_engine_log.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/d2_client_mgr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/host.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcp/dhcp6.h>
 #include <stats/stats_mgr.h>
+#include <util/stopwatch.h>
 #include <hooks/server_hooks.h>
 #include <hooks/hooks_manager.h>
 
+#include <boost/foreach.hpp>
+
 #include <cstring>
 #include <sstream>
 #include <limits>
@@ -34,6 +41,7 @@
 
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp_ddns;
 using namespace isc::hooks;
 using namespace isc::stats;
 
@@ -1272,6 +1280,230 @@ 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) {
+
+    LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
+              ALLOC_ENGINE_V6_LEASES_RECLAMATION_START)
+        .arg(max_leases)
+        .arg(timeout);
+
+    // Create stopwatch and automatically start it to measure the time
+    // taken by the routine.
+    /// @todo Monitor time elapsed and return from the lease reclamation routine
+    /// if it hits the timeout value.
+    util::Stopwatch stopwatch;
+
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+
+    Lease6Collection leases;
+    lease_mgr.getExpiredLeases6(leases, max_leases);
+
+    BOOST_FOREACH(Lease6Ptr lease, leases) {
+
+        try {
+            /// @todo execute a lease6_expire hook here.
+
+            // Generate removal name change request for D2, if required.
+            // This will return immediatelly if the DNS wasn't updated
+            // when the lease was created.
+            if (lease->duid_) {
+                queueRemovalNameChangeRequest(lease, *(lease->duid_));
+            }
+
+            // Reclaim the lease - depending on the configuration, set the
+            // expired-reclaimed state or simply remove it.
+            reclaimLeaseInDatabase<Lease6Ptr>(lease, remove_lease,
+                                              boost::bind(&LeaseMgr::updateLease6,
+                                                          &lease_mgr, _1));
+
+            // Update statistics.
+
+            // Decrease number of assigned leases.
+            if (lease->type_ == Lease::TYPE_NA) {
+                // IA_NA
+                StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
+                                                                     lease->subnet_id_,
+                                                                     "assigned-nas"),
+                                              int64_t(-1));
+
+            } else if (lease->type_ == Lease::TYPE_PD) {
+                // IA_PD
+                StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
+                                                                     lease->subnet_id_,
+                                                                     "assigned-pds"),
+                                              int64_t(-1));
+
+            }
+
+            // Increase total number of reclaimed leases.
+            StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
+
+            // Increase number of reclaimed leases for a subnet.
+            StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
+                                                                 lease->subnet_id_,
+                                                                 "reclaimed-leases"),
+                                          int64_t(1));
+
+
+        } catch (const std::exception& ex) {
+            LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V6_LEASE_RECLAMATION_FAILED)
+                .arg(lease->addr_.toText())
+                .arg(ex.what());
+        }
+    }
+
+    // Stop measuring the time.
+    stopwatch.stop();
+
+    // Mark completion of the lease reclamation routine and present some stats.
+    LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
+              ALLOC_ENGINE_V6_LEASES_RECLAMATION_COMPLETE)
+        .arg(leases.size())
+        .arg(stopwatch.logFormatTotalDuration());
+}
+
+void
+AllocEngine::reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
+                                   const bool remove_lease) {
+
+    LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
+              ALLOC_ENGINE_V4_LEASES_RECLAMATION_START)
+        .arg(max_leases)
+        .arg(timeout);
+
+    // Create stopwatch and automatically start it to measure the time
+    // taken by the routine.
+    /// @todo Monitor time elapsed and return from the lease reclamation routine
+    /// if it hits the timeout value.
+    util::Stopwatch stopwatch;
+
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+
+    Lease4Collection leases;
+    lease_mgr.getExpiredLeases4(leases, max_leases);
+
+    BOOST_FOREACH(Lease4Ptr lease, leases) {
+
+        try {
+            /// @todo execute a lease4_expire hook here.
+
+            // Generate removal name change request for D2, if required.
+            // This will return immediatelly if the DNS wasn't updated
+            // when the lease was created.
+            if (lease->client_id_) {
+                // Client id takes precedence over HW address.
+                queueRemovalNameChangeRequest(lease, lease->client_id_->getClientId());
+
+            } else {
+                // Client id is not specified for the lease. Use HW address
+                // instead.
+                queueRemovalNameChangeRequest(lease, lease->hwaddr_);
+            }
+
+            // Reclaim the lease - depending on the configuration, set the
+            // expired-reclaimed state or simply remove it.
+            reclaimLeaseInDatabase<Lease4Ptr>(lease, remove_lease,
+                                              boost::bind(&LeaseMgr::updateLease4,
+                                                          &lease_mgr, _1));
+
+            // Update statistics.
+
+            // Decrease number of assigned addresses.
+            StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
+                                                                 lease->subnet_id_,
+                                                                 "assigned-addresses"),
+                                          int64_t(-1));
+
+            // Increase total number of reclaimed leases.
+            StatsMgr::instance().addValue("reclaimed-leases", int64_t(1));
+
+            // Increase number of reclaimed leases for a subnet.
+            StatsMgr::instance().addValue(StatsMgr::generateName("subnet",
+                                                                 lease->subnet_id_,
+                                                                 "reclaimed-leases"),
+                                          int64_t(1));
+
+        } catch (const std::exception& ex) {
+            LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_V4_LEASE_RECLAMATION_FAILED)
+                .arg(lease->addr_.toText())
+                .arg(ex.what());
+        }
+    }
+
+    // Stop measuring the time.
+    stopwatch.stop();
+
+    // Mark completion of the lease reclamation routine and present some stats.
+    LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
+              ALLOC_ENGINE_V4_LEASES_RECLAMATION_COMPLETE)
+        .arg(leases.size())
+        .arg(stopwatch.logFormatTotalDuration());
+}
+
+template<typename LeasePtrType, typename IdentifierType>
+void
+AllocEngine::queueRemovalNameChangeRequest(const LeasePtrType& lease,
+                                           const IdentifierType& identifier) const {
+
+    // Check if there is a need for update.
+    if (!lease || lease->hostname_.empty() || (!lease->fqdn_fwd_ && !lease->fqdn_rev_)
+        || !CfgMgr::instance().getD2ClientMgr().ddnsEnabled()) {
+        return;
+    }
+
+    try {
+        // Create DHCID
+        std::vector<uint8_t> hostname_wire;
+        OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
+        dhcp_ddns::D2Dhcid dhcid = D2Dhcid(identifier, hostname_wire);
+
+        // Create name change request.
+        NameChangeRequestPtr ncr(new NameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
+                                                       lease->fqdn_fwd_, lease->fqdn_rev_,
+                                                       lease->hostname_,
+                                                       lease->addr_.toText(),
+                                                       dhcid, 0, lease->valid_lft_));
+        // Send name change request.
+        CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);
+
+    } catch (const std::exception& ex) {
+        LOG_ERROR(alloc_engine_logger, ALLOC_ENGINE_REMOVAL_NCR_FAILED)
+            .arg(lease->addr_.toText())
+            .arg(ex.what());
+    }
+}
+
+template<typename LeasePtrType>
+void AllocEngine::reclaimLeaseInDatabase(const LeasePtrType& lease,
+                                         const bool remove_lease,
+                                         const boost::function<void (const LeasePtrType&)>&
+                                         lease_update_fun) const {
+    LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+
+    // Reclaim the lease - depending on the configuration, set the
+    // expired-reclaimed state or simply remove it.
+    if (remove_lease) {
+        lease_mgr.deleteLease(lease->addr_);
+
+    } else {
+        // Clear FQDN information as we have already sent the
+        // name change request to remove the DNS record.
+        lease->hostname_.clear();
+        lease->fqdn_fwd_ = false;
+        lease->fqdn_rev_ = false;
+        lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+        lease_update_fun(lease);
+    }
+
+    // Lease has been reclaimed.
+    LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
+              ALLOC_ENGINE_LEASE_RECLAIMED)
+        .arg(lease->addr_.toText());
+}
+
+
 } // end of isc::dhcp namespace
 } // end of isc namespace
 

+ 86 - 0
src/lib/dhcpsrv/alloc_engine.h

@@ -26,6 +26,7 @@
 #include <dhcpsrv/lease_mgr.h>
 #include <hooks/callout_handle.h>
 
+#include <boost/function.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
 
@@ -490,6 +491,52 @@ public:
     /// @return Returns renewed lease.
     Lease6Collection renewLeases6(ClientContext6& ctx);
 
+    /// @brief Reclaims expired IPv6 leases.
+    ///
+    /// This method retrieves a collection of expired leases and reclaims them.
+    /// See http://kea.isc.org/wiki/LeaseExpirationDesign#LeasesReclamationRoutine
+    /// for the details.
+    ///
+    /// This method is executed periodically to act upon expired leases. This
+    /// includes for each lease:
+    /// - executing "lease_expire6" hook,
+    /// - removing DNS record for a lease,
+    /// - reclaiming a lease in the database, i.e. setting its state to
+    ///   "expired-reclaimed" or removing it from the lease databse,
+    /// - updating statistics of assigned and reclaimed leases
+    ///
+    /// @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 Reclaims expired IPv4 leases.
+    ///
+    /// This method retrieves a collection of expired leases and reclaims them.
+    /// See http://kea.isc.org/wiki/LeaseExpirationDesign#LeasesReclamationRoutine
+    /// for the details.
+    ///
+    /// This method is executed periodically to act upon expired leases. This
+    /// includes for each lease:
+    /// - executing "lease_expire6" hook,
+    /// - removing DNS record for a lease,
+    /// - reclaiming a lease in the database, i.e. setting its state to
+    ///   "expired-reclaimed" or removing it from the lease databse,
+    /// - updating statistics of assigned and reclaimed leases
+    ///
+    /// @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 reclaimExpiredLeases4(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 +710,45 @@ private:
     /// @param lease IPv6 lease to be extended.
     void extendLease6(ClientContext6& ctx, Lease6Ptr lease);
 
+    /// @brief Sends removal name change reuqest to D2.
+    ///
+    /// This method is exception safe.
+    ///
+    /// @param lease Pointer to a lease for which NCR should be sent.
+    /// @param identifier Identifier to be used to generate DHCID for
+    /// the DNS update. For DHCPv4 it will be hardware address or client
+    /// identifier. For DHCPv6 it will be a DUID.
+    ///
+    /// @tparam LeasePtrType Pointer to a lease.
+    /// @tparam IdentifierType HW Address, Client Identifier or DUID.
+    template<typename LeasePtrType, typename IdentifierType>
+    void queueRemovalNameChangeRequest(const LeasePtrType& lease,
+                                       const IdentifierType& identifier) const;
+
+    /// @brief Marks lease as reclaimed in the database.
+    ///
+    /// This method is called internally by the leases reclaimation routines.
+    /// Depending on the value of the @c remove_lease parameter this method
+    /// will delete the reclaimed lease from the database or set its sate
+    /// to "expired-reclaimed". In the latter case it will also clear the
+    /// FQDN information.
+    ///
+    /// This method may throw exceptions if the operation on the lease database
+    /// fails for any reason.
+    ///
+    /// @param lease Pointer to the lease.
+    /// @param remove_lease Boolean flag indicating if the lease should be
+    /// removed from the database (if true).
+    /// @param lease_update_fun Pointer to the function in the @c LeaseMgr to
+    /// be used to update the lease if the @c remove_lease is set to false.
+    ///
+    /// @tparam LeasePtrType One of the @c Lease6Ptr or @c Lease4Ptr.
+    template<typename LeasePtrType>
+    void reclaimLeaseInDatabase(const LeasePtrType& lease,
+                                const bool remove_lease,
+                                const boost::function<void (const LeasePtrType&)>&
+                                lease_update_fun) const;
+
 public:
 
     /// @brief Context information for the DHCPv4 lease allocation.

+ 54 - 0
src/lib/dhcpsrv/alloc_engine_messages.mes

@@ -14,6 +14,18 @@
 
 $NAMESPACE isc::dhcp
 
+% ALLOC_ENGINE_LEASE_RECLAIMED successfully reclaimed lease %1
+This debug message is logged when the allocation engine successfully
+reclaims a lease. The lease is now available for assignment.
+
+% ALLOC_ENGINE_REMOVAL_NCR_FAILED sending removal name change request failed for lease %1: %2
+This error message is logged when sending a removal name change request
+to DHCP DDNS failed. This name change request is usually generated when
+the lease reclamation routine acts upon expired leases. If a lease being
+reclaimed has a corresponding DNS entry it needs to be removed.
+This message indicates that removal of the DNS entry has failed.
+Nevertheless the lease will be reclaimed. 
+
 % ALLOC_ENGINE_V4_ALLOC_ERROR %1: error during attempt to allocate an IPv4 address: %2
 An error occurred during an attempt to allocate an IPv4 address, the
 reason for the failure being contained in the message.  The server will
@@ -49,6 +61,27 @@ 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_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
+message. The error may be triggered in the lease expiration hook or
+while performing the operation on the lease database.
+
+% ALLOC_ENGINE_V4_LEASES_RECLAMATION_COMPLETE reclaimed %1 leases in %2
+This debug message is logged when the allocation engine completes
+reclamation of a set of expired leases. The maximum number of leases
+to be reclaimed in a single pass of the lease reclamation routine
+is configurable using 'max-reclaim-leases' parameter. However,
+the number of reclaimed leases may also be limited by the timeout
+value, configured with 'max-reclaim-time'. The message includes the
+number of reclaimed leases and the total time.
+
+% ALLOC_ENGINE_V4_LEASES_RECLAMATION_START starting reclamation of expired leases (limit = %1 leases or %2 seconds)
+This debug message is issued when the allocation engine starts the
+reclamation of the expired leases. The maximum number of leases to
+be reclaimed and the timeout is included in the message. If any of
+these values is 0, it means "unlimited".
+
 % ALLOC_ENGINE_V4_OFFER_EXISTING_LEASE allocation engine will try to offer existing lease to the client %1
 This message is issued when the allocation engine determines that
 the client has a lease in the lease database, it doesn't have
@@ -246,6 +279,27 @@ reserved for it.
 This informational message signals that the specified client was assigned the prefix
 reserved for it.
 
+% ALLOC_ENGINE_V6_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
+message. The error may be triggered in the lease expiration hook or
+while performing the operation on the lease database.
+
+% ALLOC_ENGINE_V6_LEASES_RECLAMATION_COMPLETE reclaimed %1 leases in %2
+This debug message is logged when the allocation engine completes
+reclamation of a set of expired leases. The maximum number of leases
+to be reclaimed in a single pass of the lease reclamation routine
+is configurable using 'max-reclaim-leases' parameter. However,
+the number of reclaimed leases may also be limited by the timeout
+value, configured with 'max-reclaim-time'. The message includes the
+number of reclaimed leases and the total time.
+
+% ALLOC_ENGINE_V6_LEASES_RECLAMATION_START starting reclamation of expired leases (limit = %1 leases or %2 seconds)
+This debug message is issued when the allocation engine starts the
+reclamation of the expired leases. The maximum number of leases to
+be reclaimed and the timeout is included in the message. If any of
+these values is 0, it means "unlimited".
+
 % 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.

+ 1 - 0
src/lib/dhcpsrv/tests/Makefile.am

@@ -60,6 +60,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

File diff suppressed because it is too large
+ 1301 - 0
src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc