Browse Source

[4281] Extended MySQL host data source with DHCPv4 and DHCPv6 options.

Missing things:
- proper commentary
- formatted values for options
- rollback host insertion when failing to add reservation or option
Marcin Siodelski 9 years ago
parent
commit
718643a83a

+ 42 - 1
src/lib/dhcp/libdhcp++.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
@@ -21,9 +21,11 @@
 #include <util/buffer.h>
 #include <dhcp/option_definition.h>
 
+#include <boost/lexical_cast.hpp>
 #include <boost/shared_array.hpp>
 #include <boost/shared_ptr.hpp>
 
+#include <limits>
 #include <list>
 
 using namespace std;
@@ -844,6 +846,45 @@ LibDHCP::initVendorOptsIsc6() {
     initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE);
 }
 
+uint32_t
+LibDHCP::optionSpaceToVendorId(const std::string& option_space) {
+    if (option_space.size() < 8) {
+        // 8 is a minimal length of "vendor-X" format
+        return (0);
+    }
+    if (option_space.substr(0,7) != "vendor-") {
+        return (0);
+    }
+
+    // text after "vendor-", supposedly numbers only
+    std::string x = option_space.substr(7);
+
+    int64_t check;
+    try {
+        check = boost::lexical_cast<int64_t>(x);
+    } catch (const boost::bad_lexical_cast &) {
+        /// @todo: Should we throw here?
+        // isc_throw(BadValue, "Failed to parse vendor-X value (" << x
+        //           << ") as unsigned 32-bit integer.");
+        return (0);
+    }
+    if (check > std::numeric_limits<uint32_t>::max()) {
+        /// @todo: Should we throw here?
+        //isc_throw(BadValue, "Value " << x << "is too large"
+        //          << " for unsigned 32-bit integer.");
+        return (0);
+    }
+    if (check < 0) {
+        /// @todo: Should we throw here?
+        // isc_throw(BadValue, "Value " << x << "is negative."
+        //       << " Only 0 or larger are allowed for unsigned 32-bit integer.");
+        return (0);
+    }
+
+    // value is small enough to fit
+    return (static_cast<uint32_t>(check));
+}
+
 void initOptionSpace(OptionDefContainer& defs,
                      const OptionDefParams* params,
                      size_t params_size) {

+ 17 - 1
src/lib/dhcp/libdhcp++.h

@@ -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
@@ -14,6 +14,7 @@
 #include <util/staged_value.h>
 
 #include <iostream>
+#include <stdint.h>
 #include <string>
 
 namespace isc {
@@ -321,6 +322,21 @@ public:
     /// @brief Commits runtime option definitions.
     static void commitRuntimeOptionDefs();
 
+    /// @brief Converts option space name to vendor id.
+    ///
+    /// If the option space name is specified in the following format:
+    /// "vendor-X" where X is an uint32_t number, it is assumed to be
+    /// a vendor space and the uint32_t number is returned by this function.
+    /// If the option space name is invalid this method will return 0, which
+    /// is not a valid vendor-id, to signal an error.
+    ///
+    /// @todo remove this function once when the conversion is dealt by the
+    /// appropriate functions returning options by option space names.
+    ///
+    /// @param option_space Option space name.
+    /// @return vendor id.
+    static uint32_t optionSpaceToVendorId(const std::string& option_space);
+
 private:
 
     /// Initialize standard DHCPv4 option definitions.

+ 24 - 47
src/lib/dhcpsrv/cfg_option.cc

@@ -1,14 +1,13 @@
-// 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
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+#include <dhcp/libdhcp++.h>
 #include <dhcp/option_space.h>
 #include <dhcpsrv/cfg_option.h>
-#include <boost/lexical_cast.hpp>
 #include <dhcp/dhcp6.h>
-#include <limits>
 #include <string>
 
 namespace isc {
@@ -37,7 +36,12 @@ CfgOption::equals(const CfgOption& other) const {
 void
 CfgOption::add(const OptionPtr& option, const bool persistent,
                const std::string& option_space) {
-    if (!option) {
+    add(OptionDescriptor(option, persistent), option_space);
+}
+
+void
+CfgOption::add(const OptionDescriptor& desc, const std::string& option_space) {
+    if (!desc.option_) {
         isc_throw(isc::BadValue, "option being configured must not be NULL");
 
     } else  if (!OptionSpace::validateName(option_space)) {
@@ -45,13 +49,25 @@ CfgOption::add(const OptionPtr& option, const bool persistent,
                   << option_space << "'");
     }
 
-    const uint32_t vendor_id = optionSpaceToVendorId(option_space);
+    const uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
     if (vendor_id) {
-        vendor_options_.addItem(OptionDescriptor(option, persistent),
-                                vendor_id);
+        vendor_options_.addItem(desc, vendor_id);
     } else {
-        options_.addItem(OptionDescriptor(option, persistent), option_space);
+        options_.addItem(desc, option_space);
+    }
+}
+
+std::list<std::string>
+CfgOption::getVendorIdsSpaceNames() const {
+    std::list<uint32_t> ids = getVendorIds();
+    std::list<std::string> names;
+    for (std::list<uint32_t>::const_iterator id = ids.begin();
+         id != ids.end(); ++id) {
+        std::ostringstream s;
+        s << "vendor-" << *id;
+        names.push_back(s.str());
     }
+    return (names);
 }
 
 void
@@ -152,44 +168,5 @@ CfgOption::getAll(const uint32_t vendor_id) const {
     return (vendor_options_.getItems(vendor_id));
 }
 
-uint32_t
-CfgOption::optionSpaceToVendorId(const std::string& option_space) {
-    if (option_space.size() < 8) {
-        // 8 is a minimal length of "vendor-X" format
-        return (0);
-    }
-    if (option_space.substr(0,7) != "vendor-") {
-        return (0);
-    }
-
-    // text after "vendor-", supposedly numbers only
-    std::string x = option_space.substr(7);
-
-    int64_t check;
-    try {
-        check = boost::lexical_cast<int64_t>(x);
-    } catch (const boost::bad_lexical_cast &) {
-        /// @todo: Should we throw here?
-        // isc_throw(BadValue, "Failed to parse vendor-X value (" << x
-        //           << ") as unsigned 32-bit integer.");
-        return (0);
-    }
-    if (check > std::numeric_limits<uint32_t>::max()) {
-        /// @todo: Should we throw here?
-        //isc_throw(BadValue, "Value " << x << "is too large"
-        //          << " for unsigned 32-bit integer.");
-        return (0);
-    }
-    if (check < 0) {
-        /// @todo: Should we throw here?
-        // isc_throw(BadValue, "Value " << x << "is negative."
-        //       << " Only 0 or larger are allowed for unsigned 32-bit integer.");
-        return (0);
-    }
-
-    // value is small enough to fit
-    return (static_cast<uint32_t>(check));
-}
-
 } // end of namespace isc::dhcp
 } // end of namespace isc

+ 28 - 14
src/lib/dhcpsrv/cfg_option.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
@@ -254,6 +254,19 @@ public:
     void add(const OptionPtr& option, const bool persistent,
              const std::string& option_space);
 
+    /// @brief A variant of the method which takes option descriptor as an
+    /// argument.
+    ///
+    /// This method works exactly the same as the other variant, but it takes
+    /// option descriptor as an argument.
+    ///
+    /// @param desc Option descriptor holding option instance and other
+    /// parameters pertaining to this option.
+    /// @param option_space Option space name.
+    ///
+    /// @throw isc::BadValue if the option space is invalid.
+    void add(const OptionDescriptor& desc, const std::string& option_space);
+
     /// @brief Merges this configuration to another configuration.
     ///
     /// This method iterates over the configuration items held in this
@@ -337,20 +350,21 @@ public:
         return (*od_itr);
     }
 
-    /// @brief Converts option space name to vendor id.
-    ///
-    /// If the option space name is specified in the following format:
-    /// "vendor-X" where X is an uint32_t number, it is assumed to be
-    /// a vendor space and the uint32_t number is returned by this function.
-    /// If the option space name is invalid this method will return 0, which
-    /// is not a valid vendor-id, to signal an error.
-    ///
-    /// @todo remove this function once when the conversion is dealt by the
-    /// appropriate functions returning options by option space names.
+    /// @brief Returns a list of all configured option space names.
+    std::list<std::string> getOptionSpaceNames() const {
+        return (options_.getOptionSpaceNames());
+    }
+
+    /// @brief Returns a list of all configured  vendor identifiers.
+    std::list<uint32_t> getVendorIds() const {
+        return (vendor_options_.getOptionSpaceNames());
+    }
+
+    /// @brief Returns a list of option space names for configured vendor ids.
     ///
-    /// @param option_space Option space name.
-    /// @return vendor id.
-    static uint32_t optionSpaceToVendorId(const std::string& option_space);
+    /// For each vendor-id the option space name returned is constructed
+    /// as "vendor-<vendor-id>".
+    std::list<std::string> getVendorIdsSpaceNames() const;
 
 private:
 

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

@@ -81,7 +81,7 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len,
       ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
       hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
       dhcp6_client_classes_(dhcp6_client_classes), host_id_(0),
-      cfg_option4_(), cfg_option6_() {
+      cfg_option4_(new CfgOption()), cfg_option6_(new CfgOption()) {
 
     // Initialize host identifier.
     setIdentifier(identifier, identifier_len, identifier_type);

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


+ 2 - 0
src/lib/dhcpsrv/mysql_host_data_source.h

@@ -257,6 +257,8 @@ public:
     enum StatementIndex {
         INSERT_HOST,            // Insert new host to collection
         INSERT_V6_RESRV,        // Insert v6 reservation
+        INSERT_V4_OPTION,       // Insert DHCPv4 option
+        INSERT_V6_OPTION,       // Insert DHCPv6 option
         GET_HOST_DHCPID,        // Gets hosts by host identifier
         GET_HOST_ADDR,          // Gets hosts by IPv4 address
         GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID

+ 1 - 1
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -551,7 +551,7 @@ OptionDataParser::findOptionDefinition(const std::string& option_space,
     if (!def) {
         // Check if this is a vendor-option. If it is, get vendor-specific
         // definition.
-        uint32_t vendor_id = CfgOption::optionSpaceToVendorId(option_space);
+        uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
         if (vendor_id) {
             def = LibDHCP::getVendorOptionDef(u, vendor_id, search_key);
         }

+ 32 - 1
src/lib/dhcpsrv/tests/cfg_option_unittest.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
@@ -12,7 +12,9 @@
 #include <dhcpsrv/cfg_option.h>
 #include <boost/pointer_cast.hpp>
 #include <gtest/gtest.h>
+#include <iterator>
 #include <limits>
+#include <list>
 #include <sstream>
 
 using namespace isc;
@@ -490,5 +492,34 @@ TEST(CfgOptionTest, addVendorOptions) {
     EXPECT_TRUE(options->empty());
 }
 
+// This test verifies that option space names for the vendor options are
+// correct.
+TEST(CfgOptionTest, getVendorIdsSpaceNames) {
+    CfgOption cfg;
+
+    // Create 10 options, each goes under a different vendor id.
+    for (uint16_t code = 100; code < 110; ++code) {
+        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
+        // Generate space name for a unique vendor id.
+        std::ostringstream s;
+        s << "vendor-" << code;
+        ASSERT_NO_THROW(cfg.add(option, false, s.str()));
+    }
+
+    // We should now have 10 different vendor ids.
+    std::list<std::string> space_names = cfg.getVendorIdsSpaceNames();
+    ASSERT_EQ(10, space_names.size());
+
+    // Check that the option space names for those vendor ids are correct.
+    for (std::list<std::string>::iterator name = space_names.begin();
+         name != space_names.end(); ++name) {
+        uint16_t id = static_cast<uint16_t>(std::distance(space_names.begin(),
+                                                          name));
+        std::ostringstream s;
+        s << "vendor-" << (100 + id);
+        EXPECT_EQ(s.str(), *name);
+    }
+}
+
 
 } // end of anonymous namespace

+ 207 - 2
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -4,15 +4,30 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option4_addrlst.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/option_space.h>
+#include <dhcp/option_string.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_vendor.h>
 #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/database_connection.h>
 #include <asiolink/io_address.h>
+#include <util/buffer.h>
+#include <boost/foreach.hpp>
 #include <gtest/gtest.h>
+#include <cstring>
+#include <list>
+#include <string>
 #include <sstream>
+#include <typeinfo>
 
 using namespace std;
 using namespace isc::asiolink;
+using namespace isc::util;
 
 namespace isc {
 namespace dhcp {
@@ -20,10 +35,11 @@ namespace test {
 
 GenericHostDataSourceTest::GenericHostDataSourceTest()
     :hdsptr_() {
-
+    LibDHCP::clearRuntimeOptionDefs();
 }
 
 GenericHostDataSourceTest::~GenericHostDataSourceTest() {
+    LibDHCP::clearRuntimeOptionDefs();
 }
 
 std::vector<uint8_t>
@@ -229,12 +245,15 @@ void GenericHostDataSourceTest::compareHosts(const ConstHostPtr& host1,
     compareReservations6(host1->getIPv6Reservations(),
                          host2->getIPv6Reservations());
 
-    // And compare client classification details
+    // Compare client classification details
     compareClientClasses(host1->getClientClasses4(),
                          host2->getClientClasses4());
 
     compareClientClasses(host1->getClientClasses6(),
                          host2->getClientClasses6());
+
+    compareOptions(host1->getCfgOption4(), host2->getCfgOption4());
+    compareOptions(host1->getCfgOption6(), host2->getCfgOption6());
 }
 
 DuidPtr
@@ -313,6 +332,125 @@ GenericHostDataSourceTest::compareClientClasses(const ClientClasses& /*classes1*
     ///        This is part of the work for #4213.
 }
 
+void
+GenericHostDataSourceTest::compareOptions(const ConstCfgOptionPtr& cfg1,
+                                          const ConstCfgOptionPtr& cfg2) const {
+    ASSERT_TRUE(cfg1);
+    ASSERT_TRUE(cfg2);
+
+    std::list<std::string> option_spaces = cfg2->getOptionSpaceNames();
+    std::list<std::string> vendor_spaces = cfg2->getVendorIdsSpaceNames();
+    option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
+                         vendor_spaces.end());
+
+    BOOST_FOREACH(std::string space, option_spaces) {
+        OptionContainerPtr options1 = cfg1->getAll(space);
+        OptionContainerPtr options2 = cfg2->getAll(space);
+        ASSERT_TRUE(options1);
+        ASSERT_TRUE(options2);
+
+        ASSERT_EQ(options1->size(), options2->size());
+
+        BOOST_FOREACH(OptionDescriptor desc1, *options1) {
+            OptionDescriptor desc2 = cfg2->get(space, desc1.option_->getType());
+            ASSERT_EQ(desc1.persistent_, desc2.persistent_);
+            Option* option1 = desc1.option_.get();
+            Option* option2 = desc2.option_.get();
+
+            ASSERT_TRUE(typeid(*option1) == typeid(*option2))
+                << "Comapared DHCP options, having option code "
+                << desc1.option_->getType() << " and belonging to the "
+                << space << " option space, are represented "
+                "by different C++ classes: "
+                << typeid(*option1).name() << " vs "
+                << typeid(*option2).name();
+
+            OutputBuffer buf1(option1->len());
+            ASSERT_NO_THROW(option1->pack(buf1));
+            OutputBuffer buf2(option2->len());
+            ASSERT_NO_THROW(option2->pack(buf2));
+
+            ASSERT_EQ(buf1.getLength(), buf2.getLength());
+            ASSERT_EQ(0, memcmp(buf1.getData(), buf2.getData(), buf1.getLength()));
+        }
+    }
+}
+
+
+OptionDescriptor
+GenericHostDataSourceTest::createVendorOption(const Option::Universe& universe,
+                                              const bool persist,
+                                              const uint32_t vendor_id) const {
+    OptionVendorPtr option(new OptionVendor(universe, vendor_id));
+    OptionDescriptor desc(option, persist);
+    return (desc);
+}
+
+void
+GenericHostDataSourceTest::addTestOptions(const HostPtr& host) const {
+    // Add DHCPv4 options.
+    CfgOptionPtr opts = host->getCfgOption4();
+    opts->add(createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
+                                         true, "my-boot-file"),
+              DHCP4_OPTION_SPACE);
+    opts->add(createOption<OptionUint8>(Option::V4, DHO_DEFAULT_IP_TTL,
+                                        false, 64),
+              DHCP4_OPTION_SPACE);
+    opts->add(createOption<OptionUint32>(Option::V4, 1, false, 312131),
+              "vendor-encapsulated-options");
+    opts->add(createAddressOption<Option4AddrLst>(254, false, "192.0.2.3"),
+              DHCP4_OPTION_SPACE);
+    opts->add(createOption<Option>(Option::V4, 1, true, OptionBuffer()),
+              "isc");
+    opts->add(createAddressOption<Option4AddrLst>(2, false, "10.0.0.5",
+                                                  "10.0.0.3", "10.0.3.4"),
+              "isc");
+
+    OptionDefSpaceContainer defs;
+
+    // Add definitions for DHCPv4 non-standard options.
+    defs.addItem(OptionDefinitionPtr(new OptionDefinition("vendor-encapsulated-1",
+                                                          1, "uint32")),
+                 "vendor-encapsulated-options");
+    defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-254", 254,
+                                                          "ipv4-address", true)),
+                 DHCP4_OPTION_SPACE);
+    defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-1", 1, "empty")),
+                 "isc");
+    defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-2", 2,
+                                                          "ipv4-address", true)),
+                 "isc");
+
+    // Add DHCPv6 options.
+    opts = host->getCfgOption6();
+    opts->add(createOption<OptionString>(Option::V6, D6O_BOOTFILE_URL,
+                                         true, "my-boot-file"),
+              DHCP6_OPTION_SPACE);
+    opts->add(createOption<OptionUint32>(Option::V6, D6O_INFORMATION_REFRESH_TIME,
+                                         false, 3600),
+              DHCP6_OPTION_SPACE);
+    opts->add(createVendorOption(Option::V6, false, 2495), DHCP6_OPTION_SPACE);
+    opts->add(createAddressOption<Option6AddrLst>(1024, false, "2001:db8:1::1"),
+              DHCP6_OPTION_SPACE);
+    opts->add(createOption<Option>(Option::V6, 1, true, OptionBuffer()),
+              "isc2");
+    opts->add(createAddressOption<Option6AddrLst>(2, false, "3000::1", "3000::2",
+                                                  "3000::3"),
+              "isc2");
+
+    // Add definitions for DHCPv6 non-standard options.
+    defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-1024", 1024,
+                                                          "ipv6-address", true)),
+                 DHCP6_OPTION_SPACE);
+    defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-1", 1, "empty")),
+                 "isc2");
+    defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-2", 2,
+                                                          "ipv6-address", true)),
+                 "isc2");
+
+    LibDHCP::setRuntimeOptionDefs(defs);
+}
+
 void GenericHostDataSourceTest::testBasic4(const Host::IdentifierType& id) {
     // Make sure we have the pointer to the host data source.
     ASSERT_TRUE(hdsptr_);
@@ -898,6 +1036,73 @@ void GenericHostDataSourceTest::testMultipleReservationsDifferentOrder(){
 
 }
 
+void GenericHostDataSourceTest::testOptionsReservations4() {
+    HostPtr host = initializeHost4("192.0.2.5", Host::IDENT_HWADDR);
+    // Add a bunch of DHCPv4 and DHCPv6 options for the host.
+    ASSERT_NO_THROW(addTestOptions(host));
+    // Insert host and the options into respective tables.
+    ASSERT_NO_THROW(hdsptr_->add(host));
+    // Subnet id will be used in quries to the database.
+    SubnetID subnet_id = host->getIPv4SubnetID();
+
+    // getAll4(address)
+    ConstHostCollection hosts_by_addr = hdsptr_->getAll4(host->getIPv4Reservation());
+    ASSERT_EQ(1, hosts_by_addr.size());
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, *hosts_by_addr.begin()));
+
+    // get4(subnet_id, identifier_type, identifier, identifier_size)
+    ConstHostPtr host_by_id = hdsptr_->get4(subnet_id,
+                                            host->getIdentifierType(),
+                                            &host->getIdentifier()[0],
+                                            host->getIdentifier().size());
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
+
+    // get4(subnet_id, address)
+    ConstHostPtr host_by_addr = hdsptr_->get4(subnet_id, IOAddress("192.0.2.5"));
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_addr));
+
+    hdsptr_->get4(subnet_id, IOAddress("192.0.2.5"));
+}
+
+void GenericHostDataSourceTest::testOptionsReservations6() {
+    HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_DUID, false);
+    // Add a bunch of DHCPv4 and DHCPv6 options for the host.
+    ASSERT_NO_THROW(addTestOptions(host));
+    // Insert host, options and IPv6 reservations into respective tables.
+    ASSERT_NO_THROW(hdsptr_->add(host));
+    // Subnet id will be used in queries to the database.
+    SubnetID subnet_id = host->getIPv6SubnetID();
+
+    // get6(subnet_id, identifier_type, identifier, identifier_size)
+    ConstHostPtr host_by_id = hdsptr_->get6(subnet_id, host->getIdentifierType(),
+                                            &host->getIdentifier()[0],
+                                            host->getIdentifier().size());
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
+
+    // get6(address, prefix_len)
+    ConstHostPtr host_by_addr = hdsptr_->get6(IOAddress("2001:db8::1"), 128);
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_addr));
+}
+
+void GenericHostDataSourceTest::testOptionsReservations46() {
+    HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_HWADDR, false);
+
+    // Add a bunch of DHCPv4 and DHCPv6 options for the host.
+    ASSERT_NO_THROW(addTestOptions(host));
+    // Insert host, options and IPv6 reservations into respective tables.
+    ASSERT_NO_THROW(hdsptr_->add(host));
+    // Subnet id will be used in queries to the database.
+    SubnetID subnet_id = host->getIPv6SubnetID();
+
+    // getAll(identifier_type, identifier, identifier_size)
+    ConstHostCollection hosts_by_id = hdsptr_->getAll(host->getIdentifierType(),
+                                                      &host->getIdentifier()[0],
+                                                      host->getIdentifier().size());
+    ASSERT_EQ(1, hosts_by_id.size());
+    ASSERT_NO_FATAL_FAILURE(compareHosts(host, *hosts_by_id.begin()));
+}
+
+
 }; // namespace test
 }; // namespace dhcp
 }; // namespace isc

+ 87 - 1
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -7,9 +7,12 @@
 #ifndef GENERIC_HOST_DATA_SOURCE_UNITTEST_H
 #define GENERIC_HOST_DATA_SOURCE_UNITTEST_H
 
+#include <asiolink/io_address.h>
 #include <dhcpsrv/base_host_data_source.h>
 #include <dhcpsrv/host.h>
 #include <dhcp/classify.h>
+#include <dhcp/option.h>
+#include <boost/shared_ptr.hpp>
 #include <gtest/gtest.h>
 #include <vector>
 
@@ -122,7 +125,71 @@ public:
     /// @param classes1 first list of client classes
     /// @param classes2 second list of client classes
     void compareClientClasses(const ClientClasses& classes1,
-                              const ClientClasses& classes2);
+                              const ClientClasses& classes2);                           
+
+    /// @brief Compares options within two configurations.
+    ///
+    /// @param cfg1 First configuration.
+    /// @param cfg2 Second configuration.
+    void compareOptions(const ConstCfgOptionPtr& cfg1,
+                        const ConstCfgOptionPtr& cfg2) const;
+
+    /// @brief Creates an instance of the option for which it is possible to
+    /// specify universe, option type and value in the constructor.
+    ///
+    /// Examples of options that can be created using this function are:
+    /// - @ref OptionString
+    /// - different variants of @ref OptionInt.
+    ///
+    /// @param encapsulated_space 
+    /// @return Instance of the option created.
+    template<typename OptionType, typename DataType>
+    OptionDescriptor createOption(const Option::Universe& universe,
+                                  const uint16_t option_type,
+                                  const bool persist,
+                                  const DataType& value) const {
+        boost::shared_ptr<OptionType> option(new OptionType(universe, option_type,
+                                                            value));
+        OptionDescriptor desc(option, persist);
+        return (desc);
+    }
+
+    template<typename OptionType, typename DataType>
+    OptionDescriptor createOption(const uint16_t option_type,
+                                  const bool persist,
+                                  const DataType& value) const {
+        boost::shared_ptr<OptionType> option(new OptionType(option_type, value));
+        OptionDescriptor desc(option, persist);
+        return (desc);
+    }
+
+    template<typename OptionType>
+    OptionDescriptor
+    createAddressOption(const uint16_t option_type,
+                        const bool persist,
+                        const std::string& address1 = "",
+                        const std::string& address2 = "",
+                        const std::string& address3 = "") const {
+        typename OptionType::AddressContainer addresses;
+        if (!address1.empty()) {
+            addresses.push_back(asiolink::IOAddress(address1));
+        }
+        if (!address2.empty()) {
+            addresses.push_back(asiolink::IOAddress(address2));
+        }
+        if (!address3.empty()) {
+            addresses.push_back(asiolink::IOAddress(address3));
+        }
+        boost::shared_ptr<OptionType> option(new OptionType(option_type, addresses));
+        OptionDescriptor desc(option, persist);
+        return (desc);
+    }
+
+    OptionDescriptor createVendorOption(const Option::Universe& universe,
+                                        const bool persist,
+                                        const uint32_t vendor_id) const;
+
+    void addTestOptions(const HostPtr& host) const;
 
     /// @brief Pointer to the host data source
     HostDataSourcePtr hdsptr_;
@@ -238,6 +305,24 @@ public:
     /// Uses gtest macros to report failures.
     void testAddDuplicate4();
 
+    /// @brief Test that DHCPv4 options can be inserted and retrieved from
+    /// the database.
+    ///
+    /// Uses gtest macros to report failures.
+    void testOptionsReservations4();
+
+    /// @brief Test that DHCPv6 options can be inserted and retrieved from
+    /// the database.
+    ///
+    /// Uses gtest macros to report failures.
+    void testOptionsReservations6();
+
+    /// @brief Test that DHCPv4 and DHCPv6 options can be inserted and retrieved
+    /// with a single query to the database.
+    ///
+    /// Uses gtest macros to report failures.
+    void testOptionsReservations46();
+
     /// @brief Returns DUID with identical content as specified HW address
     ///
     /// This method does not have any sense in real life and is only useful
@@ -257,6 +342,7 @@ public:
     /// @param duid DUID to be copied
     /// @return HW address with the same value as specified DUID
     HWAddrPtr DuidToHWAddr(const DuidPtr& duid);
+
 };
 
 }; // namespace test

+ 19 - 0
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -406,4 +406,23 @@ TEST_F(MySqlHostDataSourceTest, addDuplicate4) {
     testAddDuplicate4();
 }
 
+// This test verifies that DHCPv4 options can be inserted and retrieved from
+// the MySQL host database.
+TEST_F(MySqlHostDataSourceTest, optionsReservations4) {
+    testOptionsReservations4();
+}
+
+// This test verifies that DHCPv6 options can be inserted and retrieved from
+// the MySQL host database.
+TEST_F(MySqlHostDataSourceTest, optionsReservations6) {
+    testOptionsReservations6();
+}
+
+// This test verifies that DHCPv4 and DHCPv6 options can be inserted and
+// retrieved with a single query to the database.
+TEST_F(MySqlHostDataSourceTest, optionsReservations46) {
+    testOptionsReservations46();
+}
+
+
 }; // Of anonymous namespace