/// @brief Maximum number of errors to read the leases from the lease file.
const uint32_t MAX_LEASE_ERRORS = 100;
+/// @brief A name of the environmental variable specifying the kea-lfc
+/// program location.
+///
+/// This variable can be set by tests to point to the location of the
+/// kea-lfc program within a build directory. If this variable is not
+/// set, the backend will use the location of the kea-lfc in the
+/// Kea installation directory.
const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE";
} // end of anonymous namespace
// 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.
asiolink::IntervalTimer::Callback cb =
}
template<typename LeaseFileType>
-void Memfile_LeaseMgr::
-leaseFileCleanup(boost::shared_ptr<LeaseFileType>& lease_file) {
+void Memfile_LeaseMgr::leaseFileCleanup(boost::shared_ptr<LeaseFileType>& lease_file) {
+ bool do_lfc = true;
// Check if the copy of the lease file exists already. If it does, it
// is an indication that another LFC instance may be in progress, in
// which case we don't want to rotate the current lease file to avoid
if (!lease_file_copy.exists()) {
// Close the current file so as we can move it to the copy file.
lease_file->close();
- // Move the current file to the copy file.
- rename(lease_file->getFilename().c_str(),
- lease_file_copy.getFilename().c_str());
- // Now that we moved the current file, we need to create a new one.
- lease_file.reset(new LeaseFileType(lease_file->getFilename()));
- // Leave the new file open for writing.
- lease_file->open();
+ // Move the current file to the copy file. Remember the result
+ // because we don't want to run LFC if the rename failed.
+ do_lfc = (rename(lease_file->getFilename().c_str(),
+ lease_file_copy.getFilename().c_str()) == 0);
+
+ // Regardless if we successfully moved the current file or not,
+ // we need to re-open the current file for the server to write
+ // new lease updates. If the file has been successfully moved,
+ // this will result in creation of the new file. Otherwise,
+ // an existing file will be opened.
+ try {
+ lease_file->open(true);
+
+ } catch (const CSVFileError& ex) {
+ // If we're unable to open the lease file this is a serious
+ // error because the server will not be able to persist
+ // leases.
+ /// @todo We need to better address this error. It should
+ /// trigger an alarm (once we have a monitoring system in
+ /// place) so as an administrator can correct it. In
+ /// practice it should be very rare that this happens and
+ /// is most likely related to a human error, e.g. changing
+ /// file permissions.
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_REOPEN_FAIL)
+ .arg(lease_file->getFilename());
+ // Reset the pointer to the file so as the backend doesn't
+ // try to write leases to disk.
+ lease_file.reset();
+ do_lfc = false;
+ }
}
// Once we have rotated files as needed, start the new kea-lfc process
// to perform a cleanup.
- lfc_process_->spawn();
+ 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);
+ }
+
+ } else {
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_ROTATION_FAIL)
+ .arg(lease_file->getFilename())
+ .arg(lease_file_copy.getFilename());
+ }
}
template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
-void Memfile_LeaseMgr::
-loadLeasesFromFiles(const std::string& filename,
- boost::shared_ptr<LeaseFileType>& lease_file,
- StorageType& storage) {
+void Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename,
+ boost::shared_ptr<LeaseFileType>& lease_file,
+ StorageType& storage) {
storage.clear();
// Load the leasefile.completed, if exists.
#include <boost/bind.hpp>
+#include <cstdlib>
#include <iostream>
#include <fstream>
#include <queue>
EXPECT_EQ(0, lease_mgr->getLFCCount());
}
-// This test that the callback function performing a Lease File Cleanup
-// works as expected.
-TEST_F(MemfileLeaseMgrTest, leaseFileCleanup) {
+// This test that the callback function executing the cleanup of the
+// DHCPv4 lease file works as expected.
+TEST_F(MemfileLeaseMgrTest, leaseFileCleanup4) {
+ // This string contains the lease file header, which matches
+ // the contents of the new file in which no leases have been
+ // stored.
std::string new_file_contents =
"address,hwaddr,client_id,valid_lifetime,expire,"
"subnet_id,fqdn_fwd,fqdn_rev,hostname\n";
+ // This string contains the contents of the lease file with exactly
+ // one lease, but two entries. One of the entries should be removed
+ // as a result of lease file cleanup.
std::string current_file_contents = new_file_contents +
- "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n";
+ "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n"
+ "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,,\n";
LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv"));
current_file.writeFile(current_file_contents);
+ std::string previous_file_contents = new_file_contents +
+ "192.0.2.3,03:03:03:03:03:03,,200,200,8,1,1,,\n"
+ "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,,\n";
+ LeaseFileIO previous_file(getLeaseFilePath("leasefile4_0.csv.2"));
+ previous_file.writeFile(previous_file_contents);
+
+ // Create the backend.
LeaseMgr::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "4";
pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
pmap["lfc-interval"] = "1";
+ boost::scoped_ptr<NakedMemfileLeaseMgr> lease_mgr(new NakedMemfileLeaseMgr(pmap));
- boost::scoped_ptr<NakedMemfileLeaseMgr>
- lease_mgr(new NakedMemfileLeaseMgr(pmap));
-
+ // Try to run the lease file cleanup.
ASSERT_NO_THROW(lease_mgr->lfcCallback());
+ // The new lease file should have been created and it should contain
+ // no leases.
+ ASSERT_TRUE(current_file.exists());
+ EXPECT_EQ(new_file_contents, current_file.readFile());
+
+ // Wait for the LFC process to complete.
+ ASSERT_TRUE(waitForProcess(*lease_mgr->lfc_process_, 2));
+
+ // And make sure it has returned an exit status of 0.
+ EXPECT_EQ(0, lease_mgr->lfc_process_->getExitStatus());
+
+ /// @todo Replace the following with the checks that the LFC has
+ /// completed successfully, i.e. the leasefile4_0.csv.2 exists
+ /// and it holds the cleaned up lease information.
+
+ // Until the kea-lfc is implemented and performs the cleanup, we can
+ // only check that the backend has moved the lease file to a lease
+ // file with suffix ".1".
LeaseFileIO input_file(getLeaseFilePath("leasefile4_0.csv.1"), false);
ASSERT_TRUE(input_file.exists());
+ // And this file should contain the contents of the original file.
EXPECT_EQ(current_file_contents, input_file.readFile());
+}
+
+// This test that the callback function executing the cleanup of the
+// DHCPv6 lease file works as expected.
+TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) {
+ // This string contains the lease file header, which matches
+ // the contents of the new file in which no leases have been
+ // stored.
+ std::string new_file_contents =
+ "address,duid,valid_lifetime,expire,subnet_id,"
+ "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
+ "fqdn_rev,hostname,hwaddr\n";
+
+ // This string contains the contents of the lease file with exactly
+ // one lease, but two entries. One of the entries should be removed
+ // as a result of lease file cleanup.
+ std::string current_file_contents = new_file_contents +
+ "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,,\n"
+ "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
+ "8,100,0,7,0,1,1,,\n";
+ LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv"));
+ current_file.writeFile(current_file_contents);
+ std::string previous_file_contents = new_file_contents +
+ "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,200,"
+ "8,100,0,7,0,1,1,,\n"
+ "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
+ "8,100,0,7,0,1,1,,\n";
+ LeaseFileIO previous_file(getLeaseFilePath("leasefile6_0.csv.2"));
+ previous_file.writeFile(previous_file_contents);
+
+ // Create the backend.
+ LeaseMgr::ParameterMap pmap;
+ pmap["type"] = "memfile";
+ pmap["universe"] = "6";
+ pmap["name"] = getLeaseFilePath("leasefile6_0.csv");
+ pmap["lfc-interval"] = "1";
+ boost::scoped_ptr<NakedMemfileLeaseMgr> lease_mgr(new NakedMemfileLeaseMgr(pmap));
+
+ // Try to run the lease file cleanup.
+ ASSERT_NO_THROW(lease_mgr->lfcCallback());
+
+ // The new lease file should have been created and it should contain
+ // no leases.
ASSERT_TRUE(current_file.exists());
EXPECT_EQ(new_file_contents, current_file.readFile());
+ // Wait for the LFC process to complete.
ASSERT_TRUE(waitForProcess(*lease_mgr->lfc_process_, 2));
+ // And make sure it has returned an exit status of 0.
EXPECT_EQ(0, lease_mgr->lfc_process_->getExitStatus());
+
+ /// @todo Replace the following with the checks that the LFC has
+ /// completed successfully, i.e. the leasefile6_0.csv.2 exists
+ /// and it holds the cleaned up lease information.
+
+ // Until the kea-lfc is implemented and performs the cleanup, we can
+ // only check that the backend has moved the lease file to a lease
+ // file with suffix ".1".
+ LeaseFileIO input_file(getLeaseFilePath("leasefile6_0.csv.1"), false);
+ ASSERT_TRUE(input_file.exists());
+ // And this file should contain the contents of the original file.
+ EXPECT_EQ(current_file_contents, input_file.readFile());
+}
+
+// This test verifies that EXIT_FAILURE status code is returned when
+// the LFC process fails to start.
+TEST_F(MemfileLeaseMgrTest, leaseFileCleanupStartFail) {
+ // This string contains the lease file header, which matches
+ // the contents of the new file in which no leases have been
+ // stored.
+ std::string new_file_contents =
+ "address,hwaddr,client_id,valid_lifetime,expire,"
+ "subnet_id,fqdn_fwd,fqdn_rev,hostname\n";
+
+ // Create the lease file to be used by the backend.
+ std::string current_file_contents = new_file_contents +
+ "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n";
+ LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv"));
+ current_file.writeFile(current_file_contents);
+
+ // Specify invalid path to the kea-lfc executable. This should result
+ // in failure status code returned by the child process.
+ setenv("KEA_LFC_EXECUTABLE", "foobar", 1);
+
+ // Create the backend.
+ LeaseMgr::ParameterMap pmap;
+ pmap["type"] = "memfile";
+ pmap["universe"] = "4";
+ pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
+ pmap["lfc-interval"] = "1";
+ boost::scoped_ptr<NakedMemfileLeaseMgr> lease_mgr(new NakedMemfileLeaseMgr(pmap));
+
+ // Try to run the lease file cleanup.
+ ASSERT_NO_THROW(lease_mgr->lfcCallback());
+
+ // Wait for the LFC process to complete.
+ ASSERT_TRUE(waitForProcess(*lease_mgr->lfc_process_, 2));
+
+ // And make sure it has returned an error.
+ EXPECT_EQ(EXIT_FAILURE, lease_mgr->lfc_process_->getExitStatus());
}
// Test that the backend returns a correct value of the interval