Browse Source

[4281] It is allowed to use formatted option values in the host database.

Marcin Siodelski 9 years ago
parent
commit
29bfd06c38

+ 1 - 0
src/lib/dhcpsrv/cfg_option.cc

@@ -16,6 +16,7 @@ namespace dhcp {
 bool
 OptionDescriptor::equals(const OptionDescriptor& other) const {
     return (persistent_ == other.persistent_ &&
+            formatted_value_ == other.formatted_value_ &&
             option_->equals(other.option_));
 }
 

+ 28 - 6
src/lib/dhcpsrv/cfg_option.h

@@ -31,24 +31,46 @@ namespace dhcp {
 /// to DHCP client only on request (persistent = false) or always
 /// (persistent = true).
 struct OptionDescriptor {
-    /// Option instance.
+    /// @brief Option instance.
     OptionPtr option_;
-    /// Persistent flag, if true option is always sent to the client,
-    /// if false option is sent to the client on request.
+
+    /// @briefPersistence flag.
+    ///
+    /// If true option is always sent to the client, if false option is
+    /// sent to the client on request.
     bool persistent_;
 
+    /// @brief Option value in textual (CSV) format.
+    ///
+    /// This field is used to convey option value in human readable format,
+    /// the same as used to specify option value in the server configuration.
+    /// This value is optional and can be held in the host reservations
+    /// database instead of the binary format.
+    ///
+    /// Note that this value is carried in the option descriptor, rather than
+    /// @c Option instance because it is a server specific value.
+    ///
+    /// An example of the formatted value is: 2001:db8:1::1, 23, some text
+    /// for the option which carries IPv6 address, a number and a text.
+    std::string formatted_value_;
+
     /// @brief Constructor.
     ///
     /// @param opt option
     /// @param persist if true option is always sent.
-    OptionDescriptor(const OptionPtr& opt, bool persist)
-        : option_(opt), persistent_(persist) {};
+    /// @param formatted_value option value in the textual format. Default
+    /// value is empty indicating that the value is not set.
+    OptionDescriptor(const OptionPtr& opt, bool persist,
+                     const std::string& formatted_value = "")
+        : option_(opt), persistent_(persist),
+          formatted_value_(formatted_value) {};
 
     /// @brief Constructor
     ///
     /// @param persist if true option is always sent.
     OptionDescriptor(bool persist)
-        : option_(OptionPtr()), persistent_(persist) {};
+        : option_(OptionPtr()), persistent_(persist),
+          formatted_value_() {};
 
     /// @brief Checks if the one descriptor is equal to another.
     ///

+ 72 - 36
src/lib/dhcpsrv/mysql_host_data_source.cc

@@ -17,6 +17,8 @@
 #include <util/buffer.h>
 #include <util/optional_value.h>
 
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
 #include <boost/pointer_cast.hpp>
 #include <boost/static_assert.hpp>
 
@@ -83,15 +85,15 @@ TaggedStatement tagged_statements[] = {
 
     // Inserts a single DHCPv4 option into 'dhcp4_options' table.
     {MySqlHostDataSource::INSERT_V4_OPTION,
-         "INSERT INTO dhcp4_options(option_id, code, value, space, persistent, "
-            "dhcp_client_class, dhcp4_subnet_id, host_id) "
-         " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"},
+         "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
+            "persistent, dhcp_client_class, dhcp4_subnet_id, host_id) "
+         " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
 
     // Inserts a single DHCPv6 option into 'dhcp6_options' table.
     {MySqlHostDataSource::INSERT_V6_OPTION,
-         "INSERT INTO dhcp6_options(option_id, code, value, space, persistent, "
-            "dhcp_client_class, dhcp6_subnet_id, host_id) "
-         " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"},
+         "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
+            "persistent, dhcp_client_class, dhcp6_subnet_id, host_id) "
+         " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
 
     // Retrieves host information along with IPv6 reservations associated
     // with this host. If the host exists in multiple subnets, all hosts
@@ -789,8 +791,17 @@ private:
             if (most_recent_option_id_ < option_id_) {
                 most_recent_option_id_ = option_id_;
 
-                space_[space_length_] = '\0';
-                std::string space(space_);
+                std::string space;
+                if (space_null_ == MLM_FALSE) {
+                    space_[space_length_] = '\0';
+                    space.assign(space_);
+                }
+
+                std::string formatted_value;
+                if (formatted_value_null_ == MLM_FALSE) {
+                    formatted_value_[formatted_value_length_] = '\0';
+                    formatted_value.assign(formatted_value_);
+                }
 
                 OptionDefinitionPtr def;
                 if ((space == DHCP4_OPTION_SPACE) || (space == DHCP6_OPTION_SPACE)) {
@@ -809,17 +820,26 @@ private:
                     def = LibDHCP::getRuntimeOptionDef(space, code_);
                 }
 
+
                 OptionPtr option;
-                OptionBuffer buf(value_, value_ + value_length_);
-                if (def) {
-                    option = def->optionFactory(universe_, code_, buf.begin(),
-                                                buf.end());
-                } else {
+
+                if (!def) {
+                    OptionBuffer buf(value_, value_ + value_length_);
                     option.reset(new Option(universe_, code_, buf.begin(),
                                             buf.end()));
+                } else {
+                    if (formatted_value.empty()) {
+                        OptionBuffer buf(value_, value_ + value_length_);
+                        option = def->optionFactory(universe_, code_, buf.begin(),
+                                                    buf.end());
+                    } else {
+                        std::vector<std::string> split_vec;
+                        boost::split(split_vec, formatted_value, boost::is_any_of(","));
+                        option = def->optionFactory(universe_, code_, split_vec);
+                    }
                 }
 
-                OptionDescriptor desc(option, persistent_);
+                OptionDescriptor desc(option, persistent_, formatted_value);
                 cfg->add(desc, space);
             }
         }
@@ -1442,16 +1462,16 @@ private:
 class MySqlOptionExchange {
 private:
 
-    static const size_t OPTION_COLUMNS = 8;
+    static const size_t OPTION_COLUMNS = 9;
 
 public:
 
     MySqlOptionExchange()
-        : type_(0), value_len_(0), space_(), space_len_(0),
+        : type_(0), value_len_(0), formatted_value_len_(0), space_(), space_len_(0),
           persistent_(false), client_class_(), client_class_len_(0),
           subnet_id_(0), host_id_(0), option_() {
 
-        BOOST_STATIC_ASSERT(7 < OPTION_COLUMNS);
+        BOOST_STATIC_ASSERT(8 < OPTION_COLUMNS);
     }
 
 
@@ -1482,7 +1502,8 @@ public:
             // value: BLOB NULL
             OutputBuffer buf(opt_desc.option_->len());
             opt_desc.option_->pack(buf);
-            if (buf.getLength() > opt_desc.option_->getHeaderLen()) {
+            if ((buf.getLength() > opt_desc.option_->getHeaderLen()) &&
+                 opt_desc.formatted_value_.empty()) {
                 const char* buf_ptr = static_cast<const char*>(buf.getData());
                 value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
                               buf_ptr + buf.getLength());
@@ -1497,44 +1518,56 @@ public:
                 bind_[2].buffer_type = MYSQL_TYPE_NULL;
             }
 
+            // formatted_value: TEXT NULL,
+            if (!opt_desc.formatted_value_.empty()) {
+                formatted_value_len_ = opt_desc.formatted_value_.size();
+                bind_[3].buffer_type = MYSQL_TYPE_STRING;
+                bind_[3].buffer = const_cast<char*>(opt_desc.formatted_value_.c_str());
+                bind_[3].buffer_length = formatted_value_len_;
+                bind_[3].length = &formatted_value_len_;
+
+            } else {
+                bind_[3].buffer_type = MYSQL_TYPE_NULL;
+            }
+
             // space: VARCHAR(128) NULL
             space_ = opt_space;
             space_len_ = space_.size();
-            bind_[3].buffer_type = MYSQL_TYPE_STRING;
-            bind_[3].buffer = const_cast<char*>(space_.c_str());
-            bind_[3].buffer_length = space_len_;
-            bind_[3].length = &space_len_;
+            bind_[4].buffer_type = MYSQL_TYPE_STRING;
+            bind_[4].buffer = const_cast<char*>(space_.c_str());
+            bind_[4].buffer_length = space_len_;
+            bind_[4].length = &space_len_;
 
             // persistent: TINYINT(1) NOT NULL DEFAULT 0
             persistent_ = opt_desc.persistent_;
-            bind_[4].buffer_type = MYSQL_TYPE_TINY;
-            bind_[4].buffer = reinterpret_cast<char*>(&persistent_);
-            bind_[4].is_unsigned = MLM_TRUE;
+            bind_[5].buffer_type = MYSQL_TYPE_TINY;
+            bind_[5].buffer = reinterpret_cast<char*>(&persistent_);
+            bind_[5].is_unsigned = MLM_TRUE;
 
             // dhcp_client_class: VARCHAR(128) NULL
             /// @todo Assign actual value to client class string.
             client_class_len_ = client_class_.size();
-            bind_[5].buffer_type = MYSQL_TYPE_STRING;
-            bind_[5].buffer = const_cast<char*>(client_class_.c_str());
-            bind_[5].buffer_length = client_class_len_;
-            bind_[5].length = &client_class_len_;
+            bind_[6].buffer_type = MYSQL_TYPE_STRING;
+            bind_[6].buffer = const_cast<char*>(client_class_.c_str());
+            bind_[6].buffer_length = client_class_len_;
+            bind_[6].length = &client_class_len_;
 
             // dhcp4_subnet_id: INT UNSIGNED NULL
             if (subnet_id.isSpecified()) {
                 subnet_id_ = subnet_id;
-                bind_[6].buffer_type = MYSQL_TYPE_LONG;
-                bind_[6].buffer = reinterpret_cast<char*>(subnet_id_);
-                bind_[6].is_unsigned = MLM_TRUE;
+                bind_[7].buffer_type = MYSQL_TYPE_LONG;
+                bind_[7].buffer = reinterpret_cast<char*>(subnet_id_);
+                bind_[7].is_unsigned = MLM_TRUE;
 
             } else {
-                bind_[6].buffer_type = MYSQL_TYPE_NULL;
+                bind_[7].buffer_type = MYSQL_TYPE_NULL;
             }
 
             // host_id: INT UNSIGNED NOT NULL
             host_id_ = host_id;
-            bind_[7].buffer_type = MYSQL_TYPE_LONG;
-            bind_[7].buffer = reinterpret_cast<char*>(&host_id_);
-            bind_[7].is_unsigned = MLM_TRUE;
+            bind_[8].buffer_type = MYSQL_TYPE_LONG;
+            bind_[8].buffer = reinterpret_cast<char*>(&host_id_);
+            bind_[8].is_unsigned = MLM_TRUE;
 
         } catch (const std::exception& ex) {
             isc_throw(DbOperationError,
@@ -1554,6 +1587,9 @@ private:
 
     size_t value_len_;
 
+    /// Formatted option value length.
+    unsigned long formatted_value_len_;
+
     std::string space_;
 
     size_t space_len_;

+ 41 - 23
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -354,6 +354,7 @@ GenericHostDataSourceTest::compareOptions(const ConstCfgOptionPtr& cfg1,
         BOOST_FOREACH(OptionDescriptor desc1, *options1) {
             OptionDescriptor desc2 = cfg2->get(space, desc1.option_->getType());
             ASSERT_EQ(desc1.persistent_, desc2.persistent_);
+            ASSERT_EQ(desc1.formatted_value_, desc2.formatted_value_);
             Option* option1 = desc1.option_.get();
             Option* option2 = desc2.option_.get();
 
@@ -376,33 +377,49 @@ GenericHostDataSourceTest::compareOptions(const ConstCfgOptionPtr& cfg1,
     }
 }
 
+OptionDescriptor
+GenericHostDataSourceTest::createEmptyOption(const Option::Universe& universe,
+                                             const uint16_t option_type,
+                                             const bool persist) const {
+    OptionPtr option(new Option(universe, option_type));
+    OptionDescriptor desc(option, persist);
+    return (desc);
+}
+
 
 OptionDescriptor
 GenericHostDataSourceTest::createVendorOption(const Option::Universe& universe,
                                               const bool persist,
+                                              const bool formatted,
                                               const uint32_t vendor_id) const {
     OptionVendorPtr option(new OptionVendor(universe, vendor_id));
-    OptionDescriptor desc(option, persist);
+
+    std::ostringstream s;
+    if (formatted) {
+        s << vendor_id;
+    }
+
+    OptionDescriptor desc(option, persist, s.str());
     return (desc);
 }
 
 void
-GenericHostDataSourceTest::addTestOptions(const HostPtr& host) const {
+GenericHostDataSourceTest::addTestOptions(const HostPtr& host,
+                                          const bool formatted) const {
     // Add DHCPv4 options.
     CfgOptionPtr opts = host->getCfgOption4();
     opts->add(createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
-                                         true, "my-boot-file"),
+                                         true, formatted, "my-boot-file"),
               DHCP4_OPTION_SPACE);
     opts->add(createOption<OptionUint8>(Option::V4, DHO_DEFAULT_IP_TTL,
-                                        false, 64),
+                                        false, formatted, 64),
               DHCP4_OPTION_SPACE);
-    opts->add(createOption<OptionUint32>(Option::V4, 1, false, 312131),
+    opts->add(createOption<OptionUint32>(Option::V4, 1, false, formatted, 312131),
               "vendor-encapsulated-options");
-    opts->add(createAddressOption<Option4AddrLst>(254, false, "192.0.2.3"),
+    opts->add(createAddressOption<Option4AddrLst>(254, false, formatted, "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",
+    opts->add(createEmptyOption(Option::V4, 1, true), "isc");
+    opts->add(createAddressOption<Option4AddrLst>(2, false, formatted, "10.0.0.5",
                                                   "10.0.0.3", "10.0.3.4"),
               "isc");
 
@@ -424,18 +441,19 @@ GenericHostDataSourceTest::addTestOptions(const HostPtr& host) const {
     // Add DHCPv6 options.
     opts = host->getCfgOption6();
     opts->add(createOption<OptionString>(Option::V6, D6O_BOOTFILE_URL,
-                                         true, "my-boot-file"),
+                                         true, formatted, "my-boot-file"),
               DHCP6_OPTION_SPACE);
     opts->add(createOption<OptionUint32>(Option::V6, D6O_INFORMATION_REFRESH_TIME,
-                                         false, 3600),
+                                         false, formatted, 3600),
               DHCP6_OPTION_SPACE);
-    opts->add(createVendorOption(Option::V6, false, 2495), DHCP6_OPTION_SPACE);
-    opts->add(createAddressOption<Option6AddrLst>(1024, false, "2001:db8:1::1"),
+    opts->add(createVendorOption(Option::V6, false, formatted, 2495),
               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"),
+    opts->add(createAddressOption<Option6AddrLst>(1024, false, formatted,
+                                                  "2001:db8:1::1"),
+              DHCP6_OPTION_SPACE);
+    opts->add(createEmptyOption(Option::V6, 1, true), "isc2");
+    opts->add(createAddressOption<Option6AddrLst>(2, false, formatted, "3000::1",
+                                                  "3000::2", "3000::3"),
               "isc2");
 
     // Add definitions for DHCPv6 non-standard options.
@@ -1036,10 +1054,10 @@ void GenericHostDataSourceTest::testMultipleReservationsDifferentOrder(){
 
 }
 
-void GenericHostDataSourceTest::testOptionsReservations4() {
+void GenericHostDataSourceTest::testOptionsReservations4(const bool formatted) {
     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));
+    ASSERT_NO_THROW(addTestOptions(host, formatted));
     // Insert host and the options into respective tables.
     ASSERT_NO_THROW(hdsptr_->add(host));
     // Subnet id will be used in quries to the database.
@@ -1064,10 +1082,10 @@ void GenericHostDataSourceTest::testOptionsReservations4() {
     hdsptr_->get4(subnet_id, IOAddress("192.0.2.5"));
 }
 
-void GenericHostDataSourceTest::testOptionsReservations6() {
+void GenericHostDataSourceTest::testOptionsReservations6(const bool formatted) {
     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));
+    ASSERT_NO_THROW(addTestOptions(host, formatted));
     // 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.
@@ -1084,11 +1102,11 @@ void GenericHostDataSourceTest::testOptionsReservations6() {
     ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_addr));
 }
 
-void GenericHostDataSourceTest::testOptionsReservations46() {
+void GenericHostDataSourceTest::testOptionsReservations46(const bool formatted) {
     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));
+    ASSERT_NO_THROW(addTestOptions(host, formatted));
     // 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.

+ 53 - 7
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -12,8 +12,10 @@
 #include <dhcpsrv/host.h>
 #include <dhcp/classify.h>
 #include <dhcp/option.h>
+#include <boost/algorithm/string/join.hpp>
 #include <boost/shared_ptr.hpp>
 #include <gtest/gtest.h>
+#include <sstream>
 #include <vector>
 
 namespace isc {
@@ -134,6 +136,10 @@ public:
     void compareOptions(const ConstCfgOptionPtr& cfg1,
                         const ConstCfgOptionPtr& cfg2) const;
 
+    OptionDescriptor createEmptyOption(const Option::Universe& universe,
+                                       const uint16_t option_type,
+                                       const bool persist) const;
+
     /// @brief Creates an instance of the option for which it is possible to
     /// specify universe, option type and value in the constructor.
     ///
@@ -147,19 +153,31 @@ public:
     OptionDescriptor createOption(const Option::Universe& universe,
                                   const uint16_t option_type,
                                   const bool persist,
+                                  const bool formatted,
                                   const DataType& value) const {
         boost::shared_ptr<OptionType> option(new OptionType(universe, option_type,
                                                             value));
-        OptionDescriptor desc(option, persist);
+        std::ostringstream s;
+        if (formatted) {
+            s << value;
+        }
+        OptionDescriptor desc(option, persist, s.str());
         return (desc);
     }
 
     template<typename OptionType, typename DataType>
     OptionDescriptor createOption(const uint16_t option_type,
                                   const bool persist,
+                                  const bool formatted,
                                   const DataType& value) const {
         boost::shared_ptr<OptionType> option(new OptionType(option_type, value));
-        OptionDescriptor desc(option, persist);
+
+        std::ostringstream s;
+        if (formatted) {
+            s << value;
+        }
+
+        OptionDescriptor desc(option, persist, s.str());
         return (desc);
     }
 
@@ -167,29 +185,48 @@ public:
     OptionDescriptor
     createAddressOption(const uint16_t option_type,
                         const bool persist,
+                        const bool formatted,
                         const std::string& address1 = "",
                         const std::string& address2 = "",
                         const std::string& address3 = "") const {
+        std::ostringstream s;
         typename OptionType::AddressContainer addresses;
         if (!address1.empty()) {
             addresses.push_back(asiolink::IOAddress(address1));
+            if (formatted) {
+                s << address1;
+            }
         }
         if (!address2.empty()) {
             addresses.push_back(asiolink::IOAddress(address2));
+            if (formatted) {
+                if (s.tellp() != std::streampos(0)) {
+                    s << ",";
+                }
+                s << address2;
+            }
         }
         if (!address3.empty()) {
             addresses.push_back(asiolink::IOAddress(address3));
+            if (formatted) {
+                if (s.tellp() != std::streampos(0)) {
+                    s << ",";
+                }
+                s << address3;
+            }
         }
+
         boost::shared_ptr<OptionType> option(new OptionType(option_type, addresses));
-        OptionDescriptor desc(option, persist);
+        OptionDescriptor desc(option, persist, s.str());
         return (desc);
     }
 
     OptionDescriptor createVendorOption(const Option::Universe& universe,
                                         const bool persist,
+                                        const bool formatted,
                                         const uint32_t vendor_id) const;
 
-    void addTestOptions(const HostPtr& host) const;
+    void addTestOptions(const HostPtr& host, const bool formatted) const;
 
     /// @brief Pointer to the host data source
     HostDataSourcePtr hdsptr_;
@@ -309,19 +346,28 @@ public:
     /// the database.
     ///
     /// Uses gtest macros to report failures.
-    void testOptionsReservations4();
+    ///
+    /// @param formatted Boolean value indicating if the option values
+    /// should be stored in the textual format in the database.
+    void testOptionsReservations4(const bool formatted);
 
     /// @brief Test that DHCPv6 options can be inserted and retrieved from
     /// the database.
     ///
     /// Uses gtest macros to report failures.
-    void testOptionsReservations6();
+    ///
+    /// @param formatted Boolean value indicating if the option values
+    /// should be stored in the textual format in the database.
+    void testOptionsReservations6(const bool formatted);
 
     /// @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();
+    ///
+    /// @param formatted Boolean value indicating if the option values
+    /// should be stored in the textual format in the database.
+    void testOptionsReservations46(const bool formatted);
 
     /// @brief Returns DUID with identical content as specified HW address
     ///

+ 26 - 9
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

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