summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc')
-rw-r--r--src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc724
1 files changed, 724 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc b/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc
new file mode 100644
index 0000000..35aea7f
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc
@@ -0,0 +1,724 @@
+// Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
+#include <dhcpsrv/csv_lease_file6.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/testutils/lease_file_io.h>
+#include <gtest/gtest.h>
+#include <ctime>
+#include <sstream>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::util;
+
+namespace {
+
+// DUID values used by unit tests.
+const uint8_t DUID0[] = { 0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
+const uint8_t DUID1[] = { 1, 1, 1, 1, 0xa, 1, 2, 3, 4, 5 };
+
+/// @brief Test fixture class for @c CSVLeaseFile6 validation.
+class CSVLeaseFile6Test : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Initializes IO for lease file used by unit tests.
+ CSVLeaseFile6Test();
+
+ /// @brief Prepends the absolute path to the file specified
+ /// as an argument.
+ ///
+ /// @param filename Name of the file.
+ /// @return Absolute path to the test file.
+ static std::string absolutePath(const std::string& filename);
+
+ /// @brief Create DUID object from the binary.
+ ///
+ /// @param duid Binary value representing a DUID.
+ /// @param size Size of the DUID.
+ /// @return Pointer to the @c DUID object.
+ DuidPtr makeDUID(const uint8_t* duid, const unsigned int size) const {
+ return (DuidPtr(new DUID(duid, size)));
+ }
+
+ /// @brief Create lease file that can be parsed by unit tests.
+ void writeSampleFile() const;
+
+ /// @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 reads the number of attempted reads
+ /// @param read_leases the number of valid leases read
+ /// @param read_errs the number of errors while reading leases
+ /// @param writes the number of attempted writes
+ /// @param write_leases the number of leases successfully written
+ /// @param write_errs the number of errors while writing
+ 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(reads, lease_file.getReads());
+ EXPECT_EQ(read_leases, lease_file.getReadLeases());
+ EXPECT_EQ(read_errs, lease_file.getReadErrs());
+ EXPECT_EQ(writes, lease_file.getWrites());
+ EXPECT_EQ(write_leases, lease_file.getWriteLeases());
+ EXPECT_EQ(write_errs, lease_file.getWriteErrs());
+ }
+
+ /// @brief Name of the test lease file.
+ std::string filename_;
+
+ /// @brief Object providing access to lease file IO.
+ LeaseFileIO io_;
+
+};
+
+CSVLeaseFile6Test::CSVLeaseFile6Test()
+ : filename_(absolutePath("leases6.csv")), io_(filename_) {
+}
+
+std::string
+CSVLeaseFile6Test::absolutePath(const std::string& filename) {
+ std::ostringstream s;
+ s << DHCP_DATA_DIR << "/" << filename;
+ return (s.str());
+}
+
+void
+CSVLeaseFile6Test::writeSampleFile() const {
+ io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
+ "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
+ "fqdn_rev,hostname,hwaddr,state,user_context,"
+ "hwtype,hwaddr_source\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,,1,,"
+ "1,0\n"
+ "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com,,1,,"
+ "1,0\n"
+ "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
+ "0,8,0,0,0,,,1,,"
+ "1,0\n"
+ "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,0,200,8,0,2,"
+ "16,64,0,0,,,1,{ \"foobar\": true },,"
+ "1,0\n"
+ "2001:db8:1::2,00,200,200,8,100,0,7,0,1,1,host.example.com,,0,,"
+ "1,0\n"
+ "2001:db8:1::3,00,200,200,8,100,0,7,0,1,1,host.example.com,,1,,"
+ "1,0\n");
+}
+
+// This test checks the capability to read and parse leases from the file.
+TEST_F(CSVLeaseFile6Test, parse) {
+ // Create a file to be parsed.
+ writeSampleFile();
+
+ // Open the lease file.
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.open());
+
+ // Verify the counters are cleared
+ {
+ SCOPED_TRACE("Check stats are empty");
+ checkStats(lf, 0, 0, 0, 0, 0, 0);
+ }
+
+ Lease6Ptr lease;
+ // Reading first read should be successful.
+ {
+ SCOPED_TRACE("First lease valid");
+ 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());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f", lease->duid_->toText());
+ EXPECT_EQ(200, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_EQ(100, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(7, lease->iaid_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("host.example.com", lease->hostname_);
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ EXPECT_FALSE(lease->getContext());
+ }
+
+ // Second lease is malformed - DUID is blank (i.e. ",,")
+ {
+ SCOPED_TRACE("Second lease malformed");
+ 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.
+ {
+ SCOPED_TRACE("Third lease valid");
+ 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_);
+ EXPECT_EQ("01:01:01:01:0a:01:02:03:04:05", lease->duid_->toText());
+ EXPECT_EQ(300, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(6, lease->subnet_id_);
+ EXPECT_EQ(150, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(8, lease->iaid_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_FALSE(lease->fqdn_fwd_);
+ EXPECT_FALSE(lease->fqdn_rev_);
+ EXPECT_TRUE(lease->hostname_.empty());
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ EXPECT_FALSE(lease->getContext());
+ }
+
+ // Reading the fourth lease should be successful.
+ {
+ SCOPED_TRACE("Fourth lease valid");
+ 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_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f", lease->duid_->toText());
+ EXPECT_EQ(0, lease->valid_lft_);
+ EXPECT_EQ(200, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_EQ(0, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_PD, lease->type_);
+ EXPECT_EQ(16, lease->iaid_);
+ EXPECT_EQ(64, lease->prefixlen_);
+ EXPECT_FALSE(lease->fqdn_fwd_);
+ EXPECT_FALSE(lease->fqdn_rev_);
+ EXPECT_TRUE(lease->hostname_.empty());
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ ASSERT_TRUE(lease->getContext());
+ EXPECT_EQ("{ \"foobar\": true }", lease->getContext()->str());
+ }
+
+
+ // Fifth lease is invalid - DUID is empty, state is not DECLINED
+ {
+ SCOPED_TRACE("Fifth lease invalid");
+ EXPECT_FALSE(lf.next(lease));
+ checkStats(lf, 5, 3, 2, 0, 0, 0);
+ }
+
+ // Reading the sixth lease should be successful.
+ {
+ SCOPED_TRACE("sixth lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+ checkStats(lf, 6, 4, 2, 0, 0, 0);
+
+ // Verify that the lease is correct.
+ EXPECT_EQ("2001:db8:1::3", lease->addr_.toText());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00", lease->duid_->toText());
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ }
+
+ // There are no more leases. Reading should cause no error, but the returned
+ // lease pointer should be NULL.
+ {
+ SCOPED_TRACE("Sixth read empty");
+ EXPECT_TRUE(lf.next(lease));
+ EXPECT_FALSE(lease);
+ checkStats(lf, 7, 4, 2, 0, 0, 0);
+ }
+
+ // We should be able to do it again.
+ {
+ SCOPED_TRACE("Seventh read empty");
+ EXPECT_TRUE(lf.next(lease));
+ EXPECT_FALSE(lease);
+ checkStats(lf, 8, 4, 2, 0, 0, 0);
+ }
+}
+
+// This test checks creation of the lease file and writing leases.
+TEST_F(CSVLeaseFile6Test, recreate) {
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.recreate());
+ ASSERT_TRUE(io_.exists());
+
+ // Verify the counters are cleared
+ {
+ SCOPED_TRACE("Check stats are empty");
+ 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, 8, true, true,
+ "host.example.com"));
+ lease->cltt_ = 0;
+ {
+ SCOPED_TRACE("First write");
+ 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)),
+ 8, 150, 300, 6, false, false,
+ "", HWAddrPtr(), 128));
+ lease->cltt_ = 0;
+ {
+ SCOPED_TRACE("Second write");
+ 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)),
+ 7, 150, 300, 10, false, false,
+ "", HWAddrPtr(), 64));
+ lease->cltt_ = 0;
+ lease->setContext(Element::fromJSON("{ \"foobar\": true }"));
+ {
+ SCOPED_TRACE("Third write");
+ ASSERT_NO_THROW(lf.append(*lease));
+ checkStats(lf, 0, 0, 0, 3, 3, 0);
+ }
+
+ DuidPtr empty(new DUID(DUID::EMPTY()));
+ lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:2::10"),
+ empty, 8, 150, 300, 6, false, false,
+ "", HWAddrPtr(), 128));
+ lease->cltt_ = 0;
+ {
+ SCOPED_TRACE("Fourth write - invalid, no DUID, not declined");
+ ASSERT_THROW(lf.append(*lease), BadValue);
+ checkStats(lf, 0, 0, 0, 4, 3, 1);
+ }
+
+ {
+ SCOPED_TRACE("Fifth write - valid, no DUID, declined");
+ lease->state_ = Lease::STATE_DECLINED;
+ ASSERT_NO_THROW(lf.append(*lease));
+ checkStats(lf, 0, 0, 0, 5, 4, 1);
+ }
+
+
+ EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
+ "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
+ "state,user_context,hwtype,hwaddr_source\n"
+ "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
+ "200,200,8,100,0,7,128,1,1,host.example.com,,0,,,\n"
+ "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05"
+ ",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,"
+ "300,300,10,150,2,7,64,0,0,,,0,{ \"foobar\": true },,\n"
+ "2001:db8:2::10,00,300,300,6,150,0,8,128,0,0,,,1,,,\n",
+ io_.readFile());
+}
+
+// Verifies that a 1.0 schema file with records from
+// schema 1.0, 2.0, and 3.0 loads correctly.
+TEST_F(CSVLeaseFile6Test, mixedSchemaLoad) {
+ // Create a mixed schema file
+ io_.writeFile(
+ // schema 1.0 header
+ "address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
+ "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname\n"
+ // schema 1.0 record
+ "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:01,"
+ "200,200,8,100,0,7,0,1,1,one.example.com\n"
+
+ // schema 2.0 record - has hwaddr
+ "2001:db8:1::2,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:02,"
+ "200,200,8,100,0,7,0,1,1,two.example.com,01:02:03:04:05\n"
+
+ // schema 3.0 record - has hwaddr and state
+ "2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:03,"
+ "200,200,8,100,0,7,0,1,1,three.example.com,0a:0b:0c:0d:0e,1\n"
+
+ // schema 3.1 record - has hwaddr, state and user context
+ "2001:db8:1::4,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:03,"
+ "200,200,8,100,0,7,0,1,1,three.example.com,0a:0b:0c:0d:0e,1,"
+ "{ \"foobar\": true }\n");
+
+ // Open the lease file.
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.open());
+
+ Lease6Ptr lease;
+ {
+ SCOPED_TRACE("First lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+
+ // Verify that the lease attributes are correct.
+ EXPECT_EQ("2001:db8:1::1", lease->addr_.toText());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:01", lease->duid_->toText());
+ EXPECT_EQ(200, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_EQ(100, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(7, lease->iaid_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("one.example.com", lease->hostname_);
+ // Verify that added HWaddr is empty
+ EXPECT_FALSE(lease->hwaddr_);
+ // Verify that added state is STATE_DEFAULT
+ EXPECT_EQ(Lease::STATE_DEFAULT, lease->state_);
+ EXPECT_FALSE(lease->getContext());
+ }
+
+ {
+ SCOPED_TRACE("Second lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+
+ // Verify that the lease attributes are correct.
+ EXPECT_EQ("2001:db8:1::2", lease->addr_.toText());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:02", lease->duid_->toText());
+ EXPECT_EQ(200, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_EQ(100, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(7, lease->iaid_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("two.example.com", lease->hostname_);
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ("01:02:03:04:05", lease->hwaddr_->toText(false));
+ // Verify that added state is STATE_DEFAULT
+ EXPECT_EQ(Lease::STATE_DEFAULT, lease->state_);
+ EXPECT_FALSE(lease->getContext());
+ }
+
+ {
+ SCOPED_TRACE("Third lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+
+ // Verify that the lease attributes are correct.
+ EXPECT_EQ("2001:db8:1::3", lease->addr_.toText());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:03", lease->duid_->toText());
+ EXPECT_EQ(200, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_EQ(100, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(7, lease->iaid_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("three.example.com", lease->hostname_);
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ("0a:0b:0c:0d:0e", lease->hwaddr_->toText(false));
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ EXPECT_FALSE(lease->getContext());
+ }
+
+ {
+ SCOPED_TRACE("Forth lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+
+ // Verify that the lease attributes are correct.
+ EXPECT_EQ("2001:db8:1::4", lease->addr_.toText());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:03", lease->duid_->toText());
+ EXPECT_EQ(200, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_EQ(100, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(7, lease->iaid_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("three.example.com", lease->hostname_);
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ("0a:0b:0c:0d:0e", lease->hwaddr_->toText(false));
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ ASSERT_TRUE(lease->getContext());
+ EXPECT_EQ("{ \"foobar\": true }", lease->getContext()->str());
+ }
+
+}
+
+// Verifies that a lease file with fewer header columns than the
+// minimum allowed will not open.
+TEST_F(CSVLeaseFile6Test, tooFewHeaderColumns) {
+ io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
+ "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev\n");
+
+ // Open should fail.
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_THROW(lf.open(), CSVFileError);
+}
+
+// Verifies that a lease file with an unrecognized column header
+// will not open.
+TEST_F(CSVLeaseFile6Test, invalidHeaderColumn) {
+ io_.writeFile("address,BOGUS,valid_lifetime,expire,subnet_id,pref_lifetime,"
+ "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,"
+ "hwaddr,state,user_context\n");
+
+ // Open should fail.
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_THROW(lf.open(), CSVFileError);
+}
+
+// Verifies that a lease file with more header columns than defined
+// columns will open as needing a downgrade.
+TEST_F(CSVLeaseFile6Test, downGrade) {
+ // Create a mixed schema file
+ io_.writeFile(
+ // schema 4.0 header
+ "address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
+ "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,"
+ "hwaddr,state,user_context,hwtype,hwaddr_source,FUTURE_COLUMN\n"
+
+ // schema 4.0 record
+ "2001:db8:1::3,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:03,"
+ "200,200,8,100,0,7,0,1,1,three.example.com,0a:0b:0c:0d:0e,1,"
+ "{ \"foobar\": true },1,0,FUTURE_VALUE\n");
+
+ // Open should succeed in the event someone is downgrading.
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.open());
+ EXPECT_TRUE(lf.needsConversion());
+ EXPECT_EQ(util::VersionedCSVFile::NEEDS_DOWNGRADE,
+ lf.getInputSchemaState());
+
+
+ Lease6Ptr lease;
+ {
+ SCOPED_TRACE("First lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+
+ // Verify that the lease attributes are correct.
+ EXPECT_EQ("2001:db8:1::3", lease->addr_.toText());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:03", lease->duid_->toText());
+ EXPECT_EQ(200, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_EQ(100, lease->preferred_lft_);
+ EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+ EXPECT_EQ(7, lease->iaid_);
+ EXPECT_EQ(0, lease->prefixlen_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("three.example.com", lease->hostname_);
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ("0a:0b:0c:0d:0e", lease->hwaddr_->toText(false));
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ ASSERT_TRUE(lease->getContext());
+ EXPECT_EQ("{ \"foobar\": true }", lease->getContext()->str());
+ EXPECT_EQ(1, lease->hwaddr_->htype_);
+ EXPECT_EQ(0, lease->hwaddr_->source_);
+ }
+}
+
+// Verifies that leases with no DUID are invalid, and that leases
+// with the "Empty" DUID (1 byte duid = 0x0) are valid only when
+// in the declined state.
+TEST_F(CSVLeaseFile6Test, declinedLeaseTest) {
+ io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
+ "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
+ "fqdn_rev,hostname,hwaddr,state,user_context,"
+ "hwtype,hwaddr_source\n"
+ "2001:db8:1::1,00,"
+ "200,200,8,100,0,7,0,1,1,host.example.com,,0,,,\n"
+ "2001:db8:1::1,,"
+ "200,200,8,100,0,7,0,1,1,host.example.com,,0,,,\n"
+ "2001:db8:1::1,00,"
+ "200,200,8,100,0,7,0,1,1,host.example.com,,1,,,\n");
+
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.open());
+ EXPECT_FALSE(lf.needsConversion());
+ EXPECT_EQ(util::VersionedCSVFile::CURRENT, lf.getInputSchemaState());
+ Lease6Ptr lease;
+
+ {
+ SCOPED_TRACE("\"Empty\" DUID and not declined, invalid");
+ EXPECT_FALSE(lf.next(lease));
+ EXPECT_FALSE(lease);
+ EXPECT_EQ(lf.getReadErrs(), 1);
+ EXPECT_EQ(lf.getReadMsg(),
+ "The Empty DUID is only valid for declined leases");
+ }
+
+ {
+ SCOPED_TRACE("Missing (blank) DUID and not declined, invalid");
+ EXPECT_FALSE(lf.next(lease));
+ EXPECT_FALSE(lease);
+ EXPECT_EQ(lf.getReadErrs(), 2);
+ EXPECT_EQ(lf.getReadMsg(), "Empty DUIDs are not allowed");
+ }
+
+ {
+ SCOPED_TRACE("Empty DUID and declined, valid");
+ EXPECT_TRUE(lf.next(lease));
+ EXPECT_TRUE(lease);
+ EXPECT_EQ(lf.getReadErrs(), 2);
+ EXPECT_EQ(lf.getReadMsg(), "validation not started");
+ }
+}
+
+
+// Verifies that it is possible to output a lease with very high valid
+// lifetime (infinite in RFC2131 terms) and current time, and then read
+// back this lease.
+TEST_F(CSVLeaseFile6Test, highLeaseLifetime) {
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.recreate());
+ ASSERT_TRUE(io_.exists());
+
+ // Write lease with very high lease lifetime and current time.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
+ makeDUID(DUID0, sizeof(DUID0)),
+ 7, 100, 0xFFFFFFFF, 8, true, true,
+ "host.example.com"));
+
+ // Write this lease out to the lease file.
+ ASSERT_NO_THROW(lf.append(*lease));
+
+ // Close the lease file.
+ lf.close();
+
+ Lease6Ptr lease_read;
+
+ // Re-open the file for reading.
+ ASSERT_NO_THROW(lf.open());
+
+ // Read the lease and make sure it is successful.
+ EXPECT_TRUE(lf.next(lease_read));
+ ASSERT_TRUE(lease_read);
+
+ // The valid lifetime and the cltt should match with the original lease.
+ EXPECT_EQ(lease->valid_lft_, lease_read->valid_lft_);
+ EXPECT_EQ(lease->cltt_, lease_read->cltt_);
+}
+
+// Verifies that it is possible to write and read a lease with commas
+// in hostname and user context.
+TEST_F(CSVLeaseFile6Test, embeddedCommas) {
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.recreate());
+ ASSERT_TRUE(io_.exists());
+
+ std::string hostname("host,example,com");
+ std::string context_str("{ \"bar\": true, \"foo\": false, \"x\": \"factor\" }");
+
+ // Create a lease with commas in the hostname.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
+ makeDUID(DUID0, sizeof(DUID0)),
+ 7, 100, 0xFFFFFFFF, 8, true, true,
+ hostname));
+
+ // Add the user context with commas.
+ lease->setContext(Element::fromJSON(context_str));
+
+ // Write this lease out to the lease file.
+ ASSERT_NO_THROW(lf.append(*lease));
+
+ // Close the lease file.
+ lf.close();
+
+ Lease6Ptr lease_read;
+
+ // Re-open the file for reading.
+ ASSERT_NO_THROW(lf.open());
+
+ // Read the lease and make sure it is successful.
+ EXPECT_TRUE(lf.next(lease_read));
+ ASSERT_TRUE(lease_read);
+
+ // Expect the hostname and user context to retain the commas
+ // they started with.
+ EXPECT_EQ(hostname, lease->hostname_);
+ EXPECT_EQ(context_str, lease->getContext()->str());
+}
+
+// Verifies that it is possible to write and read a lease with
+// escape tags and sequences in hostname and user context.
+TEST_F(CSVLeaseFile6Test, embeddedEscapes) {
+ CSVLeaseFile6 lf(filename_);
+ ASSERT_NO_THROW(lf.recreate());
+ ASSERT_TRUE(io_.exists());
+
+ std::string hostname("host&#xexample&#x2ccom");
+ std::string context_str("{ \"&#xbar\": true, \"foo\": false, \"x\": \"fac&#x2ctor\" }");
+
+ // Create a lease with commas in the hostname.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
+ makeDUID(DUID0, sizeof(DUID0)),
+ 7, 100, 0xFFFFFFFF, 8, true, true,
+ hostname));
+
+ // Add the user context with commas.
+ lease->setContext(Element::fromJSON(context_str));
+
+ // Write this lease out to the lease file.
+ ASSERT_NO_THROW(lf.append(*lease));
+
+ // Close the lease file.
+ lf.close();
+
+ Lease6Ptr lease_read;
+
+ // Re-open the file for reading.
+ ASSERT_NO_THROW(lf.open());
+
+ // Read the lease and make sure it is successful.
+ EXPECT_TRUE(lf.next(lease_read));
+ ASSERT_TRUE(lease_read);
+
+ // Expect the hostname and user context to retain the commas
+ // they started with.
+ EXPECT_EQ(hostname, lease->hostname_);
+ EXPECT_EQ(context_str, lease->getContext()->str());
+}
+
+
+
+
+/// @todo Currently we don't check invalid lease attributes, such as invalid
+/// lease type, invalid preferred lifetime vs valid lifetime etc. The Lease6
+/// should be extended with the function that validates lease attributes. Once
+/// this is implemented we should provide more tests for malformed leases
+/// in the CSV file. See http://oldkea.isc.org/ticket/2405.
+
+} // end of anonymous namespace