summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc')
-rw-r--r--src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc4145
1 files changed, 4145 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
new file mode 100644
index 0000000..e278eb7
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
@@ -0,0 +1,4145 @@
+// Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <database/database_connection.h>
+#include <database/db_exceptions.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcpsrv_exceptions.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
+#include <dhcpsrv/testutils/test_utils.h>
+#include <exceptions/exceptions.h>
+#include <stats/stats_mgr.h>
+#include <testutils/gtest_utils.h>
+
+#include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+#include <limits>
+#include <sstream>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::db;
+namespace ph = std::placeholders;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+// IPv4 and IPv6 addresses used in the tests
+const char* ADDRESS4[] = {
+ "192.0.2.0", "192.0.2.1", "192.0.2.2", "192.0.2.3",
+ "192.0.2.4", "192.0.2.5", "192.0.2.6", "192.0.2.7",
+ NULL
+};
+const char* ADDRESS6[] = {
+ "2001:db8::0", "2001:db8::1", "2001:db8::2", "2001:db8::3",
+ "2001:db8::4", "2001:db8::5", "2001:db8::6", "2001:db8::7",
+ NULL
+};
+
+// Lease types that correspond to ADDRESS6 leases
+static const Lease::Type LEASETYPE6[] = {
+ Lease::TYPE_NA, Lease::TYPE_TA, Lease::TYPE_PD, Lease::TYPE_NA,
+ Lease::TYPE_TA, Lease::TYPE_PD, Lease::TYPE_NA, Lease::TYPE_TA
+};
+
+GenericLeaseMgrTest::GenericLeaseMgrTest()
+ : lmptr_(NULL) {
+ // Initialize address strings and IOAddresses
+ for (int i = 0; ADDRESS4[i] != NULL; ++i) {
+ string addr(ADDRESS4[i]);
+ straddress4_.push_back(addr);
+ IOAddress ioaddr(addr);
+ ioaddress4_.push_back(ioaddr);
+ }
+
+ for (int i = 0; ADDRESS6[i] != NULL; ++i) {
+ string addr(ADDRESS6[i]);
+ straddress6_.push_back(addr);
+ IOAddress ioaddr(addr);
+ ioaddress6_.push_back(ioaddr);
+
+ /// Let's create different lease types. We use LEASETYPE6 values as
+ /// a template
+ leasetype6_.push_back(LEASETYPE6[i]);
+ }
+}
+
+GenericLeaseMgrTest::~GenericLeaseMgrTest() {
+ // Does nothing. The derived classes are expected to clean up, i.e.
+ // remove the lmptr_ pointer.
+}
+
+Lease4Ptr
+GenericLeaseMgrTest::initializeLease4(std::string address) {
+ Lease4Ptr lease(new Lease4());
+
+ // Set the address of the lease
+ lease->addr_ = IOAddress(address);
+
+ // Set other parameters. For historical reasons, address 0 is not used.
+ if (address == straddress4_[0]) {
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x08), HTYPE_ETHER));
+ lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x42)));
+ lease->valid_lft_ = 8677;
+ lease->cltt_ = 168256;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 23;
+ lease->fqdn_rev_ = true;
+ lease->fqdn_fwd_ = false;
+ lease->hostname_ = "myhost.example.com.";
+
+ } else if (address == straddress4_[1]) {
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x19), HTYPE_ETHER));
+ lease->client_id_ = ClientIdPtr(
+ new ClientId(vector<uint8_t>(8, 0x53)));
+ lease->valid_lft_ = 3677;
+ lease->cltt_ = 123456;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 73;
+ lease->fqdn_rev_ = true;
+ lease->fqdn_fwd_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ } else if (address == straddress4_[2]) {
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x2a), HTYPE_ETHER));
+ lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x64)));
+ lease->valid_lft_ = 5412;
+ lease->cltt_ = 234567;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 73; // Same as lease 1
+ lease->fqdn_rev_ = false;
+ lease->fqdn_fwd_ = false;
+ lease->hostname_ = "";
+
+ } else if (address == straddress4_[3]) {
+ // Hardware address same as lease 1.
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x19), HTYPE_ETHER));
+ lease->client_id_ = ClientIdPtr(
+ new ClientId(vector<uint8_t>(8, 0x75)));
+
+ // The times used in the next tests are deliberately restricted - we
+ // should be able to cope with valid lifetimes up to 0xffffffff.
+ // However, this will lead to overflows.
+ // @TODO: test overflow conditions when code has been fixed.
+ lease->valid_lft_ = 7000;
+ lease->cltt_ = 234567;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 37;
+ lease->fqdn_rev_ = true;
+ lease->fqdn_fwd_ = true;
+ lease->hostname_ = "otherhost.example.com.";
+
+ } else if (address == straddress4_[4]) {
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x4c), HTYPE_ETHER));
+ // Same ClientId as straddr4_[1]
+ lease->client_id_ = ClientIdPtr(
+ new ClientId(vector<uint8_t>(8, 0x53))); // Same as lease 1
+ lease->valid_lft_ = 7736;
+ lease->cltt_ = 222456;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 85;
+ lease->fqdn_rev_ = true;
+ lease->fqdn_fwd_ = true;
+ lease->hostname_ = "otherhost.example.com.";
+
+ } else if (address == straddress4_[5]) {
+ // Same as lease 1
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x19), HTYPE_ETHER));
+ // Same ClientId and IAID as straddress4_1
+ lease->client_id_ = ClientIdPtr(
+ new ClientId(vector<uint8_t>(8, 0x53))); // Same as lease 1
+ lease->valid_lft_ = 7832;
+ lease->cltt_ = 227476;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 175;
+ lease->fqdn_rev_ = false;
+ lease->fqdn_fwd_ = false;
+ lease->hostname_ = "otherhost.example.com.";
+ lease->setContext(Element::fromJSON("{ \"foo\": true }"));
+
+ } else if (address == straddress4_[6]) {
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x6e), HTYPE_ETHER));
+ // Same ClientId as straddress4_1
+ lease->client_id_ = ClientIdPtr(
+ new ClientId(vector<uint8_t>(8, 0x53))); // Same as lease 1
+ lease->valid_lft_ = 1832;
+ lease->cltt_ = 627476;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 112;
+ lease->fqdn_rev_ = false;
+ lease->fqdn_fwd_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ } else if (address == straddress4_[7]) {
+ lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(), HTYPE_ETHER)); // Empty
+ lease->client_id_ = ClientIdPtr(
+ new ClientId(vector<uint8_t>(8, 0x77)));
+ lease->valid_lft_ = 7975;
+ lease->cltt_ = 213876;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 19;
+ lease->fqdn_rev_ = true;
+ lease->fqdn_fwd_ = true;
+ lease->hostname_ = "myhost.example.com.";
+ lease->setContext(Element::fromJSON("{ \"bar\": false }"));
+
+ } else {
+ // Unknown address, return an empty pointer.
+ lease.reset();
+
+ }
+
+ return (lease);
+}
+
+Lease6Ptr
+GenericLeaseMgrTest::initializeLease6(std::string address) {
+ Lease6Ptr lease(new Lease6());
+
+ // Set the address of the lease
+ lease->addr_ = IOAddress(address);
+
+ // Set other parameters. For historical reasons, address 0 is not used.
+ if (address == straddress6_[0]) {
+ lease->type_ = leasetype6_[0];
+ lease->prefixlen_ = 4;
+ lease->iaid_ = 142;
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x77)));
+ lease->preferred_lft_ = 900;
+ lease->valid_lft_ = 8677;
+ lease->cltt_ = 168256;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 23;
+ lease->fqdn_fwd_ = true;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ } else if (address == straddress6_[1]) {
+ lease->type_ = leasetype6_[1];
+ lease->prefixlen_ = 0;
+ lease->iaid_ = 42;
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ lease->preferred_lft_ = 3600;
+ lease->valid_lft_ = 3677;
+ lease->cltt_ = 123456;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 73;
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ } else if (address == straddress6_[2]) {
+ lease->type_ = leasetype6_[2];
+ lease->prefixlen_ = 7;
+ lease->iaid_ = 89;
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x3a)));
+ lease->preferred_lft_ = 1800;
+ lease->valid_lft_ = 5412;
+ lease->cltt_ = 234567;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 73; // Same as lease 1
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = false;
+ lease->hostname_ = "myhost.example.com.";
+
+ } else if (address == straddress6_[3]) {
+ lease->type_ = leasetype6_[3];
+ lease->prefixlen_ = 28;
+ lease->iaid_ = 0xfffffffe;
+ vector<uint8_t> duid;
+ for (uint8_t i = 31; i < 126; ++i) {
+ duid.push_back(i);
+ }
+ lease->duid_ = DuidPtr(new DUID(duid));
+
+ // The times used in the next tests are deliberately restricted - we
+ // should be able to cope with valid lifetimes up to 0xffffffff.
+ // However, this will lead to overflows.
+ // @TODO: test overflow conditions when code has been fixed.
+ lease->preferred_lft_ = 7200;
+ lease->valid_lft_ = 7000;
+ lease->cltt_ = 234567;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 37;
+ lease->fqdn_fwd_ = true;
+ lease->fqdn_rev_ = false;
+ lease->hostname_ = "myhost.example.com.";
+
+ } else if (address == straddress6_[4]) {
+ // Same DUID and IAID as straddress6_1
+ lease->type_ = leasetype6_[4];
+ lease->prefixlen_ = 15;
+ lease->iaid_ = 42;
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ lease->preferred_lft_ = 4800;
+ lease->valid_lft_ = 7736;
+ lease->cltt_ = 222456;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 671;
+ lease->fqdn_fwd_ = true;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "otherhost.example.com.";
+
+ } else if (address == straddress6_[5]) {
+ // Same DUID and IAID as straddress6_1
+ lease->type_ = leasetype6_[5];
+ lease->prefixlen_ = 24;
+ lease->iaid_ = 42; // Same as lease 4
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ // Same as lease 4
+ lease->preferred_lft_ = 5400;
+ lease->valid_lft_ = 7832;
+ lease->cltt_ = 227476;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 175;
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "hostname.example.com.";
+ lease->setContext(Element::fromJSON("{ \"foo\": true }"));
+
+ } else if (address == straddress6_[6]) {
+ // Same DUID as straddress6_1
+ lease->type_ = leasetype6_[6];
+ lease->prefixlen_ = 24;
+ lease->iaid_ = 93;
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ // Same as lease 4
+ lease->preferred_lft_ = 5400;
+ lease->valid_lft_ = 1832;
+ lease->cltt_ = 627476;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 112;
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "hostname.example.com.";
+
+ } else if (address == straddress6_[7]) {
+ // Same IAID as straddress6_1
+ lease->type_ = leasetype6_[7];
+ lease->prefixlen_ = 24;
+ lease->iaid_ = 42;
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0xe5)));
+ lease->preferred_lft_ = 5600;
+ lease->valid_lft_ = 7975;
+ lease->cltt_ = 213876;
+ lease->updateCurrentExpirationTime();
+ lease->subnet_id_ = 19;
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "hostname.example.com.";
+ lease->setContext(Element::fromJSON("{ \"bar\": false }"));
+
+ } else {
+ // Unknown address, return an empty pointer.
+ lease.reset();
+
+ }
+
+ return (lease);
+}
+
+template <typename T>
+void GenericLeaseMgrTest::checkLeasesDifferent(const std::vector<T>& leases) const {
+
+ // Check they were created
+ for (size_t i = 0; i < leases.size(); ++i) {
+ ASSERT_TRUE(leases[i]);
+ }
+
+ // Check they are different
+ for (size_t i = 0; i < (leases.size() - 1); ++i) {
+ for (size_t j = (i + 1); j < leases.size(); ++j) {
+ stringstream s;
+ s << "Comparing leases " << i << " & " << j << " for equality";
+ SCOPED_TRACE(s.str());
+ EXPECT_TRUE(*leases[i] != *leases[j]);
+ }
+ }
+}
+
+vector<Lease4Ptr>
+GenericLeaseMgrTest::createLeases4() {
+
+ // Create leases for each address
+ vector<Lease4Ptr> leases;
+ for (size_t i = 0; i < straddress4_.size(); ++i) {
+ leases.push_back(initializeLease4(straddress4_[i]));
+ }
+ EXPECT_EQ(8, leases.size());
+
+ // Check all were created and that they are different.
+ checkLeasesDifferent(leases);
+
+ return (leases);
+}
+
+vector<Lease6Ptr>
+GenericLeaseMgrTest::createLeases6() {
+
+ // Create leases for each address
+ vector<Lease6Ptr> leases;
+ for (size_t i = 0; i < straddress6_.size(); ++i) {
+ leases.push_back(initializeLease6(straddress6_[i]));
+ }
+ EXPECT_EQ(8, leases.size());
+
+ // Check all were created and that they are different.
+ checkLeasesDifferent(leases);
+
+ return (leases);
+}
+
+void
+GenericLeaseMgrTest::testGetLease4ClientId() {
+ // Let's initialize a specific lease ...
+ Lease4Ptr lease = initializeLease4(straddress4_[1]);
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ Lease4Collection returned = lmptr_->getLease4(*lease->client_id_);
+
+ ASSERT_EQ(1, returned.size());
+ // We should retrieve our lease...
+ detailCompareLease(lease, *returned.begin());
+ lease = initializeLease4(straddress4_[2]);
+ returned = lmptr_->getLease4(*lease->client_id_);
+
+ ASSERT_EQ(0, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLease4NullClientId() {
+ // Let's initialize a specific lease ... But this time
+ // We keep its client id for further lookup and
+ // We clearly 'reset' it ...
+ Lease4Ptr leaseA = initializeLease4(straddress4_[4]);
+ ClientIdPtr client_id = leaseA->client_id_;
+ leaseA->client_id_ = ClientIdPtr();
+ ASSERT_TRUE(lmptr_->addLease(leaseA));
+
+ Lease4Collection returned = lmptr_->getLease4(*client_id);
+ // Shouldn't have our previous lease ...
+ ASSERT_TRUE(returned.empty());
+
+ // Add another lease with the non-NULL client id, and make sure that the
+ // lookup will not break due to existence of both leases with non-NULL and
+ // NULL client ids.
+ Lease4Ptr leaseB = initializeLease4(straddress4_[0]);
+ // Shouldn't throw any null pointer exception
+ ASSERT_TRUE(lmptr_->addLease(leaseB));
+ // Try to get the lease.
+ returned = lmptr_->getLease4(*client_id);
+ ASSERT_TRUE(returned.empty());
+
+ // Let's make it more interesting and add another lease with NULL client id.
+ Lease4Ptr leaseC = initializeLease4(straddress4_[5]);
+ leaseC->client_id_.reset();
+ ASSERT_TRUE(lmptr_->addLease(leaseC));
+ returned = lmptr_->getLease4(*client_id);
+ ASSERT_TRUE(returned.empty());
+
+ // But getting the lease with non-NULL client id should be successful.
+ returned = lmptr_->getLease4(*leaseB->client_id_);
+ ASSERT_EQ(1, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testLease4NullClientId() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Let's clear client-id pointers
+ leases[1]->client_id_ = ClientIdPtr();
+ leases[2]->client_id_ = ClientIdPtr();
+ leases[3]->client_id_ = ClientIdPtr();
+
+ // Start the tests. Add three leases to the database, read them back and
+ // check they are what we think they are.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ EXPECT_TRUE(lmptr_->addLease(leases[2]));
+ EXPECT_TRUE(lmptr_->addLease(leases[3]));
+ lmptr_->commit();
+
+ // Reopen the database to ensure that they actually got stored.
+ reopen();
+
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[3]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[3], l_returned);
+
+ // Check that we can't add a second lease with the same address
+ EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+ // Check that we can get the lease by HWAddr
+ HWAddr tmp(*leases[2]->hwaddr_);
+ Lease4Collection returned = lmptr_->getLease4(tmp);
+ ASSERT_EQ(1, returned.size());
+ detailCompareLease(leases[2], *returned.begin());
+
+ l_returned = lmptr_->getLease4(tmp, leases[2]->subnet_id_);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ // Check that we can update the lease
+ // Modify some fields in lease 1 (not the address) and update it.
+ ++leases[1]->subnet_id_;
+ leases[1]->valid_lft_ *= 2;
+ lmptr_->updateLease4(leases[1]);
+
+ // ... and check that the lease is indeed updated
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Delete a lease, check that it's gone, and that we can't delete it
+ // a second time.
+ EXPECT_TRUE(lmptr_->deleteLease(leases[1]));
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ EXPECT_FALSE(l_returned);
+ EXPECT_FALSE(lmptr_->deleteLease(leases[1]));
+
+ // Check that the second address is still there.
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+}
+
+void
+GenericLeaseMgrTest::testGetLease4HWAddr1() {
+ // Let's initialize two different leases 4 and just add the first ...
+ Lease4Ptr leaseA = initializeLease4(straddress4_[5]);
+ HWAddr hwaddrA(*leaseA->hwaddr_);
+ HWAddr hwaddrB(vector<uint8_t>(6, 0x80), HTYPE_ETHER);
+
+ EXPECT_TRUE(lmptr_->addLease(leaseA));
+
+ // we should not have a lease, with this MAC Addr
+ Lease4Collection returned = lmptr_->getLease4(hwaddrB);
+ ASSERT_EQ(0, returned.size());
+
+ // But with this one
+ returned = lmptr_->getLease4(hwaddrA);
+ ASSERT_EQ(1, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLease4HWAddr2() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the hardware address of lease 1
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ HWAddr tmp(*leases[1]->hwaddr_);
+ Lease4Collection returned = lmptr_->getLease4(tmp);
+
+ // Should be three leases, matching leases[1], [3] and [5].
+ ASSERT_EQ(3, returned.size());
+
+ // Check the lease[5] (and only this one) has an user context.
+ size_t contexts = 0;
+ for (Lease4Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ if ((*i)->getContext()) {
+ ++contexts;
+ EXPECT_EQ("{ \"foo\": true }", (*i)->getContext()->str());
+ }
+ }
+ EXPECT_EQ(1, contexts);
+
+ // Easiest way to check is to look at the addresses.
+ vector<string> addresses;
+ for (Lease4Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ addresses.push_back((*i)->addr_.toText());
+ }
+ sort(addresses.begin(), addresses.end());
+ EXPECT_EQ(straddress4_[1], addresses[0]);
+ EXPECT_EQ(straddress4_[3], addresses[1]);
+ EXPECT_EQ(straddress4_[5], addresses[2]);
+
+ // Repeat test with just one expected match
+ returned = lmptr_->getLease4(*leases[2]->hwaddr_);
+ ASSERT_EQ(1, returned.size());
+ detailCompareLease(leases[2], *returned.begin());
+
+ // Check that an empty vector is valid
+ EXPECT_TRUE(leases[7]->hwaddr_->hwaddr_.empty());
+ EXPECT_FALSE(leases[7]->client_id_->getClientId().empty());
+ returned = lmptr_->getLease4(*leases[7]->hwaddr_);
+ ASSERT_EQ(1, returned.size());
+ detailCompareLease(leases[7], *returned.begin());
+
+ // Try to get something with invalid hardware address
+ vector<uint8_t> invalid(6, 0);
+ returned = lmptr_->getLease4(invalid);
+ EXPECT_EQ(0, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testAddGetDelete6() {
+ const std::string addr234("2001:db8:1::234");
+ const std::string addr456("2001:db8:1::456");
+ const std::string addr789("2001:db8:1::789");
+ IOAddress addr(addr456);
+
+ uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ DuidPtr duid(new DUID(llt, sizeof(llt)));
+
+ uint32_t iaid = 7; // just a number
+
+ SubnetID subnet_id = 8; // just another number
+
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid, iaid, 100, 200,
+ subnet_id));
+
+ EXPECT_TRUE(lmptr_->addLease(lease));
+
+ // should not be allowed to add a second lease with the same address
+ EXPECT_FALSE(lmptr_->addLease(lease));
+
+ Lease6Ptr x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr234));
+ EXPECT_EQ(Lease6Ptr(), x);
+
+ x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
+ ASSERT_TRUE(x);
+
+ EXPECT_EQ(x->addr_, addr);
+ EXPECT_TRUE(*x->duid_ == *duid);
+ EXPECT_EQ(x->iaid_, iaid);
+ EXPECT_EQ(x->subnet_id_, subnet_id);
+
+ // These are not important from lease management perspective, but
+ // let's check them anyway.
+ EXPECT_EQ(Lease::TYPE_NA, x->type_);
+ EXPECT_EQ(100, x->preferred_lft_);
+ EXPECT_EQ(200, x->valid_lft_);
+
+ // Test getLease6(duid, iaid, subnet_id) - positive case
+ Lease6Ptr y = lmptr_->getLease6(Lease::TYPE_NA, *duid, iaid, subnet_id);
+ ASSERT_TRUE(y);
+ EXPECT_TRUE(*y->duid_ == *duid);
+ EXPECT_EQ(y->iaid_, iaid);
+ EXPECT_EQ(y->addr_, addr);
+
+ // Test getLease6(duid, iaid, subnet_id) - wrong iaid
+ uint32_t invalid_iaid = 9; // no such iaid
+ y = lmptr_->getLease6(Lease::TYPE_NA, *duid, invalid_iaid, subnet_id);
+ EXPECT_FALSE(y);
+
+ uint32_t invalid_subnet_id = 999;
+ y = lmptr_->getLease6(Lease::TYPE_NA, *duid, iaid, invalid_subnet_id);
+ EXPECT_FALSE(y);
+
+ // truncated duid
+ DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1));
+ y = lmptr_->getLease6(Lease::TYPE_NA, *invalid_duid, iaid, subnet_id);
+ EXPECT_FALSE(y);
+
+ // should return false - there's no such address
+ Lease6Ptr non_existing_lease(new Lease6(Lease::TYPE_NA, IOAddress(addr789),
+ duid, iaid, 100, 200,
+ subnet_id));
+ EXPECT_FALSE(lmptr_->deleteLease(non_existing_lease));
+
+ // this one should succeed
+ EXPECT_TRUE(lmptr_->deleteLease(x));
+
+ // after the lease is deleted, it should really be gone
+ x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
+ EXPECT_FALSE(x);
+
+ // Reopen the lease storage to make sure that lease is gone from the
+ // persistent storage.
+ reopen(V6);
+ x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
+ EXPECT_FALSE(x);
+}
+
+void
+GenericLeaseMgrTest::testMaxDate4() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ Lease4Ptr lease = leases[1];
+
+ // Set valid_lft_ to 1 day, cllt_ to max time. This should make expire
+ // time too large to store.
+ lease->valid_lft_ = 24*60*60;
+ lease->cltt_ = DatabaseConnection::MAX_DB_TIME;
+
+ // Insert should throw.
+ ASSERT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
+
+ // Set valid_lft_ to 0, which should make expire time small enough to
+ // store and try again.
+ lease->valid_lft_ = 0;
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+}
+
+void
+GenericLeaseMgrTest::testInfiniteLifeTime4() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ Lease4Ptr lease = leases[1];
+
+ // Set valid_lft_ to infinite, cllt_ to now.
+ lease->valid_lft_ = Lease::INFINITY_LFT;
+ lease->cltt_ = time(NULL);
+
+ // Insert should not throw.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+}
+
+void
+GenericLeaseMgrTest::testBasicLease4() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ leases[2]->setContext(Element::fromJSON("{ \"foobar\": 1234 }"));
+
+ // Start the tests. Add three leases to the database, read them back and
+ // check they are what we think they are.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ EXPECT_TRUE(lmptr_->addLease(leases[2]));
+ EXPECT_TRUE(lmptr_->addLease(leases[3]));
+ lmptr_->commit();
+
+ // Reopen the database to ensure that they actually got stored.
+ reopen(V4);
+
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[3]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[3], l_returned);
+
+ // Check that we can't add a second lease with the same address
+ EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+ // Delete a lease, check that it's gone, and that we can't delete it
+ // a second time.
+ EXPECT_TRUE(lmptr_->deleteLease(leases[1]));
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ EXPECT_FALSE(l_returned);
+ EXPECT_FALSE(lmptr_->deleteLease(leases[1]));
+
+ // Check that the second address is still there.
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ reopen(V4);
+
+ // The deleted lease should be still gone after we re-read leases from
+ // persistent storage.
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ EXPECT_FALSE(l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[3]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[3], l_returned);
+
+ // Update some FQDN data, so as we can check that update in
+ // persistent storage works as expected.
+ leases[2]->hostname_ = "memfile.example.com.";
+ leases[2]->fqdn_rev_ = true;
+
+ ASSERT_NO_THROW(lmptr_->updateLease4(leases[2]));
+
+ reopen(V4);
+
+ // The lease should be now updated in the storage.
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[3]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[3], l_returned);
+}
+
+
+void
+GenericLeaseMgrTest::testBasicLease6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ leases[2]->setContext(Element::fromJSON("{ \"foobar\": 1234 }"));
+
+ // Start the tests. Add three leases to the database, read them back and
+ // check they are what we think they are.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ EXPECT_TRUE(lmptr_->addLease(leases[2]));
+ EXPECT_TRUE(lmptr_->addLease(leases[3]));
+ lmptr_->commit();
+
+ // Reopen the database to ensure that they actually got stored.
+ reopen(V6);
+
+ Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[3], l_returned);
+
+ // Check that we can't add a second lease with the same address
+ EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+ // Delete a lease, check that it's gone, and that we can't delete it
+ // a second time.
+ EXPECT_TRUE(lmptr_->deleteLease(leases[1]));
+ l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ EXPECT_FALSE(l_returned);
+ EXPECT_FALSE(lmptr_->deleteLease(leases[1]));
+
+ // Check that the second address is still there.
+ l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ reopen(V6);
+
+ // The deleted lease should be still gone after we re-read leases from
+ // persistent storage.
+ l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ EXPECT_FALSE(l_returned);
+
+ // Check that the second address is still there.
+ l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ // Update some FQDN data, so as we can check that update in
+ // persistent storage works as expected.
+ leases[2]->hostname_ = "memfile.example.com.";
+ leases[2]->fqdn_rev_ = true;
+
+ ASSERT_NO_THROW(lmptr_->updateLease6(leases[2]));
+
+ reopen(V6);
+
+ // The lease should be now updated in the storage.
+ l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+}
+
+void
+GenericLeaseMgrTest::testMaxDate6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ Lease6Ptr lease = leases[1];
+
+ // Set valid_lft_ to 1 day, cllt_ to max time. This should make expire
+ // time too large to store.
+ lease->valid_lft_ = 24*60*60;
+ lease->cltt_ = DatabaseConnection::MAX_DB_TIME;
+
+ // Insert should throw.
+ ASSERT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
+
+ // Set valid_lft_ to 0, which should make expire time small enough to
+ // store and try again.
+ lease->valid_lft_ = 0;
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+}
+
+void
+GenericLeaseMgrTest::testInfiniteLifeTime6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ Lease6Ptr lease = leases[1];
+
+ // Set valid_lft_ to infinite, cllt_ to now.
+ lease->valid_lft_ = Lease::INFINITY_LFT;
+ lease->cltt_ = time(NULL);
+
+ // Insert should not throw.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+}
+
+// Checks whether a MAC address can be stored and retrieved together with
+// a lease.
+void
+GenericLeaseMgrTest::testLease6MAC() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ HWAddrPtr hwaddr1(new HWAddr(vector<uint8_t>(6, 11), HTYPE_ETHER));
+ HWAddrPtr hwaddr2(new HWAddr(vector<uint8_t>(6, 22), HTYPE_DOCSIS));
+
+ leases[1]->hwaddr_ = hwaddr1; // Add hardware address to leases 1 and 2
+ leases[2]->hwaddr_ = hwaddr2;
+ leases[3]->hwaddr_ = HWAddrPtr(); // No hardware address for the third one
+
+ // Start the tests. Add three leases to the database, read them back and
+ // check they are what we think they are.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ EXPECT_TRUE(lmptr_->addLease(leases[2]));
+ EXPECT_TRUE(lmptr_->addLease(leases[3]));
+ lmptr_->commit();
+
+ // Reopen the database to ensure that they actually got stored.
+ reopen(V6);
+
+ // First lease should have a hardware address in it
+ Lease6Ptr stored1 = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ ASSERT_TRUE(stored1);
+ ASSERT_TRUE(stored1->hwaddr_);
+ EXPECT_TRUE(*hwaddr1 == *stored1->hwaddr_);
+
+ // Second lease should have a hardware address in it
+ Lease6Ptr stored2 = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+ ASSERT_TRUE(stored2);
+ ASSERT_TRUE(stored2->hwaddr_);
+ EXPECT_TRUE(*hwaddr2 == *stored2->hwaddr_);
+
+ // Third lease should NOT have any hardware address.
+ Lease6Ptr stored3 = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
+ ASSERT_TRUE(stored3);
+ EXPECT_FALSE(stored3->hwaddr_);
+}
+
+// Checks whether a hardware address type can be stored and retrieved.
+void
+GenericLeaseMgrTest::testLease6HWTypeAndSource() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ HWAddrPtr hwaddr1(new HWAddr(vector<uint8_t>(6, 11), 123));
+ HWAddrPtr hwaddr2(new HWAddr(vector<uint8_t>(6, 22), 456));
+
+ // Those should use defines from Pkt::HWADDR_SOURCE_*, but let's
+ // test an uncommon value (and 0 which means unknown).
+ hwaddr1->source_ = HWAddr::HWADDR_SOURCE_RAW;
+ hwaddr2->source_ = HWAddr::HWADDR_SOURCE_DUID;
+
+ leases[1]->hwaddr_ = hwaddr1; // Add hardware address to leases 1 and 2
+ leases[2]->hwaddr_ = hwaddr2;
+ leases[3]->hwaddr_ = HWAddrPtr(); // No hardware address for the third one
+
+ // Start the tests. Add three leases to the database, read them back and
+ // check they are what we think they are.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ EXPECT_TRUE(lmptr_->addLease(leases[2]));
+ EXPECT_TRUE(lmptr_->addLease(leases[3]));
+ lmptr_->commit();
+
+ // Reopen the database to ensure that they actually got stored.
+ reopen(V6);
+
+ // First lease should have a hardware address in it
+ Lease6Ptr stored1 = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ ASSERT_TRUE(stored1);
+ ASSERT_TRUE(stored1->hwaddr_);
+ EXPECT_EQ(123, stored1->hwaddr_->htype_);
+ EXPECT_EQ(HWAddr::HWADDR_SOURCE_RAW, stored1->hwaddr_->source_);
+
+ // Second lease should have a hardware address in it
+ Lease6Ptr stored2 = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+ ASSERT_TRUE(stored2);
+ ASSERT_TRUE(stored2->hwaddr_);
+ EXPECT_EQ(456, stored2->hwaddr_->htype_);
+ EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, stored2->hwaddr_->source_);
+
+ // Third lease should NOT have any hardware address.
+ Lease6Ptr stored3 = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
+ ASSERT_TRUE(stored3);
+ EXPECT_FALSE(stored3->hwaddr_);
+}
+
+void
+GenericLeaseMgrTest::testLease4InvalidHostname() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Create a dummy hostname, consisting of 255 characters.
+ leases[1]->hostname_.assign(255, 'a');
+ ASSERT_TRUE(lmptr_->addLease(leases[1]));
+
+ // The new lease must be in the database.
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ detailCompareLease(leases[1], l_returned);
+
+ // Let's delete the lease, so as we can try to add it again with
+ // invalid hostname.
+ EXPECT_TRUE(lmptr_->deleteLease(leases[1]));
+
+ // Create a hostname with 256 characters. It should not be accepted.
+ leases[1]->hostname_.assign(256, 'a');
+ EXPECT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
+}
+
+/// @brief Verify that too long hostname for Lease6 is not accepted.
+void
+GenericLeaseMgrTest::testLease6InvalidHostname() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ // Create a dummy hostname, consisting of 255 characters.
+ leases[1]->hostname_.assign(255, 'a');
+ ASSERT_TRUE(lmptr_->addLease(leases[1]));
+
+ // The new lease must be in the database.
+ Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ detailCompareLease(leases[1], l_returned);
+
+ // Let's delete the lease, so as we can try to add it again with
+ // invalid hostname.
+ EXPECT_TRUE(lmptr_->deleteLease(leases[1]));
+
+ // Create a hostname with 256 characters. It should not be accepted.
+ leases[1]->hostname_.assign(256, 'a');
+ EXPECT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
+}
+
+void
+GenericLeaseMgrTest::testGetLease4HWAddrSize() {
+ // Create leases, although we need only one.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Now add leases with increasing hardware address size.
+ for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) {
+ leases[1]->hwaddr_->hwaddr_.resize(i, i);
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ Lease4Collection returned =
+ lmptr_->getLease4(*leases[1]->hwaddr_);
+
+ ASSERT_EQ(1, returned.size());
+ detailCompareLease(leases[1], *returned.begin());
+ ASSERT_TRUE(lmptr_->deleteLease(leases[1]));
+ }
+
+ // Database should not let us add one that is too big
+ // (The 42 is a random value put in each byte of the address.)
+ leases[1]->hwaddr_->hwaddr_.resize(HWAddr::MAX_HWADDR_LEN + 100, 42);
+ EXPECT_THROW(lmptr_->addLease(leases[1]), isc::db::DbOperationError);
+}
+
+void
+GenericLeaseMgrTest::testGetLease4HWAddrSubnetId() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the hardware address of lease 1 and
+ // subnet ID of lease 1. Result should be a single lease - lease 1.
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ Lease4Ptr returned = lmptr_->getLease4(*leases[1]->hwaddr_,
+ leases[1]->subnet_id_);
+
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+
+ // Try for a match to the hardware address of lease 1 and the wrong
+ // subnet ID.
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ returned = lmptr_->getLease4(*leases[1]->hwaddr_, leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to the subnet ID of lease 1 (and lease 4) but
+ // the wrong hardware address.
+ vector<uint8_t> invalid_hwaddr(15, 0x77);
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ returned = lmptr_->getLease4(HWAddr(invalid_hwaddr, HTYPE_ETHER),
+ leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to an unknown hardware address and an unknown
+ // subnet ID.
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ returned = lmptr_->getLease4(HWAddr(invalid_hwaddr, HTYPE_ETHER),
+ leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Add a second lease with the same values as the first and check that
+ // an attempt to access the database by these parameters throws a
+ // "multiple records" exception. (We expect there to be only one record
+ // with that combination, so getting them via getLeaseX() (as opposed
+ // to getLeaseXCollection() should throw an exception.)
+ EXPECT_TRUE(lmptr_->deleteLease(leases[2]));
+ leases[1]->addr_ = leases[2]->addr_;
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ EXPECT_THROW(returned = lmptr_->getLease4(*leases[1]->hwaddr_,
+ leases[1]->subnet_id_),
+ isc::db::MultipleRecords);
+
+}
+
+void
+GenericLeaseMgrTest::testGetLease4HWAddrSubnetIdSize() {
+ // Create leases, although we need only one.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Now add leases with increasing hardware address size and check
+ // that they can be retrieved.
+ for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) {
+ leases[1]->hwaddr_->hwaddr_.resize(i, i);
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ /// @todo: Simply use HWAddr directly once 2589 is implemented
+ Lease4Ptr returned = lmptr_->getLease4(*leases[1]->hwaddr_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+ ASSERT_TRUE(lmptr_->deleteLease(leases[1]));
+ }
+
+ // Database should not let us add one that is too big
+ // (The 42 is a random value put in each byte of the address.)
+ leases[1]->hwaddr_->hwaddr_.resize(HWAddr::MAX_HWADDR_LEN + 100, 42);
+ EXPECT_THROW(lmptr_->addLease(leases[1]), isc::db::DbOperationError);
+}
+
+void
+GenericLeaseMgrTest::testGetLease4ClientId2() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the Client ID address of lease 1
+ Lease4Collection returned = lmptr_->getLease4(*leases[1]->client_id_);
+
+ // Should be four leases, matching leases[1], [4], [5] and [6].
+ ASSERT_EQ(4, returned.size());
+
+ // Check the lease[5] (and only this one) has an user context.
+ size_t contexts = 0;
+ for (Lease4Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ if ((*i)->getContext()) {
+ ++contexts;
+ EXPECT_EQ("{ \"foo\": true }", (*i)->getContext()->str());
+ }
+ }
+ EXPECT_EQ(1, contexts);
+
+ // Easiest way to check is to look at the addresses.
+ vector<string> addresses;
+ for (Lease4Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ addresses.push_back((*i)->addr_.toText());
+ }
+ sort(addresses.begin(), addresses.end());
+ EXPECT_EQ(straddress4_[1], addresses[0]);
+ EXPECT_EQ(straddress4_[4], addresses[1]);
+ EXPECT_EQ(straddress4_[5], addresses[2]);
+ EXPECT_EQ(straddress4_[6], addresses[3]);
+
+ // Repeat test with just one expected match
+ returned = lmptr_->getLease4(*leases[3]->client_id_);
+ ASSERT_EQ(1, returned.size());
+ detailCompareLease(leases[3], *returned.begin());
+
+ // Try to get something with invalid client ID
+ const uint8_t invalid_data[] = {0, 0, 0};
+ ClientId invalid(invalid_data, sizeof(invalid_data));
+ returned = lmptr_->getLease4(invalid);
+ EXPECT_EQ(0, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLease4ClientIdSize() {
+ // Create leases, although we need only one.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Now add leases with increasing Client ID size can be retrieved.
+ // For speed, go from 0 to 128 is steps of 16.
+ // Intermediate client_id_max is to overcome problem if
+ // ClientId::MAX_CLIENT_ID_LEN is used in an EXPECT_EQ.
+ int client_id_max = ClientId::MAX_CLIENT_ID_LEN;
+ EXPECT_EQ(128, client_id_max);
+
+ int client_id_min = ClientId::MIN_CLIENT_ID_LEN;
+ EXPECT_EQ(2, client_id_min); // See RFC2132, section 9.14
+
+ for (uint8_t i = client_id_min; i <= client_id_max; i += 16) {
+ vector<uint8_t> clientid_vec(i, i);
+ leases[1]->client_id_.reset(new ClientId(clientid_vec));
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Collection returned = lmptr_->getLease4(*leases[1]->client_id_);
+ ASSERT_EQ(returned.size(), 1u);
+ detailCompareLease(leases[1], *returned.begin());
+ ASSERT_TRUE(lmptr_->deleteLease(leases[1]));
+ }
+
+ // Don't bother to check client IDs longer than the maximum -
+ // these cannot be constructed, and that limitation is tested
+ // in the DUID/Client ID unit tests.
+}
+
+void
+GenericLeaseMgrTest::testGetLease4ClientIdSubnetId() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the client ID of lease 1 and
+ // subnet ID of lease 1. Result should be a single lease - lease 1.
+ Lease4Ptr returned = lmptr_->getLease4(*leases[1]->client_id_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+
+ // Try for a match to the client ID of lease 1 and the wrong
+ // subnet ID.
+ returned = lmptr_->getLease4(*leases[1]->client_id_,
+ leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to the subnet ID of lease 1 (and lease 4) but
+ // the wrong client ID
+ const uint8_t invalid_data[] = {0, 0, 0};
+ ClientId invalid(invalid_data, sizeof(invalid_data));
+ returned = lmptr_->getLease4(invalid, leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to an unknown hardware address and an unknown
+ // subnet ID.
+ returned = lmptr_->getLease4(invalid, leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+}
+
+void
+GenericLeaseMgrTest::testGetLeases4SubnetId() {
+ // Get the leases to be used for the test and add to the database.
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // There should be exactly two leases for the subnet id that the second
+ // lease belongs to.
+ Lease4Collection returned = lmptr_->getLeases4(leases[1]->subnet_id_);
+ ASSERT_EQ(2, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLeases4Hostname() {
+ // Get the leases to be used for the test and add to the database.
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // There should be no lease for hostname foobar.
+ Lease4Collection returned = lmptr_->getLeases4(string("foobar"));
+ EXPECT_TRUE(returned.empty());
+
+ // There should be exactly 4 leases for the hostname of the second lease.
+ ASSERT_FALSE(leases[1]->hostname_.empty());
+ returned = lmptr_->getLeases4(leases[1]->hostname_);
+ EXPECT_EQ(4, returned.size());
+
+ // And 3 for the forth lease.
+ ASSERT_FALSE(leases[3]->hostname_.empty());
+ returned = lmptr_->getLeases4(leases[3]->hostname_);
+ EXPECT_EQ(3, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLeases4() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // All leases should be returned.
+ Lease4Collection returned = lmptr_->getLeases4();
+ ASSERT_EQ(leases.size(), returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLeases4Paged() {
+ // Get the leases to be used for the test and add to the database.
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ Lease4Collection all_leases;
+
+ IOAddress last_address = IOAddress("0.0.0.0");
+ for (auto i = 0; i < 4; ++i) {
+ Lease4Collection page = lmptr_->getLeases4(last_address, LeasePageSize(3));
+
+ // Collect leases in a common structure. They may be out of order.
+ for (Lease4Ptr lease : page) {
+ all_leases.push_back(lease);
+ }
+
+ // Empty page means there are no more leases.
+ if (page.empty()) {
+ break;
+
+ } else {
+ // Record last returned address because it is going to be used
+ // as an argument for the next call.
+ last_address = page[page.size() - 1]->addr_;
+ }
+ }
+
+ // Make sure that we got exactly the number of leases that we earlier
+ // stored in the database.
+ EXPECT_EQ(leases.size(), all_leases.size());
+
+ // Make sure that all leases that we stored in the lease database
+ // have been retrieved.
+ for (Lease4Ptr lease : leases) {
+ bool found = false;
+ for (Lease4Ptr returned_lease : all_leases) {
+ if (lease->addr_ == returned_lease->addr_) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found) << "lease for address " << lease->addr_.toText()
+ << " was not returned in any of the pages";
+ }
+
+ boost::scoped_ptr<LeasePageSize> lease_page_size;
+
+ // The maximum allowed value for the limit is max for uint32_t.
+ size_t oor = static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 1;
+ EXPECT_THROW(lease_page_size.reset(new LeasePageSize(oor)), OutOfRange);
+
+ // Zero page size is illegal too.
+ EXPECT_THROW(lease_page_size.reset(new LeasePageSize(0)), OutOfRange);
+
+ // Only IPv4 address can be used.
+ EXPECT_THROW(lmptr_->getLeases4(IOAddress("2001:db8::1"), LeasePageSize(3)),
+ InvalidAddressFamily);
+}
+
+void
+GenericLeaseMgrTest::testGetLeases6SubnetId() {
+ // Get the leases to be used for the test and add to the database.
+ vector<Lease6Ptr> leases = createLeases6();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // There should be exactly two leases for the subnet id that the second
+ // lease belongs to.
+ Lease6Collection returned = lmptr_->getLeases6(leases[1]->subnet_id_);
+ EXPECT_EQ(2, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLeases6Hostname() {
+ // Get the leases to be used for the test and add to the database.
+ vector<Lease6Ptr> leases = createLeases6();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // There should be no lease for hostname foobar.
+ Lease6Collection returned = lmptr_->getLeases6(string("foobar"));
+ EXPECT_TRUE(returned.empty());
+
+ // There should be exactly 4 leases for the hostname of the second lease.
+ ASSERT_FALSE(leases[1]->hostname_.empty());
+ returned = lmptr_->getLeases6(leases[1]->hostname_);
+ EXPECT_EQ(4, returned.size());
+
+ // One for the fifth lease.
+ ASSERT_FALSE(leases[4]->hostname_.empty());
+ returned = lmptr_->getLeases6(leases[4]->hostname_);
+ EXPECT_EQ(1, returned.size());
+
+ // And 3 for the sixth lease.
+ ASSERT_FALSE(leases[5]->hostname_.empty());
+ returned = lmptr_->getLeases6(leases[5]->hostname_);
+ EXPECT_EQ(3, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLeases6() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease6Ptr> leases = createLeases6();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // All leases should be returned.
+ Lease6Collection returned = lmptr_->getLeases6();
+ ASSERT_EQ(leases.size(), returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLeases6Paged() {
+ // Get the leases to be used for the test and add to the database.
+ vector<Lease6Ptr> leases = createLeases6();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ Lease6Collection all_leases;
+
+ IOAddress last_address = IOAddress::IPV6_ZERO_ADDRESS();
+ for (auto i = 0; i < 4; ++i) {
+ Lease6Collection page = lmptr_->getLeases6(last_address, LeasePageSize(3));
+
+ // Collect leases in a common structure. They may be out of order.
+ for (Lease6Ptr lease : page) {
+ all_leases.push_back(lease);
+ }
+
+ // Empty page means there are no more leases.
+ if (page.empty()) {
+ break;
+
+ } else {
+ // Record last returned address because it is going to be used
+ // as an argument for the next call.
+ last_address = page[page.size() - 1]->addr_;
+ }
+ }
+
+ // Make sure that we got exactly the number of leases that we earlier
+ // stored in the database.
+ EXPECT_EQ(leases.size(), all_leases.size());
+
+ // Make sure that all leases that we stored in the lease database
+ // have been retrieved.
+ for (Lease6Ptr lease : leases) {
+ bool found = false;
+ for (Lease6Ptr returned_lease : all_leases) {
+ if (lease->addr_ == returned_lease->addr_) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found) << "lease for address " << lease->addr_.toText()
+ << " was not returned in any of the pages";
+ }
+
+ // Only IPv6 address can be used.
+ EXPECT_THROW(lmptr_->getLeases6(IOAddress("192.0.2.0"), LeasePageSize(3)),
+ InvalidAddressFamily);
+}
+
+void
+GenericLeaseMgrTest::testGetLeases6DuidIaid() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ ASSERT_LE(6, leases.size()); // Expect to access leases 0 through 5
+
+ // Add them to the database
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the DUID and IAID of lease[1].
+ Lease6Collection returned = lmptr_->getLeases6(leasetype6_[1],
+ *leases[1]->duid_,
+ leases[1]->iaid_);
+
+ // Should be two leases, matching leases[1] and [4].
+ ASSERT_EQ(2, returned.size());
+
+ // Easiest way to check is to look at the addresses.
+ vector<string> addresses;
+ for (Lease6Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ addresses.push_back((*i)->addr_.toText());
+ }
+ sort(addresses.begin(), addresses.end());
+ EXPECT_EQ(straddress6_[1], addresses[0]);
+ EXPECT_EQ(straddress6_[4], addresses[1]);
+
+ // Check that nothing is returned when either the IAID or DUID match
+ // nothing.
+ returned = lmptr_->getLeases6(leasetype6_[1], *leases[1]->duid_,
+ leases[1]->iaid_ + 1);
+ EXPECT_EQ(0, returned.size());
+
+ // Alter the leases[1] DUID to match nothing in the database.
+ vector<uint8_t> duid_vector = leases[1]->duid_->getDuid();
+ ++duid_vector[0];
+ DUID new_duid(duid_vector);
+ returned = lmptr_->getLeases6(leasetype6_[1], new_duid, leases[1]->iaid_);
+ EXPECT_EQ(0, returned.size());
+}
+
+void
+GenericLeaseMgrTest::testGetLeases6DuidSize() {
+ // Create leases, although we need only one.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ // Now add leases with increasing DUID size can be retrieved.
+ // For speed, go from 0 to 128 is steps of 16.
+ int duid_max = DUID::MAX_DUID_LEN;
+ EXPECT_EQ(128, duid_max);
+
+ int duid_min = DUID::MIN_DUID_LEN;
+ EXPECT_EQ(1, duid_min);
+
+ for (uint8_t i = duid_min; i <= duid_max; i += 16) {
+ vector<uint8_t> duid_vec(i, i);
+ leases[1]->duid_.reset(new DUID(duid_vec));
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease6Collection returned = lmptr_->getLeases6(leasetype6_[1],
+ *leases[1]->duid_,
+ leases[1]->iaid_);
+ ASSERT_EQ(1, returned.size());
+ detailCompareLease(leases[1], *returned.begin());
+ ASSERT_TRUE(lmptr_->deleteLease(leases[1]));
+ }
+
+ // Don't bother to check DUIDs longer than the maximum - these cannot be
+ // constructed, and that limitation is tested in the DUID/Client ID unit
+ // tests.
+
+}
+
+void
+GenericLeaseMgrTest::testLease6LeaseTypeCheck() {
+ Lease6Ptr empty_lease(new Lease6());
+
+ DuidPtr duid(new DUID(vector<uint8_t>(8, 0x77)));
+
+ empty_lease->iaid_ = 142;
+ empty_lease->duid_ = DuidPtr(new DUID(*duid));
+ empty_lease->subnet_id_ = 23;
+ empty_lease->preferred_lft_ = 100;
+ empty_lease->valid_lft_ = 100;
+ empty_lease->cltt_ = 100;
+ empty_lease->fqdn_fwd_ = true;
+ empty_lease->fqdn_rev_ = true;
+ empty_lease->hostname_ = "myhost.example.com.";
+ empty_lease->prefixlen_ = 4;
+
+ // Make Two leases per lease type, all with the same DUID, IAID but
+ // alternate the subnet_ids.
+ vector<Lease6Ptr> leases;
+ for (int i = 0; i < 6; ++i) {
+ Lease6Ptr lease(new Lease6(*empty_lease));
+ lease->type_ = leasetype6_[i / 2];
+ lease->addr_ = IOAddress(straddress6_[i]);
+ lease->subnet_id_ += (i % 2);
+ leases.push_back(lease);
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ }
+
+ // Verify getting a single lease by type and address.
+ for (int i = 0; i < 6; ++i) {
+ // Look for exact match for each lease type.
+ Lease6Ptr returned = lmptr_->getLease6(leasetype6_[i / 2],
+ leases[i]->addr_);
+ // We should match one per lease type.
+ ASSERT_TRUE(returned);
+ EXPECT_TRUE(*returned == *leases[i]);
+
+ // Same address but wrong lease type, should not match.
+ returned = lmptr_->getLease6(leasetype6_[i / 2 + 1], leases[i]->addr_);
+ ASSERT_FALSE(returned);
+ }
+
+ // Verify getting a collection of leases by type, DUID, and IAID.
+ // Iterate over the lease types, asking for leases based on
+ // lease type, DUID, and IAID.
+ for (int i = 0; i < 3; ++i) {
+ Lease6Collection returned = lmptr_->getLeases6(leasetype6_[i],
+ *duid, 142);
+ // We should match two per lease type.
+ ASSERT_EQ(2, returned.size());
+
+ // Collection order returned is not guaranteed.
+ // Easiest way to check is to look at the addresses.
+ vector<string> addresses;
+ for (Lease6Collection::const_iterator it = returned.begin();
+ it != returned.end(); ++it) {
+ addresses.push_back((*it)->addr_.toText());
+ }
+ sort(addresses.begin(), addresses.end());
+
+ // Now verify that the lease addresses match.
+ EXPECT_EQ(addresses[0], leases[(i * 2)]->addr_.toText());
+ EXPECT_EQ(addresses[1], leases[(i * 2 + 1)]->addr_.toText());
+ }
+
+ // Verify getting a collection of leases by type, DUID, IAID, and subnet id.
+ // Iterate over the lease types, asking for leases based on
+ // lease type, DUID, IAID, and subnet_id.
+ for (int i = 0; i < 3; ++i) {
+ Lease6Collection returned = lmptr_->getLeases6(leasetype6_[i],
+ *duid, 142, 23);
+ // We should match one per lease type.
+ ASSERT_EQ(1, returned.size());
+ EXPECT_TRUE(*(returned[0]) == *leases[i * 2]);
+ }
+
+ // Verify getting a single lease by type, duid, iad, and subnet id.
+ for (int i = 0; i < 6; ++i) {
+ Lease6Ptr returned = lmptr_->getLease6(leasetype6_[i / 2],
+ *duid, 142, (23 + (i % 2)));
+ // We should match one per lease type.
+ ASSERT_TRUE(returned);
+ EXPECT_TRUE(*returned == *leases[i]);
+ }
+}
+
+void
+GenericLeaseMgrTest::testLease6LargeIaidCheck() {
+
+ DuidPtr duid(new DUID(vector<uint8_t>(8, 0x77)));
+ IOAddress addr(std::string("2001:db8:1::111"));
+ SubnetID subnet_id = 8; // random number
+
+ // Use a value we know is larger than 32-bit signed max.
+ uint32_t large_iaid = 0xFFFFFFFE;
+
+ // We should be able to add with no problems.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid, large_iaid,
+ 100, 200, subnet_id));
+ ASSERT_TRUE(lmptr_->addLease(lease));
+
+ // Sanity check that we added it.
+ Lease6Ptr found_lease = lmptr_->getLease6(Lease::TYPE_NA, addr);
+ ASSERT_TRUE(found_lease);
+ EXPECT_TRUE(*found_lease == *lease);
+
+ // Verify getLease6() duid/iaid finds it.
+ found_lease = lmptr_->getLease6(Lease::TYPE_NA, *duid, large_iaid, subnet_id);
+ ASSERT_TRUE(found_lease);
+ EXPECT_TRUE(*found_lease == *lease);
+
+ // Verify getLeases6() duid/iaid finds it.
+ Lease6Collection found_leases = lmptr_->getLeases6(Lease::TYPE_NA,
+ *duid, large_iaid);
+ // We should match the lease.
+ ASSERT_EQ(1, found_leases.size());
+ EXPECT_TRUE(*(found_leases[0]) == *lease);
+}
+
+void
+GenericLeaseMgrTest::testGetLease6DuidIaidSubnetId() {
+ // Get the leases to be used for the test and add them to the database.
+ vector<Lease6Ptr> leases = createLeases6();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the DUID and IAID of lease[1].
+ Lease6Ptr returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
+ leases[1]->iaid_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ EXPECT_TRUE(*returned == *leases[1]);
+
+ // Modify each of the three parameters (DUID, IAID, Subnet ID) and
+ // check that nothing is returned.
+ returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
+ leases[1]->iaid_ + 1, leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+
+ returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
+ leases[1]->iaid_, leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Alter the leases[1] DUID to match nothing in the database.
+ vector<uint8_t> duid_vector = leases[1]->duid_->getDuid();
+ ++duid_vector[0];
+ DUID new_duid(duid_vector);
+ returned = lmptr_->getLease6(leasetype6_[1], new_duid, leases[1]->iaid_,
+ leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+}
+
+/// @brief verifies getLeases6(DUID)
+void
+GenericLeaseMgrTest::testGetLeases6Duid() {
+ //add leases
+ IOAddress addr1(std::string("2001:db8:1::111"));
+ IOAddress addr2(std::string("2001:db8:1::222"));
+ IOAddress addr3(std::string("2001:db8:1::333"));
+
+ DuidPtr duid1(new DUID({0, 1, 1, 1, 1, 1, 1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}));
+ DuidPtr duid2(new DUID({0, 2, 2, 2, 2, 2, 2, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}));
+ DuidPtr duid3(new DUID({0, 3, 3, 3, 3, 3, 3, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}));
+ DuidPtr duid4(new DUID({0, 4, 4, 4, 4, 4, 4, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}));
+
+ uint32_t iaid = 7; // random number
+
+ SubnetID subnet_id = 8; // random number
+
+ Lease6Ptr lease1(new Lease6(Lease::TYPE_NA, addr1, duid1, iaid, 100, 200,
+ subnet_id));
+ Lease6Ptr lease2(new Lease6(Lease::TYPE_NA, addr2, duid2, iaid, 100, 200,
+ subnet_id));
+ Lease6Ptr lease3(new Lease6(Lease::TYPE_PD, addr3, duid3, iaid, 100, 200,
+ subnet_id, HWAddrPtr(), 64));
+
+ EXPECT_TRUE(lmptr_->addLease(lease1));
+ EXPECT_TRUE(lmptr_->addLease(lease2));
+ EXPECT_TRUE(lmptr_->addLease(lease3));
+
+ Lease6Collection returned1 = lmptr_->getLeases6(*(lease1->duid_));
+ Lease6Collection returned2 = lmptr_->getLeases6(*(lease2->duid_));
+ Lease6Collection returned3 = lmptr_->getLeases6(*(lease3->duid_));
+
+ //verify if the returned lease mathces
+ ASSERT_EQ(returned1.size(), 1);
+ ASSERT_EQ(returned2.size(), 1);
+ ASSERT_EQ(returned3.size(), 1);
+
+ //verify that the returned lease are same
+ EXPECT_TRUE(returned1[0]->addr_ == lease1->addr_);
+ EXPECT_TRUE(returned2[0]->addr_ == lease2->addr_);
+ EXPECT_TRUE(returned3[0]->addr_ == lease3->addr_);
+
+ //now verify we return empty for a lease that has not been stored
+ returned3 = lmptr_->getLeases6(*duid4);
+ EXPECT_TRUE(returned3.empty());
+
+ //clean up
+ ASSERT_TRUE(lmptr_->deleteLease(lease1));
+ ASSERT_TRUE(lmptr_->deleteLease(lease2));
+ ASSERT_TRUE(lmptr_->deleteLease(lease3));
+
+ //now verify we return empty for a lease that has not been stored
+ returned3 = lmptr_->getLeases6(*duid4);
+ EXPECT_TRUE(returned3.empty());
+}
+
+/// @brief Checks that getLease6() works with different DUID sizes
+void
+GenericLeaseMgrTest::testGetLease6DuidIaidSubnetIdSize() {
+
+ // Create leases, although we need only one.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ // Now add leases with increasing DUID size can be retrieved.
+ // For speed, go from 0 to 128 is steps of 16.
+ int duid_max = DUID::MAX_DUID_LEN;
+ EXPECT_EQ(128, duid_max);
+
+ int duid_min = DUID::MIN_DUID_LEN;
+ EXPECT_EQ(1, duid_min);
+
+ for (uint8_t i = duid_min; i <= duid_max; i += 16) {
+ vector<uint8_t> duid_vec(i, i);
+ leases[1]->duid_.reset(new DUID(duid_vec));
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease6Ptr returned = lmptr_->getLease6(leasetype6_[1], *leases[1]->duid_,
+ leases[1]->iaid_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+ ASSERT_TRUE(lmptr_->deleteLease(leases[1]));
+ }
+
+ // Don't bother to check DUIDs longer than the maximum - these cannot be
+ // constructed, and that limitation is tested in the DUID/Client ID unit
+ // tests.
+}
+
+void
+GenericLeaseMgrTest::testUpdateLease4() {
+ // Get the leases to be used for the test and add them to the database.
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+ lmptr_->commit();
+
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Modify some fields in lease 1 (not the address) and update it.
+ ++leases[1]->subnet_id_;
+ leases[1]->valid_lft_ *= 2;
+ leases[1]->hostname_ = "modified.hostname.";
+ leases[1]->fqdn_fwd_ = !leases[1]->fqdn_fwd_;
+ leases[1]->fqdn_rev_ = !leases[1]->fqdn_rev_;;
+ leases[1]->setContext(Element::fromJSON("{ \"foobar\": 1234 }"));
+ lmptr_->updateLease4(leases[1]);
+
+ // ... and check what is returned is what is expected.
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Alter the lease again and check.
+ ++leases[1]->subnet_id_;
+ leases[1]->cltt_ += 6;
+ leases[1]->setContext(Element::fromJSON("{ \"foo\": \"bar\" }"));
+ lmptr_->updateLease4(leases[1]);
+
+ // Explicitly clear the returned pointer before getting new data to ensure
+ // that the new data is returned.
+ l_returned.reset();
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Check we can do an update without changing data.
+ lmptr_->updateLease4(leases[1]);
+ l_returned.reset();
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Try to update the lease with the too long hostname.
+ leases[1]->hostname_.assign(256, 'a');
+ EXPECT_THROW(lmptr_->updateLease4(leases[1]), isc::db::DbOperationError);
+
+ // Try updating a lease not in the database.
+ ASSERT_TRUE(lmptr_->deleteLease(leases[2]));
+ EXPECT_THROW(lmptr_->updateLease4(leases[2]), isc::dhcp::NoSuchLease);
+}
+
+void
+GenericLeaseMgrTest::testConcurrentUpdateLease4() {
+ // Get the leases to be used for the test and add them to the database.
+ vector<Lease4Ptr> leases = createLeases4();
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+ lmptr_->commit();
+
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Save initial lease to be used for concurrent update
+ Lease4Ptr initialLease = boost::make_shared<Lease4>(*leases[1]);
+ detailCompareLease(leases[1], initialLease);
+
+ // Modify some fields in lease 1 (not the address) and update it.
+ ++leases[1]->subnet_id_;
+ leases[1]->valid_lft_ *= 2;
+ leases[1]->hostname_ = "modified.hostname.";
+ leases[1]->fqdn_fwd_ = !leases[1]->fqdn_fwd_;
+ leases[1]->fqdn_rev_ = !leases[1]->fqdn_rev_;;
+ leases[1]->setContext(Element::fromJSON("{ \"foobar\": 1234 }"));
+ lmptr_->updateLease4(leases[1]);
+
+ // ... and check what is returned is what is expected.
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Concurrently updating lease should fail
+ EXPECT_THROW(lmptr_->updateLease4(initialLease), isc::dhcp::NoSuchLease);
+}
+
+void
+GenericLeaseMgrTest::testUpdateLease6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ ASSERT_LE(3, leases.size()); // Expect to access leases 0 through 2
+
+ // Add a lease to the database and check that the lease is there.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ lmptr_->commit();
+
+ Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Modify some fields in lease 1 (not the address) and update it.
+ ++leases[1]->iaid_;
+ leases[1]->type_ = Lease::TYPE_PD;
+ leases[1]->valid_lft_ *= 2;
+ leases[1]->hostname_ = "modified.hostname.v6.";
+ leases[1]->fqdn_fwd_ = !leases[1]->fqdn_fwd_;
+ leases[1]->fqdn_rev_ = !leases[1]->fqdn_rev_;;
+ leases[1]->setContext(Element::fromJSON("{ \"foobar\": 1234 }"));
+ lmptr_->updateLease6(leases[1]);
+
+ // ... and check what is returned is what is expected.
+ l_returned.reset();
+ l_returned = lmptr_->getLease6(Lease::TYPE_PD, ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Alter the lease again and check.
+ ++leases[1]->iaid_;
+ leases[1]->type_ = Lease::TYPE_TA;
+ leases[1]->cltt_ += 6;
+ leases[1]->prefixlen_ = 93;
+ leases[1]->setContext(Element::fromJSON("{ \"foo\": \"bar\" }"));
+ lmptr_->updateLease6(leases[1]);
+
+ l_returned.reset();
+ l_returned = lmptr_->getLease6(Lease::TYPE_TA, ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Check we can do an update without changing data.
+ lmptr_->updateLease6(leases[1]);
+ l_returned.reset();
+ l_returned = lmptr_->getLease6(Lease::TYPE_TA, ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Try to update the lease with the too long hostname.
+ leases[1]->hostname_.assign(256, 'a');
+ EXPECT_THROW(lmptr_->updateLease6(leases[1]), isc::db::DbOperationError);
+
+ // Try updating a lease not in the database.
+ EXPECT_THROW(lmptr_->updateLease6(leases[2]), isc::dhcp::NoSuchLease);
+}
+
+void
+GenericLeaseMgrTest::testConcurrentUpdateLease6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ ASSERT_LE(3, leases.size()); // Expect to access leases 0 through 2
+
+ // Add a lease to the database and check that the lease is there.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ lmptr_->commit();
+
+ Lease6Ptr l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Save initial lease to be used for concurrent update
+ Lease6Ptr initialLease = boost::make_shared<Lease6>(*leases[1]);
+ detailCompareLease(leases[1], initialLease);
+
+ // Modify some fields in lease 1 (not the address) and update it.
+ ++leases[1]->iaid_;
+ leases[1]->type_ = Lease::TYPE_PD;
+ leases[1]->valid_lft_ *= 2;
+ leases[1]->hostname_ = "modified.hostname.v6.";
+ leases[1]->fqdn_fwd_ = !leases[1]->fqdn_fwd_;
+ leases[1]->fqdn_rev_ = !leases[1]->fqdn_rev_;;
+ leases[1]->setContext(Element::fromJSON("{ \"foobar\": 1234 }"));
+ lmptr_->updateLease6(leases[1]);
+
+ // ... and check what is returned is what is expected.
+ l_returned.reset();
+ l_returned = lmptr_->getLease6(Lease::TYPE_PD, ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Concurrently updating lease should fail
+ EXPECT_THROW(lmptr_->updateLease6(initialLease), isc::dhcp::NoSuchLease);
+}
+
+void
+GenericLeaseMgrTest::testRecreateLease4() {
+ // Create a lease.
+ std::vector<Lease4Ptr> leases = createLeases4();
+ // Copy the lease so as we can freely modify it.
+ Lease4Ptr lease(new Lease4(*leases[0]));
+
+ // Add a lease.
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ lmptr_->commit();
+
+ // Check that the lease has been successfully added.
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[0]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(lease, l_returned);
+
+ // Delete a lease, check that it's gone.
+ EXPECT_TRUE(lmptr_->deleteLease(leases[0]));
+ EXPECT_FALSE(lmptr_->getLease4(ioaddress4_[0]));
+
+ // Modify the copy of the lease. Increasing values or negating them ensures
+ // that they are really modified, because we will never get the same values.
+ ++lease->subnet_id_;
+ ++lease->valid_lft_;
+ lease->fqdn_fwd_ = !lease->fqdn_fwd_;
+ // Make sure that the lease has been really modified.
+ ASSERT_NE(*lease, *leases[0]);
+ // Add the updated lease.
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ lmptr_->commit();
+
+ // Reopen the lease database, so as the lease is re-read.
+ reopen(V4);
+
+ // The lease in the database should be modified.
+ l_returned = lmptr_->getLease4(ioaddress4_[0]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(lease, l_returned);
+}
+
+void
+GenericLeaseMgrTest::testRecreateLease6() {
+ // Create a lease.
+ std::vector<Lease6Ptr> leases = createLeases6();
+ // Copy the lease so as we can freely modify it.
+ Lease6Ptr lease(new Lease6(*leases[0]));
+
+ // Add a lease.
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ lmptr_->commit();
+
+ // Check that the lease has been successfully added.
+ Lease6Ptr l_returned = lmptr_->getLease6(Lease::TYPE_NA, ioaddress6_[0]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(lease, l_returned);
+
+ // Delete a lease, check that it's gone.
+ EXPECT_TRUE(lmptr_->deleteLease(leases[0]));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, ioaddress6_[0]));
+
+ // Modify the copy of the lease. Increasing values or negating them ensures
+ // that they are really modified, because we will never get the same values.
+ ++lease->subnet_id_;
+ ++lease->valid_lft_;
+ lease->fqdn_fwd_ = !lease->fqdn_fwd_;
+ // Make sure that the lease has been really modified.
+ ASSERT_NE(*lease, *leases[0]);
+ // Add the updated lease.
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ lmptr_->commit();
+
+ // Reopen the lease database, so as the lease is re-read.
+ reopen(V6);
+
+ // The lease in the database should be modified.
+ l_returned = lmptr_->getLease6(Lease::TYPE_NA, ioaddress6_[0]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(lease, l_returned);
+}
+
+void
+GenericLeaseMgrTest::testNullDuid() {
+ // Create leases, although we need only one.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ // Set DUID to empty pointer.
+ leases[1]->duid_.reset();
+
+ // Insert should throw.
+ ASSERT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
+}
+
+void
+GenericLeaseMgrTest::testVersion(int major, int minor) {
+ EXPECT_EQ(major, lmptr_->getVersion().first);
+ EXPECT_EQ(minor, lmptr_->getVersion().second);
+}
+
+void
+GenericLeaseMgrTest::testGetExpiredLeases4() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ // Make sure we have at least 6 leases there.
+ ASSERT_GE(leases.size(), 6);
+
+ // Use the same current time for all leases.
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0; i < leases.size(); ++i) {
+ // Mark every other lease as expired.
+ if (i % 2 == 0) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. The expiration time also
+ // depends on the lease index, so as we can later check that the
+ // leases are ordered by the expiration time.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+ } else {
+ // Set current time as cltt for remaining leases. These leases are
+ // not expired.
+ leases[i]->cltt_ = current_time;
+ }
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Retrieve at most 1000 expired leases.
+ Lease4Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000));
+ // Leases with even indexes should be returned as expired.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ // The expired leases should be returned from the most to least expired.
+ // This matches the reverse order to which they have been added.
+ for (Lease4Collection::reverse_iterator lease = expired_leases.rbegin();
+ lease != expired_leases.rend(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.rbegin(), lease));
+ // Multiple current index by two, because only leases with even indexes
+ // should have been returned.
+ ASSERT_LE(2 * index, leases.size());
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+
+ // Update current time for the next test.
+ current_time = time(NULL);
+ // Also, remove expired leases collected during the previous test.
+ expired_leases.clear();
+
+ // This time let's reverse the expiration time and see if they will be returned
+ // in the correct order.
+ for (int i = 0; i < leases.size(); ++i) {
+ // Update the time of expired leases with even indexes.
+ if (i % 2 == 0) {
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+ } else {
+ // Make sure remaining leases remain unexpired.
+ leases[i]->cltt_ = current_time + 100;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease4(leases[i]));
+ }
+
+ // Retrieve expired leases again. The limit of 0 means return all expired
+ // leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0));
+ // The same leases should be returned.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ // This time leases should be returned in the non-reverse order.
+ for (Lease4Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ ASSERT_LE(2 * index, leases.size());
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+
+ // Remember expired leases returned.
+ std::vector<Lease4Ptr> saved_expired_leases = expired_leases;
+
+ // Remove expired leases again.
+ expired_leases.clear();
+
+ // Limit the number of leases to be returned to 2.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 2));
+
+ // Make sure we have exactly 2 leases returned.
+ ASSERT_EQ(2, expired_leases.size());
+
+ // Test that most expired leases have been returned.
+ for (Lease4Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ ASSERT_LE(2 * index, leases.size());
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+
+ // Mark every other expired lease as reclaimed.
+ for (int i = 0; i < saved_expired_leases.size(); ++i) {
+ if (i % 2 != 0) {
+ saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease4(saved_expired_leases[i]));
+ }
+
+ expired_leases.clear();
+
+ // This the returned leases should exclude reclaimed ones. So the number
+ // of returned leases should be roughly half of the expired leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0));
+ ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2),
+ expired_leases.size());
+
+ // Make sure that returned leases are those that are not reclaimed, i.e.
+ // those that have even index.
+ for (Lease4Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
+ }
+}
+
+void
+GenericLeaseMgrTest::testGetExpiredLeases6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ // Make sure we have at least 6 leases there.
+ ASSERT_GE(leases.size(), 6);
+
+ // Use the same current time for all leases.
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0; i < leases.size(); ++i) {
+ // Mark every other lease as expired.
+ if (i % 2 == 0) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. The expiration time also
+ // depends on the lease index, so as we can later check that the
+ // leases are ordered by the expiration time.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+ } else {
+ // Set current time as cltt for remaining leases. These leases are
+ // not expired.
+ leases[i]->cltt_ = current_time;
+ }
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Retrieve at most 1000 expired leases.
+ Lease6Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000));
+ // Leases with even indexes should be returned as expired.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ // The expired leases should be returned from the most to least expired.
+ // This matches the reverse order to which they have been added.
+ for (Lease6Collection::reverse_iterator lease = expired_leases.rbegin();
+ lease != expired_leases.rend(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.rbegin(), lease));
+ // Multiple current index by two, because only leases with even indexes
+ // should have been returned.
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+
+ // Update current time for the next test.
+ current_time = time(NULL);
+ // Also, remove expired leases collected during the previous test.
+ expired_leases.clear();
+
+ // This time let's reverse the expiration time and see if they will be returned
+ // in the correct order.
+ for (int i = 0; i < leases.size(); ++i) {
+ // Update the time of expired leases with even indexes.
+ if (i % 2 == 0) {
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+
+ } else {
+ // Make sure remaining leases remain unexpired.
+ leases[i]->cltt_ = current_time + 100;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease6(leases[i]));
+ }
+
+ // Retrieve expired leases again. The limit of 0 means return all expired
+ // leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+ // The same leases should be returned.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ // This time leases should be returned in the non-reverse order.
+ for (Lease6Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+
+ // Remember expired leases returned.
+ std::vector<Lease6Ptr> saved_expired_leases = expired_leases;
+
+ // Remove expired leases again.
+ expired_leases.clear();
+
+ // Limit the number of leases to be returned to 2.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 2));
+
+ // Make sure we have exactly 2 leases returned.
+ ASSERT_EQ(2, expired_leases.size());
+
+ // Test that most expired leases have been returned.
+ for (Lease6Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+
+ // Mark every other expired lease as reclaimed.
+ for (int i = 0; i < saved_expired_leases.size(); ++i) {
+ if (i % 2 != 0) {
+ saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease6(saved_expired_leases[i]));
+ }
+
+ expired_leases.clear();
+
+ // This the returned leases should exclude reclaimed ones. So the number
+ // of returned leases should be roughly half of the expired leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+ ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2),
+ expired_leases.size());
+
+ // Make sure that returned leases are those that are not reclaimed, i.e.
+ // those that have even index.
+ for (Lease6Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
+ }
+}
+
+void
+GenericLeaseMgrTest::testInfiniteAreNotExpired4() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ Lease4Ptr lease = leases[1];
+
+ // Set valid_lft_ to infinite. Leave the small cltt even it won't happen
+ // in the real world to catch more possible issues.
+ lease->valid_lft_ = Lease::INFINITY_LFT;
+
+ // Add it to the database.
+ ASSERT_TRUE(lmptr_->addLease(leases[1]));
+
+ // Retrieve at most 10 expired leases.
+ Lease4Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 10));
+
+ // No lease should be returned.
+ EXPECT_EQ(0, expired_leases.size());
+}
+
+void
+GenericLeaseMgrTest::testInfiniteAreNotExpired6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ Lease6Ptr lease = leases[1];
+
+ // Set valid_lft_ to infinite. Leave the small cltt even it won't happen
+ // in the real world to catch more possible issues.
+ lease->valid_lft_ = Lease::INFINITY_LFT;
+
+ // Add it to the database.
+ ASSERT_TRUE(lmptr_->addLease(leases[1]));
+
+ // Retrieve at most 10 expired leases.
+ Lease6Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 10));
+
+ // No lease should be returned.
+ EXPECT_EQ(0, expired_leases.size());
+}
+
+void
+GenericLeaseMgrTest::testDeleteExpiredReclaimedLeases4() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ // Make sure we have at least 6 leases there.
+ ASSERT_GE(leases.size(), 6);
+
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0; i < leases.size(); ++i) {
+ // Mark every other lease as expired.
+ if (i % 2 == 0) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. We also substract the value
+ // of 10, 20, 30, 40 etc, depending on the lease index. This
+ // simulates different expiration times for various leases.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - i * 10;
+ // Set reclaimed state.
+ leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+
+ } else {
+ // Other leases are left as not expired - client last transmission
+ // time set to current time.
+ leases[i]->cltt_ = current_time;
+ }
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Keep reclaimed lease for 15 seconds after expiration.
+ const uint32_t lease_affinity_time = 15;
+
+ // Delete expired and reclaimed leases which have expired earlier than
+ // 15 seconds ago. This should affect leases with index 2, 3, 4 etc.
+ uint64_t deleted_num;
+ uint64_t should_delete_num = 0;
+ ASSERT_NO_THROW(
+ deleted_num = lmptr_->deleteExpiredReclaimedLeases4(lease_affinity_time)
+ );
+
+ for (size_t i = 0; i < leases.size(); ++i) {
+ // Obtain lease from the server.
+ Lease4Ptr lease = lmptr_->getLease4(leases[i]->addr_);
+
+ // If the lease is reclaimed and the expiration time passed more than
+ // 15 seconds ago, the lease should have been deleted.
+ if (leases[i]->stateExpiredReclaimed() &&
+ ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) {
+ EXPECT_FALSE(lease) << "The following lease should have been"
+ " deleted: " << leases[i]->toText();
+ ++should_delete_num;
+ } else {
+ // If the lease is not reclaimed or it has expired less than
+ // 15 seconds ago, the lease should still be there.
+ EXPECT_TRUE(lease) << "The following lease shouldn't have been"
+ " deleted: " << leases[i]->toText();
+ }
+ }
+
+ // Check that the number of leases deleted is correct.
+ EXPECT_EQ(deleted_num, should_delete_num);
+
+ // Make sure we can make another attempt, when there are no more leases
+ // to be deleted.
+ ASSERT_NO_THROW(
+ deleted_num = lmptr_->deleteExpiredReclaimedLeases4(lease_affinity_time)
+ );
+ // No lease should have been deleted.
+ EXPECT_EQ(0, deleted_num);
+
+ // Reopen the database. This to ensure that the leases have been deleted
+ // from the persistent storage.
+ reopen(V4);
+
+ for (size_t i = 0; i < leases.size(); ++i) {
+ /// @todo Leases with empty HW address are dropped by the memfile
+ /// backend. We will have to reevaluate if this is right behavior
+ /// of the backend when client identifier is present.
+ if (leases[i]->hwaddr_ && leases[i]->hwaddr_->hwaddr_.empty()) {
+ continue;
+ }
+ // Obtain lease from the server.
+ Lease4Ptr lease = lmptr_->getLease4(leases[i]->addr_);
+
+ // If the lease is reclaimed and the expiration time passed more than
+ // 15 seconds ago, the lease should have been deleted.
+ if (leases[i]->stateExpiredReclaimed() &&
+ ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) {
+ EXPECT_FALSE(lease) << "The following lease should have been"
+ " deleted: " << leases[i]->toText();
+
+ } else {
+ // If the lease is not reclaimed or it has expired less than
+ // 15 seconds ago, the lease should still be there.
+ EXPECT_TRUE(lease) << "The following lease shouldn't have been"
+ " deleted: " << leases[i]->toText();
+ }
+ }
+}
+
+void
+GenericLeaseMgrTest::testDeleteExpiredReclaimedLeases6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ // Make sure we have at least 6 leases there.
+ ASSERT_GE(leases.size(), 6);
+
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0; i < leases.size(); ++i) {
+ // Mark every other lease as expired.
+ if (i % 2 == 0) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. We also substract the value
+ // of 10, 20, 30, 40 etc, depending on the lease index. This
+ // simulates different expiration times for various leases.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - i * 10;
+ // Set reclaimed state.
+ leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+
+ } else {
+ // Other leases are left as not expired - client last transmission
+ // time set to current time.
+ leases[i]->cltt_ = current_time;
+ }
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Keep reclaimed lease for 15 seconds after expiration.
+ const uint32_t lease_affinity_time = 15;
+
+ // Delete expired and reclaimed leases which have expired earlier than
+ // 15 seconds ago. This should affect leases with index 2, 3, 4 etc.
+ uint64_t deleted_num;
+ uint64_t should_delete_num = 0;
+ ASSERT_NO_THROW(
+ deleted_num = lmptr_->deleteExpiredReclaimedLeases6(lease_affinity_time)
+ );
+
+ for (size_t i = 0; i < leases.size(); ++i) {
+ // Obtain lease from the server.
+ Lease6Ptr lease = lmptr_->getLease6(leases[i]->type_, leases[i]->addr_);
+
+ // If the lease is reclaimed and the expiration time passed more than
+ // 15 seconds ago, the lease should have been deleted.
+ if (leases[i]->stateExpiredReclaimed() &&
+ ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) {
+ EXPECT_FALSE(lease) << "The following lease should have been"
+ " deleted: " << leases[i]->toText();
+ ++should_delete_num;
+
+ } else {
+ // If the lease is not reclaimed or it has expired less than
+ // 15 seconds ago, the lease should still be there.
+ EXPECT_TRUE(lease) << "The following lease shouldn't have been"
+ " deleted: " << leases[i]->toText();
+ }
+ }
+ // Check that the number of deleted leases is correct.
+ EXPECT_EQ(should_delete_num, deleted_num);
+
+ // Make sure we can make another attempt, when there are no more leases
+ // to be deleted.
+ ASSERT_NO_THROW(
+ deleted_num = lmptr_->deleteExpiredReclaimedLeases6(lease_affinity_time)
+ );
+ // No lease should have been deleted.
+ EXPECT_EQ(0, deleted_num);
+
+ // Reopen the database. This to ensure that the leases have been deleted
+ // from the persistent storage.
+ reopen(V6);
+
+ for (size_t i = 0; i < leases.size(); ++i) {
+ // Obtain lease from the server.
+ Lease6Ptr lease = lmptr_->getLease6(leases[i]->type_, leases[i]->addr_);
+
+ // If the lease is reclaimed and the expiration time passed more than
+ // 15 seconds ago, the lease should have been deleted.
+ if (leases[i]->stateExpiredReclaimed() &&
+ ((leases[i]->getExpirationTime() + lease_affinity_time) < current_time)) {
+ EXPECT_FALSE(lease) << "The following lease should have been"
+ " deleted: " << leases[i]->toText();
+
+ } else {
+ // If the lease is not reclaimed or it has expired less than
+ // 15 seconds ago, the lease should still be there.
+ EXPECT_TRUE(lease) << "The following lease shouldn't have been"
+ " deleted: " << leases[i]->toText();
+ }
+ }
+}
+
+void
+GenericLeaseMgrTest::testGetDeclinedLeases4() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Make sure we have at least 8 leases there.
+ ASSERT_GE(leases.size(), 8);
+
+ // Use the same current time for all leases.
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0; i < leases.size(); ++i) {
+
+ // Mark the first half of the leases as DECLINED
+ if (i < leases.size()/2) {
+ // Mark as declined with 1000 seconds of probation-period
+ leases[i]->decline(1000);
+ }
+
+ // Mark every second lease as expired.
+ if (i % 2 == 0) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. The expiration time also
+ // depends on the lease index, so as we can later check that the
+ // leases are ordered by the expiration time.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+ } else {
+ // Set current time as cltt for remaining leases. These leases are
+ // not expired.
+ leases[i]->cltt_ = current_time;
+ }
+
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // The leases are:
+ // 0 - declined, expired
+ // 1 - declined, not expired
+ // 2 - declined, expired
+ // 3 - declined, not expired
+ // 4 - default, expired
+ // 5 - default, not expired
+ // 6 - default, expired
+ // 7 - default, not expired
+
+ // Retrieve at most 1000 expired leases.
+ Lease4Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000));
+
+ // Leases with even indexes should be returned as expired. It shouldn't
+ // matter if the state is default or expired. The getExpiredLeases4 does
+ // not pay attention to state, just expiration timers.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ unsigned int declined_state = 0;
+ unsigned int default_state = 0;
+
+ // The expired leases should be returned from the most to least expired.
+ // This matches the reverse order to which they have been added.
+ for (Lease4Collection::reverse_iterator lease = expired_leases.rbegin();
+ lease != expired_leases.rend(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.rbegin(), lease));
+ // Multiple current index by two, because only leases with even indexes
+ // should have been returned.
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+
+ // Count leases in default and declined states
+ if ((*lease)->state_ == Lease::STATE_DEFAULT) {
+ default_state++;
+ } else if ((*lease)->state_ == Lease::STATE_DECLINED) {
+ declined_state++;
+ }
+ }
+
+ // LeaseMgr is supposed to return both default and declined leases
+ EXPECT_NE(0, declined_state);
+ EXPECT_NE(0, default_state);
+
+ // Update current time for the next test.
+ current_time = time(NULL);
+ // Also, remove expired leases collected during the previous test.
+ expired_leases.clear();
+
+ // This time let's reverse the expiration time and see if they will be returned
+ // in the correct order.
+ leases = createLeases4();
+ for (int i = 0; i < leases.size(); ++i) {
+
+ // Mark the second half of the leases as DECLINED
+ if (i >= leases.size()/2) {
+ // Mark as declined with 1000 seconds of probation-period
+ leases[i]->decline(1000);
+ }
+
+ // Update the time of expired leases with even indexes.
+ if (i % 2 == 0) {
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+
+ } else {
+ // Make sure remaining leases remain unexpired.
+ leases[i]->cltt_ = current_time + 100;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease4(leases[i]));
+ }
+
+ // Retrieve expired leases again. The limit of 0 means return all expired
+ // leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0));
+ // The same leases should be returned.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ // This time leases should be returned in the non-reverse order.
+ declined_state = 0;
+ default_state = 0;
+ for (Lease4Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+
+ // Count leases in default and declined states
+ if ((*lease)->state_ == Lease::STATE_DEFAULT) {
+ default_state++;
+ } else if ((*lease)->state_ == Lease::STATE_DECLINED) {
+ declined_state++;
+ }
+ }
+
+ // Check that both declined and default state leases were returned.
+ EXPECT_NE(0, declined_state);
+ EXPECT_NE(0, default_state);
+
+ // Remove expired leases again.
+ expired_leases.clear();
+
+ // Limit the number of leases to be returned to 2.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 2));
+
+ // Make sure we have exactly 2 leases returned.
+ ASSERT_EQ(2, expired_leases.size());
+
+ // Test that most expired leases have been returned.
+ for (Lease4Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+}
+
+void
+GenericLeaseMgrTest::testGetDeclinedLeases6() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ // Make sure we have at least 8 leases there.
+ ASSERT_GE(leases.size(), 8);
+
+ // Use the same current time for all leases.
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0; i < leases.size(); ++i) {
+
+ // Mark the first half of the leases as DECLINED
+ if (i < leases.size()/2) {
+ // Mark as declined with 1000 seconds of probation-period
+ leases[i]->decline(1000);
+ }
+
+ // Mark every second lease as expired.
+ if (i % 2 == 0) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. The expiration time also
+ // depends on the lease index, so as we can later check that the
+ // leases are ordered by the expiration time.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+ } else {
+ // Set current time as cltt for remaining leases. These leases are
+ // not expired.
+ leases[i]->cltt_ = current_time;
+ }
+
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // The leases are:
+ // 0 - declined, expired
+ // 1 - declined, not expired
+ // 2 - declined, expired
+ // 3 - declined, not expired
+ // 4 - default, expired
+ // 5 - default, not expired
+ // 6 - default, expired
+ // 7 - default, not expired
+
+ // Retrieve at most 1000 expired leases.
+ Lease6Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000));
+
+ // Leases with even indexes should be returned as expired. It shouldn't
+ // matter if the state is default or expired. The getExpiredLeases4 does
+ // not pay attention to state, just expiration timers.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ unsigned int declined_state = 0;
+ unsigned int default_state = 0;
+
+ // The expired leases should be returned from the most to least expired.
+ // This matches the reverse order to which they have been added.
+ for (Lease6Collection::reverse_iterator lease = expired_leases.rbegin();
+ lease != expired_leases.rend(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.rbegin(), lease));
+ // Multiple current index by two, because only leases with even indexes
+ // should have been returned.
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+
+ // Count leases in default and declined states
+ if ((*lease)->state_ == Lease::STATE_DEFAULT) {
+ default_state++;
+ } else if ((*lease)->state_ == Lease::STATE_DECLINED) {
+ declined_state++;
+ }
+ }
+
+ // LeaseMgr is supposed to return both default and declined leases
+ EXPECT_NE(0, declined_state);
+ EXPECT_NE(0, default_state);
+
+ // Update current time for the next test.
+ current_time = time(NULL);
+ // Also, remove expired leases collected during the previous test.
+ expired_leases.clear();
+
+ // This time let's reverse the expiration time and see if they will be returned
+ // in the correct order.
+ leases = createLeases6();
+ for (int i = 0; i < leases.size(); ++i) {
+
+ // Mark the second half of the leases as DECLINED
+ if (i >= leases.size()/2) {
+ // Mark as declined with 1000 seconds of probation-period
+ leases[i]->decline(1000);
+ }
+
+ // Update the time of expired leases with even indexes.
+ if (i % 2 == 0) {
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+
+ } else {
+ // Make sure remaining leases remain unexpired.
+ leases[i]->cltt_ = current_time + 100;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease6(leases[i]));
+ }
+
+ // Retrieve expired leases again. The limit of 0 means return all expired
+ // leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+ // The same leases should be returned.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2), expired_leases.size());
+
+ // This time leases should be returned in the non-reverse order.
+ declined_state = 0;
+ default_state = 0;
+ for (Lease6Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+
+ // Count leases in default and declined states
+ if ((*lease)->state_ == Lease::STATE_DEFAULT) {
+ default_state++;
+ } else if ((*lease)->state_ == Lease::STATE_DECLINED) {
+ declined_state++;
+ }
+ }
+
+ // Check that both declined and default state leases were returned.
+ EXPECT_NE(0, declined_state);
+ EXPECT_NE(0, default_state);
+
+ // Remove expired leases again.
+ expired_leases.clear();
+
+ // Limit the number of leases to be returned to 2.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 2));
+
+ // Make sure we have exactly 2 leases returned.
+ ASSERT_EQ(2, expired_leases.size());
+
+ // Test that most expired leases have been returned.
+ for (Lease6Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(leases[2 * index]->addr_, (*lease)->addr_);
+ }
+}
+
+void
+GenericLeaseMgrTest::checkStat(const std::string& name,
+ const int64_t expected_value) {
+ stats::ObservationPtr obs =
+ stats::StatsMgr::instance().getObservation(name);
+
+ ASSERT_TRUE(obs) << " stat: " << name << " not found ";
+ ASSERT_EQ(expected_value, obs->getInteger().first)
+ << " stat: " << name << " value wrong";
+}
+
+void
+GenericLeaseMgrTest::checkLeaseStats(const StatValMapList& expectedStats) {
+ // Global accumulators
+ int64_t declined_addresses = 0;
+ int64_t reclaimed_declined_addresses = 0;
+
+ // Iterate over all stats for each subnet
+ for (int subnet_idx = 0; subnet_idx < expectedStats.size(); ++subnet_idx) {
+ BOOST_FOREACH(StatValPair expectedStat, expectedStats[subnet_idx]) {
+ // Verify the per subnet value.
+ checkStat(stats::StatsMgr::generateName("subnet", subnet_idx+1,
+ expectedStat.first),
+ expectedStat.second);
+
+ // Add the value to globals as needed.
+ if (expectedStat.first == "declined-addresses") {
+ declined_addresses += expectedStat.second;
+ } else if (expectedStat.first == "reclaimed-declined-addresses") {
+ reclaimed_declined_addresses += expectedStat.second;
+ }
+ }
+ }
+
+ // Verify the globals.
+ checkStat("declined-addresses", declined_addresses);
+ checkStat("reclaimed-declined-addresses", reclaimed_declined_addresses);
+}
+
+Lease4Ptr
+GenericLeaseMgrTest::makeLease4(const std::string& address,
+ const SubnetID& subnet_id,
+ const uint32_t state,
+ const ConstElementPtr user_context /* = ConstElementPtr() */) {
+ Lease4Ptr lease(new Lease4());
+
+ // set the address
+ lease->addr_ = IOAddress(address);
+
+ // make a MAC from the address
+ std::vector<uint8_t> hwaddr = lease->addr_.toBytes();
+ hwaddr.push_back(0);
+ hwaddr.push_back(0);
+
+ lease->hwaddr_.reset(new HWAddr(hwaddr, HTYPE_ETHER));
+ lease->valid_lft_ = 86400;
+ lease->cltt_ = 168256;
+ lease->subnet_id_ = subnet_id;
+ lease->state_ = state;
+ if (user_context) {
+ lease->setContext(user_context);
+ }
+
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ return lease;
+}
+
+Lease6Ptr
+GenericLeaseMgrTest::makeLease6(const Lease::Type& type,
+ const std::string& address,
+ uint8_t prefix_len,
+ const SubnetID& subnet_id,
+ const uint32_t state,
+ const ConstElementPtr user_context /* = ConstElementPtr() */) {
+ IOAddress addr(address);
+
+ // make a DUID from the address
+ std::vector<uint8_t> bytes = addr.toBytes();
+ bytes.push_back(prefix_len);
+
+ Lease6Ptr lease(new Lease6(type, addr, DuidPtr(new DUID(bytes)), 77,
+ 16000, 24000, subnet_id, HWAddrPtr(),
+ prefix_len));
+ lease->state_ = state;
+ if (user_context) {
+ lease->setContext(user_context);
+ }
+
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ return lease;
+}
+
+void
+GenericLeaseMgrTest::testRecountLeaseStats4() {
+ using namespace stats;
+
+ StatsMgr::instance().removeAll();
+
+ // Create two subnets.
+ int num_subnets = 2;
+ CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4();
+ Subnet4Ptr subnet;
+ Pool4Ptr pool;
+
+ subnet.reset(new Subnet4(IOAddress("192.0.1.0"), 24, 1, 2, 3, 1));
+ pool.reset(new Pool4(IOAddress("192.0.1.0"), 24));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ subnet.reset(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 2));
+ pool.reset(new Pool4(IOAddress("192.0.2.0"), 24));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+ // Create the expected stats list. At this point, the only stat
+ // that should be non-zero is total-addresses.
+ StatValMapList expectedStats(num_subnets);
+ for (int i = 0; i < num_subnets; ++i) {
+ expectedStats[i]["total-addresses"] = 256;
+ expectedStats[i]["assigned-addresses"] = 0;
+ expectedStats[i]["declined-addresses"] = 0;
+ expectedStats[i]["reclaimed-declined-addresses"] = 0;
+ expectedStats[i]["reclaimed-leases"] = 0;
+ }
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Recount stats. We should have the same results.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Check that cumulative global stats always exist.
+ EXPECT_TRUE(StatsMgr::instance().getObservation("cumulative-assigned-addresses"));
+
+ // Now let's insert some leases into subnet 1.
+ int subnet_id = 1;
+
+ // Insert one lease in default state, i.e. assigned.
+ Lease4Ptr lease1 = makeLease4("192.0.1.1", subnet_id);
+
+ // Insert one lease in declined state.
+ Lease4Ptr lease2 = makeLease4("192.0.1.2", subnet_id, Lease::STATE_DECLINED);
+
+ // Insert one lease in the expired state.
+ makeLease4("192.0.1.3", subnet_id, Lease::STATE_EXPIRED_RECLAIMED);
+
+ // Insert another lease in default state, i.e. assigned.
+ makeLease4("192.0.1.4", subnet_id);
+
+ // Update the expected stats list for subnet 1.
+ expectedStats[subnet_id - 1]["assigned-addresses"] = 3; // 2 + 1 declined
+ expectedStats[subnet_id - 1]["declined-addresses"] = 1;
+
+ // Now let's add leases to subnet 2.
+ subnet_id = 2;
+
+ // Insert one declined lease.
+ makeLease4("192.0.2.2", subnet_id, Lease::STATE_DECLINED);
+
+ // Update the expected stats.
+ expectedStats[subnet_id - 1]["assigned-addresses"] = 1; // 0 + 1 declined
+ expectedStats[subnet_id - 1]["declined-addresses"] = 1;
+
+ // Now Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Delete some leases from subnet, and update the expected stats.
+ EXPECT_TRUE(lmptr_->deleteLease(lease1));
+ expectedStats[0]["assigned-addresses"] = 2;
+
+ EXPECT_TRUE(lmptr_->deleteLease(lease2));
+ expectedStats[0]["assigned-addresses"] = 1;
+ expectedStats[0]["declined-addresses"] = 0;
+
+ // Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+}
+
+
+void
+GenericLeaseMgrTest::testRecountLeaseStats6() {
+ using namespace stats;
+
+ StatsMgr::instance().removeAll();
+
+ // Create two subnets.
+ int num_subnets = 2;
+ CfgSubnets6Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets6();
+ Subnet6Ptr subnet;
+ Pool6Ptr pool;
+ StatValMapList expectedStats(num_subnets);
+
+ int subnet_id = 1;
+ subnet.reset(new Subnet6(IOAddress("3001:1::"), 64, 1, 2, 3, 4, subnet_id));
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("3001:1::"),
+ IOAddress("3001:1::FF")));
+ subnet->addPool(pool);
+ expectedStats[subnet_id - 1]["total-nas"] = 256;
+
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("3001:1:2::"),96,112));
+ subnet->addPool(pool);
+ expectedStats[subnet_id - 1]["total-pds"] = 65536;
+ cfg->add(subnet);
+
+ ++subnet_id;
+ subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4,
+ subnet_id));
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 120));
+ subnet->addPool(pool);
+ expectedStats[subnet_id - 1]["total-nas"] = 256;
+ expectedStats[subnet_id - 1]["total-pds"] = 0;
+ cfg->add(subnet);
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+
+ // Create the expected stats list. At this point, the only stat
+ // that should be non-zero is total-nas/total-pds.
+ for (int i = 0; i < num_subnets; ++i) {
+ expectedStats[i]["assigned-nas"] = 0;
+ expectedStats[i]["declined-addresses"] = 0;
+ expectedStats[i]["reclaimed-declined-addresses"] = 0;
+ expectedStats[i]["assigned-pds"] = 0;
+ expectedStats[i]["reclaimed-leases"] = 0;
+ }
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+
+ // Recount stats. We should have the same results.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats6());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Check that cumulative global stats always exist.
+ EXPECT_TRUE(StatsMgr::instance().getObservation("cumulative-assigned-nas"));
+ EXPECT_TRUE(StatsMgr::instance().getObservation("cumulative-assigned-pds"));
+
+ // Now let's insert some leases into subnet 1.
+ subnet_id = 1;
+
+ // Insert three assigned NAs.
+ makeLease6(Lease::TYPE_NA, "3001:1::1", 0, subnet_id);
+ Lease6Ptr lease2 = makeLease6(Lease::TYPE_NA, "3001:1::2", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "3001:1::3", 0, subnet_id);
+ expectedStats[subnet_id - 1]["assigned-nas"] = 5; // 3 + 2 declined
+
+ // Insert two declined NAs.
+ makeLease6(Lease::TYPE_NA, "3001:1::4", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ makeLease6(Lease::TYPE_NA, "3001:1::5", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ expectedStats[subnet_id - 1]["declined-addresses"] = 2;
+
+ // Insert one expired NA.
+ makeLease6(Lease::TYPE_NA, "3001:1::6", 0, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+
+ // Insert two assigned PDs.
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0100::", 112, subnet_id);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0200::", 112, subnet_id);
+ expectedStats[subnet_id - 1]["assigned-pds"] = 2;
+
+ // Insert two expired PDs.
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0300::", 112, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0400::", 112, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+
+ // Now let's add leases to subnet 2.
+ subnet_id = 2;
+
+ // Insert two assigned NAs.
+ makeLease6(Lease::TYPE_NA, "2001:db81::1", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "2001:db81::2", 0, subnet_id);
+ expectedStats[subnet_id - 1]["assigned-nas"] = 3; // 2 + 1 declined
+
+ // Insert one declined NA.
+ Lease6Ptr lease3 = makeLease6(Lease::TYPE_NA, "2001:db81::3", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ expectedStats[subnet_id - 1]["declined-addresses"] = 1;
+
+ // Now Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats6());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+
+ // Delete some leases and update the expected stats.
+ EXPECT_TRUE(lmptr_->deleteLease(lease2));
+ expectedStats[0]["assigned-nas"] = 4;
+
+ EXPECT_TRUE(lmptr_->deleteLease(lease3));
+ expectedStats[1]["assigned-nas"] = 2;
+ expectedStats[1]["declined-addresses"] = 0;
+
+ // Recount the stats.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats6());
+
+ // Make sure stats are as expected.
+ ASSERT_NO_FATAL_FAILURE(checkLeaseStats(expectedStats));
+}
+
+void
+GenericLeaseMgrTest::testWipeLeases6() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease6Ptr> leases = createLeases6();
+ leases[0]->subnet_id_ = 1;
+ leases[1]->subnet_id_ = 1;
+ leases[2]->subnet_id_ = 1;
+ leases[3]->subnet_id_ = 22;
+ leases[4]->subnet_id_ = 333;
+ leases[5]->subnet_id_ = 333;
+ leases[6]->subnet_id_ = 333;
+ leases[7]->subnet_id_ = 333;
+
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Let's try something simple. There shouldn't be any leases in
+ // subnet 2. The keep deleting the leases, perhaps in a different
+ // order they were added.
+ EXPECT_EQ(0, lmptr_->wipeLeases6(2));
+ EXPECT_EQ(4, lmptr_->wipeLeases6(333));
+ EXPECT_EQ(3, lmptr_->wipeLeases6(1));
+ EXPECT_EQ(1, lmptr_->wipeLeases6(22));
+
+ // All the leases should be gone now. Check that that repeated
+ // attempt to delete them will not result in any additional removals.
+ EXPECT_EQ(0, lmptr_->wipeLeases6(1));
+ EXPECT_EQ(0, lmptr_->wipeLeases6(22));
+ EXPECT_EQ(0, lmptr_->wipeLeases6(333));
+}
+
+void
+GenericLeaseMgrTest::testWipeLeases4() {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ leases[0]->subnet_id_ = 1;
+ leases[1]->subnet_id_ = 1;
+ leases[2]->subnet_id_ = 1;
+ leases[3]->subnet_id_ = 22;
+ leases[4]->subnet_id_ = 333;
+ leases[5]->subnet_id_ = 333;
+ leases[6]->subnet_id_ = 333;
+ leases[7]->subnet_id_ = 333;
+
+ for (size_t i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Let's try something simple. There shouldn't be any leases in
+ // subnet 2. The keep deleting the leases, perhaps in a different
+ // order they were added.
+ EXPECT_EQ(0, lmptr_->wipeLeases4(2));
+ EXPECT_EQ(4, lmptr_->wipeLeases4(333));
+ EXPECT_EQ(3, lmptr_->wipeLeases4(1));
+ EXPECT_EQ(1, lmptr_->wipeLeases4(22));
+
+ // All the leases should be gone now. Check that that repeated
+ // attempt to delete them will not result in any additional removals.
+ EXPECT_EQ(0, lmptr_->wipeLeases4(1));
+ EXPECT_EQ(0, lmptr_->wipeLeases4(22));
+ EXPECT_EQ(0, lmptr_->wipeLeases4(333));
+}
+
+void
+LeaseMgrDbLostCallbackTest::SetUp() {
+ destroySchema();
+ createSchema();
+ isc::dhcp::LeaseMgrFactory::destroy();
+}
+
+void
+LeaseMgrDbLostCallbackTest::TearDown() {
+ destroySchema();
+ isc::dhcp::LeaseMgrFactory::destroy();
+}
+
+void
+LeaseMgrDbLostCallbackTest::testNoCallbackOnOpenFailure() {
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
+
+ // Set the connectivity recovered callback.
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1);
+
+ // Set the connectivity failed callback.
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1);
+
+ ASSERT_THROW(LeaseMgrFactory::create(invalidConnectString()),
+ DbOpenError);
+
+ io_service_->poll();
+
+ EXPECT_EQ(0, db_lost_callback_called_);
+ EXPECT_EQ(0, db_recovered_callback_called_);
+ EXPECT_EQ(0, db_failed_callback_called_);
+}
+
+void
+LeaseMgrDbLostCallbackTest::testDbLostAndRecoveredCallback() {
+ // Set the connectivity lost callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
+
+ // Set the connectivity recovered callback.
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1);
+
+ // Set the connectivity failed callback.
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1);
+
+ std::string access = validConnectString();
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ // Connect to the lease backend.
+ ASSERT_NO_THROW(LeaseMgrFactory::create(access));
+
+ // The most recently opened socket should be for our SQL client.
+ int sql_socket = test::findLastSocketFd();
+ ASSERT_TRUE(sql_socket > -1);
+
+ // Verify we can execute a query. We do not care if
+ // we find a lease or not.
+ LeaseMgr& lm = LeaseMgrFactory::instance();
+
+ Lease4Ptr lease;
+ ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")));
+
+ // Now close the sql socket out from under backend client
+ ASSERT_EQ(0, close(sql_socket));
+
+ // A query should fail with DbConnectionUnusable.
+ ASSERT_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")),
+ DbConnectionUnusable);
+
+ io_service_->poll();
+
+ // Our lost and recovered connectivity callback should have been invoked.
+ EXPECT_EQ(1, db_lost_callback_called_);
+ EXPECT_EQ(1, db_recovered_callback_called_);
+ EXPECT_EQ(0, db_failed_callback_called_);
+}
+
+void
+LeaseMgrDbLostCallbackTest::testDbLostAndFailedCallback() {
+ // Set the connectivity lost callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
+
+ // Set the connectivity recovered callback.
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1);
+
+ // Set the connectivity failed callback.
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1);
+
+ std::string access = validConnectString();
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ // Connect to the lease backend.
+ ASSERT_NO_THROW(LeaseMgrFactory::create(access));
+
+ // The most recently opened socket should be for our SQL client.
+ int sql_socket = test::findLastSocketFd();
+ ASSERT_TRUE(sql_socket > -1);
+
+ // Verify we can execute a query. We do not care if
+ // we find a lease or not.
+ LeaseMgr& lm = LeaseMgrFactory::instance();
+
+ Lease4Ptr lease;
+ ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")));
+
+ access = invalidConnectString();
+ // by adding an invalid access will cause the manager factory to throw
+ // resulting in failure to recreate the manager
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ // Now close the sql socket out from under backend client
+ ASSERT_EQ(0, close(sql_socket));
+
+ // A query should fail with DbConnectionUnusable.
+ ASSERT_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")),
+ DbConnectionUnusable);
+
+ io_service_->poll();
+
+ // Our lost and failed connectivity callback should have been invoked.
+ EXPECT_EQ(1, db_lost_callback_called_);
+ EXPECT_EQ(0, db_recovered_callback_called_);
+ EXPECT_EQ(1, db_failed_callback_called_);
+}
+
+void
+LeaseMgrDbLostCallbackTest::testDbLostAndRecoveredAfterTimeoutCallback() {
+ // Set the connectivity lost callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
+
+ // Set the connectivity recovered callback.
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1);
+
+ // Set the connectivity failed callback.
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1);
+
+ std::string access = validConnectString();
+ std::string extra = " max-reconnect-tries=3 reconnect-wait-time=1";
+ access += extra;
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ // Connect to the lease backend.
+ ASSERT_NO_THROW(LeaseMgrFactory::create(access));
+
+ // The most recently opened socket should be for our SQL client.
+ int sql_socket = test::findLastSocketFd();
+ ASSERT_TRUE(sql_socket > -1);
+
+ // Verify we can execute a query. We do not care if
+ // we find a lease or not.
+ LeaseMgr& lm = LeaseMgrFactory::instance();
+
+ Lease4Ptr lease;
+ ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")));
+
+ access = invalidConnectString();
+ access += extra;
+ // by adding an invalid access will cause the manager factory to throw
+ // resulting in failure to recreate the manager
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ // Now close the sql socket out from under backend client
+ ASSERT_EQ(0, close(sql_socket));
+
+ // A query should fail with DbConnectionUnusable.
+ ASSERT_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")),
+ DbConnectionUnusable);
+
+ io_service_->poll();
+
+ // Our lost connectivity callback should have been invoked.
+ EXPECT_EQ(1, db_lost_callback_called_);
+ EXPECT_EQ(0, db_recovered_callback_called_);
+ EXPECT_EQ(0, db_failed_callback_called_);
+
+ access = validConnectString();
+ access += extra;
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ sleep(1);
+
+ io_service_->poll();
+
+ // Our lost and recovered connectivity callback should have been invoked.
+ EXPECT_EQ(2, db_lost_callback_called_);
+ EXPECT_EQ(1, db_recovered_callback_called_);
+ EXPECT_EQ(0, db_failed_callback_called_);
+
+ sleep(1);
+
+ io_service_->poll();
+
+ // No callback should have been invoked.
+ EXPECT_EQ(2, db_lost_callback_called_);
+ EXPECT_EQ(1, db_recovered_callback_called_);
+ EXPECT_EQ(0, db_failed_callback_called_);
+}
+
+void
+LeaseMgrDbLostCallbackTest::testDbLostAndFailedAfterTimeoutCallback() {
+ // Set the connectivity lost callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
+
+ // Set the connectivity recovered callback.
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1);
+
+ // Set the connectivity failed callback.
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1);
+
+ std::string access = validConnectString();
+ std::string extra = " max-reconnect-tries=3 reconnect-wait-time=1";
+ access += extra;
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ // Connect to the lease backend.
+ ASSERT_NO_THROW(LeaseMgrFactory::create(access));
+
+ // The most recently opened socket should be for our SQL client.
+ int sql_socket = test::findLastSocketFd();
+ ASSERT_TRUE(sql_socket > -1);
+
+ // Verify we can execute a query. We do not care if
+ // we find a lease or not.
+ LeaseMgr& lm = LeaseMgrFactory::instance();
+
+ Lease4Ptr lease;
+ ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")));
+
+ access = invalidConnectString();
+ access += extra;
+ // by adding an invalid access will cause the manager factory to throw
+ // resulting in failure to recreate the manager
+ CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access);
+
+ // Now close the sql socket out from under backend client
+ ASSERT_EQ(0, close(sql_socket));
+
+ // A query should fail with DbConnectionUnusable.
+ ASSERT_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")),
+ DbConnectionUnusable);
+
+ io_service_->poll();
+
+ // Our lost connectivity callback should have been invoked.
+ EXPECT_EQ(1, db_lost_callback_called_);
+ EXPECT_EQ(0, db_recovered_callback_called_);
+ EXPECT_EQ(0, db_failed_callback_called_);
+
+ sleep(1);
+
+ io_service_->poll();
+
+ // Our lost connectivity callback should have been invoked.
+ EXPECT_EQ(2, db_lost_callback_called_);
+ EXPECT_EQ(0, db_recovered_callback_called_);
+ EXPECT_EQ(0, db_failed_callback_called_);
+
+ sleep(1);
+
+ io_service_->poll();
+
+ // Our lost and failed connectivity callback should have been invoked.
+ EXPECT_EQ(3, db_lost_callback_called_);
+ EXPECT_EQ(0, db_recovered_callback_called_);
+ EXPECT_EQ(1, db_failed_callback_called_);
+}
+
+void
+GenericLeaseMgrTest::checkLeaseRange(const Lease4Collection& returned,
+ const std::vector<std::string>& expected_addresses) {
+ ASSERT_EQ(expected_addresses.size(), returned.size());
+
+ for (auto a = returned.cbegin(); a != returned.cend(); ++a) {
+ EXPECT_EQ(expected_addresses[std::distance(returned.cbegin(), a)],
+ (*a)->addr_.toText());
+ }
+}
+
+void
+GenericLeaseMgrTest::checkQueryAgainstRowSet(const LeaseStatsQueryPtr& query,
+ const RowSet& expected_rows) {
+ ASSERT_TRUE(query) << "query is null";
+
+ int rows_matched = 0;
+ LeaseStatsRow row;
+ while (query->getNextRow(row)) {
+ auto found_row = expected_rows.find(row);
+ if (found_row == expected_rows.end()) {
+ ADD_FAILURE() << "query row not in expected set"
+ << " id: " << row.subnet_id_
+ << " type: " << row.lease_type_
+ << " state: " << row.lease_state_
+ << " count: " << row.state_count_;
+ } else {
+ if (row.state_count_ != (*found_row).state_count_) {
+ ADD_FAILURE() << "row count wrong for"
+ << " id: " << row.subnet_id_
+ << " type: " << row.lease_type_
+ << " state: " << row.lease_state_
+ << " count: " << row.state_count_
+ << "; expected: " << (*found_row).state_count_;
+ } else {
+ ++rows_matched;
+ }
+ }
+ }
+
+ ASSERT_EQ(rows_matched, expected_rows.size()) << "rows mismatched";
+}
+
+void
+GenericLeaseMgrTest::testLeaseStatsQuery4() {
+ // Create three subnets.
+ CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4();
+ Subnet4Ptr subnet;
+ Pool4Ptr pool;
+
+ subnet.reset(new Subnet4(IOAddress("192.0.1.0"), 24, 1, 2, 3, 1));
+ pool.reset(new Pool4(IOAddress("192.0.1.0"), 24));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ subnet.reset(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 2));
+ pool.reset(new Pool4(IOAddress("192.0.2.0"), 24));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ subnet.reset(new Subnet4(IOAddress("192.0.3.0"), 24, 1, 2, 3, 3));
+ pool.reset(new Pool4(IOAddress("192.0.3.0"), 24));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+ // Make sure invalid values throw.
+ LeaseStatsQueryPtr query;
+ ASSERT_THROW(query = lmptr_->startSubnetLeaseStatsQuery4(0), BadValue);
+ ASSERT_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery4(0,1), BadValue);
+ ASSERT_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery4(1,0), BadValue);
+ ASSERT_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery4(10,1), BadValue);
+
+ // Start tests with an empty expected row set.
+ RowSet expected_rows;
+
+ // Before we add leases, test an empty return for get all subnets
+ {
+ SCOPED_TRACE("GET ALL WITH NO LEASES");
+ ASSERT_NO_THROW(query = lmptr_->startLeaseStatsQuery4());
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Now let's insert some leases into subnet 1.
+ // Two leases in the default state, i.e. assigned.
+ // One lease in declined state.
+ // One lease in the expired state.
+ int subnet_id = 1;
+ makeLease4("192.0.1.1", subnet_id);
+ makeLease4("192.0.1.2", subnet_id, Lease::STATE_DECLINED);
+ makeLease4("192.0.1.3", subnet_id, Lease::STATE_EXPIRED_RECLAIMED);
+ makeLease4("192.0.1.4", subnet_id);
+
+ // Now let's add leases to subnet 2.
+ // One declined lease.
+ subnet_id = 2;
+ makeLease4("192.0.2.2", subnet_id, Lease::STATE_DECLINED);
+
+ // Now add leases to subnet 3
+ // Two leases in default state, i.e. assigned.
+ // One declined lease.
+ subnet_id = 3;
+ makeLease4("192.0.3.1", subnet_id);
+ makeLease4("192.0.3.2", subnet_id);
+ makeLease4("192.0.3.3", subnet_id, Lease::STATE_DECLINED);
+
+ // Test single subnet for non-matching subnet
+ {
+ SCOPED_TRACE("NO MATCHING SUBNET");
+ ASSERT_NO_THROW(query = lmptr_->startSubnetLeaseStatsQuery4(777));
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test an empty range
+ {
+ SCOPED_TRACE("EMPTY SUBNET RANGE");
+ ASSERT_NO_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery4(777, 900));
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test a single subnet
+ {
+ SCOPED_TRACE("SINGLE SUBNET");
+ // Add expected rows for Subnet 2
+ expected_rows.insert(LeaseStatsRow(2, Lease::STATE_DECLINED, 1));
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startSubnetLeaseStatsQuery4(2));
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test a range of subnets
+ {
+ SCOPED_TRACE("SUBNET RANGE");
+ // Add expected rows for Subnet 3
+ expected_rows.insert(LeaseStatsRow(3, Lease::STATE_DEFAULT, 2));
+ expected_rows.insert(LeaseStatsRow(3, Lease::STATE_DECLINED, 1));
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery4(2,3));
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test all subnets
+ {
+ SCOPED_TRACE("ALL SUBNETS");
+ // Add expected rows for Subnet 1
+ expected_rows.insert(LeaseStatsRow(1, Lease::STATE_DEFAULT, 2));
+ expected_rows.insert(LeaseStatsRow(1, Lease::STATE_DECLINED, 1));
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startLeaseStatsQuery4());
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+}
+
+void
+GenericLeaseMgrTest::testLeaseStatsQuery6() {
+ // Create three subnets.
+ CfgSubnets6Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets6();
+ Subnet6Ptr subnet;
+ Pool6Ptr pool;
+
+ int subnet_id = 1;
+ subnet.reset(new Subnet6(IOAddress("3001:1::"), 64, 1, 2, 3, 4, subnet_id));
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("3001:1::"),
+ IOAddress("3001:1::FF")));
+ subnet->addPool(pool);
+
+ pool.reset(new Pool6(Lease::TYPE_PD, IOAddress("3001:1:2::"),96,112));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ ++subnet_id;
+ subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4,
+ subnet_id));
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 120));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ ++subnet_id;
+ subnet.reset(new Subnet6(IOAddress("2002:db8:1::"), 64, 1, 2, 3, 4,
+ subnet_id));
+ pool.reset(new Pool6(Lease::TYPE_NA, IOAddress("2002:db8:1::"), 120));
+ subnet->addPool(pool);
+ cfg->add(subnet);
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+ // Make sure invalid values throw.
+ LeaseStatsQueryPtr query;
+ ASSERT_THROW(query = lmptr_->startSubnetLeaseStatsQuery6(0), BadValue);
+ ASSERT_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery6(0,1), BadValue);
+ ASSERT_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery6(1,0), BadValue);
+ ASSERT_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery6(10,1), BadValue);
+
+ // Start tests with an empty expected row set.
+ RowSet expected_rows;
+
+ // Before we add leases, test an empty return for get all subnets
+ {
+ SCOPED_TRACE("GET ALL WITH NO LEASES");
+ ASSERT_NO_THROW(query = lmptr_->startLeaseStatsQuery6());
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+
+ // Now let's insert some leases into subnet 1.
+ // Three assigned NAs.
+ // Two declined NAs.
+ // One expired NA.
+ // Two assigned PDs.
+ // Two expired PDs.
+ subnet_id = 1;
+ makeLease6(Lease::TYPE_NA, "3001:1::1", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "3001:1::2", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "3001:1::3", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "3001:1::4", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ makeLease6(Lease::TYPE_NA, "3001:1::5", 0, subnet_id,
+ Lease::STATE_DECLINED);
+ makeLease6(Lease::TYPE_NA, "3001:1::6", 0, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0100::", 112, subnet_id);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0200::", 112, subnet_id);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0300::", 112, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+ makeLease6(Lease::TYPE_PD, "3001:1:2:0400::", 112, subnet_id,
+ Lease::STATE_EXPIRED_RECLAIMED);
+
+ // Now let's add leases to subnet 2.
+ // Two assigned NAs
+ // One declined NAs
+ subnet_id = 2;
+ makeLease6(Lease::TYPE_NA, "2001:db81::1", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "2001:db81::2", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "2001:db81::3", 0, subnet_id,
+ Lease::STATE_DECLINED);
+
+ // Now let's add leases to subnet 3.
+ // Two assigned NAs
+ // One declined NAs
+ subnet_id = 3;
+ makeLease6(Lease::TYPE_NA, "2002:db81::1", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "2002:db81::2", 0, subnet_id);
+ makeLease6(Lease::TYPE_NA, "2002:db81::3", 0, subnet_id,
+ Lease::STATE_DECLINED);
+
+ // Test single subnet for non-matching subnet
+ {
+ SCOPED_TRACE("NO MATCHING SUBNET");
+ ASSERT_NO_THROW(query = lmptr_->startSubnetLeaseStatsQuery6(777));
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test an empty range
+ {
+ SCOPED_TRACE("EMPTY SUBNET RANGE");
+ ASSERT_NO_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery6(777, 900));
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test a single subnet
+ {
+ SCOPED_TRACE("SINGLE SUBNET");
+ // Add expected row for Subnet 2
+ expected_rows.insert(LeaseStatsRow(2, Lease::TYPE_NA, Lease::STATE_DEFAULT, 2));
+ expected_rows.insert(LeaseStatsRow(2, Lease::TYPE_NA, Lease::STATE_DECLINED, 1));
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startSubnetLeaseStatsQuery6(2));
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test a range of subnets
+ {
+ SCOPED_TRACE("SUBNET RANGE");
+ // Add expected rows for Subnet 3
+ expected_rows.insert(LeaseStatsRow(3, Lease::TYPE_NA, Lease::STATE_DEFAULT, 2));
+ expected_rows.insert(LeaseStatsRow(3, Lease::TYPE_NA, Lease::STATE_DECLINED, 1));
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startSubnetRangeLeaseStatsQuery6(2,3));
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+
+ // Test all subnets
+ {
+ SCOPED_TRACE("ALL SUBNETS");
+ // Add expected rows for Subnet 1
+ expected_rows.insert(LeaseStatsRow(1, Lease::TYPE_NA, Lease::STATE_DEFAULT, 3));
+ expected_rows.insert(LeaseStatsRow(1, Lease::TYPE_NA, Lease::STATE_DECLINED, 2));
+ expected_rows.insert(LeaseStatsRow(1, Lease::TYPE_PD, Lease::STATE_DEFAULT, 2));
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startLeaseStatsQuery6());
+
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+ }
+}
+
+void
+GenericLeaseMgrTest::testLeaseStatsQueryAttribution4() {
+ // Create two subnets for the same range.
+ CfgSubnets4Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets4();
+ Subnet4Ptr subnet;
+
+ subnet.reset(new Subnet4(IOAddress("192.0.1.0"), 24, 1, 2, 3, 1));
+ cfg->add(subnet);
+
+ // Note it is even allowed to use 192.0.1.1/24 here...
+ subnet.reset(new Subnet4(IOAddress("192.0.1.0"), 25, 1, 2, 3, 2));
+ cfg->add(subnet);
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+ LeaseStatsQueryPtr query;
+ RowSet expected_rows;
+
+ // Now let's insert two leases into subnet 1.
+ int subnet_id = 1;
+ makeLease4("192.0.1.1", subnet_id);
+ Lease4Ptr lease = makeLease4("192.0.1.2", subnet_id);
+
+ // And one lease into subnet 2.
+ subnet_id = 2;
+ makeLease4("192.0.1.3", subnet_id);
+
+ // Move a lease to the second subnet.
+ lease->subnet_id_ = subnet_id;
+ EXPECT_NO_THROW(lmptr_->updateLease4(lease));
+
+ // Add expected rows for Subnets.
+ expected_rows.insert(LeaseStatsRow(1, Lease::STATE_DEFAULT, 1));
+ expected_rows.insert(LeaseStatsRow(2, Lease::STATE_DEFAULT, 2));
+
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startLeaseStatsQuery4());
+
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+}
+
+void
+GenericLeaseMgrTest::testLeaseStatsQueryAttribution6() {
+ // Create two subnets.
+ CfgSubnets6Ptr cfg = CfgMgr::instance().getStagingCfg()->getCfgSubnets6();
+ Subnet6Ptr subnet;
+
+ int subnet_id = 1;
+ subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4,
+ subnet_id));
+ cfg->add(subnet);
+
+ ++subnet_id;
+ subnet.reset(new Subnet6(IOAddress("2001:db8:1::1"), 64, 1, 2, 3, 4,
+ subnet_id));
+ cfg->add(subnet);
+
+ ASSERT_NO_THROW(CfgMgr::instance().commit());
+
+ LeaseStatsQueryPtr query;
+ RowSet expected_rows;
+
+ // Now let's insert two leases into subnet 1.
+ subnet_id = 1;
+ makeLease6(Lease::TYPE_NA, "2001:db81::1", 0, subnet_id);
+ Lease6Ptr lease = makeLease6(Lease::TYPE_NA, "2001:db81::2", 0, subnet_id);
+
+ // And one lease into subnet 2.
+ subnet_id = 2;
+ makeLease6(Lease::TYPE_NA, "2001:db81::3", 0, subnet_id);
+
+ // Move a lease to the second subnet.
+ lease->subnet_id_ = subnet_id;
+ EXPECT_NO_THROW(lmptr_->updateLease6(lease));
+
+ // Add expected rows for Subnets.
+ expected_rows.insert(LeaseStatsRow(1, Lease::TYPE_NA,
+ Lease::STATE_DEFAULT, 1));
+ expected_rows.insert(LeaseStatsRow(2, Lease::TYPE_NA,
+ Lease::STATE_DEFAULT, 2));
+ // Start the query
+ ASSERT_NO_THROW(query = lmptr_->startLeaseStatsQuery6());
+
+ // Verify contents
+ checkQueryAgainstRowSet(query, expected_rows);
+}
+
+void
+GenericLeaseMgrTest::testLeaseLimits4() {
+ std::string text;
+ ElementPtr user_context;
+
+ // -- A limit of 0 always denies a lease. --
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "client-classes": [ { "name": "foo", "address-limit": 0 } ] } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits4(user_context));
+ EXPECT_EQ(text, "address limit 0 for client class \"foo\", current lease count 0");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "subnet": { "id": 1, "address-limit": 0 } } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits4(user_context));
+ EXPECT_EQ(text, "address limit 0 for subnet ID 1, current lease count 0");
+
+ // -- A limit of 1 with no leases should allow a lease. --
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "client-classes": [ { "name": "foo", "address-limit": 1 } ] } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits4(user_context));
+ EXPECT_EQ(text, "");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "subnet": { "id": 1, "address-limit": 1 } } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits4(user_context));
+ EXPECT_EQ(text, "");
+
+ // -- A limit of 1 with 1 current lease should deny further leases. --
+
+ makeLease4("192.0.1.1", 1, Lease::STATE_DEFAULT, Element::fromJSON(
+ R"({ "ISC": { "client-classes": [ "foo" ] } })"));
+
+ // Since we did not go through allocation engine stats won't be altered.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats4());
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "client-classes": [ { "name": "foo", "address-limit": 1 } ] } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits4(user_context));
+ EXPECT_EQ(text, "address limit 1 for client class \"foo\", current lease count 1");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "subnet": { "id": 1, "address-limit": 1 } } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits4(user_context));
+ EXPECT_EQ(text, "address limit 1 for subnet ID 1, current lease count 1");
+}
+
+void
+GenericLeaseMgrTest::testLeaseLimits6() {
+ std::string text;
+ ElementPtr user_context;
+
+ // -- A limit of 0 always denies a lease. --
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "client-classes": [ { "name": "foo", "address-limit": 0 } ] } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "address limit 0 for client class \"foo\", current lease count 0");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "subnet": { "id": 1, "address-limit": 0 } } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "address limit 0 for subnet ID 1, current lease count 0");
+
+ // -- A limit of 1 with no leases should allow a lease. --
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "client-classes": [ { "name": "foo", "address-limit": 1 } ] } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "subnet": { "id": 1, "address-limit": 1 } } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "");
+
+ // -- A limit of 1 with 1 current lease should deny further leases. --
+ makeLease6(Lease::TYPE_NA, "2001:db8::", 0, 1, Lease::STATE_DEFAULT, Element::fromJSON(
+ R"({ "ISC": { "client-classes": [ "foo" ] } })"));
+
+ makeLease6(Lease::TYPE_PD, "2001:db8:1::", 64, 1, Lease::STATE_DEFAULT, Element::fromJSON(
+ R"({ "ISC": { "client-classes": [ "foo" ] } })"));
+
+ // Since we did not go through allocation engine stats won't be altered.
+ ASSERT_NO_THROW(lmptr_->recountLeaseStats6());
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "client-classes": [ { "name": "foo", "address-limit": 1 } ] } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "address limit 1 for client class \"foo\", current lease count 1");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "client-classes": [ { "name": "foo", "prefix-limit": 1 } ] } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "prefix limit 1 for client class \"foo\", current lease count 1");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "subnet": { "id": 1, "address-limit": 1 } } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "address limit 1 for subnet ID 1, current lease count 1");
+
+ user_context = Element::fromJSON(R"({ "ISC": { "limits": {
+ "subnet": { "id": 1, "prefix-limit": 1 } } } })");
+ ASSERT_NO_THROW_LOG(text = LeaseMgrFactory::instance().checkLimits6(user_context));
+ EXPECT_EQ(text, "prefix limit 1 for subnet ID 1, current lease count 1");
+}
+
+ElementPtr
+GenericLeaseMgrTest::makeContextWithClasses(const std::list<ClientClass>& classes) {
+ ElementPtr ctx = Element::createMap();
+ if (classes.size()) {
+ ElementPtr clist = Element::createList();
+ for (auto client_class : classes ) {
+ clist->add(Element::create(client_class));
+ }
+
+ ElementPtr client_classes = Element::createMap();
+ client_classes->set("client-classes", clist);
+ ctx->set("ISC", client_classes);
+ }
+
+ return (ctx);
+}
+
+void
+GenericLeaseMgrTest::testClassLeaseCount4() {
+ // Make user-contexts with different class lists.
+ std::list<ClientClass> classes1{"water"};
+ ElementPtr ctx1 = makeContextWithClasses(classes1);
+
+ std::list<ClientClass> classes2{"melon"};
+ ElementPtr ctx2 = makeContextWithClasses(classes2);
+
+ // Counts should be 0.
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("water"));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("melon"));
+
+ // Create a lease to add to the lease store.
+ vector<Lease4Ptr> leases = createLeases4();
+ Lease4Ptr lease = leases[1];
+
+ // Set the lease state to STATE_DEFAULT so it classes should be counted.
+ lease->state_ = Lease::STATE_DEFAULT;
+
+ // Add class list 1 to the lease.
+ lease->setContext(ctx1);
+
+ // Add the lease to the lease store and verify class lease counts.
+ ASSERT_NO_THROW_LOG(lmptr_->addLease(lease));
+ EXPECT_EQ(1, lmptr_->getClassLeaseCount("water"));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("melon"));
+
+ // Re-fetch lease. This returns a copy of the persisted lease, which is
+ // what Kea logic always does. Fetches a copy. Otherwise we're changing
+ // the persisted lease which would make old and new the same thing.
+ lease = lmptr_->getLease4(lease->addr_);
+ ASSERT_TRUE(lease);
+
+ // Change the class list.
+ lease->setContext(ctx2);
+
+ // Update the lease in the lease store and verify class lease counts.
+ ASSERT_NO_THROW_LOG(lmptr_->updateLease4(lease));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("water"));
+ EXPECT_EQ(1, lmptr_->getClassLeaseCount("melon"));
+
+ lease = lmptr_->getLease4(lease->addr_);
+ ASSERT_TRUE(lease);
+
+ // Now delete the lease from the store and verify counts.
+ ASSERT_NO_THROW_LOG(lmptr_->deleteLease(lease));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("water"));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("melon"));
+
+ lease = lmptr_->getLease4(lease->addr_);
+ ASSERT_FALSE(lease);
+}
+
+void
+GenericLeaseMgrTest::testClassLeaseCount6(Lease::Type ltype) {
+ ASSERT_TRUE(ltype == Lease::TYPE_NA || ltype == Lease::TYPE_PD);
+
+ // Make user-contexts with different class lists.
+ std::list<ClientClass> classes1{"water"};
+ ElementPtr ctx1 = makeContextWithClasses(classes1);
+
+ std::list<ClientClass> classes2{"melon"};
+ ElementPtr ctx2 = makeContextWithClasses(classes2);
+
+ // Counts should be 0.
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("water", ltype));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("melon", ltype));
+
+ // Create a lease to add to the lease store.
+ vector<Lease6Ptr> leases = createLeases6();
+ Lease6Ptr lease = leases[1];
+ lease->type_ = ltype;
+
+ // Set the lease state to STATE_DEFAULT so it classes should be counted.
+ lease->state_ = Lease::STATE_DEFAULT;
+
+ // Add class list 1 to the lease.
+ lease->setContext(ctx1);
+
+ // Add the lease to the lease store and verify class lease counts.
+ ASSERT_NO_THROW_LOG(lmptr_->addLease(lease));
+ EXPECT_EQ(1, lmptr_->getClassLeaseCount("water", ltype));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("melon", ltype));
+
+ // Re-fetch lease. This returns a copy of the persisted lease, which is
+ // what Kea logic always does. Fetches a copy. Otherwise we're changing
+ // the persisted lease which would make old and new the same thing.
+ lease = lmptr_->getLease6(ltype, lease->addr_);
+ ASSERT_TRUE(lease);
+
+ // Change the class list.
+ lease->setContext(ctx2);
+
+ // Update the lease in the lease store and verify class lease counts.
+ ASSERT_NO_THROW_LOG(lmptr_->updateLease6(lease));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("water", ltype));
+ EXPECT_EQ(1, lmptr_->getClassLeaseCount("melon", ltype));
+
+ lease = lmptr_->getLease6(ltype, lease->addr_);
+ ASSERT_TRUE(lease);
+
+ // Now delete the lease from the store and verify counts.
+ ASSERT_NO_THROW_LOG(lmptr_->deleteLease(lease));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("water", ltype));
+ EXPECT_EQ(0, lmptr_->getClassLeaseCount("melon", ltype));
+
+ lease = lmptr_->getLease6(ltype, lease->addr_);
+ ASSERT_FALSE(lease);
+}
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc