lease_file_loader.h 8.74 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#ifndef LEASE_FILE_LOADER_H
#define LEASE_FILE_LOADER_H

#include <dhcpsrv/dhcpsrv_log.h>
19
#include <dhcpsrv/memfile_lease_storage.h>
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
#include <util/csv_file.h>

#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.
///
/// @todo Add a method which dumps all leases from the storage to a
/// specified lease file.
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
65 66
    /// @c CSVLeaseFile6 object representing the lease file. The file
    /// doesn't need to be open because the method re-opens the file.
67 68 69 70 71 72
    /// @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.
73 74 75 76 77
    /// @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.
78 79 80 81 82 83 84 85 86
    /// @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,
87 88
                     const uint32_t max_errors = 0xFFFFFFFF,
                     const bool close_file_on_exit = true) {
89 90 91 92

        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LEASE_FILE_LOAD)
            .arg(lease_file.getFilename());

93 94 95 96 97
        // 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();

98 99 100 101 102 103 104 105 106 107
        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)) {
                // A value of 0xFFFFFFFF 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.
108 109 110 111 112 113 114 115
                if (++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();
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
                    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());

                // 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 {
                        **lease_it = *lease;
                    }
                }

            } else {
                // Being here means that we hit the end of file.
                break;

            }
        }
158 159 160 161

        if (close_file_on_exit) {
            lease_file.close();
        }
162
    }
163

164
    /// @brief Write leases from the storage into a lease file
165 166 167 168 169
    ///
    /// 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.
    /// 
170 171
    /// This method writes all entries in the storage to the file, it does
    /// not perform any checks for expiration or duplication.
172 173 174 175 176
    ///
    /// @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
177
    /// should be written.
178 179 180 181 182 183 184 185 186 187 188 189 190
    /// @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();

191
        // Iterate over the storage area writing out the leases
192 193 194 195 196 197
        for (typename StorageType::const_iterator lease = storage.begin();
             lease != storage.end();
             ++lease) {
            lease_file.append(**lease);
        }

198
        // Close the file
199 200
        lease_file.close();
    }
201 202
};

203 204
} // namesapce dhcp
} // namespace isc
205 206

#endif // LEASE_FILE_LOADER_H