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

#include <config.h>
16
17
18
19

#include <asiolink/io_address.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/mysql_lease_mgr.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
20
#include <dhcpsrv/tests/test_utils.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
21
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
22
#include <exceptions/exceptions.h>
23

24
25
#include <gtest/gtest.h>

26
#include <algorithm>
27
28
#include <iostream>
#include <sstream>
29
#include <string>
30
#include <utility>
31
32
33
34

using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
35
using namespace isc::dhcp::test;
36
37
38
39
using namespace std;

namespace {

40
// This holds statements to create and destroy the schema.
41
#include "schema_mysql_copy.h"
42

43
// Connection strings.
Stephen Morris's avatar
Stephen Morris committed
44
// Database: keatest
45
// Host: localhost
Stephen Morris's avatar
Stephen Morris committed
46
47
// Username: keatest
// Password: keatest
48
49
const char* VALID_TYPE = "type=mysql";
const char* INVALID_TYPE = "type=unknown";
50
const char* VALID_NAME = "name=keatest";
51
52
53
54
55
56
57
58
59
60
61
62
const char* INVALID_NAME = "name=invalidname";
const char* VALID_HOST = "host=localhost";
const char* INVALID_HOST = "host=invalidhost";
const char* VALID_USER = "user=keatest";
const char* INVALID_USER = "user=invaliduser";
const char* VALID_PASSWORD = "password=keatest";
const char* INVALID_PASSWORD = "password=invalid";

// Given a combination of strings above, produce a connection string.
string connectionString(const char* type, const char* name, const char* host,
                        const char* user, const char* password) {
    const string space = " ";
63
64
65
66
67
    string result = "";

    if (type != NULL) {
        result += string(type);
    }
68
    if (name != NULL) {
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
        if (! result.empty()) {
            result += space;
        }
        result += string(name);
    }

    if (host != NULL) {
        if (! result.empty()) {
            result += space;
        }
        result += string(host);
    }

    if (user != NULL) {
        if (! result.empty()) {
            result += space;
        }
        result += string(user);
    }

    if (password != NULL) {
        if (! result.empty()) {
            result += space;
        }
        result += string(password);
    }

    return (result);
97
98
99
}

// Return valid connection string
Stephen Morris's avatar
Stephen Morris committed
100
101
string
validConnectionString() {
102
103
104
105
    return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
                             VALID_USER, VALID_PASSWORD));
}

106
107
108
// @brief Clear everything from the database
//
// There is no error checking in this code: if something fails, one of the
109
// tests will (should) fall over.
110
void destroySchema() {
111
    MySqlHolder mysql;
112
113

    // Open database
114
    (void) mysql_real_connect(mysql, "localhost", "keatest",
115
116
117
118
                              "keatest", "keatest", 0, NULL, 0);

    // Get rid of everything in it.
    for (int i = 0; destroy_statement[i] != NULL; ++i) {
119
        (void) mysql_query(mysql, destroy_statement[i]);
120
121
122
123
124
125
126
127
128
129
    }
}

// @brief Create the Schema
//
// Creates all the tables in what is assumed to be an empty database.
//
// There is no error checking in this code: if it fails, one of the tests
// will fall over.
void createSchema() {
130
    MySqlHolder mysql;
131
132

    // Open database
133
    (void) mysql_real_connect(mysql, "localhost", "keatest",
134
135
                              "keatest", "keatest", 0, NULL, 0);

136
    // Execute creation statements.
137
    for (int i = 0; create_statement[i] != NULL; ++i) {
138
        (void) mysql_query(mysql, create_statement[i]);
139
140
141
    }
}

142
143
144
145
/// @brief Test fixture class for testing MySQL Lease Manager
///
/// Opens the database prior to each test and closes it afterwards.
/// All pending transactions are deleted prior to closure.
146

147
class MySqlLeaseMgrTest : public GenericLeaseMgrTest {
148
public:
149
150
151
    /// @brief Constructor
    ///
    /// Deletes everything from the database and opens it.
152
    MySqlLeaseMgrTest() {
153

154
        // Ensure schema is the correct one.
155
156
        destroySchema();
        createSchema();
157

158
        // Connect to the database
159
160
161
162
163
164
165
166
167
168
        try {
            LeaseMgrFactory::create(validConnectionString());
        } catch (...) {
            std::cerr << "*** ERROR: unable to open database. The test\n"
                         "*** environment is broken and must be fixed before\n"
                         "*** the MySQL tests will run correctly.\n"
                         "*** The reason for the problem is described in the\n"
                         "*** accompanying exception output.\n";
            throw;
        }
Stephen Morris's avatar
Stephen Morris committed
169
170
171
        lmptr_ = &(LeaseMgrFactory::instance());
    }

172
173
    /// @brief Destructor
    ///
174
175
    /// Rolls back all pending transactions.  The deletion of lmptr_ will close
    /// the database.  Then reopen it and delete everything created by the test.
176
177
178
    virtual ~MySqlLeaseMgrTest() {
        lmptr_->rollback();
        LeaseMgrFactory::destroy();
179
        destroySchema();
180
181
    }

182
183
184
185
    /// @brief Reopen the database
    ///
    /// Closes the database and re-open it.  Anything committed should be
    /// visible.
Stephen Morris's avatar
Stephen Morris committed
186
187
    void reopen() {
        LeaseMgrFactory::destroy();
188
189
        LeaseMgrFactory::create(validConnectionString());
        lmptr_ = &(LeaseMgrFactory::instance());
190
191
192
193
    }

};

194
195
196
197
198
199
/// @brief Check that database can be opened
///
/// This test checks if the MySqlLeaseMgr can be instantiated.  This happens
/// only if the database can be opened.  Note that this is not part of the
/// MySqlLeaseMgr test fixure set.  This test checks that the database can be
/// opened: the fixtures assume that and check basic operations.
200
201

TEST(MySqlOpenTest, OpenDatabase) {
202
203
204
205
206
207
208

    // Schema needs to be created for the test to work.
    destroySchema();
    createSchema();

    // Check that lease manager open the database opens correctly and tidy up.
    //  If it fails, print the error message.
209
210
211
212
213
214
    try {
        LeaseMgrFactory::create(validConnectionString());
        EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
        LeaseMgrFactory::destroy();
    } catch (const isc::Exception& ex) {
        FAIL() << "*** ERROR: unable to open database, reason:\n"
215
               << "    " << ex.what() << "\n"
216
217
218
219
               << "*** The test environment is broken and must be fixed\n"
               << "*** before the MySQL tests will run correctly.\n";
    }

220
221
222
    // Check that attempting to get an instance of the lease manager when
    // none is set throws an exception.
    EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
223

224
225
226
    // Check that wrong specification of backend throws an exception.
    // (This is really a check on LeaseMgrFactory, but is convenient to
    // perform here.)
227
228
229
    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
        NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
        InvalidParameter);
230
    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
231
        INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
232
        InvalidType);
233
234

    // Check that invalid login data causes an exception.
235
    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
236
237
        VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
        DbOpenError);
238
    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
239
240
        VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
        DbOpenError);
241
    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
242
243
        VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
        DbOpenError);
244
    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
245
246
247
        VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
        DbOpenError);

248
249
250
251
    // Check for missing parameters
    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
        VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
        NoDatabaseName);
252
253
254

    // Tidy up after the test
    destroySchema();
255
256
}

257
258
259
260
/// @brief Check the getType() method
///
/// getType() returns a string giving the type of the backend, which should
/// always be "mysql".
261
TEST_F(MySqlLeaseMgrTest, getType) {
262
263
264
    EXPECT_EQ(std::string("mysql"), lmptr_->getType());
}

265
266
267
268
269
270
271
272
273
274
275
/// @brief Check conversion functions
///
/// The server works using cltt and valid_filetime.  In the database, the
/// information is stored as expire_time and valid-lifetime, which are
/// related by
///
/// expire_time = cltt + valid_lifetime
///
/// This test checks that the conversion is correct.  It does not check that the
/// data is entered into the database correctly, only that the MYSQL_TIME
/// structure used for the entry is correctly set up.
276
TEST_F(MySqlLeaseMgrTest, checkTimeConversion) {
277
278
    const time_t cltt = time(NULL);
    const uint32_t valid_lft = 86400;       // 1 day
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
    struct tm tm_expire;
    MYSQL_TIME mysql_expire;

    // Work out what the broken-down time will be for one day
    // after the current time.
    time_t expire_time = cltt + valid_lft;
    (void) localtime_r(&expire_time, &tm_expire);

    // Convert to the database time
    MySqlLeaseMgr::convertToDatabaseTime(cltt, valid_lft, mysql_expire);

    // Are the times the same?
    EXPECT_EQ(tm_expire.tm_year + 1900, mysql_expire.year);
    EXPECT_EQ(tm_expire.tm_mon + 1,  mysql_expire.month);
    EXPECT_EQ(tm_expire.tm_mday, mysql_expire.day);
    EXPECT_EQ(tm_expire.tm_hour, mysql_expire.hour);
    EXPECT_EQ(tm_expire.tm_min, mysql_expire.minute);
    EXPECT_EQ(tm_expire.tm_sec, mysql_expire.second);
    EXPECT_EQ(0, mysql_expire.second_part);
    EXPECT_EQ(0, mysql_expire.neg);
299

300
301
    // Convert back
    time_t converted_cltt = 0;
302
    MySqlLeaseMgr::convertFromDatabaseTime(mysql_expire, valid_lft, converted_cltt);
303
304
305
    EXPECT_EQ(cltt, converted_cltt);
}

306

307
/// @brief Check getName() returns correct database name
308
309
310
311
TEST_F(MySqlLeaseMgrTest, getName) {
    EXPECT_EQ(std::string("keatest"), lmptr_->getName());
}

312
/// @brief Check that getVersion() returns the expected version
313
TEST_F(MySqlLeaseMgrTest, checkVersion) {
314
    // Check version
315
316
    pair<uint32_t, uint32_t> version;
    ASSERT_NO_THROW(version = lmptr_->getVersion());
317
318
    EXPECT_EQ(CURRENT_VERSION_VERSION, version.first);
    EXPECT_EQ(CURRENT_VERSION_MINOR, version.second);
319
320
}

321
322
323
324
////////////////////////////////////////////////////////////////////////////////
/// LEASE4 /////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

325
/// @brief Basic Lease4 Checks
326
///
327
328
/// Checks that the addLease, getLease4 (by address) and deleteLease (with an
/// IPv4 address) works.
329
TEST_F(MySqlLeaseMgrTest, basicLease4) {
330
331
    testBasicLease4();
}
332

333
/// @brief Lease4 update tests
334
///
335
336
337
/// Checks that we are able to update a lease in the database.
TEST_F(MySqlLeaseMgrTest, updateLease4) {
    testUpdateLease4();
338
339
}

340
/// @brief Check GetLease4 methods - access by Hardware Address
341
342
343
TEST_F(MySqlLeaseMgrTest, getLease4HWAddr1) {
    testGetLease4HWAddr1();
}
344

345
346
347
/// @brief Check GetLease4 methods - access by Hardware Address
TEST_F(MySqlLeaseMgrTest, getLease4HWAddr2) {
    testGetLease4HWAddr2();
348
}
349

350
351
352
353
// @brief Get lease4 by hardware address (2)
//
// Check that the system can cope with getting a hardware address of
// any size.
354
355
TEST_F(MySqlLeaseMgrTest, getLease4HWAddrSize) {
    testGetLease4HWAddrSize();
356
357
}

358
359
360
361
/// @brief Check GetLease4 methods - access by Hardware Address & Subnet ID
///
/// Adds leases to the database and checks that they can be accessed via
/// a combination of hardware address and subnet ID
362
TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) {
363
    testGetLease4HWAddrSubnetId();
364
365
366
367
368
369
}

// @brief Get lease4 by hardware address and subnet ID (2)
//
// Check that the system can cope with getting a hardware address of
// any size.
370
371
372
TEST_F(MySqlLeaseMgrTest, getLease4HWAddrSubnetIdSize) {
    testGetLease4HWAddrSubnetIdSize();
}
373

374
375
376
// This test was derived from memfile.
TEST_F(MySqlLeaseMgrTest, getLease4ClientId) {
    testGetLease4ClientId();
377
378
}

379
380
381
382
/// @brief Check GetLease4 methods - access by Client ID
///
/// Adds leases to the database and checks that they can be accessed via
/// the Client ID.
383
384
TEST_F(MySqlLeaseMgrTest, getLease4ClientId2) {
    testGetLease4ClientId2();
385
386
}

387
388
389
390
// @brief Get Lease4 by client ID (2)
//
// Check that the system can cope with a client ID of any size.
TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSize) {
391
    testGetLease4ClientIdSize();
392
393
}

394
395
396
397
/// @brief Check GetLease4 methods - access by Client ID & Subnet ID
///
/// Adds leases to the database and checks that they can be accessed via
/// a combination of client and subnet IDs.
398
TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSubnetId) {
399
    testGetLease4ClientIdSubnetId();
400
401
}

402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/// @brief Basic Lease4 Checks
///
/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id),
/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
/// (client-id is optional and may not be present)
TEST_F(MySqlLeaseMgrTest, lease4NullClientId) {
    testLease4NullClientId();
}

/// @brief Verify that too long hostname for Lease4 is not accepted.
///
/// Checks that the it is not possible to create a lease when the hostname
/// length exceeds 255 characters.
TEST_F(MySqlLeaseMgrTest, lease4InvalidHostname) {
    testLease4InvalidHostname();
}

////////////////////////////////////////////////////////////////////////////////
/// LEASE6 /////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

// Test checks whether simple add, get and delete operations are possible
// on Lease6
TEST_F(MySqlLeaseMgrTest, testAddGetDelete6) {
    testAddGetDelete6(false);
}

/// @brief Basic Lease6 Checks
///
/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
/// IPv6 address) works.
TEST_F(MySqlLeaseMgrTest, basicLease6) {
    testBasicLease6();
}

/// @brief Verify that too long hostname for Lease6 is not accepted.
///
/// Checks that the it is not possible to create a lease when the hostname
/// length exceeds 255 characters.
TEST_F(MySqlLeaseMgrTest, lease6InvalidHostname) {
    testLease6InvalidHostname();
}

445
446
447
/// @brief Check GetLease6 methods - access by DUID/IAID
///
/// Adds leases to the database and checks that they can be accessed via
448
/// a combination of DUID and IAID.
449
TEST_F(MySqlLeaseMgrTest, getLeases6DuidIaid) {
450
    testGetLeases6DuidIaid();
451
452
}

453
454
455
// Check that the system can cope with a DUID of allowed size.
TEST_F(MySqlLeaseMgrTest, getLeases6DuidSize) {
    testGetLeases6DuidSize();
456
457
}

458
459
460
461
462
463
464
/// @brief Check that getLease6 methods discriminate by lease type.
///
/// Adds six leases, two per lease type all with the same duid and iad but
/// with alternating subnet_ids.
/// It then verifies that all of getLeases6() method variants correctly
/// discriminate between the leases based on lease type alone.
TEST_F(MySqlLeaseMgrTest, lease6LeaseTypeCheck) {
465
    testLease6LeaseTypeCheck();
466
467
}

468
469
470
471
/// @brief Check GetLease6 methods - access by DUID/IAID/SubnetID
///
/// Adds leases to the database and checks that they can be accessed via
/// a combination of DIUID and IAID.
472
TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetId) {
473
    testGetLease6DuidIaidSubnetId();
474
475
}

476
// Test checks that getLease6() works with different DUID sizes
477
TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
478
    testGetLease6DuidIaidSubnetIdSize();
479
480
}

481
482
483
/// @brief Lease6 update tests
///
/// Checks that we are able to update a lease in the database.
484
TEST_F(MySqlLeaseMgrTest, updateLease6) {
485
    testUpdateLease6();
486
487
}

488
}; // Of anonymous namespace