Parcourir la source

[trac3665] Add code to do the cleanup moves for the lease files

After we have completed writing the lease file and moved it to finish
we need to delete the previous and copy files if they exist and move
the finish file to previous.
Shawn Routhier il y a 10 ans
Parent
commit
5566ccda94

+ 37 - 0
src/bin/lfc/lfc_controller.cc

@@ -20,6 +20,7 @@
 #include <sstream>
 #include <sstream>
 #include <unistd.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdlib.h>
+#include <cerrno>
 
 
 using namespace std;
 using namespace std;
 using namespace isc::util;
 using namespace isc::util;
@@ -77,6 +78,16 @@ LFCController::launch(int argc, char* argv[]) {
 
 
     // do other work (TBD)
     // do other work (TBD)
     std::cerr << "Add code to perform lease cleanup" << std::endl;
     std::cerr << "Add code to perform lease cleanup" << std::endl;
+    // If we don't have a finish file do the processing
+
+    // We either already had a finish file or just created one, do the
+    // file cleanup, we don't want to return after the catch as we
+    // still need to cleanup the pid file
+    try {
+        fileCleanup();
+    } catch (const RunTimeFail& run_ex) {
+        std::cerr << run_ex.what() << std::endl;
+    }
 
 
     // delete the pid file for this instance
     // delete the pid file for this instance
     try {
     try {
@@ -268,5 +279,31 @@ LFCController::getVersion(const bool extended) const{
     return (version_stream.str());
     return (version_stream.str());
 }
 }
 
 
+void
+LFCController::processLeases() const {
+}
+
+void
+LFCController::fileCleanup() const {
+    // Remove the old previous file
+    if ((remove(previous_file_.c_str()) != 0) &&
+        (errno != ENOENT)) {
+        isc_throw(RunTimeFail, "Unable to delete previous file '"
+                  << previous_file_ << "' error: " << strerror(errno));
+    }
+
+    // Remove the copy file
+    if ((remove(copy_file_.c_str()) != 0) &&
+        (errno != ENOENT)) {
+        isc_throw(RunTimeFail, "Unable to delete copy file '"
+                  << copy_file_ << "' error: " << strerror(errno));
+    }
+
+    // Rename the finish file to be the previous file
+    if (rename(finish_file_.c_str(), previous_file_.c_str()) != 0)
+        isc_throw(RunTimeFail, "Unable to move finish (" << finish_file_
+                  << ") to previous (" << previous_file_
+                  << ") error: " << strerror(errno));
+}
 }; // namespace isc::lfc
 }; // namespace isc::lfc
 }; // namespace isc
 }; // namespace isc

+ 24 - 2
src/bin/lfc/lfc_controller.h

@@ -28,6 +28,13 @@ public:
         isc::Exception(file, line, what) { };
         isc::Exception(file, line, what) { };
 };
 };
 
 
+/// @brief Exceptions thrown when the processing fails
+class RunTimeFail : public isc::Exception {
+public:
+    RunTimeFail(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief Process controller for LFC process
 /// @brief Process controller for LFC process
 ///
 ///
 /// This class provides the LFC process functions. These are used to:
 /// This class provides the LFC process functions. These are used to:
@@ -36,7 +43,6 @@ public:
 /// the lease files as necessary.
 /// the lease files as necessary.
 ///
 ///
 /// @todo The current code simply processes the command line we still need to
 /// @todo The current code simply processes the command line we still need to
-/// -# handle PID file manipulation
 /// -# invoke the code to read, process and write the lease files
 /// -# invoke the code to read, process and write the lease files
 /// -# rename and delete the shell files as required
 /// -# rename and delete the shell files as required
 class LFCController {
 class LFCController {
@@ -61,7 +67,10 @@ public:
     /// -# parse command line arguments
     /// -# parse command line arguments
     /// -# verify that it is the only instance
     /// -# verify that it is the only instance
     /// -# create pid file
     /// -# create pid file
-    /// -# .... TBD
+    /// -# read leases files TBD
+    /// -# write lease file TBD
+    /// -# move leases files TBD
+    /// -# cleanup artifacts TBD
     /// -# remove pid file
     /// -# remove pid file
     /// -# exit to the caller
     /// -# exit to the caller
     ///
     ///
@@ -145,6 +154,19 @@ public:
     std::string getPidFile() const {
     std::string getPidFile() const {
         return (pid_file_);
         return (pid_file_);
     }
     }
+
+    /// @brief Process files.  Read in the leases from any previous & copy
+    /// files we have and write the results out to the output file.  Upon
+    /// completion of the write move the file to the finish file.
+    void processLeases() const;
+
+    /// @brief Cleanup files.  After we have a finish file, either from
+    /// doing the cleanup or because a previous instance was interrupted,
+    /// delete the work files (previous & copy) and move the finish file
+    /// to be the new previous file.
+    ///
+    /// @throw RunTimeFail if the command line parameters are invalid.
+    void fileCleanup() const;
     //@}
     //@}
 
 
 private:
 private:

+ 190 - 5
src/bin/lfc/tests/lfc_controller_unittests.cc

@@ -14,16 +14,111 @@
 
 
 #include <lfc/lfc_controller.h>
 #include <lfc/lfc_controller.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
+#include <fstream>
+#include <cerrno>
 
 
 using namespace isc::lfc;
 using namespace isc::lfc;
 using namespace std;
 using namespace std;
 
 
 namespace {
 namespace {
 
 
+// Filenames used for testing.
+const char* PREVIOUS = "lease_file.2";
+const char* COPY   = "lease_file.1";
+const char* FINISH = "lease_file.completed";
+const char* OUTPUT = "lease_file.output";
+const char* PID    = "lease_file.pid";
+
+class LFCControllerTest : public ::testing::Test {
+public:
+    string pstr_;
+    string cstr_;
+    string fstr_;
+    string ostr_;
+    string istr_;
+
+    /// @brief Create a file and write the filename into it.
+    void touchFile(const std::string& filename, int);
+
+    /// @brief check the file to see if i matches what was written to it.
+    bool checkFile(const std::string& filename, int);
+
+protected:
+    /// @brief Sets up the file names and Removes any old test
+    /// files before the test
+    virtual void SetUp() {
+        // set up the test files we need
+        std::ostringstream prev_str;
+        std::ostringstream copy_str;
+        std::ostringstream fin_str;
+        std::ostringstream out_str;
+        std::ostringstream pid_str;
+
+        prev_str << TEST_DATA_BUILDDIR << "/" << PREVIOUS;
+        pstr_ = prev_str.str();
+
+        copy_str << TEST_DATA_BUILDDIR << "/" << COPY;
+        cstr_ = copy_str.str();
+
+        fin_str << TEST_DATA_BUILDDIR << "/" << FINISH;
+        fstr_ = fin_str.str();
+
+        out_str << TEST_DATA_BUILDDIR << "/" << OUTPUT;
+        ostr_ = out_str.str();
+
+        pid_str << TEST_DATA_BUILDDIR << "/" << PID;
+        istr_ = pid_str.str();
+
+        // and remove any outstanding test files
+        removeTestFile();
+    }
+
+    /// @brief Removes any remaining test files after the test
+    virtual void TearDown() {
+        removeTestFile();
+    }
+
+private:
+
+    /// @brief Removes any remaining test files
+    void removeTestFile() const {
+        remove(pstr_.c_str());
+        remove(cstr_.c_str());
+        remove(fstr_.c_str());
+        remove(ostr_.c_str());
+        remove(istr_.c_str());
+    }
+
+};
+
+void
+LFCControllerTest::touchFile(const std::string& filename, int i) {
+    std::ofstream fs;
+
+    fs.open(filename, std::ofstream::out);
+    fs << i << std::endl;
+    fs.close();
+}
+
+bool
+LFCControllerTest::checkFile(const std::string& filename, int i) {
+    std::ifstream fs;
+    int j;
+
+    fs.open(filename, std::ifstream::in);
+    fs >> j;
+    fs.close();
+
+    if (i == j)
+        return (true);
+    return (false);
+}
+
+
 /// @brief Verify initial state of LFC controller.
 /// @brief Verify initial state of LFC controller.
 /// Create an instance of the controller and see that
 /// Create an instance of the controller and see that
 /// all of the initial values are empty as expected.
 /// all of the initial values are empty as expected.
-TEST(LFCControllerTest, initialValues) {
+TEST_F(LFCControllerTest, initialValues) {
     LFCController lfc_controller;
     LFCController lfc_controller;
 
 
     // Verify that we start with all the private variables empty
     // Verify that we start with all the private variables empty
@@ -39,7 +134,7 @@ TEST(LFCControllerTest, initialValues) {
 /// @brief Verify that parsing a full command line works.
 /// @brief Verify that parsing a full command line works.
 /// Parse a complete command line then verify the parsed
 /// Parse a complete command line then verify the parsed
 /// and saved data matches our expectations.
 /// and saved data matches our expectations.
-TEST(LFCControllerTest, fullCommandLine) {
+TEST_F(LFCControllerTest, fullCommandLine) {
     LFCController lfc_controller;
     LFCController lfc_controller;
 
 
     // Verify that standard options can be parsed without error
     // Verify that standard options can be parsed without error
@@ -75,7 +170,7 @@ TEST(LFCControllerTest, fullCommandLine) {
 /// Parse a command line that is correctly formatted but isn't complete
 /// Parse a command line that is correctly formatted but isn't complete
 /// (doesn't include some options or an some option arguments).  We
 /// (doesn't include some options or an some option arguments).  We
 /// expect that the parse will fail with an InvalidUsage exception.
 /// expect that the parse will fail with an InvalidUsage exception.
-TEST(LFCControllerTest, notEnoughData) {
+TEST_F(LFCControllerTest, notEnoughData) {
     LFCController lfc_controller;
     LFCController lfc_controller;
 
 
     // Test the results if we don't include all of the required arguments
     // Test the results if we don't include all of the required arguments
@@ -112,7 +207,7 @@ TEST(LFCControllerTest, notEnoughData) {
 /// to verify that we don't stop parsing when we find all of the
 /// to verify that we don't stop parsing when we find all of the
 /// required arguments.  We exepct the parse to fail with an
 /// required arguments.  We exepct the parse to fail with an
 /// InvalidUsage exception.
 /// InvalidUsage exception.
-TEST(LFCControllerTest, tooMuchData) {
+TEST_F(LFCControllerTest, tooMuchData) {
     LFCController lfc_controller;
     LFCController lfc_controller;
 
 
     // The standard options plus some others
     // The standard options plus some others
@@ -144,7 +239,7 @@ TEST(LFCControllerTest, tooMuchData) {
 /// @brief Verify that unknown arguments cause the parse to fail.
 /// @brief Verify that unknown arguments cause the parse to fail.
 /// Parse some unknown arguments to verify that we generate the
 /// Parse some unknown arguments to verify that we generate the
 /// proper InvalidUsage exception.
 /// proper InvalidUsage exception.
-TEST(LFCControllerTest, someBadData) {
+TEST_F(LFCControllerTest, someBadData) {
     LFCController lfc_controller;
     LFCController lfc_controller;
 
 
     // Some random arguments
     // Some random arguments
@@ -160,4 +255,94 @@ TEST(LFCControllerTest, someBadData) {
     EXPECT_THROW(lfc_controller.parseArgs(argc, argv), InvalidUsage);
     EXPECT_THROW(lfc_controller.parseArgs(argc, argv), InvalidUsage);
 }
 }
 
 
+/// @brief Verify that we do file_cleanup correctly.  We create different
+/// files and see if we properly delete and move them.
+TEST_F(LFCControllerTest, fileCleanup) {
+    LFCController lfc_controller, lfc_controller_launch;
+
+    // We can use the same arguments and controller for all of the tests
+    // as the files get redone for each subtest.
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-4"),
+                     const_cast<char*>("-x"),
+                     const_cast<char*>(pstr_.c_str()),
+                     const_cast<char*>("-i"),
+                     const_cast<char*>(cstr_.c_str()),
+                     const_cast<char*>("-o"),
+                     const_cast<char*>(ostr_.c_str()),
+                     const_cast<char*>("-c"),
+                     const_cast<char*>("config"),
+                     const_cast<char*>("-f"),
+                     const_cast<char*>(fstr_.c_str()),
+                     const_cast<char*>("-p"),
+                     const_cast<char*>(istr_.c_str()),
+                     const_cast<char*>("-d")
+    };
+    int argc = 15;
+    lfc_controller.parseArgs(argc, argv);
+
+    // Test 1: Start with no files - we expect an execption as there
+    // is no file to copy.
+    EXPECT_THROW(lfc_controller.fileCleanup(), RunTimeFail);
+
+
+    // Test 2: Create a file for each of previous, copy and finish.  We should
+    // delete the previous and copy files then move finish to previous.
+    touchFile(pstr_.c_str(), 1);
+    touchFile(cstr_.c_str(), 2);
+    touchFile(fstr_.c_str(), 3);
+
+    lfc_controller.fileCleanup();
+
+    // verify finish is now previous and copy and finish are gone
+    EXPECT_TRUE(checkFile(pstr_.c_str(), 3));
+    EXPECT_TRUE((remove(cstr_.c_str()) != 0) && (errno == ENOENT));
+    EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
+    remove(pstr_.c_str());
+
+
+    // Test 3: Create a file for previous and finish but not copy.
+    touchFile(pstr_.c_str(), 4);
+    touchFile(fstr_.c_str(), 6);
+
+    lfc_controller.fileCleanup();
+
+    // verify finish is now previous and copy and finish are gone
+    EXPECT_TRUE(checkFile(pstr_.c_str(), 6));
+    EXPECT_TRUE((remove(cstr_.c_str()) != 0) && (errno == ENOENT));
+    EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
+    remove(pstr_.c_str());
+
+
+    // Test 4: Create a file for copy and finish but not previous.
+    touchFile(cstr_.c_str(), 8);
+    touchFile(fstr_.c_str(), 9);
+
+    lfc_controller.fileCleanup();
+
+    // verify finish is now previous and copy and finish are gone
+    EXPECT_TRUE(checkFile(pstr_.c_str(), 9));
+    EXPECT_TRUE((remove(cstr_.c_str()) != 0) && (errno == ENOENT));
+    EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
+    remove(pstr_.c_str());
+
+
+    // Test 5: rerun test 2 but using launch instead of cleanup
+    // as we already have a finish file we shouldn't do any extra
+    // processing
+    touchFile(pstr_.c_str(), 10);
+    touchFile(cstr_.c_str(), 11);
+    touchFile(fstr_.c_str(), 12);
+
+    lfc_controller_launch.launch(argc, argv);
+
+    // verify finish is now previous and copy and finish are gone
+    // as we ran launch we also check to see if the pid is gone.
+    EXPECT_TRUE(checkFile(pstr_.c_str(), 12));
+    EXPECT_TRUE((remove(cstr_.c_str()) != 0) && (errno == ENOENT));
+    EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
+    EXPECT_TRUE((remove(istr_.c_str()) != 0) && (errno == ENOENT));
+    remove(pstr_.c_str());
+}
+
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 4 - 1
src/lib/util/pid_file.cc

@@ -16,6 +16,7 @@
 #include <cstdio>
 #include <cstdio>
 #include <signal.h>
 #include <signal.h>
 #include <unistd.h>
 #include <unistd.h>
+#include <cerrno>
 
 
 namespace isc {
 namespace isc {
 namespace util {
 namespace util {
@@ -87,7 +88,8 @@ PIDFile::write(int pid) const {
 
 
 void
 void
 PIDFile::deleteFile() const {
 PIDFile::deleteFile() const {
-    if (remove(filename_.c_str()) != 0) {
+    if ((remove(filename_.c_str()) != 0) &&
+        (errno != ENOENT)) {
         isc_throw(PIDFileError, "Unable to delete PID file '"
         isc_throw(PIDFileError, "Unable to delete PID file '"
                   << filename_ << "'");
                   << filename_ << "'");
     }
     }
@@ -95,3 +97,4 @@ PIDFile::deleteFile() const {
 
 
 } // namespace isc::util
 } // namespace isc::util
 } // namespace isc
 } // namespace isc
+

+ 8 - 1
src/lib/util/tests/pid_file_unittest.cc

@@ -64,7 +64,7 @@ protected:
 private:
 private:
     /// @brief Removes any remaining test files
     /// @brief Removes any remaining test files
     void removeTestFile() const {
     void removeTestFile() const {
-        remove(TESTNAME);
+        remove(absolutePath(TESTNAME).c_str());
     }
     }
 
 
 };
 };
@@ -192,4 +192,11 @@ TEST_F(PIDFileTest, pidWriteFail) {
     EXPECT_THROW(pid_file.write(10), PIDFileError);
     EXPECT_THROW(pid_file.write(10), PIDFileError);
 }
 }
 
 
+/// @brief Test deleting a file that doesn't exist
+TEST_F(PIDFileTest, noDeleteFile) {
+    PIDFile pid_file(absolutePath(TESTNAME));
+
+    // Delete a file we haven't created
+    pid_file.deleteFile();
+}
 } // end of anonymous namespace
 } // end of anonymous namespace