Commit 3e0491cb authored by Shawn Routhier's avatar Shawn Routhier

[trac3665] Dump leases from storage to file and use this in LFC

Add the write method to lease_file_loader to dump the leaes files
from a storage container into a file.

Update the LFC code to use the lease_file_loader functions to
load the leases from the appropriate files and then to write
them to the output file and finally to move the file around.
parent 5566ccda
...@@ -15,7 +15,15 @@ ...@@ -15,7 +15,15 @@
#include <lfc/lfc_controller.h> #include <lfc/lfc_controller.h>
#include <util/pid_file.h> #include <util/pid_file.h>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
#include <dhcpsrv/csv_lease_file4.h>
#include <dhcpsrv/csv_lease_file6.h>
#include <dhcpsrv/memfile_lease_storage.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_file_loader.h>
#include <config.h> #include <config.h>
#include <boost/shared_ptr.hpp>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <unistd.h> #include <unistd.h>
...@@ -24,6 +32,7 @@ ...@@ -24,6 +32,7 @@
using namespace std; using namespace std;
using namespace isc::util; using namespace isc::util;
using namespace isc::dhcp;
namespace isc { namespace isc {
namespace lfc { namespace lfc {
...@@ -35,6 +44,9 @@ const char* LFCController::lfc_app_name_ = "DhcpLFC"; ...@@ -35,6 +44,9 @@ const char* LFCController::lfc_app_name_ = "DhcpLFC";
/// @brief Defines the executable name. /// @brief Defines the executable name.
const char* LFCController::lfc_bin_name_ = "kea-lfc"; const char* LFCController::lfc_bin_name_ = "kea-lfc";
/// @brief Maximum number of errors to read the leases from the lease file.
const uint32_t MAX_LEASE_ERRORS = 100;
LFCController::LFCController() LFCController::LFCController()
: protocol_version_(0), verbose_(false), config_file_(""), previous_file_(""), : protocol_version_(0), verbose_(false), config_file_(""), previous_file_(""),
copy_file_(""), output_file_(""), finish_file_(""), pid_file_("") { copy_file_(""), output_file_(""), finish_file_(""), pid_file_("") {
...@@ -77,13 +89,22 @@ LFCController::launch(int argc, char* argv[]) { ...@@ -77,13 +89,22 @@ LFCController::launch(int argc, char* argv[]) {
} }
// do other work (TBD) // 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 // If we don't have a finish file do the processing
if (access(finish_file_.c_str(), F_OK) == -1) {
std::cerr << "LFC Processing files" << std::endl;
if (protocol_version_ == 4) {
processLeases<Lease4, CSVLeaseFile4, Lease4Storage>();
} else {
processLeases<Lease6, CSVLeaseFile6, Lease6Storage>();
}
}
// We either already had a finish file or just created one, do the // 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 // file cleanup, we don't want to return after the catch as we
// still need to cleanup the pid file // still need to cleanup the pid file
try { try {
std::cerr << "LFC cleaning files" << std::endl;
fileCleanup(); fileCleanup();
} catch (const RunTimeFail& run_ex) { } catch (const RunTimeFail& run_ex) {
std::cerr << run_ex.what() << std::endl; std::cerr << run_ex.what() << std::endl;
...@@ -239,7 +260,7 @@ LFCController::parseArgs(int argc, char* argv[]) { ...@@ -239,7 +260,7 @@ LFCController::parseArgs(int argc, char* argv[]) {
<< "Previous or ex lease file: " << previous_file_ << std::endl << "Previous or ex lease file: " << previous_file_ << std::endl
<< "Copy lease file: " << copy_file_ << std::endl << "Copy lease file: " << copy_file_ << std::endl
<< "Output lease file: " << output_file_ << std::endl << "Output lease file: " << output_file_ << std::endl
<< "Finishn file: " << finish_file_ << std::endl << "Finish file: " << finish_file_ << std::endl
<< "Config file: " << config_file_ << std::endl << "Config file: " << config_file_ << std::endl
<< "PID file: " << pid_file_ << std::endl; << "PID file: " << pid_file_ << std::endl;
} }
...@@ -279,8 +300,35 @@ LFCController::getVersion(const bool extended) const{ ...@@ -279,8 +300,35 @@ LFCController::getVersion(const bool extended) const{
return (version_stream.str()); return (version_stream.str());
} }
template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
void void
LFCController::processLeases() const { LFCController::processLeases() const {
LeaseFileType lfPrev(previous_file_.c_str());
LeaseFileType lfCopy(copy_file_.c_str());
LeaseFileType lfOutput(output_file_.c_str());
StorageType storage;
storage.clear();
// If a previous file exists read the entries into storage
if (lfPrev.exists()) {
LeaseFileLoader::load<LeaseObjectType>(lfPrev, storage,
MAX_LEASE_ERRORS);
}
// Follow that with the copy of the current lease file
if (lfCopy.exists()) {
LeaseFileLoader::load<LeaseObjectType>(lfCopy, storage,
MAX_LEASE_ERRORS);
}
// Write the result out to the output file
LeaseFileLoader::write<LeaseObjectType>(lfOutput, storage);
// Once we've finished the output file move it to the complete file
if (rename(output_file_.c_str(), finish_file_.c_str()) != 0)
isc_throw(RunTimeFail, "Unable to move output (" << output_file_
<< ") to complete (" << finish_file_
<< ") error: " << strerror(errno));
} }
void void
......
...@@ -158,6 +158,7 @@ public: ...@@ -158,6 +158,7 @@ public:
/// @brief Process files. Read in the leases from any previous & copy /// @brief Process files. Read in the leases from any previous & copy
/// files we have and write the results out to the output file. Upon /// files we have and write the results out to the output file. Upon
/// completion of the write move the file to the finish file. /// completion of the write move the file to the finish file.
template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
void processLeases() const; void processLeases() const;
/// @brief Cleanup files. After we have a finish file, either from /// @brief Cleanup files. After we have a finish file, either from
......
...@@ -22,24 +22,25 @@ using namespace std; ...@@ -22,24 +22,25 @@ 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 { class LFCControllerTest : public ::testing::Test {
public: public:
string pstr_;
string cstr_; string pstr_; ///< String for name for pid file
string fstr_; string xstr_; ///< String for name for previous file
string ostr_; string istr_; ///< String for name for copy file
string istr_; string ostr_; ///< String for name for output file
string fstr_; ///< String for name for finish file
string cstr_; ///< String for name for config file
/// @brief Create a file and write the filename into it. /// @brief Create a file and write the filename into it.
void touchFile(const std::string& filename, int); void touchFile(const std::string& filename, int);
/// @brief Create a file and write the given string into it.
void writeFile(const std::string& filename, const std::string& contents) const;
/// @brief Read a string from a file
std::string readFile(const std::string& contents) const;
/// @brief check the file to see if i matches what was written to it. /// @brief check the file to see if i matches what was written to it.
bool checkFile(const std::string& filename, int); bool checkFile(const std::string& filename, int);
...@@ -48,26 +49,13 @@ protected: ...@@ -48,26 +49,13 @@ protected:
/// files before the test /// files before the test
virtual void SetUp() { virtual void SetUp() {
// set up the test files we need // set up the test files we need
std::ostringstream prev_str; string baseDir = TEST_DATA_BUILDDIR;
std::ostringstream copy_str; pstr_ = baseDir + "/" + "lease_file." + "pid"; // pid
std::ostringstream fin_str; xstr_ = baseDir + "/" + "lease_file." + "2"; // previous
std::ostringstream out_str; istr_ = baseDir + "/" + "lease_file." + "1"; // copy
std::ostringstream pid_str; ostr_ = baseDir + "/" + "lease_file." + "output"; // output
fstr_ = baseDir + "/" + "lease_file." + "completed"; // finish
prev_str << TEST_DATA_BUILDDIR << "/" << PREVIOUS; cstr_ = baseDir + "/" + "config_file"; // config
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 // and remove any outstanding test files
removeTestFile(); removeTestFile();
...@@ -83,10 +71,10 @@ private: ...@@ -83,10 +71,10 @@ private:
/// @brief Removes any remaining test files /// @brief Removes any remaining test files
void removeTestFile() const { void removeTestFile() const {
remove(pstr_.c_str()); remove(pstr_.c_str());
remove(cstr_.c_str()); remove(xstr_.c_str());
remove(fstr_.c_str());
remove(ostr_.c_str());
remove(istr_.c_str()); remove(istr_.c_str());
remove(ostr_.c_str());
remove(fstr_.c_str());
} }
}; };
...@@ -98,6 +86,29 @@ LFCControllerTest::touchFile(const std::string& filename, int i) { ...@@ -98,6 +86,29 @@ LFCControllerTest::touchFile(const std::string& filename, int i) {
fs.open(filename, std::ofstream::out); fs.open(filename, std::ofstream::out);
fs << i << std::endl; fs << i << std::endl;
fs.close(); fs.close();
}
std::string
LFCControllerTest::readFile(const std::string& filename) const {
std::ifstream fs;
fs.open(filename, std::ifstream::in);
std::string contents((std::istreambuf_iterator<char>(fs)),
std::istreambuf_iterator<char>());
fs.close();
return (contents);
}
void
LFCControllerTest::writeFile(const std::string& filename,
const std::string& contents) const {
std::ofstream fs(filename, std::ofstream::out);
if (fs.is_open()) {
fs << contents;
fs.close();
}
} }
bool bool
...@@ -261,24 +272,26 @@ TEST_F(LFCControllerTest, fileCleanup) { ...@@ -261,24 +272,26 @@ TEST_F(LFCControllerTest, fileCleanup) {
LFCController lfc_controller, lfc_controller_launch; LFCController lfc_controller, lfc_controller_launch;
// We can use the same arguments and controller for all of the tests // We can use the same arguments and controller for all of the tests
// as the files get redone for each subtest. // as the files get redone for each subtest. We leave "-d" in the arg
// list but don't pass it as we use 14 as the argument count. This
// makes it easy to turn it on by simply increasing argc below to 15
char* argv[] = { const_cast<char*>("progName"), char* argv[] = { const_cast<char*>("progName"),
const_cast<char*>("-4"), const_cast<char*>("-4"),
const_cast<char*>("-x"), const_cast<char*>("-x"),
const_cast<char*>(pstr_.c_str()), const_cast<char*>(xstr_.c_str()),
const_cast<char*>("-i"), const_cast<char*>("-i"),
const_cast<char*>(cstr_.c_str()), const_cast<char*>(istr_.c_str()),
const_cast<char*>("-o"), const_cast<char*>("-o"),
const_cast<char*>(ostr_.c_str()), const_cast<char*>(ostr_.c_str()),
const_cast<char*>("-c"), const_cast<char*>("-c"),
const_cast<char*>("config"), const_cast<char*>(cstr_.c_str()),
const_cast<char*>("-f"), const_cast<char*>("-f"),
const_cast<char*>(fstr_.c_str()), const_cast<char*>(fstr_.c_str()),
const_cast<char*>("-p"), const_cast<char*>("-p"),
const_cast<char*>(istr_.c_str()), const_cast<char*>(pstr_.c_str()),
const_cast<char*>("-d") const_cast<char*>("-d")
}; };
int argc = 15; int argc = 14;
lfc_controller.parseArgs(argc, argv); lfc_controller.parseArgs(argc, argv);
// Test 1: Start with no files - we expect an execption as there // Test 1: Start with no files - we expect an execption as there
...@@ -288,41 +301,41 @@ TEST_F(LFCControllerTest, fileCleanup) { ...@@ -288,41 +301,41 @@ TEST_F(LFCControllerTest, fileCleanup) {
// Test 2: Create a file for each of previous, copy and finish. We should // 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. // delete the previous and copy files then move finish to previous.
touchFile(pstr_.c_str(), 1); touchFile(xstr_.c_str(), 1);
touchFile(cstr_.c_str(), 2); touchFile(istr_.c_str(), 2);
touchFile(fstr_.c_str(), 3); touchFile(fstr_.c_str(), 3);
lfc_controller.fileCleanup(); lfc_controller.fileCleanup();
// verify finish is now previous and copy and finish are gone // verify finish is now previous and copy and finish are gone
EXPECT_TRUE(checkFile(pstr_.c_str(), 3)); EXPECT_TRUE(checkFile(xstr_.c_str(), 3));
EXPECT_TRUE((remove(cstr_.c_str()) != 0) && (errno == ENOENT)); EXPECT_TRUE((remove(istr_.c_str()) != 0) && (errno == ENOENT));
EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT)); EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
remove(pstr_.c_str()); remove(pstr_.c_str());
// Test 3: Create a file for previous and finish but not copy. // Test 3: Create a file for previous and finish but not copy.
touchFile(pstr_.c_str(), 4); touchFile(xstr_.c_str(), 4);
touchFile(fstr_.c_str(), 6); touchFile(fstr_.c_str(), 6);
lfc_controller.fileCleanup(); lfc_controller.fileCleanup();
// verify finish is now previous and copy and finish are gone // verify finish is now previous and copy and finish are gone
EXPECT_TRUE(checkFile(pstr_.c_str(), 6)); EXPECT_TRUE(checkFile(xstr_.c_str(), 6));
EXPECT_TRUE((remove(cstr_.c_str()) != 0) && (errno == ENOENT)); EXPECT_TRUE((remove(istr_.c_str()) != 0) && (errno == ENOENT));
EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT)); EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
remove(pstr_.c_str()); remove(pstr_.c_str());
// Test 4: Create a file for copy and finish but not previous. // Test 4: Create a file for copy and finish but not previous.
touchFile(cstr_.c_str(), 8); touchFile(istr_.c_str(), 8);
touchFile(fstr_.c_str(), 9); touchFile(fstr_.c_str(), 9);
lfc_controller.fileCleanup(); lfc_controller.fileCleanup();
// verify finish is now previous and copy and finish are gone // verify finish is now previous and copy and finish are gone
EXPECT_TRUE(checkFile(pstr_.c_str(), 9)); EXPECT_TRUE(checkFile(xstr_.c_str(), 9));
EXPECT_TRUE((remove(cstr_.c_str()) != 0) && (errno == ENOENT)); EXPECT_TRUE((remove(istr_.c_str()) != 0) && (errno == ENOENT));
EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT)); EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
remove(pstr_.c_str()); remove(pstr_.c_str());
...@@ -330,19 +343,174 @@ TEST_F(LFCControllerTest, fileCleanup) { ...@@ -330,19 +343,174 @@ TEST_F(LFCControllerTest, fileCleanup) {
// Test 5: rerun test 2 but using launch instead of cleanup // Test 5: rerun test 2 but using launch instead of cleanup
// as we already have a finish file we shouldn't do any extra // as we already have a finish file we shouldn't do any extra
// processing // processing
touchFile(pstr_.c_str(), 10); touchFile(xstr_.c_str(), 10);
touchFile(cstr_.c_str(), 11); touchFile(istr_.c_str(), 11);
touchFile(fstr_.c_str(), 12); touchFile(fstr_.c_str(), 12);
lfc_controller_launch.launch(argc, argv); lfc_controller_launch.launch(argc, argv);
// verify finish is now previous and copy and finish are gone // 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. // as we ran launch we also check to see if the pid is gone.
EXPECT_TRUE(checkFile(pstr_.c_str(), 12)); EXPECT_TRUE(checkFile(xstr_.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)); EXPECT_TRUE((remove(istr_.c_str()) != 0) && (errno == ENOENT));
EXPECT_TRUE((remove(fstr_.c_str()) != 0) && (errno == ENOENT));
EXPECT_TRUE((remove(pstr_.c_str()) != 0) && (errno == ENOENT));
remove(pstr_.c_str()); remove(pstr_.c_str());
} }
/// @brief Verify that we properly combine and clean up files
///
/// This is mostly a retest as we already test that the loader and
/// writer functions work in their own tests but we combine it all
/// here. This is the v4 version
TEST_F(LFCControllerTest, programLaunch4) {
LFCController lfc_controller;
// 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*>(xstr_.c_str()),
const_cast<char*>("-i"),
const_cast<char*>(istr_.c_str()),
const_cast<char*>("-o"),
const_cast<char*>(ostr_.c_str()),
const_cast<char*>("-c"),
const_cast<char*>(cstr_.c_str()),
const_cast<char*>("-f"),
const_cast<char*>(fstr_.c_str()),
const_cast<char*>("-p"),
const_cast<char*>(pstr_.c_str()),
const_cast<char*>("-d")
};
int argc = 14;
lfc_controller.parseArgs(argc, argv);
// Create the test previous file
writeFile(xstr_.c_str(),
"address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
"fqdn_fwd,fqdn_rev,hostname\n"
"192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,"
"host.example.com\n"
"192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,"
"0,0,\n"
"192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n"
"192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,135,7,"
"0,0,\n"
"192.0.2.1,06:07:08:09:0a:bc,,200,500,8,1,1,"
"host.example.com\n"
"192.0.2.5,06:07:08:09:0a:bc,,200,200,8,1,1,"
"host.example.com\n");
// Create the test copy file
writeFile(istr_.c_str(),
"address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
"fqdn_fwd,fqdn_rev,hostname\n"
"192.0.2.1,06:07:08:09:0a:bc,,200,800,8,1,1,"
"host.example.com\n"
"192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,150,7,"
"0,0,\n"
"192.0.2.5,06:07:08:09:0a:bc,,200,0,8,1,1,"
"host.example.com\n");
// Run the cleanup
lfc_controller.launch(argc, argv);
// Compare the results
EXPECT_EQ(readFile(xstr_.c_str()),
"address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
"fqdn_fwd,fqdn_rev,hostname\n"
"192.0.2.1,06:07:08:09:0a:bc,,200,800,8,1,1,"
"host.example.com\n"
"192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,150,7,"
"0,0,\n");
}
/// @brief Verify that we properly combine and clean up files
///
/// This is mostly a retest as we already test that the loader and
/// writer functions work in their own tests but we combine it all
/// here. This is the v6 version
TEST_F(LFCControllerTest, programLaunch6) {
LFCController lfc_controller;
// 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*>("-6"),
const_cast<char*>("-x"),
const_cast<char*>(xstr_.c_str()),
const_cast<char*>("-i"),
const_cast<char*>(istr_.c_str()),
const_cast<char*>("-o"),
const_cast<char*>(ostr_.c_str()),
const_cast<char*>("-c"),
const_cast<char*>(cstr_.c_str()),
const_cast<char*>("-f"),
const_cast<char*>(fstr_.c_str()),
const_cast<char*>("-p"),
const_cast<char*>(pstr_.c_str()),
const_cast<char*>("-d")
};
int argc = 14;
lfc_controller.parseArgs(argc, argv);
// Create the test previous file
writeFile(xstr_.c_str(),
"address,duid,valid_lifetime,expire,subnet_id,"
"pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
"fqdn_rev,hostname,hwaddr\n"
"2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
"200,200,8,100,0,7,0,1,1,host.example.com,\n"
"2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,\n"
"2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
"0,8,0,0,0,,\n"
"3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,100,200,8,0,2,"
"16,64,0,0,,\n"
"2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,800,6,150,"
"0,8,0,0,0,,\n"
"2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
"200,400,8,100,0,7,0,1,1,host.example.com,\n"
);
// Create the test copy file
writeFile(istr_.c_str(),
"address,duid,valid_lifetime,expire,subnet_id,"
"pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
"fqdn_rev,hostname,hwaddr\n"
"2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,1000,6,150,"
"0,8,0,0,0,,\n"
"2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
"0,200,8,100,0,7,0,1,1,host.example.com,\n"
"2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
"200,600,8,100,0,7,0,1,1,host.example.com,\n"
"3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,100,400,8,0,2,"
"16,64,0,0,,\n"
);
// Run the cleanup
lfc_controller.launch(argc, argv);
// Compare the results
EXPECT_EQ(readFile(xstr_.c_str()),
"address,duid,valid_lifetime,expire,subnet_id,"
"pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
"fqdn_rev,hostname,hwaddr\n"
"2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
"200,600,8,100,0,7,0,1,1,host.example.com,\n"
"2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,1000,6,150,"
"0,8,0,0,0,,\n"
"3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,100,400,8,0,2,"
"16,64,0,0,,\n"
);
}
} // end of anonymous namespace } // end of anonymous namespace
...@@ -160,9 +160,47 @@ public: ...@@ -160,9 +160,47 @@ public:
lease_file.close(); lease_file.close();
} }
} }
/// @brief Write leaes from the storage into a lease file
///
/// This method iterates over the @c Lease4 or @c Lease6 object in the
/// storage specified in the arguments and writes them to the file
/// specified in the arguments.
///
/// This method writes all entries in the storege to the file, it does
/// not perform any checks for expriation or duplication.
///
/// @param lease_file A reference to the @c CSVLeaseFile4 or
/// @c CSVLeaseFile6 object representing the lease file. The file
/// doesn't need to be open because the method re-opens the file.
/// @param storage A reference to the container from which leases
/// should be written..
/// @tparam LeasePtrType A @c Lease4 or @c Lease6.
/// @tparam LeaseFileType A @c CSVLeaseFile4 or @c CSVLeaseFile6.
/// @tparam StorageType A @c Lease4Storage or @c Lease6Storage.
///
template<typename LeaseObjectType, typename LeaseFileType,
typename StorageType>
static void write(LeaseFileType& lease_file, const StorageType& storage) {
// Reopen the file, as we don't know whether the file is open
// and we also don't know its current state.
lease_file.close();
lease_file.open();
// Iterate over the storage area writing out the leases
for (typename StorageType::const_iterator lease = storage.begin();
lease != storage.end();
++lease) {
lease_file.append(**lease);
}
// Close the file
lease_file.close();
}
}; };
} } // namesapce dhcp
} } // namespace isc
#endif // LEASE_FILE_LOADER_H #endif // LEASE_FILE_LOADER_H
...@@ -79,6 +79,37 @@ public: ...@@ -79,6 +79,37 @@ public:
return (LeasePtrType()); return (LeasePtrType());
} }
/// @brief Tests the write function.
///
/// This method writes the leases from the storage container to the lease file
/// then compares the output to the string provided in the aguments to verify
/// the write was correct. The order of the leases in the output will dpend
/// on the order in which the container provides the leases.
///
/// @param storage A reference to the container to be written to the file
/// @param compStr The string to compare to what was read from the file
///
/// @tparam LeaseStorage Type of the container: @c Lease4Container
/// @c Lease6Container.
///
template<typename LeaseObjectType, typename LeaseFileType,