Commit d78c0095 authored by Marcin Siodelski's avatar Marcin Siodelski

[3360] Addressed review comments.

parent a5e69119
......@@ -1535,7 +1535,6 @@ AC_CONFIG_FILES([compatcheck/Makefile
src/lib/dhcpsrv/Makefile
src/lib/dhcpsrv/tests/Makefile
src/lib/dhcpsrv/tests/test_libraries.h
src/lib/dhcpsrv/tests/testdata/Makefile
src/lib/dhcp/tests/Makefile
src/lib/dns/benchmarks/Makefile
src/lib/dns/gen-rdatacode.py
......
......@@ -3732,7 +3732,7 @@ Dhcp4/subnet4 [] list (default)
<screen>
&gt; <userinput>config set Dhcp4/lease-database/type "memfile"</userinput>
&gt; <userinput>config set Dhcp4/lease-database/persist true</userinput>
&gt; <userinput>config set Dhcp4/lease-database/leasefile "/tmp/kea-leases4.csv"</userinput>
&gt; <userinput>config set Dhcp4/lease-database/name "/tmp/kea-leases4.csv"</userinput>
&gt; <userinput>config commit</userinput>
</screen>
will change the default location of the lease file to /tmp/kea-leases4.csv.
......
......@@ -197,12 +197,6 @@
"item_type": "boolean",
"item_optional": true,
"item_default": true
},
{
"item_name": "leasefile",
"item_type": "string",
"item_optional": true,
"item_default": ""
}
]
},
......
......@@ -191,12 +191,6 @@
"item_type": "boolean",
"item_optional": true,
"item_default": true
},
{
"item_name": "leasefile",
"item_type": "string",
"item_optional": true,
"item_default": ""
}
]
},
......
......@@ -54,11 +54,18 @@ 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(":"),
boost::algorithm::token_compress_on);
boost::algorithm::token_compress_off);
std::ostringstream s;
for (size_t i = 0; i < split_text.size(); ++i) {
if (split_text[i].size() == 1) {
// If there are multiple tokens and the current one is empty, it
// means that two consecutive colons were specified. This is not
// allowed for client identifier.
if ((split_text.size() > 1) && split_text[i].empty()) {
isc_throw(isc::BadValue, "invalid identifier '" << text << "': "
<< " tokens must be separated with a single colon");
} else if (split_text[i].size() == 1) {
s << "0";
} else if (split_text[i].size() > 2) {
......
......@@ -65,15 +65,23 @@ std::string HWAddr::toText(bool include_htype) const {
}
HWAddr
HWAddr::fromText(const std::string& text) {
HWAddr::fromText(const std::string& text, const uint8_t htype) {
/// @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);
boost::algorithm::token_compress_off);
std::ostringstream s;
for (size_t i = 0; i < split_text.size(); ++i) {
if (split_text[i].size() == 1) {
// If there are multiple tokens and the current one is empty, it
// means that two consecutive colons were specified. This is not
// allowed for hardware address.
if ((split_text.size() > 1) && split_text[i].empty()) {
isc_throw(isc::BadValue, "failed to create hardware address"
" from text '" << text << "': tokens of the hardware"
" address must be separated with a single colon");
} else if (split_text[i].size() == 1) {
s << "0";
} else if (split_text[i].size() > 2) {
......@@ -89,7 +97,7 @@ HWAddr::fromText(const std::string& text) {
isc_throw(isc::BadValue, "failed to create hwaddr from text '"
<< text << "': " << ex.what());
}
return (HWAddr(binary, HTYPE_ETHER));
return (HWAddr(binary, htype));
}
bool HWAddr::operator==(const HWAddr& other) const {
......
......@@ -79,9 +79,11 @@ public:
/// type.
///
/// @param text HW address in the textual format.
/// @param htype Hardware type.
///
/// @return Instance of the HW address created from text.
static HWAddr fromText(const std::string& text);
static HWAddr fromText(const std::string& text,
const uint8_t htype = HTYPE_ETHER);
/// @brief Compares two hardware addresses for equality
bool operator==(const HWAddr& other) const;
......
......@@ -145,11 +145,11 @@ TEST(DuidTest, fromText) {
duid.reset(new DUID(DUID::fromText("00:a:bb:D:ee:EF:ab")))
);
EXPECT_EQ("00:0a:bb:0d:ee:ef:ab", duid->toText());
// Repeated colon sign in the DUID should be ignored.
ASSERT_NO_THROW(
duid.reset(new DUID(DUID::fromText("00::bb:D:ee:EF:ab")))
// Repeated colon sign is not allowed.
EXPECT_THROW(
duid.reset(new DUID(DUID::fromText("00::bb:D:ee:EF:ab"))),
isc::BadValue
);
EXPECT_EQ("00:bb:0d:ee:ef:ab", duid->toText());
// DUID with excessive number of digits for one of the bytes.
EXPECT_THROW(
duid.reset(new DUID(DUID::fromText("00:01:021:03:04:05:06"))),
......@@ -299,15 +299,16 @@ TEST(ClientIdTest, fromText) {
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")
// Repeated colon sign in the ClientId is not allowed.
EXPECT_THROW(
ClientId::fromText("00::bb:D:ee:EF:ab"),
isc::BadValue
);
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
ClientId::fromText("00:01:021:03:04:05:06"),
isc::BadValue
);
}
......
......@@ -149,6 +149,12 @@ TEST(HWAddrTest, fromText) {
);
EXPECT_TRUE(hwaddr->toText(false).empty());
// HWAddr should not allow multiple consecutive colons.
EXPECT_THROW(
hwaddr.reset(new HWAddr(HWAddr::fromText("00::01:00:bc:0d:67"))),
isc::BadValue
);
// 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"))),
......
......@@ -33,6 +33,8 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
# Make sure the generated files are deleted in a "clean" operation
CLEANFILES = *.gcno *.gcda dhcpsrv_messages.h dhcpsrv_messages.cc s-messages
# Remove CSV files created by the CSVLeaseFile6 and CSVLeaseFile4 unit tests.
CLEANFILES += *.csv
lib_LTLIBRARIES = libb10-dhcpsrv.la
libb10_dhcpsrv_la_SOURCES =
......
......@@ -46,20 +46,20 @@ CSVLeaseFile4::append(const Lease4& lease) const {
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.
// 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
// false value.
try {
// Get the row of CSV values.
CSVRow row;
CSVFile::next(row);
// The empty row signals EOF.
if (row == CSVFile::EMPTY_ROW()) {
lease.reset();
return (true);
}
// 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.
......@@ -68,7 +68,7 @@ CSVLeaseFile4::next(Lease4Ptr& lease) {
if (client_id) {
client_id_vec = client_id->getClientId();
}
size_t client_id_len = client_id_vec.empty() ? 0 : client_id_vec.size();
size_t client_id_len = client_id_vec.size();
// Get the HW address. It should never be empty and the readHWAddr checks
// that.
......
......@@ -46,20 +46,20 @@ CSVLeaseFile6::append(const Lease6& lease) const {
bool
CSVLeaseFile6::next(Lease6Ptr& 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.
// 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
// false value.
try {
// Get the row of CSV values.
CSVRow row;
CSVFile::next(row);
// The empty row signals EOF.
if (row == CSVFile::EMPTY_ROW()) {
lease.reset();
return (true);
}
lease.reset(new Lease6(readType(row), readAddress(row), readDUID(row),
readIAID(row), readPreferred(row),
readValid(row), 0, 0, // t1, t2 = 0
......
......@@ -34,7 +34,6 @@ namespace dhcp {
DbAccessParser::DbAccessParser(const std::string&, const ParserContext& ctx)
: values_(), ctx_(ctx)
{
ctx_ = ctx;
}
// Parse the configuration and check that the various keywords are consistent.
......
......@@ -447,7 +447,7 @@ Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
std::string lease_file;
try {
lease_file = getParameter("leasefile");
lease_file = getParameter("name");
} catch (const Exception& ex) {
lease_file = getDefaultLeaseFilePath(u);
}
......
......@@ -57,19 +57,15 @@ namespace dhcp {
///
/// Originally, the Memfile backend didn't write leases to disk. This was
/// particularly useful for testing server performance in non-disk bound
/// conditions. In order to preserve this capability, the new parameters
/// "persist4=yes|no" and "persist6=yes|no" has been introduced in the
/// database access string. For example, database access string:
/// "type=memfile persist4=no persist6=yes" disables disk writes to disk
/// of DHCPv4 leases enables writes to disk of DHCPv6 leases.
/// conditions. In order to preserve this capability, the new parameter
/// "persist=true|false" has been introduced in the database access string.
/// For example, database access string: "type=memfile persist=true"
/// enables writes of leases to a disk.
///
/// The lease file locations can be specified for DHCPv4 and DHCPv6 leases
/// with the following two parameters in the database access string:
/// - leasefile4
/// - leasefile6
///
/// They specify the absolute path to the files (including file names).
/// If they are not specified, the default location in the installation
/// The lease file locations can be specified with the "name=[path]"
/// parameter in the database access string. The [path] is the
/// absolute path to the file (including file name). If this parameter
/// is not specified, the default location in the installation
/// directory is used: var/bind10/kea-leases4.csv and
/// var/bind10/kea-leases6.csv.
class Memfile_LeaseMgr : public LeaseMgr {
......
SUBDIRS = . testdata
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcpsrv/tests/testdata\"
AM_CPPFLAGS += -DDHCP_DATA_DIR=\"$(abs_top_builddir)/src/lib/dhcpsrv/tests/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcpsrv/tests\"
AM_CPPFLAGS += -DDHCP_DATA_DIR=\"$(abs_top_builddir)/src/lib/dhcpsrv/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
AM_CXXFLAGS = $(B10_CXXFLAGS)
......
......@@ -248,7 +248,7 @@ TEST_F(DbAccessParserTest, emptyKeyword) {
TEST_F(DbAccessParserTest, persistV4Memfile) {
const char* config[] = {"type", "memfile",
"persist", "true",
"leasefile", "/opt/bind10/var/kea-leases4.csv",
"name", "/opt/bind10/var/kea-leases4.csv",
NULL};
string json_config = toJson(config);
......@@ -267,7 +267,7 @@ TEST_F(DbAccessParserTest, persistV4Memfile) {
TEST_F(DbAccessParserTest, persistV6Memfile) {
const char* config[] = {"type", "memfile",
"persist", "true",
"leasefile", "/opt/bind10/var/kea-leases6.csv",
"name", "/opt/bind10/var/kea-leases6.csv",
NULL};
string json_config = toJson(config);
......
......@@ -673,7 +673,13 @@ GenericLeaseMgrTest::testAddGetDelete6(bool check_t1_t2) {
// after the lease is deleted, it should really be gone
x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
EXPECT_EQ(Lease6Ptr(), x);
EXPECT_FALSE(x);
// Reopen the lease storage to make sure that lease is gone from the
// persistent storage.
reopen(V6);
x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
EXPECT_FALSE(x);
}
void
......@@ -689,7 +695,7 @@ GenericLeaseMgrTest::testBasicLease4() {
lmptr_->commit();
// Reopen the database to ensure that they actually got stored.
reopen();
reopen(V4);
Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
ASSERT_TRUE(l_returned);
......@@ -718,7 +724,7 @@ GenericLeaseMgrTest::testBasicLease4() {
ASSERT_TRUE(l_returned);
detailCompareLease(leases[2], l_returned);
reopen();
reopen(V4);
// The deleted lease should be still gone after we re-read leases from
// persistent storage.
......@@ -740,7 +746,7 @@ GenericLeaseMgrTest::testBasicLease4() {
ASSERT_NO_THROW(lmptr_->updateLease4(leases[2]));
reopen();
reopen(V4);
// The lease should be now updated in the storage.
l_returned = lmptr_->getLease4(ioaddress4_[2]);
......@@ -1420,6 +1426,87 @@ GenericLeaseMgrTest::testUpdateLease6() {
EXPECT_THROW(lmptr_->updateLease6(leases[2]), isc::dhcp::NoSuchLease);
}
void
GenericLeaseMgrTest::testRecreateLease4() {
// Create a lease.
std::vector<Lease4Ptr> leases = createLeases4();
// Copy the lease so as we can freely modify it.
Lease4Ptr lease(new Lease4(*leases[0]));
// Add a lease.
EXPECT_TRUE(lmptr_->addLease(lease));
lmptr_->commit();
// Check that the lease has been successfuly added.
Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[0]);
ASSERT_TRUE(l_returned);
detailCompareLease(lease, l_returned);
// Delete a lease, check that it's gone.
EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[0]));
EXPECT_FALSE(lmptr_->getLease4(ioaddress4_[0]));
// Modify the copy of the lease. Increasing values or negating them ensures
// that they are really modified, because we will never get the same values.
++lease->subnet_id_;
++lease->valid_lft_;
lease->fqdn_fwd_ = !lease->fqdn_fwd_;
// Make sure that the lease has been really modified.
ASSERT_NE(*lease, *leases[1]);
// Add the updated lease.
EXPECT_TRUE(lmptr_->addLease(lease));
lmptr_->commit();
// Reopen the lease database, so as the lease is re-read.
reopen(V4);
// The lease in the database should be modified.
l_returned = lmptr_->getLease4(ioaddress4_[0]);
ASSERT_TRUE(l_returned);
detailCompareLease(lease, l_returned);
}
void
GenericLeaseMgrTest::testRecreateLease6() {
// Create a lease.
std::vector<Lease6Ptr> leases = createLeases6();
// Copy the lease so as we can freely modify it.
Lease6Ptr lease(new Lease6(*leases[0]));
// Add a lease.
EXPECT_TRUE(lmptr_->addLease(lease));
lmptr_->commit();
// Check that the lease has been successfuly added.
Lease6Ptr l_returned = lmptr_->getLease6(Lease::TYPE_NA, ioaddress6_[0]);
ASSERT_TRUE(l_returned);
detailCompareLease(lease, l_returned);
// Delete a lease, check that it's gone.
EXPECT_TRUE(lmptr_->deleteLease(ioaddress6_[0]));
EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, ioaddress6_[0]));
// Modify the copy of the lease. Increasing values or negating them ensures
// that they are really modified, because we will never get the same values.
++lease->subnet_id_;
++lease->valid_lft_;
lease->fqdn_fwd_ = !lease->fqdn_fwd_;
// Make sure that the lease has been really modified.
ASSERT_NE(*lease, *leases[1]);
// Add the updated lease.
EXPECT_TRUE(lmptr_->addLease(lease));
lmptr_->commit();
// Reopen the lease database, so as the lease is re-read.
reopen(V6);
// The lease in the database should be modified.
l_returned = lmptr_->getLease6(Lease::TYPE_NA, ioaddress6_[0]);
ASSERT_TRUE(l_returned);
detailCompareLease(lease, l_returned);
}
}; // namespace test
}; // namespace dhcp
}; // namespace isc
......@@ -167,6 +167,13 @@ public:
/// @todo: check if it does overlap with @ref testGetLease4NullClientId()
void testLease4NullClientId();
/// @brief Check that the DHCPv4 lease can be added, removed and recreated.
///
/// This test creates a lease, removes it and then recreates it with some
/// of the attributes changed. Next it verifies that the lease in the
/// persistent storage has been updated as expected.
void testRecreateLease4();
/// @brief Basic Lease6 Checks
///
/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
......@@ -231,6 +238,13 @@ public:
/// Checks that the code is able to update an IPv6 lease in the database.
void testUpdateLease6();
/// @brief Check that the DHCPv6 lease can be added, removed and recreated.
///
/// This test creates a lease, removes it and then recreates it with some
/// of the attributes changed. Next it verifies that the lease in the
/// persistent storage has been updated as expected.
void testRecreateLease6();
/// @brief String forms of IPv4 addresses
std::vector<std::string> straddress4_;
......
......@@ -21,10 +21,20 @@ namespace isc {
namespace dhcp {
namespace test {
/// @brief This class contains functions to perform IO operations on files.
///
/// This class is solely used by unit tests. Some tests often need files
/// as an input. This class allows for easy creation of text files that can
/// be later used by unit tests. It also provides method to read the contents
/// of the existing file and remove existing file (cleanup after unit test).
class LeaseFileIO {
public:
/// @brief Constructor
///
/// @param filename Abolsute path to the file.
LeaseFileIO(const std::string& filename);
/// @brief Destructor.
~LeaseFileIO();
/// @brief Check if test file exists on disk.
......
......@@ -96,7 +96,7 @@ public:
static std::string getConfigString(Universe u) {
std::ostringstream s;
s << "type=memfile " << (u == V4 ? "universe=4 " : "universe=6 ")
<< "leasefile="
<< "name="
<< getLeaseFilePath(u == V4 ? "leasefile4_0.csv" : "leasefile6_0.csv");
return (s.str());
}
......@@ -134,13 +134,13 @@ TEST_F(MemfileLeaseMgrTest, constructor) {
EXPECT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
pmap["persist"] = "true";
pmap["leasefile"] = getLeaseFilePath("leasefile4_1.csv");
pmap["name"] = getLeaseFilePath("leasefile4_1.csv");
EXPECT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
// Expecting that persist parameter is yes or no. Everything other than
// that is wrong.
pmap["persist"] = "bogus";
pmap["leasefile"] = getLeaseFilePath("leasefile4_1.csv");
pmap["name"] = getLeaseFilePath("leasefile4_1.csv");
EXPECT_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)), isc::BadValue);
}
......@@ -160,10 +160,10 @@ TEST_F(MemfileLeaseMgrTest, getLeaseFilePath) {
LeaseMgr::ParameterMap pmap;
pmap["universe"] = "4";
pmap["leasefile"] = getLeaseFilePath("leasefile4_1.csv");
pmap["name"] = getLeaseFilePath("leasefile4_1.csv");
boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
EXPECT_EQ(pmap["leasefile"],
EXPECT_EQ(pmap["name"],
lease_mgr->getLeaseFilePath(Memfile_LeaseMgr::V4));
pmap["persist"] = "false";
......@@ -183,7 +183,7 @@ TEST_F(MemfileLeaseMgrTest, persistLeases) {
LeaseMgr::ParameterMap pmap;
pmap["universe"] = "4";
// Specify the names of the lease files. Leases will be written.
pmap["leasefile"] = getLeaseFilePath("leasefile4_1.csv");
pmap["name"] = getLeaseFilePath("leasefile4_1.csv");
boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
lease_mgr.reset(new Memfile_LeaseMgr(pmap));
......@@ -191,7 +191,7 @@ TEST_F(MemfileLeaseMgrTest, persistLeases) {
EXPECT_FALSE(lease_mgr->persistLeases(Memfile_LeaseMgr::V6));
pmap["universe"] = "6";
pmap["leasefile"] = getLeaseFilePath("leasefile6_1.csv");
pmap["name"] = getLeaseFilePath("leasefile6_1.csv");
lease_mgr.reset(new Memfile_LeaseMgr(pmap));
EXPECT_FALSE(lease_mgr->persistLeases(Memfile_LeaseMgr::V4));
EXPECT_TRUE(lease_mgr->persistLeases(Memfile_LeaseMgr::V6));
......@@ -384,6 +384,25 @@ TEST_F(MemfileLeaseMgrTest, DISABLED_updateLease6) {
testUpdateLease6();
}
/// @brief DHCPv4 Lease recreation tests
///
/// Checks that the lease can be created, deleted and recreated with
/// different parameters. It also checks that the re-created lease is
/// correctly stored in the lease database.
TEST_F(MemfileLeaseMgrTest, testRecreateLease4) {
startBackend(V4);
testRecreateLease4();
}
/// @brief DHCPv6 Lease recreation tests
///
/// Checks that the lease can be created, deleted and recreated with
/// different parameters. It also checks that the re-created lease is
/// correctly stored in the lease database.
TEST_F(MemfileLeaseMgrTest, testRecreateLease6) {
startBackend(V6);
testRecreateLease6();
}
// The following tests are not applicable for memfile. When adding
// new tests to the list here, make sure to provide brief explanation
......
......@@ -478,4 +478,22 @@ TEST_F(MySqlLeaseMgrTest, updateLease6) {
testUpdateLease6();
}
/// @brief DHCPv4 Lease recreation tests
///
/// Checks that the lease can be created, deleted and recreated with
/// different parameters. It also checks that the re-created lease is
/// correctly stored in the lease database.
TEST_F(MySqlLeaseMgrTest, testRecreateLease4) {
testRecreateLease4();
}
/// @brief DHCPv6 Lease recreation tests
///
/// Checks that the lease can be created, deleted and recreated with
/// different parameters. It also checks that the re-created lease is
/// correctly stored in the lease database.
TEST_F(MySqlLeaseMgrTest, testRecreateLease6) {
testRecreateLease6();
}
}; // Of anonymous namespace
SUBDIRS = .
# CSV files are created by unit tests which check the CSVLeaseFile6
# and CSVLeaseFile4 classes.
CLEANFILES = *.csv
......@@ -13,6 +13,9 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <util/csv_file.h>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/constants.hpp>
#include <boost/algorithm/string/split.hpp>
#include <fstream>
#include <sstream>
......@@ -20,53 +23,21 @@ namespace isc {
namespace util {