memfile_lease_mgr.cc 25.4 KB
Newer Older
1
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
//
// 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.

15
#include <dhcpsrv/cfgmgr.h>
16
#include <dhcpsrv/dhcpsrv_log.h>
17
#include <dhcpsrv/lease_file_loader.h>
18
#include <dhcpsrv/memfile_lease_mgr.h>
19
#include <exceptions/exceptions.h>
20
#include <util/pid_file.h>
21
#include <util/process_spawn.h>
22
#include <cstdio>
23
#include <iostream>
24
#include <sstream>
25

26
27
28
29
30
namespace {

/// @brief Maximum number of errors to read the leases from the lease file.
const uint32_t MAX_LEASE_ERRORS = 100;

31
32
33
34
35
36
37
/// @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.
38
39
const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE";

40
41
} // end of anonymous namespace

42
using namespace isc;
43
using namespace isc::dhcp;
44
using namespace isc::util;
45

46
Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
47
48
    : LeaseMgr(parameters), lfc_timer_(*getIOService()),
      lfc_process_() {
49
50
51
52
53
    // Check the universe and use v4 file or v6 file.
    std::string universe = getParameter("universe");
    if (universe == "4") {
        std::string file4 = initLeaseFilePath(V4);
        if (!file4.empty()) {
54
55
            loadLeasesFromFiles<Lease4, CSVLeaseFile4>(file4, lease_file4_,
                                                       storage4_);
56
57
58
59
        }
    } else {
        std::string file6 = initLeaseFilePath(V6);
        if (!file6.empty()) {
60
61
            loadLeasesFromFiles<Lease6, CSVLeaseFile6>(file6, lease_file6_,
                                                       storage6_);
62
        }
63
    }
64
65
66
67
68

    // If lease persistence have been disabled for both v4 and v6,
    // issue a warning. It is ok not to write leases to disk when
    // doing testing, but it should not be done in normal server
    // operation.
69
   if (!persistLeases(V4) && !persistLeases(V6)) {
70
        LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE);
71
72

    } else  {
73
        lfcSetup();
74
    }
75
76
77
}

Memfile_LeaseMgr::~Memfile_LeaseMgr() {
78
79
80
81
82
83
84
85
    if (lease_file4_) {
        lease_file4_->close();
        lease_file4_.reset();
    }
    if (lease_file6_) {
        lease_file6_->close();
        lease_file6_.reset();
    }
86
87
}

88
89
bool
Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
90
91
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText());
92

93
94
95
96
    if (getLease4(lease->addr_)) {
        // there is a lease with specified address already
        return (false);
    }
97
98
99
100

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
101
    if (persistLeases(V4)) {
102
103
104
        lease_file4_->append(*lease);
    }

105
106
    storage4_.insert(lease);
    return (true);
107
108
}

109
110
bool
Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
111
112
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText());
113

114
    if (getLease6(lease->type_, lease->addr_)) {
115
116
117
        // there is a lease with specified address already
        return (false);
    }
118
119
120
121

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
122
    if (persistLeases(V6)) {
123
124
125
        lease_file6_->append(*lease);
    }

126
127
128
129
    storage6_.insert(lease);
    return (true);
}

130
131
Lease4Ptr
Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
132
133
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
134

135
136
137
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
    const SearchIndex& idx = storage4_.get<0>();
    Lease4Storage::iterator l = idx.find(addr);
138
139
140
    if (l == storage4_.end()) {
        return (Lease4Ptr());
    } else {
141
        return (Lease4Ptr(new Lease4(**l)));
142
    }
143
144
}

145
146
Lease4Collection
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
147
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
148
              DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
149
150
151
152
153
154
155
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
    Lease4Collection collection;
    const SearchIndex& idx = storage4_.get<0>();
    for(SearchIndex::const_iterator lease = idx.begin();
        lease != idx.end(); ++lease) {

        // Every Lease4 has a hardware address, so we can compare it
156
        if ( (*(*lease)->hwaddr_) == hwaddr) {
157
            collection.push_back((*lease));
158
159
        }
    }
160

161
    return (collection);
162
163
}

164
165
Lease4Ptr
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
166
167
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
168
169
        .arg(hwaddr.toText());

170
171
172
    // We are going to use index #1 of the multi index container.
    // We define SearchIndex locally in this function because
    // currently only this function uses this index.
173
    typedef Lease4Storage::nth_index<1>::type SearchIndex;
174
    // Get the index.
175
    const SearchIndex& idx = storage4_.get<1>();
176
177
178
179
    // Try to find the lease using HWAddr and subnet id.
    SearchIndex::const_iterator lease =
        idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id));
    // Lease was not found. Return empty pointer to the caller.
180
    if (lease == idx.end()) {
181
        return (Lease4Ptr());
182
183
    }

184
    // Lease was found. Return it to the caller.
185
    return (Lease4Ptr(new Lease4(**lease)));
186
187
}

188
Lease4Collection
189
Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
190
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
191
              DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
192
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
193
194
195
196
197
198
199
    Lease4Collection collection;
    const SearchIndex& idx = storage4_.get<0>();
    for(SearchIndex::const_iterator lease = idx.begin();
        lease != idx.end(); ++ lease) {

        // client-id is not mandatory in DHCPv4. There can be a lease that does
        // not have a client-id. Dereferencing null pointer would be a bad thing
200
201
        if((*lease)->client_id_ && *(*lease)->client_id_ == client_id) {
            collection.push_back((*lease));
202
203
204
205
        }
    }

    return (collection);
206
}
207

208
209
Lease4Ptr
Memfile_LeaseMgr::getLease4(const ClientId& client_id,
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
                            const HWAddr& hwaddr,
                            SubnetID subnet_id) const {
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID).arg(client_id.toText())
                                                        .arg(hwaddr.toText())
                                                        .arg(subnet_id);

    // We are going to use index #3 of the multi index container.
    // We define SearchIndex locally in this function because
    // currently only this function uses this index.
    typedef Lease4Storage::nth_index<3>::type SearchIndex;
    // Get the index.
    const SearchIndex& idx = storage4_.get<3>();
    // Try to get the lease using client id, hardware address and subnet id.
    SearchIndex::const_iterator lease =
        idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_,
                                   subnet_id));

    if (lease == idx.end()) {
        // Lease was not found. Return empty pointer to the caller.
        return (Lease4Ptr());
    }

    // Lease was found. Return it to the caller.
    return (*lease);
}

Lease4Ptr
Memfile_LeaseMgr::getLease4(const ClientId& client_id,
                            SubnetID subnet_id) const {
240
241
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
242
              .arg(client_id.toText());
243

244
245
246
    // We are going to use index #2 of the multi index container.
    // We define SearchIndex locally in this function because
    // currently only this function uses this index.
247
    typedef Lease4Storage::nth_index<2>::type SearchIndex;
248
    // Get the index.
249
    const SearchIndex& idx = storage4_.get<2>();
250
251
252
253
    // Try to get the lease using client id and subnet id.
    SearchIndex::const_iterator lease =
        idx.find(boost::make_tuple(client_id.getClientId(), subnet_id));
    // Lease was not found. Return empty pointer to the caller.
254
    if (lease == idx.end()) {
255
        return (Lease4Ptr());
256
    }
257
    // Lease was found. Return it to the caller.
258
    return (Lease4Ptr(new Lease4(**lease)));
259
260
}

261
Lease6Ptr
262
Memfile_LeaseMgr::getLease6(Lease::Type type,
263
                            const isc::asiolink::IOAddress& addr) const {
264
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
265
266
267
              DHCPSRV_MEMFILE_GET_ADDR6)
        .arg(addr.toText())
        .arg(Lease::typeToText(type));
268
    Lease6Storage::iterator l = storage6_.find(addr);
269
    if (l == storage6_.end() || !(*l) || ((*l)->type_ != type)) {
270
271
        return (Lease6Ptr());
    } else {
272
        return (Lease6Ptr(new Lease6(**l)));
273
274
275
    }
}

276
Lease6Collection
277
Memfile_LeaseMgr::getLeases6(Lease::Type type,
278
                            const DUID& duid, uint32_t iaid) const {
279
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
280
281
282
283
              DHCPSRV_MEMFILE_GET_IAID_DUID)
        .arg(iaid)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
284

285
286
287
288
289
290
291
292
293
294
295
    // We are going to use index #1 of the multi index container.
    typedef Lease6Storage::nth_index<1>::type SearchIndex;
    // Get the index.
    const SearchIndex& idx = storage6_.get<1>();
    // Try to get the lease using the DUID, IAID and lease type.
    std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
        idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
    Lease6Collection collection;
    for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
        collection.push_back(Lease6Ptr(new Lease6(**lease)));
    }
296

297
    return (collection);
298
299
}

300
Lease6Collection
301
Memfile_LeaseMgr::getLeases6(Lease::Type type,
302
303
                             const DUID& duid, uint32_t iaid,
                             SubnetID subnet_id) const {
304
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
305
              DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
306
307
308
309
        .arg(iaid)
        .arg(subnet_id)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
310

311
312
313
314
    // We are going to use index #1 of the multi index container.
    typedef Lease6Storage::nth_index<1>::type SearchIndex;
    // Get the index.
    const SearchIndex& idx = storage6_.get<1>();
315
316
317
318
319
320
321
322
323
    // Try to get the lease using the DUID, IAID and lease type.
    std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
        idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
    Lease6Collection collection;
    for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
        // Filter out the leases which subnet id doesn't match.
        if((*lease)->subnet_id_ == subnet_id) {
            collection.push_back(Lease6Ptr(new Lease6(**lease)));
        }
324
    }
325
326

    return (collection);
327
328
}

329
330
void
Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
331
332
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
333

334
335
336
    Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
    if (lease_it == storage4_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
337
                  << lease->addr_ << " - no such lease");
338
    }
339
340
341
342

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
343
    if (persistLeases(V4)) {
344
345
346
        lease_file4_->append(*lease);
    }

347
    **lease_it = *lease;
348
349
}

350
351
void
Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
352
353
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
354

355
356
357
    Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
    if (lease_it == storage6_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
358
                  << lease->addr_ << " - no such lease");
359
    }
360
361
362
363

    // Try to write a lease to disk first. If this fails, the lease will
    // not be inserted to the memory and the disk and in-memory data will
    // remain consistent.
364
    if (persistLeases(V6)) {
365
366
367
        lease_file6_->append(*lease);
    }

368
    **lease_it = *lease;
369
370
}

371
372
bool
Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
373
374
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
375
    if (addr.isV4()) {
376
377
378
379
380
381
        // v4 lease
        Lease4Storage::iterator l = storage4_.find(addr);
        if (l == storage4_.end()) {
            // No such lease
            return (false);
        } else {
382
            if (persistLeases(V4)) {
383
384
385
386
387
388
389
390
                // Copy the lease. The valid lifetime needs to be modified and
                // we don't modify the original lease.
                Lease4 lease_copy = **l;
                // Setting valid lifetime to 0 means that lease is being
                // removed.
                lease_copy.valid_lft_ = 0;
                lease_file4_->append(lease_copy);
            }
391
392
393
            storage4_.erase(l);
            return (true);
        }
394

395
    } else {
396
        // v6 lease
397
398
399
400
401
        Lease6Storage::iterator l = storage6_.find(addr);
        if (l == storage6_.end()) {
            // No such lease
            return (false);
        } else {
402
            if (persistLeases(V6)) {
403
404
405
406
407
408
409
410
411
                // Copy the lease. The lifetimes need to be modified and we
                // don't modify the original lease.
                Lease6 lease_copy = **l;
                // Setting lifetimes to 0 means that lease is being removed.
                lease_copy.valid_lft_ = 0;
                lease_copy.preferred_lft_ = 0;
                lease_file6_->append(lease_copy);
            }

412
413
414
            storage6_.erase(l);
            return (true);
        }
415
416
417
    }
}

418
419
std::string
Memfile_LeaseMgr::getDescription() const {
420
421
422
423
    return (std::string("This is a dummy memfile backend implementation.\n"
                        "It does not offer any useful lease management and its only\n"
                        "purpose is to test abstract lease manager API."));
}
424
425
426

void
Memfile_LeaseMgr::commit() {
427
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
428
429
430
431
}

void
Memfile_LeaseMgr::rollback() {
432
433
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ROLLBACK);
434
}
435

436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
std::string
Memfile_LeaseMgr::appendSuffix(const std::string& file_name,
                               const LFCFileType& file_type) {
    std::string name(file_name);
    switch (file_type) {
    case FILE_INPUT:
        name += ".1";
        break;
    case FILE_PREVIOUS:
        name += ".2";
        break;
    case FILE_OUTPUT:
        name += ".output";
        break;
    case FILE_FINISH:
        name += ".completed";
        break;
453
454
455
    case FILE_PID:
        name += ".pid";
        break;
456
457
458
459
460
461
462
463
464
    default:
        // Do not append any suffix for the FILE_CURRENT.
        ;
    }

    return (name);
}


465
466
467
468
469
uint32_t
Memfile_LeaseMgr::getIOServiceExecInterval() const {
    return (static_cast<uint32_t>(lfc_timer_.getInterval() / 1000));
}

470
471
472
473
474
475
476
477
478
std::string
Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const {
    std::ostringstream s;
    s << CfgMgr::instance().getDataDir() << "/kea-leases";
    s << (u == V4 ? "4" : "6");
    s << ".csv";
    return (s.str());
}

479
480
481
482
483
484
485
486
487
std::string
Memfile_LeaseMgr::getLeaseFilePath(Universe u) const {
    if (u == V4) {
        return (lease_file4_ ? lease_file4_->getFilename() : "");
    }

    return (lease_file6_ ? lease_file6_->getFilename() : "");
}

488
489
bool
Memfile_LeaseMgr::persistLeases(Universe u) const {
490
491
492
493
494
495
496
497
    // Currently, if the lease file IO is not created, it means that writes to
    // disk have been explicitly disabled by the administrator. At some point,
    // there may be a dedicated ON/OFF flag implemented to control this.
    if (u == V4 && lease_file4_) {
        return (true);
    }

    return (u == V6 && lease_file6_);
498
499
500
501
}

std::string
Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
502
    std::string persist_val;
503
    try {
504
        persist_val = getParameter("persist");
505
    } catch (const Exception& ex) {
506
507
        // If parameter persist hasn't been specified, we use a default value
        // 'yes'.
508
        persist_val = "true";
509
    }
510
    // If persist_val is 'false' we will not store leases to disk, so let's
511
    // return empty file name.
512
    if (persist_val == "false") {
513
        return ("");
514

515
516
517
    } else if (persist_val != "true") {
        isc_throw(isc::BadValue, "invalid value 'persist="
                  << persist_val << "'");
518
519
    }

520
521
    std::string lease_file;
    try {
522
        lease_file = getParameter("name");
523
524
525
526
527
    } catch (const Exception& ex) {
        lease_file = getDefaultLeaseFilePath(u);
    }
    return (lease_file);
}
528

529
void
530
Memfile_LeaseMgr::lfcSetup() {
531
532
533
534
535
536
537
538
539
540
541
    std::string lfc_interval_str = "0";
    try {
        lfc_interval_str = getParameter("lfc-interval");
    } catch (const std::exception& ex) {
        // Ignore and default to 0.
    }

    uint32_t lfc_interval = 0;
    try {
        lfc_interval = boost::lexical_cast<uint32_t>(lfc_interval_str);
    } catch (boost::bad_lexical_cast& ex) {
542
        isc_throw(isc::BadValue, "invalid value of the lfc-interval "
543
544
545
                  << lfc_interval_str << " specified");
    }

546
547
    // If LFC is enabled, we have to setup the interval timer and prepare for
    // executing the kea-lfc process.
548
    if (lfc_interval > 0) {
549
        std::string executable;
550
        char* c_executable = getenv(KEA_LFC_EXECUTABLE_ENV_NAME);
551
552
553
554
555
556
        if (c_executable == NULL) {
            executable = KEA_LFC_EXECUTABLE;

        } else {
            executable = c_executable;
        }
557
558

        // Set the timer to call callback function periodically.
559
560
        asiolink::IntervalTimer::Callback cb =
            boost::bind(&Memfile_LeaseMgr::lfcCallback, this);
561
        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
562
        lfc_timer_.setup(cb, lfc_interval * 1000);
563
564
565
566
567
568
569
570
571
572
573
574

        // Start preparing the command line for kea-lfc.

        // Gather the base file name.
        std::string lease_file = lease_file4_ ? lease_file4_->getFilename() :
            lease_file6_->getFilename();

        // Create the other names by appending suffixes to the base name.
        util::ProcessArgs args;
        // Universe: v4 or v6.
        args.push_back(lease_file4_ ? "-4" : "-6");
        // Previous file.
575
        args.push_back("-x");
576
577
578
579
580
581
582
583
584
585
        args.push_back(appendSuffix(lease_file, FILE_PREVIOUS));
        // Input file.
        args.push_back("-i");
        args.push_back(appendSuffix(lease_file, FILE_INPUT));
        // Output file.
        args.push_back("-o");
        args.push_back(appendSuffix(lease_file, FILE_OUTPUT));
        // Finish file.
        args.push_back("-f");
        args.push_back(appendSuffix(lease_file, FILE_FINISH));
586
587
588
589
        // PID file.
        args.push_back("-p");
        args.push_back(appendSuffix(lease_file, FILE_PID));

590
591
592
593
594
595
        // The configuration file is currently unused.
        args.push_back("-c");
        args.push_back("ignored-path");

        // Create the process (do not start it yet).
        lfc_process_.reset(new util::ProcessSpawn(std::string(c_executable), args));
596
597
598
    }
}

599

600
601
void
Memfile_LeaseMgr::lfcCallback() {
602
    LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_START);
603
604
605
606
607
608
609
610
611
612
613
614

    // Check if we're in the v4 or v6 space and use the appropriate file.
    if (lease_file4_) {
        leaseFileCleanup(lease_file4_);

    } else if (lease_file6_) {
        leaseFileCleanup(lease_file6_);

    }
}

template<typename LeaseFileType>
615
616
void Memfile_LeaseMgr::leaseFileCleanup(boost::shared_ptr<LeaseFileType>& lease_file) {
    bool do_lfc = true;
617
618
619
620
    // 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
    // overriding the contents of the existing file.
621
622
    CSVFile lease_file_copy(appendSuffix(lease_file->getFilename(), FILE_INPUT));
    if (!lease_file_copy.exists()) {
623
        // Close the current file so as we can move it to the copy file.
624
        lease_file->close();
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
        // 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;
        }
655
    }
656
657
    // Once we have rotated files as needed, start the new kea-lfc process
    // to perform a cleanup.
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
    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());
    }
673
674
}

675
template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
676
677
678
void Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename,
                                           boost::shared_ptr<LeaseFileType>& lease_file,
                                           StorageType& storage) {
679
680
681
682
683
684
685
686
687
688
689
690
    // Check if the instance of the LFC is running right now. If it is
    // running, we refuse to load leases as the LFC may be writing to the
    // lease files right now. When the user retries server configuration
    // it should go through.
    /// @todo Consider applying a timeout for an LFC and retry when this
    /// timeout elapses.
    PIDFile pid_file(appendSuffix(filename, FILE_PID));
    if (pid_file.check()) {
        isc_throw(DbOpenError, "unable to load leases from files while the "
                  "lease file cleanup is in progress");
    }

691
    storage.clear();
692

693
694
695
696
697
    // Load the leasefile.completed, if exists.
    lease_file.reset(new LeaseFileType(std::string(filename + ".completed")));
    if (lease_file->exists()) {
        LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
                                               MAX_LEASE_ERRORS);
698

699
700
701
    } else {
        // If the leasefile.completed doesn't exist, let's load the leases
        // from leasefile.2 and leasefile.1, if they exist.
702
        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_PREVIOUS)));
703
704
705
        if (lease_file->exists()) {
            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
                                                   MAX_LEASE_ERRORS);
706
707
        }

708
        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_INPUT)));
709
        if (lease_file->exists()) {
710
            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
711
                                                   MAX_LEASE_ERRORS);
712
        }
713
    }
714
715
716
717
718
719
720
721
722
723

    // Always load leases from the primary lease file. If the lease file
    // doesn't exist it will be created by the LeaseFileLoader. Note
    // that the false value passed as the last parameter to load
    // function causes the function to leave the file open after
    // it is parsed. This file will be used by the backend to record
    // future lease updates.
    lease_file.reset(new LeaseFileType(filename));
    LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
                                           MAX_LEASE_ERRORS, false);;
724
}