lease_file_loader.h 9.41 KB
Newer Older
1
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
2
//
3
4
5
// 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/.
6
7
8
9
10

#ifndef LEASE_FILE_LOADER_H
#define LEASE_FILE_LOADER_H

#include <dhcpsrv/dhcpsrv_log.h>
11
#include <dhcpsrv/memfile_lease_storage.h>
12
#include <util/versioned_csv_file.h>
13
14
15
16
17
18
19
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

#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
55
56
    /// @c CSVLeaseFile6 object representing the lease file. The file
    /// doesn't need to be open because the method re-opens the file.
57
58
59
60
61
62
    /// @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.
63
64
65
66
67
    /// @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.
68
69
70
71
72
73
74
75
76
    /// @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,
77
78
                     const uint32_t max_errors = 0xFFFFFFFF,
                     const bool close_file_on_exit = true) {
79
80
81
82

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

83
84
85
86
87
        // 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();

88
89
90
91
92
93
        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)) {
94
95
96
97
                LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LEASE_LOAD_ROW_ERROR)
                            .arg(lease_file.getReads())
                            .arg(lease_file.getReadMsg());

98
99
100
101
                // 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.
102
103
104
105
106
107
108
109
                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();
110
111
112
113
114
115
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
                    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 {
142
143
                        // Use replace to re-index leases on update.
                        storage.replace(lease_it, lease);
144
145
146
147
148
149
150
151
152
                    }
                }

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

            }
        }
153

154
155
156
157
158
159
        if (lease_file.needsConversion()) {
            LOG_WARN(dhcpsrv_logger,
                     (lease_file.getInputSchemaState()
                      == util::VersionedCSVFile::NEEDS_UPGRADE
                      ?  DHCPSRV_MEMFILE_NEEDS_UPGRADING
                      : DHCPSRV_MEMFILE_NEEDS_DOWNGRADING))
160
                     .arg(lease_file.getFilename())
161
                     .arg(lease_file.getSchemaVersion());
162
163
        }

164
165
166
        if (close_file_on_exit) {
            lease_file.close();
        }
167
    }
168

169
    /// @brief Write leases from the storage into a lease file
170
171
172
173
    ///
    /// 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.
174
    ///
175
176
    /// This method writes all entries in the storage to the file, it does
    /// not perform any checks for expiration or duplication.
177
    ///
178
179
180
181
182
183
184
185
    /// 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.
    ///
186
187
188
189
    /// @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
190
    /// should be written.
191
    ///
192
    /// @tparam LeaseObjectType A @c Lease4 or @c Lease6.
193
194
195
196
197
198
199
200
201
202
    /// @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();

203
        // Iterate over the storage area writing out the leases
204
205
206
        for (typename StorageType::const_iterator lease = storage.begin();
             lease != storage.end();
             ++lease) {
207
208
            try {
                lease_file.append(**lease);
209
            } catch (const isc::Exception&) {
Francis Dupont's avatar
Francis Dupont committed
210
211
212
                // Close the file
                lease_file.close();
                throw;
213
            }
214
215
        }

216
        // Close the file
217
218
        lease_file.close();
    }
219
220
};

221
222
} // namesapce dhcp
} // namespace isc
223
224

#endif // LEASE_FILE_LOADER_H