summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/lease_file_loader.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/lease_file_loader.h')
-rw-r--r--src/lib/dhcpsrv/lease_file_loader.h246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/lease_file_loader.h b/src/lib/dhcpsrv/lease_file_loader.h
new file mode 100644
index 0000000..b4f724a
--- /dev/null
+++ b/src/lib/dhcpsrv/lease_file_loader.h
@@ -0,0 +1,246 @@
+// Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef LEASE_FILE_LOADER_H
+#define LEASE_FILE_LOADER_H
+
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/memfile_lease_storage.h>
+#include <util/versioned_csv_file.h>
+#include <dhcpsrv/sanity_checker.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Utility class to manage bulk of leases in the lease files.
+///
+/// This class exposes methods which allow for bulk loading leases from
+/// the lease file and dumping the leases held in memory into the
+/// lease file. There are two major use cases for this class:
+/// - load leases by the DHCP server when the server starts up or
+/// reloads configuration,
+/// - an application performing a lease file cleanup rewrites the whole
+/// lease file to remove the redundant lease entries.
+///
+/// In the former case, this class is used by the @c MemFile_LeaseMgr.
+/// In the latter case, this class is used by the standalone application
+/// which reads the whole lease file into memory (storage) and then
+/// dumps the leases held in the storage to another file.
+///
+/// The methods in this class are templated so as they can be used both
+/// with the @c Lease4Storage and @c Lease6Storage to process the DHCPv4
+/// and DHCPv6 leases respectively.
+///
+class LeaseFileLoader {
+public:
+
+ /// @brief Load leases from the lease file into the specified storage.
+ ///
+ /// This method iterates over the entries in the lease file in the
+ /// CSV format, creates @c Lease4 or @c Lease6 objects and inserts
+ /// them into the storage to which reference is specified as an
+ /// argument. If there are multiple entries for the particular lease
+ /// in the lease file the entries further in the lease file override
+ /// the previous entries.
+ ///
+ /// If the method finds the entry with the valid lifetime of 0 it
+ /// means that the particular lease was released and the method
+ /// removes an existing lease from the container.
+ ///
+ /// @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 to which leases
+ /// should be inserted.
+ /// @param max_errors Maximum number of corrupted leases in the
+ /// lease file. The method will skip corrupted leases but after
+ /// exceeding the specified number of errors it will throw an
+ /// exception. A value of 0 (default) disables the limit check.
+ /// @param close_file_on_exit A boolean flag which indicates if
+ /// the file should be closed after it has been successfully parsed.
+ /// One case when the file is not opened is when the server starts
+ /// up, reads the leases in the file and then leaves the file open
+ /// for writing future lease updates.
+ /// @tparam LeaseObjectType A @c Lease4 or @c Lease6.
+ /// @tparam LeaseFileType A @c CSVLeaseFile4 or @c CSVLeaseFile6.
+ /// @tparam StorageType A @c Lease4Storage or @c Lease6Storage.
+ ///
+ /// @throw isc::util::CSVFileError when the maximum number of errors
+ /// has been exceeded.
+ template<typename LeaseObjectType, typename LeaseFileType,
+ typename StorageType>
+ static void load(LeaseFileType& lease_file, StorageType& storage,
+ const uint32_t max_errors = 0,
+ const bool close_file_on_exit = true) {
+
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LEASE_FILE_LOAD)
+ .arg(lease_file.getFilename());
+
+ // 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();
+
+ // Create lease sanity checker if checking is enabled.
+ boost::scoped_ptr<SanityChecker> lease_checker;
+ if (SanityChecker::leaseCheckingEnabled(false)) {
+ // Since lease file is loaded during the configuration,
+ // we have to use staging config, rather than current
+ // config for this (false = staging).
+ lease_checker.reset(new SanityChecker());
+ }
+
+ boost::shared_ptr<LeaseObjectType> lease;
+ // Track the number of corrupted leases.
+ uint32_t errcnt = 0;
+ while (true) {
+ // Unable to parse the lease.
+ if (!lease_file.next(lease)) {
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LEASE_LOAD_ROW_ERROR)
+ .arg(lease_file.getReads())
+ .arg(lease_file.getReadMsg());
+
+ // A value of 0 indicates that we don't return
+ // until the whole file is parsed, even if errors occur.
+ // Otherwise, check if we have exceeded the maximum number
+ // of errors and throw an exception if we have.
+ if (max_errors && (++errcnt > max_errors)) {
+ // If we break parsing the CSV file because of too many
+ // errors, it doesn't make sense to keep the file open.
+ // This is because the caller wouldn't know where we
+ // stopped parsing and where the internal file pointer
+ // is. So, there are probably no cases when the caller
+ // would continue to use the open file.
+ lease_file.close();
+ isc_throw(util::CSVFileError, "exceeded maximum number of"
+ " failures " << max_errors << " to read a lease"
+ " from the lease file "
+ << lease_file.getFilename());
+ }
+ // Skip the corrupted lease.
+ continue;
+ }
+
+ // Lease was found and we successfully parsed it.
+ if (lease) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
+ DHCPSRV_MEMFILE_LEASE_LOAD)
+ .arg(lease->toText());
+
+ if (lease_checker) {
+ // If the lease is insane the checker will reset the lease pointer.
+ // As lease file is loaded during the configuration, we have
+ // to use staging config, rather than current config for this
+ // (false = staging).
+ lease_checker->checkLease(lease, false);
+ if (!lease) {
+ continue;
+ }
+ }
+
+ // Check if this lease exists.
+ typename StorageType::iterator lease_it =
+ storage.find(lease->addr_);
+ // The lease doesn't exist yet. Insert the lease if
+ // it has a positive valid lifetime.
+ if (lease_it == storage.end()) {
+ if (lease->valid_lft_ > 0) {
+ storage.insert(lease);
+ }
+ } else {
+ // The lease exists. If the new entry has a valid
+ // lifetime of 0 it is an indication to remove the
+ // existing entry. Otherwise, we update the lease.
+ if (lease->valid_lft_ == 0) {
+ storage.erase(lease_it);
+
+ } else {
+ // Use replace to re-index leases on update.
+ storage.replace(lease_it, lease);
+ }
+ }
+
+ } else {
+ // Being here means that we hit the end of file.
+ break;
+
+ }
+ }
+
+ if (lease_file.needsConversion()) {
+ LOG_WARN(dhcpsrv_logger,
+ (lease_file.getInputSchemaState()
+ == util::VersionedCSVFile::NEEDS_UPGRADE
+ ? DHCPSRV_MEMFILE_NEEDS_UPGRADING
+ : DHCPSRV_MEMFILE_NEEDS_DOWNGRADING))
+ .arg(lease_file.getFilename())
+ .arg(lease_file.getSchemaVersion());
+ }
+
+ if (close_file_on_exit) {
+ lease_file.close();
+ }
+ }
+
+ /// @brief Write leases 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 storage to the file, it does
+ /// not perform any checks for expiration or duplication.
+ ///
+ /// The order in which the entries will be written to the file depends
+ /// on the first index in the multi-index container. Currently that
+ /// is the v4 or v6 IP address and they are written from lowest to highest.
+ ///
+ /// Before writing the method will close the file if it is open
+ /// and reopen it for writing. After completion it will close
+ /// the file.
+ ///
+ /// @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 LeaseObjectType 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) {
+ try {
+ lease_file.append(**lease);
+ } catch (const isc::Exception&) {
+ // Close the file
+ lease_file.close();
+ throw;
+ }
+ }
+
+ // Close the file
+ lease_file.close();
+ }
+};
+
+} // namespace dhcp
+} // namespace isc
+
+#endif // LEASE_FILE_LOADER_H