Browse Source

[trac3667] Complete logging work and add soem stats

Complete the current working on using the basic logging system
for messages from LFC

Add lease_stats to provide a class to gather statistics
about reading and writing leases to files.  Currently this
is used by the CSV file code and output by LFC.  The
information counted is: number of attempts, number of leases
and number of errors generated during reads and writes.
Shawn Routhier 10 years ago
parent
commit
6c4cc8c913

+ 12 - 1
src/bin/lfc/lfc_controller.cc

@@ -64,7 +64,7 @@ LFCController::launch(int argc, char* argv[], const bool test_mode) {
     // If we aren't running in test mode initialize the logging
     // system with the defaults.
     if (!test_mode)
-        initLogger(lfc_app_name_, WARN, 0, NULL, false);
+        initLogger(lfc_app_name_, INFO, 0, NULL, false);
 
     try {
         parseArgs(argc, argv);
@@ -345,6 +345,17 @@ LFCController::processLeases() const {
     LeaseFileType lf_output(getOutputFile());
     LeaseFileLoader::write<LeaseObjectType>(lf_output, storage);
 
+    // If desired log the stats
+    LOG_INFO(lfc_logger, LFC_READ_MESSAGE)
+      .arg(lf_prev.getReadLeases() + lf_copy.getReadLeases())
+      .arg(lf_prev.getReads() + lf_copy.getReads())
+      .arg(lf_prev.getReadErrs() + lf_copy.getReadErrs());
+
+    LOG_INFO(lfc_logger, LFC_WRITE_MESSAGE)
+      .arg(lf_output.getWriteLeases())
+      .arg(lf_output.getWrites())
+      .arg(lf_output.getWriteErrs());
+
     // Once we've finished the output file move it to the complete file
     if (rename(getOutputFile().c_str(), getFinishFile().c_str()) != 0) {
         isc_throw(RunTimeFail, "Unable to move output (" << output_file_

+ 11 - 1
src/bin/lfc/lfc_messages.mes

@@ -32,7 +32,17 @@ lease files.
 This message is issued just before LFC starts rotating the
 lease files - removing the old and replacing them with the new.
 
-% LFC_FAILURE_MESSAGE LFC failed: %1
+% LFC_FAILURE_MESSAGE : %1
 This message is issued if LFC detected a failure when trying
 to manipulate the files.  It includes a more specifc error string.
 
+% LFC_READ_MESSAGE Leases: %1, attempts: %2, errors: %3.
+This message print out the number of leases that were read, the
+number of attempts to read leases and the number of errors
+encountered while reading.
+
+% LFC_WRITE_MESSAGE Leases: %1, attempts: %2, errors: %3.
+This message print out the number of leases that were written, the
+number of attempts to write leases and the number of errors
+encountered while writing.
+

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

@@ -570,10 +570,10 @@ TEST_F(LFCControllerTest, launch6) {
     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";
 
-    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";
-    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";
+    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";
+    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";
 
     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";

+ 1 - 0
src/lib/dhcpsrv/Makefile.am

@@ -85,6 +85,7 @@ libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
 libkea_dhcpsrv_la_SOURCES += lease_file_loader.h
 libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
 libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
+libkea_dhcpsrv_la_SOURCES += lease_stats.h
 libkea_dhcpsrv_la_SOURCES += logging.cc logging.h
 libkea_dhcpsrv_la_SOURCES += logging_info.cc logging_info.h
 libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h

+ 38 - 3
src/lib/dhcpsrv/csv_lease_file4.cc

@@ -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
 // purpose with or without fee is hereby granted, provided that the above
@@ -26,10 +26,25 @@ CSVLeaseFile4::CSVLeaseFile4(const std::string& filename)
 }
 
 void
-CSVLeaseFile4::append(const Lease4& lease) const {
+CSVLeaseFile4::open() {
+    // Call the base class to open the file
+    CSVFile::open();
+
+    // and clear any statistics we may have
+    clearStatistics();
+}
+
+void
+CSVLeaseFile4::append(const Lease4& lease) {
+    // Bump the number of write attempts
+    ++writes_;
+
     CSVRow row(getColumnCount());
     row.writeAt(getColumnIndex("address"), lease.addr_.toText());
     if (!lease.hwaddr_) {
+        // Bump the error counter
+        ++write_errs_;
+
         isc_throw(BadValue, "Lease4 must have hardware address specified.");
     }
     row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
@@ -43,11 +58,24 @@ CSVLeaseFile4::append(const Lease4& lease) const {
     row.writeAt(getColumnIndex("fqdn_fwd"), lease.fqdn_fwd_);
     row.writeAt(getColumnIndex("fqdn_rev"), lease.fqdn_rev_);
     row.writeAt(getColumnIndex("hostname"), lease.hostname_);
-    CSVFile::append(row);
+
+    try {
+        CSVFile::append(row);
+    } catch (const std::exception& ex) {
+        // Catch any errors so we can bump the error counter than rethrow it
+        ++write_errs_;
+        throw;
+    }
+
+    // Bump the number of leases written
+    ++write_leases_;
 }
 
 bool
 CSVLeaseFile4::next(Lease4Ptr& lease) {
+    // Bump the number of read attempts
+    ++reads_;
+
     // Read the CSV row and try to create a lease from the values read.
     // This may easily result in exception. We don't want this function
     // to throw exceptions, so we catch them all and rather return the
@@ -88,12 +116,19 @@ CSVLeaseFile4::next(Lease4Ptr& lease) {
                                readHostname(row)));
 
     } catch (std::exception& ex) {
+        // bump the read error count
+        ++read_errs_;
+
         // The lease might have been created, so let's set it back to NULL to
         // signal that lease hasn't been parsed.
         lease.reset();
         setReadMsg(ex.what());
         return (false);
     }
+
+    // bump the number of leases read
+    ++read_leases_;
+
     return (true);
 }
 

+ 11 - 3
src/lib/dhcpsrv/csv_lease_file4.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
 // purpose with or without fee is hereby granted, provided that the above
@@ -19,6 +19,7 @@
 #include <dhcp/duid.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/subnet.h>
+#include <dhcpsrv/lease_stats.h>
 #include <util/csv_file.h>
 #include <stdint.h>
 #include <string>
@@ -38,7 +39,7 @@ namespace dhcp {
 /// validation (see http://kea.isc.org/ticket/2405). However, when #2405
 /// is implemented, the @c next function may need to be updated to use the
 /// validation capablity of @c Lease4.
-class CSVLeaseFile4 : public isc::util::CSVFile {
+class CSVLeaseFile4 : public isc::util::CSVFile, public LeaseFileStats {
 public:
 
     /// @brief Constructor.
@@ -48,6 +49,13 @@ public:
     /// @param filename Name of the lease file.
     CSVLeaseFile4(const std::string& filename);
 
+    /// @brief Opens a lease file.
+    ///
+    /// This function is used to clear the statistics while
+    /// calling the base class open.  It doesn't throw any
+    /// exceptions of its own but the base class may.
+    void open();
+
     /// @brief Appends the lease record to the CSV file.
     ///
     /// This function doesn't throw exceptions itself. In theory, exceptions
@@ -56,7 +64,7 @@ public:
     /// error.
     ///
     /// @param lease Structure representing a DHCPv4 lease.
-    void append(const Lease4& lease) const;
+    void append(const Lease4& lease);
 
     /// @brief Reads next lease from the CSV file.
     ///

+ 34 - 3
src/lib/dhcpsrv/csv_lease_file6.cc

@@ -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
 // purpose with or without fee is hereby granted, provided that the above
@@ -27,7 +27,19 @@ CSVLeaseFile6::CSVLeaseFile6(const std::string& filename)
 }
 
 void
-CSVLeaseFile6::append(const Lease6& lease) const {
+CSVLeaseFile6::open() {
+    // Call the base class to open the file
+    CSVFile::open();
+
+    // and clear any statistics we may have
+    clearStatistics();
+}
+
+void
+CSVLeaseFile6::append(const Lease6& lease) {
+    // Bump the number of write attempts
+    ++writes_;
+
     CSVRow row(getColumnCount());
     row.writeAt(getColumnIndex("address"), lease.addr_.toText());
     row.writeAt(getColumnIndex("duid"), lease.duid_->toText());
@@ -46,11 +58,23 @@ CSVLeaseFile6::append(const Lease6& lease) const {
         // We may not have hardware information
         row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
     }
-    CSVFile::append(row);
+    try {
+        CSVFile::append(row);
+    } catch (const std::exception& ex) {
+        // Catch any errors so we can bump the error counter than rethrow it
+        ++write_errs_;
+        throw;
+    }
+
+    // Bump the number of leases written
+    ++write_leases_;
 }
 
 bool
 CSVLeaseFile6::next(Lease6Ptr& lease) {
+    // Bump the number of read attempts
+    ++reads_;
+
     // Read the CSV row and try to create a lease from the values read.
     // This may easily result in exception. We don't want this function
     // to throw exceptions, so we catch them all and rather return the
@@ -77,12 +101,19 @@ CSVLeaseFile6::next(Lease6Ptr& lease) {
         lease->hostname_ = readHostname(row);
 
     } catch (std::exception& ex) {
+        // bump the read error count
+        ++read_errs_;
+
         // The lease might have been created, so let's set it back to NULL to
         // signal that lease hasn't been parsed.
         lease.reset();
         setReadMsg(ex.what());
         return (false);
     }
+
+    // bump the number of leases read
+    ++read_leases_;
+
     return (true);
 }
 

+ 11 - 3
src/lib/dhcpsrv/csv_lease_file6.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
 // purpose with or without fee is hereby granted, provided that the above
@@ -19,6 +19,7 @@
 #include <dhcp/duid.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/subnet.h>
+#include <dhcpsrv/lease_stats.h>
 #include <util/csv_file.h>
 #include <stdint.h>
 #include <string>
@@ -37,7 +38,7 @@ namespace dhcp {
 /// validation (see http://kea.isc.org/ticket/2405). However, when #2405
 /// is implemented, the @c next function may need to be updated to use the
 /// validation capablity of @c Lease6.
-class CSVLeaseFile6 : public isc::util::CSVFile {
+  class CSVLeaseFile6 : public isc::util::CSVFile, public LeaseFileStats {
 public:
 
     /// @brief Constructor.
@@ -47,6 +48,13 @@ public:
     /// @param filename Name of the lease file.
     CSVLeaseFile6(const std::string& filename);
 
+    /// @brief Opens a lease file.
+    ///
+    /// This function is used to clear the statistics while
+    /// calling the base class open.  It doesn't throw any
+    /// exceptions of its own but the base class may.
+    void open();
+
     /// @brief Appends the lease record to the CSV file.
     ///
     /// This function doesn't throw exceptions itself. In theory, exceptions
@@ -55,7 +63,7 @@ public:
     /// error.
     ///
     /// @param lease Structure representing a DHCPv6 lease.
-    void append(const Lease6& lease) const;
+    void append(const Lease6& lease);
 
     /// @brief Reads next lease from the CSV file.
     ///

+ 1 - 3
src/lib/dhcpsrv/lease_file_loader.h

@@ -43,8 +43,6 @@ namespace dhcp {
 /// with the @c Lease4Storage and @c Lease6Storage to process the DHCPv4
 /// and DHCPv6 leases respectively.
 ///
-/// @todo Add a method which dumps all leases from the storage to a
-/// specified lease file.
 class LeaseFileLoader {
 public:
 
@@ -184,7 +182,7 @@ public:
     /// @param storage A reference to the container from which leases
     /// should be written.
     ///
-    /// @tparam LeasePtrType A @c Lease4 or @c Lease6.
+    /// @tparam LeaseObjectType A @c Lease4 or @c Lease6.
     /// @tparam LeaseFileType A @c CSVLeaseFile4 or @c CSVLeaseFile6.
     /// @tparam StorageType A @c Lease4Storage or @c Lease6Storage.
     template<typename LeaseObjectType, typename LeaseFileType,

+ 106 - 0
src/lib/dhcpsrv/lease_stats.h

@@ -0,0 +1,106 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LEASE_STATS_H
+#define LEASE_STATS_H
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Provides statistics for leases.
+///
+/// This class provides a common space for statistics that we wish
+/// to keep about leases.  Currently this is foe use with lease files
+/// but it may be exannded in the future.
+
+class LeaseFileStats {
+public:
+    /// @brief Constructor
+    ///
+    /// Initializes the stats variables to zeros
+    LeaseFileStats() {
+        clearStatistics();
+    }
+
+    /// @brief Destructor
+    ~LeaseFileStats() {
+    }
+
+    /// @brief Gets the number of attempts to read a lease
+    uint32_t getReads() {
+        return (reads_);
+    }
+
+    /// @brief Gets the number of leases read
+    uint32_t getReadLeases() {
+        return (read_leases_);
+    }
+
+    /// @brief Gets the number of errors when reading leases
+    uint32_t getReadErrs() {
+        return (read_errs_);
+    }
+
+    /// @brief Gets the number of attempts to write a lease
+    uint32_t getWrites() {
+        return (writes_);
+    }
+
+    /// @brief Gets the number of leases written
+    uint32_t getWriteLeases() {
+        return (write_leases_);
+    }
+
+    /// @brief Gets the number of errors when writting leases
+    uint32_t getWriteErrs() {
+        return (write_errs_);
+    }
+
+    /// @brief Clears the statistics
+    void clearStatistics() {
+        reads_        = 0;
+        read_leases_  = 0;
+        read_errs_    = 0;
+        writes_       = 0;
+        write_leases_ = 0;
+        write_errs_   = 0;
+    }
+
+protected:
+    /// @brief Number of attempts to read a lease (calls to next), reset to 0 on open
+    uint32_t reads_;
+
+    /// @brief Number of leases read, reset to 0 on open
+    uint32_t read_leases_;
+
+    /// @brief Number of errors when reading, reset to 0 on open
+    uint32_t read_errs_;
+
+    /// @brief Number of attempts to write a lease, reset to 0 on open
+    uint32_t writes_;
+
+    /// @brief Number of lease written, reset to 0 on open
+    uint32_t write_leases_;
+
+    /// @brief Number of errors when writing , reset to 0 on open
+    uint32_t write_errs_;
+
+private:
+
+};
+
+} // namespace isc::dhcp
+} // namesapce isc
+
+#endif // LEASE_STATS_H

+ 31 - 2
src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc

@@ -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
 // purpose with or without fee is hereby granted, provided that the above
@@ -57,6 +57,19 @@ public:
     /// @brief Creates the lease file to be parsed by unit tests.
     void writeSampleFile() const;
 
+    /// @brief Checks the stats for the file
+    void checkStats(CSVLeaseFile4& lease_file,
+                    uint32_t reads, uint32_t read_leases,
+                    uint32_t read_errs, uint32_t writes,
+                    uint32_t write_leases, uint32_t write_errs) const {
+        EXPECT_EQ(lease_file.getReads(), reads);
+        EXPECT_EQ(lease_file.getReadLeases(), read_leases);
+        EXPECT_EQ(lease_file.getReadErrs(), read_errs);
+        EXPECT_EQ(lease_file.getWrites(), writes);
+        EXPECT_EQ(lease_file.getWriteLeases(), write_leases);
+        EXPECT_EQ(lease_file.getWriteErrs(), write_errs);
+    }
+
     /// @brief Name of the test lease file.
     std::string filename_;
 
@@ -104,10 +117,14 @@ TEST_F(CSVLeaseFile4Test, parse) {
     boost::scoped_ptr<CSVLeaseFile4> lf(new CSVLeaseFile4(filename_));
     ASSERT_NO_THROW(lf->open());
 
+    // Verify the counters are cleared
+    checkStats(*lf, 0, 0, 0, 0, 0, 0);
+
     Lease4Ptr lease;
     // Reading first read should be successful.
     EXPECT_TRUE(lf->next(lease));
     ASSERT_TRUE(lease);
+    checkStats(*lf, 1, 1, 0, 0, 0, 0);
 
     // Verify that the lease attributes are correct.
     EXPECT_EQ("192.0.2.1", lease->addr_.toText());
@@ -123,11 +140,14 @@ TEST_F(CSVLeaseFile4Test, parse) {
 
     // Second lease is malformed - HW address is empty.
     EXPECT_FALSE(lf->next(lease));
+    checkStats(*lf, 2, 1, 1, 0, 0, 0);
 
     // Even though parsing previous lease failed, reading the next lease should be
     // successful.
     EXPECT_TRUE(lf->next(lease));
     ASSERT_TRUE(lease);
+    checkStats(*lf, 3, 2, 1, 0, 0, 0);
+
     // Verify that the third lease is correct.
     EXPECT_EQ("192.0.3.15", lease->addr_.toText());
     HWAddr hwaddr3(*lease->hwaddr_);
@@ -145,11 +165,12 @@ TEST_F(CSVLeaseFile4Test, parse) {
     // lease pointer should be NULL.
     EXPECT_TRUE(lf->next(lease));
     EXPECT_FALSE(lease);
+    checkStats(*lf, 4, 2, 1, 0, 0, 0);
 
     // We should be able to do it again.
     EXPECT_TRUE(lf->next(lease));
     EXPECT_FALSE(lease);
-
+    checkStats(*lf, 5, 2, 1, 0, 0, 0);
 }
 
 // This test checks creation of the lease file and writing leases.
@@ -157,6 +178,10 @@ TEST_F(CSVLeaseFile4Test, recreate) {
     boost::scoped_ptr<CSVLeaseFile4> lf(new CSVLeaseFile4(filename_));
     ASSERT_NO_THROW(lf->recreate());
     ASSERT_TRUE(io_.exists());
+
+    // Verify the counters are cleared
+    checkStats(*lf, 0, 0, 0, 0, 0, 0);
+
     // Create first lease, with NULL client id.
     Lease4Ptr lease(new Lease4(IOAddress("192.0.3.2"),
                                hwaddr0_,
@@ -164,12 +189,16 @@ TEST_F(CSVLeaseFile4Test, recreate) {
                                200, 50, 80, 0, 8, true, true,
                                "host.example.com"));
     ASSERT_NO_THROW(lf->append(*lease));
+    checkStats(*lf, 0, 0, 0, 1, 1, 0);
+
     // Create second lease, with non-NULL client id.
     lease.reset(new Lease4(IOAddress("192.0.3.10"),
                            hwaddr1_,
                            CLIENTID0, sizeof(CLIENTID0),
                            100, 60, 90, 0, 7));
     ASSERT_NO_THROW(lf->append(*lease));
+    checkStats(*lf, 0, 0, 0, 2, 2, 0);
+
     // Close the lease file.
     lf->close();
     // Check that the contents of the csv file are correct.

+ 31 - 2
src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc

@@ -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
 // purpose with or without fee is hereby granted, provided that the above
@@ -63,6 +63,19 @@ public:
     /// @brief Create lease file that can be parsed by unit tests.
     void writeSampleFile() const;
 
+    /// @brief Checks the stats for the file
+    void checkStats(CSVLeaseFile6& lease_file,
+                    uint32_t reads, uint32_t read_leases,
+                    uint32_t read_errs, uint32_t writes,
+                    uint32_t write_leases, uint32_t write_errs) const {
+        EXPECT_EQ(lease_file.getReads(), reads);
+        EXPECT_EQ(lease_file.getReadLeases(), read_leases);
+        EXPECT_EQ(lease_file.getReadErrs(), read_errs);
+        EXPECT_EQ(lease_file.getWrites(), writes);
+        EXPECT_EQ(lease_file.getWriteLeases(), write_leases);
+        EXPECT_EQ(lease_file.getWriteErrs(), write_errs);
+    }
+
     /// @brief Name of the test lease file.
     std::string filename_;
 
@@ -105,10 +118,14 @@ TEST_F(CSVLeaseFile6Test, parse) {
     boost::scoped_ptr<CSVLeaseFile6> lf(new CSVLeaseFile6(filename_));
     ASSERT_NO_THROW(lf->open());
 
+    // Verify the counters are cleared
+    checkStats(*lf, 0, 0, 0, 0, 0, 0);
+
     Lease6Ptr lease;
     // Reading first read should be successful.
     EXPECT_TRUE(lf->next(lease));
     ASSERT_TRUE(lease);
+    checkStats(*lf, 1, 1, 0, 0, 0, 0);
 
     // Verify that the lease attributes are correct.
     EXPECT_EQ("2001:db8:1::1", lease->addr_.toText());
@@ -127,11 +144,14 @@ TEST_F(CSVLeaseFile6Test, parse) {
 
     // Second lease is malformed - DUID is empty.
     EXPECT_FALSE(lf->next(lease));
+    checkStats(*lf, 2, 1, 1, 0, 0, 0);
 
     // Even, parsing previous lease failed, reading the next lease should be
     // successful.
     EXPECT_TRUE(lf->next(lease));
     ASSERT_TRUE(lease);
+    checkStats(*lf, 3, 2, 1, 0, 0, 0);
+
     // Verify that the third lease is correct.
     EXPECT_EQ("2001:db8:2::10", lease->addr_.toText());
     ASSERT_TRUE(lease->duid_);
@@ -150,6 +170,8 @@ TEST_F(CSVLeaseFile6Test, parse) {
     // Reading the fourth lease should be successful.
     EXPECT_TRUE(lf->next(lease));
     ASSERT_TRUE(lease);
+    checkStats(*lf, 4, 3, 1, 0, 0, 0);
+
     // Verify that the lease is correct.
     EXPECT_EQ("3000:1::", lease->addr_.toText());
     ASSERT_TRUE(lease->duid_);
@@ -169,11 +191,12 @@ TEST_F(CSVLeaseFile6Test, parse) {
     // lease pointer should be NULL.
     EXPECT_TRUE(lf->next(lease));
     EXPECT_FALSE(lease);
+    checkStats(*lf, 5, 3, 1, 0, 0, 0);
 
     // We should be able to do it again.
     EXPECT_TRUE(lf->next(lease));
     EXPECT_FALSE(lease);
-
+    checkStats(*lf, 6, 3, 1, 0, 0, 0);
 }
 
 // This test checks creation of the lease file and writing leases.
@@ -182,12 +205,16 @@ TEST_F(CSVLeaseFile6Test, recreate) {
     ASSERT_NO_THROW(lf->recreate());
     ASSERT_TRUE(io_.exists());
 
+    // Verify the counters are cleared
+    checkStats(*lf, 0, 0, 0, 0, 0, 0);
+
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
                                makeDUID(DUID0, sizeof(DUID0)),
                                7, 100, 200, 50, 80, 8, true, true,
                                "host.example.com"));
     lease->cltt_ = 0;
     ASSERT_NO_THROW(lf->append(*lease));
+    checkStats(*lf, 0, 0, 0, 1, 1, 0);
 
     lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:2::10"),
                            makeDUID(DUID1, sizeof(DUID1)),
@@ -195,6 +222,7 @@ TEST_F(CSVLeaseFile6Test, recreate) {
                            "", HWAddrPtr(), 128));
     lease->cltt_ = 0;
     ASSERT_NO_THROW(lf->append(*lease));
+    checkStats(*lf, 0, 0, 0, 2, 2, 0);
 
     lease.reset(new Lease6(Lease::TYPE_PD, IOAddress("3000:1:1::"),
                            makeDUID(DUID0, sizeof(DUID0)),
@@ -202,6 +230,7 @@ TEST_F(CSVLeaseFile6Test, recreate) {
                            "", HWAddrPtr(), 64));
     lease->cltt_ = 0;
     ASSERT_NO_THROW(lf->append(*lease));
+    checkStats(*lf, 0, 0, 0, 3, 3, 0);
 
     EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
               "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"

+ 175 - 104
src/lib/dhcpsrv/tests/lease_file_loader_unittest.cc

@@ -86,15 +86,17 @@ public:
     /// the write was correct.  The order of the leases in the output will depend
     /// on the order in which the container provides the leases.
     ///
+    /// @param lease_file A reference to the file to write to
     /// @param storage A reference to the container to be written to the file
     /// @param compare The string to compare to what was read from the file
     ///
-    /// @tparam LeaseStorage Type of the container: @c Lease4Container
-    /// @c Lease6Container.
+    /// @tparam LeaseObjectType A @c Lease4 or @c Lease6.
+    /// @tparam LeaseFileType A @c CSVLeaseFile4 or @c CSVLeaseFile6.
+    /// @tparam StorageType A @c Lease4Storage or @c Lease6Storage.
     ///
     template<typename LeaseObjectType, typename LeaseFileType,
              typename StorageType>
-    void writeLeases(LeaseFileType lease_file,
+    void writeLeases(LeaseFileType& lease_file,
                      const StorageType& storage,
                      const std::string& compare) {
         // Prepare for a new file, close and remove the old
@@ -109,12 +111,48 @@ public:
         EXPECT_EQ(compare, io_.readFile());
     }
 
+    /// @brief Checks the stats for the file
+    ///
+    /// This method is passed a leasefile and the values for the statistics it
+    /// should have for comparison.
+    ///
+    /// @param lease_file A reference to the file we are using
+    /// @param the statistics, in order, reads attempted, leases read, errors
+    /// while reading, writes attempted, leases written, and errors while writing
+    ///
+    /// @tparam LeaseFileType A @c CSVLeaseFile4 or @c CSVLeaseFile6.
+    template<typename LeaseFileType>
+    void checkStats(LeaseFileType& lease_file,
+                    uint32_t reads, uint32_t read_leases,
+                    uint32_t read_errs, uint32_t writes,
+                    uint32_t write_leases, uint32_t write_errs) const {
+        EXPECT_EQ(lease_file.getReads(), reads);
+        EXPECT_EQ(lease_file.getReadLeases(), read_leases);
+        EXPECT_EQ(lease_file.getReadErrs(), read_errs);
+        EXPECT_EQ(lease_file.getWrites(), writes);
+        EXPECT_EQ(lease_file.getWriteLeases(), write_leases);
+        EXPECT_EQ(lease_file.getWriteErrs(), write_errs);
+    }
 
     /// @brief Name of the test lease file.
     std::string filename_;
 
     /// @brief Object providing access to lease file IO.
     LeaseFileIO io_;
+
+    std::string v4_hdr_; ///< String for the header of the v4 csv test file
+    std::string v6_hdr_; ///< String for the header of the v6 csv test file
+
+protected:
+    /// @brief Sets up the header strings
+    virtual void SetUp() {
+        v4_hdr_ = "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
+                  "fqdn_fwd,fqdn_rev,hostname\n";
+
+        v6_hdr_ = "address,duid,valid_lifetime,expire,subnet_id,"
+                  "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
+                  "fqdn_rev,hostname,hwaddr\n";
+    }
 };
 
 LeaseFileLoaderTest::LeaseFileLoaderTest()
@@ -135,20 +173,25 @@ LeaseFileLoaderTest::absolutePath(const std::string& filename) {
 // It also tests the write function by writing the storage to a file
 // and comparing that with the expected value.
 TEST_F(LeaseFileLoaderTest, loadWrite4) {
+    std::string test_str;
+    std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
+                      "200,200,8,1,1,host.example.com\n";
+    std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
+                      "200,500,8,1,1,host.example.com\n";
+
+    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";
+    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";
+
+    std::string c_1 = "192.0.2.3,,a:11:01:04,"
+                      "200,200,8,1,1,host.example.com\n";
+
     // 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
     // be discarded.
-    io_.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,"
-                  "host.example.com\n"
-                  "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,"
-                  "0,0,\n"
-                  "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n"
-                  "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,135,7,"
-                  "0,0,\n"
-                  "192.0.2.1,06:07:08:09:0a:bc,,200,500,8,1,1,"
-                  "host.example.com\n");
+    test_str = v4_hdr_ + a_1 + b_1 + c_1 + b_2 + a_2;
+    io_.writeFile(test_str);
 
     boost::scoped_ptr<CSVLeaseFile4> lf(new CSVLeaseFile4(filename_));
     ASSERT_NO_THROW(lf->open());
@@ -157,6 +200,9 @@ TEST_F(LeaseFileLoaderTest, loadWrite4) {
     Lease4Storage storage;
     ASSERT_NO_THROW(LeaseFileLoader::load<Lease4>(*lf, storage, 10));
 
+    // We should have made 6 attempts to read, with 4 leases read and 1 error
+    checkStats(*lf, 6, 4, 1, 0, 0, 0);
+
     // There are two unique leases.
     ASSERT_EQ(2, storage.size());
 
@@ -178,14 +224,11 @@ TEST_F(LeaseFileLoaderTest, loadWrite4) {
     ASSERT_TRUE(lease);
     EXPECT_EQ(35, lease->cltt_);
 
-    writeLeases<Lease4, CSVLeaseFile4, Lease4Storage>
-        (*lf, storage,
-         "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-         "fqdn_fwd,fqdn_rev,hostname\n"
-         "192.0.2.1,06:07:08:09:0a:bc,,200,500,8,1,1,"
-         "host.example.com\n"
-         "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,135,7,"
-         "0,0,\n");
+    test_str = v4_hdr_ + a_2 + b_2;
+    writeLeases<Lease4, CSVLeaseFile4, Lease4Storage>(*lf, storage, test_str);
+
+    // We should have made 2 attempts to write, with 2 leases written and 0 errors
+    checkStats(*lf, 0, 0, 0, 2, 2, 0);
 }
 
 // This test verifies that the lease with a valid lifetime of 0
@@ -195,19 +238,23 @@ TEST_F(LeaseFileLoaderTest, loadWrite4) {
 // It also tests the write function by writing the storage to a file
 // and comparing that with the expected value.
 TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) {
+    std::string test_str;
+    std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
+                      "200,200,8,1,1,host.example.com\n";
+    std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
+                      "0,500,8,1,1,host.example.com\n";
+
+    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";
+    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";
+
+
     // Create lease file in which one of the entries for 192.0.2.1
     // has a valid_lifetime of 0 and results in the deletion of the
     // lease.
-    io_.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,"
-                  "host.example.com\n"
-                  "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,"
-                  "0,0,\n"
-                  "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,135,7,"
-                  "0,0,\n"
-                  "192.0.2.1,06:07:08:09:0a:bc,,0,500,8,1,1,"
-                  "host.example.com\n");
+    test_str = v4_hdr_ + a_1 + b_1 + b_2 + a_2;
+    io_.writeFile(test_str);
 
     boost::scoped_ptr<CSVLeaseFile4> lf(new CSVLeaseFile4(filename_));
     ASSERT_NO_THROW(lf->open());
@@ -215,6 +262,9 @@ TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) {
     Lease4Storage storage;
     ASSERT_NO_THROW(LeaseFileLoader::load<Lease4>(*lf, storage, 10));
 
+    // We should have made 5 attempts to read, with 4 leases read and 0 error
+    checkStats(*lf, 5, 4, 0, 0, 0, 0);
+
     // There should only be one lease. The one with the valid_lifetime
     // of 0 should be removed.
     ASSERT_EQ(1, storage.size());
@@ -223,12 +273,11 @@ TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) {
     ASSERT_TRUE(lease);
     EXPECT_EQ(35, lease->cltt_);
 
-    writeLeases<Lease4, CSVLeaseFile4, Lease4Storage>
-        (*lf, storage,
-         "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-         "fqdn_fwd,fqdn_rev,hostname\n"
-         "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,135,7,"
-         "0,0,\n");
+    test_str = v4_hdr_ + b_2;
+    writeLeases<Lease4, CSVLeaseFile4, Lease4Storage>(*lf, storage, test_str);
+
+    // We should have made 1 attempts to write, with 1 leases written and 0 errors
+    checkStats(*lf, 0, 0, 0, 1, 1, 0);
 }
 
 // This test verifies that the DHCPv6 leases can be loaded from the lease
@@ -238,22 +287,28 @@ TEST_F(LeaseFileLoaderTest, loadWrite4LeaseRemove) {
 // It also tests the write function by writing the storage to a file
 // and comparing that with the expected value.
 TEST_F(LeaseFileLoaderTest, loadWrite6) {
+    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,"
+                      "200,200,8,100,0,7,0,1,1,host.example.com,\n";
+    std::string a_2 = "2001:db8:1::1,,"
+                      "200,200,8,100,0,7,0,1,1,host.example.com,\n";
+    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";
+
+    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";
+    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";
+
+    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";
+
+
+
     // Create a lease file with three valid leases: 2001:db8:1::1,
     // 3000:1:: and 2001:db8:2::10.
-    io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
-                  "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-                  "fqdn_rev,hostname,hwaddr\n"
-                  "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"
-                  "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
-                  "0,8,0,0,0,,\n"
-                  "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"
-                  "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,800,6,150,"
-                  "0,8,0,0,0,,\n"
-                  "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");
+    test_str = v6_hdr_ + a_1 + a_2 + b_1 + c_1 + b_2 + a_3;
+    io_.writeFile(test_str);
 
     boost::scoped_ptr<CSVLeaseFile6> lf(new CSVLeaseFile6(filename_));
     ASSERT_NO_THROW(lf->open());
@@ -262,6 +317,9 @@ TEST_F(LeaseFileLoaderTest, loadWrite6) {
     Lease6Storage storage;
     ASSERT_NO_THROW(LeaseFileLoader::load<Lease6>(*lf, storage, 10));
 
+    // We should have made 7 attempts to read, with 5 leases read and 1 error
+    checkStats(*lf, 7, 5, 1, 0, 0, 0);
+
     // There should be 3 unique leases.
     ASSERT_EQ(3, storage.size());
 
@@ -283,17 +341,11 @@ TEST_F(LeaseFileLoaderTest, loadWrite6) {
     ASSERT_TRUE(lease);
     EXPECT_EQ(500, lease->cltt_);
 
-    writeLeases<Lease6, CSVLeaseFile6, Lease6Storage>
-        (*lf, storage,
-         "address,duid,valid_lifetime,expire,subnet_id,"
-         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-         "fqdn_rev,hostname,hwaddr\n"
-         "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"
-         "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,800,6,150,"
-         "0,8,0,0,0,,\n"
-         "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");
+    test_str = v6_hdr_ + a_3 + b_2 + c_1;
+    writeLeases<Lease6, CSVLeaseFile6, Lease6Storage>(*lf, storage, test_str);
+
+    // We should have made 3 attempts to write, with 3 leases written and 0 errors
+    checkStats(*lf, 0, 0, 0, 3, 3, 0);
 }
 
 // This test verifies that the lease with a valid lifetime of 0
@@ -303,20 +355,22 @@ TEST_F(LeaseFileLoaderTest, loadWrite6) {
 // It also tests the write function by writing the storage to a file
 // and comparing that with the expected value.
 TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) {
+    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,"
+                      "200,200,8,100,0,7,0,1,1,host.example.com,\n";
+    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";
+
+    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";
+    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";
+
     // 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
     // deleted.
-    io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
-                  "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-                  "fqdn_rev,hostname,hwaddr\n"
-                  "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:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
-                  "0,8,0,0,0,,\n"
-                  "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,800,6,150,"
-                  "0,8,0,0,0,,\n"
-                  "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");
+    test_str = v6_hdr_ + a_1 + b_1 + b_2 + a_2;
+    io_.writeFile(test_str);
 
     boost::scoped_ptr<CSVLeaseFile6> lf(new CSVLeaseFile6(filename_));
     ASSERT_NO_THROW(lf->open());
@@ -325,6 +379,9 @@ TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) {
     Lease6Storage storage;
     ASSERT_NO_THROW(LeaseFileLoader::load<Lease6>(*lf, storage, 10));
 
+    // We should have made 5 attempts to read, with 4 leases read and 0 error
+    checkStats(*lf, 5, 4, 0, 0, 0, 0);
+
     // There should be only one lease for 2001:db8:2::10. The other one
     // should have been deleted (or rather not loaded).
     ASSERT_EQ(1, storage.size());
@@ -333,32 +390,31 @@ TEST_F(LeaseFileLoaderTest, loadWrite6LeaseRemove) {
     ASSERT_TRUE(lease);
     EXPECT_EQ(500, lease->cltt_);
 
-    writeLeases<Lease6, CSVLeaseFile6, Lease6Storage>
-        (*lf, storage,
-         "address,duid,valid_lifetime,expire,subnet_id,"
-         "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-         "fqdn_rev,hostname,hwaddr\n"
-         "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,800,6,150,"
-         "0,8,0,0,0,,\n");
+    test_str = v6_hdr_ + b_2;
+    writeLeases<Lease6, CSVLeaseFile6, Lease6Storage>(*lf, storage, test_str);
+
+    // We should have made 1 attempts to write, with 1 leases written and 0 errors
+    checkStats(*lf, 0, 0, 0, 1, 1, 0);
 }
 
 // This test verifies that the exception is thrown when the specific
 // number of errors in the test data occur during reading of the lease
 // file.
 TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
+    std::string test_str;
+    std::string a_1 = "192.0.2.1,06:07:08:09:0a:bc,,"
+                      "200,200,8,1,1,host.example.com\n";
+    std::string a_2 = "192.0.2.1,06:07:08:09:0a:bc,,"
+                      "200,500,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\n";
+
+    std::string c_1 = "192.0.2.10,01:02:03:04:05:06,,200,300,8,1,1,\n";
+
     // Create a lease file for which there is a number of invalid
-    // entries.
-    io_.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,"
-                  "host.example.com\n"
-                  "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n"
-                  "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n"
-                  "192.0.2.10,01:02:03:04:05:06,,200,300,8,1,1,,\n"
-                  "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n"
-                  "192.0.2.3,,a:11:01:04,200,200,8,1,1,host.example.com\n"
-                  "192.0.2.1,06:07:08:09:0a:bc,,200,500,8,1,1,"
-                  "host.example.com\n");
+    // entries.  b_1 is invalid and gets used multiple times.
+    test_str = v4_hdr_ + a_1 + b_1 + b_1 + c_1 + b_1 + b_1 + a_2;
+    io_.writeFile(test_str);
 
     boost::scoped_ptr<CSVLeaseFile4> lf(new CSVLeaseFile4(filename_));
     ASSERT_NO_THROW(lf->open());
@@ -369,6 +425,9 @@ TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
     ASSERT_THROW(LeaseFileLoader::load<Lease4>(*lf, storage, 3),
                  util::CSVFileError);
 
+    // We should have made 6 attempts to read, with 2 leases read and 4 error
+    checkStats(*lf, 6, 2, 4, 0, 0, 0);
+
     lf->close();
     ASSERT_NO_THROW(lf->open());
 
@@ -377,6 +436,9 @@ TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
     storage.clear();
     ASSERT_NO_THROW(LeaseFileLoader::load<Lease4>(*lf, storage, 4));
 
+    // We should have made 8 attempts to read, with 3 leases read and 4 error
+    checkStats(*lf, 8, 3, 4, 0, 0, 0);
+
     ASSERT_EQ(2, storage.size());
 
     Lease4Ptr lease = getLease<Lease4Ptr>("192.0.2.1", storage);
@@ -386,6 +448,12 @@ TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
     lease = getLease<Lease4Ptr>("192.0.2.10", storage);
     ASSERT_TRUE(lease);
     EXPECT_EQ(100, lease->cltt_);
+
+    test_str = v4_hdr_ + a_2 + c_1;
+    writeLeases<Lease4, CSVLeaseFile4, Lease4Storage>(*lf, storage, test_str);
+
+    // We should have made 1 attempts to write, with 1 leases written and 0 errors
+    checkStats(*lf, 0, 0, 0, 2, 2, 0);
 }
 
 // This test verifies that the lease with a valid lifetime set to 0 is
@@ -395,11 +463,13 @@ TEST_F(LeaseFileLoaderTest, loadMaxErrors) {
 // It also tests the write function by writing the storage to a file
 // and comparing that with the expected value.
 TEST_F(LeaseFileLoaderTest, loadWriteLeaseWithZeroLifetime) {
+    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";
+
     // Create lease file. The second lease has a valid lifetime of 0.
-    io_.writeFile("address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-                  "fqdn_fwd,fqdn_rev,hostname\n"
-                  "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,,\n"
-                  "192.0.2.3,06:07:08:09:0a:bd,,0,200,8,1,1,,\n");
+    test_str = v4_hdr_ + a_1 + b_2;
+    io_.writeFile(test_str);
 
     boost::scoped_ptr<CSVLeaseFile4> lf(new CSVLeaseFile4(filename_));
     ASSERT_NO_THROW(lf->open());
@@ -409,6 +479,9 @@ TEST_F(LeaseFileLoaderTest, loadWriteLeaseWithZeroLifetime) {
     Lease4Storage storage;
     ASSERT_NO_THROW(LeaseFileLoader::load<Lease4>(*lf, storage, 0));
 
+    // We should have made 3 attempts to read, with 2 leases read and 0 error
+    checkStats(*lf, 3, 2, 0, 0, 0, 0);
+
     // The first lease should be present.
     Lease4Ptr lease = getLease<Lease4Ptr>("192.0.2.1", storage);
     ASSERT_TRUE(lease);
@@ -417,12 +490,10 @@ TEST_F(LeaseFileLoaderTest, loadWriteLeaseWithZeroLifetime) {
     // The lease with a valid lifetime of 0 should not be loaded.
     EXPECT_FALSE(getLease<Lease4Ptr>("192.0.2.3", storage));
 
-    writeLeases<Lease4, CSVLeaseFile4, Lease4Storage>
-        (*lf, storage,
-         "address,hwaddr,client_id,valid_lifetime,expire,subnet_id,"
-         "fqdn_fwd,fqdn_rev,hostname\n"
-         "192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,\n");
-}
-
+    test_str = v4_hdr_ + a_1;
+    writeLeases<Lease4, CSVLeaseFile4, Lease4Storage>(*lf, storage, test_str);
 
+    // We should have made 1 attempts to write, with 1 leases written and 0 errors
+    checkStats(*lf, 0, 0, 0, 1, 1, 0);
+}
 } // end of anonymous namespace