Browse Source

[3669] Re-organized the LFC in Memfile_LeaseMgr to use updated ProcessSpawn

Marcin Siodelski 10 years ago
parent
commit
e4f8a223c0

+ 260 - 127
src/lib/dhcpsrv/memfile_lease_mgr.cc

@@ -19,6 +19,7 @@
 #include <exceptions/exceptions.h>
 #include <util/pid_file.h>
 #include <util/process_spawn.h>
+#include <util/signal_set.h>
 #include <cstdio>
 #include <cstring>
 #include <errno.h>
@@ -41,13 +42,190 @@ const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE";
 
 } // end of anonymous namespace
 
-using namespace isc;
-using namespace isc::dhcp;
 using namespace isc::util;
 
+namespace isc {
+namespace dhcp {
+
+/// @brief Represents a configuration for Lease File Cleanup.
+///
+/// This class is solely used by the @c Memfile_LeaseMgr as a configuration
+/// information storage for %Lease File Cleanup. Internally, it creates
+/// the interval timer and assigns a callback function (pointer to which is
+/// passed in the constructor), which will be called at the specified
+/// intervals to perform the cleanup. It is also responsible for creating
+/// and maintaing the object which is used to spawn the new process which
+/// executes the @c kea-lfc program.
+///
+/// This functionality is enclosed in a separate class so as the implementation
+/// details are not exposed in the @c Memfile_LeaseMgr header file and
+/// to maintain a single place with the LFC configuration, instead of multiple
+/// members and functions scattered in the @c Memfile_LeaseMgr class.
+class LFCSetup {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Assigns a pointer to the function triggered to perform the cleanup.
+    /// This pointer should point to the appropriate method of the
+    /// @c Memfile_LeaseMgr class.
+    ///
+    /// @param callback A pointer to the callback function.
+    /// @param io_service An io service used to create the interval timer.
+    LFCSetup(asiolink::IntervalTimer::Callback callback,
+             asiolink::IOService& io_service);
+
+    /// @brief Sets the new configuration for the %Lease File Cleanup.
+    ///
+    /// @param lfc_interval An interval in seconds at which the cleanup should
+    /// be performed.
+    /// @param lease_file4 A pointer to the DHCPv4 lease file to be cleaned up
+    /// or NULL. If this is NULL, the @c lease_file6 must be non-null.
+    /// @param lease_file6 A pointer to the DHCPv6 lease file to be cleaned up
+    /// or NULL. If this is NULL, the @c lease_file4 must be non-null.
+    void setup(const uint32_t lfc_interval,
+               const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
+               const boost::shared_ptr<CSVLeaseFile6>& lease_file6);
+
+    /// @brief Spawns a new process.
+    void execute();
+
+    /// @brief Returns interval at which the cleanup is performed.
+    ///
+    /// @return Interval in milliseconds.
+    long getInterval() const;
+
+    /// @brief Checks if the lease file cleanup is in progress.
+    ///
+    /// @return true if the lease file cleanup is being executed.
+    bool isRunning() const;
+
+    /// @brief Returns exit code of the last completed cleanup.
+    int getExitStatus() const;
+
+private:
+
+    /// @brief Interval timer for LFC.
+    asiolink::IntervalTimer timer_;
+
+    /// @brief A pointer to the @c ProcessSpawn object used to execute
+    /// the LFC.
+    boost::scoped_ptr<util::ProcessSpawn> process_;
+
+    /// @brief A pointer to the callback function executed by the timer.
+    asiolink::IntervalTimer::Callback callback_;
+
+    /// @brief A PID of the last executed LFC process.
+    pid_t pid_;
+};
+
+LFCSetup::LFCSetup(asiolink::IntervalTimer::Callback callback,
+                   asiolink::IOService& io_service)
+    : timer_(io_service), process_(), callback_(callback), pid_(0) {
+}
+
+void
+LFCSetup::setup(const uint32_t lfc_interval,
+                const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
+                const boost::shared_ptr<CSVLeaseFile6>& lease_file6) {
+
+    // If LFC is enabled, we have to setup the interval timer and prepare for
+    // executing the kea-lfc process.
+    if (lfc_interval > 0) {
+        std::string executable;
+        char* c_executable = getenv(KEA_LFC_EXECUTABLE_ENV_NAME);
+        if (c_executable == NULL) {
+            executable = KEA_LFC_EXECUTABLE;
+
+        } else {
+            executable = c_executable;
+        }
+
+        // Set the timer to call callback function periodically.
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
+        // Multiple the lfc_interval value by 1000 as this value specifies
+        // a timeout in seconds, whereas the setup() method expects the
+        // timeout in milliseconds.
+        timer_.setup(callback_, lfc_interval * 1000);
+
+        // Start preparing the command line for kea-lfc.
+
+        // Gather the base file name.
+        std::string lease_file = lease_file4 ? lease_file4->getFilename() :
+            lease_file6->getFilename();
+
+        // Create the other names by appending suffixes to the base name.
+        util::ProcessArgs args;
+        // Universe: v4 or v6.
+        args.push_back(lease_file4 ? "-4" : "-6");
+        // Previous file.
+        args.push_back("-x");
+        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
+                                                      Memfile_LeaseMgr::FILE_PREVIOUS));
+        // Input file.
+        args.push_back("-i");
+        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
+                                                      Memfile_LeaseMgr::FILE_INPUT));
+        // Output file.
+        args.push_back("-o");
+        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
+                                                      Memfile_LeaseMgr::FILE_OUTPUT));
+        // Finish file.
+        args.push_back("-f");
+        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
+                                                      Memfile_LeaseMgr::FILE_FINISH));
+        // PID file.
+        args.push_back("-p");
+        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
+                                                      Memfile_LeaseMgr::FILE_PID));
+
+        // The configuration file is currently unused.
+        args.push_back("-c");
+        args.push_back("ignored-path");
+
+        // Create the process (do not start it yet).
+        process_.reset(new util::ProcessSpawn(executable, args));
+    }
+}
+
+long
+LFCSetup::getInterval() const {
+    return (timer_.getInterval());
+}
+
+void
+LFCSetup::execute() {
+    try {
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_EXECUTE)
+            .arg(process_->getCommandLine());
+        pid_ = process_->spawn();
+        std::cout << process_->getCommandLine() << std::endl;
+
+    } catch (const ProcessSpawnError& ex) {
+        LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SPAWN_FAIL);
+    }
+}
+
+bool
+LFCSetup::isRunning() const {
+    return (process_ && process_->isRunning(pid_));
+}
+
+int
+LFCSetup::getExitStatus() const {
+    if (!process_) {
+        isc_throw(InvalidOperation, "unable to obtain LFC process exit code: "
+                  " the process is NULL");
+    }
+    return (process_->getExitStatus(pid_));
+}
+
+
 Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
-    : LeaseMgr(parameters), lfc_timer_(*getIOService()),
-      lfc_process_() {
+    : LeaseMgr(parameters),
+      lfc_setup_(new LFCSetup(boost::bind(&Memfile_LeaseMgr::lfcCallback, this),
+                              *getIOService()))
+    {
     // Check the universe and use v4 file or v6 file.
     std::string universe = getParameter("universe");
     if (universe == "4") {
@@ -466,7 +644,7 @@ Memfile_LeaseMgr::appendSuffix(const std::string& file_name,
 
 uint32_t
 Memfile_LeaseMgr::getIOServiceExecInterval() const {
-    return (static_cast<uint32_t>(lfc_timer_.getInterval() / 1000));
+    return (static_cast<uint32_t>(lfc_setup_->getInterval() / 1000));
 }
 
 std::string
@@ -528,79 +706,67 @@ Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
     return (lease_file);
 }
 
-void
-Memfile_LeaseMgr::lfcSetup() {
-    std::string lfc_interval_str = "0";
-    try {
-        lfc_interval_str = getParameter("lfc-interval");
-    } catch (const std::exception& ex) {
-        // Ignore and default to 0.
+template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
+void Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename,
+                                           boost::shared_ptr<LeaseFileType>& lease_file,
+                                           StorageType& storage) {
+    // Check if the instance of the LFC is running right now. If it is
+    // running, we refuse to load leases as the LFC may be writing to the
+    // lease files right now. When the user retries server configuration
+    // it should go through.
+    /// @todo Consider applying a timeout for an LFC and retry when this
+    /// timeout elapses.
+    PIDFile pid_file(appendSuffix(filename, FILE_PID));
+    if (pid_file.check()) {
+        isc_throw(DbOpenError, "unable to load leases from files while the "
+                  "lease file cleanup is in progress");
     }
 
-    uint32_t lfc_interval = 0;
-    try {
-        lfc_interval = boost::lexical_cast<uint32_t>(lfc_interval_str);
-    } catch (boost::bad_lexical_cast& ex) {
-        isc_throw(isc::BadValue, "invalid value of the lfc-interval "
-                  << lfc_interval_str << " specified");
-    }
+    storage.clear();
 
-    // If LFC is enabled, we have to setup the interval timer and prepare for
-    // executing the kea-lfc process.
-    if (lfc_interval > 0) {
-        std::string executable;
-        char* c_executable = getenv(KEA_LFC_EXECUTABLE_ENV_NAME);
-        if (c_executable == NULL) {
-            executable = KEA_LFC_EXECUTABLE;
+    // Load the leasefile.completed, if exists.
+    lease_file.reset(new LeaseFileType(std::string(filename + ".completed")));
+    if (lease_file->exists()) {
+        LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
+                                               MAX_LEASE_ERRORS);
 
-        } else {
-            executable = c_executable;
+    } else {
+        // If the leasefile.completed doesn't exist, let's load the leases
+        // from leasefile.2 and leasefile.1, if they exist.
+        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_PREVIOUS)));
+        if (lease_file->exists()) {
+            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
+                                                   MAX_LEASE_ERRORS);
         }
 
-        // Set the timer to call callback function periodically.
-        asiolink::IntervalTimer::Callback cb =
-            boost::bind(&Memfile_LeaseMgr::lfcCallback, this);
-        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
-        // Multiple the lfc_interval value by 1000 as this value specifies
-        // a timeout in seconds, whereas the setup() method expects the
-        // timeout in milliseconds.
-        lfc_timer_.setup(cb, lfc_interval * 1000);
-
-        // Start preparing the command line for kea-lfc.
-
-        // Gather the base file name.
-        std::string lease_file = lease_file4_ ? lease_file4_->getFilename() :
-            lease_file6_->getFilename();
+        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_INPUT)));
+        if (lease_file->exists()) {
+            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
+                                                   MAX_LEASE_ERRORS);
+        }
+    }
 
-        // Create the other names by appending suffixes to the base name.
-        util::ProcessArgs args;
-        // Universe: v4 or v6.
-        args.push_back(lease_file4_ ? "-4" : "-6");
-        // Previous file.
-        args.push_back("-x");
-        args.push_back(appendSuffix(lease_file, FILE_PREVIOUS));
-        // Input file.
-        args.push_back("-i");
-        args.push_back(appendSuffix(lease_file, FILE_INPUT));
-        // Output file.
-        args.push_back("-o");
-        args.push_back(appendSuffix(lease_file, FILE_OUTPUT));
-        // Finish file.
-        args.push_back("-f");
-        args.push_back(appendSuffix(lease_file, FILE_FINISH));
-        // PID file.
-        args.push_back("-p");
-        args.push_back(appendSuffix(lease_file, FILE_PID));
+    // Always load leases from the primary lease file. If the lease file
+    // doesn't exist it will be created by the LeaseFileLoader. Note
+    // that the false value passed as the last parameter to load
+    // function causes the function to leave the file open after
+    // it is parsed. This file will be used by the backend to record
+    // future lease updates.
+    lease_file.reset(new LeaseFileType(filename));
+    LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
+                                           MAX_LEASE_ERRORS, false);;
+}
 
-        // The configuration file is currently unused.
-        args.push_back("-c");
-        args.push_back("ignored-path");
 
-        // Create the process (do not start it yet).
-        lfc_process_.reset(new util::ProcessSpawn(executable, args));
-    }
+bool
+Memfile_LeaseMgr::isLFCRunning() const {
+    return (lfc_setup_->isRunning());
 }
 
+int
+Memfile_LeaseMgr::getLFCExitStatus() const {
+    return (lfc_setup_->getExitStatus());
+}
 
 void
 Memfile_LeaseMgr::lfcCallback() {
@@ -608,16 +774,37 @@ Memfile_LeaseMgr::lfcCallback() {
 
     // Check if we're in the v4 or v6 space and use the appropriate file.
     if (lease_file4_) {
-        leaseFileCleanup(lease_file4_);
+        lfcExecute(lease_file4_);
 
     } else if (lease_file6_) {
-        leaseFileCleanup(lease_file6_);
+        lfcExecute(lease_file6_);
+    }
+}
+
+void
+Memfile_LeaseMgr::lfcSetup() {
+    std::string lfc_interval_str = "0";
+    try {
+        lfc_interval_str = getParameter("lfc-interval");
+    } catch (const std::exception& ex) {
+        // Ignore and default to 0.
+    }
+
+    uint32_t lfc_interval = 0;
+    try {
+        lfc_interval = boost::lexical_cast<uint32_t>(lfc_interval_str);
+    } catch (boost::bad_lexical_cast& ex) {
+        isc_throw(isc::BadValue, "invalid value of the lfc-interval "
+                  << lfc_interval_str << " specified");
+    }
 
+    if (lfc_interval > 0) {
+        lfc_setup_->setup(lfc_interval, lease_file4_, lease_file6_);
     }
 }
 
 template<typename LeaseFileType>
-void Memfile_LeaseMgr::leaseFileCleanup(boost::shared_ptr<LeaseFileType>& lease_file) {
+void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file) {
     bool do_lfc = true;
     // This string will hold a reason for the failure to rote the lease files.
     std::string error_string = "(no details)";
@@ -665,14 +852,7 @@ void Memfile_LeaseMgr::leaseFileCleanup(boost::shared_ptr<LeaseFileType>& lease_
     // Once we have rotated files as needed, start the new kea-lfc process
     // to perform a cleanup.
     if (do_lfc) {
-        try {
-            lfc_process_->spawn();
-            LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_EXECUTE)
-                .arg(lfc_process_->getCommandLine());
-
-        } catch (const ProcessSpawnError& ex) {
-            LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SPAWN_FAIL);
-        }
+        lfc_setup_->execute();
 
     } else {
         LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_ROTATION_FAIL)
@@ -682,53 +862,6 @@ void Memfile_LeaseMgr::leaseFileCleanup(boost::shared_ptr<LeaseFileType>& lease_
     }
 }
 
-template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
-void Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename,
-                                           boost::shared_ptr<LeaseFileType>& lease_file,
-                                           StorageType& storage) {
-    // Check if the instance of the LFC is running right now. If it is
-    // running, we refuse to load leases as the LFC may be writing to the
-    // lease files right now. When the user retries server configuration
-    // it should go through.
-    /// @todo Consider applying a timeout for an LFC and retry when this
-    /// timeout elapses.
-    PIDFile pid_file(appendSuffix(filename, FILE_PID));
-    if (pid_file.check()) {
-        isc_throw(DbOpenError, "unable to load leases from files while the "
-                  "lease file cleanup is in progress");
-    }
-
-    storage.clear();
-
-    // Load the leasefile.completed, if exists.
-    lease_file.reset(new LeaseFileType(std::string(filename + ".completed")));
-    if (lease_file->exists()) {
-        LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
-                                               MAX_LEASE_ERRORS);
-
-    } else {
-        // If the leasefile.completed doesn't exist, let's load the leases
-        // from leasefile.2 and leasefile.1, if they exist.
-        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_PREVIOUS)));
-        if (lease_file->exists()) {
-            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
-                                                   MAX_LEASE_ERRORS);
-        }
 
-        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_INPUT)));
-        if (lease_file->exists()) {
-            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
-                                                   MAX_LEASE_ERRORS);
-        }
-    }
-
-    // Always load leases from the primary lease file. If the lease file
-    // doesn't exist it will be created by the LeaseFileLoader. Note
-    // that the false value passed as the last parameter to load
-    // function causes the function to leave the file open after
-    // it is parsed. This file will be used by the backend to record
-    // future lease updates.
-    lease_file.reset(new LeaseFileType(filename));
-    LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
-                                           MAX_LEASE_ERRORS, false);;
-}
+} // end of namespace isc::dhcp
+} // end of namespace isc

+ 119 - 76
src/lib/dhcpsrv/memfile_lease_mgr.h

@@ -29,6 +29,8 @@
 namespace isc {
 namespace dhcp {
 
+class LFCSetup;
+
 /// @brief Concrete implementation of a lease database backend using flat file.
 ///
 /// This class implements a lease database backend using CSV files to store
@@ -105,7 +107,7 @@ public:
 
     /// @brief Specifies universe (V4, V6)
     ///
-    /// This enumeration is used by various functions in Memfile Lease Manager,
+    /// This enumeration is used by various functions in Memfile %Lease Manager,
     /// to identify the lease type referred to. In particular, it is used by
     /// functions operating on the lease files to distinguish between lease
     /// files for DHCPv4 and DHCPv6.
@@ -114,18 +116,10 @@ public:
         V6
     };
 
-    /// @brief Types of the lease files used by the Lease File Cleanup.
-    ///
-    /// This enumeration is used by a method which appends the appropriate
-    /// suffix to the lease file name.
-    enum LFCFileType {
-        FILE_CURRENT,
-        FILE_INPUT,
-        FILE_PREVIOUS,
-        FILE_OUTPUT,
-        FILE_FINISH,
-        FILE_PID
-    };
+    /// @name Methods implementing the API of the lease database backend.
+    ///       The following methods are implementing the API of the
+    ///       @c LeaseMgr to manage leases.
+    //@{
 
     /// @brief The sole lease manager constructor
     ///
@@ -335,12 +329,30 @@ public:
     /// support transactions, this is a no-op.
     virtual void rollback();
 
+    //@}
+
+    /// @name Public type and method used to determine file names for LFC.
+    //@{
+
+    /// @brief Types of the lease files used by the %Lease File Cleanup.
+    ///
+    /// This enumeration is used by a method which appends the appropriate
+    /// suffix to the lease file name.
+    enum LFCFileType {
+        FILE_CURRENT,
+        FILE_INPUT,
+        FILE_PREVIOUS,
+        FILE_OUTPUT,
+        FILE_FINISH,
+        FILE_PID
+    };
+
     /// @brief Appends appropriate suffix to the file name.
     ///
     /// The suffix is selected using the LFC file type specified as a
     /// parameter. Each file type uses a unique suffix or no suffix:
     /// - Current File: no suffix
-    /// - Lease File Copy or Input File: ".1"
+    /// - %Lease File Copy or Input File: ".1"
     /// - Previous File: ".2"
     /// - LFC Output File: ".output"
     /// - LFC Finish File: ".completed"
@@ -353,11 +365,18 @@ public:
     /// @return A lease file name with a suffix appended.
     static std::string appendSuffix(const std::string& file_name,
                                     const LFCFileType& file_type);
+    //@}
+
+
+    /// @name Miscellaneous public convenience methods.
+    ///       The following methods allow for retrieving useful information
+    ///       about the state of the backend.
+    //@{
 
     /// @brief Returns the interval at which the @c IOService events should
     /// be released.
     ///
-    /// The Memfile backend may install a timer to execute the Lease File
+    /// The Memfile backend may install a timer to execute the %Lease File
     /// Cleanup periodically. If this timer is installed, the method returns
     /// the LFC interval in milliseconds.
     ///
@@ -393,35 +412,10 @@ public:
     /// server shut down.
     bool persistLeases(Universe u) const;
 
-protected:
-
-    /// @brief A callback function triggering Lease File Cleanup.
-    ///
-    /// This method is virtual so as it can be overriden and customized in
-    /// the unit tests. In particular, the unit test which checks that the
-    /// callback function has been executed would override this function
-    /// to increase the execution counter each time it is executed.
-    ///
-    /// @todo Once the callback is implemented, there is a need to
-    /// extend the documentation of this method. Currently, it simply
-    /// logs that it has been called.
-    virtual void lfcCallback();
+    //@}
 
 private:
 
-    /// @brief Setup the periodic Lease File Cleanup.
-    ///
-    /// This method checks if the @c lfc-interval configuration parameter
-    /// is set to a non-zero value and sets up the interval timer to
-    /// perform the Lease File Cleanup periodically. It also prepares the
-    /// path and arguments for the @c kea-lfc application which will be
-    /// executed to perform the cleanup. By default the backend will use
-    /// the path to the kea-lfc in the Kea installation directory. If
-    /// the unit tests need to override this path (with the path in the
-    /// Kea build directory, the @c KEA_LFC_EXECUTABLE environmental
-    /// variable should be set to hold an absolute path to the kea-lfc
-    /// excutable.
-    void lfcSetup();
 
     /// @brief Initialize the location of the lease file.
     ///
@@ -440,31 +434,6 @@ private:
     /// argument to this function.
     std::string initLeaseFilePath(Universe u);
 
-    /// @brief Performs a lease file cleanup for DHCPv4 or DHCPv6.
-    ///
-    /// This method performs all the actions necessary to prepare for the
-    /// execution of the LFC and if these actions are sucessful, it executes
-    /// the @c kea-lfc application as a background process to process (cleanup)
-    /// the lease files.
-    ///
-    /// For the design and the terminology used in this description refer to
-    /// the http://kea.isc.org/wiki/LFCDesign.
-    ///
-    /// If the method finds that the Lease File Copy exists it simply runs
-    /// the @c kea-lfc application.
-    ///
-    /// If the Lease File Copy doesn't exist it moves the Current Lease File
-    /// to Lease File Copy, and then recreates the Current Lease File without
-    /// any lease entries. If the file has been successfully moved, it runs
-    /// the @c kea-lfc application.
-    ///
-    /// @param lease_file A pointer to the object representing the Current
-    /// Lease File (DHCPv4 or DHCPv6 lease file).
-    ///
-    /// @tparam LeaseFileType One of @c CSVLeaseFile4 or @c CSVLeaseFile6.
-    template<typename LeaseFileType>
-    void leaseFileCleanup(boost::shared_ptr<LeaseFileType>& lease_file);
-
     /// @brief Load leases from the persistent storage.
     ///
     /// This method loads DHCPv4 or DHCPv6 leases from lease files in the
@@ -492,7 +461,7 @@ private:
     ///
     /// @note: When the server starts up or is reconfigured it will try to
     /// read leases from the lease files using this method. It is possible
-    /// that the Lease File Cleanup is performed upon the lease files to
+    /// that the %Lease File Cleanup is performed upon the lease files to
     /// be read by this method. This may result in conflicts between the
     /// server process and the LFC. To prevent it, the method checks if the
     /// instance of the @c kea-lfc is running (using the PID file) before it
@@ -533,19 +502,93 @@ private:
     /// @brief Holds the pointer to the DHCPv6 lease file IO.
     boost::shared_ptr<CSVLeaseFile6> lease_file6_;
 
-    /// @brief A timer scheduled to perform Lease File Cleanup.
-    asiolink::IntervalTimer lfc_timer_;
+public:
+
+    /// @name Public methods to retrieve information about the LFC process state.
+    ///       These methods are meant to be used by unit tests to retrieve the
+    ///       state of the spawned LFC process before validating the result of
+    ///       the lease file cleanup.
+    //@{
+
+    /// @brief Checks if the process performing lease file cleanup is running.
+    ///
+    /// @return true if the process performing lease file cleanup is running.
+    bool isLFCRunning() const;
+
+    /// @brief Returns the status code returned by the last executed
+    /// LFC process.
+    int getLFCExitStatus() const;
+    //@}
+
+
+    /// @name Protected methods used for %Lease File Cleanup.
+    /// The following methods are protected so as they can be accessed and
+    /// tested by unit tests.
+    //@{
 
 protected:
 
-    /// @brief An object representing the kea-lfc process.
+    /// @brief A callback function triggering %Lease File Cleanup (LFC).
+    ///
+    /// This method is executed periodically to start the lease file cleanup.
+    /// It checks whether the file is a DHCPv4 lease file or DHCPv6 lease file
+    /// and executes the @c Memfile_LeaseMgr::lfcExecute private method
+    /// with the appropriate parameters.
+    ///
+    /// This method is virtual so as it can be overridden and customized in
+    /// the unit tests. In particular, the unit test which checks that the
+    /// callback function has been executed would override this function
+    /// to increase the execution counter each time it is executed.
+    virtual void lfcCallback();
+    //@}
+
+    /// @name Private methods and members used for %Lease File Cleanup.
+    //@{
+
+private:
+
+    /// @brief Setup the periodic %Lease File Cleanup.
     ///
-    /// This object is created when the LFC is configured to be executed
-    /// periodically. It holds the path to kea-lfc program and the
-    /// arguments. When the LFC timer kicks the lease file cleanup
-    /// this object is used to spawn the kea-lfc as a background
-    /// process.
-    boost::scoped_ptr<util::ProcessSpawn> lfc_process_;
+    /// This method checks if the @c lfc-interval configuration parameter
+    /// is set to a non-zero value and sets up the interval timer to
+    /// perform the %Lease File Cleanup periodically. It also prepares the
+    /// path and arguments for the @c kea-lfc application which will be
+    /// executed to perform the cleanup. By default the backend will use
+    /// the path to the kea-lfc in the Kea installation directory. If
+    /// the unit tests need to override this path (with the path in the
+    /// Kea build directory, the @c KEA_LFC_EXECUTABLE environmental
+    /// variable should be set to hold an absolute path to the kea-lfc
+    /// excutable.
+    void lfcSetup();
+
+    /// @brief Performs a lease file cleanup for DHCPv4 or DHCPv6.
+    ///
+    /// This method performs all the actions necessary to prepare for the
+    /// execution of the LFC and if these actions are successful, it executes
+    /// the @c kea-lfc application as a background process to process (cleanup)
+    /// the lease files.
+    ///
+    /// For the design and the terminology used in this description refer to
+    /// the http://kea.isc.org/wiki/LFCDesign.
+    ///
+    /// If the method finds that the %Lease File Copy exists it simply runs
+    /// the @c kea-lfc application.
+    ///
+    /// If the %Lease File Copy doesn't exist it moves the Current %Lease File
+    /// to Lease File Copy, and then recreates the Current Lease File without
+    /// any lease entries. If the file has been successfully moved, it runs
+    /// the @c kea-lfc application.
+    ///
+    /// @param lease_file A pointer to the object representing the Current
+    /// %Lease File (DHCPv4 or DHCPv6 lease file).
+    ///
+    /// @tparam LeaseFileType One of @c CSVLeaseFile4 or @c CSVLeaseFile6.
+    template<typename LeaseFileType>
+    void lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file);
+
+    /// @brief A pointer to the Lease File Cleanup configuration.
+    boost::scoped_ptr<LFCSetup> lfc_setup_;
+    //@}
 
 };
 

+ 15 - 9
src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc

@@ -94,7 +94,6 @@ public:
     }
 
     using Memfile_LeaseMgr::lfcCallback;
-    using Memfile_LeaseMgr::lfc_process_;
 };
 
 /// @brief Test fixture class for @c Memfile_LeaseMgr
@@ -227,11 +226,11 @@ public:
     /// @param timeout Timeout in seconds.
     ///
     /// @return true if the process ended, false otherwise
-    bool waitForProcess(const util::ProcessSpawn& process,
+    bool waitForProcess(const Memfile_LeaseMgr& lease_mgr,
                         const uint8_t timeout) {
         uint32_t iterations = 0;
         const uint32_t iterations_max = timeout * 1000;
-        while (process.isRunning() && (iterations < iterations_max)) {
+        while (lease_mgr.isLFCRunning() && (iterations < iterations_max)) {
             usleep(1000);
             ++iterations;
         }
@@ -439,10 +438,12 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup4) {
     EXPECT_EQ(new_file_contents, current_file.readFile());
 
     // Wait for the LFC process to complete.
-    ASSERT_TRUE(waitForProcess(*lease_mgr->lfc_process_, 2));
+    ASSERT_TRUE(waitForProcess(*lease_mgr, 2));
 
     // And make sure it has returned an exit status of 0.
-    EXPECT_EQ(0, lease_mgr->lfc_process_->getExitStatus());
+    EXPECT_EQ(0, lease_mgr->getLFCExitStatus())
+        << "Executing the LFC process failed: make sure that"
+        " the kea-lfc program has been compiled.";
 
     // Check if we can still write to the lease file.
     std::vector<uint8_t> hwaddr_vec(6);
@@ -515,10 +516,13 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) {
     EXPECT_EQ(new_file_contents, current_file.readFile());
 
     // Wait for the LFC process to complete.
-    ASSERT_TRUE(waitForProcess(*lease_mgr->lfc_process_, 2));
+    ASSERT_TRUE(waitForProcess(*lease_mgr, 2));
 
     // And make sure it has returned an exit status of 0.
-    EXPECT_EQ(0, lease_mgr->lfc_process_->getExitStatus());
+    EXPECT_EQ(0, lease_mgr->getLFCExitStatus())
+        << "Executing the LFC process failed: make sure that"
+        " the kea-lfc program has been compiled.";
+
 
     // Check if we can still write to the lease file.
     std::vector<uint8_t> duid_vec(13);
@@ -578,10 +582,12 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanupStartFail) {
     ASSERT_NO_THROW(lease_mgr->lfcCallback());
 
     // Wait for the LFC process to complete.
-    ASSERT_TRUE(waitForProcess(*lease_mgr->lfc_process_, 2));
+    ASSERT_TRUE(waitForProcess(*lease_mgr, 2));
 
     // And make sure it has returned an error.
-    EXPECT_EQ(EXIT_FAILURE, lease_mgr->lfc_process_->getExitStatus());
+    EXPECT_EQ(EXIT_FAILURE, lease_mgr->getLFCExitStatus())
+        << "Executing the LFC process failed: make sure that"
+        " the kea-lfc program has been compiled.";
 }
 
 // Test that the backend returns a correct value of the interval