Browse Source

[master] Merge branch 'trac3966'

Marcin Siodelski 9 years ago
parent
commit
419832a627

+ 37 - 6
src/bin/admin/scripts/mysql/dhcpdb_create.mysql

@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2013  Internet Systems Consortium.
+# Copyright (C) 2012-2015  Internet Systems Consortium.
 #
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
@@ -80,8 +80,8 @@ CREATE TABLE lease6 (
 
     ) ENGINE = INNODB;
 
-# Create search indexes for lease4 table 
-# index by iaid, subnet_id, and duid 
+# Create search indexes for lease4 table
+# index by iaid, subnet_id, and duid
 CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid);
 
 # ... and a definition of lease6 types.  This table is a convenience for
@@ -250,6 +250,40 @@ UPDATE schema_version
 SET version = '3', minor = '0';
 # This line concludes database upgrade to version 3.0.
 
+# This line starts database upgrade to version 4.0.
+# Upgrade extending MySQL schema with the state columns for lease tables.
+
+# Add state column to the lease4 table.
+ALTER TABLE lease4
+    ADD COLUMN state INT UNSIGNED DEFAULT 0;
+
+# Add state column to the lease6 table.
+ALTER TABLE lease6
+    ADD COLUMN state INT UNSIGNED DEFAULT 0;
+
+# Create indexes for querying leases in a given state and segregated
+# by the expiration time. One of the applications is to retrieve all
+# expired leases. However, these indexes can be also used to retrieve
+# leases in a given state regardless of the expiration time.
+CREATE INDEX lease4_by_state_expire ON lease4 (state, expire);
+CREATE INDEX lease6_by_state_expire ON lease6 (state, expire);
+
+# Create table holding mapping of the lease states to their names.
+# This is not used in queries from the DHCP server but rather in
+# direct queries from the lease database management tools.
+CREATE TABLE IF NOT EXISTS lease_state (
+  state INT UNSIGNED PRIMARY KEY NOT NULL,
+  name VARCHAR(64) NOT NULL);
+
+# Insert currently defined state names.
+INSERT INTO lease_state VALUES (0, "default");
+INSERT INTO lease_state VALUES (1, "declined");
+INSERT INTO lease_state VALUES (2, "expired-reclaimed");
+
+UPDATE schema_version
+SET version = '4', minor = '0';
+# This line concludes database upgrade to version 4.0.
+
 # Notes:
 #
 # Indexes
@@ -262,9 +296,6 @@ SET version = '3', minor = '0';
 #
 # The most likely additional indexes will cover the following columns:
 #
-# expire
-# To speed up the deletion of expired leases from the database.
-#
 # hwaddr and client_id
 # For lease stability: if a client requests a new lease, try to find an
 # existing or recently expired lease for it so that it can keep using the

+ 38 - 0
src/bin/admin/scripts/mysql/lease_dump_4.0.sh

@@ -0,0 +1,38 @@
+#!/bin/sh
+# Copyright (C) 2015  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Specifies the MySQL sql for schema-version 4.0 required to produce 
+# lease output that includes a header row containing column names,
+# followed by the lease data. The text is used in a single call 
+# to the mysql command line tool.
+
+# SQL for DHCPv4 leases
+lease4_dump_sql="\
+SELECT 'address', 'hwaddr', 'client_id', 'valid_lifetime', 'expire',\
+'subnet_id', 'fqdn_fwd', 'fqdn_rev', 'hostname', 'state';\
+SELECT INET_NTOA(address), IFNULL(HEX(hwaddr), ''), IFNULL(HEX(client_id), ''),\
+valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, state from lease4"
+
+# SQL for DHCPv6 leases
+lease6_dump_sql="\
+SELECT 'address', 'duid', 'valid_lifetime', 'expire',\
+'subnet_id', 'pref_lifetime', 'lease_type', 'iaid', 'prefix_len', 'fqdn_fwd',\
+'fqdn_rev', 'hostname', 'hwaddr', 'hwtype', 'hwaddr_source', 'state';\
+SELECT a.address, IFNULL(HEX(a.duid), ''), a.valid_lifetime,\
+a.expire, a.subnet_id, a.pref_lifetime, IFNULL(b.name, ''), a.iaid, a.prefix_len,\
+a.fqdn_fwd, a.fqdn_rev, hostname, IFNULL(HEX(hwaddr), ''), IFNULL(hwtype, ''),\
+IFNULL(hwaddr_source, ''), state \
+FROM lease6 a left outer join lease6_types b on (a.lease_type = b.lease_type)"

+ 4 - 4
src/bin/admin/tests/data/mysql.lease4_dump_test.reference.csv

@@ -1,4 +1,4 @@
-address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname
-0.0.0.10,3230,3330,40,2015-01-01 01:15:30,50,1,1,one.example.com
-0.0.0.11,,313233,40,2015-02-02 02:30:45,50,1,1,
-0.0.0.12,3232,,40,2015-03-03 11:01:07,50,1,1,three.example.com
+address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state
+0.0.0.10,3230,3330,40,2015-01-01 01:15:30,50,1,1,one.example.com,0
+0.0.0.11,,313233,40,2015-02-02 02:30:45,50,1,1,,0
+0.0.0.12,3232,,40,2015-03-03 11:01:07,50,1,1,three.example.com,0

+ 4 - 4
src/bin/admin/tests/data/mysql.lease6_dump_test.reference.csv

@@ -1,4 +1,4 @@
-address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source
-10,3230,30,2015-04-04 01:15:30,40,50,IA_TA,60,70,1,1,one.example.com,3830,90,100
-11,,30,2015-05-05 02:30:45,40,50,IA_TA,60,70,1,1,,3830,90,100
-12,3231,30,2015-06-06 11:01:07,40,50,IA_TA,60,70,1,1,three.example.com,3830,90,100
+address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state
+10,3230,30,2015-04-04 01:15:30,40,50,IA_TA,60,70,1,1,one.example.com,3830,90,100,0
+11,,30,2015-05-05 02:30:45,40,50,IA_TA,60,70,1,1,,3830,90,100,0
+12,3231,30,2015-06-06 11:01:07,40,50,IA_TA,60,70,1,1,three.example.com,3830,90,100,0

+ 6 - 6
src/bin/admin/tests/mysql_tests.sh.in

@@ -302,9 +302,9 @@ mysql_lease4_dump_test() {
 
     # Insert the reference record
     insert_sql="\
-insert into lease4 values(10,20,30,40,\"2015-01-01 01:15:30\",50,1,1,\"one.example.com\");\
-insert into lease4 values(11,NULL,123,40,\"2015-02-02 02:30:45\",50,1,1,\"\");\
-insert into lease4 values(12,22,NULL,40,\"2015-03-03 11:01:07\",50,1,1,\"three.example.com\");"
+insert into lease4 values(10,20,30,40,\"2015-01-01 01:15:30\",50,1,1,\"one.example.com\", 0);\
+insert into lease4 values(11,NULL,123,40,\"2015-02-02 02:30:45\",50,1,1,\"\", 0);\
+insert into lease4 values(12,22,NULL,40,\"2015-03-03 11:01:07\",50,1,1,\"three.example.com\", 0);"
 
     mysql_execute "$insert_sql"
     ERRCODE=$?
@@ -362,9 +362,9 @@ mysql_lease6_dump_test() {
 
     # Insert the reference record
     insert_sql="\
-insert into lease6 values(10,20,30,\"2015-04-04 01:15:30\",40,50,1,60,70,1,1,\"one.example.com\",80,90,100);\
-insert into lease6 values(11,NULL,30,\"2015-05-05 02:30:45\",40,50,1,60,70,1,1,\"\",80,90,100);\
-insert into lease6 values(12,21,30,\"2015-06-06 11:01:07\",40,50,1,60,70,1,1,\"three.example.com\",80,90,100);"
+insert into lease6 values(10,20,30,\"2015-04-04 01:15:30\",40,50,1,60,70,1,1,\"one.example.com\",80,90,100, 0);\
+insert into lease6 values(11,NULL,30,\"2015-05-05 02:30:45\",40,50,1,60,70,1,1,\"\",80,90,100, 0);\
+insert into lease6 values(12,21,30,\"2015-06-06 11:01:07\",40,50,1,60,70,1,1,\"three.example.com\",80,90,100, 0);"
 
     mysql_execute "$insert_sql"
     ERRCODE=$?

+ 43 - 6
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -231,17 +231,17 @@ A debug message issued when the server is attempting to delete a lease
 for the specified address from the memory file database for the specified
 address.
 
-% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4 deleting reclaimed leases expired more than %1 s ago
+% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4 deleting reclaimed IPv4 leases that expired more than %1 seconds ago
 A debug message issued when the server is removing reclaimed DHCPv4
 leases which have expired longer than a specified period of time.
-The argument specified the number of seconds since leases' expiration
-before they can be removed.
+The argument is the amount of time Kea waits after a reclaimed
+lease expires before considering its removal.
 
-% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6 deleting reclaimed leases expired more than %1 s ago
+% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6 deleting reclaimed IPv6 leases that expired more than %1 seconds ago
 A debug message issued when the server is removing reclaimed DHCPv6
 leases which have expired longer than a specified period of time.
-The argument specified the number of seconds since leases' expiration
-before they can be removed.
+The argument is the amount of time Kea waits after a reclaimed
+lease expires before considering its removal.
 
 % DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START starting deletion of %1 expired-reclaimed leases
 A debug message issued wheb the server has found expired-reclaimed
@@ -266,6 +266,16 @@ A debug message issued when the server is attempting to obtain an IPv4
 lease from the memory file database for a client with the specified
 client ID, hardware address and subnet ID.
 
+% DHCPSRV_MEMFILE_GET_EXPIRED4 obtaining maximum %1 of expired IPv4 leases
+A debug message issued when the server is attempting to obtain expired
+IPv4 leases to reclaim them. The maximum number of leases to be retrieved
+is logged in the message.
+
+% DHCPSRV_MEMFILE_GET_EXPIRED6 obtaining maximum %1 of expired IPv6 leases
+A debug message issued when the server is attempting to obtain expired
+IPv6 leases to reclaim them. The maximum number of leases to be retrieved
+is logged in the message.
+
 % DHCPSRV_MEMFILE_GET_HWADDR obtaining IPv4 leases for hardware address %1
 A debug message issued when the server is attempting to obtain a set of
 IPv4 leases from the memory file database for a client with the specified
@@ -391,6 +401,23 @@ connection including database name and username needed to access it
 A debug message issued when the server is attempting to delete a lease for
 the specified address from the MySQL database for the specified address.
 
+% DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED4 deleting reclaimed IPv4 leases that expired more than %1 seconds ago
+A debug message issued when the server is removing reclaimed DHCPv4
+leases which have expired longer than a specified period of time.
+The argument is the amount of time Kea waits after a reclaimed
+lease expires before considering its removal.
+
+% DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED6 deleting reclaimed IPv6 leases that expired more than %1 seconds ago
+A debug message issued when the server is removing reclaimed DHCPv6
+leases which have expired longer than a specified period of time.
+The argument is the amount of time Kea waits after a reclaimed
+lease expires before considering its removal.
+
+% DHCPSRV_MYSQL_DELETED_EXPIRED_RECLAIMED deleted %1 reclaimed leases from the database
+A debug message issued when the server has removed a number of reclaimed
+leases from the database. The number of removed leases is included in the
+message.
+
 % DHCPSRV_MYSQL_GET_ADDR4 obtaining IPv4 lease for address %1
 A debug message issued when the server is attempting to obtain an IPv4
 lease from the MySQL database for the specified address.
@@ -404,6 +431,16 @@ A debug message issued when the server is attempting to obtain a set
 of IPv4 leases from the MySQL database for a client with the specified
 client identification.
 
+% DHCPSRV_MYSQL_GET_EXPIRED4 obtaining maximum %1 of expired IPv4 leases
+A debug message issued when the server is attempting to obtain expired
+IPv4 leases to reclaim them. The maximum number of leases to be retrieved
+is logged in the message.
+
+% DHCPSRV_MYSQL_GET_EXPIRED6 obtaining maximum %1 of expired IPv6 leases
+A debug message issued when the server is attempting to obtain expired
+IPv6 leases to reclaim them. The maximum number of leases to be retrieved
+is logged in the message.
+
 % DHCPSRV_MYSQL_GET_HWADDR obtaining IPv4 leases for hardware address %1
 A debug message issued when the server is attempting to obtain a set
 of IPv4 leases from the MySQL database for a client with the specified

+ 6 - 0
src/lib/dhcpsrv/memfile_lease_mgr.cc

@@ -510,6 +510,9 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type,
 void
 Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
                                     const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_EXPIRED4)
+        .arg(max_leases);
+
     // Obtain the index which segragates leases by state and time.
     const Lease6StorageExpirationIndex& index = storage6_.get<ExpirationIndexTag>();
 
@@ -532,6 +535,9 @@ Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
 void
 Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
                                     const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_EXPIRED6)
+        .arg(max_leases);
+
     // Obtain the index which segragates leases by state and time.
     const Lease4StorageExpirationIndex& index = storage4_.get<ExpirationIndexTag>();
 

+ 212 - 59
src/lib/dhcpsrv/mysql_lease_mgr.cc

@@ -25,6 +25,7 @@
 
 #include <iostream>
 #include <iomanip>
+#include <limits.h>
 #include <sstream>
 #include <string>
 #include <time.h>
@@ -125,44 +126,65 @@ struct TaggedStatement {
 TaggedStatement tagged_statements[] = {
     {MySqlLeaseMgr::DELETE_LEASE4,
                     "DELETE FROM lease4 WHERE address = ?"},
+    {MySqlLeaseMgr::DELETE_LEASE4_STATE_EXPIRED,
+                    "DELETE FROM lease4 "
+                        "WHERE state = ? AND expire < ?"},
     {MySqlLeaseMgr::DELETE_LEASE6,
                     "DELETE FROM lease6 WHERE address = ?"},
+    {MySqlLeaseMgr::DELETE_LEASE6_STATE_EXPIRED,
+                    "DELETE FROM lease6 "
+                        "WHERE state = ? AND expire < ?"},
     {MySqlLeaseMgr::GET_LEASE4_ADDR,
                     "SELECT address, hwaddr, client_id, "
                         "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "state "
                             "FROM lease4 "
                             "WHERE address = ?"},
     {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
                     "SELECT address, hwaddr, client_id, "
                         "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "state "
                             "FROM lease4 "
                             "WHERE client_id = ?"},
     {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
                     "SELECT address, hwaddr, client_id, "
                         "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "state "
                             "FROM lease4 "
                             "WHERE client_id = ? AND subnet_id = ?"},
     {MySqlLeaseMgr::GET_LEASE4_HWADDR,
                     "SELECT address, hwaddr, client_id, "
                         "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "state "
                             "FROM lease4 "
                             "WHERE hwaddr = ?"},
     {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
                     "SELECT address, hwaddr, client_id, "
                         "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "state "
                             "FROM lease4 "
                             "WHERE hwaddr = ? AND subnet_id = ?"},
+    {MySqlLeaseMgr::GET_LEASE4_EXPIRE,
+                    "SELECT address, hwaddr, client_id, "
+                        "valid_lifetime, expire, subnet_id, "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "state "
+                            "FROM lease4 "
+                            "WHERE state != ? AND expire < ? "
+                            "ORDER BY expire "
+                            "LIMIT ?"},
     {MySqlLeaseMgr::GET_LEASE6_ADDR,
                     "SELECT address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
                         "lease_type, iaid, prefix_len, "
                         "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source "
+                        "hwaddr, hwtype, hwaddr_source, "
+                        "state "
                             "FROM lease6 "
                             "WHERE address = ? AND lease_type = ?"},
     {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
@@ -170,7 +192,8 @@ TaggedStatement tagged_statements[] = {
                         "expire, subnet_id, pref_lifetime, "
                         "lease_type, iaid, prefix_len, "
                         "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source "
+                        "hwaddr, hwtype, hwaddr_source, "
+                        "state "
                             "FROM lease6 "
                             "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
     {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
@@ -178,36 +201,50 @@ TaggedStatement tagged_statements[] = {
                         "expire, subnet_id, pref_lifetime, "
                         "lease_type, iaid, prefix_len, "
                         "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source "
+                        "hwaddr, hwtype, hwaddr_source, "
+                        "state "
                             "FROM lease6 "
                             "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
                             "AND lease_type = ?"},
+    {MySqlLeaseMgr::GET_LEASE6_EXPIRE,
+                    "SELECT address, duid, valid_lifetime, "
+                        "expire, subnet_id, pref_lifetime, "
+                        "lease_type, iaid, prefix_len, "
+                        "fqdn_fwd, fqdn_rev, hostname, "
+                        "hwaddr, hwtype, hwaddr_source, "
+                        "state "
+                            "FROM lease6 "
+                            "WHERE state != ? AND expire < ? "
+                            "ORDER BY expire "
+                            "LIMIT ?"},
     {MySqlLeaseMgr::GET_VERSION,
                     "SELECT version, minor FROM schema_version"},
     {MySqlLeaseMgr::INSERT_LEASE4,
                     "INSERT INTO lease4(address, hwaddr, client_id, "
                         "valid_lifetime, expire, subnet_id, "
-                        "fqdn_fwd, fqdn_rev, hostname) "
-                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+                        "fqdn_fwd, fqdn_rev, hostname, state) "
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
     {MySqlLeaseMgr::INSERT_LEASE6,
                     "INSERT INTO lease6(address, duid, valid_lifetime, "
                         "expire, subnet_id, pref_lifetime, "
                         "lease_type, iaid, prefix_len, "
                         "fqdn_fwd, fqdn_rev, hostname, "
-                        "hwaddr, hwtype, hwaddr_source) "
-                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+                        "hwaddr, hwtype, hwaddr_source, "
+                        "state) "
+                            "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
     {MySqlLeaseMgr::UPDATE_LEASE4,
                     "UPDATE lease4 SET address = ?, hwaddr = ?, "
                         "client_id = ?, valid_lifetime = ?, expire = ?, "
                         "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
-                        "hostname = ? "
+                        "hostname = ?, state = ? "
                             "WHERE address = ?"},
     {MySqlLeaseMgr::UPDATE_LEASE6,
                     "UPDATE lease6 SET address = ?, duid = ?, "
                         "valid_lifetime = ?, expire = ?, subnet_id = ?, "
                         "pref_lifetime = ?, lease_type = ?, iaid = ?, "
                         "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
-                        "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ? "
+                        "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
+                        "state = ? "
                             "WHERE address = ?"},
     // End of list sentinel
     {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
@@ -299,7 +336,7 @@ public:
 
 class MySqlLease4Exchange : public MySqlLeaseExchange {
     /// @brief Set number of database columns for this lease structure
-    static const size_t LEASE_COLUMNS = 9;
+    static const size_t LEASE_COLUMNS = 10;
 
 public:
     /// @brief Constructor
@@ -309,7 +346,8 @@ public:
     MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0),
                             client_id_null_(MLM_FALSE),
                             subnet_id_(0), valid_lifetime_(0),
-                            fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0) {
+                            fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0),
+                            state_(0) {
         memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
         memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
@@ -325,7 +363,8 @@ public:
         columns_[6] = "fqdn_fwd";
         columns_[7] = "fqdn_rev";
         columns_[8] = "hostname";
-        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
+        columns_[9] = "state";
+        BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
     }
 
     /// @brief Create MYSQL_BIND objects for Lease4 Pointer
@@ -447,11 +486,17 @@ public:
             // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                               // reasons, see memset() above
 
+            // state: uint32_t.
+            bind_[9].buffer_type = MYSQL_TYPE_LONG;
+            bind_[9].buffer = reinterpret_cast<char*>(&lease_->state_);
+            bind_[9].is_unsigned = MLM_TRUE;
+            // bind_[9].is_null = &MLM_FALSE; // commented out for performance
+                                              // reasons, see memset() above
             // Add the error flags
             setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
             // .. and check that we have the numbers correct at compile time.
-            BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
+            BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
 
         } catch (const std::exception& ex) {
             isc_throw(DbOperationError,
@@ -549,22 +594,29 @@ public:
         // bind_[8].is_null = &MLM_FALSE; // commented out for performance
                                           // reasons, see memset() above
 
+        // state:  uint32_t
+        bind_[9].buffer_type = MYSQL_TYPE_LONG;
+        bind_[9].buffer = reinterpret_cast<char*>(&state_);
+        bind_[9].is_unsigned = MLM_TRUE;
+        // bind_[9].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
+
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
         // .. and check that we have the numbers correct at compile time.
-        BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
+        BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
         return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
     }
 
-    /// @brief Copy Received Data into Lease6 Object
+    /// @brief Copy Received Data into Lease4 Object
     ///
     /// Called after the MYSQL_BIND array created by createBindForReceive()
     /// has been used, this copies data from the internal member variables
-    /// into a Lease4 objec.
+    /// into a Lease4 object.
     ///
     /// @return Lease4Ptr Pointer to a Lease6 object holding the relevant
     ///         data.
@@ -588,10 +640,12 @@ public:
         HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_, HTYPE_ETHER));
 
         // note that T1 and T2 are not stored
-        return (Lease4Ptr(new Lease4(addr4_, hwaddr,
+        Lease4Ptr lease(new Lease4(addr4_, hwaddr,
                                      client_id_buffer_, client_id_length_,
                                      valid_lifetime_, 0, 0, cltt, subnet_id_,
-                                     fqdn_fwd_, fqdn_rev_, hostname)));
+                                     fqdn_fwd_, fqdn_rev_, hostname));
+        lease->state_ = state_;
+        return (lease);
     }
 
     /// @brief Return columns in error
@@ -639,6 +693,7 @@ private:
     char            hostname_buffer_[HOSTNAME_MAX_LEN];
                                         ///< Client hostname
     unsigned long   hostname_length_;   ///< Client hostname length
+    uint32_t        state_;             ///< Lease state
 
 };
 
@@ -659,7 +714,7 @@ private:
 
 class MySqlLease6Exchange : public MySqlLeaseExchange {
     /// @brief Set number of database columns for this lease structure
-    static const size_t LEASE_COLUMNS = 15;
+    static const size_t LEASE_COLUMNS = 16;
 
 public:
     /// @brief Constructor
@@ -671,7 +726,8 @@ public:
                             pref_lifetime_(0), subnet_id_(0), valid_lifetime_(0),
                             fqdn_fwd_(false), fqdn_rev_(false),
                             hostname_length_(0), hwaddr_length_(0),
-                            hwaddr_null_(MLM_FALSE), hwtype_(0), hwaddr_source_(0) {
+                            hwaddr_null_(MLM_FALSE), hwtype_(0), hwaddr_source_(0),
+                            state_(0) {
         memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
         memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
@@ -694,7 +750,8 @@ public:
         columns_[12] = "hwaddr";
         columns_[13] = "hwtype";
         columns_[14] = "hwaddr_source";
-        BOOST_STATIC_ASSERT(14 < LEASE_COLUMNS);
+        columns_[15] = "state";
+        BOOST_STATIC_ASSERT(15 < LEASE_COLUMNS);
     }
 
     /// @brief Create MYSQL_BIND objects for Lease6 Pointer
@@ -904,6 +961,13 @@ public:
                 bind_[14].is_null = &hwaddr_null_;
             }
 
+            // state:  uint32_t
+            bind_[15].buffer_type = MYSQL_TYPE_LONG;
+            bind_[15].buffer = reinterpret_cast<char*>(&lease_->state_);
+            bind_[15].is_unsigned = MLM_TRUE;
+            // bind_[15].is_null = &MLM_FALSE; // commented out for performance
+                                               // reasons, see memset() above
+
             // Add the error flags
             setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
@@ -1051,11 +1115,17 @@ public:
         bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
         bind_[14].is_unsigned = MLM_TRUE;
 
+        // state:  uint32_t
+        bind_[15].buffer_type = MYSQL_TYPE_LONG;
+        bind_[15].buffer = reinterpret_cast<char*>(&state_);
+        bind_[15].is_unsigned = MLM_TRUE;
+        // bind_[15].is_null = &MLM_FALSE; // commented out for performance
+                                           // reasons, see memset() above
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
 
         // .. and check that we have the numbers correct at compile time.
-        BOOST_STATIC_ASSERT(14 < LEASE_COLUMNS);
+        BOOST_STATIC_ASSERT(15 < LEASE_COLUMNS);
 
         // Add the data to the vector.  Note the end element is one after the
         // end of the array.
@@ -1128,6 +1198,9 @@ public:
         MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
         result->cltt_ = cltt;
 
+        // Set state.
+        result->state_ = state_;
+
         return (result);
     }
 
@@ -1181,6 +1254,7 @@ private:
     my_bool         hwaddr_null_;       ///< Used when HWAddr is null
     uint16_t        hwtype_;            ///< Hardware type
     uint32_t        hwaddr_source_;     ///< Source of the hardware address
+    uint32_t        state_;             ///< Lease state.
 };
 
 
@@ -1290,13 +1364,32 @@ MySqlLeaseMgr::getDBVersion() {
 //    from a time read from the database into a local time.
 
 void
+MySqlLeaseMgr::convertToDatabaseTime(const time_t input_time,
+                                     MYSQL_TIME& output_time) {
+
+    // Convert to broken-out time
+    struct tm time_tm;
+    (void) localtime_r(&input_time, &time_tm);
+
+    // Place in output expire structure.
+    output_time.year = time_tm.tm_year + 1900;
+    output_time.month = time_tm.tm_mon + 1;     // Note different base
+    output_time.day = time_tm.tm_mday;
+    output_time.hour = time_tm.tm_hour;
+    output_time.minute = time_tm.tm_min;
+    output_time.second = time_tm.tm_sec;
+    output_time.second_part = 0;                // No fractional seconds
+    output_time.neg = my_bool(0);               // Not negative
+}
+
+void
 MySqlLeaseMgr::convertToDatabaseTime(const time_t cltt,
                                      const uint32_t valid_lifetime,
                                      MYSQL_TIME& expire) {
 
     // Calculate expiry time. Store it in the 64-bit value so as we can detect
     // overflows.
-    int64_t expire_time_64 = static_cast<int64_t>(cltt) +
+    const int64_t expire_time_64 = static_cast<int64_t>(cltt) +
         static_cast<int64_t>(valid_lifetime);
 
     // Even on 64-bit systems MySQL doesn't seem to accept the timestamps
@@ -1305,21 +1398,7 @@ MySqlLeaseMgr::convertToDatabaseTime(const time_t cltt,
         isc_throw(BadValue, "Time value is too large: " << expire_time_64);
     }
 
-    const time_t expire_time = static_cast<const time_t>(expire_time_64);
-
-    // Convert to broken-out time
-    struct tm expire_tm;
-    (void) localtime_r(&expire_time, &expire_tm);
-
-    // Place in output expire structure.
-    expire.year = expire_tm.tm_year + 1900;
-    expire.month = expire_tm.tm_mon + 1;     // Note different base
-    expire.day = expire_tm.tm_mday;
-    expire.hour = expire_tm.tm_hour;
-    expire.minute = expire_tm.tm_min;
-    expire.second = expire_tm.tm_sec;
-    expire.second_part = 0;                  // No fractional seconds
-    expire.neg = my_bool(0);                 // Not negative
+    convertToDatabaseTime(static_cast<time_t>(expire_time_64), expire);
 }
 
 void
@@ -1933,17 +2012,57 @@ MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
 }
 
 void
-MySqlLeaseMgr::getExpiredLeases6(Lease6Collection&, const size_t) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getExpiredLeases6 is currently"
-              " not implemented");
+MySqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
+                                 const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_EXPIRED6)
+        .arg(max_leases);
+    getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
 }
 
 void
-MySqlLeaseMgr::getExpiredLeases4(Lease4Collection&, const size_t) const {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::getExpiredLeases4 is currently"
-              " not implemented");
+MySqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
+                                 const size_t max_leases) const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_EXPIRED4)
+        .arg(max_leases);
+    getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
+}
+
+template<typename LeaseCollection>
+void
+MySqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
+                                      const size_t max_leases,
+                                      StatementIndex statement_index) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[3];
+    memset(inbind, 0, sizeof(inbind));
+
+    // Exclude reclaimed leases.
+    uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
+    inbind[0].buffer_type = MYSQL_TYPE_LONG;
+    inbind[0].buffer = reinterpret_cast<char*>(&state);
+    inbind[0].is_unsigned = MLM_TRUE;
+
+    // Expiration timestamp.
+    MYSQL_TIME expire_time;
+    convertToDatabaseTime(time(NULL), expire_time);
+    inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
+    inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
+    inbind[1].buffer_length = sizeof(expire_time);
+
+    // If the number of leases is 0, we will return all leases. This is
+    // achieved by setting the limit to a very high value.
+    uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
+        std::numeric_limits<uint32_t>::max();
+    inbind[2].buffer_type = MYSQL_TYPE_LONG;
+    inbind[2].buffer = reinterpret_cast<char*>(&limit);
+    inbind[2].is_unsigned = MLM_TRUE;
+
+    // Get the data
+    getLeaseCollection(statement_index, inbind, expired_leases);
 }
 
+
+
 // Update lease methods.  These comprise common code that handles the actual
 // update, and type-specific methods that set up the parameters for the prepared
 // statement depending on the type of lease.
@@ -2036,7 +2155,7 @@ MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
 // case, a single method for both V4 and V6 addresses) and a common method that
 // handles the common processing.
 
-bool
+uint64_t
 MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
 
     // Bind the input parameters to the statement
@@ -2049,7 +2168,7 @@ MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
 
     // See how many rows were affected.  Note that the statement may delete
     // multiple rows.
-    return (mysql_stmt_affected_rows(statements_[stindex]) > 0);
+    return (static_cast<uint64_t>(mysql_stmt_affected_rows(statements_[stindex])));
 }
 
 
@@ -2069,7 +2188,7 @@ MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
         inbind[0].buffer = reinterpret_cast<char*>(&addr4);
         inbind[0].is_unsigned = MLM_TRUE;
 
-        return (deleteLeaseCommon(DELETE_LEASE4, inbind));
+        return (deleteLeaseCommon(DELETE_LEASE4, inbind) > 0);
 
     } else {
         std::string addr6 = addr.toText();
@@ -2082,22 +2201,56 @@ MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
         inbind[0].buffer_length = addr6_length;
         inbind[0].length = &addr6_length;
 
-        return (deleteLeaseCommon(DELETE_LEASE6, inbind));
+        return (deleteLeaseCommon(DELETE_LEASE6, inbind) > 0);
     }
 }
 
 uint64_t
-MySqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t) {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::deleteExpiredReclaimedLeases4"
-              " is not implemented");
+MySqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED4)
+        .arg(secs);
+    return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
 }
 
 uint64_t
-MySqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t) {
-    isc_throw(NotImplemented, "MySqlLeaseMgr::deleteExpiredReclaimedLeases6"
-              " is not implemented");
+MySqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED6)
+        .arg(secs);
+    return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
 }
 
+uint64_t
+MySqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
+                                                  StatementIndex statement_index) {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[2];
+    memset(inbind, 0, sizeof(inbind));
+
+    // State is reclaimed.
+    uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
+    inbind[0].buffer_type = MYSQL_TYPE_LONG;
+    inbind[0].buffer = reinterpret_cast<char*>(&state);
+    inbind[0].is_unsigned = MLM_TRUE;
+
+    // Expiration timestamp.
+    MYSQL_TIME expire_time;
+    convertToDatabaseTime(time(NULL) - static_cast<time_t>(secs), expire_time);
+    inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
+    inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
+    inbind[1].buffer_length = sizeof(expire_time);
+
+    // Get the number of deleted leases and log it.
+    uint64_t deleted_leases = deleteLeaseCommon(statement_index, inbind);
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_MYSQL_DELETED_EXPIRED_RECLAIMED)
+        .arg(deleted_leases);
+
+    return (deleted_leases);
+}
+
+
 
 // Miscellaneous database methods.
 

+ 63 - 19
src/lib/dhcpsrv/mysql_lease_mgr.h

@@ -455,6 +455,14 @@ public:
     /// the lease file - which may be read by the user - it is the expiry time
     /// of the lease.
 
+    /// @brief Convert time_t value to database time.
+    ///
+    /// @param input_time A time_t value representing time.
+    /// @param output_time Reference to MYSQL_TIME object where converted time
+    ///        will be put.
+    static
+    void convertToDatabaseTime(const time_t input_time, MYSQL_TIME& output_time);
+
     /// @brief Convert Lease Time to Database Times
     ///
     /// Within the DHCP servers, times are stored as client last transmit time
@@ -504,22 +512,26 @@ public:
     ///
     /// The contents of the enum are indexes into the list of SQL statements
     enum StatementIndex {
-        DELETE_LEASE4,              // Delete from lease4 by address
-        DELETE_LEASE6,              // Delete from lease6 by address
-        GET_LEASE4_ADDR,            // Get lease4 by address
-        GET_LEASE4_CLIENTID,        // Get lease4 by client ID
-        GET_LEASE4_CLIENTID_SUBID,  // Get lease4 by client ID & subnet ID
-        GET_LEASE4_HWADDR,          // Get lease4 by HW address
-        GET_LEASE4_HWADDR_SUBID,    // Get lease4 by HW address & subnet ID
-        GET_LEASE6_ADDR,            // Get lease6 by address
-        GET_LEASE6_DUID_IAID,       // Get lease6 by DUID and IAID
-        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
-        GET_VERSION,                // Obtain version number
-        INSERT_LEASE4,              // Add entry to lease4 table
-        INSERT_LEASE6,              // Add entry to lease6 table
-        UPDATE_LEASE4,              // Update a Lease4 entry
-        UPDATE_LEASE6,              // Update a Lease6 entry
-        NUM_STATEMENTS              // Number of statements
+        DELETE_LEASE4,               // Delete from lease4 by address
+        DELETE_LEASE4_STATE_EXPIRED, // Delete expired lease4 in a given state
+        DELETE_LEASE6,               // Delete from lease6 by address
+        DELETE_LEASE6_STATE_EXPIRED, // Delete expired lease6 in a given state
+        GET_LEASE4_ADDR,             // Get lease4 by address
+        GET_LEASE4_CLIENTID,         // Get lease4 by client ID
+        GET_LEASE4_CLIENTID_SUBID,   // Get lease4 by client ID & subnet ID
+        GET_LEASE4_HWADDR,           // Get lease4 by HW address
+        GET_LEASE4_HWADDR_SUBID,     // Get lease4 by HW address & subnet ID
+        GET_LEASE4_EXPIRE,           // Get lease4 by expiration.
+        GET_LEASE6_ADDR,             // Get lease6 by address
+        GET_LEASE6_DUID_IAID,        // Get lease6 by DUID and IAID
+        GET_LEASE6_DUID_IAID_SUBID,  // Get lease6 by DUID, IAID and subnet ID
+        GET_LEASE6_EXPIRE,           // Get lease6 by expiration.
+        GET_VERSION,                 // Obtain version number
+        INSERT_LEASE4,               // Add entry to lease4 table
+        INSERT_LEASE6,               // Add entry to lease6 table
+        UPDATE_LEASE4,               // Update a Lease4 entry
+        UPDATE_LEASE6,               // Update a Lease6 entry
+        NUM_STATEMENTS               // Number of statements
     };
 
 private:
@@ -665,6 +677,26 @@ private:
     void getLease(StatementIndex stindex, MYSQL_BIND* bind,
                    Lease6Ptr& result) const;
 
+
+    /// @brief Get expired leases common code.
+    ///
+    /// This method retrieves expired and not reclaimed leases from the
+    /// lease database. The returned leases are ordered by the expiration
+    /// time. The maximum number of leases to be returned is specified
+    /// as an argument.
+    ///
+    /// @param [out] expired_leases Reference to the container where the
+    ///        retrieved leases are put.
+    /// @param max_leases Maximum number of leases to be returned.
+    /// @param statement_index One of the @c GET_LEASE4_EXPIRE or
+    ///        @c GET_LEASE6_EXPIRE.
+    ///
+    /// @tparam One of the @c Lease4Collection or @c Lease6Collection.
+    template<typename LeaseCollection>
+    void getExpiredLeasesCommon(LeaseCollection& expired_leases,
+                                const size_t max_leases,
+                                StatementIndex statement_index) const;
+
     /// @brief Update lease common code
     ///
     /// Holds the common code for updating a lease.  It binds the parameters
@@ -696,12 +728,24 @@ private:
     ///        (Note that the number is determined by the number of parameters
     ///        in the statement.)
     ///
-    /// @return true if one or more rows were deleted, false if none were
-    ///         deleted.
+    /// @return Number of deleted leases.
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.
-    bool deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind);
+    uint64_t deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind);
+
+    /// @brief Delete expired-reclaimed leases.
+    ///
+    /// @param secs Number of seconds since expiration of leases before
+    /// they can be removed. Leases which have expired later than this
+    /// time will not be deleted.
+    /// @param statement_index One of the @c DELETE_LEASE4_STATE_EXPIRED or
+    ///        @c DELETE_LEASE6_STATE_EXPIRED.
+    ///
+    /// @return Number of leases deleted.
+    uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
+                                                StatementIndex statement_index);
+
 
     /// @brief Check Error and Throw Exception
     ///

+ 32 - 0
src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc

@@ -532,4 +532,36 @@ TEST_F(MySqlLeaseMgrTest, testLease6HWTypeAndSource) {
     testLease6HWTypeAndSource();
 }
 
+/// @brief Check that the expired DHCPv4 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(MySqlLeaseMgrTest, getExpiredLeases4) {
+    testGetExpiredLeases4();
+}
+
+/// @brief Check that the expired DHCPv6 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(MySqlLeaseMgrTest, getExpiredLeases6) {
+    testGetExpiredLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv6 leases are removed.
+TEST_F(MySqlLeaseMgrTest, deleteExpiredReclaimedLeases6) {
+    testDeleteExpiredReclaimedLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv4 leases are removed.
+TEST_F(MySqlLeaseMgrTest, deleteExpiredReclaimedLeases4) {
+    testDeleteExpiredReclaimedLeases4();
+}
+
 }; // Of anonymous namespace

+ 29 - 4
src/lib/dhcpsrv/tests/schema_mysql_copy.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -100,9 +100,9 @@ const char* create_statement[] = {
 
     // Schema upgrade to 2.0 starts here.
     "ALTER TABLE lease6 "
-    "ADD COLUMN hwaddr varbinary(20),"
-    "ADD COLUMN hwtype smallint unsigned,"
-    "ADD COLUMN hwaddr_source int unsigned;",
+        "ADD COLUMN hwaddr varbinary(20),"
+        "ADD COLUMN hwtype smallint unsigned,"
+        "ADD COLUMN hwaddr_source int unsigned;",
 
     // Production schema has lease_hwaddr_source table. It is not used by
     // kea code and is simply useful for formulating more human readable
@@ -125,6 +125,31 @@ const char* create_statement[] = {
     "UPDATE schema_version SET version=\"2\", minor=\"0\";",
     // Schema upgrade to 2.0 ends here.
 
+    // Schema upgrade to 4.0 starts here.
+    "ALTER TABLE lease4 "
+        "ADD COLUMN state INT UNSIGNED DEFAULT 0",
+
+    "ALTER TABLE lease6 "
+        "ADD COLUMN state INT UNSIGNED DEFAULT 0",
+
+    "CREATE INDEX lease4_by_state_expire ON lease4 (state, expire)",
+    "CREATE INDEX lease6_by_state_expire ON lease6 (state, expire)",
+
+    // Production schema includes the lease_state table which maps
+    // the lease states to their names. This is not used in the unit tests
+    // so it is commented out.
+
+    /*"CREATE TABLE IF NOT EXISTS lease_state (",
+        "state INT UNSIGNED PRIMARY KEY NOT NULL,"
+        "name VARCHAR(64) NOT NULL);",
+
+    "INSERT INTO lease_state VALUES (0, \"default\");",
+    "INSERT INTO lease_state VALUES (1, \"declined\");",
+    "INSERT INTO lease_state VALUES (2, \"expired-reclaimed\");",*/
+
+
+    // Schema upgrade to 4.0 ends here.
+
     NULL
 };