BIND 10 trac3360, updated. 21a15e9e506bcb1879382ca6d532da6c9b4c6d0c [3360] Implemented the CSV lease file parser for DHCPv4.
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Mar 24 13:32:20 UTC 2014
The branch, trac3360 has been updated
via 21a15e9e506bcb1879382ca6d532da6c9b4c6d0c (commit)
from 587f21a5bc1243c06b52bb4b70d23b7dc4300645 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 21a15e9e506bcb1879382ca6d532da6c9b4c6d0c
Author: Marcin Siodelski <marcin at isc.org>
Date: Mon Mar 24 14:32:10 2014 +0100
[3360] Implemented the CSV lease file parser for DHCPv4.
-----------------------------------------------------------------------
Summary of changes:
src/lib/dhcp/duid.cc | 57 ++++---
src/lib/dhcp/duid.h | 46 ++++--
src/lib/dhcp/hwaddr.cc | 40 ++++-
src/lib/dhcp/hwaddr.h | 32 +++-
src/lib/dhcp/tests/duid_unittest.cc | 32 ++++
src/lib/dhcp/tests/hwaddr_unittest.cc | 27 +++-
src/lib/dhcpsrv/Makefile.am | 1 +
.../{csv_lease_file6.cc => csv_lease_file4.cc} | 120 +++++++-------
.../{csv_lease_file6.h => csv_lease_file4.h} | 59 +++----
src/lib/dhcpsrv/tests/Makefile.am | 1 +
src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc | 166 ++++++++++++++++++++
src/lib/dhcpsrv/tests/testdata/leases4_0.csv | 4 +
12 files changed, 446 insertions(+), 139 deletions(-)
copy src/lib/dhcpsrv/{csv_lease_file6.cc => csv_lease_file4.cc} (55%)
copy src/lib/dhcpsrv/{csv_lease_file6.h => csv_lease_file4.h} (74%)
create mode 100644 src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc
create mode 100644 src/lib/dhcpsrv/tests/testdata/leases4_0.csv
-----------------------------------------------------------------------
diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc
index 64f9c63..6ad2129 100644
--- a/src/lib/dhcp/duid.cc
+++ b/src/lib/dhcp/duid.cc
@@ -49,24 +49,8 @@ DUID::DUID(const uint8_t* data, size_t len) {
duid_ = std::vector<uint8_t>(data, data + len);
}
-const std::vector<uint8_t>& DUID::getDuid() const {
- return (duid_);
-}
-
-DUID::DUIDType DUID::getType() const {
- if (duid_.size() < 2) {
- return (DUID_UNKNOWN);
- }
- uint16_t type = (duid_[0] << 8) + duid_[1];
- if (type < DUID_MAX) {
- return (static_cast<DUID::DUIDType>(type));
- } else {
- return (DUID_UNKNOWN);
- }
-}
-
-DUID
-DUID::fromText(const std::string& text) {
+std::vector<uint8_t>
+DUID::decode(const std::string& text) {
/// @todo optimize stream operations here.
std::vector<std::string> split_text;
boost::split(split_text, text, boost::is_any_of(":"),
@@ -78,22 +62,41 @@ DUID::fromText(const std::string& text) {
s << "0";
} else if (split_text[i].size() > 2) {
- isc_throw(isc::BadValue, "invalid DUID '" << text << "'");
+ isc_throw(isc::BadValue, "invalid identifier '" << text << "'");
}
s << split_text[i];
}
- if (s.str().empty()) {
- isc_throw(isc::BadValue, "empty DUID is not allowed");
- }
std::vector<uint8_t> binary;
try {
util::encode::decodeHex(s.str(), binary);
} catch (const Exception& ex) {
- isc_throw(isc::BadValue, "failed to create DUID from text '"
+ isc_throw(isc::BadValue, "failed to create identifier from text '"
<< text << "': " << ex.what());
}
- return DUID(&binary[0], binary.size());
+ return (binary);
+}
+
+const std::vector<uint8_t>& DUID::getDuid() const {
+ return (duid_);
+}
+
+DUID::DUIDType DUID::getType() const {
+ if (duid_.size() < 2) {
+ return (DUID_UNKNOWN);
+ }
+ uint16_t type = (duid_[0] << 8) + duid_[1];
+ if (type < DUID_MAX) {
+ return (static_cast<DUID::DUIDType>(type));
+ } else {
+ return (DUID_UNKNOWN);
+ }
+}
+
+DUID
+DUID::fromText(const std::string& text) {
+ std::vector<uint8_t> binary = decode(text);
+ return DUID(binary);
}
std::string DUID::toText() const {
@@ -152,6 +155,12 @@ std::string ClientId::toText() const {
return (DUID::toText());
}
+ClientIdPtr
+ClientId::fromText(const std::string& text) {
+ std::vector<uint8_t> binary = decode(text);
+ return (ClientIdPtr(new ClientId(binary)));
+}
+
// Compares two client-ids
bool ClientId::operator==(const ClientId& other) const {
return (this->duid_ == other.duid_);
diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h
index 19d164d..4feba1b 100644
--- a/src/lib/dhcp/duid.h
+++ b/src/lib/dhcp/duid.h
@@ -72,13 +72,7 @@ class DUID {
/// @brief Create DUID from the textual format.
///
/// This static function parses a DUID specified in the textual format.
- /// The format being parsed should match the DUID representation returned
- /// by the @c DUID::toText method, i.e. the pairs of hexadecimal digits
- /// representing bytes of DUID must be separated by colons. Usually the
- /// single byte is represented by two hexadecimal digits. However, this
- /// function allows one digit per byte. In this case, a zero is prepended
- /// before the conversion. For example, a DUID 0:1:2::4:5 equals to
- /// 00:01:02:00:04:05.
+ /// Internally it uses @c DUID::decode to parse the DUID.
///
/// @param text DUID in the hexadecimal format with digits representing
/// individual bytes separated by colons.
@@ -96,6 +90,23 @@ class DUID {
bool operator!=(const DUID& other) const;
protected:
+
+ /// @brief Decodes the textual format of the DUID.
+ ///
+ /// The format being parsed should match the DUID representation returned
+ /// by the @c DUID::toText method, i.e. the pairs of hexadecimal digits
+ /// representing bytes of DUID must be separated by colons. Usually the
+ /// single byte is represented by two hexadecimal digits. However, this
+ /// function allows one digit per byte. In this case, a zero is prepended
+ /// before the conversion. For example, a DUID 0:1:2::4:5 equals to
+ /// 00:01:02:00:04:05.
+ ///
+ /// @param text DUID in the hexadecimal format with digits representing
+ /// individual bytes separated by colons.
+ ///
+ /// @throw isc::BadValue if parsing the DUID failed.
+ static std::vector<uint8_t> decode(const std::string& text);
+
/// The actual content of the DUID
std::vector<uint8_t> duid_;
};
@@ -103,7 +114,10 @@ class DUID {
/// @brief Shared pointer to a DUID
typedef boost::shared_ptr<DUID> DuidPtr;
-
+/// @brief Forward declaration to the @c ClientId class.
+class ClientId;
+/// @brief Shared pointer to a Client ID.
+typedef boost::shared_ptr<ClientId> ClientIdPtr;
/// @brief Holds Client identifier or client IPv4 address
///
@@ -147,6 +161,19 @@ public:
/// @brief Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
std::string toText() const;
+ /// @brief Create client identifier from the textual format.
+ ///
+ /// This static function creates the instance of the @c ClientId from the
+ /// textual format. Internally it calls @c DUID::fromText. The format of
+ /// the input must match the format of the DUID in @c DUID::fromText.
+ ///
+ /// @param text Client identifier in the textual format.
+ ///
+ /// @return Pointer to the instance of the @c ClientId.
+ /// @throw isc::BadValue if parsing the client identifier failed.
+ /// @throw isc::OutOfRange if the client identifier is truncated.
+ static ClientIdPtr fromText(const std::string& text);
+
/// @brief Compares two client-ids for equality
bool operator==(const ClientId& other) const;
@@ -154,9 +181,6 @@ public:
bool operator!=(const ClientId& other) const;
};
-/// @brief Shared pointer to a Client ID.
-typedef boost::shared_ptr<ClientId> ClientIdPtr;
-
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/lib/dhcp/hwaddr.cc b/src/lib/dhcp/hwaddr.cc
index eb23b44..0ca10ea 100644
--- a/src/lib/dhcp/hwaddr.cc
+++ b/src/lib/dhcp/hwaddr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -15,6 +15,10 @@
#include <dhcp/hwaddr.h>
#include <dhcp/dhcp4.h>
#include <exceptions/exceptions.h>
+#include <util/encode/hex.h>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/constants.hpp>
+#include <boost/algorithm/string/split.hpp>
#include <iomanip>
#include <sstream>
#include <vector>
@@ -42,9 +46,11 @@ HWAddr::HWAddr(const std::vector<uint8_t>& hwaddr, uint8_t htype)
}
}
-std::string HWAddr::toText() const {
+std::string HWAddr::toText(bool include_htype) const {
std::stringstream tmp;
- tmp << "hwtype=" << static_cast<int>(htype_) << " ";
+ if (include_htype) {
+ tmp << "hwtype=" << static_cast<int>(htype_) << " ";
+ }
tmp << std::hex;
bool delim = false;
for (std::vector<uint8_t>::const_iterator it = hwaddr_.begin();
@@ -58,6 +64,34 @@ std::string HWAddr::toText() const {
return (tmp.str());
}
+HWAddr
+HWAddr::fromText(const std::string& text) {
+ /// @todo optimize stream operations here.
+ std::vector<std::string> split_text;
+ boost::split(split_text, text, boost::is_any_of(":"),
+ boost::algorithm::token_compress_on);
+
+ std::ostringstream s;
+ for (size_t i = 0; i < split_text.size(); ++i) {
+ if (split_text[i].size() == 1) {
+ s << "0";
+
+ } else if (split_text[i].size() > 2) {
+ isc_throw(isc::BadValue, "invalid hwaddr '" << text << "'");
+ }
+ s << split_text[i];
+ }
+
+ std::vector<uint8_t> binary;
+ try {
+ util::encode::decodeHex(s.str(), binary);
+ } catch (const Exception& ex) {
+ isc_throw(isc::BadValue, "failed to create hwaddr from text '"
+ << text << "': " << ex.what());
+ }
+ return (HWAddr(binary, HTYPE_ETHER));
+}
+
bool HWAddr::operator==(const HWAddr& other) const {
return ((this->htype_ == other.htype_) &&
(this->hwaddr_ == other.hwaddr_));
diff --git a/src/lib/dhcp/hwaddr.h b/src/lib/dhcp/hwaddr.h
index 848ad23..b296b2d 100644
--- a/src/lib/dhcp/hwaddr.h
+++ b/src/lib/dhcp/hwaddr.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -54,8 +54,34 @@ public:
// Hardware type
uint8_t htype_;
- /// @brief Returns textual representation of a client-id (e.g. 00:01:02:03)
- std::string toText() const;
+ /// @brief Returns textual representation of a hardware address
+ /// (e.g. 00:01:02:03:04:05)
+ ///
+ /// @param include_htype Boolean value which controls whether the hardware
+ /// type is included in the returned string (true), or not (false).
+ ///
+ /// @return Hardware address in the textual format.
+ std::string toText(bool include_htype = true) const;
+
+ /// @brief Creates instance of the hardware address from textual format.
+ ///
+ /// This function parses HW address specified as text and creates the
+ /// corresponding @c HWAddr instance. The hexadecimal digits representing
+ /// individual bytes of the hardware address should be separated with
+ /// colons. Typically, two digits per byte are used. However, this function
+ /// allows for 1 digit per HW address byte. In this case, the digit is
+ /// prepended with '0' during conversion to binary value.
+ ///
+ /// This function can be used to perform a reverse operation to the
+ /// @c HWAddr::toText(false).
+ ///
+ /// The instance created by this function sets HTYPE_ETHER as a hardware
+ /// type.
+ ///
+ /// @param text HW address in the textual format.
+ ///
+ /// @return Instance of the HW address created from text.
+ static HWAddr fromText(const std::string& text);
/// @brief Compares two hardware addresses for equality
bool operator==(const HWAddr& other) const;
diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc
index 9d3ec75..71b2d02 100644
--- a/src/lib/dhcp/tests/duid_unittest.cc
+++ b/src/lib/dhcp/tests/duid_unittest.cc
@@ -280,4 +280,36 @@ TEST(ClientIdTest, toText) {
EXPECT_EQ("00:01:02:03:04:ff:fe", clientid.toText());
}
+// This test checks that the ClientId instance can be created from the textual
+// format and that error is reported if the textual format is invalid.
+TEST(ClientIdTest, fromText) {
+ ClientIdPtr cid;
+ // ClientId with only decimal digits.
+ ASSERT_NO_THROW(
+ cid = ClientId::fromText("00:01:02:03:04:05:06")
+ );
+ EXPECT_EQ("00:01:02:03:04:05:06", cid->toText());
+ // ClientId with some hexadecimal digits (upper case and lower case).
+ ASSERT_NO_THROW(
+ cid = ClientId::fromText("00:aa:bb:CD:ee:EF:ab")
+ );
+ EXPECT_EQ("00:aa:bb:cd:ee:ef:ab", cid->toText());
+ // ClientId with one digit for a particular byte.
+ ASSERT_NO_THROW(
+ cid = ClientId::fromText("00:a:bb:D:ee:EF:ab")
+ );
+ EXPECT_EQ("00:0a:bb:0d:ee:ef:ab", cid->toText());
+ // Repeated colon sign in the ClientId should be ignored.
+ ASSERT_NO_THROW(
+ cid = ClientId::fromText("00::bb:D:ee:EF:ab")
+ );
+ EXPECT_EQ("00:bb:0d:ee:ef:ab", cid->toText());
+ // ClientId with excessive number of digits for one of the bytes.
+ EXPECT_THROW(
+ cid = ClientId::fromText("00:01:021:03:04:05:06"),
+ isc::BadValue
+ );
+}
+
+
} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/hwaddr_unittest.cc b/src/lib/dhcp/tests/hwaddr_unittest.cc
index bf2eb9a..03084ea 100644
--- a/src/lib/dhcp/tests/hwaddr_unittest.cc
+++ b/src/lib/dhcp/tests/hwaddr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012, 2014 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
@@ -108,6 +108,9 @@ TEST(HWAddrTest, toText) {
EXPECT_EQ("hwtype=15 00:01:02:03:04:05", hw->toText());
+ // In some cases we don't want htype value to be included. Check that
+ // it can be forced.
+ EXPECT_EQ("00:01:02:03:04:05", hw->toText(false));
}
TEST(HWAddrTest, stringConversion) {
@@ -131,5 +134,27 @@ TEST(HWAddrTest, stringConversion) {
EXPECT_EQ(std::string("hwtype=1 c3:07:a2:e8:42"), result);
}
+// Checks that the HW address can be created from the textual format.
+TEST(HWAddrTest, fromText) {
+ scoped_ptr<HWAddr> hwaddr;
+ // Create HWAddr from text.
+ ASSERT_NO_THROW(
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:A:bc:d:67")));
+ );
+ EXPECT_EQ("00:01:0a:bc:0d:67", hwaddr->toText(false));
+
+ // HWAddr class should allow empty address.
+ ASSERT_NO_THROW(
+ hwaddr.reset(new HWAddr(HWAddr::fromText("")));
+ );
+ EXPECT_TRUE(hwaddr->toText(false).empty());
+
+ // There should be no more than two digits per byte of the HW addr.
+ EXPECT_THROW(
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:00A:bc:0d:67"))),
+ isc::BadValue
+ );
+
+}
} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 3d0ff91..69a9f25 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -39,6 +39,7 @@ libb10_dhcpsrv_la_SOURCES =
libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
libb10_dhcpsrv_la_SOURCES += callout_handle_store.h
+libb10_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
libb10_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
libb10_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
libb10_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
diff --git a/src/lib/dhcpsrv/csv_lease_file4.cc b/src/lib/dhcpsrv/csv_lease_file4.cc
new file mode 100644
index 0000000..3434ae6
--- /dev/null
+++ b/src/lib/dhcpsrv/csv_lease_file4.cc
@@ -0,0 +1,178 @@
+// Copyright (C) 2014 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.
+
+#include <dhcpsrv/csv_lease_file4.h>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+CSVLeaseFile4::CSVLeaseFile4(const std::string& filename)
+ : CSVFile(filename) {
+ initColumns();
+}
+
+void
+CSVLeaseFile4::append(const Lease4& lease) const {
+ CSVRow row(getColumnCount());
+ row.writeAt(getColumnIndex("address"), lease.addr_.toText());
+ HWAddr hwaddr(lease.hwaddr_, HTYPE_ETHER);
+ row.writeAt(getColumnIndex("hwaddr"), hwaddr.toText(false));
+ // Client id may be unset (NULL).
+ if (lease.client_id_) {
+ row.writeAt(getColumnIndex("client_id"), lease.client_id_->toText());
+ }
+ row.writeAt(getColumnIndex("valid_lifetime"), lease.valid_lft_);
+ row.writeAt(getColumnIndex("expire"), lease.cltt_ + lease.valid_lft_);
+ row.writeAt(getColumnIndex("subnet_id"), lease.subnet_id_);
+ 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);
+}
+
+bool
+CSVLeaseFile4::next(Lease4Ptr& lease) {
+ // We will return NULL pointer if the lease is not read.
+ lease.reset();
+ // Get the row of CSV values.
+ CSVRow row;
+ CSVFile::next(row);
+ // The empty row signals EOF.
+ if (row == CSVFile::EMPTY_ROW()) {
+ return (true);
+ }
+
+ // 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 false value.
+ try {
+ // Get client id. It is possible that the client id is empty and the
+ // returned pointer is NULL. This is ok, but if the client id is NULL,
+ // we need to be careful to not use the NULL pointer.
+ ClientIdPtr client_id = readClientId(row);
+ std::vector<uint8_t> client_id_vec;
+ if (client_id) {
+ client_id_vec = client_id->getClientId();
+ }
+ size_t client_id_len = client_id_vec.empty() ? 0 : client_id_vec.size();
+
+ // Get the HW address. It should never be empty and the readHWAddr checks
+ // that.
+ HWAddr hwaddr = readHWAddr(row);
+ lease.reset(new Lease4(readAddress(row),
+ &hwaddr.hwaddr_[0], hwaddr.hwaddr_.size(),
+ client_id_vec.empty() ? NULL : &client_id_vec[0],
+ client_id_len,
+ readValid(row),
+ 0, 0, // t1, t2 = 0
+ readCltt(row),
+ readSubnetID(row),
+ readFqdnFwd(row),
+ readFqdnRev(row),
+ readHostname(row)));
+
+ } catch (std::exception& ex) {
+ // 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);
+ }
+ return (true);
+}
+
+void
+CSVLeaseFile4::initColumns() {
+ addColumn("address");
+ addColumn("hwaddr");
+ addColumn("client_id");
+ addColumn("valid_lifetime");
+ addColumn("expire");
+ addColumn("subnet_id");
+ addColumn("fqdn_fwd");
+ addColumn("fqdn_rev");
+ addColumn("hostname");
+}
+
+IOAddress
+CSVLeaseFile4::readAddress(const CSVRow& row) {
+ IOAddress address(row.readAt(getColumnIndex("address")));
+ return (address);
+}
+
+HWAddr
+CSVLeaseFile4::readHWAddr(const CSVRow& row) {
+ HWAddr hwaddr = HWAddr::fromText(row.readAt(getColumnIndex("hwaddr")));
+ if (hwaddr.hwaddr_.empty()) {
+ isc_throw(isc::BadValue, "hardware address in the lease file"
+ " must not be empty");
+ }
+ return (hwaddr);
+}
+
+ClientIdPtr
+CSVLeaseFile4::readClientId(const CSVRow& row) {
+ std::string client_id = row.readAt(getColumnIndex("client_id"));
+ // NULL client ids are allowed in DHCPv4.
+ if (client_id.empty()) {
+ return (ClientIdPtr());
+ }
+ ClientIdPtr cid = ClientId::fromText(client_id);
+ return (cid);
+}
+
+uint32_t
+CSVLeaseFile4::readValid(const CSVRow& row) {
+ uint32_t valid =
+ row.readAndConvertAt<uint32_t>(getColumnIndex("valid_lifetime"));
+ return (valid);
+}
+
+time_t
+CSVLeaseFile4::readCltt(const CSVRow& row) {
+ uint32_t cltt = row.readAndConvertAt<uint32_t>(getColumnIndex("expire"))
+ - readValid(row);
+ return (cltt);
+}
+
+SubnetID
+CSVLeaseFile4::readSubnetID(const CSVRow& row) {
+ SubnetID subnet_id =
+ row.readAndConvertAt<SubnetID>(getColumnIndex("subnet_id"));
+ return (subnet_id);
+}
+
+bool
+CSVLeaseFile4::readFqdnFwd(const CSVRow& row) {
+ bool fqdn_fwd = row.readAndConvertAt<bool>(getColumnIndex("fqdn_fwd"));
+ return (fqdn_fwd);
+}
+
+bool
+CSVLeaseFile4::readFqdnRev(const CSVRow& row) {
+ bool fqdn_rev = row.readAndConvertAt<bool>(getColumnIndex("fqdn_rev"));
+ return (fqdn_rev);
+}
+
+std::string
+CSVLeaseFile4::readHostname(const CSVRow& row) {
+ std::string hostname = row.readAt(getColumnIndex("hostname"));
+ return (hostname);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcpsrv/csv_lease_file4.h b/src/lib/dhcpsrv/csv_lease_file4.h
new file mode 100644
index 0000000..ce68b91
--- /dev/null
+++ b/src/lib/dhcpsrv/csv_lease_file4.h
@@ -0,0 +1,153 @@
+// Copyright (C) 2014 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 CSV_LEASE_FILE4_H
+#define CSV_LEASE_FILE4_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/subnet.h>
+#include <util/csv_file.h>
+#include <stdint.h>
+#include <string>
+#include <time.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Provides methods to access CSV file with DHCPv4 leases.
+///
+/// This class contains methods customized to read DHCPv4 leases from the CSV
+/// file. It expects that the CSV file being parsed, contains the set of columns
+/// with well known names (initialized in the class constructor).
+///
+/// @todo This class doesn't validate the lease values read from the file.
+/// The @c Lease4 is a structure that should be itself responsible for this
+/// validation (see http://bind10.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 {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Initializes columns of the lease file.
+ ///
+ /// @param filename Name of the lease file.
+ CSVLeaseFile4(const std::string& filename);
+
+ /// @brief Appends the lease record to the CSV file.
+ ///
+ /// This function doesn't throw exceptions itself. In theory, exceptions
+ /// are possible when the index of the indexes of the values being written
+ /// to the file are invalid. However, this would have been a programming
+ /// error.
+ ///
+ /// @param lease Structure representing a DHCPv4 lease.
+ void append(const Lease4& lease) const;
+
+ /// @brief Reads next lease from the CSV file.
+ ///
+ /// If this function hits an error during lease read, it sets the error
+ /// message using @c CSVFile::setReadMsg and returns false. The error
+ /// string may be read using @c CSVFile::getReadMsg.
+ ///
+ /// This function is exception safe.
+ ///
+ /// @param [out] lease Pointer to the lease read from CSV file or
+ /// NULL pointer if lease hasn't been read.
+ ///
+ /// @return Boolean value indicating that the new lease has been
+ /// read from the CSV file (if true), or that the error has occurred
+ /// (false).
+ ///
+ /// @todo Make sure that the values read from the file are correct.
+ /// The appropriate @c Lease4 validation mechanism should be used once
+ /// ticket http://bind10.isc.org/ticket/2405 is implemented.
+ bool next(Lease4Ptr& lease);
+
+private:
+
+ /// @brief Initializes columns of the CSV file holding leases.
+ ///
+ /// This function initializes the following columns:
+ /// - address
+ /// - hwaddr
+ /// - client_id
+ /// - valid_lifetime
+ /// - expire
+ /// - subnet_id
+ /// - fqdn_fwd
+ /// - fqdn_rev
+ /// - hostname
+ void initColumns();
+
+ ///
+ /// @name Methods which read specific lease fields from the CSV row.
+ ///
+ //@{
+ ///
+ /// @brief Reads lease address from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ asiolink::IOAddress readAddress(const util::CSVRow& row);
+
+ /// @brief Reads HW address from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ HWAddr readHWAddr(const util::CSVRow& row);
+
+ /// @brief Reads client identifier from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ ClientIdPtr readClientId(const util::CSVRow& row);
+
+ /// @brief Reads valid lifetime from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ uint32_t readValid(const util::CSVRow& row);
+
+ /// @brief Reads cltt value from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ time_t readCltt(const util::CSVRow& row);
+
+ /// @brief Reads subnet id from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ SubnetID readSubnetID(const util::CSVRow& row);
+
+ /// @brief Reads the FQDN forward flag from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ bool readFqdnFwd(const util::CSVRow& row);
+
+ /// @brief Reads the FQDN reverse flag from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ bool readFqdnRev(const util::CSVRow& row);
+
+ /// @brief Reads hostname from the CSV file row.
+ ///
+ /// @param row CSV file holding lease values.
+ std::string readHostname(const util::CSVRow& row);
+ //@}
+
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // CSV_LEASE_FILE4_H
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index ba7f719..f00bf6b 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -56,6 +56,7 @@ libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc
libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
diff --git a/src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc b/src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc
new file mode 100644
index 0000000..fd29230
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright (C) 2014 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.
+
+#include <config.h>
+#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
+#include <dhcpsrv/csv_lease_file4.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/tests/lease_file_io.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <sstream>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::util;
+
+namespace {
+
+// HWADDR values used by unit tests.
+const uint8_t HWADDR0[] = { 0, 1, 2, 3, 4, 5 };
+const uint8_t HWADDR1[] = { 0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf };
+
+const uint8_t CLIENTID0[] = { 1, 2, 3, 4 };
+const uint8_t CLIENTID1[] = { 0xa, 0xb, 0xc, 0xd };
+
+/// @brief Test fixture class for @c CSVLeaseFile4 validation.
+class CSVLeaseFile4Test : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Initializes IO for lease file used by unit tests.
+ CSVLeaseFile4Test();
+
+ /// @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 Name of the test lease file.
+ std::string filename_;
+
+ /// @brief Object providing access to lease file IO.
+ LeaseFileIO io_;
+
+};
+
+CSVLeaseFile4Test::CSVLeaseFile4Test()
+ : filename_(absolutePath("leases4.csv")), io_(filename_) {
+}
+
+std::string
+CSVLeaseFile4Test::absolutePath(const std::string& filename) {
+ std::ostringstream s;
+ s << TEST_DATA_BUILDDIR << "/" << filename;
+ return (s.str());
+}
+
+// This test checks the capability to read and parse leases from the file.
+TEST_F(CSVLeaseFile4Test, parse) {
+ // Open the lease file.
+ boost::scoped_ptr<CSVLeaseFile4>
+ lf(new CSVLeaseFile4(absolutePath("leases4_0.csv")));
+ ASSERT_NO_THROW(lf->open());
+
+ Lease4Ptr lease;
+ // Reading first read should be successful.
+ EXPECT_TRUE(lf->next(lease));
+ ASSERT_TRUE(lease);
+
+ // Verify that the lease attributes are correct.
+ EXPECT_EQ("192.0.2.1", lease->addr_.toText());
+ HWAddr hwaddr1(lease->hwaddr_, HTYPE_ETHER);
+ EXPECT_EQ("06:07:08:09:0a:bc", hwaddr1.toText(false));
+ EXPECT_FALSE(lease->client_id_);
+ EXPECT_EQ(200, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(8, lease->subnet_id_);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("host.example.com", lease->hostname_);
+
+ // Second lease is malformed - HW address is empty.
+ EXPECT_FALSE(lf->next(lease));
+
+ // Even though parsing previous lease failed, reading the next lease should be
+ // successful.
+ EXPECT_TRUE(lf->next(lease));
+ ASSERT_TRUE(lease);
+ // Verify that the third lease is correct.
+ EXPECT_EQ("192.0.3.15", lease->addr_.toText());
+ HWAddr hwaddr3(lease->hwaddr_, HTYPE_ETHER);
+ EXPECT_EQ("dd:de:ba:0d:1b:2e:3e:4f", hwaddr3.toText(false));
+ ASSERT_TRUE(lease->client_id_);
+ EXPECT_EQ("0a:00:01:04", lease->client_id_->toText());
+ EXPECT_EQ(100, lease->valid_lft_);
+ EXPECT_EQ(0, lease->cltt_);
+ EXPECT_EQ(7, lease->subnet_id_);
+ EXPECT_FALSE(lease->fqdn_fwd_);
+ EXPECT_FALSE(lease->fqdn_rev_);
+ EXPECT_TRUE(lease->hostname_.empty());
+
+ // There are no more leases. Reading should cause no error, but the returned
+ // lease pointer should be NULL.
+ EXPECT_TRUE(lf->next(lease));
+ EXPECT_FALSE(lease);
+
+ // We should be able to do it again.
+ EXPECT_TRUE(lf->next(lease));
+ EXPECT_FALSE(lease);
+
+}
+
+// This test checks creation of the lease file and writing leases.
+TEST_F(CSVLeaseFile4Test, recreate) {
+ boost::scoped_ptr<CSVLeaseFile4> lf(new CSVLeaseFile4(filename_));
+ ASSERT_NO_THROW(lf->recreate());
+ ASSERT_TRUE(io_.exists());
+ // Create first lease, with NULL client id.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.3.2"),
+ HWADDR0, sizeof(HWADDR0),
+ NULL, 0,
+ 200, 50, 80, 0, 8, true, true,
+ "host.example.com"));
+ ASSERT_NO_THROW(lf->append(*lease));
+ // Create second lease, with non-NULL client id.
+ lease.reset(new Lease4(IOAddress("192.0.3.10"),
+ HWADDR1, sizeof(HWADDR1),
+ CLIENTID0, sizeof(CLIENTID0),
+ 100, 60, 90, 0, 7));
+ ASSERT_NO_THROW(lf->append(*lease));
+ // Close the lease file.
+ lf->close();
+ // Check that the contents of the csv file are correct.
+ 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"
+ "192.0.3.10,0d:0e:0a:0d:0b:0e:0e:0f,01:02:03:04,100,100,7,0,"
+ "0,\n",
+ io_.readFile());
+}
+
+/// @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://bind10.isc.org/ticket/2405.
+
+} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/testdata/leases4_0.csv b/src/lib/dhcpsrv/tests/testdata/leases4_0.csv
new file mode 100644
index 0000000..de8b9fc
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/testdata/leases4_0.csv
@@ -0,0 +1,4 @@
+address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname
+192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,host.example.com
+192.0.2.1,,a:11:01:04,200,200,8,1,1,host.example.com
+192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,0,0,
More information about the bind10-changes
mailing list