Commit 4e9fb31a authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[4212] Patch as provided by Adam Kalmus

 - Tomek verified that it applied cleanly, compiled and
   unit-tests passed.
parent 01f0cb49
......@@ -46,8 +46,10 @@ public:
/// @brief Type of the reservation.
///
/// Currently supported types are NA and PD.
/// Added TA type for IPv6 Reservation implementation.
enum Type {
TYPE_NA,
TYPE_TA,
TYPE_PD
};
......
......@@ -14,6 +14,9 @@
#include <config.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
......@@ -51,14 +54,16 @@ const size_t HOSTNAME_MAX_LEN = 255;
TaggedStatement tagged_statements[] = {
{MySqlHostDataSource::INSERT_HOST,
"INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
"dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
"dhcp4_client_classes, dhcp6_client_classes) "
"dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
"dhcp4_client_classes, dhcp6_client_classes) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
{MySqlHostDataSource::INSERT_V6_RESRV,
"INSERT INTO ipv6_reservations(host_id, address, prefix_len, type, dhcp6_iaid) "
"VALUES (?,?,?,?,?)"},
"INSERT INTO ipv6_reservations(reservation_id, address, prefix_len, type, "
"dhcp6_iaid, host_id) "
"VALUES (?,?,?,?,?,?)"},
{MySqlHostDataSource::GET_V6_RESRV,
"SELECT address, prefix_len, type, dhcp6_iaid FROM ipv6_reservations "
"SELECT reservation_id, address, prefix_len, type, dhcp6_iaid, host_id "
"FROM ipv6_reservations "
"WHERE host_id = ?"},
{MySqlHostDataSource::GET_HOST_HWADDR_DUID,
"SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
......@@ -97,8 +102,8 @@ TaggedStatement tagged_statements[] = {
"dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
"dhcp4_client_classes, dhcp6_client_classes "
"FROM hosts h, ipv6_reservations r "
"WHERE h.host_id = r.host_id AND r.prefix_len = ? "
" AND r.address = ?"},
"WHERE h.host_id = r.host_id "
"AND r.address = ? AND r.prefix_len = ?"},
{MySqlHostDataSource::GET_VERSION,
"SELECT version, minor FROM schema_version"},
{MySqlHostDataSource::NUM_STATEMENTS, NULL}
......@@ -560,7 +565,7 @@ private:
class MySqlIPv6ReservationExchange {
/// @brief Set number of database columns for this reservation structure
static const size_t RESRV_COLUMNS = 5;
static const size_t RESRV_COLUMNS = 6;
public:
......@@ -636,14 +641,15 @@ public:
return (result);
}
/// @brief Create MYSQL_BIND objects for Host Pointer
/// @brief Create MYSQL_BIND objects for IPv6 Reservation
///
/// Fills in the MYSQL_BIND array for sending data in the Host object to
/// the database.
/// Fills in the MYSQL_BIND array for sending data in the IPv6 Reservation
/// object to the database.
///
/// @param host Host object to be added to the database.
/// None of the fields in the host reservation are modified -
/// the host data is only read.
/// @param resv IPv6 reservation object to be added to the database.
/// None of the fields in the reservation are modified -
/// the reservation data is only read.
/// @param id ID of a host owning this reservation
///
/// @return Vector of MySQL BIND objects representing the data to be added.
std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv, const HostID& id) {
......@@ -662,10 +668,12 @@ public:
// Set up the structures for the various components of the host structure.
try {
// host_id : INT UNSIGNED NOT NULL
host_id_ = static_cast<uint32_t>(NULL);
// reservation_id INT UNSIGNED NOT NULL
// The host_id is auto_incremented by MySQL database,
// so we need to pass the NULL value
reservation_id_ = static_cast<uint32_t>(NULL);
bind_[0].buffer_type = MYSQL_TYPE_LONG;
bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
bind_[0].buffer = reinterpret_cast<char*>(&reservation_id_);
bind_[0].is_unsigned = MLM_TRUE;
// address VARCHAR(39)
......@@ -697,10 +705,15 @@ public:
bind_[4].buffer = reinterpret_cast<char*>(&iaid_);
bind_[4].is_unsigned = MLM_TRUE;
// host_id INT UNSIGNED NOT NULL
bind_[5].buffer_type = MYSQL_TYPE_LONG;
bind_[5].buffer = reinterpret_cast<char*>(&host_id_);
bind_[5].is_unsigned = MLM_TRUE;
} catch (const std::exception& ex) {
isc_throw(DbOperationError,
"Could not create bind array from Host: "
<< host_->getHostname() << ", reason: " << ex.what());
"Could not create bind array from IPv6 Reservation: "
<< resv_.toText() << ", reason: " << ex.what());
}
// Add the data to the vector. Note the end element is one after the
......@@ -723,13 +736,43 @@ public:
// code that explicitly sets is_null is there, but is commented out.
memset(bind_, 0, sizeof(bind_));
/// @todo: set bind_[X] fields here.
// reservation_id INT UNSIGNED NOT NULL
bind_[0].buffer_type = MYSQL_TYPE_LONG;
bind_[0].buffer = reinterpret_cast<char*>(&reservation_id_);
bind_[0].is_unsigned = MLM_TRUE;
// address VARCHAR(39)
bind_[1].buffer_type = MYSQL_TYPE_BLOB;
bind_[1].buffer = reinterpret_cast<char*>
(const_cast<char*>(&address_[0]));
bind_[1].buffer_length = address_len_;
bind_[1].length = &address_len_;
// prefix_len tinyint
bind_[2].buffer_type = MYSQL_TYPE_TINY;
bind_[2].buffer = reinterpret_cast<char*>(&prefix_len_);
bind_[2].is_unsigned = MLM_TRUE;
// type tinyint
bind_[3].buffer_type = MYSQL_TYPE_TINY;
bind_[3].buffer = reinterpret_cast<char*>(&type_);
bind_[3].is_unsigned = MLM_TRUE;
// dhcp6_iaid INT UNSIGNED
bind_[4].buffer_type = MYSQL_TYPE_LONG;
bind_[4].buffer = reinterpret_cast<char*>(&iaid_);
bind_[4].is_unsigned = MLM_TRUE;
// host_id INT UNSIGNED NOT NULL
bind_[5].buffer_type = MYSQL_TYPE_LONG;
bind_[5].buffer = reinterpret_cast<char*>(&host_id_);
bind_[5].is_unsigned = MLM_TRUE;
// Add the error flags
setErrorIndicators(bind_, error_, RESRV_COLUMNS);
// .. and check that we have the numbers correct at compile time.
BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS);
// Add the data to the vector. Note the end element is one after the
// end of the array.
......@@ -745,9 +788,31 @@ public:
///
/// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
IPv6Resrv getIPv6ReservData(){
/// @todo: Implement actual extraction.
IPv6Resrv r(IPv6Resrv::TYPE_NA, IOAddress("::"), 128);
// Set the IPv6 Reservation type (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
IPv6Resrv::Type type = IPv6Resrv::TYPE_NA;
switch (type_) {
case 0:
type = IPv6Resrv::TYPE_NA;
break;
case 1:
type = IPv6Resrv::TYPE_TA;
break;
case 2:
type = IPv6Resrv::TYPE_PD;
break;
default:
isc_throw(BadValue,
"invalid IPv6 reservation type returned: "
<< static_cast<int>(type_)
<< ". Only 0, 1 or 2 are allowed.");
}
IPv6Resrv r(type, IOAddress(address_), prefix_len_);
return (r);
}
......@@ -787,6 +852,7 @@ public:
private:
uint64_t host_id_; /// Host unique identifier
uint64_t reservation_id_; /// Host unique identifier
size_t host_id_length_; /// Length of the host unique ID
std::string address_; ///< Address (or prefix)
size_t address_len_; ///< Length of the textual address representation
......@@ -805,9 +871,9 @@ private:
IPv6Resrv resv_;
MYSQL_BIND bind_[RESRV_COLUMNS];
std::string columns_[RESRV_COLUMNS]; /// Column names
std::string columns_[RESRV_COLUMNS]; /// Column names
my_bool error_[RESRV_COLUMNS]; /// Error array
HostPtr host_; // Pointer to Host object
HostPtr host_; /// Pointer to Host object
};
// MySqlHostDataSource Constructor and Destructor
......@@ -879,21 +945,10 @@ MySqlHostDataSource::add(const HostPtr& host) {
return;
}
// Ok, there are v6 reservations. Let's insert them. But first, we need
// to learn what's the host_id of the host we just added.
/// @todo: See how get6() is done in hostMgr - calls with duid only first,
/// and if can't find it, then calls with hwaddr only.
ConstHostPtr from_db = get6(host->getIPv6SubnetID(), host->getDuid(),
host->getHWAddress());
if (!from_db) {
// Oops, we have a problem. We can't find the host we just added.
isc_throw(DbOperationError, "Unable to retrieve the host that just "
"had been added");
}
// Gets the last inserted hosts id
uint64_t host_id = mysql_insert_id(conn_.mysql_);
for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second; ++resv) {
addResv(resv->second, from_db->getHostId());
addResv(resv->second, host_id);
}
}
}
......@@ -926,6 +981,88 @@ MySqlHostDataSource::addQuery(StatementIndex stindex,
}
}
void
MySqlHostDataSource::getIPv6ReservationCollection(StatementIndex stindex,
MYSQL_BIND* bind, boost::shared_ptr<MySqlIPv6ReservationExchange> exchange,
IPv6ResrvCollection& result) const {
// Bind the selection parameters to the statement
int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
checkError(status, stindex, "unable to bind WHERE clause parameter");
// Set up the MYSQL_BIND array for the data being returned and bind it to
// the statement.
std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
checkError(status, stindex, "unable to bind SELECT clause parameters");
// Execute the statement
status = mysql_stmt_execute(conn_.statements_[stindex]);
checkError(status, stindex, "unable to execute");
// Ensure that all the lease information is retrieved in one go to avoid
// overhead of going back and forth between client and server.
status = mysql_stmt_store_result(conn_.statements_[stindex]);
checkError(status, stindex, "unable to set up for storing all results");
// Set up the fetch "release" object to release resources associated
// with the call to mysql_stmt_fetch when this method exits, then
// retrieve the data.
MySqlFreeResult fetch_release(conn_.statements_[stindex]);
while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
try {
result.insert(IPv6ResrvTuple(exchange->getIPv6ReservData().getType(),
exchange->getIPv6ReservData()));
} catch (const isc::BadValue& ex) {
// Rethrow the exception with a bit more data.
isc_throw(BadValue, ex.what() << ". Statement is <" <<
conn_.text_statements_[stindex] << ">");
}
}
// How did the fetch end?
if (status == 1) {
// Error - unable to fetch results
checkError(status, stindex, "unable to fetch results");
} else if (status == MYSQL_DATA_TRUNCATED) {
// Data truncated - throw an exception indicating what was at fault
isc_throw(DataTruncated, conn_.text_statements_[stindex]
<< " returned truncated data: columns affected are "
<< exchange->getErrorColumns());
}
}
IPv6ResrvCollection
MySqlHostDataSource::getAllReservations(HostID host_id) const{
// Set up the WHERE clause value
MYSQL_BIND inbind[1];
memset(inbind, 0, sizeof(inbind));
uint32_t id = static_cast<uint32_t>(host_id);
inbind[0].buffer_type = MYSQL_TYPE_LONG;
inbind[0].buffer = reinterpret_cast<char*>(&id);
inbind[0].is_unsigned = MLM_TRUE;
IPv6ResrvCollection result;
getIPv6ReservationCollection(GET_V6_RESRV, inbind, resvExchange_, result);
return (result);
}
void
MySqlHostDataSource::assignReservations(HostPtr& host) const {
IPv6ResrvCollection reservations;
reservations = getAllReservations(host->getHostId());
for (IPv6ResrvIterator resv = reservations.begin(); resv != reservations.end(); ++resv){
host->addReservation(resv->second);
}
}
void
MySqlHostDataSource::getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
boost::shared_ptr<MySqlHostReservationExchange> exchange,
......@@ -955,9 +1092,12 @@ MySqlHostDataSource::getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
// retrieve the data.
MySqlFreeResult fetch_release(conn_.statements_[stindex]);
int count = 0;
HostPtr host;
while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
try {
result.push_back(exchange->getHostData());
host = exchange->getHostData();
assignReservations(host);
result.push_back(host);
} catch (const isc::BadValue& ex) {
// Rethrow the exception with a bit more data.
......@@ -1228,15 +1368,22 @@ MySqlHostDataSource::get6(const asiolink::IOAddress& prefix,
MYSQL_BIND inbind[2];
memset(inbind, 0, sizeof(inbind));
inbind[0].buffer_type = MYSQL_TYPE_LONG;
inbind[0].buffer = reinterpret_cast<char*>(&prefix.toBytes()[0]);
inbind[0].is_unsigned = MLM_TRUE;
std::string addr6 = prefix.toText();
unsigned long addr6_length = addr6.size();
inbind[0].buffer_type = MYSQL_TYPE_BLOB;
inbind[0].buffer = reinterpret_cast<char*>
(const_cast<char*>(addr6.c_str()));
inbind[0].length = &addr6_length;
inbind[0].buffer_length = addr6_length;
uint8_t tmp = prefix_len;
inbind[1].buffer_type = MYSQL_TYPE_LONG;
inbind[1].buffer_type = MYSQL_TYPE_TINY;
inbind[1].buffer = reinterpret_cast<char*>(&tmp);
inbind[1].is_unsigned = MLM_TRUE;
ConstHostCollection collection;
getHostCollection(GET_HOST_PREFIX, inbind, hostExchange_,
collection, true);
......
......@@ -165,6 +165,21 @@ public:
virtual ConstHostPtr
get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
/// @brief Returns all IPv6 reservations assigned to single host
///
/// @param host_id ID of a host owning IPv6 reservations
///
/// @return Collection of IPv6 reservations
virtual IPv6ResrvCollection
getAllReservations(HostID host_id) const;
/// @brief Retrieves all IPv6 reservations for a single host and then
/// adds them to that host.
///
/// @param host Pointer to a host to be populated with IPv6 reservations.
void
assignReservations(HostPtr& host) const;
/// @brief Adds a new host to the collection.
///
/// The implementations of this method should guard against duplicate
......@@ -279,6 +294,25 @@ private:
boost::shared_ptr<MySqlHostReservationExchange> exchange,
ConstHostCollection& result, bool single = false) const;
/// @brief Get IPv6 Reservation Collection
///
/// This method obtains multiple IPv6 reservations from the database.
///
/// @param stindex Index of statement being executed
/// @param bind MYSQL_BIND array for input parameters
/// @param exchange Exchange object to use
/// @param result IPv6ResrvCollection object returned. Note that any
/// reservations in the collection when this method is called
/// are not erased: the new data is appended to the end.
///
/// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
void getIPv6ReservationCollection(StatementIndex stindex, MYSQL_BIND* bind,
boost::shared_ptr<MySqlIPv6ReservationExchange> exchange,
IPv6ResrvCollection& result) const;
/// @brief Check Error and Throw Exception
///
/// Virtually all MySQL functions return a status which, if non-zero,
......@@ -308,7 +342,8 @@ private:
/// @brief Adds IPv6 Reservation
///
/// @todo: describe parameters here
/// @param resv IPv6 Reservation to be added
/// @param id ID of a host owning this reservation
void addResv(const IPv6Resrv& resv, HostID id);
// Members
......
......@@ -299,8 +299,11 @@ GenericHostDataSourceTest::compareReservations6(IPv6ResrvRange resrv1,
if (std::distance(resrv1.first, resrv1.second) > 0) {
if (expect_match){
/// @todo Compare every reservation from both hosts
/// This is part of the work for #4212.
for (; resrv1.first != resrv1.second; resrv1.first++, resrv2.first++){
EXPECT_EQ(resrv1.first->second.getType(), resrv2.first->second.getType());
EXPECT_EQ(resrv1.first->second.getPrefixLen(), resrv2.first->second.getPrefixLen());
EXPECT_EQ(resrv1.first->second.getPrefix(), resrv2.first->second.getPrefix());
}
}
}
}
......@@ -608,8 +611,8 @@ void GenericHostDataSourceTest::testGet6ByHWAddr() {
ASSERT_TRUE(hdsptr_);
// Create a host reservations.
HostPtr host1 = initializeHost6("2001:db8::0", BaseHostDataSource::ID_HWADDR, true);
HostPtr host2 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_HWADDR, true);
HostPtr host1 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_HWADDR, true);
HostPtr host2 = initializeHost6("2001:db8::2", BaseHostDataSource::ID_HWADDR, true);
// Sanity check: make sure the hosts have different HW addresses.
ASSERT_TRUE(host1->getHWAddress());
......@@ -639,8 +642,8 @@ void GenericHostDataSourceTest::testGet6ByClientId() {
ASSERT_TRUE(hdsptr_);
// Create a host reservations.
HostPtr host1 = initializeHost6("2001:db8::0", BaseHostDataSource::ID_DUID, true);
HostPtr host2 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
HostPtr host1 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
HostPtr host2 = initializeHost6("2001:db8::2", BaseHostDataSource::ID_DUID, true);
// Sanity check: make sure the hosts have different HW addresses.
ASSERT_TRUE(host1->getDuid());
......@@ -754,8 +757,7 @@ void GenericHostDataSourceTest::testAddDuplicate() {
ASSERT_TRUE(hdsptr_);
// Create a host reservations.
HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID,
true);
HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
// Add this reservation once.
ASSERT_NO_THROW(hdsptr_->add(host));
......@@ -765,6 +767,59 @@ void GenericHostDataSourceTest::testAddDuplicate() {
}
void GenericHostDataSourceTest::testAddr6AndPrefix(){
// Make sure we have the pointer to the host data source.
ASSERT_TRUE(hdsptr_);
// Create a host reservations with prefix reservation (prefix = true)
HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
// Create IPv6 reservation (for an address) and add it to the host
IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::2"), 128);
host->addReservation(resv);
// Add this reservation
ASSERT_NO_THROW(hdsptr_->add(host));
// Get this host by DUID
ConstHostPtr from_hds = hdsptr_->get6(host->getIPv6SubnetID(), host->getDuid(), HWAddrPtr());
// Make sure we got something back
ASSERT_TRUE(from_hds);
// Check if reservations are the same
compareReservations6(host->getIPv6Reservations(), from_hds->getIPv6Reservations(), true);
}
void GenericHostDataSourceTest::testMultipletReservations(){
// Make sure we have the pointer to the host data source.
ASSERT_TRUE(hdsptr_);
uint8_t len = 128;
HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, false);
// Add some reservations
IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), len);
IPv6Resrv resv2(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::7"), len);
IPv6Resrv resv3(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::8"), len);
host->addReservation(resv1);
host->addReservation(resv2);
host->addReservation(resv3);
ASSERT_NO_THROW(hdsptr_->add(host));
ConstHostPtr from_hds = hdsptr_->get6(IOAddress("2001:db8::1"), len);
// Make sure we got something back
ASSERT_TRUE(from_hds);
// Check if hosts are the same
compareHosts(host, from_hds);
}
}; // namespace test
}; // namespace dhcp
}; // namespace isc
......@@ -206,6 +206,15 @@ public:
/// Uses gtest macros to report failures.
void testGet6ByClientId();
/// @brief Test verifies if a host reservation can be stored with both
/// IPv6 address and prefix.
/// Uses gtest macros to report failures.
void testAddr6AndPrefix();
/// @brief Tests if host with multiple IPv6 reservations can be added and then
/// retrieved correctly.
void testMultipletReservations();
/// @brief Test if host reservations made for different IPv6 subnets
/// are handled correctly.
///
......
......@@ -280,53 +280,49 @@ TEST_F(MySqlHostDataSourceTest, DISABLED_hwaddrOrClientId2) {
// Test verifies that host with IPv6 address and DUID can be added and
// later retrieved by IPv6 address.
TEST_F(MySqlHostDataSourceTest, get6AddrWithDuid) {
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6(BaseHostDataSource::ID_DUID, false);
}
// Test verifies that host with IPv6 address and HWAddr can be added and
// later retrieved by IPv6 address.
TEST_F(MySqlHostDataSourceTest, get6AddrWithHWAddr) {
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6(BaseHostDataSource::ID_HWADDR, false);
}
// Test verifies that host with IPv6 prefix and DUID can be added and
// later retrieved by IPv6 prefix.
TEST_F(MySqlHostDataSourceTest, get6PrefixWithDuid) {
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6(BaseHostDataSource::ID_DUID, true);
}
// Test verifies that host with IPv6 prefix and HWAddr can be added and
// later retrieved by IPv6 prefix.
TEST_F(MySqlHostDataSourceTest, get6PrefixWithHWaddr) {
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGetByIPv6(BaseHostDataSource::ID_HWADDR, true);
}
// Test verifies if a host reservation can be added and later retrieved by
// hardware address.
TEST_F(MySqlHostDataSourceTest, get6ByHWaddr) {
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGet6ByHWAddr();
}
// Test verifies if a host reservation can be added and later retrieved by
// client identifier.
TEST_F(MySqlHostDataSourceTest, get6ByClientId) {
/// @todo: Uncomment when IPv6 support (4212) is implemented.
testGet6ByClientId();
}
// Test verifies if a host reservation can be stored with both IPv6 address and
// prefix.
TEST_F(MySqlHostDataSourceTest, addr6AndPrefix) {
/// @todo: Implement this test as part of #4212.
testAddr6AndPrefix();
}
/// @todo: Add host reservation with an IPv6 address and IPv6 prefix,
/// retrieve it and verify that both v6 address and prefix are retrieved
/// correctly.
// Tests if host with multiple IPv6 reservations can be added and then
// retrieved correctly. Test checks reservations comparing.
TEST_F(MySqlHostDataSourceTest, multipleReservations){
testMultipletReservations();
}
// Test verifies if multiple client classes for IPv4 can be stored.
......