Browse Source

[master] Merge branch 'trac4212'

Marcin Siodelski 9 years ago
parent
commit
7948104393

+ 2 - 1
AUTHORS

@@ -84,7 +84,8 @@ We have received the following contributions:
    2015-01: Extract MAC address from remote-id
    2015-05: MySQL schema extended to cover host reservation
    2015-10: Common MySQL Connector Pool
-   2015-12: MySQL host data source implemented.
+   2015-12: MySQL host data source implemented
+   2016-02: IPv6 reservations implemented
 
  - Jinmei Tatuya
    2015-10: Pkt4o6 class improvements

+ 14 - 1
src/lib/dhcp/classify.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 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
@@ -10,6 +10,7 @@
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/algorithm/string/constants.hpp>
 #include <boost/algorithm/string/split.hpp>
+#include <sstream>
 #include <vector>
 
 namespace isc {
@@ -28,6 +29,18 @@ ClientClasses::ClientClasses(const std::string& class_names)
         }
     }
 }
+
+std::string
+ClientClasses::toText(const std::string& separator) const {
+    std::stringstream s;
+    for (const_iterator class_it = begin(); class_it != end(); ++class_it) {
+        if (class_it != begin()) {
+            s << separator;
+        }
+        s << *class_it;
+    }
+    return (s.str());
+}
     
 } // end of namespace isc::dhcp
 } // end of namespace isc

+ 8 - 1
src/lib/dhcp/classify.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 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
@@ -65,6 +65,13 @@ namespace dhcp {
         contains(const ClientClass& x) const {
             return (find(x) != end());
         }
+
+        /// @brief Returns all class names as text
+        ///
+        /// @param separator Separator to be used between class names. The
+        /// default separator comprises comma sign followed by space
+        /// character.
+        std::string toText(const std::string& separator = ", ") const;
     };
 
 };

+ 25 - 1
src/lib/dhcp/tests/classify_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 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
@@ -107,3 +107,27 @@ TEST(ClassifyTest, ClientClassesIterator) {
     EXPECT_TRUE(seengamma);
     EXPECT_FALSE(seendelta);
 }
+
+// Check that the ClientClasses::toText function returns
+// correct values.
+TEST(ClassifyTest, ClientClassesToText) {
+    // No classes.
+    ClientClasses classes;
+    EXPECT_TRUE(classes.toText().empty());
+
+    // Insert single class name and see if it doesn't include spurious
+    // comma after it.
+    classes.insert("alpha");
+    EXPECT_EQ("alpha", classes.toText());
+
+    // Insert next class name and see that both classes are present.
+    classes.insert("gamma");
+    EXPECT_EQ("alpha, gamma", classes.toText());
+
+    // Insert third class and make sure they get ordered alphabetically.
+    classes.insert("beta");
+    EXPECT_EQ("alpha, beta, gamma", classes.toText());
+
+    // Check non-standard separator.
+    EXPECT_EQ("alpha.beta.gamma", classes.toText("."));
+}

+ 2 - 2
src/lib/dhcpsrv/host.cc

@@ -78,7 +78,7 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len,
       ipv6_subnet_id_(ipv6_subnet_id),
       ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
       hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
-      dhcp6_client_classes_(dhcp6_client_classes),
+      dhcp6_client_classes_(dhcp6_client_classes), host_id_(0),
       cfg_option4_(), cfg_option6_() {
 
     // Initialize HWAddr or DUID
@@ -100,7 +100,7 @@ Host::Host(const std::string& identifier, const std::string& identifier_name,
       ipv6_subnet_id_(ipv6_subnet_id),
       ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
       hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
-      dhcp6_client_classes_(dhcp6_client_classes),
+      dhcp6_client_classes_(dhcp6_client_classes), host_id_(0),
       cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
 
     // Initialize HWAddr or DUID

+ 20 - 0
src/lib/dhcpsrv/host.h

@@ -22,6 +22,9 @@
 namespace isc {
 namespace dhcp {
 
+/// @brief HostID (used only when storing in MySQL or Postgres)
+typedef uint64_t HostID;
+
 /// @brief IPv6 reservation for a host.
 ///
 /// This class represents a reservation for a host of a single IPv6
@@ -447,6 +450,18 @@ public:
     /// @brief Returns information about the host in the textual format.
     std::string toText() const;
 
+    /// @brief Sets Host ID (primary key in MySQL and Postgres backends)
+    /// @param id HostId value
+    void setHostId(HostID id) {
+        host_id_ = id;
+    }
+
+    /// @brief Returns Host ID (primary key in MySQL and Postgres backends)
+    /// @return id HostId value (or 0 if not set)
+    HostID getHostId() const {
+        return (host_id_);
+    }
+
 private:
 
     /// @brief Adds new client class for DHCPv4 or DHCPv6.
@@ -482,6 +497,11 @@ private:
     ClientClasses dhcp4_client_classes_;
     /// @brief Collection of classes associated with a DHCPv6 client.
     ClientClasses dhcp6_client_classes_;
+
+    /// @brief HostID (a unique identifier assigned when the host is stored in
+    ///                MySQL or Pgsql)
+    uint64_t host_id_;
+
     /// @brief Pointer to the DHCPv4 option data configuration for this host.
     CfgOptionPtr cfg_option4_;
     /// @brief Pointer to the DHCPv6 option data configuration for this host.

+ 5 - 16
src/lib/dhcpsrv/mysql_connection.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -22,21 +22,10 @@ using namespace std;
 namespace isc {
 namespace dhcp {
 
-/// @brief Maximum size of database fields
-///
-/// The following constants define buffer sizes for variable length database
-/// fields.  The values should be greater than or equal to the length set in
-/// the schema definition.
-///
-/// The exception is the length of any VARCHAR fields: buffers for these should
-/// be set greater than or equal to the length of the field plus 1: this allows
-/// for the insertion of a trailing null whatever data is returned.
-
-const my_bool MLM_FALSE = 0;                ///< False value
-const my_bool MLM_TRUE = 1;                 ///< True value
-
-///@}
-
+const my_bool MLM_FALSE = 0;
+const my_bool MLM_TRUE = 1;
+const int MLM_MYSQL_FETCH_SUCCESS = 0;
+const int MLM_MYSQL_FETCH_FAILURE = 1;
 
 // Open the database using the parameters passed to the constructor.
 

+ 18 - 7
src/lib/dhcpsrv/mysql_connection.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -16,20 +16,31 @@
 namespace isc {
 namespace dhcp {
 
-/// @brief MySQL True/False constants
+/// @name MySQL constants.
 ///
-/// Declare typed values so as to avoid problems of data conversion.  These
-/// are local to the file but are given the prefix MLM (MySql Lease Manager) to
-/// avoid any likely conflicts with variables in header files named TRUE or
-/// FALSE.
+//@{
+
+/// @brief MySQL false value.
 extern const my_bool MLM_FALSE;
+
+/// @brief MySQL true value.
 extern const my_bool MLM_TRUE;
 
-// Define the current database schema values
+/// @brief MySQL fetch success code.
+extern const int MLM_MYSQL_FETCH_SUCCESS;
+
+/// @brief MySQL fetch failure code.
+extern const int MLM_MYSQL_FETCH_FAILURE;
 
+//@}
+
+/// @name Current database schema version values.
+//@{
 const uint32_t CURRENT_VERSION_VERSION = 3;
 const uint32_t CURRENT_VERSION_MINOR = 0;
 
+//@}
+
 /// @brief Fetch and Release MySQL Results
 ///
 /// When a MySQL statement is expected, to fetch the results the function

File diff suppressed because it is too large
+ 896 - 244
src/lib/dhcpsrv/mysql_host_data_source.cc


+ 18 - 94
src/lib/dhcpsrv/mysql_host_data_source.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -7,26 +7,20 @@
 #ifndef MYSQL_HOST_DATA_SOURCE_H
 #define MYSQL_HOST_DATA_SOURCE_H
 
-#include <dhcp/hwaddr.h>
 #include <dhcpsrv/base_host_data_source.h>
 #include <dhcpsrv/mysql_connection.h>
 
-#include <boost/scoped_ptr.hpp>
-#include <boost/utility.hpp>
-#include <mysql.h>
-
 namespace isc {
 namespace dhcp {
 
-// Forward declaration of the Host exchange objects.  These classes are defined
-// in the .cc file.
-class MySqlHostReservationExchange;
+/// Forward declaration to the implementation of the @ref MySqlHostDataSource.
+class MySqlHostDataSourceImpl;
 
 /// @brief MySQL Host Data Source
 ///
-/// This class provides the @ref isc::dhcp::BaseHostDataSource interface to the MySQL
-/// database.  Use of this backend presupposes that a MySQL database is
-/// available and that the Kea schema has been created within it.
+/// This class implements the @ref isc::dhcp::BaseHostDataSource interface to
+/// the MySQL database. Use of this backend presupposes that a MySQL database
+/// is available and that the Kea schema has been created within it.
 class MySqlHostDataSource: public BaseHostDataSource {
 public:
 
@@ -54,7 +48,9 @@ public:
     ///        failed.
     MySqlHostDataSource(const DatabaseConnection::ParameterMap& parameters);
 
-    /// @brief Destructor (closes database)
+    /// @brief Virtual destructor.
+    ///
+    /// Releases prepared MySQL statements used by the backend.
     virtual ~MySqlHostDataSource();
 
     /// @brief Return all hosts for the specified HW address or DUID.
@@ -108,7 +104,7 @@ public:
     /// @return Const @c Host object using a specified HW address or DUID.
     virtual ConstHostPtr
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
-            const DuidPtr& duid = DuidPtr()) const;
+         const DuidPtr& duid = DuidPtr()) const;
 
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// a reservation for a specified IPv4 address.
@@ -179,9 +175,9 @@ public:
 
     /// @brief Returns backend name.
     ///
-    /// Each backend have specific name, e.g. "mysql" or "sqlite".
+    /// Each backend have specific name.
     ///
-    /// @return Name of the backend.
+    /// @return "mysql".
     virtual std::string getName() const;
 
     /// @brief Returns description of the backend.
@@ -203,25 +199,20 @@ public:
 
     /// @brief Commit Transactions
     ///
-    /// Commits all pending database operations.  On databases that don't
-    /// support transactions, this is a no-op.
+    /// Commits all pending database operations.
     virtual void commit();
 
     /// @brief Rollback Transactions
     ///
-    /// Rolls back all pending database operations.  On databases that don't
-    /// support transactions, this is a no-op.
+    /// Rolls back all pending database operations.
     virtual void rollback();
 
-    MySqlConnection* getDatabaseConnection() {
-        return &conn_;
-    }
-
     /// @brief Statement Tags
     ///
     /// The contents of the enum are indexes into the list of SQL statements
     enum StatementIndex {
         INSERT_HOST,            // Insert new host to collection
+        INSERT_V6_RESRV,        // Insert v6 reservation
         GET_HOST_HWADDR_DUID,   // Gets hosts by DUID and/or HW address
         GET_HOST_ADDR,          // Gets hosts by IPv4 address
         GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
@@ -233,81 +224,14 @@ public:
     };
 
 private:
-    /// @brief Add Host Code
-    ///
-    /// This method performs adding a host operation.
-    /// It binds the contents of the host object to
-    /// the prepared statement and adds it to the database.
-    ///
-    /// @param stindex Index of statemnent being executed
-    /// @param bind MYSQL_BIND array that has been created for the host
-    ///
-    /// @htrow isc::dhcp::DuplicateEntry Database throws duplicate entry error
-    void addHost(StatementIndex stindex, std::vector<MYSQL_BIND>& bind);
-
-    /// @brief Get Host Collection Code
-    ///
-    /// This method obtains multiple hosts 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 ConstHostCollection object returned.  Note that any hosts
-    ///        in the collection when this method is called are not erased: the
-    ///        new data is appended to the end.
-    /// @param single If true, only a single data item is to be retrieved.
-    ///        If more than one is present, a MultipleRecords exception will
-    ///        be thrown.
-    ///
-    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
-    /// @throw isc::dhcp::DbOperationError An operation on the open database has
-    ///        failed.
-    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
-    ///        from the database where only one was expected.
-    void getHostCollection(StatementIndex stindex, MYSQL_BIND* bind,
-            boost::shared_ptr<MySqlHostReservationExchange> exchange,
-            ConstHostCollection& result, bool single = false) const;
-
-    /// @brief Check Error and Throw Exception
-    ///
-    /// Virtually all MySQL functions return a status which, if non-zero,
-    /// indicates an error.  This inline function conceals a lot of error
-    /// checking/exception-throwing code.
-    ///
-    /// @param status Status code: non-zero implies an error
-    /// @param index Index of statement that caused the error
-    /// @param what High-level description of the error
-    ///
-    /// @throw isc::dhcp::DbOperationError An operation on the open database
-    ///        has failed.
-    inline void checkError(int status, StatementIndex index,
-            const char* what) const {
-        if (status != 0) {
-            isc_throw(DbOperationError, what << " for <"
-                    << conn_.text_statements_[index] << ">, reason: "
-                    << mysql_error(conn_.mysql_) << " (error code "
-                    << mysql_errno(conn_.mysql_) << ")");
-        }
-    }
 
-    /// @brief Checks if Host with same parameters already been added.
+    /// @brief Checks if the specified host already exists in the database.
     ///
     /// @param host Pointer to the new @c Host object being added.
     bool checkIfExists(const HostPtr& host);
 
-    // Members
-
-    /// The exchange objects are used for transfer of data to/from the database.
-    /// They are pointed-to objects as the contents may change in "const" calls,
-    /// while the rest of this object does not.  (At alternative would be to
-    /// declare them as "mutable".)
-
-    /// @brief MySQL Host Reservation Exchange object
-    boost::shared_ptr<MySqlHostReservationExchange> hostExchange_;
-
-    /// @brief MySQL connection
-    MySqlConnection conn_;
-
+    /// @brief Pointer to the implementation of the @ref MySqlHostDataSource.
+    MySqlHostDataSourceImpl* impl_; 
 };
 
 }

+ 154 - 21
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -242,8 +242,7 @@ void GenericHostDataSourceTest::compareHosts(const ConstHostPtr& host1,
 
     // Compare IPv6 reservations
     compareReservations6(host1->getIPv6Reservations(),
-                         host2->getIPv6Reservations(),
-                         true);
+                         host2->getIPv6Reservations());
 
     // And compare client classification details
     compareClientClasses(host1->getClientClasses4(),
@@ -274,25 +273,50 @@ GenericHostDataSourceTest::DuidToHWAddr(const DuidPtr& duid) {
 
 void
 GenericHostDataSourceTest::compareReservations6(IPv6ResrvRange resrv1,
-                                                IPv6ResrvRange resrv2,
-                                                bool expect_match) {
+                                                IPv6ResrvRange resrv2) {
 
     // Compare number of reservations for both hosts
     if (std::distance(resrv1.first, resrv1.second) !=
             std::distance(resrv2.first, resrv2.second)){
-        // Number of reservations is not equal.
-        // Let's see if it's a problem.
-        if (expect_match) {
-            ADD_FAILURE()<< "Reservation comparison failed, "
-                    "hosts got different number of reservations.";
-        }
+        ADD_FAILURE()<< "Reservation comparison failed, "
+            "hosts got different number of reservations.";
         return;
     }
 
+    // Iterate over the range of reservations to find a match in the
+    // reference range.
+    for (IPv6ResrvIterator r1 = resrv1.first; r1 != resrv1.second; ++r1) {
+        IPv6ResrvIterator r2 = resrv2.first;
+        for (; r2 != resrv2.second; ++r2) {
+            // IPv6Resrv object implements equality operator.
+            if (r1->second == r2->second) {
+                break;
+            }
+        }
+        // If r2 iterator reached the end of the range it means that there
+        // is no match.
+        if (r2 == resrv2.second) {
+            ADD_FAILURE() << "No match found for reservation: "
+                          << resrv1.first->second.getPrefix().toText();
+        }
+    }
+
     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++) {
+            IPv6ResrvIterator iter = resrv2.first;
+            while (iter != resrv2.second) {
+                if((resrv1.first->second.getType() == iter->second.getType()) &&
+                        (resrv1.first->second.getPrefixLen() == iter->second.getPrefixLen()) &&
+                        (resrv1.first->second.getPrefix() == iter->second.getPrefix())) {
+                    break;
+                }
+                iter++;
+                if (iter == resrv2.second) {
+                    ADD_FAILURE()<< "Reservation comparison failed, "
+                    "no match for reservation: "
+                    << resrv1.first->second.getPrefix().toText();
+                }
+            }
         }
     }
 }
@@ -600,8 +624,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());
@@ -631,8 +655,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());
@@ -741,19 +765,128 @@ void GenericHostDataSourceTest::testGetByIPv6(BaseHostDataSource::IdType id,
     EXPECT_FALSE(hdsptr_->get6(IOAddress("2001:db8::5"), len));
 }
 
-void GenericHostDataSourceTest::testAddDuplicate() {
+void GenericHostDataSourceTest::testAddDuplicate6WithSameDUID() {
+    // Make sure we have the pointer to the host data source.
+    ASSERT_TRUE(hdsptr_);
+
+    // Create a host reservations.
+    HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
+
+    // Add this reservation once.
+    ASSERT_NO_THROW(hdsptr_->add(host));
+
+    // Then try to add it again, it should throw an exception.
+    ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
+}
+
+void GenericHostDataSourceTest::testAddDuplicate6WithSameHWAddr() {
     // Make sure we have the pointer to the host data source.
     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_HWADDR, true);
 
     // Add this reservation once.
     ASSERT_NO_THROW(hdsptr_->add(host));
 
     // Then try to add it again, it should throw an exception.
     ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
+}
+
+void GenericHostDataSourceTest::testAddDuplicate4() {
+    // Make sure we have the pointer to the host data source.
+    ASSERT_TRUE(hdsptr_);
+
+    // Create a host reservations.
+    HostPtr host = initializeHost4("192.0.2.1", false);
+
+    // Add this reservation once.
+    ASSERT_NO_THROW(hdsptr_->add(host));
+
+    // Then try to add it again, it should throw an exception.
+    ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
+}
+
+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());
+}
+
+void GenericHostDataSourceTest::testMultipleReservations(){
+    // 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);
+    IPv6Resrv resv4(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::9"), len);
+
+    host->addReservation(resv1);
+    host->addReservation(resv2);
+    host->addReservation(resv3);
+    host->addReservation(resv4);
+
+    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);
+}
+
+void GenericHostDataSourceTest::testMultipleReservationsDifferentOrder(){
+    // Make sure we have the pointer to the host data source.
+    ASSERT_TRUE(hdsptr_);
+    uint8_t len = 128;
+
+    HostPtr host1 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, false);
+    HostPtr host2 = 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);
+    IPv6Resrv resv4(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::9"), len);
+
+    host1->addReservation(resv1);
+    host1->addReservation(resv2);
+    host1->addReservation(resv3);
+    host1->addReservation(resv4);
+
+    host2->addReservation(resv4);
+    host2->addReservation(resv3);
+    host2->addReservation(resv2);
+    host2->addReservation(resv1);
+
+    // Check if reservations are the same
+    compareReservations6(host1->getIPv6Reservations(), host2->getIPv6Reservations());
 
 }
 

+ 27 - 5
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -104,8 +104,7 @@ public:
     ///
     /// @param resv1 first IPv6 reservations list
     /// @param resv2 second IPv6 reservations list
-    void compareReservations6(IPv6ResrvRange resv1, IPv6ResrvRange resv2,
-                              bool expect_match);
+    void compareReservations6(IPv6ResrvRange resv1, IPv6ResrvRange resv2);
 
     /// @brief Compares two client classes
     ///
@@ -198,6 +197,19 @@ 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 testMultipleReservations();
+
+    /// @brief Tests if compareIPv6Reservations() method treats same pool of
+    ///        reservations but added in different order as equal.
+    void testMultipleReservationsDifferentOrder();
+
     /// @brief Test if host reservations made for different IPv6 subnets
     ///        are handled correctly.
     ///
@@ -207,10 +219,20 @@ public:
     /// @param id identifier type (ID_HWADDR or ID_DUID)
     void testSubnetId6(int subnets, BaseHostDataSource::IdType id);
 
-    /// @brief Test if the duplicate host instances can't be inserted.
+    /// @brief Test if the duplicate host with same DUID can't be inserted.
+    ///
+    /// Uses gtest macros to report failures.
+    void testAddDuplicate6WithSameDUID();
+
+    /// @brief Test if the duplicate host with same HWAddr can't be inserted.
+    ///
+    /// Uses gtest macros to report failures.
+    void testAddDuplicate6WithSameHWAddr();
+
+    /// @brief Test if the duplicate IPv4 host with can't be inserted.
     ///
     /// Uses gtest macros to report failures.
-    void testAddDuplicate();
+    void testAddDuplicate4();
 
     /// @brief Returns DUID with identical content as specified HW address
     ///

+ 14 - 0
src/lib/dhcpsrv/tests/host_unittest.cc

@@ -717,4 +717,18 @@ TEST(HostTest, toText) {
               host->toText());
 }
 
+// Test verifies if the host can store HostId properly.
+TEST(HostTest, hostId) {
+    boost::scoped_ptr<Host> host;
+    ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address",
+                                        SubnetID(1), SubnetID(2),
+                                        IOAddress("192.0.2.3"),
+                                        "myhost.example.com")));
+    EXPECT_EQ(0, host->getHostId());
+
+    EXPECT_NO_THROW(host->setHostId(12345));
+
+    EXPECT_EQ(12345, host->getHostId());
+}
+
 } // end of anonymous namespace

+ 38 - 20
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -271,54 +271,56 @@ 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, DISABLED_get6AddrWithDuid) {
-    /// @todo: Uncomment when IPv6 support (4212) is implemented.
+TEST_F(MySqlHostDataSourceTest, get6AddrWithDuid) {
     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, DISABLED_get6AddrWithHWAddr) {
-    /// @todo: Uncomment when IPv6 support (4212) is implemented.
+TEST_F(MySqlHostDataSourceTest, get6AddrWithHWAddr) {
     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, DISABLED_get6PrefixWithDuid) {
-    /// @todo: Uncomment when IPv6 support (4212) is implemented.
+TEST_F(MySqlHostDataSourceTest, get6PrefixWithDuid) {
     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, DISABLED_get6PrefixWithHWaddr) {
-    /// @todo: Uncomment when IPv6 support (4212) is implemented.
+TEST_F(MySqlHostDataSourceTest, get6PrefixWithHWaddr) {
     testGetByIPv6(BaseHostDataSource::ID_HWADDR, true);
 }
 
 // Test verifies if a host reservation can be added and later retrieved by
 // hardware address.
-TEST_F(MySqlHostDataSourceTest, DISABLED_get6ByHWaddr) {
-    /// @todo: Uncomment when IPv6 support (4212) is implemented.
+TEST_F(MySqlHostDataSourceTest, get6ByHWaddr) {
     testGet6ByHWAddr();
 }
 
 // Test verifies if a host reservation can be added and later retrieved by
 // client identifier.
-TEST_F(MySqlHostDataSourceTest, DISABLED_get6ByClientId) {
-    /// @todo: Uncomment when IPv6 support (4212) is implemented.
+TEST_F(MySqlHostDataSourceTest, get6ByClientId) {
     testGet6ByClientId();
 }
 
 // Test verifies if a host reservation can be stored with both IPv6 address and
 // prefix.
-TEST_F(MySqlHostDataSourceTest, DISABLED_addr6AndPrefix) {
-    /// @todo: Implement this test as part of #4212.
+TEST_F(MySqlHostDataSourceTest, addr6AndPrefix) {
+    testAddr6AndPrefix();
+}
+
+// Tests if host with multiple IPv6 reservations can be added and then
+// retrieved correctly. Test checks reservations comparing.
+TEST_F(MySqlHostDataSourceTest, multipleReservations){
+    testMultipleReservations();
+}
 
-    /// @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 compareIPv6Reservations() method treats same pool of reservations
+// but added in different order as equal.
+TEST_F(MySqlHostDataSourceTest, multipleReservationsDifferentOrder){
+    testMultipleReservationsDifferentOrder();
 }
 
 // Test verifies if multiple client classes for IPv4 can be stored.
@@ -339,7 +341,7 @@ TEST_F(MySqlHostDataSourceTest, DISABLED_multipleClientClasses6) {
 
 // Test verifies if multiple client classes for both IPv4 and IPv6 can be stored.
 TEST_F(MySqlHostDataSourceTest, DISABLED_multipleClientClassesBoth) {
-    /// @todo: Implement this test as part of #4213..
+    /// @todo: Implement this test as part of #4213.
 
     /// Add host reservation with a multiple v4 and v6 client-classes, retrieve
     /// it and make sure that all client classes are retrieved properly. Also,
@@ -377,8 +379,24 @@ TEST_F(MySqlHostDataSourceTest, subnetId6) {
 // Test if the duplicate host instances can't be inserted. The test logic is as
 // follows: try to add multiple instances of the same host reservation and
 // verify that the second and following attempts will throw exceptions.
-TEST_F(MySqlHostDataSourceTest, addDuplicate) {
-    testAddDuplicate();
+// Hosts with same DUID.
+TEST_F(MySqlHostDataSourceTest, addDuplicate6WithDUID) {
+    testAddDuplicate6WithSameDUID();
+}
+
+// Test if the duplicate host instances can't be inserted. The test logic is as
+// follows: try to add multiple instances of the same host reservation and
+// verify that the second and following attempts will throw exceptions.
+// Hosts with same HWAddr.
+TEST_F(MySqlHostDataSourceTest, addDuplicate6WithHWAddr) {
+    testAddDuplicate6WithSameHWAddr();
+}
+
+// Test if the duplicate IPv4 host instances can't be inserted. The test logic is as
+// follows: try to add multiple instances of the same host reservation and
+// verify that the second and following attempts will throw exceptions.
+TEST_F(MySqlHostDataSourceTest, addDuplicate4) {
+    testAddDuplicate4();
 }
 
 }; // Of anonymous namespace