cql_lease_mgr.cc 63.9 KB
Newer Older
1
// Copyright (C) 2015-2018 Deutsche Telekom AG.
2
//
3
4
// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
//          Andrei Pavel <andrei.pavel@qualitance.com>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//           http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <config.h>
Andrei Pavel's avatar
Andrei Pavel committed
19

Tomek Mrugalski's avatar
Tomek Mrugalski committed
20
#include <dhcpsrv/cql_lease_mgr.h>
Andrei Pavel's avatar
Andrei Pavel committed
21
22
#include <dhcpsrv/dhcpsrv_log.h>

23
24
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
25

26
27
28
#include <asiolink/io_address.h>

using isc::asiolink::IOAddress;
29
30
31
32

namespace isc {
namespace dhcp {

33
34
static constexpr size_t HOSTNAME_MAX_LEN = 255u;
static constexpr size_t ADDRESS6_TEXT_MAX_LEN = 39u;
35

Tomek Mrugalski's avatar
Tomek Mrugalski committed
36
37
/// @brief Common CQL and Lease Data Methods
///
38
/// The @ref CqlLease4Exchange and @ref CqlLease6Exchange classes provide the
Tomek Mrugalski's avatar
Tomek Mrugalski committed
39
40
41
/// functionality to set up binding information between variables in the
/// program and data extracted from the database. This class is the common
/// base to both of them, containing some common methods.
42
43
class CqlLeaseExchange : public CqlExchange {
public:
44
45
46
    /// @brief Constructor
    ///
    /// @param connection already open Cassandra connection.
47
48
49
50
    CqlLeaseExchange(const CqlConnection &connection)
        : connection_(connection), valid_lifetime_(0), expire_(0),
          subnet_id_(0), fqdn_fwd_(cass_false), fqdn_rev_(cass_false),
          state_(0) {
51
    }
52

53
54
55
56
57
58
59
60
    /// @brief Create BIND array to receive C++ data.
    ///
    /// Used in executeSelect() to retrieve from database
    ///
    /// @param data array of bound objects representing data to be retrieved
    /// @param statement_tag prepared statement being executed; defaults to an
    ///     invalid index
    virtual void
Razvan Becheriu's avatar
Razvan Becheriu committed
61
    createBindForSelect(AnyArray &data, StatementTag statement_tag = NULL) override = 0;
62
63
64
65
66
67
68
69
70
71

    /// @brief Copy received data into the derived class' object.
    ///
    /// Copies information about the entity to be retrieved into a holistic
    /// object. Called in @ref executeSelect(). Not implemented for base class
    /// CqlExchange. To be implemented in derived classes.
    ///
    /// @return a pointer to the object retrieved.
    virtual boost::any retrieve() override = 0;

Andrei Pavel's avatar
Andrei Pavel committed
72
protected:
73
74
75
76
77
78
79
80
81
82
83
84
    /// @brief Database connection
    const CqlConnection &connection_;

    /// @brief Hardware address
    CassBlob hwaddr_;

    /// @brief Lease timer
    cass_int64_t valid_lifetime_;

    /// @brief Lease expiry time
    cass_int64_t expire_;

85
    /// @brief Subnet identifier
86
87
    cass_int32_t subnet_id_;

88
    /// @brief Has forward DNS update been performed?
89
90
    cass_bool_t fqdn_fwd_;

91
    /// @brief Has reverse DNS update been performed?
92
93
94
95
96
97
98
    cass_bool_t fqdn_rev_;

    /// @brief Client hostname
    std::string hostname_;

    /// @brief Lease state
    cass_int32_t state_;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
99
100
};

101
/// @brief Exchange Lease4 information between Kea and CQL
102
///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
103
/// On any CQL operation, arrays of CQL BIND structures must be built to
Tomek Mrugalski's avatar
Tomek Mrugalski committed
104
/// describe the parameters in the prepared statements. Where information is
105
/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
Tomek Mrugalski's avatar
Tomek Mrugalski committed
106
/// structure is identical. This class handles the creation of that array.
107
///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
108
/// Owing to the CQL API, the process requires some intermediate variables
Tomek Mrugalski's avatar
Tomek Mrugalski committed
109
/// to hold things like data length etc. This object holds those variables.
110
///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
111
/// @note There are no unit tests for this class. It is tested indirectly
Tomek Mrugalski's avatar
Tomek Mrugalski committed
112
113
/// in all CqlLeaseMgr::xxx4() calls where it is used.
class CqlLease4Exchange : public CqlLeaseExchange {
114
115
116
public:
    /// @brief Constructor
    ///
Razvan Becheriu's avatar
Razvan Becheriu committed
117
118
119
    /// The initialization of the variables here is only to satisfy
    /// cppcheck - all variables are initialized/set in the methods before
    /// they are used.
120
121
    ///
    /// @param connection connection used for this query
122
    explicit CqlLease4Exchange(const CqlConnection &connection);
123

Tomek Mrugalski's avatar
Tomek Mrugalski committed
124
    /// @brief Create CQL_BIND objects for Lease4 Pointer
125
    ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
126
    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
127
    /// the database. Used for INSERT statements.
128
129
130
    ///
    /// @param lease The lease information to be inserted
    /// @param data Lease info will be stored here in CQL format
131
    void createBindForInsert(const Lease4Ptr &lease, AnyArray &data);
132

133
134
135
136
    /// @brief Create CQL_BIND objects for Lease4 Pointer
    ///
    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
    /// the database. Used for UPDATE statements.
137
138
139
140
    ///
    /// @param lease Updated lease information.
    /// @param data lease info in CQL format will be stored here
    /// @param statement_tag tag identifying the query (optional)
Razvan Becheriu's avatar
Razvan Becheriu committed
141
    void createBindForUpdate(const Lease4Ptr &lease, AnyArray &data,
142
                             StatementTag statement_tag = NULL);
143

144
145
146
147
    /// @brief Create CQL_BIND objects for Lease4 Pointer
    ///
    /// Fills in the CQL_BIND array for sending data in the Lease4 object to
    /// the database. Used for DELETE statements.
148
149
150
151
    ///
    /// @param address address of the lease to be deleted
    /// @param data lease info in CQL format will be stored here
    /// @param statement_tag tag identifying the query (optional)
152
153
154
    void createBindForDelete(const IOAddress &address,
                             AnyArray &data,
                             StatementTag statement_tag = NULL);
155
156
157

    /// @brief Create BIND array to receive data
    ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
158
    /// Creates a CQL_BIND array to receive Lease4 data from the database.
159
160
161
    ///
    /// @param data info returned by CQL will be stored here
    /// @param statement_tag tag identifying the query (optional)
162
    virtual void
163
    createBindForSelect(AnyArray &data, StatementTag statement_tag = NULL) override;
164

165
166
167
    /// @brief Retrieves the Lease4 object in Kea format
    ///
    /// @return C++ representation of the object being returned
168
169
    virtual boost::any retrieve() override;

170
171
172
173
174
175
    /// @brief Retrieves zero or more IPv4 leases
    ///
    /// @param statement_tag query to be executed
    /// @param data parameters for the query
    /// @param result this lease collection will be updated
    void getLeaseCollection(StatementTag &statement_tag, AnyArray &data,
176
177
                            Lease4Collection &result);

178
179
180
181
182
    /// @brief Retrieves one IPv4 lease
    ///
    /// @param statement_tag query to be executed
    /// @param data parameters for the query
    /// @param result pointer to the lease being returned (or null)
183
184
185
    void
    getLease(StatementTag &statement_tag, AnyArray &data, Lease4Ptr &result);

186
187
188
189
190
191
192
193
    /// @brief Returns expired leases.
    ///
    /// This method returns up to specified number (see max_leases) of
    /// expired leases.
    ///
    /// @param max_leases at most this number of leases will be returned
    /// @param expired_leases expired leases will be stored here
    void getExpiredLeases(const size_t &max_leases, Lease4Collection &expired_leases);
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

    /// @brief Cassandra statements
    static StatementMap tagged_statements_;

    /// @brief Statement tags definitions
    /// @{
    // Add entry to lease4 table
    static constexpr StatementTag INSERT_LEASE4 = "INSERT_LEASE4";
    // Update a Lease4 entry
    static constexpr StatementTag UPDATE_LEASE4 = "UPDATE_LEASE4";
    // Delete from lease4 by address
    static constexpr StatementTag DELETE_LEASE4 = "DELETE_LEASE4";
    // Delete expired lease4s in certain state
    static constexpr StatementTag GET_LEASE4_EXPIRE = "GET_LEASE4_EXPIRE";
    // Get lease4 by address
    static constexpr StatementTag GET_LEASE4_ADDR = "GET_LEASE4_ADDR";
    // Get lease4 by client ID
    static constexpr StatementTag GET_LEASE4_CLIENTID = "GET_LEASE4_CLIENTID";
    // Get lease4 by client ID & subnet ID
    static constexpr StatementTag GET_LEASE4_CLIENTID_SUBID =
        "GET_LEASE4_CLIENTID_SUBID";
    // Get lease4 by HW address
    static constexpr StatementTag GET_LEASE4_HWADDR = "GET_LEASE4_HWADDR";
    // Get lease4 by HW address & subnet ID
    static constexpr StatementTag GET_LEASE4_HWADDR_SUBID =
        "GET_LEASE4_HWADDR_SUBID";
    /// @}

private:
    // Pointer to lease object
    Lease4Ptr lease_;
    // IPv4 address
    cass_int32_t address_;
    // Client identification
    CassBlob client_id_;
};  // CqlLease4Exchange

constexpr StatementTag CqlLease4Exchange::INSERT_LEASE4;
constexpr StatementTag CqlLease4Exchange::UPDATE_LEASE4;
constexpr StatementTag CqlLease4Exchange::DELETE_LEASE4;
constexpr StatementTag CqlLease4Exchange::GET_LEASE4_EXPIRE;
constexpr StatementTag CqlLease4Exchange::GET_LEASE4_ADDR;
constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID;
constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID_SUBID;
constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR;
constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR_SUBID;

StatementMap CqlLease4Exchange::tagged_statements_{

243
244
245
    // Inserts new IPv4 lease
    {INSERT_LEASE4,
     {INSERT_LEASE4,
246
247
248
249
250
251
252
253
      "INSERT INTO lease4( "
      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
      "fqdn_fwd, fqdn_rev, hostname, state "
      ") VALUES ( "
      "?, ?, ?, ?, ?, ?, ?, ?, ?, ? "
      ") "
      "IF NOT EXISTS "}},

254
255
256
    // Updates existing IPv4 lease
    {UPDATE_LEASE4,
     {UPDATE_LEASE4,
257
258
259
260
261
262
263
264
265
266
267
268
269
      "UPDATE lease4 SET "
      "hwaddr = ?, "
      "client_id = ?, "
      "subnet_id = ?, "
      "valid_lifetime = ?, "
      "expire = ?, "
      "fqdn_fwd = ?, "
      "fqdn_rev = ?, "
      "hostname = ?, "
      "state = ? "
      "WHERE address = ? "
      "IF EXISTS "}},

270
271
272
    // Deletes existing IPv4 lease
    {DELETE_LEASE4,
     {DELETE_LEASE4,
273
274
275
276
      "DELETE FROM lease4 "
      "WHERE address = ? "
      "IF EXISTS "}},

277
278
279
    // Gets up to a certain number of expired IPv4 leases
    {GET_LEASE4_EXPIRE,
     {GET_LEASE4_EXPIRE,
280
281
282
283
284
285
286
287
288
      "SELECT "
      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
      "fqdn_fwd, fqdn_rev, hostname, state "
      "FROM lease4 "
      "WHERE state = ? "
      "AND expire < ? "
      "LIMIT ? "
      "ALLOW FILTERING "}},

289
290
291
    // Gets an IPv4 lease with specified IPv4 address
    {GET_LEASE4_ADDR,
     {GET_LEASE4_ADDR,
292
293
294
295
296
297
      "SELECT "
      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
      "fqdn_fwd, fqdn_rev, hostname, state "
      "FROM lease4 "
      "WHERE address = ? "}},

298
299
300
    // Gets an IPv4 lease(s) with specified client-id
    {GET_LEASE4_CLIENTID,
     {GET_LEASE4_CLIENTID,
301
302
303
304
305
306
307
      "SELECT "
      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
      "fqdn_fwd, fqdn_rev, hostname, state "
      "FROM lease4 "
      "WHERE client_id = ? "
      "ALLOW FILTERING "}},

308
309
310
    // Gets an IPv4 lease with specified client-id and subnet-id
    {GET_LEASE4_CLIENTID_SUBID,
     {GET_LEASE4_CLIENTID_SUBID,
311
312
313
314
315
316
317
318
      "SELECT "
      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
      "fqdn_fwd, fqdn_rev, hostname, state "
      "FROM lease4 "
      "WHERE client_id = ? "
      "AND subnet_id = ? "
      "ALLOW FILTERING "}},

319
320
321
    // Gets all IPv4 leases with specified hardware address
    {GET_LEASE4_HWADDR,
     {GET_LEASE4_HWADDR,
322
323
324
325
326
327
328
      "SELECT "
      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
      "fqdn_fwd, fqdn_rev, hostname, state "
      "FROM lease4 "
      "WHERE hwaddr = ? "
      "ALLOW FILTERING "}},

329
330
    // Gets an IPv4 lease with specified hardware addr and subnet-id
    {GET_LEASE4_HWADDR_SUBID,
Razvan Becheriu's avatar
Razvan Becheriu committed
331
     {GET_LEASE4_HWADDR_SUBID,
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
      "SELECT "
      "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
      "fqdn_fwd, fqdn_rev, hostname, state "
      "FROM lease4 "
      "WHERE hwaddr = ? "
      "AND subnet_id = ? "
      "ALLOW FILTERING "}},

};

CqlLease4Exchange::CqlLease4Exchange(const CqlConnection &connection)
    : CqlLeaseExchange(connection), address_(0) {
}

void
CqlLease4Exchange::createBindForInsert(const Lease4Ptr &lease, AnyArray &data) {
    if (!lease) {
        isc_throw(BadValue, "CqlLease4Exchange::createBindForInsert(): "
                            "Lease4 object is NULL");
    }
    // Store lease object to ensure it remains valid.
    lease_ = lease;
    // Set up the structures for the various components of the lease4
    // structure.

    try {
Andrei Pavel's avatar
Andrei Pavel committed
358
        // address: int
359
360
361
        // The address in the Lease structure is an IOAddress object.
        // Convert this to an integer for storage.
        address_ = static_cast<cass_int32_t>(lease->addr_.toUint32());
362

Andrei Pavel's avatar
Andrei Pavel committed
363
        // hwaddr: blob
364
365
366
367
368
369
370
371
372
373
374
375
376
        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
                isc_throw(DbOperationError,
                          "hardware address "
                              << lease_->hwaddr_->toText() << " of length "
                              << lease_->hwaddr_->hwaddr_.size()
                              << " exceeds maximum allowed length of "
                              << HWAddr::MAX_HWADDR_LEN);
            }
            hwaddr_ = lease_->hwaddr_->hwaddr_;
        } else {
            hwaddr_.clear();
        }
377

Andrei Pavel's avatar
Andrei Pavel committed
378
        // client_id: blob
379
380
381
382
383
        if (lease_->client_id_ && lease->client_id_->getClientId().size() > 0) {
            client_id_ = lease_->client_id_->getClientId();
        } else {
            client_id_.clear();
        }
384

385
        // valid lifetime: bigint
Razvan Becheriu's avatar
Razvan Becheriu committed
386
        valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
387

Andrei Pavel's avatar
Andrei Pavel committed
388
        // expire: bigint
389
390
391
392
393
394
395
        // The lease structure holds the client last transmission time
        /// (cltt_)
        // For convenience for external tools, this is converted to lease
        // expiry time (expire). The relationship is given by:
        // expire = cltt_ + valid_lft_
        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
                                           expire_);
396

Andrei Pavel's avatar
Andrei Pavel committed
397
        // subnet_id: int
398
        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
399

Andrei Pavel's avatar
Andrei Pavel committed
400
        // fqdn_fwd: boolean
401
        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
402

Andrei Pavel's avatar
Andrei Pavel committed
403
        // fqdn_rev: boolean
404
        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
405

Andrei Pavel's avatar
Andrei Pavel committed
406
        // hostname: varchar
407
408
409
410
411
412
413
414
        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
            isc_throw(BadValue,
                      "hostname " << lease_->hostname_ << " of length "
                                  << lease_->hostname_.size()
                                  << " exceeds maximum allowed length of "
                                  << HOSTNAME_MAX_LEN);
        }
        hostname_ = lease_->hostname_;
415

Andrei Pavel's avatar
Andrei Pavel committed
416
        // state: int
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
        state_ = static_cast<cass_int32_t>(lease_->state_);

        // Start with a fresh array.
        data.clear();
        data.add(&address_);
        data.add(&hwaddr_);
        data.add(&client_id_);
        data.add(&valid_lifetime_);
        data.add(&expire_);
        data.add(&subnet_id_);
        data.add(&fqdn_fwd_);
        data.add(&fqdn_rev_);
        data.add(&hostname_);
        data.add(&state_);

    } catch (const Exception &ex) {
Razvan Becheriu's avatar
Razvan Becheriu committed
433
434
435
        isc_throw(DbOperationError, "CqlLease4Exchange::createBindForInsert(): "
                  "could not create bind array from Lease4: " << lease_->addr_.toText()
                  << ", reason: " << ex.what());
Andrei Pavel's avatar
Andrei Pavel committed
436
    }
437
}
438

439
void
440
441
CqlLease4Exchange::createBindForUpdate(const Lease4Ptr &lease, AnyArray &data,
                                       StatementTag /* unused */) {
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
    if (!lease) {
        isc_throw(BadValue, "CqlLease4Exchange::createBindForUpdate(): "
                            "Lease4 object is NULL");
    }
    // Store lease object to ensure it remains valid.
    lease_ = lease;
    // Set up the structures for the various components of the lease4
    // structure.

    try {
        // address: int
        // The address in the Lease structure is an IOAddress object.
        // Convert this to an integer for storage.
        address_ = static_cast<cass_int32_t>(lease->addr_.toUint32());

        // hwaddr: blob
        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
                isc_throw(DbOperationError,
                          "hardware address "
                              << lease_->hwaddr_->toText() << " of length "
                              << lease_->hwaddr_->hwaddr_.size()
                              << " exceeds maximum allowed length of "
                              << HWAddr::MAX_HWADDR_LEN);
466
            }
467
468
469
470
471
472
473
474
475
476
477
            hwaddr_ = lease_->hwaddr_->hwaddr_;
        } else {
            hwaddr_.clear();
        }

        // client_id: blob
        if (lease_->client_id_ && lease->client_id_->getClientId().size() > 0) {
            client_id_ = lease_->client_id_->getClientId();
        } else {
            client_id_.clear();
        }
478

479
        // valid lifetime: bigint
Razvan Becheriu's avatar
Razvan Becheriu committed
480
        valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
481

482
483
484
485
486
487
488
489
        // expire: bigint
        // The lease structure holds the client last transmission time
        /// (cltt_)
        // For convenience for external tools, this is converted to lease
        // expiry time (expire). The relationship is given by:
        // expire = cltt_ + valid_lft_
        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
                                           expire_);
490

491
492
        // subnet_id: int
        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
493

494
495
        // fqdn_fwd: boolean
        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
496

497
498
499
500
501
502
503
504
505
506
        // fqdn_rev: boolean
        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;

        // hostname: varchar
        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
            isc_throw(BadValue,
                      "hostname " << lease_->hostname_ << " of length "
                                  << lease_->hostname_.size()
                                  << " exceeds maximum allowed length of "
                                  << HOSTNAME_MAX_LEN);
507
        }
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
        hostname_ = lease_->hostname_;

        // state: int
        state_ = static_cast<cass_int32_t>(lease_->state_);

        // Start with a fresh array.
        data.clear();
        data.add(&hwaddr_);
        data.add(&client_id_);
        data.add(&subnet_id_);
        data.add(&valid_lifetime_);
        data.add(&expire_);
        data.add(&fqdn_fwd_);
        data.add(&fqdn_rev_);
        data.add(&hostname_);
        data.add(&state_);
        data.add(&address_);

    } catch (const Exception &ex) {
        isc_throw(DbOperationError,
                  "CqlLease4Exchange::createBindUpdate(): "
                  "could not create bind array from Lease4: "
                      << lease_->addr_.toText() << ", reason: " << ex.what());
Andrei Pavel's avatar
Andrei Pavel committed
531
    }
532
533
534
}

void
535
536
CqlLease4Exchange::createBindForDelete(const IOAddress &address, AnyArray &data,
                                       StatementTag /* unused */) {
537
538
    // Set up the structures for the various components of the lease4
    // structure.
Andrei Pavel's avatar
Andrei Pavel committed
539

540
541
542
543
544
545
546
547
548
549
550
    try {
        // address: int
        address_ = static_cast<cass_int32_t>(address.toUint32());

        // Start with a fresh array.
        data.clear();
        data.add(&address_);

    } catch (const Exception &ex) {
        isc_throw(DbOperationError,
                  "CqlLease4Exchange::createBindForDelete(): "
Razvan Becheriu's avatar
Razvan Becheriu committed
551
552
                  "could not create bind array with address: "
                      << address_ << ", reason: " << ex.what());
553
    }
554
}
555

556
void
557
CqlLease4Exchange::createBindForSelect(AnyArray &data, StatementTag /* unused */) {
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

    // Start with a fresh array.
    data.clear();

    // address: blob
    data.add(&address_);

    // hwaddr: blob
    data.add(&hwaddr_);

    // client_id: blob
    data.add(&client_id_);

    // valid_lifetime: bigint
    data.add(&valid_lifetime_);

    // expire: bigint
    data.add(&expire_);

    // subnet_id: int
    data.add(&subnet_id_);

    // fqdn_fwd: boolean
    data.add(&fqdn_fwd_);

    // fqdn_rev: boolean
    data.add(&fqdn_rev_);

    // hostname: varchar
    data.add(&hostname_);

    // state: int
    data.add(&state_);
}

boost::any
CqlLease4Exchange::retrieve() {
    try {
        // Sanity checks
        if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
            isc_throw(BadValue,
                      "hardware address "
                          << HWAddr(hwaddr_, HTYPE_ETHER).toText()
                          << " of length " << hwaddr_.size()
                          << " exceeds maximum allowed length of "
                          << HWAddr::MAX_HWADDR_LEN);
        }
        if (client_id_.size() > ClientId::MAX_CLIENT_ID_LEN) {
            isc_throw(BadValue,
                      "client ID " << ClientId(client_id_).toText()
                                   << " of length " << client_id_.size()
                                   << " exceeds maximum allowed length of "
                                   << ClientId::MAX_CLIENT_ID_LEN);
        }
        if (hostname_.size() > HOSTNAME_MAX_LEN) {
            isc_throw(BadValue,
                      "hostname" << hostname_ << " of length "
                                 << hostname_.size()
                                 << " exceeds maximum allowed length of "
                                 << HOSTNAME_MAX_LEN);
        }

        time_t cltt = 0;
        CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);

        // Recreate the hardware address.
        HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));

        uint32_t addr4 = static_cast<uint32_t>(address_);

        Lease4Ptr result(new Lease4(addr4, hwaddr, client_id_.data(),
                                    client_id_.size(), valid_lifetime_, 0, 0,
                                    cltt, subnet_id_, fqdn_fwd_, fqdn_rev_,
                                    hostname_));

        result->state_ = state_;

635
        return (result);
636
637
    } catch (const Exception &ex) {
        isc_throw(DbOperationError,
Razvan Becheriu's avatar
Razvan Becheriu committed
638
                  "CqlLease4Exchange::retrieve(): "
639
640
641
642
643
644
                  "could not convert data to Lease4, reason: "
                      << ex.what());
    }
}

void
645
CqlLease4Exchange::getLeaseCollection(StatementTag &statement_tag, AnyArray &data,
646
647
648
649
650
651
652
653
654
655
656
657
658
                                      Lease4Collection &result) {
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
        .arg(statement_tag);

    AnyArray collection = executeSelect(connection_, data, statement_tag);

    // Transfer Lease4 objects to result.
    for (boost::any &element : collection) {
        result.push_back(boost::any_cast<Lease4Ptr>(element));
    }
}

void
659
CqlLease4Exchange::getLease(StatementTag &statement_tag, AnyArray &data,
660
661
662
663
664
665
666
667
668
                            Lease4Ptr &result) {
    // This particular method is called when only one or zero matches is
    // expected.
    Lease4Collection collection;
    getLeaseCollection(statement_tag, data, collection);

    // Return single record if present, else clear the lease.
    const size_t collection_size = collection.size();
    if (collection_size >= 2u) {
669
670
671
672
        isc_throw(MultipleRecords,
                  "CqlLease4Exchange::getLease(): multiple records were found in "
                  "the database where only one was expected for statement "
                  << statement_tag);
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
    } else if (collection_size == 0u) {
        result.reset();
    } else {
        result = *collection.begin();
    }
}

void
CqlLease4Exchange::getExpiredLeases(const size_t &max_leases,
                                    Lease4Collection &expired_leases) {
    // Set up the WHERE clause value
    cass_int32_t keep_state = Lease::STATE_EXPIRED_RECLAIMED;
    cass_int64_t timestamp = static_cast<cass_int64_t>(time(NULL));

    // If the number of leases is 0, we will return all leases. This is
    // achieved by setting the limit to a very high value.
    cass_int32_t limit = max_leases > 0u ?
                             static_cast<cass_int32_t>(max_leases) :
                             std::numeric_limits<cass_int32_t>::max();

    for (cass_int32_t state = Lease::STATE_DEFAULT;
         state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
        if (state == keep_state) {
            continue;
        }

        AnyArray data;
        data.add(&state);
        data.add(&timestamp);
        data.add(&limit);

        // Retrieve leases from the database.
        Lease4Collection temp_collection;
        getLeaseCollection(CqlLease4Exchange::GET_LEASE4_EXPIRE, data,
                           temp_collection);

        for (Lease4Ptr &lease : temp_collection) {
            expired_leases.push_back(lease);
        }
    }
}
714

715
/// @brief Exchange Lease6 information between Kea and CQL
716
///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
717
/// On any CQL operation, arrays of CQL BIND structures must be built to
Tomek Mrugalski's avatar
Tomek Mrugalski committed
718
/// describe the parameters in the prepared statements. Where information is
719
/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
Tomek Mrugalski's avatar
Tomek Mrugalski committed
720
/// structure is identical. This class handles the creation of that array.
721
///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
722
/// Owing to the CQL API, the process requires some intermediate variables
Tomek Mrugalski's avatar
Tomek Mrugalski committed
723
/// to hold things like data length etc. This object holds those variables.
724
///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
725
/// @note There are no unit tests for this class. It is tested indirectly
Tomek Mrugalski's avatar
Tomek Mrugalski committed
726
727
/// in all CqlLeaseMgr::xxx6() calls where it is used.
class CqlLease6Exchange : public CqlLeaseExchange {
728
729
730
public:
    /// @brief Constructor
    ///
Razvan Becheriu's avatar
Razvan Becheriu committed
731
    /// The initialization of the variables here is only to satisfy
732
733
734
735
    /// cppcheck - all variables are initialized/set in the methods before
    /// they are used.
    ///
    /// @param connection connection used for this query
736
737
    explicit CqlLease6Exchange(const CqlConnection &connection);

738
    /// @brief Create CQL_BIND objects for Lease6 Pointer
739
    ///
Razvan Becheriu's avatar
Razvan Becheriu committed
740
    /// Fills in the CQL_BIND array for sending data in the Lease6 object to
741
    /// the database. Used for INSERT statements.
742
743
744
    ///
    /// @param lease The lease information to be inserted
    /// @param data Lease info will be stored here in CQL format
745
746
    void createBindForInsert(const Lease6Ptr &lease, AnyArray &data);

747
    /// @brief Create CQL_BIND objects for Lease6 Pointer
748
    ///
Razvan Becheriu's avatar
Razvan Becheriu committed
749
    /// Fills in the CQL_BIND array for sending data in the Lease6 object to
750
    /// the database. Used for UPDATE statements.
751
752
753
754
755
    ///
    /// @param lease Updated lease information.
    /// @param data lease info in CQL format will be stored here
    /// @param statement_tag tag identifying the query (optional)
    void createBindForUpdate(const Lease6Ptr &lease, AnyArray &data,
756
757
758
759
                             StatementTag statement_tag = NULL);

    /// @brief Create CQL_BIND objects for Lease4 Pointer
    ///
Razvan Becheriu's avatar
Razvan Becheriu committed
760
    /// Fills in the CQL_BIND array for sending data in the Lease6 object to
761
    /// the database. Used for DELETE statements.
762
763
764
765
    ///
    /// @param address address of the lease to be deleted
    /// @param data lease info in CQL format will be stored here
    /// @param statement_tag tag identifying the query (optional)
Razvan Becheriu's avatar
Razvan Becheriu committed
766
767
    void createBindForDelete(const IOAddress &address,
                             AnyArray &data,
768
                             StatementTag statement_tag = NULL);
769

770
    /// @brief Create BIND array to receive data
771
    ///
772
    /// Creates a CQL_BIND array to receive Lease6 data from the database.
773
774
775
    ///
    /// @param data info returned by CQL will be stored here
    /// @param statement_tag tag identifying the query (optional)
Razvan Becheriu's avatar
Razvan Becheriu committed
776
777
    virtual void
    createBindForSelect(AnyArray &data, StatementTag statement_tag = NULL) override;
778

779
780
781
    /// @brief Retrieves the Lease6 object in Kea format
    ///
    /// @return C++ representation of the object being returned
Razvan Becheriu's avatar
Razvan Becheriu committed
782
    virtual boost::any retrieve() override;
783

784
785
786
787
788
789
    /// @brief Retrieves zero or more IPv6 leases
    ///
    /// @param statement_tag query to be executed
    /// @param data parameters for the query
    /// @param result this lease collection will be updated
    void getLeaseCollection(StatementTag &statement_tag, AnyArray &data,
790
791
                            Lease6Collection &result);

792
793
794
795
796
    /// @brief Retrieves one IPv6 lease
    ///
    /// @param statement_tag query to be executed
    /// @param data parameters for the query
    /// @param result pointer to the lease being returned (or null)
797
798
799
    void
    getLease(StatementTag &statement_tag, AnyArray &data, Lease6Ptr &result);

800
801
802
803
804
805
806
807
    /// @brief Returns expired leases.
    ///
    /// This method returns up to specified number (see max_leases) of
    /// expired leases.
    ///
    /// @param max_leases at most this number of leases will be returned
    /// @param expired_leases expired leases will be stored here
    void getExpiredLeases(const size_t &max_leases, Lease6Collection &expired_leases);
808
809
810
811
812
813
814
815
816
817
818
819

    /// @brief Cassandra statements
    static StatementMap tagged_statements_;

    /// @brief Statement tags definitions
    /// @{
    static constexpr StatementTag INSERT_LEASE6 = "INSERT_LEASE6";
    static constexpr StatementTag UPDATE_LEASE6 = "UPDATE_LEASE6";
    static constexpr StatementTag DELETE_LEASE6 = "DELETE_LEASE6";
    static constexpr StatementTag GET_LEASE6_EXPIRE = "GET_LEASE6_EXPIRE";
    static constexpr StatementTag GET_LEASE6_ADDR = "GET_LEASE6_ADDR";
    static constexpr StatementTag GET_LEASE6_DUID_IAID = "GET_LEASE6_DUID_IAID";
820
    static constexpr StatementTag GET_LEASE6_DUID_IAID_SUBID = "GET_LEASE6_DUID_IAID_SUBID";
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
    // @}

private:
    /// @brief Lease
    Lease6Ptr lease_;

    /// @brief IPv6 address
    std::string address_;

    /// @brief Preferred lifetime
    cass_int64_t pref_lifetime_;

    /// @brief Client identifier
    CassBlob duid_;

    /// @brief Identity association identifier
    cass_int32_t iaid_;

839
    /// @brief Lease type (NA, TA or PD)
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
    cass_int32_t lease_type_;

    /// @brief Prefix length
    cass_int32_t prefix_len_;

    /// @brief Hardware type
    cass_int32_t hwtype_;

    /// @brief Source of the hardware address
    cass_int32_t hwaddr_source_;
};  // CqlLease6Exchange

constexpr StatementTag CqlLease6Exchange::INSERT_LEASE6;
constexpr StatementTag CqlLease6Exchange::UPDATE_LEASE6;
constexpr StatementTag CqlLease6Exchange::DELETE_LEASE6;
constexpr StatementTag CqlLease6Exchange::GET_LEASE6_EXPIRE;
constexpr StatementTag CqlLease6Exchange::GET_LEASE6_ADDR;
constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID;
constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID_SUBID;

StatementMap CqlLease6Exchange::tagged_statements_ = {

862
863
864
    // Inserts new IPv6 lease
    {INSERT_LEASE6,
     {INSERT_LEASE6,
865
866
867
868
869
870
871
872
873
      "INSERT INTO lease6("
      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
      "hwaddr_source, state "
      ") VALUES ("
      "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
      ") "
      "IF NOT EXISTS "}},

874
875
876
    // Updates existing IPv6 lease
    {UPDATE_LEASE6,
     {UPDATE_LEASE6,
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
      "UPDATE lease6 SET "
      "valid_lifetime = ?, "
      "expire = ?, "
      "pref_lifetime = ?, "
      "duid = ?, "
      "iaid = ?, "
      "subnet_id = ?, "
      "lease_type = ?, "
      "prefix_len = ?, "
      "fqdn_fwd = ?, "
      "fqdn_rev = ?, "
      "hostname = ?, "
      "hwaddr = ?, "
      "hwtype = ?, "
      "hwaddr_source = ?, "
      "state = ? "
      "WHERE address = ? "
      "IF EXISTS "}},

896
897
898
    // Deletes existing IPv6 lease
    {DELETE_LEASE6,
     {DELETE_LEASE6,
899
900
901
902
      "DELETE FROM lease6 "
      "WHERE address = ? "
      "IF EXISTS "}},

903
904
905
    // Gets up to a certain number of expired IPv6 leases
    {GET_LEASE6_EXPIRE,
     {GET_LEASE6_EXPIRE,
906
907
908
909
910
911
912
913
914
915
      "SELECT "
      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
      "hwaddr_source, state "
      "FROM lease6 "
      "WHERE state = ? "
      "AND expire < ? "
      "LIMIT ? "
      "ALLOW FILTERING "}},

916
917
918
    // Gets an IPv6 lease with specified IPv4 address
    {GET_LEASE6_ADDR,
     {GET_LEASE6_ADDR,
919
920
921
922
923
924
925
926
927
      "SELECT "
      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
      "hwaddr_source, state "
      "FROM lease6 "
      "WHERE address = ? "
      "AND lease_type = ? "
      "ALLOW FILTERING "}},

928
929
930
    // Gets an IPv6 lease(s) with specified duid and iaid
    {GET_LEASE6_DUID_IAID,
     {GET_LEASE6_DUID_IAID,
931
932
933
934
935
936
937
938
939
      "SELECT "
      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
      "hwaddr_source, state "
      "FROM lease6 "
      "WHERE duid = ? AND iaid = ? "
      "AND lease_type = ? "
      "ALLOW FILTERING "}},

940
941
942
    // Gets an IPv6 lease with specified duid, iaid and subnet-id
    {GET_LEASE6_DUID_IAID_SUBID,
     {GET_LEASE6_DUID_IAID_SUBID,
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
      "SELECT "
      "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
      "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
      "hwaddr_source, state "
      "FROM lease6 "
      "WHERE duid = ? AND iaid = ? "
      "AND lease_type = ? "
      "AND subnet_id = ? "
      "ALLOW FILTERING "}},

};

CqlLease6Exchange::CqlLease6Exchange(const CqlConnection &connection)
    : CqlLeaseExchange(connection), pref_lifetime_(0), iaid_(0), lease_type_(0),
      prefix_len_(0), hwtype_(0), hwaddr_source_(0) {
}
959

960
961
962
void
CqlLease6Exchange::createBindForInsert(const Lease6Ptr &lease, AnyArray &data) {
    if (!lease) {
Razvan Becheriu's avatar
Razvan Becheriu committed
963
964
        isc_throw(BadValue, "CqlLease6Exchange::createBindForInsert(): "
                            "Lease6 object is NULL");
965
966
967
968
969
970
971
972
973
974
    }
    // Store lease object to ensure it remains valid.
    lease_ = lease;

    // Set up the structures for the various components of the lease4
    // structure.
    try {
        // address: varchar
        address_ = lease_->addr_.toText();
        if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
975
976
            isc_throw(BadValue, "address " << address_ << " of length " << address_.size()
                      << " exceeds maximum allowed length of " << ADDRESS6_TEXT_MAX_LEN);
977
978
        }

979
980
981
982
983
984
985
986
987
        // valid lifetime: bigint
        valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);

        // expire: bigint
        // The lease structure holds the client last transmission time
        // (cltt_)
        // For convenience for external tools, this is converted to lease
        // expiry time (expire). The relationship is given by:
        // expire = cltt_ + valid_lft_
988
        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_);
989
990
991
992
993
994
995
996
997

        // subnet_id: int
        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);

        // pref_lifetime: bigint
        pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);

        // duid: blob
        if (!lease_->duid_) {
998
999
            isc_throw(DbOperationError, "lease6 with address " << address_
                      << " is missing mandatory duid");
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
        }
        duid_ = lease_->duid_->getDuid();

        // iaid: int
        iaid_ = static_cast<cass_int32_t>(lease_->iaid_);

        // lease_type: int
        lease_type_ = static_cast<cass_int32_t>(lease_->type_);

        // prefix_len: int
        prefix_len_ = static_cast<cass_int32_t>(lease_->prefixlen_);

        // fqdn_fwd: boolean
        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;

        // fqdn_rev: boolean
        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;

        // hostname: varchar
        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
1020
1021
1022
            isc_throw(BadValue, "hostname" << lease_->hostname_ << " of length "
                      << lease_->hostname_.size() << " exceeds maximum allowed length of "
                      << HOSTNAME_MAX_LEN);
1023
1024
1025
1026
1027
1028
        }
        hostname_ = lease_->hostname_;

        // hwaddr: blob
        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
1029
1030
1031
                isc_throw(DbOperationError, "hardware address " << lease_->hwaddr_->toText()
                          << " of length " << lease_->hwaddr_->hwaddr_.size()
                          << " exceeds maximum allowed length of " << HWAddr::MAX_HWADDR_LEN);
1032
            }
1033
1034
1035
1036
            hwaddr_ = lease_->hwaddr_->hwaddr_;
        } else {
            hwaddr_.clear();
        }
1037

1038
1039
1040
1041
1042
1043
        // hwtype: int
        if (lease_->hwaddr_) {
            hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
        } else {
            hwtype_ = 0;
        }
1044

1045
1046
1047
1048
1049
1050
        // hwaddr_source: int
        if (lease_->hwaddr_) {
            hwaddr_source_ =
                static_cast<cass_int32_t>(lease_->hwaddr_->source_);
        } else {
            hwaddr_source_ = 0;
1051
        }
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077

        // state: int
        state_ = static_cast<cass_int32_t>(lease_->state_);

        // Start with a fresh array.
        data.clear();

        // Add them all to data.
        data.add(&address_);
        data.add(&valid_lifetime_);
        data.add(&expire_);
        data.add(&subnet_id_);
        data.add(&pref_lifetime_);
        data.add(&duid_);
        data.add(&iaid_);
        data.add(&lease_type_);
        data.add(&prefix_len_);
        data.add(&fqdn_fwd_);
        data.add(&fqdn_rev_);
        data.add(&hostname_);
        data.add(&hwaddr_);
        data.add(&hwtype_);
        data.add(&hwaddr_source_);
        data.add(&state_);

    } catch (const Exception &ex) {
1078
1079
1080
        isc_throw(DbOperationError, "CqlLease6Exchange::createBindForInsert(): "
                  "could not create bind array from Lease6: " << lease_->addr_.toText()
                  << ", reason: " << ex.what());
1081
    }
1082
1083
}

1084
1085
1086
void
CqlLease6Exchange::createBindForUpdate(const Lease6Ptr &lease, AnyArray &data,
                                       StatementTag /* unused */) {
1087
    if (!lease) {
Razvan Becheriu's avatar
Razvan Becheriu committed
1088
1089
        isc_throw(BadValue, "CqlLease6Exchange::createBindForUpdate(): "
                            "Lease6 object is NULL");
1090
1091
1092
    }
    // Store lease object to ensure it remains valid.
    lease_ = lease;
1093

1094
1095
1096
    // Set up the structures for the various components of the lease4
    // structure.
    try {
Andrei Pavel's avatar
Andrei Pavel committed
1097
        // address: varchar
1098
1099
1100
1101
1102
1103
1104
        address_ = lease_->addr_.toText();
        if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
            isc_throw(BadValue,
                      "address " << address_ << " of length " << address_.size()
                                 << " exceeds maximum allowed length of "
                                 << ADDRESS6_TEXT_MAX_LEN);
        }
1105

1106
1107
        // valid lifetime: bigint
        valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
1108

Andrei Pavel's avatar
Andrei Pavel committed
1109
        // expire: bigint
1110
1111
1112
1113
1114
1115
1116
        // The lease structure holds the client last transmission time
        // (cltt_)
        // For convenience for external tools, this is converted to lease
        // expiry time (expire). The relationship is given by:
        // expire = cltt_ + valid_lft_
        CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
                                           expire_);
1117

Andrei Pavel's avatar
Andrei Pavel committed
1118
        // subnet_id: int
1119
        subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
1120

Andrei Pavel's avatar
Andrei Pavel committed
1121
        // pref_lifetime: bigint
1122
        pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
1123

1124
1125
1126
1127
1128
1129
1130
        // duid: blob
        if (!lease_->duid_) {
            isc_throw(DbOperationError,
                      "lease6 with address " << address_
                                             << " is missing mandatory duid");
        }
        duid_ = lease_->duid_->getDuid();
1131

Andrei Pavel's avatar
Andrei Pavel committed
1132
        // iaid: int
1133
1134
1135
1136
        iaid_ = static_cast<cass_int32_t>(lease_->iaid_);

        // lease_type: int
        lease_type_ = static_cast<cass_int32_t>(lease_->type_);
1137

Andrei Pavel's avatar
Andrei Pavel committed
1138
        // prefix_len: int
1139
        prefix_len_ = static_cast<cass_int32_t>(lease_->prefixlen_);
1140

Andrei Pavel's avatar
Andrei Pavel committed
1141
        // fqdn_fwd: boolean
1142
        fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
1143

Andrei Pavel's avatar
Andrei Pavel committed
1144
        // fqdn_rev: boolean
1145
        fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
1146

Andrei Pavel's avatar
Andrei Pavel committed
1147
        // hostname: varchar
1148
1149
1150
1151
1152
1153
1154
1155
        if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
            isc_throw(BadValue,
                      "hostname" << lease_->hostname_ << " of length "
                                 << lease_->hostname_.size()
                                 << " exceeds maximum allowed length of "
                                 << HOSTNAME_MAX_LEN);
        }
        hostname_ = lease_->hostname_;
1156

Andrei Pavel's avatar
Andrei Pavel committed
1157
        // hwaddr: blob
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
        if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
            if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
                isc_throw(DbOperationError,
                          "hardware address "
                              << lease_->hwaddr_->toText() << " of length "
                              << lease_->hwaddr_->hwaddr_.size()
                              << " exceeds maximum allowed length of "
                              << HWAddr::MAX_HWADDR_LEN);
            }
            hwaddr_ = lease_->hwaddr_->hwaddr_;
        } else {
            hwaddr_.clear();
        }
1171

Andrei Pavel's avatar
Andrei Pavel committed
1172
        // hwtype: int
1173
1174
1175
1176
1177
        if (lease_->hwaddr_) {
            hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
        } else {
            hwtype_ = 0;
        }
1178

Andrei Pavel's avatar
Andrei Pavel committed
1179
        // hwaddr_source: int
1180
1181
1182
1183
1184
1185
        if (lease_->hwaddr_) {
            hwaddr_source_ =
                static_cast<cass_int32_t>(lease_->hwaddr_->source_);
        } else {
            hwaddr_source_ = 0;
        }
1186

Andrei Pavel's avatar
Andrei Pavel committed
1187
        // state: int
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
        state_ = static_cast<cass_int32_t>(lease_->state_);

        // Start with a fresh array.
        data.clear();

        // Add them all to data.
        data.add(&valid_lifetime_);
        data.add(&expire_);
        data.add(&pref_lifetime_);
        data.add(&duid_);
        data.add(&iaid_);
        data.add(&subnet_id_);
        data.add(&lease_type_);
        data.add(&prefix_len_);
        data.add(&fqdn_fwd_);
        data.add(&fqdn_rev_);
        data.add(&hostname_);
        data.add(&hwaddr_);
        data.add(&hwtype_);
        data.add(&hwaddr_source_);
        data.add(&state_);
        data.add(&address_);

    } catch (const Exception &ex) {
        isc_throw(DbOperationError,
                  "CqlLease6Exchange::createBindForUpdate(): "
                  "could not create bind array from Lease6: "
                      << lease_->addr_.toText() << ", reason: " << ex.what());
Andrei Pavel's avatar
Andrei Pavel committed
1216
    }
1217
}
1218

1219
void
1220
1221
CqlLease6Exchange::createBindForDelete(const IOAddress &address, AnyArray &data,
                                       StatementTag /* unused */) {
1222
1223
1224
1225

    // Set up the structures for the various components of the lease4
    // structure.
    try {
Razvan Becheriu's avatar
Razvan Becheriu committed
1226
        // address: varchar
1227
        address_ = address.toText();
1228

1229
1230
1231
        // Start with a fresh array.
        data.clear();
        data.add(&address_);
1232

1233
1234
1235
1236
1237
    } catch (const Exception &ex) {
        isc_throw(DbOperationError,
                  "CqlLease6Exchange::createBindForDelete(): "
                  "could not create bind array with address: "
                      << address_ << ", reason: " << ex.what());
Andrei Pavel's avatar
Andrei Pavel committed
1238
    }
1239
}
Andrei Pavel's avatar
Andrei Pavel committed
1240

1241
void
1242
CqlLease6Exchange::createBindForSelect(AnyArray &data, StatementTag /* unused */) {
1243

1244
1245
    // Start with a fresh array.
    data.clear();
1246

1247
1248
    // address: varchar
    data.add(&address_);
1249

1250
1251
    // valid_lifetime_: bigint
    data.add(&valid_lifetime_);
1252

1253
1254
    // expire: bigint
    data.add(&expire_);
1255

1256
1257
    // subnet_id: int
    data.add(&subnet_id_);
1258

1259
1260
    // pref_lifetime: bigint
    data.add(&pref_lifetime_);
Andrei Pavel's avatar
Andrei Pavel committed
1261

1262
1263
    // duid: blob
    data.add(&duid_);
1264

1265
1266
    // iaid: int
    data.add(&iaid_);
1267

1268
1269
    // lease_type: int
    data.add(&lease_type_);
1270

1271
1272
    // prefix_len: int
    data.add(&prefix_len_);
1273

1274
1275
    // fqdn_fwd: boolean
    data.add(&fqdn_fwd_);
1276

1277
1278
    // fqdn_rev: boolean
    data.add(&fqdn_rev_);
1279

1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
    // hostname: varchar
    data.add(&hostname_);

    // hwaddr: blob
    data.add(&hwaddr_);

    // hwtype: int
    data.add(&hwtype_);

    // hwaddr_source: int
    data.add(&hwaddr_source_);
1291

1292
1293
    // state: int
    data.add(&state_);
1294
1295
}

1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
boost::any
CqlLease6Exchange::retrieve() {
    try {
        // Sanity checks
        if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
            isc_throw(BadValue,
                      "address " << address_ << " of length " << address_.size()
                                 << " exceeds maximum allowed length of "
                                 << ADDRESS6_TEXT_MAX_LEN);
        }
        if (duid_.size() > DUID::MAX_DUID_LEN) {
            isc_throw(BadValue,
                      "duid " << DUID(duid_).toText() << " of length "
                              << duid_.size()
                              << " exceeds maximum allowed length of "
                              << DUID::MAX_DUID_LEN);
        }
        if (lease_type_ != Lease::TYPE_NA && lease_type_ != Lease::TYPE_TA &&
            lease_type_ != Lease::TYPE_PD) {
            isc_throw(BadValue,
                      "invalid lease type "
                          << lease_type_ << " for lease with address "
                          << address_ << ". Expected 0, 1 or 2.");
        }
        if (hostname_.size() > HOSTNAME_MAX_LEN) {
            isc_throw(BadValue,
                      "hostname " << hostname_ << " of length "
                                  << hostname_.size()
                                  << " exceeds maximum allowed length of "
                                  << HOSTNAME_MAX_LEN);
        }
        if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
            isc_throw(BadValue,
                      "hwaddr " << HWAddr(hwaddr_, hwtype_).toText(false)
                                << " of length " << hwaddr_.size()
                                << " exceeds maximum allowed length of "
                                << HWAddr::MAX_HWADDR_LEN);
        }
1334

1335
        IOAddress addr(address_);
1336

1337
1338
1339
1340
1341
1342
        DuidPtr duid(new DUID(duid_));
        HWAddrPtr hwaddr;
        if (hwaddr_.size()) {
            hwaddr.reset(new HWAddr(hwaddr_, hwtype_));
            hwaddr->source_ = hwaddr_source_;
        }
1343

1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
        // Create the lease and set the cltt (after converting from the
        // expire time retrieved from the database).
        Lease6Ptr result(
            new Lease6(static_cast<Lease::Type>(lease_type_), addr, duid, iaid_,
                       pref_lifetime_, valid_lifetime_, 0, 0, subnet_id_,
                       fqdn_fwd_, fqdn_rev_, hostname_, hwaddr, prefix_len_));

        time_t cltt = 0;
        CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
        result->cltt_ = cltt;
1354

1355
        result->state_ = state_;
Razvan Becheriu's avatar
Razvan Becheriu committed
1356
1357

        return (result);
1358
    } catch (const Exception &ex) {
Andrei Pavel's avatar
Andrei Pavel committed
1359
        isc_throw(DbOperationError,
1360
                  "CqlLease6Exchange::retrieve(): "
Razvan Becheriu's avatar
Razvan Becheriu committed
1361
                  "could not convert data to Lease6, reason: "
1362
                      << ex.what());
1363
    }
1364
1365
    return Lease6Ptr();
}
1366

1367
void
1368
CqlLease6Exchange::getLeaseCollection(StatementTag &statement_tag, AnyArray &data,
1369
1370
1371
1372
1373
                                      Lease6Collection &result) {
    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
        .arg(s