Browse Source

[master] Merge branch 'trac3965'

Marcin Siodelski 9 years ago
parent
commit
dd5b954535

+ 21 - 21
src/bin/lfc/tests/lfc_controller_unittests.cc

@@ -90,11 +90,11 @@ protected:
         cstr_ = base_dir + "/" + "config_file";     // config
         cstr_ = base_dir + "/" + "config_file";     // config
 
 
         v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
         v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n";
+                  "fqdn_fwd,fqdn_rev,hostname,state\n";
 
 
         v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id,"
         v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-                  "fqdn_rev,hostname,hwaddr\n";
+                  "fqdn_rev,hostname,hwaddr,state\n";
 
 
         // and remove any outstanding test files
         // and remove any outstanding test files
         removeTestFile();
         removeTestFile();
@@ -402,26 +402,26 @@ TEST_F(LFCControllerTest, launch4) {
     // We have several entries for different leases, the naming is:
     // We have several entries for different leases, the naming is:
     // <lease letter>_<version#>
     // <lease letter>_<version#>
     string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
     string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                 "200,200,8,1,1,host.example.com\n";
+                 "200,200,8,1,1,host.example.com,1\n";
     string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
     string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                 "200,500,8,1,1,host.example.com\n";
+                 "200,500,8,1,1,host.example.com,1\n";
     string a_3 = "192.0.2.1,06:07:08:09:0a:bc,,"
     string a_3 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                 "200,800,8,1,1,host.example.com\n";
+                 "200,800,8,1,1,host.example.com,1\n";
 
 
     string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
     string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
-                 "100,100,7,0,0,\n";
+                 "100,100,7,0,0,,1\n";
     string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
     string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
-                 "100,135,7,0,0,\n";
+                 "100,135,7,0,0,,1\n";
     string b_3 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
     string b_3 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
-                 "100,150,7,0,0,\n";
+                 "100,150,7,0,0,,1\n";
 
 
     string c_1 = "192.0.2.3,,a:11:01:04,"
     string c_1 = "192.0.2.3,,a:11:01:04,"
-                 "200,200,8,1,1,host.example.com\n";
+                 "200,200,8,1,1,host.example.com,1\n";
 
 
     string d_1 = "192.0.2.5,16:17:18:19:1a:bc,,"
     string d_1 = "192.0.2.5,16:17:18:19:1a:bc,,"
-                 "200,200,8,1,1,host.example.com\n";
+                 "200,200,8,1,1,host.example.com,1\n";
     string d_2 = "192.0.2.5,16:17:18:19:1a:bc,,"
     string d_2 = "192.0.2.5,16:17:18:19:1a:bc,,"
-                 "0,200,8,1,1,host.example.com\n";
+                 "0,200,8,1,1,host.example.com,1\n";
 
 
     // Subtest 1: both previous and copy available.
     // Subtest 1: both previous and copy available.
     // Create the test previous file
     // Create the test previous file
@@ -556,27 +556,27 @@ TEST_F(LFCControllerTest, launch6) {
     // We have several entries for different leases, the naming is:
     // We have several entries for different leases, the naming is:
     // <lease letter>_<version#>.
     // <lease letter>_<version#>.
     string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                 "200,200,8,100,0,7,0,1,1,host.example.com,\n";
-    string a_2 = "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,\n";
+                 "200,200,8,100,0,7,0,1,1,host.example.com,,1\n";
+    string a_2 = "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,,1\n";
     string a_3 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     string a_3 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                 "200,400,8,100,0,7,0,1,1,host.example.com,\n";
+                 "200,400,8,100,0,7,0,1,1,host.example.com,,1\n";
     string a_4 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     string a_4 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                 "0,200,8,100,0,7,0,1,1,host.example.com,\n";
+                 "0,200,8,100,0,7,0,1,1,host.example.com,,1\n";
 
 
     string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
     string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
-                 "300,300,6,150,0,8,0,0,0,,\n";
+                 "300,300,6,150,0,8,0,0,0,,,1\n";
     string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
     string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
-                 "300,800,6,150,0,8,0,0,0,,\n";
+                 "300,800,6,150,0,8,0,0,0,,,1\n";
     string b_3 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
     string b_3 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
-                 "300,1000,6,150,0,8,0,0,0,,\n";
+                 "300,1000,6,150,0,8,0,0,0,,,1\n";
 
 
     string c_1 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     string c_1 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                 "100,200,8,0,2,16,64,0,0,,\n";
+                 "100,200,8,0,2,16,64,0,0,,,1\n";
     string c_2 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     string c_2 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                 "100,400,8,0,2,16,64,0,0,,\n";
+                 "100,400,8,0,2,16,64,0,0,,,1\n";
 
 
     string d_1 = "2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     string d_1 = "2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                 "200,600,8,100,0,7,0,1,1,host.example.com,\n";
+                 "200,600,8,100,0,7,0,1,1,host.example.com,,1\n";
 
 
     // Subtest 1: bot previous and copy available
     // Subtest 1: bot previous and copy available
     // Create the test previous file
     // Create the test previous file

+ 0 - 2
src/lib/dhcpsrv/alloc_engine.cc

@@ -931,7 +931,6 @@ AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
     expired->t2_ = ctx.subnet_->getT2();
     expired->t2_ = ctx.subnet_->getT2();
     expired->cltt_ = time(NULL);
     expired->cltt_ = time(NULL);
     expired->subnet_id_ = ctx.subnet_->getID();
     expired->subnet_id_ = ctx.subnet_->getID();
-    expired->fixed_ = false;
     expired->hostname_ = ctx.hostname_;
     expired->hostname_ = ctx.hostname_;
     expired->fqdn_fwd_ = ctx.fwd_dns_update_;
     expired->fqdn_fwd_ = ctx.fwd_dns_update_;
     expired->fqdn_rev_ = ctx.rev_dns_update_;
     expired->fqdn_rev_ = ctx.rev_dns_update_;
@@ -1912,7 +1911,6 @@ AllocEngine::reuseExpiredLease4(Lease4Ptr& expired,
     }
     }
 
 
     updateLease4Information(expired, ctx);
     updateLease4Information(expired, ctx);
-    expired->fixed_ = false;
 
 
     LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA,
     LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE_DETAIL_DATA,
               ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA)
               ALLOC_ENGINE_V4_REUSE_EXPIRED_LEASE_DATA)

+ 9 - 0
src/lib/dhcpsrv/csv_lease_file4.cc

@@ -59,6 +59,7 @@ CSVLeaseFile4::append(const Lease4& lease) {
     row.writeAt(getColumnIndex("fqdn_fwd"), lease.fqdn_fwd_);
     row.writeAt(getColumnIndex("fqdn_fwd"), lease.fqdn_fwd_);
     row.writeAt(getColumnIndex("fqdn_rev"), lease.fqdn_rev_);
     row.writeAt(getColumnIndex("fqdn_rev"), lease.fqdn_rev_);
     row.writeAt(getColumnIndex("hostname"), lease.hostname_);
     row.writeAt(getColumnIndex("hostname"), lease.hostname_);
+    row.writeAt(getColumnIndex("state"), lease.state_);
 
 
     try {
     try {
         CSVFile::append(row);
         CSVFile::append(row);
@@ -115,6 +116,7 @@ CSVLeaseFile4::next(Lease4Ptr& lease) {
                                readFqdnFwd(row),
                                readFqdnFwd(row),
                                readFqdnRev(row),
                                readFqdnRev(row),
                                readHostname(row)));
                                readHostname(row)));
+        lease->state_ = readState(row);
 
 
     } catch (std::exception& ex) {
     } catch (std::exception& ex) {
         // bump the read error count
         // bump the read error count
@@ -144,6 +146,7 @@ CSVLeaseFile4::initColumns() {
     addColumn("fqdn_fwd");
     addColumn("fqdn_fwd");
     addColumn("fqdn_rev");
     addColumn("fqdn_rev");
     addColumn("hostname");
     addColumn("hostname");
+    addColumn("state");
 }
 }
 
 
 IOAddress
 IOAddress
@@ -212,5 +215,11 @@ CSVLeaseFile4::readHostname(const CSVRow& row) {
     return (hostname);
     return (hostname);
 }
 }
 
 
+uint32_t
+CSVLeaseFile4::readState(const util::CSVRow& row) {
+    uint32_t state = row.readAndConvertAt<uint32_t>(getColumnIndex("state"));
+    return (state);
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 15 - 9
src/lib/dhcpsrv/csv_lease_file4.h

@@ -102,6 +102,7 @@ private:
     /// - fqdn_fwd
     /// - fqdn_fwd
     /// - fqdn_rev
     /// - fqdn_rev
     /// - hostname
     /// - hostname
+    /// - state
     void initColumns();
     void initColumns();
 
 
     ///
     ///
@@ -111,48 +112,53 @@ private:
     ///
     ///
     /// @brief Reads lease address from the CSV file row.
     /// @brief Reads lease address from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     asiolink::IOAddress readAddress(const util::CSVRow& row);
     asiolink::IOAddress readAddress(const util::CSVRow& row);
 
 
     /// @brief Reads HW address from the CSV file row.
     /// @brief Reads HW address from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     HWAddr readHWAddr(const util::CSVRow& row);
     HWAddr readHWAddr(const util::CSVRow& row);
 
 
     /// @brief Reads client identifier from the CSV file row.
     /// @brief Reads client identifier from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     ClientIdPtr readClientId(const util::CSVRow& row);
     ClientIdPtr readClientId(const util::CSVRow& row);
 
 
     /// @brief Reads valid lifetime from the CSV file row.
     /// @brief Reads valid lifetime from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     uint32_t readValid(const util::CSVRow& row);
     uint32_t readValid(const util::CSVRow& row);
 
 
     /// @brief Reads cltt value from the CSV file row.
     /// @brief Reads cltt value from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     time_t readCltt(const util::CSVRow& row);
     time_t readCltt(const util::CSVRow& row);
 
 
     /// @brief Reads subnet id from the CSV file row.
     /// @brief Reads subnet id from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     SubnetID readSubnetID(const util::CSVRow& row);
     SubnetID readSubnetID(const util::CSVRow& row);
 
 
     /// @brief Reads the FQDN forward flag from the CSV file row.
     /// @brief Reads the FQDN forward flag from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     bool readFqdnFwd(const util::CSVRow& row);
     bool readFqdnFwd(const util::CSVRow& row);
 
 
     /// @brief Reads the FQDN reverse flag from the CSV file row.
     /// @brief Reads the FQDN reverse flag from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     bool readFqdnRev(const util::CSVRow& row);
     bool readFqdnRev(const util::CSVRow& row);
 
 
     /// @brief Reads hostname from the CSV file row.
     /// @brief Reads hostname from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     std::string readHostname(const util::CSVRow& row);
     std::string readHostname(const util::CSVRow& row);
+
+    /// @brief Reads lease state from the CSV file row.
+    ///
+    /// @param row CSV file row holding lease information.
+    uint32_t readState(const util::CSVRow& row);
     //@}
     //@}
 
 
 };
 };

+ 8 - 19
src/lib/dhcpsrv/csv_lease_file6.cc

@@ -59,6 +59,7 @@ CSVLeaseFile6::append(const Lease6& lease) {
         // We may not have hardware information
         // We may not have hardware information
         row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
         row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
     }
     }
+    row.writeAt(getColumnIndex("state"), lease.state_);
     try {
     try {
         CSVFile::append(row);
         CSVFile::append(row);
     } catch (const std::exception&) {
     } catch (const std::exception&) {
@@ -100,6 +101,7 @@ CSVLeaseFile6::next(Lease6Ptr& lease) {
         lease->fqdn_fwd_ = readFqdnFwd(row);
         lease->fqdn_fwd_ = readFqdnFwd(row);
         lease->fqdn_rev_ = readFqdnRev(row);
         lease->fqdn_rev_ = readFqdnRev(row);
         lease->hostname_ = readHostname(row);
         lease->hostname_ = readHostname(row);
+        lease->state_ = readState(row);
 
 
     } catch (std::exception& ex) {
     } catch (std::exception& ex) {
         // bump the read error count
         // bump the read error count
@@ -133,6 +135,7 @@ CSVLeaseFile6::initColumns() {
     addColumn("fqdn_rev");
     addColumn("fqdn_rev");
     addColumn("hostname");
     addColumn("hostname");
     addColumn("hwaddr");
     addColumn("hwaddr");
+    addColumn("state");
 }
 }
 
 
 Lease::Type
 Lease::Type
@@ -226,10 +229,7 @@ CSVLeaseFile6::readHWAddr(const CSVRow& row) {
         // Let's return a pointer to new freshly created copy.
         // Let's return a pointer to new freshly created copy.
         return (HWAddrPtr(new HWAddr(hwaddr)));
         return (HWAddrPtr(new HWAddr(hwaddr)));
 
 
-    } catch (const CSVFileError&) {
-        // That's ok, we may be reading old CSV file that didn't store hwaddr
-        return (HWAddrPtr());
-    } catch (const BadValue& ex) {
+    } catch (const std::exception& ex) {
         // That's worse. There was something in the file, but its conversion
         // That's worse. There was something in the file, but its conversion
         // to HWAddr failed. Let's log it on warning and carry on.
         // to HWAddr failed. Let's log it on warning and carry on.
         LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_READ_HWADDR_FAIL)
         LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_READ_HWADDR_FAIL)
@@ -239,21 +239,10 @@ CSVLeaseFile6::readHWAddr(const CSVRow& row) {
     }
     }
 }
 }
 
 
-bool
-CSVLeaseFile6::validateHeader(const isc::util::CSVRow& header) {
-
-    if (!CSVFile::validateHeader(header)) {
-
-        // One possible validation failure is that we're reading Kea 0.9
-        // lease file that didn't have hwaddr column. Let's add it and
-        // try to revalidate.
-        isc::util::CSVRow copy = header;
-        copy.append("hwaddr");
-        return CSVFile::validateHeader(copy);
-    } else {
-        return (true);
-    }
-
+uint32_t
+CSVLeaseFile6::readState(const util::CSVRow& row) {
+    uint32_t state = row.readAndConvertAt<uint32_t>(getColumnIndex("state"));
+    return (state);
 }
 }
 
 
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp

+ 19 - 25
src/lib/dhcpsrv/csv_lease_file6.h

@@ -87,18 +87,6 @@ public:
     /// ticket http://kea.isc.org/ticket/2405 is implemented.
     /// ticket http://kea.isc.org/ticket/2405 is implemented.
     bool next(Lease6Ptr& lease);
     bool next(Lease6Ptr& lease);
 
 
-protected:
-    /// @brief This function validates the header of the Lease6 CSV file.
-    ///
-    /// It works similar to @c CSVFile::validateHeader, but if the validation
-    /// fails, it attempts to add hwaddr column and retry validation.
-    /// That's useful when attmepting to read CSV file generated in 0.9
-    /// (did not have hwaddr field) in 0.9.1 or later code.
-    ///
-    /// @param header A row holding a header.
-    /// @return true if header matches the columns; false otherwise.
-    virtual bool validateHeader(const isc::util::CSVRow& header);
-
 private:
 private:
 
 
     /// @brief Initializes columns of the CSV file holding leases.
     /// @brief Initializes columns of the CSV file holding leases.
@@ -117,6 +105,7 @@ private:
     /// - fqdn_rev
     /// - fqdn_rev
     /// - hostname
     /// - hostname
     /// - hwaddr
     /// - hwaddr
+    /// - state
     void initColumns();
     void initColumns();
 
 
     ///
     ///
@@ -126,69 +115,74 @@ private:
     ///
     ///
     /// @brief Reads lease type from the CSV file row.
     /// @brief Reads lease type from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     Lease::Type readType(const util::CSVRow& row);
     Lease::Type readType(const util::CSVRow& row);
 
 
     /// @brief Reads lease address from the CSV file row.
     /// @brief Reads lease address from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     asiolink::IOAddress readAddress(const util::CSVRow& row);
     asiolink::IOAddress readAddress(const util::CSVRow& row);
 
 
     /// @brief Reads DUID from the CSV file row.
     /// @brief Reads DUID from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     DuidPtr readDUID(const util::CSVRow& row);
     DuidPtr readDUID(const util::CSVRow& row);
 
 
     /// @brief Reads IAID from the CSV file row.
     /// @brief Reads IAID from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     uint32_t readIAID(const util::CSVRow& row);
     uint32_t readIAID(const util::CSVRow& row);
 
 
     /// @brief Reads preferred lifetime from the CSV file row.
     /// @brief Reads preferred lifetime from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     uint32_t readPreferred(const util::CSVRow& row);
     uint32_t readPreferred(const util::CSVRow& row);
 
 
     /// @brief Reads valid lifetime from the CSV file row.
     /// @brief Reads valid lifetime from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     uint32_t readValid(const util::CSVRow& row);
     uint32_t readValid(const util::CSVRow& row);
 
 
     /// @brief Reads cltt value from the CSV file row.
     /// @brief Reads cltt value from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     uint32_t readCltt(const util::CSVRow& row);
     uint32_t readCltt(const util::CSVRow& row);
 
 
     /// @brief Reads subnet id from the CSV file row.
     /// @brief Reads subnet id from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     SubnetID readSubnetID(const util::CSVRow& row);
     SubnetID readSubnetID(const util::CSVRow& row);
 
 
     /// @brief Reads prefix length from the CSV file row.
     /// @brief Reads prefix length from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     uint8_t readPrefixLen(const util::CSVRow& row);
     uint8_t readPrefixLen(const util::CSVRow& row);
 
 
     /// @brief Reads the FQDN forward flag from the CSV file row.
     /// @brief Reads the FQDN forward flag from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     bool readFqdnFwd(const util::CSVRow& row);
     bool readFqdnFwd(const util::CSVRow& row);
 
 
     /// @brief Reads the FQDN reverse flag from the CSV file row.
     /// @brief Reads the FQDN reverse flag from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     bool readFqdnRev(const util::CSVRow& row);
     bool readFqdnRev(const util::CSVRow& row);
 
 
     /// @brief Reads hostname from the CSV file row.
     /// @brief Reads hostname from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     std::string readHostname(const util::CSVRow& row);
     std::string readHostname(const util::CSVRow& row);
 
 
     /// @brief Reads HW address from the CSV file row.
     /// @brief Reads HW address from the CSV file row.
     ///
     ///
-    /// @param row CSV file holding lease values.
+    /// @param row CSV file row holding lease information.
     /// @return pointer to the HWAddr structure that was read
     /// @return pointer to the HWAddr structure that was read
     HWAddrPtr readHWAddr(const util::CSVRow& row);
     HWAddrPtr readHWAddr(const util::CSVRow& row);
+
+    /// @brief Reads lease state from the CSV file row.
+    ///
+    /// @param row CSV file row holding lease information.
+    uint32_t readState(const util::CSVRow& row);
     //@}
     //@}
 
 
 };
 };

+ 17 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -231,6 +231,23 @@ 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
 for the specified address from the memory file database for the specified
 address.
 address.
 
 
+% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4 deleting reclaimed leases expired more than %1 s 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.
+
+% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6 deleting reclaimed leases expired more than %1 s 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.
+
+% DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START starting deletion of %1 expired-reclaimed leases
+A debug message issued wheb the server has found expired-reclaimed
+leases to be removed. The number of leases to be removed is logged
+in the message.
+
 % DHCPSRV_MEMFILE_GET_ADDR4 obtaining IPv4 lease for address %1
 % DHCPSRV_MEMFILE_GET_ADDR4 obtaining IPv4 lease for address %1
 A debug message issued when the server is attempting to obtain an IPv4
 A debug message issued when the server is attempting to obtain an IPv4
 lease from the memory file database for the specified address.
 lease from the memory file database for the specified address.

+ 57 - 22
src/lib/dhcpsrv/lease.cc

@@ -15,6 +15,7 @@
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>
 #include <util/pointer_util.h>
 #include <util/pointer_util.h>
 #include <sstream>
 #include <sstream>
+#include <iostream>
 
 
 using namespace isc::util;
 using namespace isc::util;
 using namespace std;
 using namespace std;
@@ -22,13 +23,17 @@ using namespace std;
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
+const uint32_t Lease::STATE_DEFAULT = 0x0;
+const uint32_t Lease::STATE_DECLINED = 0x1;
+const uint32_t Lease::STATE_EXPIRED_RECLAIMED = 0x2;
+
 Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
 Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
              uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
              uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
              const bool fqdn_fwd, const bool fqdn_rev,
              const bool fqdn_fwd, const bool fqdn_rev,
              const std::string& hostname, const HWAddrPtr& hwaddr)
              const std::string& hostname, const HWAddrPtr& hwaddr)
     :addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt),
     :addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt),
-     subnet_id_(subnet_id), fixed_(false), hostname_(hostname),
-     fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr) {
+     subnet_id_(subnet_id), hostname_(hostname), fqdn_fwd_(fqdn_fwd),
+    fqdn_rev_(fqdn_rev), hwaddr_(hwaddr), state_(STATE_DEFAULT) {
 }
 }
 
 
 
 
@@ -52,11 +57,37 @@ Lease::typeToText(Lease::Type type) {
    }
    }
 }
 }
 
 
-bool Lease::expired() const {
+std::string
+Lease::basicStatesToText(const uint32_t state) {
+    switch (state) {
+    case STATE_DEFAULT:
+        return ("default");
+    case STATE_DECLINED:
+        return ("declined");
+    case STATE_EXPIRED_RECLAIMED:
+        return ("expired-reclaimed");
+    default:
+        // The default case will be handled further on
+        ;
+    }
+    std::ostringstream s;
+    s << "unknown (" << state << ")";
+    return s.str();
+}
+
+bool
+Lease::expired() const {
+    return (getExpirationTime() < time(NULL));
+}
+
+bool
+Lease::stateExpiredReclaimed() const {
+    return (state_ == STATE_EXPIRED_RECLAIMED);
+}
 
 
-    // Let's use int64 to avoid problems with negative/large uint32 values
-    int64_t expire_time = cltt_ + valid_lft_;
-    return (expire_time < time(NULL));
+int64_t
+Lease::getExpirationTime() const {
+    return (static_cast<int64_t>(cltt_) + valid_lft_);
 }
 }
 
 
 bool
 bool
@@ -69,11 +100,10 @@ Lease::hasIdenticalFqdn(const Lease& other) const {
 Lease4::Lease4(const Lease4& other)
 Lease4::Lease4(const Lease4& other)
     : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
     : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
             other.subnet_id_, other.cltt_, other.fqdn_fwd_,
             other.subnet_id_, other.cltt_, other.fqdn_fwd_,
-            other.fqdn_rev_, other.hostname_, other.hwaddr_),
-            ext_(other.ext_) {
+            other.fqdn_rev_, other.hostname_, other.hwaddr_) {
 
 
-    fixed_ = other.fixed_;
-    comments_ = other.comments_;
+    // Copy over fields derived from Lease.
+    state_ = other.state_;
 
 
     // Copy the hardware address if it is defined.
     // Copy the hardware address if it is defined.
     if (other.hwaddr_) {
     if (other.hwaddr_) {
@@ -105,10 +135,13 @@ Lease4::Lease4(const isc::asiolink::IOAddress& address,
 
 
     : Lease(address, t1, t2, valid_lifetime, subnet_id, cltt, fqdn_fwd,
     : Lease(address, t1, t2, valid_lifetime, subnet_id, cltt, fqdn_fwd,
             fqdn_rev, hostname, hw_address),
             fqdn_rev, hostname, hw_address),
-      ext_(0), client_id_(client_id) {
+      client_id_(client_id) {
 }
 }
 
 
-
+std::string
+Lease4::statesToText(const uint32_t state) {
+    return (Lease::basicStatesToText(state));
+}
 
 
 const std::vector<uint8_t>&
 const std::vector<uint8_t>&
 Lease4::getClientIdVector() const {
 Lease4::getClientIdVector() const {
@@ -155,12 +188,10 @@ Lease4::operator=(const Lease4& other) {
         valid_lft_ = other.valid_lft_;
         valid_lft_ = other.valid_lft_;
         cltt_ = other.cltt_;
         cltt_ = other.cltt_;
         subnet_id_ = other.subnet_id_;
         subnet_id_ = other.subnet_id_;
-        fixed_ = other.fixed_;
         hostname_ = other.hostname_;
         hostname_ = other.hostname_;
         fqdn_fwd_ = other.fqdn_fwd_;
         fqdn_fwd_ = other.fqdn_fwd_;
         fqdn_rev_ = other.fqdn_rev_;
         fqdn_rev_ = other.fqdn_rev_;
-        comments_ = other.comments_;
-        ext_ = other.ext_;
+        state_ = other.state_;
 
 
         // Copy the hardware address if it is defined.
         // Copy the hardware address if it is defined.
         if (other.hwaddr_) {
         if (other.hwaddr_) {
@@ -215,6 +246,11 @@ Lease6::Lease6()
             duid_(DuidPtr()), preferred_lft_(0) {
             duid_(DuidPtr()), preferred_lft_(0) {
 }
 }
 
 
+std::string
+Lease6::statesToText(const uint32_t state) {
+    return (Lease::basicStatesToText(state));
+}
+
 const std::vector<uint8_t>&
 const std::vector<uint8_t>&
 Lease6::getDuidVector() const {
 Lease6::getDuidVector() const {
     if (!duid_) {
     if (!duid_) {
@@ -239,7 +275,8 @@ Lease6::toText() const {
            << "Valid life:    " << valid_lft_ << "\n"
            << "Valid life:    " << valid_lft_ << "\n"
            << "Cltt:          " << cltt_ << "\n"
            << "Cltt:          " << cltt_ << "\n"
            << "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n"
            << "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n"
-           << "Subnet ID:     " << subnet_id_ << "\n";
+           << "Subnet ID:     " << subnet_id_ << "\n"
+           << "State:         " << statesToText(state_) << "\n";
 
 
     return (stream.str());
     return (stream.str());
 }
 }
@@ -255,7 +292,8 @@ Lease4::toText() const {
            << "Cltt:          " << cltt_ << "\n"
            << "Cltt:          " << cltt_ << "\n"
            << "Hardware addr: " << (hwaddr_ ? hwaddr_->toText(false) : "(none)") << "\n"
            << "Hardware addr: " << (hwaddr_ ? hwaddr_->toText(false) : "(none)") << "\n"
            << "Client id:     " << (client_id_ ? client_id_->toText() : "(none)") << "\n"
            << "Client id:     " << (client_id_ ? client_id_->toText() : "(none)") << "\n"
-           << "Subnet ID:     " << subnet_id_ << "\n";
+           << "Subnet ID:     " << subnet_id_ << "\n"
+           << "State:         " << statesToText(state_) << "\n";
 
 
     return (stream.str());
     return (stream.str());
 }
 }
@@ -266,17 +304,15 @@ Lease4::operator==(const Lease4& other) const {
     return (nullOrEqualValues(hwaddr_, other.hwaddr_) &&
     return (nullOrEqualValues(hwaddr_, other.hwaddr_) &&
             nullOrEqualValues(client_id_, other.client_id_) &&
             nullOrEqualValues(client_id_, other.client_id_) &&
             addr_ == other.addr_ &&
             addr_ == other.addr_ &&
-            ext_ == other.ext_ &&
             subnet_id_ == other.subnet_id_ &&
             subnet_id_ == other.subnet_id_ &&
             t1_ == other.t1_ &&
             t1_ == other.t1_ &&
             t2_ == other.t2_ &&
             t2_ == other.t2_ &&
             valid_lft_ == other.valid_lft_ &&
             valid_lft_ == other.valid_lft_ &&
             cltt_ == other.cltt_ &&
             cltt_ == other.cltt_ &&
-            fixed_ == other.fixed_ &&
             hostname_ == other.hostname_ &&
             hostname_ == other.hostname_ &&
             fqdn_fwd_ == other.fqdn_fwd_ &&
             fqdn_fwd_ == other.fqdn_fwd_ &&
             fqdn_rev_ == other.fqdn_rev_ &&
             fqdn_rev_ == other.fqdn_rev_ &&
-            comments_ == other.comments_);
+            state_ == other.state_);
 }
 }
 
 
 bool
 bool
@@ -293,11 +329,10 @@ Lease6::operator==(const Lease6& other) const {
             t2_ == other.t2_ &&
             t2_ == other.t2_ &&
             cltt_ == other.cltt_ &&
             cltt_ == other.cltt_ &&
             subnet_id_ == other.subnet_id_ &&
             subnet_id_ == other.subnet_id_ &&
-            fixed_ == other.fixed_ &&
             hostname_ == other.hostname_ &&
             hostname_ == other.hostname_ &&
             fqdn_fwd_ == other.fqdn_fwd_ &&
             fqdn_fwd_ == other.fqdn_fwd_ &&
             fqdn_rev_ == other.fqdn_rev_ &&
             fqdn_rev_ == other.fqdn_rev_ &&
-            comments_ == other.comments_);
+            state_ == other.state_);
 }
 }
 
 
 std::ostream&
 std::ostream&

+ 66 - 21
src/lib/dhcpsrv/lease.h

@@ -48,6 +48,29 @@ struct Lease {
     /// @return text decription
     /// @return text decription
     static std::string typeToText(Type type);
     static std::string typeToText(Type type);
 
 
+    /// @name Common lease states constants.
+    //@{
+    ///
+    /// @brief A lease in the default state.
+    static const uint32_t STATE_DEFAULT;
+
+    /// @brief Declined lease.
+    static const uint32_t STATE_DECLINED;
+
+    /// @brief Expired and reclaimed lease.
+    static const uint32_t STATE_EXPIRED_RECLAIMED;
+
+    //@}
+
+    /// @brief Returns name(s) of the basic lease state(s).
+    ///
+    /// @param state A numeric value holding a state information.
+    /// Some states may be composite, i.e. the single state value
+    /// maps to multiple logical states of the lease.
+    ///
+    /// @return Comma separated list of state names.
+    static std::string basicStatesToText(const uint32_t state);
+
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// @param addr IP address
     /// @param addr IP address
@@ -108,11 +131,6 @@ struct Lease {
     /// Specifies the identification of the subnet to which the lease belongs.
     /// Specifies the identification of the subnet to which the lease belongs.
     SubnetID subnet_id_;
     SubnetID subnet_id_;
 
 
-    /// @brief Fixed lease?
-    ///
-    /// Fixed leases are kept after they are released/expired.
-    bool fixed_;
-
     /// @brief Client hostname
     /// @brief Client hostname
     ///
     ///
     /// This field may be empty
     /// This field may be empty
@@ -133,11 +151,16 @@ struct Lease {
     /// This information may not be available in certain cases.
     /// This information may not be available in certain cases.
     HWAddrPtr hwaddr_;
     HWAddrPtr hwaddr_;
 
 
-    /// @brief Lease comments
+    /// @brief Holds the lease state(s).
+    ///
+    /// This is the field that holds the lease state(s). Typically, a
+    /// lease remains in a single states. However, it is posible to
+    /// define a value for state which indicates that the lease remains
+    /// in multiple logical states.
     ///
     ///
-    /// Currently not used. It may be used for keeping comments made by the
-    /// system administrator.
-    std::string comments_;
+    /// The defined states are represented by the "STATE_*" constants
+    /// belonging to this class.
+    uint32_t state_;
 
 
     /// @brief Convert Lease to Printable Form
     /// @brief Convert Lease to Printable Form
     ///
     ///
@@ -148,6 +171,12 @@ struct Lease {
     /// @return true if the lease is expired
     /// @return true if the lease is expired
     bool expired() const;
     bool expired() const;
 
 
+    /// @brief Indicates if the lease is in the "expired-reclaimed" state.
+    ///
+    /// @return true if the lease is in the "expired-reclaimed" state, false
+    /// otherwise.
+    bool stateExpiredReclaimed() const;
+
     /// @brief Returns true if the other lease has equal FQDN data.
     /// @brief Returns true if the other lease has equal FQDN data.
     ///
     ///
     /// @param other Lease which FQDN data is to be compared with our lease.
     /// @param other Lease which FQDN data is to be compared with our lease.
@@ -165,6 +194,12 @@ struct Lease {
     ///
     ///
     /// @return const reference to the hardware address
     /// @return const reference to the hardware address
     const std::vector<uint8_t>& getHWAddrVector() const;
     const std::vector<uint8_t>& getHWAddrVector() const;
+
+    /// @brief Returns lease expiration time.
+    ///
+    /// The lease expiration time is a sum of a client last transmission time
+    /// and valid lifetime.
+    int64_t getExpirationTime() const;
 };
 };
 
 
 /// @brief Structure that holds a lease for IPv4 address
 /// @brief Structure that holds a lease for IPv4 address
@@ -175,16 +210,6 @@ struct Lease {
 /// extensively, direct access is warranted.
 /// extensively, direct access is warranted.
 struct Lease4 : public Lease {
 struct Lease4 : public Lease {
 
 
-    /// @brief Address extension
-    ///
-    /// It is envisaged that in some cases IPv4 address will be accompanied
-    /// with some additional data. One example of such use are Address + Port
-    /// solutions (or Port-restricted Addresses), where several clients may get
-    /// the same address, but different port ranges. This feature is not
-    /// expected to be widely used.  Under normal circumstances, the value
-    /// should be 0.
-    uint32_t ext_;
-
     /// @brief Client identifier
     /// @brief Client identifier
     ///
     ///
     /// @todo Should this be a pointer to a client ID or the ID itself?
     /// @todo Should this be a pointer to a client ID or the ID itself?
@@ -211,7 +236,7 @@ struct Lease4 : public Lease {
            const bool fqdn_fwd = false, const bool fqdn_rev = false,
            const bool fqdn_fwd = false, const bool fqdn_rev = false,
            const std::string& hostname = "")
            const std::string& hostname = "")
         : Lease(addr, t1, t2, valid_lft, subnet_id, cltt, fqdn_fwd, fqdn_rev,
         : Lease(addr, t1, t2, valid_lft, subnet_id, cltt, fqdn_fwd, fqdn_rev,
-                hostname, hwaddr), ext_(0) {
+                hostname, hwaddr) {
         if (clientid_len) {
         if (clientid_len) {
             client_id_.reset(new ClientId(clientid, clientid_len));
             client_id_.reset(new ClientId(clientid, clientid_len));
         }
         }
@@ -246,7 +271,7 @@ struct Lease4 : public Lease {
     /// @brief Default constructor
     /// @brief Default constructor
     ///
     ///
     /// Initialize fields that don't have a default constructor.
     /// Initialize fields that don't have a default constructor.
-    Lease4() : Lease(0, 0, 0, 0, 0, 0, false, false, "", HWAddrPtr()), ext_(0)
+    Lease4() : Lease(0, 0, 0, 0, 0, 0, false, false, "", HWAddrPtr())
     {
     {
     }
     }
 
 
@@ -255,6 +280,16 @@ struct Lease4 : public Lease {
     /// @param other the @c Lease4 object to be copied.
     /// @param other the @c Lease4 object to be copied.
     Lease4(const Lease4& other);
     Lease4(const Lease4& other);
 
 
+    /// @brief Returns name of the lease states specific to DHCPv4.
+    ///
+    /// @todo Currently it simply returns common states for DHCPv4 and DHCPv6.
+    /// This method will have to be extended to handle DHCPv4 specific states
+    /// when they are defined.
+    ///
+    /// @param state Numeric value holding lease states.
+    /// @return Comma separated list of lease state names.
+    static std::string statesToText(const uint32_t state);
+
     /// @brief Returns a client identifier.
     /// @brief Returns a client identifier.
     ///
     ///
     /// @warning Since the function returns the reference to a vector (not a
     /// @warning Since the function returns the reference to a vector (not a
@@ -440,6 +475,16 @@ struct Lease6 : public Lease {
     /// Initialize fields that don't have a default constructor.
     /// Initialize fields that don't have a default constructor.
     Lease6();
     Lease6();
 
 
+    /// @brief Returns name of the lease states specific to DHCPv6.
+    ///
+    /// @todo Currently it simply returns common states for DHCPv4 and DHCPv6.
+    /// This method will have to be extended to handle DHCPv6 specific states
+    /// when they are defined.
+    ///
+    /// @param state Numeric value holding lease states.
+    /// @return Comma separated list of lease state names.
+    static std::string statesToText(const uint32_t state);
+
     /// @brief Returns a reference to a vector representing a DUID.
     /// @brief Returns a reference to a vector representing a DUID.
     ///
     ///
     /// @warning Since the function returns the reference to a vector (not a
     /// @warning Since the function returns the reference to a vector (not a

+ 45 - 1
src/lib/dhcpsrv/lease_mgr.h

@@ -291,7 +291,6 @@ public:
     virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
     virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
                                         uint32_t iaid, SubnetID subnet_id) const = 0;
                                         uint32_t iaid, SubnetID subnet_id) const = 0;
 
 
-
     /// @brief returns zero or one IPv6 lease for a given duid+iaid+subnet_id
     /// @brief returns zero or one IPv6 lease for a given duid+iaid+subnet_id
     ///
     ///
     /// This function is mostly intended to be used in unit-tests during the
     /// This function is mostly intended to be used in unit-tests during the
@@ -318,6 +317,33 @@ public:
     Lease6Ptr getLease6(Lease::Type type, const DUID& duid,
     Lease6Ptr getLease6(Lease::Type type, const DUID& duid,
                         uint32_t iaid, SubnetID subnet_id) const;
                         uint32_t iaid, SubnetID subnet_id) const;
 
 
+    /// @brief Returns a collection of expired DHCPv6 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases6(Lease6Collection& expired_leases,
+                                   const size_t max_leases) const = 0;
+
+
+    /// @brief Returns a collection of expired DHCPv4 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases4(Lease4Collection& expired_leases,
+                                   const size_t max_leases) const = 0;
+
     /// @brief Updates IPv4 lease.
     /// @brief Updates IPv4 lease.
     ///
     ///
     /// @param lease4 The lease to be updated.
     /// @param lease4 The lease to be updated.
@@ -338,6 +364,24 @@ public:
     /// @return true if deletion was successful, false if no such lease exists
     /// @return true if deletion was successful, false if no such lease exists
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0;
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr) = 0;
 
 
+    /// @brief Deletes all expired and reclaimed DHCPv4 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs) = 0;
+
+    /// @brief Deletes all expired and reclaimed DHCPv6 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs) = 0;
+
     /// @brief Return backend type
     /// @brief Return backend type
     ///
     ///
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)

+ 174 - 49
src/lib/dhcpsrv/memfile_lease_mgr.cc

@@ -25,6 +25,7 @@
 #include <cstring>
 #include <cstring>
 #include <errno.h>
 #include <errno.h>
 #include <iostream>
 #include <iostream>
+#include <limits>
 #include <sstream>
 #include <sstream>
 
 
 namespace {
 namespace {
@@ -320,10 +321,9 @@ Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
               DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
 
 
-    typedef Lease4Storage::nth_index<0>::type SearchIndex;
-    const SearchIndex& idx = storage4_.get<0>();
-    Lease4Storage::iterator l = idx.find(addr);
-    if (l == storage4_.end()) {
+    const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>();
+    Lease4StorageAddressIndex::iterator l = idx.find(addr);
+    if (l == idx.end()) {
         return (Lease4Ptr());
         return (Lease4Ptr());
     } else {
     } else {
         return (Lease4Ptr(new Lease4(**l)));
         return (Lease4Ptr(new Lease4(**l)));
@@ -334,10 +334,9 @@ Lease4Collection
 Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
 Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
               DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
-    typedef Lease4Storage::nth_index<0>::type SearchIndex;
     Lease4Collection collection;
     Lease4Collection collection;
-    const SearchIndex& idx = storage4_.get<0>();
-    for(SearchIndex::const_iterator lease = idx.begin();
+    const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>();
+    for(Lease4StorageAddressIndex::const_iterator lease = idx.begin();
         lease != idx.end(); ++lease) {
         lease != idx.end(); ++lease) {
 
 
         // Every Lease4 has a hardware address, so we can compare it
         // Every Lease4 has a hardware address, so we can compare it
@@ -355,14 +354,11 @@ Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
               DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
               DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
         .arg(hwaddr.toText());
         .arg(hwaddr.toText());
 
 
-    // We are going to use index #1 of the multi index container.
-    // We define SearchIndex locally in this function because
-    // currently only this function uses this index.
-    typedef Lease4Storage::nth_index<1>::type SearchIndex;
-    // Get the index.
-    const SearchIndex& idx = storage4_.get<1>();
+    // Get the index by HW Address and Subnet Identifier.
+    const Lease4StorageHWAddressSubnetIdIndex& idx =
+        storage4_.get<HWAddressSubnetIdIndexTag>();
     // Try to find the lease using HWAddr and subnet id.
     // Try to find the lease using HWAddr and subnet id.
-    SearchIndex::const_iterator lease =
+    Lease4StorageHWAddressSubnetIdIndex::const_iterator lease =
         idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id));
         idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id));
     // Lease was not found. Return empty pointer to the caller.
     // Lease was not found. Return empty pointer to the caller.
     if (lease == idx.end()) {
     if (lease == idx.end()) {
@@ -377,10 +373,9 @@ Lease4Collection
 Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
 Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
               DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
-    typedef Lease4Storage::nth_index<0>::type SearchIndex;
     Lease4Collection collection;
     Lease4Collection collection;
-    const SearchIndex& idx = storage4_.get<0>();
-    for(SearchIndex::const_iterator lease = idx.begin();
+    const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>();
+    for(Lease4StorageAddressIndex::const_iterator lease = idx.begin();
         lease != idx.end(); ++ lease) {
         lease != idx.end(); ++ lease) {
 
 
         // client-id is not mandatory in DHCPv4. There can be a lease that does
         // client-id is not mandatory in DHCPv4. There can be a lease that does
@@ -402,14 +397,11 @@ Memfile_LeaseMgr::getLease4(const ClientId& client_id,
                                                         .arg(hwaddr.toText())
                                                         .arg(hwaddr.toText())
                                                         .arg(subnet_id);
                                                         .arg(subnet_id);
 
 
-    // We are going to use index #3 of the multi index container.
-    // We define SearchIndex locally in this function because
-    // currently only this function uses this index.
-    typedef Lease4Storage::nth_index<3>::type SearchIndex;
-    // Get the index.
-    const SearchIndex& idx = storage4_.get<3>();
+    // Get the index by client id, HW address and subnet id.
+    const Lease4StorageClientIdHWAddressSubnetIdIndex& idx =
+        storage4_.get<ClientIdHWAddressSubnetIdIndexTag>();
     // Try to get the lease using client id, hardware address and subnet id.
     // Try to get the lease using client id, hardware address and subnet id.
-    SearchIndex::const_iterator lease =
+    Lease4StorageClientIdHWAddressSubnetIdIndex::const_iterator lease =
         idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_,
         idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_,
                                    subnet_id));
                                    subnet_id));
 
 
@@ -429,14 +421,11 @@ Memfile_LeaseMgr::getLease4(const ClientId& client_id,
               DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
               DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
               .arg(client_id.toText());
               .arg(client_id.toText());
 
 
-    // We are going to use index #2 of the multi index container.
-    // We define SearchIndex locally in this function because
-    // currently only this function uses this index.
-    typedef Lease4Storage::nth_index<2>::type SearchIndex;
-    // Get the index.
-    const SearchIndex& idx = storage4_.get<2>();
+    // Get the index by client and subnet id.
+    const Lease4StorageClientIdSubnetIdIndex& idx =
+        storage4_.get<ClientIdSubnetIdIndexTag>();
     // Try to get the lease using client id and subnet id.
     // Try to get the lease using client id and subnet id.
-    SearchIndex::const_iterator lease =
+    Lease4StorageClientIdSubnetIdIndex::const_iterator lease =
         idx.find(boost::make_tuple(client_id.getClientId(), subnet_id));
         idx.find(boost::make_tuple(client_id.getClientId(), subnet_id));
     // Lease was not found. Return empty pointer to the caller.
     // Lease was not found. Return empty pointer to the caller.
     if (lease == idx.end()) {
     if (lease == idx.end()) {
@@ -470,15 +459,15 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type,
         .arg(duid.toText())
         .arg(duid.toText())
         .arg(Lease::typeToText(type));
         .arg(Lease::typeToText(type));
 
 
-    // We are going to use index #1 of the multi index container.
-    typedef Lease6Storage::nth_index<1>::type SearchIndex;
-    // Get the index.
-    const SearchIndex& idx = storage6_.get<1>();
+    // Get the index by DUID, IAID, lease type.
+    const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get<DuidIaidTypeIndexTag>();
     // Try to get the lease using the DUID, IAID and lease type.
     // Try to get the lease using the DUID, IAID and lease type.
-    std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
+    std::pair<Lease6StorageDuidIaidTypeIndex::const_iterator,
+              Lease6StorageDuidIaidTypeIndex::const_iterator> l =
         idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
         idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
     Lease6Collection collection;
     Lease6Collection collection;
-    for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
+    for(Lease6StorageDuidIaidTypeIndex::const_iterator lease =
+            l.first; lease != l.second; ++lease) {
         collection.push_back(Lease6Ptr(new Lease6(**lease)));
         collection.push_back(Lease6Ptr(new Lease6(**lease)));
     }
     }
 
 
@@ -496,15 +485,15 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type,
         .arg(duid.toText())
         .arg(duid.toText())
         .arg(Lease::typeToText(type));
         .arg(Lease::typeToText(type));
 
 
-    // We are going to use index #1 of the multi index container.
-    typedef Lease6Storage::nth_index<1>::type SearchIndex;
-    // Get the index.
-    const SearchIndex& idx = storage6_.get<1>();
+    // Get the index by DUID, IAID, lease type.
+    const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get<DuidIaidTypeIndexTag>();
     // Try to get the lease using the DUID, IAID and lease type.
     // Try to get the lease using the DUID, IAID and lease type.
-    std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
+    std::pair<Lease6StorageDuidIaidTypeIndex::const_iterator,
+              Lease6StorageDuidIaidTypeIndex::const_iterator> l =
         idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
         idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
     Lease6Collection collection;
     Lease6Collection collection;
-    for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
+    for(Lease6StorageDuidIaidTypeIndex::const_iterator lease =
+            l.first; lease != l.second; ++lease) {
         // Filter out the leases which subnet id doesn't match.
         // Filter out the leases which subnet id doesn't match.
         if((*lease)->subnet_id_ == subnet_id) {
         if((*lease)->subnet_id_ == subnet_id) {
             collection.push_back(Lease6Ptr(new Lease6(**lease)));
             collection.push_back(Lease6Ptr(new Lease6(**lease)));
@@ -515,11 +504,59 @@ Memfile_LeaseMgr::getLeases6(Lease::Type type,
 }
 }
 
 
 void
 void
+Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
+                                    const size_t max_leases) const {
+    // Obtain the index which segragates leases by state and time.
+    const Lease6StorageExpirationIndex& index = storage6_.get<ExpirationIndexTag>();
+
+    // Retrieve leases which are not reclaimed and which haven't expired. The
+    // 'less-than' operator will be used for both components of the index. So,
+    // for the 'state' 'false' is less than 'true'. Also the leases with
+    // expiration time lower than current time will be returned.
+    Lease6StorageExpirationIndex::const_iterator ub =
+        index.upper_bound(boost::make_tuple(false, time(NULL)));
+
+    // Copy only the number of leases indicated by the max_leases parameter.
+    for (Lease6StorageExpirationIndex::const_iterator lease = index.begin();
+         (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) <
+                                                 max_leases));
+         ++lease) {
+        expired_leases.push_back(Lease6Ptr(new Lease6(**lease)));
+    }
+}
+
+void
+Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
+                                    const size_t max_leases) const {
+    // Obtain the index which segragates leases by state and time.
+    const Lease4StorageExpirationIndex& index = storage4_.get<ExpirationIndexTag>();
+
+    // Retrieve leases which are not reclaimed and which haven't expired. The
+    // 'less-than' operator will be used for both components of the index. So,
+    // for the 'state' 'false' is less than 'true'. Also the leases with
+    // expiration time lower than current time will be returned.
+    Lease4StorageExpirationIndex::const_iterator ub =
+        index.upper_bound(boost::make_tuple(false, time(NULL)));
+
+    // Copy only the number of leases indicated by the max_leases parameter.
+    for (Lease4StorageExpirationIndex::const_iterator lease = index.begin();
+         (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) <
+                                                 max_leases));
+         ++lease) {
+        expired_leases.push_back(Lease4Ptr(new Lease4(**lease)));
+    }
+}
+
+void
 Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
 Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
               DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
 
 
-    Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
+    // Obtain 'by address' index.
+    Lease4StorageAddressIndex& index = storage4_.get<AddressIndexTag>();
+
+    // Lease must exist if it is to be updated.
+    Lease4StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_);
     if (lease_it == storage4_.end()) {
     if (lease_it == storage4_.end()) {
         isc_throw(NoSuchLease, "failed to update the lease with address "
         isc_throw(NoSuchLease, "failed to update the lease with address "
                   << lease->addr_ << " - no such lease");
                   << lease->addr_ << " - no such lease");
@@ -532,7 +569,8 @@ Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
         lease_file4_->append(*lease);
         lease_file4_->append(*lease);
     }
     }
 
 
-    **lease_it = *lease;
+    // Use replace() to re-index leases.
+    index.replace(lease_it, Lease4Ptr(new Lease4(*lease)));
 }
 }
 
 
 void
 void
@@ -540,8 +578,12 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
     LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
               DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
               DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
 
 
-    Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
-    if (lease_it == storage6_.end()) {
+    // Obtain 'by address' index.
+    Lease6StorageAddressIndex& index = storage6_.get<AddressIndexTag>();
+
+    // Lease must exist if it is to be updated.
+    Lease6StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_);
+    if (lease_it == index.end()) {
         isc_throw(NoSuchLease, "failed to update the lease with address "
         isc_throw(NoSuchLease, "failed to update the lease with address "
                   << lease->addr_ << " - no such lease");
                   << lease->addr_ << " - no such lease");
     }
     }
@@ -553,7 +595,8 @@ Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
         lease_file6_->append(*lease);
         lease_file6_->append(*lease);
     }
     }
 
 
-    **lease_it = *lease;
+    // Use replace() to re-index leases.
+    index.replace(lease_it, Lease6Ptr(new Lease6(*lease)));
 }
 }
 
 
 bool
 bool
@@ -603,6 +646,89 @@ Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     }
     }
 }
 }
 
 
+uint64_t
+Memfile_LeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4)
+        .arg(secs);
+    return (deleteExpiredReclaimedLeases<
+            Lease4StorageExpirationIndex, Lease4
+            >(secs, V4, storage4_, lease_file4_));
+}
+
+uint64_t
+Memfile_LeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6)
+        .arg(secs);
+    return (deleteExpiredReclaimedLeases<
+            Lease6StorageExpirationIndex, Lease6
+            >(secs, V6, storage6_, lease_file6_));
+}
+
+template<typename IndexType, typename LeaseType, typename StorageType,
+         typename LeaseFileType>
+uint64_t
+Memfile_LeaseMgr::deleteExpiredReclaimedLeases(const uint32_t secs,
+                                               const Universe& universe,
+                                               StorageType& storage,
+                                               LeaseFileType& lease_file) const {
+    // Obtain the index which segragates leases by state and time.
+    IndexType& index = storage.template get<ExpirationIndexTag>();
+
+    // This returns the first element which is greater than the specified
+    // tuple (true, time(NULL) - secs). However, the range between the
+    // beginnng of the index and returned element also includes all the
+    // elements for which the first value is false (lease state is NOT
+    // reclaimed), because false < true. All elements between the
+    // beginning of the index and the element returned, for which the
+    // first value is true, represent the reclaimed leases which should
+    // be deleted, because their expiration time + secs has occured earlier
+    // than current time.
+    typename IndexType::const_iterator upper_limit =
+        index.upper_bound(boost::make_tuple(true, time(NULL) - secs));
+
+    // Now, we have to exclude all elements of the index which represent
+    // leases in the state other than reclaimed - with the first value
+    // in the index equal to false. Note that elements in the index are
+    // ordered from the lower to the higher ones. So, all elements with
+    // the first value of false are placed before the elements with the
+    // value of true. Hence, we have to find the first element which
+    // contains value of true. The time value is the lowest possible.
+    typename IndexType::const_iterator lower_limit =
+        index.upper_bound(boost::make_tuple(true, std::numeric_limits<int64_t>::min()));
+
+    // If there are some elements in this range, delete them.
+    uint64_t num_leases = static_cast<uint64_t>(std::distance(lower_limit, upper_limit));
+    if (num_leases > 0) {
+
+        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+                  DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START)
+            .arg(num_leases);
+
+        // If lease persistence is enabled, we also have to mark leases
+        // as deleted in the lease file. We do this by setting the
+        // lifetime to 0.
+        if (persistLeases(universe)) {
+            for (typename IndexType::const_iterator lease = lower_limit;
+                 lease != upper_limit; ++lease) {
+                // Copy lease to not affect the lease in the container.
+                LeaseType lease_copy(**lease);
+                // Set the valid lifetime to 0 to indicate the removal
+                // of the lease.
+                lease_copy.valid_lft_ = 0;
+                lease_file->append(lease_copy);
+            }
+        }
+
+        // Erase leases from memory.
+        index.erase(lower_limit, upper_limit);
+    }
+    // Return number of leases deleted.
+    return (num_leases);
+}
+
+
 std::string
 std::string
 Memfile_LeaseMgr::getDescription() const {
 Memfile_LeaseMgr::getDescription() const {
     return (std::string("This is a dummy memfile backend implementation.\n"
     return (std::string("This is a dummy memfile backend implementation.\n"
@@ -871,6 +997,5 @@ void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file)
     }
     }
 }
 }
 
 
-
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 82 - 0
src/lib/dhcpsrv/memfile_lease_mgr.h

@@ -259,6 +259,33 @@ public:
                                         uint32_t iaid,
                                         uint32_t iaid,
                                         SubnetID subnet_id) const;
                                         SubnetID subnet_id) const;
 
 
+    /// @brief Returns a collection of expired DHCPv6 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases6(Lease6Collection& expired_leases,
+                                   const size_t max_leases) const;
+
+
+    /// @brief Returns a collection of expired DHCPv4 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases4(Lease4Collection& expired_leases,
+                                   const size_t max_leases) const;
+
     /// @brief Updates IPv4 lease.
     /// @brief Updates IPv4 lease.
     ///
     ///
     /// @warning This function does not validate the pointer to the lease.
     /// @warning This function does not validate the pointer to the lease.
@@ -287,6 +314,61 @@ public:
     /// @return true if deletion was successful, false if no such lease exists
     /// @return true if deletion was successful, false if no such lease exists
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
 
 
+    /// @brief Deletes all expired-reclaimed DHCPv4 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs);
+
+    /// @brief Deletes all expired-reclaimed DHCPv6 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs);
+
+private:
+
+    /// @brief Deletes all expired-reclaimed leases.
+    ///
+    /// This private method is called by both of the public methods:
+    /// @c deleteExpiredReclaimedLeases4 and
+    /// @c deleteExpiredReclaimedLeases6 to remove all expired
+    /// reclaimed DHCPv4 or DHCPv6 leases respectively.
+    ///
+    /// @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 universe V4 or V6.
+    /// @param storage Reference to the container where leases are held.
+    /// Some expired-reclaimed leases will be removed from this container.
+    /// @param lease_file Reference to a DHCPv4 or DHCPv6 lease file
+    /// instance where leases should be marked as deleted.
+    ///
+    /// @return Number of leases deleted.
+    ///
+    /// @tparam IndexType Index type to be used to search for the
+    /// expired-reclaimed leases, i.e.
+    /// @c Lease4StorageExpirationIndex or @c Lease6StorageExpirationIndex.
+    /// @tparam LeaseType Lease type, i.e. @c Lease4 or @c Lease6.
+    /// @tparam StorageType Type of storage where leases are held, i.e.
+    /// @c Lease4Storage or @c Lease6Storage.
+    /// @tparam LeaseFileType Type of the lease file, i.e. DHCPv4 or
+    /// DHCPv6 lease file type.
+    template<typename IndexType, typename LeaseType, typename StorageType,
+             typename LeaseFileType>
+    uint64_t deleteExpiredReclaimedLeases(const uint32_t secs,
+                                          const Universe& universe,
+                                          StorageType& storage,
+                                          LeaseFileType& lease_file) const;
+
+public:
+
     /// @brief Return backend type
     /// @brief Return backend type
     ///
     ///
     /// Returns the type of the backend.
     /// Returns the type of the backend.

+ 114 - 3
src/lib/dhcpsrv/memfile_lease_storage.h

@@ -12,8 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#ifndef INMEMORY_LEASE_STORAGE_H
-#define INMEMORY_LEASE_STORAGE_H
+#ifndef MEMFILE_LEASE_STORAGE_H
+#define MEMFILE_LEASE_STORAGE_H
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>
@@ -31,11 +31,40 @@
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
+
+/// @brief Tag for indexes by address.
+struct AddressIndexTag { };
+
+/// @brief Tag for indexes by DUID, IAID, lease type tuple.
+struct DuidIaidTypeIndexTag { };
+
+/// @brief Tag for indexes by expiration time.
+struct ExpirationIndexTag { };
+
+/// @brief Tag for indexes by HW address, subnet identifier tuple.
+struct HWAddressSubnetIdIndexTag { };
+
+/// @brief Tag for indexes by client and subnet identifiers.
+struct ClientIdSubnetIdIndexTag { };
+
+/// @brief Tag for indexes by client id, HW address and subnet id.
+struct ClientIdHWAddressSubnetIdIndexTag { };
+
+/// @name Multi index containers holding DHCPv4 and DHCPv6 leases.
+///
+//@{
+
 /// @brief A multi index container holding DHCPv6 leases.
 /// @brief A multi index container holding DHCPv6 leases.
 ///
 ///
 /// The leases in the container may be accessed using different indexes:
 /// The leases in the container may be accessed using different indexes:
 /// - using an IPv6 address,
 /// - using an IPv6 address,
 /// - using a composite index: DUID, IAID and lease type.
 /// - using a composite index: DUID, IAID and lease type.
+/// - using a composite index: boolean flag indicating if the state is
+///   "expired-reclaimed" and expiration time.
+///
+/// Indexes can be accessed using the index number (from 0 to 2) or a
+/// name tag. It is recommended to use the tags to access indexes as
+/// they do not depend on the order of indexes in the container.
 typedef boost::multi_index_container<
 typedef boost::multi_index_container<
     // It holds pointers to Lease6 objects.
     // It holds pointers to Lease6 objects.
     Lease6Ptr,
     Lease6Ptr,
@@ -44,11 +73,13 @@ typedef boost::multi_index_container<
         // This index sorts leases by IPv6 addresses represented as
         // This index sorts leases by IPv6 addresses represented as
         // IOAddress objects.
         // IOAddress objects.
         boost::multi_index::ordered_unique<
         boost::multi_index::ordered_unique<
+            boost::multi_index::tag<AddressIndexTag>,
             boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
             boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
         >,
         >,
 
 
         // Specification of the second index starts here.
         // Specification of the second index starts here.
         boost::multi_index::ordered_non_unique<
         boost::multi_index::ordered_non_unique<
+            boost::multi_index::tag<DuidIaidTypeIndexTag>,
             // This is a composite index that will be used to search for
             // This is a composite index that will be used to search for
             // the lease using three attributes: DUID, IAID and lease type.
             // the lease using three attributes: DUID, IAID and lease type.
             boost::multi_index::composite_key<
             boost::multi_index::composite_key<
@@ -62,6 +93,24 @@ typedef boost::multi_index_container<
                 boost::multi_index::member<Lease6, uint32_t, &Lease6::iaid_>,
                 boost::multi_index::member<Lease6, uint32_t, &Lease6::iaid_>,
                 boost::multi_index::member<Lease6, Lease::Type, &Lease6::type_>
                 boost::multi_index::member<Lease6, Lease::Type, &Lease6::type_>
             >
             >
+        >,
+
+        // Specification of the third index starts here.
+        boost::multi_index::ordered_non_unique<
+            boost::multi_index::tag<ExpirationIndexTag>,
+            // This is a composite index that will be used to search for
+            // the expired leases. Depending on the value of the first component
+            // of the search key, the reclaimed or not reclaimed leases will can
+            // be searched.
+            boost::multi_index::composite_key<
+                Lease6,
+                // The boolean value specifying if lease is reclaimed or not.
+                boost::multi_index::const_mem_fun<Lease, bool,
+                                                  &Lease::stateExpiredReclaimed>,
+                // Lease expiration time.
+                boost::multi_index::const_mem_fun<Lease, int64_t,
+                                                  &Lease::getExpirationTime>
+            >
         >
         >
      >
      >
 > Lease6Storage; // Specify the type name of this container.
 > Lease6Storage; // Specify the type name of this container.
@@ -73,6 +122,12 @@ typedef boost::multi_index_container<
 /// - composite index: HW address and subnet id,
 /// - composite index: HW address and subnet id,
 /// - composite index: client id and subnet id,
 /// - composite index: client id and subnet id,
 /// - composite index: HW address, client id and subnet id
 /// - composite index: HW address, client id and subnet id
+/// - using a composite index: boolean flag indicating if the state is
+///   "expired-reclaimed" and expiration time.
+///
+/// Indexes can be accessed using the index number (from 0 to 4) or a
+/// name tag. It is recommended to use the tags to access indexes as
+/// they do not depend on the order of indexes in the container.
 typedef boost::multi_index_container<
 typedef boost::multi_index_container<
     // It holds pointers to Lease4 objects.
     // It holds pointers to Lease4 objects.
     Lease4Ptr,
     Lease4Ptr,
@@ -82,6 +137,7 @@ typedef boost::multi_index_container<
         // This index sorts leases by IPv4 addresses represented as
         // This index sorts leases by IPv4 addresses represented as
         // IOAddress objects.
         // IOAddress objects.
         boost::multi_index::ordered_unique<
         boost::multi_index::ordered_unique<
+            boost::multi_index::tag<AddressIndexTag>,
             // The IPv4 address are held in addr_ members that belong to
             // The IPv4 address are held in addr_ members that belong to
             // Lease class.
             // Lease class.
             boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
             boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
@@ -89,6 +145,7 @@ typedef boost::multi_index_container<
 
 
         // Specification of the second index starts here.
         // Specification of the second index starts here.
         boost::multi_index::ordered_non_unique<
         boost::multi_index::ordered_non_unique<
+            boost::multi_index::tag<HWAddressSubnetIdIndexTag>,
             // This is a composite index that combines two attributes of the
             // This is a composite index that combines two attributes of the
             // Lease4 object: hardware address and subnet id.
             // Lease4 object: hardware address and subnet id.
             boost::multi_index::composite_key<
             boost::multi_index::composite_key<
@@ -109,6 +166,7 @@ typedef boost::multi_index_container<
 
 
         // Specification of the third index starts here.
         // Specification of the third index starts here.
         boost::multi_index::ordered_non_unique<
         boost::multi_index::ordered_non_unique<
+            boost::multi_index::tag<ClientIdSubnetIdIndexTag>,
             // This is a composite index that uses two values to search for a
             // This is a composite index that uses two values to search for a
             // lease: client id and subnet id.
             // lease: client id and subnet id.
             boost::multi_index::composite_key<
             boost::multi_index::composite_key<
@@ -124,6 +182,7 @@ typedef boost::multi_index_container<
 
 
         // Specification of the fourth index starts here.
         // Specification of the fourth index starts here.
         boost::multi_index::ordered_non_unique<
         boost::multi_index::ordered_non_unique<
+            boost::multi_index::tag<ClientIdHWAddressSubnetIdIndexTag>,
             // This is a composite index that uses three values to search for a
             // This is a composite index that uses three values to search for a
             // lease: client id, HW address and subnet id.
             // lease: client id, HW address and subnet id.
             boost::multi_index::composite_key<
             boost::multi_index::composite_key<
@@ -141,11 +200,63 @@ typedef boost::multi_index_container<
                 // The subnet id is accessed through the subnet_id_ member.
                 // The subnet id is accessed through the subnet_id_ member.
                 boost::multi_index::member<Lease, SubnetID, &Lease::subnet_id_>
                 boost::multi_index::member<Lease, SubnetID, &Lease::subnet_id_>
             >
             >
+        >,
+
+        // Specification of the fifth index starts here.
+        boost::multi_index::ordered_non_unique<
+            boost::multi_index::tag<ExpirationIndexTag>,
+            // This is a composite index that will be used to search for
+            // the expired leases. Depending on the value of the first component
+            // of the search key, the reclaimed or not reclaimed leases will can
+            // be searched.
+            boost::multi_index::composite_key<
+                Lease4,
+                // The boolean value specifying if lease is reclaimed or not.
+                boost::multi_index::const_mem_fun<Lease, bool,
+                                                  &Lease::stateExpiredReclaimed>,
+                // Lease expiration time.
+                boost::multi_index::const_mem_fun<Lease, int64_t,
+                                                  &Lease::getExpirationTime>
+            >
         >
         >
     >
     >
 > Lease4Storage; // Specify the type name for this container.
 > Lease4Storage; // Specify the type name for this container.
 
 
+//@}
+
+/// @name Indexes used by the multi index containers
+///
+//@{
+
+/// @brief DHCPv6 lease storage index by address.
+typedef Lease6Storage::index<AddressIndexTag>::type Lease6StorageAddressIndex;
+
+/// @brief DHCPv6 lease storage index by DUID, IAID, lease type.
+typedef Lease6Storage::index<DuidIaidTypeIndexTag>::type Lease6StorageDuidIaidTypeIndex;
+
+/// @brief DHCPv6 lease storage index by expiration time.
+typedef Lease6Storage::index<ExpirationIndexTag>::type Lease6StorageExpirationIndex;
+
+/// @brief DHCPv4 lease storage index by address.
+typedef Lease4Storage::index<AddressIndexTag>::type Lease4StorageAddressIndex;
+
+/// @brief DHCPv4 lease storage index by exiration time.
+typedef Lease4Storage::index<ExpirationIndexTag>::type Lease4StorageExpirationIndex;
+
+/// @brief DHCPv4 lease storage index by HW address and subnet identifier.
+typedef Lease4Storage::index<HWAddressSubnetIdIndexTag>::type
+Lease4StorageHWAddressSubnetIdIndex;
+
+/// @brief DHCPv4 lease storage index by client and subnet identifier.
+typedef Lease4Storage::index<ClientIdSubnetIdIndexTag>::type
+Lease4StorageClientIdSubnetIdIndex;
+
+/// @brief DHCPv4 lease storage index by client id, HW address and subnet id.
+typedef Lease4Storage::index<ClientIdHWAddressSubnetIdIndexTag>::type
+Lease4StorageClientIdHWAddressSubnetIdIndex;
+
+//@}
 } // end of isc::dhcp namespace
 } // end of isc::dhcp namespace
 } // end of isc namespace
 } // end of isc namespace
 
 
-#endif // INMEMORY_LEASE_STORAGE_H
+#endif // MEMFILE_LEASE_STORAGE_H

+ 25 - 0
src/lib/dhcpsrv/mysql_lease_mgr.cc

@@ -1932,6 +1932,18 @@ MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
     return (result);
     return (result);
 }
 }
 
 
+void
+MySqlLeaseMgr::getExpiredLeases6(Lease6Collection&, const size_t) const {
+    isc_throw(NotImplemented, "MySqlLeaseMgr::getExpiredLeases6 is currently"
+              " not implemented");
+}
+
+void
+MySqlLeaseMgr::getExpiredLeases4(Lease4Collection&, const size_t) const {
+    isc_throw(NotImplemented, "MySqlLeaseMgr::getExpiredLeases4 is currently"
+              " not implemented");
+}
+
 // Update lease methods.  These comprise common code that handles the actual
 // Update lease methods.  These comprise common code that handles the actual
 // update, and type-specific methods that set up the parameters for the prepared
 // update, and type-specific methods that set up the parameters for the prepared
 // statement depending on the type of lease.
 // statement depending on the type of lease.
@@ -2074,6 +2086,19 @@ MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     }
     }
 }
 }
 
 
+uint64_t
+MySqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t) {
+    isc_throw(NotImplemented, "MySqlLeaseMgr::deleteExpiredReclaimedLeases4"
+              " is not implemented");
+}
+
+uint64_t
+MySqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t) {
+    isc_throw(NotImplemented, "MySqlLeaseMgr::deleteExpiredReclaimedLeases6"
+              " is not implemented");
+}
+
+
 // Miscellaneous database methods.
 // Miscellaneous database methods.
 
 
 std::string
 std::string

+ 45 - 0
src/lib/dhcpsrv/mysql_lease_mgr.h

@@ -317,6 +317,33 @@ public:
     virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
     virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
                                         uint32_t iaid, SubnetID subnet_id) const;
                                         uint32_t iaid, SubnetID subnet_id) const;
 
 
+    /// @brief Returns a collection of expired DHCPv6 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases6(Lease6Collection& expired_leases,
+                                   const size_t max_leases) const;
+
+
+    /// @brief Returns a collection of expired DHCPv4 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases4(Lease4Collection& expired_leases,
+                                   const size_t max_leases) const;
+
     /// @brief Updates IPv4 lease.
     /// @brief Updates IPv4 lease.
     ///
     ///
     /// Updates the record of the lease in the database (as identified by the
     /// Updates the record of the lease in the database (as identified by the
@@ -354,6 +381,24 @@ public:
     ///        failed.
     ///        failed.
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
 
 
+    /// @brief Deletes all expired-reclaimed DHCPv4 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs);
+
+    /// @brief Deletes all expired-reclaimed DHCPv6 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs);
+
     /// @brief Return backend type
     /// @brief Return backend type
     ///
     ///
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)

+ 25 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -1382,6 +1382,19 @@ PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
     return (result);
     return (result);
 }
 }
 
 
+void
+PgSqlLeaseMgr::getExpiredLeases6(Lease6Collection&, const size_t) const {
+    isc_throw(NotImplemented, "PgSqlLeaseMgr::getExpiredLeases6 is currently"
+              " not implemented");
+}
+
+void
+PgSqlLeaseMgr::getExpiredLeases4(Lease4Collection&, const size_t) const {
+    isc_throw(NotImplemented, "PgSqlLeaseMgr::getExpiredLeases4 is currently"
+              " not implemented");
+}
+
+
 template <typename LeasePtr>
 template <typename LeasePtr>
 void
 void
 PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
 PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
@@ -1494,6 +1507,18 @@ PgSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     return (deleteLeaseCommon(DELETE_LEASE6, bind_array));
     return (deleteLeaseCommon(DELETE_LEASE6, bind_array));
 }
 }
 
 
+uint64_t
+PgSqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t) {
+    isc_throw(NotImplemented, "PgSqlLeaseMgr::deleteExpiredReclaimedLeases4"
+              " is not implemented");
+}
+
+uint64_t
+PgSqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t) {
+    isc_throw(NotImplemented, "PgSqlLeaseMgr::deleteExpiredReclaimedLeases6"
+              " is not implemented");
+}
+
 string
 string
 PgSqlLeaseMgr::getName() const {
 PgSqlLeaseMgr::getName() const {
     string name = "";
     string name = "";

+ 45 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.h

@@ -322,6 +322,33 @@ public:
     virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
     virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
                                         uint32_t iaid, SubnetID subnet_id) const;
                                         uint32_t iaid, SubnetID subnet_id) const;
 
 
+    /// @brief Returns a collection of expired DHCPv6 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases6(Lease6Collection& expired_leases,
+                                   const size_t max_leases) const;
+
+
+    /// @brief Returns a collection of expired DHCPv4 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases4(Lease4Collection& expired_leases,
+                                   const size_t max_leases) const;
+
     /// @brief Updates IPv4 lease.
     /// @brief Updates IPv4 lease.
     ///
     ///
     /// Updates the record of the lease in the database (as identified by the
     /// Updates the record of the lease in the database (as identified by the
@@ -359,6 +386,24 @@ public:
     ///        failed.
     ///        failed.
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
     virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
 
 
+    /// @brief Deletes all expired-reclaimed DHCPv4 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs);
+
+    /// @brief Deletes all expired-reclaimed DHCPv6 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.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs);
+
     /// @brief Return backend type
     /// @brief Return backend type
     ///
     ///
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)

+ 10 - 7
src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc

@@ -111,12 +111,12 @@ CSVLeaseFile4Test::absolutePath(const std::string& filename) {
 void
 void
 CSVLeaseFile4Test::writeSampleFile() const {
 CSVLeaseFile4Test::writeSampleFile() const {
     io_.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     io_.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
+                  "fqdn_fwd,fqdn_rev,hostname,state\n"
                   "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,"
                   "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,"
-                  "host.example.com\n"
-                  "192.0.2.1,,a:11:01:04,200,200,8,1,1,host.example.com\n"
+                  "host.example.com,0\n"
+                  "192.0.2.1,,a:11:01:04,200,200,8,1,1,host.example.com,1\n"
                   "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,"
                   "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,"
-                  "0,0,\n");
+                  "0,0,,1\n");
 }
 }
 
 
 // This test checks the capability to read and parse leases from the file.
 // This test checks the capability to read and parse leases from the file.
@@ -153,6 +153,7 @@ TEST_F(CSVLeaseFile4Test, parse) {
     EXPECT_TRUE(lease->fqdn_fwd_);
     EXPECT_TRUE(lease->fqdn_fwd_);
     EXPECT_TRUE(lease->fqdn_rev_);
     EXPECT_TRUE(lease->fqdn_rev_);
     EXPECT_EQ("host.example.com", lease->hostname_);
     EXPECT_EQ("host.example.com", lease->hostname_);
+    EXPECT_EQ(Lease::STATE_DEFAULT, lease->state_);
     }
     }
 
 
     // Second lease is malformed - HW address is empty.
     // Second lease is malformed - HW address is empty.
@@ -182,6 +183,7 @@ TEST_F(CSVLeaseFile4Test, parse) {
     EXPECT_FALSE(lease->fqdn_fwd_);
     EXPECT_FALSE(lease->fqdn_fwd_);
     EXPECT_FALSE(lease->fqdn_rev_);
     EXPECT_FALSE(lease->fqdn_rev_);
     EXPECT_TRUE(lease->hostname_.empty());
     EXPECT_TRUE(lease->hostname_.empty());
+    EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
     }
     }
 
 
     // There are no more leases. Reading should cause no error, but the returned
     // There are no more leases. Reading should cause no error, but the returned
@@ -217,6 +219,7 @@ TEST_F(CSVLeaseFile4Test, recreate) {
                                NULL, 0,
                                NULL, 0,
                                200, 50, 80, 0, 8, true, true,
                                200, 50, 80, 0, 8, true, true,
                                "host.example.com"));
                                "host.example.com"));
+    lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
     {
     {
     SCOPED_TRACE("First write");
     SCOPED_TRACE("First write");
     ASSERT_NO_THROW(lf->append(*lease));
     ASSERT_NO_THROW(lf->append(*lease));
@@ -238,10 +241,10 @@ TEST_F(CSVLeaseFile4Test, recreate) {
     lf->close();
     lf->close();
     // Check that the contents of the csv file are correct.
     // Check that the contents of the csv file are correct.
     EXPECT_EQ("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     EXPECT_EQ("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-              "fqdn_fwd,fqdn_rev,hostname\n"
-              "192.0.3.2,00:01:02:03:04:05,,200,200,8,1,1,host.example.com\n"
+              "fqdn_fwd,fqdn_rev,hostname,state\n"
+              "192.0.3.2,00:01:02:03:04:05,,200,200,8,1,1,host.example.com,2\n"
               "192.0.3.10,0d:0e:0a:0d:0b:0e:0e:0f,01:02:03:04,100,100,7,0,"
               "192.0.3.10,0d:0e:0a:0d:0b:0e:0e:0f,01:02:03:04,100,100,7,0,"
-              "0,\n",
+              "0,,0\n",
               io_.readFile());
               io_.readFile());
 }
 }
 
 

+ 10 - 9
src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc

@@ -110,14 +110,14 @@ void
 CSVLeaseFile6Test::writeSampleFile() const {
 CSVLeaseFile6Test::writeSampleFile() const {
     io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
     io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-                  "fqdn_rev,hostname,hwaddr\n"
+                  "fqdn_rev,hostname,hwaddr,state\n"
                   "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
                   "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                  "200,200,8,100,0,7,0,1,1,host.example.com\n"
-                  "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com\n"
+                  "200,200,8,100,0,7,0,1,1,host.example.com,,1\n"
+                  "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,,1\n"
                   "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
                   "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
-                  "0,8,0,0,0,\n"
+                  "0,8,0,0,0,,,1\n"
                   "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,0,200,8,0,2,"
                   "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,0,200,8,0,2,"
-                  "16,64,0,0,,\n");
+                  "16,64,0,0,,,1\n");
 }
 }
 
 
 // This test checks the capability to read and parse leases from the file.
 // This test checks the capability to read and parse leases from the file.
@@ -277,13 +277,14 @@ TEST_F(CSVLeaseFile6Test, recreate) {
     }
     }
 
 
     EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-              "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+              "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+              "state\n"
               "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
               "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-              "200,200,8,100,0,7,0,1,1,host.example.com,\n"
+              "200,200,8,100,0,7,0,1,1,host.example.com,,0\n"
               "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05"
               "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05"
-              ",300,300,6,150,0,8,128,0,0,,\n"
+              ",300,300,6,150,0,8,128,0,0,,,0\n"
               "3000:1:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
               "3000:1:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-              "300,300,10,150,2,7,64,0,0,,\n",
+              "300,300,10,150,2,7,64,0,0,,,0\n",
               io_.readFile());
               io_.readFile());
 }
 }
 
 

+ 433 - 7
src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc

@@ -79,11 +79,8 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
     lease->addr_ = IOAddress(address);
     lease->addr_ = IOAddress(address);
 
 
     // Initialize unused fields.
     // Initialize unused fields.
-    lease->ext_ = 0;                            // Not saved
     lease->t1_ = 0;                             // Not saved
     lease->t1_ = 0;                             // Not saved
     lease->t2_ = 0;                             // Not saved
     lease->t2_ = 0;                             // Not saved
-    lease->fixed_ = false;                      // Unused
-    lease->comments_ = std::string("");         // Unused
 
 
     // Set other parameters.  For historical reasons, address 0 is not used.
     // Set other parameters.  For historical reasons, address 0 is not used.
     if (address == straddress4_[0]) {
     if (address == straddress4_[0]) {
@@ -199,8 +196,6 @@ GenericLeaseMgrTest::initializeLease6(std::string address) {
     // Initialize unused fields.
     // Initialize unused fields.
     lease->t1_ = 0;                             // Not saved
     lease->t1_ = 0;                             // Not saved
     lease->t2_ = 0;                             // Not saved
     lease->t2_ = 0;                             // Not saved
-    lease->fixed_ = false;                      // Unused
-    lease->comments_ = std::string("");         // Unused
 
 
     // Set other parameters.  For historical reasons, address 0 is not used.
     // Set other parameters.  For historical reasons, address 0 is not used.
     if (address == straddress6_[0]) {
     if (address == straddress6_[0]) {
@@ -1304,8 +1299,6 @@ GenericLeaseMgrTest::testLease6LeaseTypeCheck() {
     // Initialize unused fields.
     // Initialize unused fields.
     empty_lease->t1_ = 0;                             // Not saved
     empty_lease->t1_ = 0;                             // Not saved
     empty_lease->t2_ = 0;                             // Not saved
     empty_lease->t2_ = 0;                             // Not saved
-    empty_lease->fixed_ = false;                      // Unused
-    empty_lease->comments_ = std::string("");         // Unused
     empty_lease->iaid_ = 142;
     empty_lease->iaid_ = 142;
     empty_lease->duid_ = DuidPtr(new DUID(*duid));
     empty_lease->duid_ = DuidPtr(new DUID(*duid));
     empty_lease->subnet_id_ = 23;
     empty_lease->subnet_id_ = 23;
@@ -1657,6 +1650,439 @@ GenericLeaseMgrTest::testVersion(int major, int minor) {
     EXPECT_EQ(minor, lmptr_->getVersion().second);
     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.
+        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));
+        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));
+        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::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();
+        }
+    }
+}
 
 
 }; // namespace test
 }; // namespace test
 }; // namespace dhcp
 }; // namespace dhcp

+ 37 - 1
src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -265,6 +265,42 @@ public:
     /// @param minor Expected minor version to be reported.
     /// @param minor Expected minor version to be reported.
     void testVersion(int major, int minor);
     void testVersion(int major, int minor);
 
 
+    /// @brief Checks that the expired DHCPv4 leases can be retrieved.
+    ///
+    /// This test checks the following:
+    /// - all expired and not reclaimed leases are retured
+    /// - number of leases returned can be limited
+    /// - leases are returned in the order from the most expired to the
+    ///   least expired
+    /// - reclaimed leases are not returned.
+    void testGetExpiredLeases4();
+
+    /// @brief Checks that the expired DHCPv6 leases can be retrieved.
+    ///
+    /// This test checks the following:
+    /// - all expired and not reclaimed leases are retured
+    /// - number of leases returned can be limited
+    /// - leases are returned in the order from the most expired to the
+    ///   least expired
+    /// - reclaimed leases are not returned.
+    void testGetExpiredLeases6();
+
+    /// @brief Checks that selected expired-reclaimed DHCPv6 leases
+    /// are removed.
+    ///
+    /// This creates a number of DHCPv6 leases and marks some of them
+    /// as expired-reclaimed. It later verifies that the expired-reclaimed
+    /// leases can be removed.
+    void testDeleteExpiredReclaimedLeases6();
+
+    /// @brief Checks that selected expired-reclaimed DHCPv4 leases
+    /// are removed.
+    ///
+    /// This creates a number of DHCPv4 leases and marks some of them
+    /// as expired-reclaimed. It later verifies that the expired-reclaimed
+    /// leases can be removed.
+    void testDeleteExpiredReclaimedLeases4();
+
     /// @brief String forms of IPv4 addresses
     /// @brief String forms of IPv4 addresses
     std::vector<std::string>  straddress4_;
     std::vector<std::string>  straddress4_;
 
 

+ 27 - 27
src/lib/dhcpsrv/tests/lease_file_loader_unittest.cc

@@ -151,11 +151,11 @@ protected:
     /// @brief Sets up the header strings
     /// @brief Sets up the header strings
     virtual void SetUp() {
     virtual void SetUp() {
         v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
         v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n";
+                  "fqdn_fwd,fqdn_rev,hostname,state\n";
 
 
         v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id,"
         v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-                  "fqdn_rev,hostname,hwaddr\n";
+                  "fqdn_rev,hostname,hwaddr,state\n";
     }
     }
 };
 };
 
 
@@ -179,17 +179,17 @@ LeaseFileLoaderTest::absolutePath(const std::string& filename) {
 TEST_F(LeaseFileLoaderTest, loadWrite4) {
 TEST_F(LeaseFileLoaderTest, loadWrite4) {
     std::string test_str;
     std::string test_str;
     std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
     std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                      "200,200,8,1,1,host.example.com\n";
+                      "200,200,8,1,1,host.example.com,1\n";
     std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
     std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                      "200,500,8,1,1,host.example.com\n";
+                      "200,500,8,1,1,host.example.com,1\n";
 
 
     std::string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
     std::string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
-                      "100,100,7,0,0,\n";
+                      "100,100,7,0,0,,1\n";
     std::string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
     std::string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
-                      "100,135,7,0,0,\n";
+                      "100,135,7,0,0,,1\n";
 
 
     std::string c_1 = "192.0.2.3,,a:11:01:04,"
     std::string c_1 = "192.0.2.3,,a:11:01:04,"
-                      "200,200,8,1,1,host.example.com\n";
+                      "200,200,8,1,1,host.example.com,1\n";
 
 
     // Create lease file with leases for 192.0.2.1, 192.0.3.15. The lease
     // Create lease file with leases for 192.0.2.1, 192.0.3.15. The lease
     // entry for the 192.0.2.3 is invalid (lacks HW address) and should
     // entry for the 192.0.2.3 is invalid (lacks HW address) and should
@@ -250,14 +250,14 @@ TEST_F(LeaseFileLoaderTest, loadWrite4) {
 TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) {
 TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) {
     std::string test_str;
     std::string test_str;
     std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
     std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                      "200,200,8,1,1,host.example.com\n";
+                      "200,200,8,1,1,host.example.com,1\n";
     std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
     std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                      "0,500,8,1,1,host.example.com\n";
+                      "0,500,8,1,1,host.example.com,1\n";
 
 
     std::string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
     std::string b_1 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
-                      "100,100,7,0,0,\n";
+                      "100,100,7,0,0,,1\n";
     std::string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
     std::string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
-                      "100,135,7,0,0,\n";
+                      "100,135,7,0,0,,1\n";
 
 
 
 
     // Create lease file in which one of the entries for 192.0.2.1
     // Create lease file in which one of the entries for 192.0.2.1
@@ -305,19 +305,19 @@ TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) {
 TEST_F(LeaseFileLoaderTest, loadWrite6) {
 TEST_F(LeaseFileLoaderTest, loadWrite6) {
     std::string test_str;
     std::string test_str;
     std::string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     std::string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                      "200,200,8,100,0,7,0,1,1,host.example.com,\n";
+                      "200,200,8,100,0,7,0,1,1,host.example.com,,1\n";
     std::string a_2 = "2001:db8:1::1,,"
     std::string a_2 = "2001:db8:1::1,,"
-                      "200,200,8,100,0,7,0,1,1,host.example.com,\n";
+                      "200,200,8,100,0,7,0,1,1,host.example.com,,1\n";
     std::string a_3 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     std::string a_3 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                      "200,400,8,100,0,7,0,1,1,host.example.com,\n";
+                      "200,400,8,100,0,7,0,1,1,host.example.com,,1\n";
 
 
     std::string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
     std::string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
-                      "300,300,6,150,0,8,0,0,0,,\n";
+                      "300,300,6,150,0,8,0,0,0,,,1\n";
     std::string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
     std::string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
-                      "300,800,6,150,0,8,0,0,0,,\n";
+                      "300,800,6,150,0,8,0,0,0,,,1\n";
 
 
     std::string c_1 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     std::string c_1 = "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                      "100,200,8,0,2,16,64,0,0,,\n";
+                      "100,200,8,0,2,16,64,0,0,,,1\n";
 
 
 
 
 
 
@@ -379,14 +379,14 @@ TEST_F(LeaseFileLoaderTest, loadWrite6) {
 TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) {
 TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) {
     std::string test_str;
     std::string test_str;
     std::string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     std::string a_1 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                      "200,200,8,100,0,7,0,1,1,host.example.com,\n";
+                      "200,200,8,100,0,7,0,1,1,host.example.com,,1\n";
     std::string a_2 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
     std::string a_2 = "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-                      "0,400,8,100,0,7,0,1,1,host.example.com,\n";
+                      "0,400,8,100,0,7,0,1,1,host.example.com,,1\n";
 
 
     std::string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
     std::string b_1 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
-                      "300,300,6,150,0,8,0,0,0,,\n";
+                      "300,300,6,150,0,8,0,0,0,,,1\n";
     std::string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
     std::string b_2 = "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,"
-                      "300,800,6,150,0,8,0,0,0,,\n";
+                      "300,800,6,150,0,8,0,0,0,,,1\n";
 
 
     // Create lease file in which one of the entries for the 2001:db8:1::1
     // Create lease file in which one of the entries for the 2001:db8:1::1
     // has valid lifetime set to 0, in which case the lease should be
     // has valid lifetime set to 0, in which case the lease should be
@@ -431,13 +431,13 @@ TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) {
 TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
 TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
     std::string test_str;
     std::string test_str;
     std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
     std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                      "200,200,8,1,1,host.example.com\n";
+                      "200,200,8,1,1,host.example.com,1\n";
     std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
     std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
-                      "200,500,8,1,1,host.example.com\n";
+                      "200,500,8,1,1,host.example.com,1\n";
 
 
-    std::string b_1 = "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n";
+    std::string b_1 = "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com,1\n";
 
 
-    std::string c_1 = "192.0.2.10,01:02:03:04:05:06,,200,300,8,1,1,\n";
+    std::string c_1 = "192.0.2.10,01:02:03:04:05:06,,200,300,8,1,1,,1\n";
 
 
     // Create a lease file for which there is a number of invalid
     // Create a lease file for which there is a number of invalid
     // entries.  b_1 is invalid and gets used multiple times.
     // entries.  b_1 is invalid and gets used multiple times.
@@ -501,8 +501,8 @@ TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
 // and comparing that with the expected value.
 // and comparing that with the expected value.
 TEST_F(LeaseFileLoaderTest, loadWriteLeaseWithZeroLifetime) {
 TEST_F(LeaseFileLoaderTest, loadWriteLeaseWithZeroLifetime) {
     std::string test_str;
     std::string test_str;
-    std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,\n";
-    std::string b_2 = "192.0.2.3,06:07:08:09:0a:bd,,0,200,8,1,1,\n";
+    std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,,1\n";
+    std::string b_2 = "192.0.2.3,06:07:08:09:0a:bd,,0,200,8,1,1,,1\n";
 
 
     // Create lease file. The second lease has a valid lifetime of 0.
     // Create lease file. The second lease has a valid lifetime of 0.
     test_str = v4_hdr_ + a_1 + b_2;
     test_str = v4_hdr_ + a_1 + b_2;

+ 38 - 1
src/lib/dhcpsrv/tests/lease_mgr_unittest.cc

@@ -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
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -173,6 +173,23 @@ public:
         return (leases6_);
         return (leases6_);
     }
     }
 
 
+
+    /// @brief Returns expired DHCPv6 leases.
+    ///
+    /// This method is not implemented.
+    virtual void getExpiredLeases6(Lease6Collection&, const size_t) const {
+        isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases6 is not"
+                  " implemented");
+    }
+
+    /// @brief Returns expired DHCPv4 leases.
+    ///
+    /// This method is not implemented.
+    virtual void getExpiredLeases4(Lease4Collection&, const size_t) const {
+        isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases4 is not"
+                  " implemented");
+    }
+
     /// @brief Updates IPv4 lease.
     /// @brief Updates IPv4 lease.
     ///
     ///
     /// @param lease4 The lease to be updated.
     /// @param lease4 The lease to be updated.
@@ -197,6 +214,26 @@ public:
         return (false);
         return (false);
     }
     }
 
 
+    /// @brief Deletes all expired and reclaimed DHCPv4 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.
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t) {
+        isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases4"
+                  " is not implemented");
+    }
+
+    /// @brief Deletes all expired and reclaimed DHCPv6 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.
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t) {
+        isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases6"
+                  " is not implemented");
+    }
+
     /// @brief Returns backend type.
     /// @brief Returns backend type.
     ///
     ///
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)

+ 48 - 41
src/lib/dhcpsrv/tests/lease_unittest.cc

@@ -96,7 +96,6 @@ TEST_F(Lease4Test, constructor) {
                      "hostname.example.com.");
                      "hostname.example.com.");
 
 
         EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
         EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
-        EXPECT_EQ(0, lease.ext_);
         EXPECT_TRUE(util::equalValues(hwaddr_, lease.hwaddr_));
         EXPECT_TRUE(util::equalValues(hwaddr_, lease.hwaddr_));
         EXPECT_TRUE(util::equalValues(clientid_, lease.client_id_));
         EXPECT_TRUE(util::equalValues(clientid_, lease.client_id_));
         EXPECT_EQ(0, lease.t1_);
         EXPECT_EQ(0, lease.t1_);
@@ -104,11 +103,10 @@ TEST_F(Lease4Test, constructor) {
         EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
         EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
         EXPECT_EQ(current_time, lease.cltt_);
         EXPECT_EQ(current_time, lease.cltt_);
         EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
         EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
-        EXPECT_FALSE(lease.fixed_);
         EXPECT_EQ("hostname.example.com.", lease.hostname_);
         EXPECT_EQ("hostname.example.com.", lease.hostname_);
         EXPECT_TRUE(lease.fqdn_fwd_);
         EXPECT_TRUE(lease.fqdn_fwd_);
         EXPECT_TRUE(lease.fqdn_rev_);
         EXPECT_TRUE(lease.fqdn_rev_);
-        EXPECT_TRUE(lease.comments_.empty());
+        EXPECT_EQ(Lease::STATE_DEFAULT, lease.state_);
     }
     }
 }
 }
 
 
@@ -126,6 +124,10 @@ TEST_F(Lease4Test, copyConstructor) {
     Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
     Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
                  SUBNET_ID);
                  SUBNET_ID);
 
 
+    // Declined is a non-default state. We'll see if the state will be copied
+    // or the default state will be set for the copied lease.
+    lease.state_ = Lease::STATE_DECLINED;
+
     // Use copy constructor to copy the lease.
     // Use copy constructor to copy the lease.
     Lease4 copied_lease(lease);
     Lease4 copied_lease(lease);
 
 
@@ -163,8 +165,14 @@ TEST_F(Lease4Test, operatorAssign) {
     Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
     Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
                  SUBNET_ID);
                  SUBNET_ID);
 
 
-    // Use assignment operator to assign the lease.
-    Lease4 copied_lease = lease;
+    // Declined is a non-default state. We'll see if the state will be copied
+    // or the default state will be set for the copied lease.
+    lease.state_ = Lease::STATE_DECLINED;
+
+    // Create a default lease.
+    Lease4 copied_lease;
+    // Use assignment operator to assign new lease.
+    copied_lease = lease;
 
 
     // Both leases should be now equal. When doing this check we assume that
     // Both leases should be now equal. When doing this check we assume that
     // the equality operator works correctly.
     // the equality operator works correctly.
@@ -284,13 +292,6 @@ TEST_F(Lease4Test, operatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
 
-    ++lease1.ext_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.ext_ = lease2.ext_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
     ++lease1.hwaddr_->hwaddr_[0];
     ++lease1.hwaddr_->hwaddr_[0];
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_TRUE(lease1 != lease2);
     EXPECT_TRUE(lease1 != lease2);
@@ -343,13 +344,6 @@ TEST_F(Lease4Test, operatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
 
-    lease1.fixed_ = !lease1.fixed_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fixed_ = lease2.fixed_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
     lease1.hostname_ += std::string("Something random");
     lease1.hostname_ += std::string("Something random");
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_TRUE(lease1 != lease2);
     EXPECT_TRUE(lease1 != lease2);
@@ -371,10 +365,10 @@ TEST_F(Lease4Test, operatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
 
-    lease1.comments_ += std::string("Something random");
+    lease1.state_  += 1;
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_TRUE(lease1 != lease2);
     EXPECT_TRUE(lease1 != lease2);
-    lease1.comments_ = lease2.comments_;
+    lease2.state_ += 1;
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 }
 }
@@ -430,7 +424,8 @@ TEST_F(Lease4Test, toText) {
              << "Cltt:          12345678\n"
              << "Cltt:          12345678\n"
              << "Hardware addr: " << hwaddr_->toText(false) << "\n"
              << "Hardware addr: " << hwaddr_->toText(false) << "\n"
              << "Client id:     " << clientid_->toText() << "\n"
              << "Client id:     " << clientid_->toText() << "\n"
-             << "Subnet ID:     789\n";
+             << "Subnet ID:     789\n"
+             << "State:         default\n";
 
 
     EXPECT_EQ(expected.str(), lease.toText());
     EXPECT_EQ(expected.str(), lease.toText());
 
 
@@ -445,10 +440,18 @@ TEST_F(Lease4Test, toText) {
              << "Cltt:          12345678\n"
              << "Cltt:          12345678\n"
              << "Hardware addr: (none)\n"
              << "Hardware addr: (none)\n"
              << "Client id:     (none)\n"
              << "Client id:     (none)\n"
-             << "Subnet ID:     789\n";
+             << "Subnet ID:     789\n"
+             << "State:         default\n";
     EXPECT_EQ(expected.str(), lease.toText());
     EXPECT_EQ(expected.str(), lease.toText());
 }
 }
 
 
+// Verify that the lease states are correctly returned in the textual format.
+TEST_F(Lease4Test, stateToText) {
+    EXPECT_EQ("default", Lease4::statesToText(Lease::STATE_DEFAULT));
+    EXPECT_EQ("declined", Lease4::statesToText(Lease::STATE_DECLINED));
+    EXPECT_EQ("expired-reclaimed", Lease4::statesToText(Lease::STATE_EXPIRED_RECLAIMED));
+}
+
 /// @brief Creates an instance of the lease with certain FQDN data.
 /// @brief Creates an instance of the lease with certain FQDN data.
 ///
 ///
 /// @param hostname Hostname.
 /// @param hostname Hostname.
@@ -467,7 +470,7 @@ Lease6 createLease6(const std::string& hostname, const bool fqdn_fwd,
 
 
 // Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
 // Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
 // This test checks if the Lease6 structure can be instantiated correctly
 // This test checks if the Lease6 structure can be instantiated correctly
-TEST(Lease6, Lease6ConstructorDefault) {
+TEST(Lease6Test, Lease6ConstructorDefault) {
 
 
     // check a variety of addresses with different bits set.
     // check a variety of addresses with different bits set.
     const char* ADDRESS[] = {
     const char* ADDRESS[] = {
@@ -514,7 +517,7 @@ TEST(Lease6, Lease6ConstructorDefault) {
 
 
 // This test verifies that the Lease6 constructor which accepts FQDN data,
 // This test verifies that the Lease6 constructor which accepts FQDN data,
 // sets the data correctly for the lease.
 // sets the data correctly for the lease.
-TEST(Lease6, Lease6ConstructorWithFQDN) {
+TEST(Lease6Test, Lease6ConstructorWithFQDN) {
 
 
     // check a variety of addresses with different bits set.
     // check a variety of addresses with different bits set.
     const char* ADDRESS[] = {
     const char* ADDRESS[] = {
@@ -563,7 +566,7 @@ TEST(Lease6, Lease6ConstructorWithFQDN) {
 /// Checks that the operator==() correctly compares two leases for equality.
 /// Checks that the operator==() correctly compares two leases for equality.
 /// As operator!=() is also defined for this class, every check on operator==()
 /// As operator!=() is also defined for this class, every check on operator==()
 /// is followed by the reverse check on operator!=().
 /// is followed by the reverse check on operator!=().
-TEST(Lease6, OperatorEquals) {
+TEST(Lease6Test, operatorEquals) {
 
 
     // check a variety of addresses with different bits set.
     // check a variety of addresses with different bits set.
     const IOAddress addr("2001:db8:1::456");
     const IOAddress addr("2001:db8:1::456");
@@ -665,13 +668,6 @@ TEST(Lease6, OperatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
 
-    lease1.fixed_ = !lease1.fixed_;
-    EXPECT_FALSE(lease1 == lease2);
-    EXPECT_TRUE(lease1 != lease2);
-    lease1.fixed_ = lease2.fixed_;
-    EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
-    EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
     lease1.hostname_ += std::string("Something random");
     lease1.hostname_ += std::string("Something random");
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_TRUE(lease1 != lease2);
     EXPECT_TRUE(lease1 != lease2);
@@ -693,16 +689,16 @@ TEST(Lease6, OperatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
 
-    lease1.comments_ += std::string("Something random");
+    lease1.state_  += 1;
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_TRUE(lease1 != lease2);
     EXPECT_TRUE(lease1 != lease2);
-    lease1.comments_ = lease2.comments_;
+    lease2.state_ += 1;
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 }
 }
 
 
 // Checks if lease expiration is calculated properly
 // Checks if lease expiration is calculated properly
-TEST(Lease6, Lease6Expired) {
+TEST(Lease6Test, Lease6Expired) {
     const IOAddress addr("2001:db8:1::456");
     const IOAddress addr("2001:db8:1::456");
     const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
     const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
     const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
     const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
@@ -727,7 +723,7 @@ TEST(Lease6, Lease6Expired) {
 
 
 // Verify that the DUID can be returned as a vector object and if DUID is NULL
 // Verify that the DUID can be returned as a vector object and if DUID is NULL
 // the empty vector is returned.
 // the empty vector is returned.
-TEST(Lease6, getDuidVector) {
+TEST(Lease6Test, getDuidVector) {
     // Create a lease.
     // Create a lease.
     Lease6 lease;
     Lease6 lease;
     // By default, the lease should have client id set to NULL. If it doesn't,
     // By default, the lease should have client id set to NULL. If it doesn't,
@@ -746,7 +742,7 @@ TEST(Lease6, getDuidVector) {
 }
 }
 
 
 // Verify the behavior of the function which checks FQDN data for equality.
 // Verify the behavior of the function which checks FQDN data for equality.
-TEST(Lease6, hasIdenticalFqdn) {
+TEST(Lease6Test, hasIdenticalFqdn) {
     Lease6 lease = createLease6("myhost.example.com.", true, true);
     Lease6 lease = createLease6("myhost.example.com.", true, true);
     EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
     EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
                                                     true, true)));
                                                     true, true)));
@@ -763,7 +759,7 @@ TEST(Lease6, hasIdenticalFqdn) {
 }
 }
 
 
 // Verify that toText() method reports Lease4 structure properly.
 // Verify that toText() method reports Lease4 structure properly.
-TEST(Lease6, toText) {
+TEST(Lease6Test, toText) {
 
 
     HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
     HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
 
 
@@ -773,6 +769,7 @@ TEST(Lease6, toText) {
     Lease6 lease(Lease::TYPE_NA, IOAddress("2001:db8::1"), duid, 123456,
     Lease6 lease(Lease::TYPE_NA, IOAddress("2001:db8::1"), duid, 123456,
                  400, 800, 100, 200, 5678, hwaddr, 128);
                  400, 800, 100, 200, 5678, hwaddr, 128);
     lease.cltt_ = 12345678;
     lease.cltt_ = 12345678;
+    lease.state_ = Lease::STATE_DECLINED;
     
     
     std::stringstream expected;
     std::stringstream expected;
     expected << "Type:          IA_NA(" << static_cast<int>(Lease::TYPE_NA) << ")\n"
     expected << "Type:          IA_NA(" << static_cast<int>(Lease::TYPE_NA) << ")\n"
@@ -783,7 +780,8 @@ TEST(Lease6, toText) {
              << "Valid life:    800\n"
              << "Valid life:    800\n"
              << "Cltt:          12345678\n"
              << "Cltt:          12345678\n"
              << "Hardware addr: " << hwaddr->toText(false) << "\n"
              << "Hardware addr: " << hwaddr->toText(false) << "\n"
-             << "Subnet ID:     5678\n";
+             << "Subnet ID:     5678\n"
+             << "State:         declined\n";
 
 
     EXPECT_EQ(expected.str(), lease.toText());
     EXPECT_EQ(expected.str(), lease.toText());
 
 
@@ -798,8 +796,17 @@ TEST(Lease6, toText) {
              << "Valid life:    800\n"
              << "Valid life:    800\n"
              << "Cltt:          12345678\n"
              << "Cltt:          12345678\n"
              << "Hardware addr: (none)\n"
              << "Hardware addr: (none)\n"
-             << "Subnet ID:     5678\n";
+             << "Subnet ID:     5678\n"
+             << "State:         declined\n";
     EXPECT_EQ(expected.str(), lease.toText());
     EXPECT_EQ(expected.str(), lease.toText());
 }
 }
 
 
+// Verify that the lease states are correctly returned in the textual format.
+TEST(Lease6Test, stateToText) {
+    EXPECT_EQ("default", Lease6::statesToText(Lease::STATE_DEFAULT));
+    EXPECT_EQ("declined", Lease6::statesToText(Lease::STATE_DECLINED));
+    EXPECT_EQ("expired-reclaimed", Lease6::statesToText(Lease::STATE_EXPIRED_RECLAIMED));
+}
+
+
 }; // end of anonymous namespace
 }; // end of anonymous namespace

+ 131 - 124
src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc

@@ -404,20 +404,20 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup4) {
     // stored.
     // stored.
     std::string new_file_contents =
     std::string new_file_contents =
         "address,hwaddr,client_id,valid_lifetime,expire,"
         "address,hwaddr,client_id,valid_lifetime,expire,"
-        "subnet_id,fqdn_fwd,fqdn_rev,hostname\n";
+        "subnet_id,fqdn_fwd,fqdn_rev,hostname,state\n";
 
 
     // This string contains the contents of the lease file with exactly
     // This string contains the contents of the lease file with exactly
     // one lease, but two entries. One of the entries should be removed
     // one lease, but two entries. One of the entries should be removed
     // as a result of lease file cleanup.
     // as a result of lease file cleanup.
     std::string current_file_contents = new_file_contents +
     std::string current_file_contents = new_file_contents +
-        "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n"
-        "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,,\n";
+        "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n"
+        "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,,1\n";
     LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv"));
     LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv"));
     current_file.writeFile(current_file_contents);
     current_file.writeFile(current_file_contents);
 
 
     std::string previous_file_contents = new_file_contents +
     std::string previous_file_contents = new_file_contents +
-        "192.0.2.3,03:03:03:03:03:03,,200,200,8,1,1,,\n"
-        "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,,\n";
+        "192.0.2.3,03:03:03:03:03:03,,200,200,8,1,1,,1\n"
+        "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,,1\n";
     LeaseFileIO previous_file(getLeaseFilePath("leasefile4_0.csv.2"));
     LeaseFileIO previous_file(getLeaseFilePath("leasefile4_0.csv.2"));
     previous_file.writeFile(previous_file_contents);
     previous_file.writeFile(previous_file_contents);
 
 
@@ -453,15 +453,15 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup4) {
     ASSERT_NO_THROW(lease_mgr->addLease(new_lease));
     ASSERT_NO_THROW(lease_mgr->addLease(new_lease));
 
 
     std::string updated_file_contents = new_file_contents +
     std::string updated_file_contents = new_file_contents +
-        "192.0.2.45,00:00:00:00:00:00,,100,100,1,0,0,\n";
+        "192.0.2.45,00:00:00:00:00:00,,100,100,1,0,0,,0\n";
     EXPECT_EQ(updated_file_contents, current_file.readFile());
     EXPECT_EQ(updated_file_contents, current_file.readFile());
 
 
     // This string contains the contents of the lease file we
     // This string contains the contents of the lease file we
     // expect after the LFC run.  It has two leases with one
     // expect after the LFC run.  It has two leases with one
     // entry each.
     // entry each.
     std::string result_file_contents = new_file_contents +
     std::string result_file_contents = new_file_contents +
-        "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,\n"
-        "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,\n";
+        "192.0.2.2,02:02:02:02:02:02,,200,800,8,1,1,,1\n"
+        "192.0.2.3,03:03:03:03:03:03,,200,800,8,1,1,,1\n";
 
 
     // The LFC should have created a file with the two leases and moved it
     // The LFC should have created a file with the two leases and moved it
     // to leasefile4_0.csv.2
     // to leasefile4_0.csv.2
@@ -480,24 +480,24 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) {
     std::string new_file_contents =
     std::string new_file_contents =
         "address,duid,valid_lifetime,expire,subnet_id,"
         "address,duid,valid_lifetime,expire,subnet_id,"
         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-        "fqdn_rev,hostname,hwaddr\n";
+        "fqdn_rev,hostname,hwaddr,state\n";
 
 
     // This string contains the contents of the lease file with exactly
     // This string contains the contents of the lease file with exactly
     // one lease, but two entries. One of the entries should be removed
     // one lease, but two entries. One of the entries should be removed
     // as a result of lease file cleanup.
     // as a result of lease file cleanup.
     std::string current_file_contents = new_file_contents +
     std::string current_file_contents = new_file_contents +
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200,"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200,"
-        "8,100,0,7,0,1,1,,\n"
+        "8,100,0,7,0,1,1,,,1\n"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
-        "8,100,0,7,0,1,1,,\n";
+        "8,100,0,7,0,1,1,,,1\n";
     LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv"));
     LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv"));
     current_file.writeFile(current_file_contents);
     current_file.writeFile(current_file_contents);
 
 
     std::string previous_file_contents = new_file_contents +
     std::string previous_file_contents = new_file_contents +
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,200,"
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,200,"
-        "8,100,0,7,0,1,1,,\n"
+        "8,100,0,7,0,1,1,,,1\n"
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
-        "8,100,0,7,0,1,1,,\n";
+        "8,100,0,7,0,1,1,,,1\n";
     LeaseFileIO previous_file(getLeaseFilePath("leasefile6_0.csv.2"));
     LeaseFileIO previous_file(getLeaseFilePath("leasefile6_0.csv.2"));
     previous_file.writeFile(previous_file_contents);
     previous_file.writeFile(previous_file_contents);
 
 
@@ -536,7 +536,7 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) {
 
 
     std::string update_file_contents = new_file_contents +
     std::string update_file_contents = new_file_contents +
         "3000::1,00:00:00:00:00:00:00:00:00:00:00:00:00,400,"
         "3000::1,00:00:00:00:00:00:00:00:00:00:00:00:00,400,"
-        "400,2,300,0,123,128,0,0,,\n";
+        "400,2,300,0,123,128,0,0,,,0\n";
     EXPECT_EQ(update_file_contents, current_file.readFile());
     EXPECT_EQ(update_file_contents, current_file.readFile());
 
 
     // This string contains the contents of the lease file we
     // This string contains the contents of the lease file we
@@ -544,9 +544,9 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanup6) {
     // entry each.
     // entry each.
     std::string result_file_contents = new_file_contents +
     std::string result_file_contents = new_file_contents +
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
-        "8,100,0,7,0,1,1,,\n"
+        "8,100,0,7,0,1,1,,,1\n"
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
-        "8,100,0,7,0,1,1,,\n";
+        "8,100,0,7,0,1,1,,,1\n";
 
 
     // The LFC should have created a file with the two leases and moved it
     // The LFC should have created a file with the two leases and moved it
     // to leasefile6_0.csv.2
     // to leasefile6_0.csv.2
@@ -564,11 +564,11 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCleanupStartFail) {
     // stored.
     // stored.
     std::string new_file_contents =
     std::string new_file_contents =
         "address,hwaddr,client_id,valid_lifetime,expire,"
         "address,hwaddr,client_id,valid_lifetime,expire,"
-        "subnet_id,fqdn_fwd,fqdn_rev,hostname\n";
+        "subnet_id,fqdn_fwd,fqdn_rev,hostname,state\n";
 
 
     // Create the lease file to be used by the backend.
     // Create the lease file to be used by the backend.
     std::string current_file_contents = new_file_contents +
     std::string current_file_contents = new_file_contents +
-        "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n";
+        "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n";
     LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv"));
     LeaseFileIO current_file(getLeaseFilePath("leasefile4_0.csv"));
     current_file.writeFile(current_file_contents);
     current_file.writeFile(current_file_contents);
 
 
@@ -605,15 +605,15 @@ TEST_F(MemfileLeaseMgrTest, leaseFileFinish) {
     std::string new_file_contents =
     std::string new_file_contents =
         "address,duid,valid_lifetime,expire,subnet_id,"
         "address,duid,valid_lifetime,expire,subnet_id,"
         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-        "fqdn_rev,hostname,hwaddr\n";
+        "fqdn_rev,hostname,hwaddr,state\n";
 
 
     // This string contains the contents of the current lease file.
     // This string contains the contents of the current lease file.
     // It should not be moved.
     // It should not be moved.
     std::string current_file_contents = new_file_contents +
     std::string current_file_contents = new_file_contents +
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200,"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200,"
-        "8,100,0,7,0,1,1,,\n"
+        "8,100,0,7,0,1,1,,,1\n"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
-        "8,100,0,7,0,1,1,,\n";
+        "8,100,0,7,0,1,1,,,1\n";
     LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv"));
     LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv"));
     current_file.writeFile(current_file_contents);
     current_file.writeFile(current_file_contents);
 
 
@@ -621,7 +621,7 @@ TEST_F(MemfileLeaseMgrTest, leaseFileFinish) {
     // be moved to the previous file.
     // be moved to the previous file.
     std::string finish_file_contents = new_file_contents +
     std::string finish_file_contents = new_file_contents +
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
-        "8,100,0,7,0,1,1,,\n";
+        "8,100,0,7,0,1,1,,,1\n";
     LeaseFileIO finish_file(getLeaseFilePath("leasefile6_0.csv.completed"));
     LeaseFileIO finish_file(getLeaseFilePath("leasefile6_0.csv.completed"));
     finish_file.writeFile(finish_file_contents);
     finish_file.writeFile(finish_file_contents);
 
 
@@ -668,15 +668,15 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCopy) {
     std::string new_file_contents =
     std::string new_file_contents =
         "address,duid,valid_lifetime,expire,subnet_id,"
         "address,duid,valid_lifetime,expire,subnet_id,"
         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-        "fqdn_rev,hostname,hwaddr\n";
+        "fqdn_rev,hostname,hwaddr,state\n";
 
 
     // This string contains the contents of the current lease file.
     // This string contains the contents of the current lease file.
     // It should not be moved.
     // It should not be moved.
     std::string current_file_contents = new_file_contents +
     std::string current_file_contents = new_file_contents +
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200,"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,200,"
-        "8,100,0,7,0,1,1,,\n"
+        "8,100,0,7,0,1,1,,,1\n"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
         "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,200,800,"
-        "8,100,0,7,0,1,1,,\n";
+        "8,100,0,7,0,1,1,,,1\n";
     LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv"));
     LeaseFileIO current_file(getLeaseFilePath("leasefile6_0.csv"));
     current_file.writeFile(current_file_contents);
     current_file.writeFile(current_file_contents);
 
 
@@ -686,7 +686,7 @@ TEST_F(MemfileLeaseMgrTest, leaseFileCopy) {
     // the same.
     // the same.
     std::string input_file_contents = new_file_contents +
     std::string input_file_contents = new_file_contents +
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
         "2001:db8:1::2,01:01:01:01:01:01:01:01:01:01:01:01:01,200,800,"
-        "8,100,0,7,0,1,1,,\n";
+        "8,100,0,7,0,1,1,,,1\n";
     LeaseFileIO input_file(getLeaseFilePath("leasefile6_0.csv.1"));
     LeaseFileIO input_file(getLeaseFilePath("leasefile6_0.csv.1"));
     input_file.writeFile(input_file_contents);
     input_file.writeFile(input_file_contents);
 
 
@@ -872,15 +872,48 @@ TEST_F(MemfileLeaseMgrTest, getLeases6DuidIaid) {
     testGetLeases6DuidIaid();
     testGetLeases6DuidIaid();
 }
 }
 
 
-// Check that the system can cope with a DUID of allowed size.
-
-/// @todo: test disabled, because Memfile_LeaseMgr::getLeases6(Lease::Type,
-/// const DUID& duid, uint32_t iaid) const is not implemented yet.
+/// @brief Check that the system can cope with a DUID of allowed size.
 TEST_F(MemfileLeaseMgrTest, getLeases6DuidSize) {
 TEST_F(MemfileLeaseMgrTest, getLeases6DuidSize) {
     startBackend(V6);
     startBackend(V6);
     testGetLeases6DuidSize();
     testGetLeases6DuidSize();
 }
 }
 
 
+/// @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(MemfileLeaseMgrTest, getExpiredLeases4) {
+    startBackend(V4);
+    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(MemfileLeaseMgrTest, getExpiredLeases6) {
+    startBackend(V6);
+    testGetExpiredLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv6 leases are removed.
+TEST_F(MemfileLeaseMgrTest, deleteExpiredReclaimedLeases6) {
+    startBackend(V6);
+    testDeleteExpiredReclaimedLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv4 leases are removed.
+TEST_F(MemfileLeaseMgrTest, deleteExpiredReclaimedLeases4) {
+    startBackend(V4);
+    testDeleteExpiredReclaimedLeases4();
+}
+
 /// @brief Check that getLease6 methods discriminate by lease type.
 /// @brief Check that getLease6 methods discriminate by lease type.
 ///
 ///
 /// Adds six leases, two per lease type all with the same duid and iad but
 /// Adds six leases, two per lease type all with the same duid and iad but
@@ -975,43 +1008,6 @@ TEST_F(MemfileLeaseMgrTest, testLease6Mac) {
     testLease6MAC();
     testLease6MAC();
 }
 }
 
 
-/// @brief Tests whether memfile is able to work with old CSV file (without mac)
-///
-/// Ticket #3555 introduced MAC address support in Lease6. Instead of developing
-/// an upgrade script, the code is written in a way that allows reading old CSV
-/// (i.e. format that was used in Kea 0.9), hence no upgrade is necessary.
-TEST_F(MemfileLeaseMgrTest, testUpgrade0_9_0_to_0_9_1) {
-
-    // Let's write a CSV file without hwaddr column. Sorry about the long
-    // lines, but nobody was around to whine about 80 columns limit when CSV
-    // format was invented :).
-    string csv_nohwaddr =
-        "address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname\n"
-        "2001:db8::1,42:42:42:42:42:42:42:42,3677,127133,73,3600,1,42,0,0,1,myhost.example.com.\n"
-        "2001:db8::2,3a:3a:3a:3a:3a:3a:3a:3a,5412,239979,73,1800,2,89,7,0,0,myhost.example.com.\n"
-        "2001:db8::3,1f:20:21:22:23:24:25:26,7000,241567,37,7200,0,4294967294,28,1,0,myhost.example.com.\n";
-
-    ofstream csv(getLeaseFilePath("leasefile6_0.csv").c_str(), ios::out | ios::trunc);
-    ASSERT_TRUE(csv.is_open());
-    csv << csv_nohwaddr;
-    csv.close();
-
-    startBackend(V6);
-
-    // None of the leases should have any hardware addresses assigned.
-    Lease6Ptr stored1 = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
-    ASSERT_TRUE(stored1);
-    EXPECT_FALSE(stored1->hwaddr_);
-
-    Lease6Ptr stored2 = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
-    ASSERT_TRUE(stored2);
-    EXPECT_FALSE(stored2->hwaddr_);
-
-    Lease6Ptr stored3 = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
-    ASSERT_TRUE(stored3);
-    EXPECT_FALSE(stored3->hwaddr_);
-}
-
 // Check that memfile reports version correctly.
 // Check that memfile reports version correctly.
 TEST_F(MemfileLeaseMgrTest, versionCheck) {
 TEST_F(MemfileLeaseMgrTest, versionCheck) {
 
 
@@ -1033,22 +1029,22 @@ TEST_F(MemfileLeaseMgrTest, versionCheck) {
 TEST_F(MemfileLeaseMgrTest, load4MultipleLeaseFiles) {
 TEST_F(MemfileLeaseMgrTest, load4MultipleLeaseFiles) {
     LeaseFileIO io2(getLeaseFilePath("leasefile4_0.csv.2"));
     LeaseFileIO io2(getLeaseFilePath("leasefile4_0.csv.2"));
     io2.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     io2.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n"
-                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,\n");
+                  "fqdn_fwd,fqdn_rev,hostname,state\n"
+                  "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n"
+                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,1\n");
 
 
     LeaseFileIO io1(getLeaseFilePath("leasefile4_0.csv.1"));
     LeaseFileIO io1(getLeaseFilePath("leasefile4_0.csv.1"));
     io1.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     io1.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,\n"
-                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,\n"
-                  "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,\n");
+                  "fqdn_fwd,fqdn_rev,hostname,state\n"
+                  "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,1\n"
+                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,1\n"
+                  "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,1\n");
 
 
     LeaseFileIO io(getLeaseFilePath("leasefile4_0.csv"));
     LeaseFileIO io(getLeaseFilePath("leasefile4_0.csv"));
     io.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     io.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                 "fqdn_fwd,fqdn_rev,hostname\n"
-                 "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,\n"
-                 "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,\n");
+                 "fqdn_fwd,fqdn_rev,hostname,state\n"
+                 "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,1\n"
+                 "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,1\n");
 
 
     startBackend(V4);
     startBackend(V4);
 
 
@@ -1091,27 +1087,27 @@ TEST_F(MemfileLeaseMgrTest, load4MultipleLeaseFiles) {
 TEST_F(MemfileLeaseMgrTest, load4CompletedFile) {
 TEST_F(MemfileLeaseMgrTest, load4CompletedFile) {
     LeaseFileIO io2(getLeaseFilePath("leasefile4_0.csv.2"));
     LeaseFileIO io2(getLeaseFilePath("leasefile4_0.csv.2"));
     io2.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     io2.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,\n"
-                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,\n");
+                  "fqdn_fwd,fqdn_rev,hostname,state\n"
+                  "192.0.2.2,02:02:02:02:02:02,,200,200,8,1,1,,1\n"
+                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,200,8,1,1,,1\n");
 
 
     LeaseFileIO io1(getLeaseFilePath("leasefile4_0.csv.1"));
     LeaseFileIO io1(getLeaseFilePath("leasefile4_0.csv.1"));
     io1.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     io1.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,\n"
-                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,\n"
-                  "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,\n");
+                  "fqdn_fwd,fqdn_rev,hostname,state\n"
+                  "192.0.2.1,01:01:01:01:01:01,,200,200,8,1,1,,1\n"
+                  "192.0.2.11,bb:bb:bb:bb:bb:bb,,200,400,8,1,1,,1\n"
+                  "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,200,8,1,1,,1\n");
 
 
     LeaseFileIO io(getLeaseFilePath("leasefile4_0.csv"));
     LeaseFileIO io(getLeaseFilePath("leasefile4_0.csv"));
     io.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     io.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                 "fqdn_fwd,fqdn_rev,hostname\n"
-                 "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,\n"
-                 "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,\n");
+                 "fqdn_fwd,fqdn_rev,hostname,state\n"
+                 "192.0.2.10,0a:0a:0a:0a:0a:0a,,200,200,8,1,1,,1\n"
+                 "192.0.2.12,cc:cc:cc:cc:cc:cc,,200,400,8,1,1,,1\n");
 
 
     LeaseFileIO ioc(getLeaseFilePath("leasefile4_0.csv.completed"));
     LeaseFileIO ioc(getLeaseFilePath("leasefile4_0.csv.completed"));
     ioc.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
     ioc.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.13,ff:ff:ff:ff:ff:ff,,200,200,8,1,1,,\n");
+                  "fqdn_fwd,fqdn_rev,hostname,state\n"
+                  "192.0.2.13,ff:ff:ff:ff:ff:ff,,200,200,8,1,1,,1\n");
 
 
     startBackend(V4);
     startBackend(V4);
 
 
@@ -1167,29 +1163,32 @@ TEST_F(MemfileLeaseMgrTest, load4LFCInProgress) {
 TEST_F(MemfileLeaseMgrTest, load6MultipleLeaseFiles) {
 TEST_F(MemfileLeaseMgrTest, load6MultipleLeaseFiles) {
     LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2"));
     LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2"));
     io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                  "state\n"
                   "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01,"
                   "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01,"
-                  "200,200,8,100,0,7,0,1,1,,\n"
+                  "200,200,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
-                  "200,200,8,100,0,7,0,1,1,,\n");
+                  "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1"));
     LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1"));
     io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                  "state\n"
                   "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03,"
                   "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03,"
-                  "200,200,8,100,0,7,0,1,1,,\n"
+                  "200,200,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
-                  "300,800,8,100,0,7,0,1,1,,\n"
+                  "300,800,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
                   "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
-                  "200,200,8,100,0,7,0,1,1,,\n");
+                  "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                 "state\n"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
-                 "400,1000,8,100,0,7,0,1,1,,\n"
+                 "400,1000,8,100,0,7,0,1,1,,,1\n"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
-                 "200,200,8,100,0,7,0,1,1,,\n");
+                 "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     startBackend(V6);
     startBackend(V6);
 
 
@@ -1230,21 +1229,23 @@ TEST_F(MemfileLeaseMgrTest, load6MultipleLeaseFiles) {
 TEST_F(MemfileLeaseMgrTest, load6MultipleNoSecondFile) {
 TEST_F(MemfileLeaseMgrTest, load6MultipleNoSecondFile) {
     LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1"));
     LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1"));
     io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                  "state\n"
                   "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03,"
                   "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03,"
-                  "200,200,8,100,0,7,0,1,1,,\n"
+                  "200,200,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
-                  "300,800,8,100,0,7,0,1,1,,\n"
+                  "300,800,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
                   "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
-                  "200,200,8,100,0,7,0,1,1,,\n");
+                  "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                 "state\n"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
-                 "400,1000,8,100,0,7,0,1,1,,\n"
+                 "400,1000,8,100,0,7,0,1,1,,,1\n"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
-                 "200,200,8,100,0,7,0,1,1,,\n");
+                 "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     startBackend(V6);
     startBackend(V6);
 
 
@@ -1276,19 +1277,21 @@ TEST_F(MemfileLeaseMgrTest, load6MultipleNoSecondFile) {
 TEST_F(MemfileLeaseMgrTest, load6MultipleNoFirstFile) {
 TEST_F(MemfileLeaseMgrTest, load6MultipleNoFirstFile) {
     LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2"));
     LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2"));
     io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                  "state\n"
                   "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01,"
                   "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01,"
-                  "200,200,8,100,0,7,0,1,1,,\n"
+                  "200,200,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
-                  "200,200,8,100,0,7,0,1,1,,\n");
+                  "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                 "state\n"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
-                 "400,1000,8,100,0,7,0,1,1,,\n"
+                 "400,1000,8,100,0,7,0,1,1,,,1\n"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
-                 "200,200,8,100,0,7,0,1,1,,\n");
+                 "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     startBackend(V6);
     startBackend(V6);
 
 
@@ -1322,35 +1325,39 @@ TEST_F(MemfileLeaseMgrTest, load6MultipleNoFirstFile) {
 TEST_F(MemfileLeaseMgrTest, load6CompletedFile) {
 TEST_F(MemfileLeaseMgrTest, load6CompletedFile) {
     LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2"));
     LeaseFileIO io2(getLeaseFilePath("leasefile6_0.csv.2"));
     io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io2.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                  "state\n"
                   "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01,"
                   "2001:db8:1::1,01:01:01:01:01:01:01:01:01:01:01:01:01,"
-                  "200,200,8,100,0,7,0,1,1,,\n"
+                  "200,200,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
-                  "200,200,8,100,0,7,0,1,1,,\n");
+                  "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1"));
     LeaseFileIO io1(getLeaseFilePath("leasefile6_0.csv.1"));
     io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io1.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                  "state\n"
                   "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03,"
                   "2001:db8:1::3,03:03:03:03:03:03:03:03:03:03:03:03:03,"
-                  "200,200,8,100,0,7,0,1,1,,\n"
+                  "200,200,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
                   "2001:db8:1::2,02:02:02:02:02:02:02:02:02:02:02:02:02,"
-                  "300,800,8,100,0,7,0,1,1,,\n"
+                  "300,800,8,100,0,7,0,1,1,,,1\n"
                   "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
                   "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
-                  "200,200,8,100,0,7,0,1,1,,\n");
+                  "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     LeaseFileIO io(getLeaseFilePath("leasefile6_0.csv"));
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     io.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                 "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                 "state\n"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
                  "2001:db8:1::4,04:04:04:04:04:04:04:04:04:04:04:04:04,"
-                 "400,1000,8,100,0,7,0,1,1,,\n"
+                 "400,1000,8,100,0,7,0,1,1,,,1\n"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
                  "2001:db8:1::5,05:05:05:05:05:05:05:05:05:05:05:05:05,"
-                 "200,200,8,100,0,7,0,1,1,,\n");
+                 "200,200,8,100,0,7,0,1,1,,,1\n");
 
 
     LeaseFileIO ioc(getLeaseFilePath("leasefile6_0.csv.completed"));
     LeaseFileIO ioc(getLeaseFilePath("leasefile6_0.csv.completed"));
     ioc.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     ioc.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
+                  "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+                  "state\n"
                   "2001:db8:1::125,ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff,"
                   "2001:db8:1::125,ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff,"
-                  "400,1000,8,100,0,7,0,1,1,,\n");
+                  "400,1000,8,100,0,7,0,1,1,,,1\n");
 
 
     startBackend(V6);
     startBackend(V6);