lease_file_loader.h 9.64 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
#include <util/versioned_csv_file.h>
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

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

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

91
92
93
94
95
        // 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();

96
97
98
99
100
101
102
103
104
105
        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.
106
107
108
109
110
111
112
113
                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();
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
                    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;

            }
        }
156

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

167
168
169
        if (close_file_on_exit) {
            lease_file.close();
        }
170
    }
171

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

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

219
        // Close the file
220
221
        lease_file.close();
    }
222
223
};

224
225
} // namesapce dhcp
} // namespace isc
226
227

#endif // LEASE_FILE_LOADER_H