Commit 5566ccda authored by Shawn Routhier's avatar Shawn Routhier

[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.
parent c8bf8ed9
......@@ -20,6 +20,7 @@
#include <sstream>
#include <unistd.h>
#include <stdlib.h>
#include <cerrno>
using namespace std;
using namespace isc::util;
......@@ -77,6 +78,16 @@ LFCController::launch(int argc, char* argv[]) {
// do other work (TBD)
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
try {
......@@ -268,5 +279,31 @@ LFCController::getVersion(const bool extended) const{
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
......@@ -28,6 +28,13 @@ public:
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
///
/// This class provides the LFC process functions. These are used to:
......@@ -36,7 +43,6 @@ public:
/// the lease files as necessary.
///
/// @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
/// -# rename and delete the shell files as required
class LFCController {
......@@ -61,7 +67,10 @@ public:
/// -# parse command line arguments
/// -# verify that it is the only instance
/// -# create pid file
/// -# .... TBD
/// -# read leases files TBD
/// -# write lease file TBD
/// -# move leases files TBD
/// -# cleanup artifacts TBD
/// -# remove pid file
/// -# exit to the caller
///
......@@ -145,6 +154,19 @@ public:
std::string getPidFile() const {
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:
......
......@@ -14,16 +14,111 @@
#include <lfc/lfc_controller.h>
#include <gtest/gtest.h>
#include <fstream>
#include <cerrno>
using namespace isc::lfc;
using namespace std;
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.
/// Create an instance of the controller and see that
/// all of the initial values are empty as expected.
TEST(LFCControllerTest, initialValues) {
TEST_F(LFCControllerTest, initialValues) {
LFCController lfc_controller;
// 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.
/// Parse a complete command line then verify the parsed
/// and saved data matches our expectations.
TEST(LFCControllerTest, fullCommandLine) {
TEST_F(LFCControllerTest, fullCommandLine) {
LFCController lfc_controller;
// 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
/// (doesn't include some options or an some option arguments). We
/// expect that the parse will fail with an InvalidUsage exception.
TEST(LFCControllerTest, notEnoughData) {
TEST_F(LFCControllerTest, notEnoughData) {
LFCController lfc_controller;
// 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
/// required arguments. We exepct the parse to fail with an
/// InvalidUsage exception.
TEST(LFCControllerTest, tooMuchData) {
TEST_F(LFCControllerTest, tooMuchData) {
LFCController lfc_controller;
// The standard options plus some others
......@@ -144,7 +239,7 @@ TEST(LFCControllerTest, tooMuchData) {
/// @brief Verify that unknown arguments cause the parse to fail.
/// Parse some unknown arguments to verify that we generate the
/// proper InvalidUsage exception.
TEST(LFCControllerTest, someBadData) {
TEST_F(LFCControllerTest, someBadData) {
LFCController lfc_controller;
// Some random arguments
......@@ -160,4 +255,94 @@ TEST(LFCControllerTest, someBadData) {
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
......@@ -16,6 +16,7 @@
#include <cstdio>
#include <signal.h>
#include <unistd.h>
#include <cerrno>
namespace isc {
namespace util {
......@@ -87,7 +88,8 @@ PIDFile::write(int pid) const {
void
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 '"
<< filename_ << "'");
}
......@@ -95,3 +97,4 @@ PIDFile::deleteFile() const {
} // namespace isc::util
} // namespace isc
......@@ -64,7 +64,7 @@ protected:
private:
/// @brief Removes any remaining test files
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);
}
/// @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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment