memfile_lease_mgr.cc 30.3 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 <config.h>
16
#include <dhcpsrv/cfgmgr.h>
17
#include <dhcpsrv/dhcpsrv_log.h>
18
#include <dhcpsrv/lease_file_loader.h>
19
#include <dhcpsrv/memfile_lease_mgr.h>
20
#include <exceptions/exceptions.h>
21
#include <util/pid_file.h>
22
#include <util/process_spawn.h>
23
#include <util/signal_set.h>
24
#include <cstdio>
25
26
#include <cstring>
#include <errno.h>
27
#include <iostream>
28
#include <sstream>
29

30
31
32
33
34
namespace {

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

35
36
37
38
39
40
41
/// @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.
42
43
const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE";

44
45
} // end of anonymous namespace

46
using namespace isc::util;
47

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
namespace isc {
namespace dhcp {

/// @brief Represents a configuration for Lease File Cleanup.
///
/// This class is solely used by the @c Memfile_LeaseMgr as a configuration
/// information storage for %Lease File Cleanup. Internally, it creates
/// the interval timer and assigns a callback function (pointer to which is
/// passed in the constructor), which will be called at the specified
/// intervals to perform the cleanup. It is also responsible for creating
/// and maintaing the object which is used to spawn the new process which
/// executes the @c kea-lfc program.
///
/// This functionality is enclosed in a separate class so as the implementation
/// details are not exposed in the @c Memfile_LeaseMgr header file and
/// to maintain a single place with the LFC configuration, instead of multiple
/// members and functions scattered in the @c Memfile_LeaseMgr class.
class LFCSetup {
public:

    /// @brief Constructor.
    ///
    /// Assigns a pointer to the function triggered to perform the cleanup.
    /// This pointer should point to the appropriate method of the
    /// @c Memfile_LeaseMgr class.
    ///
    /// @param callback A pointer to the callback function.
    /// @param io_service An io service used to create the interval timer.
    LFCSetup(asiolink::IntervalTimer::Callback callback,
             asiolink::IOService& io_service);

    /// @brief Sets the new configuration for the %Lease File Cleanup.
    ///
    /// @param lfc_interval An interval in seconds at which the cleanup should
    /// be performed.
    /// @param lease_file4 A pointer to the DHCPv4 lease file to be cleaned up
    /// or NULL. If this is NULL, the @c lease_file6 must be non-null.
    /// @param lease_file6 A pointer to the DHCPv6 lease file to be cleaned up
    /// or NULL. If this is NULL, the @c lease_file4 must be non-null.
    void setup(const uint32_t lfc_interval,
               const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
               const boost::shared_ptr<CSVLeaseFile6>& lease_file6);

    /// @brief Spawns a new process.
    void execute();

    /// @brief Returns interval at which the cleanup is performed.
    ///
    /// @return Interval in milliseconds.
    long getInterval() const;

    /// @brief Checks if the lease file cleanup is in progress.
    ///
    /// @return true if the lease file cleanup is being executed.
    bool isRunning() const;

    /// @brief Returns exit code of the last completed cleanup.
    int getExitStatus() const;

private:

    /// @brief Interval timer for LFC.
    asiolink::IntervalTimer timer_;

    /// @brief A pointer to the @c ProcessSpawn object used to execute
    /// the LFC.
    boost::scoped_ptr<util::ProcessSpawn> process_;

    /// @brief A pointer to the callback function executed by the timer.
    asiolink::IntervalTimer::Callback callback_;

    /// @brief A PID of the last executed LFC process.
    pid_t pid_;
};

LFCSetup::LFCSetup(asiolink::IntervalTimer::Callback callback,
                   asiolink::IOService& io_service)
    : timer_(io_service), process_(), callback_(callback), pid_(0) {
}

void
LFCSetup::setup(const uint32_t lfc_interval,
                const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
                const boost::shared_ptr<CSVLeaseFile6>& lease_file6) {

    // If LFC is enabled, we have to setup the interval timer and prepare for
    // executing the kea-lfc process.
    if (lfc_interval > 0) {
        std::string executable;
        char* c_executable = getenv(KEA_LFC_EXECUTABLE_ENV_NAME);
        if (c_executable == NULL) {
            executable = KEA_LFC_EXECUTABLE;

        } else {
            executable = c_executable;
        }

        // Set the timer to call callback function periodically.
        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
        // Multiple the lfc_interval value by 1000 as this value specifies
        // a timeout in seconds, whereas the setup() method expects the
        // timeout in milliseconds.
        timer_.setup(callback_, lfc_interval * 1000);

        // 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.
        args.push_back("-x");
        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
                                                      Memfile_LeaseMgr::FILE_PREVIOUS));
        // Input file.
        args.push_back("-i");
        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
                                                      Memfile_LeaseMgr::FILE_INPUT));
        // Output file.
        args.push_back("-o");
        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
                                                      Memfile_LeaseMgr::FILE_OUTPUT));
        // Finish file.
        args.push_back("-f");
        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
                                                      Memfile_LeaseMgr::FILE_FINISH));
        // PID file.
        args.push_back("-p");
        args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
                                                      Memfile_LeaseMgr::FILE_PID));

        // The configuration file is currently unused.
        args.push_back("-c");
        args.push_back("ignored-path");

        // Create the process (do not start it yet).
        process_.reset(new util::ProcessSpawn(executable, args));
    }
}

long
LFCSetup::getInterval() const {
    return (timer_.getInterval());
}

void
LFCSetup::execute() {
    try {
        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_EXECUTE)
            .arg(process_->getCommandLine());
        pid_ = process_->spawn();

    } catch (const ProcessSpawnError& ex) {
        LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SPAWN_FAIL);
    }
}

bool
LFCSetup::isRunning() const {
    return (process_ && process_->isRunning(pid_));
}

int
LFCSetup::getExitStatus() const {
    if (!process_) {
        isc_throw(InvalidOperation, "unable to obtain LFC process exit code: "
                  " the process is NULL");
    }
    return (process_->getExitStatus(pid_));
}


224
Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
225
226
227
228
    : LeaseMgr(parameters),
      lfc_setup_(new LFCSetup(boost::bind(&Memfile_LeaseMgr::lfcCallback, this),
                              *getIOService()))
    {
229
230
231
232
233
    // 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()) {
234
235
            loadLeasesFromFiles<Lease4, CSVLeaseFile4>(file4, lease_file4_,
                                                       storage4_);
236
237
238
239
        }
    } else {
        std::string file6 = initLeaseFilePath(V6);
        if (!file6.empty()) {
240
241
            loadLeasesFromFiles<Lease6, CSVLeaseFile6>(file6, lease_file6_,
                                                       storage6_);
242
        }
243
    }
244
245
246
247
248

    // 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.
249
   if (!persistLeases(V4) && !persistLeases(V6)) {
250
        LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE);
251
252

    } else  {
253
        lfcSetup();
254
    }
255
256
257
}

Memfile_LeaseMgr::~Memfile_LeaseMgr() {
258
259
260
261
262
263
264
265
    if (lease_file4_) {
        lease_file4_->close();
        lease_file4_.reset();
    }
    if (lease_file6_) {
        lease_file6_->close();
        lease_file6_.reset();
    }
266
267
}

268
269
bool
Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
270
271
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText());
272

273
274
275
276
    if (getLease4(lease->addr_)) {
        // there is a lease with specified address already
        return (false);
    }
277
278
279
280

    // 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.
281
    if (persistLeases(V4)) {
282
283
284
        lease_file4_->append(*lease);
    }

285
286
    storage4_.insert(lease);
    return (true);
287
288
}

289
290
bool
Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
291
292
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText());
293

294
    if (getLease6(lease->type_, lease->addr_)) {
295
296
297
        // there is a lease with specified address already
        return (false);
    }
298
299
300
301

    // 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.
302
    if (persistLeases(V6)) {
303
304
305
        lease_file6_->append(*lease);
    }

306
307
308
309
    storage6_.insert(lease);
    return (true);
}

310
311
Lease4Ptr
Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
312
313
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
314

315
316
317
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
    const SearchIndex& idx = storage4_.get<0>();
    Lease4Storage::iterator l = idx.find(addr);
318
319
320
    if (l == storage4_.end()) {
        return (Lease4Ptr());
    } else {
321
        return (Lease4Ptr(new Lease4(**l)));
322
    }
323
324
}

325
326
Lease4Collection
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
327
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
328
              DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
329
330
331
332
333
334
335
    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
336
        if ( (*(*lease)->hwaddr_) == hwaddr) {
337
            collection.push_back((*lease));
338
339
        }
    }
340

341
    return (collection);
342
343
}

344
345
Lease4Ptr
Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
346
347
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
348
349
        .arg(hwaddr.toText());

350
351
352
    // 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.
353
    typedef Lease4Storage::nth_index<1>::type SearchIndex;
354
    // Get the index.
355
    const SearchIndex& idx = storage4_.get<1>();
356
357
358
359
    // 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.
360
    if (lease == idx.end()) {
361
        return (Lease4Ptr());
362
363
    }

364
    // Lease was found. Return it to the caller.
365
    return (Lease4Ptr(new Lease4(**lease)));
366
367
}

368
Lease4Collection
369
Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
370
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
371
              DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
372
    typedef Lease4Storage::nth_index<0>::type SearchIndex;
373
374
375
376
377
378
379
    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
380
381
        if((*lease)->client_id_ && *(*lease)->client_id_ == client_id) {
            collection.push_back((*lease));
382
383
384
385
        }
    }

    return (collection);
386
}
387

388
389
Lease4Ptr
Memfile_LeaseMgr::getLease4(const ClientId& client_id,
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
                            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 {
420
421
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
422
              .arg(client_id.toText());
423

424
425
426
    // 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.
427
    typedef Lease4Storage::nth_index<2>::type SearchIndex;
428
    // Get the index.
429
    const SearchIndex& idx = storage4_.get<2>();
430
431
432
433
    // 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.
434
    if (lease == idx.end()) {
435
        return (Lease4Ptr());
436
    }
437
    // Lease was found. Return it to the caller.
438
    return (Lease4Ptr(new Lease4(**lease)));
439
440
}

441
Lease6Ptr
442
Memfile_LeaseMgr::getLease6(Lease::Type type,
443
                            const isc::asiolink::IOAddress& addr) const {
444
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
445
446
447
              DHCPSRV_MEMFILE_GET_ADDR6)
        .arg(addr.toText())
        .arg(Lease::typeToText(type));
448
    Lease6Storage::iterator l = storage6_.find(addr);
449
    if (l == storage6_.end() || !(*l) || ((*l)->type_ != type)) {
450
451
        return (Lease6Ptr());
    } else {
452
        return (Lease6Ptr(new Lease6(**l)));
453
454
455
    }
}

456
Lease6Collection
457
Memfile_LeaseMgr::getLeases6(Lease::Type type,
458
                            const DUID& duid, uint32_t iaid) const {
459
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
460
461
462
463
              DHCPSRV_MEMFILE_GET_IAID_DUID)
        .arg(iaid)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
464

465
466
467
468
469
470
471
472
473
474
475
    // 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)));
    }
476

477
    return (collection);
478
479
}

480
Lease6Collection
481
Memfile_LeaseMgr::getLeases6(Lease::Type type,
482
483
                             const DUID& duid, uint32_t iaid,
                             SubnetID subnet_id) const {
484
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
485
              DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
486
487
488
489
        .arg(iaid)
        .arg(subnet_id)
        .arg(duid.toText())
        .arg(Lease::typeToText(type));
490

491
492
493
494
    // 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>();
495
496
497
498
499
500
501
502
503
    // 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)));
        }
504
    }
505
506

    return (collection);
507
508
}

509
510
void
Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
511
512
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
513

514
515
516
    Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
    if (lease_it == storage4_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
517
                  << lease->addr_ << " - no such lease");
518
    }
519
520
521
522

    // 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.
523
    if (persistLeases(V4)) {
524
525
526
        lease_file4_->append(*lease);
    }

527
    **lease_it = *lease;
528
529
}

530
531
void
Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
532
533
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
534

535
536
537
    Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
    if (lease_it == storage6_.end()) {
        isc_throw(NoSuchLease, "failed to update the lease with address "
538
                  << lease->addr_ << " - no such lease");
539
    }
540
541
542
543

    // 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.
544
    if (persistLeases(V6)) {
545
546
547
        lease_file6_->append(*lease);
    }

548
    **lease_it = *lease;
549
550
}

551
552
bool
Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
553
554
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
555
    if (addr.isV4()) {
556
557
558
559
560
561
        // v4 lease
        Lease4Storage::iterator l = storage4_.find(addr);
        if (l == storage4_.end()) {
            // No such lease
            return (false);
        } else {
562
            if (persistLeases(V4)) {
563
564
565
566
567
568
569
570
                // 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);
            }
571
572
573
            storage4_.erase(l);
            return (true);
        }
574

575
    } else {
576
        // v6 lease
577
578
579
580
581
        Lease6Storage::iterator l = storage6_.find(addr);
        if (l == storage6_.end()) {
            // No such lease
            return (false);
        } else {
582
            if (persistLeases(V6)) {
583
584
585
586
587
588
589
590
591
                // 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);
            }

592
593
594
            storage6_.erase(l);
            return (true);
        }
595
596
597
    }
}

598
599
std::string
Memfile_LeaseMgr::getDescription() const {
600
601
602
603
    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."));
}
604
605
606

void
Memfile_LeaseMgr::commit() {
607
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
608
609
610
611
}

void
Memfile_LeaseMgr::rollback() {
612
613
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
              DHCPSRV_MEMFILE_ROLLBACK);
614
}
615

616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
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;
633
634
635
    case FILE_PID:
        name += ".pid";
        break;
636
637
638
639
640
641
642
643
644
    default:
        // Do not append any suffix for the FILE_CURRENT.
        ;
    }

    return (name);
}


645
646
uint32_t
Memfile_LeaseMgr::getIOServiceExecInterval() const {
647
    return (static_cast<uint32_t>(lfc_setup_->getInterval() / 1000));
648
649
}

650
651
652
653
654
655
656
657
658
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());
}

659
660
661
662
663
664
665
666
667
std::string
Memfile_LeaseMgr::getLeaseFilePath(Universe u) const {
    if (u == V4) {
        return (lease_file4_ ? lease_file4_->getFilename() : "");
    }

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

668
669
bool
Memfile_LeaseMgr::persistLeases(Universe u) const {
670
671
672
673
674
675
676
677
    // 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_);
678
679
680
681
}

std::string
Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
682
    std::string persist_val;
683
    try {
684
        persist_val = getParameter("persist");
685
    } catch (const Exception& ex) {
686
687
        // If parameter persist hasn't been specified, we use a default value
        // 'yes'.
688
        persist_val = "true";
689
    }
690
    // If persist_val is 'false' we will not store leases to disk, so let's
691
    // return empty file name.
692
    if (persist_val == "false") {
693
        return ("");
694

695
696
697
    } else if (persist_val != "true") {
        isc_throw(isc::BadValue, "invalid value 'persist="
                  << persist_val << "'");
698
699
    }

700
701
    std::string lease_file;
    try {
702
        lease_file = getParameter("name");
703
704
705
706
707
    } catch (const Exception& ex) {
        lease_file = getDefaultLeaseFilePath(u);
    }
    return (lease_file);
}
708

709
710
711
712
713
714
715
716
717
718
719
720
721
722
template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
void Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename,
                                           boost::shared_ptr<LeaseFileType>& lease_file,
                                           StorageType& storage) {
    // 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");
723
724
    }

725
    storage.clear();
726

727
728
729
730
731
    // 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);
732

733
734
735
736
737
738
739
    } else {
        // If the leasefile.completed doesn't exist, let's load the leases
        // from leasefile.2 and leasefile.1, if they exist.
        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_PREVIOUS)));
        if (lease_file->exists()) {
            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
                                                   MAX_LEASE_ERRORS);
740
        }
741

742
743
744
745
746
747
        lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_INPUT)));
        if (lease_file->exists()) {
            LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
                                                   MAX_LEASE_ERRORS);
        }
    }
748

749
750
751
752
753
754
755
756
    // 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,
Francis Dupont's avatar
Francis Dupont committed
757
                                           MAX_LEASE_ERRORS, false);
758
}
759

760

761
762
763
bool
Memfile_LeaseMgr::isLFCRunning() const {
    return (lfc_setup_->isRunning());
764
765
}

766
767
768
769
int
Memfile_LeaseMgr::getLFCExitStatus() const {
    return (lfc_setup_->getExitStatus());
}
770

771
772
void
Memfile_LeaseMgr::lfcCallback() {
773
    LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_START);
774
775
776

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

    } else if (lease_file6_) {
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
        lfcExecute(lease_file6_);
    }
}

void
Memfile_LeaseMgr::lfcSetup() {
    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) {
        isc_throw(isc::BadValue, "invalid value of the lfc-interval "
                  << lfc_interval_str << " specified");
    }
800

801
802
    if (lfc_interval > 0) {
        lfc_setup_->setup(lfc_interval, lease_file4_, lease_file6_);
803
804
805
806
    }
}

template<typename LeaseFileType>
807
void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file) {
808
    bool do_lfc = true;
809

810
811
    // Check the status of the LFC instance.
    // If the finish file exists or the copy of the lease file exists it
812
813
814
    // is an indication that another LFC instance may be in progress or
    // may be stalled. In that case we don't want to rotate the current
    // lease file to avoid overriding the contents of the existing file.
815
    CSVFile lease_file_finish(appendSuffix(lease_file->getFilename(), FILE_FINISH));
816
    CSVFile lease_file_copy(appendSuffix(lease_file->getFilename(), FILE_INPUT));
817
    if (!lease_file_finish.exists() && !lease_file_copy.exists()) {
818
        // Close the current file so as we can move it to the copy file.
819
        lease_file->close();
820
821
822
823
        // 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);
824
825
826
827
828
829
830

        if (!do_lfc) {
            LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_RENAME_FAIL)
                .arg(lease_file->getFilename())
                .arg(lease_file_copy.getFilename())
                .arg(strerror(errno));
        }
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850

        // 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)
851
852
                .arg(lease_file->getFilename())
                .arg(ex.what());
853
854
855
856
857
            // Reset the pointer to the file so as the backend doesn't
            // try to write leases to disk.
            lease_file.reset();
            do_lfc = false;
        }
858
    }
859
860
    // Once the files have been rotated, or untouched if another LFC had
    // not finished, a new process is started.
861
    if (do_lfc) {
862
        lfc_setup_->execute();
863
    }
864
865
}

866

867
868
} // end of namespace isc::dhcp
} // end of namespace isc