]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[421-create-config-backend-for-dhcpv6] Began update of MySQL CB for DHCPv6 421-create-config-backend-for-dhcpv6
authorFrancis Dupont <fdupont@isc.org>
Sun, 20 Jan 2019 16:17:08 +0000 (17:17 +0100)
committerFrancis Dupont <fdupont@isc.org>
Sun, 20 Jan 2019 16:17:08 +0000 (17:17 +0100)
15 files changed:
configure.ac
src/hooks/dhcp/Makefile.am
src/hooks/dhcp/mysql_cb/Makefile.am
src/hooks/dhcp/mysql_cb/mysql_cb_callouts.cc
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc [new file with mode: 0644]
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h [new file with mode: 0644]
src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc
src/hooks/dhcp/mysql_cb/mysql_cb_impl.h
src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h
src/hooks/dhcp/mysql_cb/tests/Makefile.am
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_mgr_unittest.cc [new file with mode: 0644]
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc [new file with mode: 0644]
src/share/database/scripts/mysql/dhcpdb_create.mysql

index a721b37dda7f41e1767058af7c03436e6c0d5722..54181319d09455dde44d768eb54ddf93630503fc 100644 (file)
@@ -1544,6 +1544,8 @@ AC_CONFIG_FILES([Makefile
                  src/hooks/dhcp/high_availability/tests/Makefile
                  src/hooks/dhcp/lease_cmds/Makefile
                  src/hooks/dhcp/lease_cmds/tests/Makefile
+                 src/hooks/dhcp/mysql_cb/Makefile
+                 src/hooks/dhcp/mysql_cb/tests/Makefile
                  src/hooks/dhcp/user_chk/Makefile
                  src/hooks/dhcp/user_chk/tests/Makefile
                  src/hooks/dhcp/user_chk/tests/test_data_files_config.h
index d8e33e26c86bb32a1251508ef5579190c6bfa2f1..902a3105ecdcf3e3b2a5a846427e7c8fb8a68047 100644 (file)
@@ -1 +1,5 @@
 SUBDIRS = high_availability lease_cmds stat_cmds user_chk
+
+if HAVE_MYSQL
+SUBDIRS += mysql_cb
+endif
index 9ebc47e7ac3ee44730b5baead7b579d7bfb94136..f28d0c244bf60a460c13768320205c7ee74881a6 100644 (file)
@@ -26,6 +26,7 @@ noinst_LTLIBRARIES = libmysqlcb.la
 
 libmysqlcb_la_SOURCES  = mysql_cb_callouts.cc
 libmysqlcb_la_SOURCES += mysql_cb_dhcp4.cc mysql_cb_dhcp4.h
+libmysqlcb_la_SOURCES += mysql_cb_dhcp6.cc mysql_cb_dhcp6.h
 libmysqlcb_la_SOURCES += mysql_cb_impl.cc mysql_cb_impl.h
 libmysqlcb_la_SOURCES += mysql_query_macros_dhcp.h
 libmysqlcb_la_SOURCES += version.cc
index 5a23969a8e5e349dc90f39be7dd073cc8b4d89a7..34bcd5f18b0343518c576a99d0c442e76aff139d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 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
@@ -11,6 +11,7 @@
 #include <config.h>
 #include <hooks/hooks.h>
 #include <mysql_cb_dhcp4.h>
+#include <mysql_cb_dhcp6.h>
 
 using namespace isc::hooks;
 
@@ -25,6 +26,7 @@ int load(LibraryHandle& /* handle */) {
 
     // Register MySQL CB factory with CB Manager
     isc::dhcp::MySqlConfigBackendDHCPv4::registerBackendType();
+    isc::dhcp::MySqlConfigBackendDHCPv6::registerBackendType();
 
     return (0);
 }
@@ -35,6 +37,7 @@ int load(LibraryHandle& /* handle */) {
 int unload() {
 
     // Unregister the factory and remove MySQL backends
+    isc::dhcp::MySqlConfigBackendDHCPv6::unregisterBackendType();
     isc::dhcp::MySqlConfigBackendDHCPv4::unregisterBackendType();
     return (0);
 }
index dfcff9c910b7cdc1de37d0241cdffcf191bc9196..3f852e676311525f9cad9b6435ffdc838c624156 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 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
@@ -148,7 +148,7 @@ public:
                 StampedValuePtr stamped_value(new StampedValue(out_bindings[1]->getString(),
                                                                out_bindings[2]->getString()));
                 stamped_value->setModificationTime(out_bindings[3]->getTimestamp());
-                parameters.push_back(stamped_value);
+                parameters.insert(stamped_value);
             }
         });
     }
@@ -252,7 +252,7 @@ public:
         // statement.
         MySqlBindingCollection out_bindings = {
             MySqlBinding::createInteger<uint32_t>(), // subnet_id
-            MySqlBinding::createString(SUBNET_PREFIX_BUF_LENGTH), // subnet_prefix
+            MySqlBinding::createString(SUBNET4_PREFIX_BUF_LENGTH), // subnet_prefix
             MySqlBinding::createString(DHCP4O6_INTERFACE_BUF_LENGTH), // 4o6_interface
             MySqlBinding::createString(DHCP4O6_INTERFACE_ID_BUF_LENGTH), // 4o6_interface_id
             MySqlBinding::createString(DHCP4O6_SUBNET_BUF_LENGTH), // 4o6_subnet
@@ -447,7 +447,7 @@ public:
                 (last_pool_option_id < out_bindings[25]->getInteger<uint64_t>())) {
                 last_pool_option_id = out_bindings[25]->getInteger<uint64_t>();
 
-                OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 25);
+                OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 25);
                 if (desc) {
                     last_pool->getCfgOption()->add(*desc, desc->space_name_);
                 }
@@ -458,7 +458,7 @@ public:
                 (last_option_id < out_bindings[37]->getInteger<uint64_t>())) {
                 last_option_id = out_bindings[37]->getInteger<uint64_t>();
 
-                OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 37);
+                OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 37);
                 if (desc) {
                     last_subnet->getCfgOption()->add(*desc, desc->space_name_);
                 }
@@ -616,7 +616,7 @@ public:
                 (last_pool_option_id < out_bindings[5]->getInteger<uint64_t>())) {
                 last_pool_option_id = out_bindings[5]->getInteger<uint64_t>();
 
-                OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 5);
+                OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 5);
                 if (desc) {
                     last_pool->getCfgOption()->add(*desc, desc->space_name_);
                 }
@@ -977,7 +977,7 @@ public:
                 (last_option_id < out_bindings[13]->getInteger<uint64_t>())) {
                 last_option_id = out_bindings[13]->getInteger<uint64_t>();
 
-                OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 13);
+                OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 13);
                 if (desc) {
                     last_network->getCfgOption()->add(*desc, desc->space_name_);
                 }
@@ -1481,7 +1481,7 @@ public:
             MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(code)),
             MySqlBinding::createString(space)
         };
-        getOptions(GET_OPTION4_CODE_SPACE, in_bindings, Option::V4, options);
+        getOptions4(GET_OPTION4_CODE_SPACE, in_bindings, Option::V4, options);
         return (options.empty() ? OptionDescriptorPtr() :
                 OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
     }
@@ -1499,8 +1499,8 @@ public:
             MySqlBindingCollection in_bindings = {
                 MySqlBinding::createString(tag)
             };
-            getOptions(MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4,
-                       in_bindings, Option::V4, options);
+            getOptions4(MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4,
+                        in_bindings, Option::V4, options);
         }
 
         return (options);
@@ -1522,8 +1522,8 @@ public:
                 MySqlBinding::createString(tag),
                 MySqlBinding::createTimestamp(modification_time)
             };
-            getOptions(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4,
-                       in_bindings, Option::V4, options);
+            getOptions4(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4,
+                        in_bindings, Option::V4, options);
         }
 
         return (options);
@@ -1558,8 +1558,8 @@ public:
             MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(code)),
             MySqlBinding::createString(space)
         };
-        getOptions(GET_OPTION4_SUBNET_ID_CODE_SPACE, in_bindings, Option::V4,
-                   options);
+        getOptions4(GET_OPTION4_SUBNET_ID_CODE_SPACE, in_bindings, Option::V4,
+                    options);
         return (options.empty() ? OptionDescriptorPtr() :
                 OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
     }
@@ -1593,8 +1593,8 @@ public:
             MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(code)),
             MySqlBinding::createString(space)
         };
-        getOptions(GET_OPTION4_POOL_ID_CODE_SPACE, in_bindings, Option::V4,
-                   options);
+        getOptions4(GET_OPTION4_POOL_ID_CODE_SPACE, in_bindings, Option::V4,
+                    options);
         return (options.empty() ? OptionDescriptorPtr() :
                 OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
     }
@@ -1629,8 +1629,8 @@ public:
             MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(code)),
             MySqlBinding::createString(space)
         };
-        getOptions(GET_OPTION4_SHARED_NETWORK_CODE_SPACE, in_bindings, Option::V4,
-                   options);
+        getOptions4(GET_OPTION4_SHARED_NETWORK_CODE_SPACE, in_bindings,
+                    Option::V4, options);
         return (options.empty() ? OptionDescriptorPtr() :
                 OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
     }
@@ -2019,33 +2019,32 @@ TaggedStatementArray tagged_statements = { {
 
     // Retrieves global option by code and space.
     { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE,
-      MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+      MYSQL_GET_OPTION4(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
     },
 
     // Retrieves all global options.
     { MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4,
-      MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 0)
+      MYSQL_GET_OPTION4(AND o.scope_id = 0)
     },
 
     // Retrieves modified options.
     { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4,
-      MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 0 AND o.modification_ts > ?)
+      MYSQL_GET_OPTION4(AND o.scope_id = 0 AND o.modification_ts > ?)
     },
 
     // Retrieves an option for a given subnet, option code and space.
     { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_SUBNET_ID_CODE_SPACE,
-      MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_GET_OPTION4(AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Retrieves an option for a given pool, option code and space.
     { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_POOL_ID_CODE_SPACE,
-      MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_GET_OPTION4(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Retrieves an option for a given shared network, option code and space.
     { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_SHARED_NETWORK_CODE_SPACE,
-      MYSQL_GET_OPTION(dhcp4,
-                       AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+      MYSQL_GET_OPTION4(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
     },
 
     // Insert global parameter.
@@ -2091,7 +2090,7 @@ TaggedStatementArray tagged_statements = { {
 
     // Insert pool for a subnet.
     { MySqlConfigBackendDHCPv4Impl::INSERT_POOL4,
-      MYSQL_INSERT_POOL(dhcp4)
+      MYSQL_INSERT_POOL4()
     },
 
     // Insert a shared network.
@@ -2128,7 +2127,7 @@ TaggedStatementArray tagged_statements = { {
 
     // Insert subnet specific option.
     { MySqlConfigBackendDHCPv4Impl::INSERT_OPTION4,
-      MYSQL_INSERT_OPTION(dhcp4)
+      MYSQL_INSERT_OPTION4()
     },
 
     // Insert association of the DHCP option with a server.
@@ -2190,24 +2189,22 @@ TaggedStatementArray tagged_statements = { {
 
     // Update existing global option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
-      MYSQL_UPDATE_OPTION(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
     },
 
     // Update existing subnet level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
-      MYSQL_UPDATE_OPTION(dhcp4,
-                          AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4(AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing pool level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID,
-      MYSQL_UPDATE_OPTION(dhcp4, AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing shared network level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SHARED_NETWORK,
-      MYSQL_UPDATE_OPTION(dhcp4,
-                          AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
     },
 
     // Delete global parameter by name.
@@ -2237,7 +2234,7 @@ TaggedStatementArray tagged_statements = { {
 
     // Delete pools for a subnet.
     { MySqlConfigBackendDHCPv4Impl::DELETE_POOLS4_SUBNET_ID,
-      MYSQL_DELETE_POOLS(dhcp4)
+      MYSQL_DELETE_POOLS4()
     },
 
     // Delete shared network by name.
diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc
new file mode 100644 (file)
index 0000000..14c8cf9
--- /dev/null
@@ -0,0 +1,2950 @@
+// Copyright (C) 2019 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 <mysql_cb_dhcp6.h>
+#include <mysql_cb_impl.h>
+#include <mysql_query_macros_dhcp.h>
+#include <cc/data.h>
+#include <asiolink/addr_utilities.h>
+#include <config_backend/constants.h>
+#include <database/db_exceptions.h>
+#include <dhcp/classify.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_data_types.h>
+#include <dhcp/option_space.h>
+#include <dhcpsrv/config_backend_dhcp6_mgr.h>
+#include <dhcpsrv/network.h>
+#include <dhcpsrv/pool.h>
+#include <dhcpsrv/lease.h>
+#include <util/buffer.h>
+#include <mysql/mysql_connection.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/pointer_cast.hpp>
+#include <mysql.h>
+#include <mysqld_error.h>
+#include <array>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+using namespace isc::cb;
+using namespace isc::db;
+using namespace isc::data;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Implementation of the MySQL Configuration Backend.
+class MySqlConfigBackendDHCPv6Impl : public MySqlConfigBackendImpl {
+public:
+
+    /// @brief Statement tags.
+    ///
+    /// The contents of the enum are indexes into the list of SQL statements.
+    /// It is assumed that the order is such that the indices of statements
+    /// reading the database are less than those of statements modifying the
+    /// database.
+    enum StatementIndex {
+        GET_GLOBAL_PARAMETER6,
+        GET_ALL_GLOBAL_PARAMETERS6,
+        GET_MODIFIED_GLOBAL_PARAMETERS6,
+        GET_SUBNET6_ID,
+        GET_SUBNET6_PREFIX,
+        GET_ALL_SUBNETS6,
+        GET_MODIFIED_SUBNETS6,
+        GET_POOL6_RANGE,
+        GET_PD_POOL,
+        GET_SHARED_NETWORK6_NAME,
+        GET_ALL_SHARED_NETWORKS6,
+        GET_MODIFIED_SHARED_NETWORKS6,
+        GET_OPTION_DEF6_CODE_SPACE,
+        GET_ALL_OPTION_DEFS6,
+        GET_MODIFIED_OPTION_DEFS6,
+        GET_OPTION6_CODE_SPACE,
+        GET_ALL_OPTIONS6,
+        GET_MODIFIED_OPTIONS6,
+        GET_OPTION6_SUBNET_ID_CODE_SPACE,
+        GET_OPTION6_POOL_ID_CODE_SPACE,
+        GET_OPTION6_PD_POOL_ID_CODE_SPACE,
+        GET_OPTION6_SHARED_NETWORK_CODE_SPACE,
+        INSERT_GLOBAL_PARAMETER6,
+        INSERT_GLOBAL_PARAMETER6_SERVER,
+        INSERT_SUBNET6,
+        INSERT_SUBNET6_SERVER,
+        INSERT_POOL6,
+        INSERT_PD_POOL,
+        INSERT_SHARED_NETWORK6,
+        INSERT_SHARED_NETWORK6_SERVER,
+        INSERT_OPTION_DEF6,
+        INSERT_OPTION_DEF6_SERVER,
+        INSERT_OPTION6,
+        INSERT_OPTION6_SERVER,
+        UPDATE_GLOBAL_PARAMETER6,
+        UPDATE_SUBNET6,
+        UPDATE_SHARED_NETWORK6,
+        UPDATE_OPTION_DEF6,
+        UPDATE_OPTION6,
+        UPDATE_OPTION6_SUBNET_ID,
+        UPDATE_OPTION6_POOL_ID,
+        UPDATE_OPTION6_PD_POOL_ID,
+        UPDATE_OPTION6_SHARED_NETWORK,
+        DELETE_GLOBAL_PARAMETER6,
+        DELETE_ALL_GLOBAL_PARAMETERS6,
+        DELETE_SUBNET6_ID,
+        DELETE_SUBNET6_PREFIX,
+        DELETE_ALL_SUBNETS6,
+        DELETE_POOLS6_SUBNET_ID,
+        DELETE_PD_POOLS_SUBNET_ID,
+        DELETE_SHARED_NETWORK6_NAME,
+        DELETE_ALL_SHARED_NETWORKS6,
+        DELETE_OPTION_DEF6_CODE_NAME,
+        DELETE_ALL_OPTION_DEFS6,
+        DELETE_OPTION6,
+        DELETE_OPTION6_SUBNET_ID,
+        DELETE_OPTION6_POOL_RANGE,
+        DELETE_OPTION6_PD_POOL,
+        DELETE_OPTION6_SHARED_NETWORK,
+        DELETE_OPTIONS6_SUBNET_ID,
+        DELETE_OPTIONS6_SHARED_NETWORK,
+        NUM_STATEMENTS
+    };
+
+    /// @brief Constructor.
+    ///
+    /// @param parameters A data structure relating keywords and values
+    /// concerned with the database.
+    explicit MySqlConfigBackendDHCPv6Impl(const DatabaseConnection::ParameterMap&
+                                          parameters);
+
+    /// @brief Sends query to retrieve multiple global parameters.
+    ///
+    /// @param index Index of the query to be used.
+    /// @param in_bindings Input bindings specifying selection criteria. The
+    /// size of the bindings collection must match the number of placeholders
+    /// in the prepared statement. The input bindings collection must be empty
+    /// if the query contains no WHERE clause.
+    /// @param [out] subnets Reference to the container where fetched parameters
+    /// will be inserted.
+    void getGlobalParameters6(const StatementIndex& index,
+                              const MySqlBindingCollection& in_bindings,
+                              StampedValueCollection& parameters) {
+        // The following parameters from the dhcp6_global_parameter table are
+        // returned:
+        // - id
+        // - name - parameter name
+        // - value - parameter value
+        // - modification_ts - modification timestamp.
+        MySqlBindingCollection out_bindings = {
+            MySqlBinding::createInteger<uint64_t>(), // id
+            MySqlBinding::createString(GLOBAL_PARAMETER_NAME_BUF_LENGTH), // name
+            MySqlBinding::createString(GLOBAL_PARAMETER_VALUE_BUF_LENGTH), // value
+            MySqlBinding::createTimestamp() // modification_ts
+        };
+
+        conn_.selectQuery(index, in_bindings, out_bindings,
+                          [&parameters]
+                          (MySqlBindingCollection& out_bindings) {
+            if (!out_bindings[1]->getString().empty()) {
+                StampedValuePtr stamped_value(new StampedValue(out_bindings[1]->getString(),
+                                                               out_bindings[2]->getString()));
+                stamped_value->setModificationTime(out_bindings[3]->getTimestamp());
+                parameters.insert(stamped_value);
+            }
+        });
+    }
+
+    /// @brief Sends query to retrieve global parameter.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Name of the parameter to be retrieved.
+    ///
+    /// @return Pointer to the retrieved value or null if such parameter
+    /// doesn't exist.
+    StampedValuePtr getGlobalParameter6(const ServerSelector& server_selector,
+                                        const std::string& name) {
+        StampedValueCollection parameters;
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createString(name)
+            };
+
+            getGlobalParameters6(GET_GLOBAL_PARAMETER6, in_bindings, parameters);
+        }
+
+        return (parameters.empty() ? StampedValuePtr() : *parameters.begin());
+    }
+
+    /// @brief Sends query to insert or update global parameter.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Name of the global parameter.
+    /// @param value Value of the global parameter.
+    void createUpdateGlobalParameter6(const db::ServerSelector& server_selector,
+                                      const StampedValuePtr& value) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "creating or updating global parameter");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(value->getName()),
+            MySqlBinding::createString(value->getValue()),
+            MySqlBinding::createTimestamp(value->getModificationTime()),
+            MySqlBinding::createString(tag),
+            MySqlBinding::createString(value->getName())
+        };
+
+        MySqlTransaction transaction(conn_);
+
+        // Try to update the existing row.
+        if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_GLOBAL_PARAMETER6,
+                                    in_bindings) == 0) {
+
+            // No such parameter found, so let's insert it. We have to adjust the
+            // bindings collection to match the prepared statement for insert.
+            in_bindings.pop_back();
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6,
+                              in_bindings);
+
+            // Successfully inserted global parameter. Now, we have to associate it
+            // with the server tag.
+
+            // Let's first get the primary key of the global parameter.
+            uint64_t id = mysql_insert_id(conn_.mysql_);
+
+            // Create bindings for inserting the association into
+            // dhcp6_global_parameter_server table.
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createInteger<uint64_t>(id), // parameter_id
+                MySqlBinding::createString(tag), // tag used to obtain server_id
+                MySqlBinding::createTimestamp(value->getModificationTime()), // modification_ts
+            };
+
+            // Insert association.
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6_SERVER,
+                              in_server_bindings);
+        }
+
+        transaction.commit();
+    }
+
+    /// @brief Sends query to the database to retrieve multiple subnets.
+    ///
+    /// Query should order subnets by subnet_id.
+    ///
+    /// @param index Index of the query to be used.
+    /// @param in_bindings Input bindings specifying selection criteria. The
+    /// size of the bindings collection must match the number of placeholders
+    /// in the prepared statement. The input bindings collection must be empty
+    /// if the query contains no WHERE clause.
+    /// @param [out] subnets Reference to the container where fetched subnets
+    /// will be inserted.
+    void getSubnets6(const StatementIndex& index,
+                     const MySqlBindingCollection& in_bindings,
+                     Subnet6Collection& subnets) {
+        // Create output bindings. The order must match that in the prepared
+        // statement.
+        MySqlBindingCollection out_bindings = {
+            MySqlBinding::createInteger<uint32_t>(), // subnet_id
+            MySqlBinding::createString(SUBNET6_PREFIX_BUF_LENGTH), // subnet_prefix
+            MySqlBinding::createString(CLIENT_CLASS_BUF_LENGTH), // client_class
+            MySqlBinding::createString(INTERFACE_BUF_LENGTH), // interface
+            MySqlBinding::createTimestamp(), // modification_ts
+            MySqlBinding::createInteger<uint32_t>(), // preferred_lifetime
+            MySqlBinding::createInteger<uint8_t>(), // rapid_commit
+            MySqlBinding::createInteger<uint32_t>(), // rebind_timer
+            MySqlBinding::createString(RELAY_BUF_LENGTH), // relay
+            MySqlBinding::createInteger<uint32_t>(), // renew_timer
+            MySqlBinding::createString(REQUIRE_CLIENT_CLASSES_BUF_LENGTH), // require_client_classes
+            MySqlBinding::createInteger<uint8_t>(), // reservation_mode
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // shared_network_name
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context
+            MySqlBinding::createInteger<uint32_t>(), // valid_lifetime
+            MySqlBinding::createInteger<uint64_t>(), // pool: id
+            MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: start_address
+            MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: end_address
+            MySqlBinding::createInteger<uint32_t>(), // pool: dhcp6_subnet_id
+            MySqlBinding::createTimestamp(), // pool: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // pd pool: id
+            MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pd pool: prefix
+            MySqlBinding::createInteger<uint8_t>(), // pd pool: prefix_length
+            MySqlBinding::createInteger<uint8_t>(), // pd pool: delegated_prefix_length
+            MySqlBinding::createInteger<uint32_t>(), // pd pool: dhcp6_subnet_id
+            MySqlBinding::createTimestamp(), // pd pool: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+            MySqlBinding::createInteger<uint16_t>(), // pool option: code
+            MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pool option: value
+            MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pool option: formatted_value
+            MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pool option: space
+            MySqlBinding::createInteger<uint8_t>(), // pool option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp6_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // pool option: scope_id
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pool option: user_context
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pool option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pool_id
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pd_pool_id
+            MySqlBinding::createTimestamp(), // pool option: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // pd pool option: option_id
+            MySqlBinding::createInteger<uint16_t>(), // pd pool option: code
+            MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pd pool option: value
+            MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pd pool option: formatted_value
+            MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pd pool option: space
+            MySqlBinding::createInteger<uint8_t>(), // pd pool option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // pd pool option: dhcp6_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // pd pool option: scope_id
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pd pool option: user_context
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pd pool option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // pd pool option: pool_id
+            MySqlBinding::createInteger<uint64_t>(), // pd pool option: pd_pool_id
+            MySqlBinding::createTimestamp(), // pd pool option: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // option: option_id
+            MySqlBinding::createInteger<uint16_t>(), // option: code
+            MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // option: value
+            MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // option: formatted_value
+            MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // option: space
+            MySqlBinding::createInteger<uint8_t>(), // option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // option: dhcp6_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // option: scope_id
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // option: user_context
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // option: pool_id
+            MySqlBinding::createInteger<uint64_t>(), // option: pd_pool_id
+            MySqlBinding::createTimestamp() // option: modification_ts
+        };
+
+        uint64_t last_pool_id = 0;
+        uint64_t last_pd_pool_id = 0;
+        uint64_t last_pool_option_id = 0;
+        uint64_t last_pd_pool_option_id = 0;
+        uint64_t last_option_id = 0;
+
+        Pool6Ptr last_pool;
+        Pool6Ptr last_pd_pool;
+
+        // Execute actual query.
+        conn_.selectQuery(index, in_bindings, out_bindings,
+                          [this, &subnets, &last_pool, &last_pd_pool,
+                           &last_pool_id, &last_pd_pool_id,
+                           &last_pool_option_id, &last_pd_pool_option_id,
+                           &last_option_id]
+                          (MySqlBindingCollection& out_bindings) {
+            // Get pointer to the last subnet in the collection.
+            Subnet6Ptr last_subnet;
+            if (!subnets.empty()) {
+                last_subnet = *subnets.rbegin();
+            }
+
+            // Subnet has been returned. Assuming that subnets are ordered by
+            // subnet identifier, if the subnet identifier of the current row
+            // is different than the subnet identifier of the previously returned
+            // row, it means that we have to construct new subnet object.
+            if (!last_subnet || (last_subnet->getID() != out_bindings[0]->getInteger<uint32_t>())) {
+
+                // Reset pool id, because current row defines new subnet. Subsequent
+                // rows will contain pool information.
+                last_pool_id = 0;
+
+                // subnet_id
+                SubnetID subnet_id(out_bindings[0]->getInteger<uint32_t>());
+
+                // subnet_prefix
+                std::string subnet_prefix = out_bindings[1]->getString();
+                auto prefix_pair = Subnet6::parsePrefix(subnet_prefix);
+
+                // preferred lifetime
+                uint32_t preferred_lifetime = out_bindings[5]->getIntegerOrDefault<uint32_t>(0);
+
+                // renew_timer
+                uint32_t renew_timer = out_bindings[9]->getIntegerOrDefault<uint32_t>(0);
+
+                // rebind_timer
+                uint32_t rebind_timer = out_bindings[7]->getIntegerOrDefault<uint32_t>(0);
+
+                // valid_lifetime
+                uint32_t valid_lifetime = out_bindings[14]->getIntegerOrDefault<uint32_t>(0);
+
+                // Create subnet with basic settings.
+                last_subnet.reset(new Subnet6(prefix_pair.first,
+                                              prefix_pair.second,
+                                              renew_timer, rebind_timer,
+                                              preferred_lifetime,
+                                              valid_lifetime, subnet_id));
+
+                // client_class
+                if (!out_bindings[2]->amNull()) {
+                    last_subnet->allowClientClass(out_bindings[2]->getString());
+                }
+
+                // interface
+                last_subnet->setIface(out_bindings[3]->getStringOrDefault(""));
+
+                // modification_ts
+                last_subnet->setModificationTime(out_bindings[4]->getTimestamp());
+                // rapid_commit
+                last_subnet->setRapidCommit(static_cast<bool>
+                                            (out_bindings[6]->getIntegerOrDefault<uint8_t>(0)));
+
+                // relay
+                ElementPtr relay_element = out_bindings[8]->getJSON();
+                if (relay_element) {
+                    if (relay_element->getType() != Element::list) {
+                        isc_throw(BadValue, "invalid relay value "
+                                  << out_bindings[8]->getString());
+                    }
+                    for (auto i = 0; i < relay_element->size(); ++i) {
+                        auto relay_address_element = relay_element->get(i);
+                        if (relay_address_element->getType() != Element::string) {
+                            isc_throw(BadValue, "relay address must be a string");
+                        }
+                        last_subnet->addRelayAddress(IOAddress(relay_element->get(i)->stringValue()));
+                    }
+                }
+
+                // require_client_classes
+                ElementPtr require_element = out_bindings[10]->getJSON();
+                if (require_element) {
+                    if (require_element->getType() != Element::list) {
+                        isc_throw(BadValue, "invalid require_client_classes value "
+                                  << out_bindings[10]->getString());
+                    }
+                    for (auto i = 0; i < require_element->size(); ++i) {
+                        auto require_item = require_element->get(i);
+                        if (require_item->getType() != Element::string) {
+                            isc_throw(BadValue, "elements of require_client_classes list must"
+                                      "be valid strings");
+                        }
+                        last_subnet->requireClientClass(require_item->stringValue());
+                    }
+                }
+
+                // reservation_mode
+                last_subnet->setHostReservationMode(static_cast<Subnet6::HRMode>
+                    (out_bindings[11]->getIntegerOrDefault<uint8_t>(Subnet6::HR_ALL)));
+
+                // shared_network_name
+                last_subnet->setSharedNetworkName(out_bindings[12]->getStringOrDefault(""));
+
+                // user_context
+                ElementPtr user_context = out_bindings[13]->getJSON();
+                if (user_context) {
+                    last_subnet->setContext(user_context);
+                }
+
+                // Subnet ready. Add it to the list.
+                subnets.push_back(last_subnet);
+            }
+
+            // If the row contains information about the pool and it
+            // appears to be new pool entry (checked by comparing pool
+            // id), let's create the new pool and add it to the subnet.
+            if (!out_bindings[15]->amNull() &&
+                !out_bindings[16]->getString().empty() &&
+                !out_bindings[17]->getString().empty() &&
+                (out_bindings[15]->getInteger<uint64_t>() > last_pool_id)) {
+                last_pool_id = out_bindings[15]->getInteger<uint64_t>();
+                last_pool.reset(new Pool6(Lease::TYPE_NA,
+                                          IOAddress(out_bindings[16]->getString()),
+                                          IOAddress(out_bindings[17]->getString())));
+                last_subnet->addPool(last_pool);
+            }
+
+            // If the row contains information about the pd pool and it
+            // appears to be new pd pool entry (checked by comparing pd pool
+            // id), let's create the new pd pool and add it to the subnet.
+            if (!out_bindings[20]->amNull() &&
+                !out_bindings[21]->getString().empty() &&
+                (out_bindings[22]->getInteger<uint8_t>() != 0) &&
+                (out_bindings[23]->getInteger<uint8_t>() != 0) &&
+                (out_bindings[20]->getInteger<uint64_t>() > last_pd_pool_id)) {
+                last_pd_pool_id = out_bindings[20]->getInteger<uint64_t>();
+                last_pd_pool.reset(new Pool6(Lease::TYPE_PD,
+                                             IOAddress(out_bindings[21]->getString()),
+                                             out_bindings[22]->getInteger<uint8_t>(),
+                                             out_bindings[23]->getInteger<uint8_t>()));
+                last_subnet->addPool(last_pd_pool);
+            }
+
+            // Parse pool specific option.
+            if (last_pool && !out_bindings[26]->amNull() &&
+                (last_pool_option_id < out_bindings[26]->getInteger<uint64_t>())) {
+                last_pool_option_id = out_bindings[26]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 26);
+                if (desc) {
+                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+
+            // Parse pd pool specific option.
+            if (last_pd_pool && !out_bindings[39]->amNull() &&
+                (last_pd_pool_option_id < out_bindings[39]->getInteger<uint64_t>())) {
+                last_pd_pool_option_id = out_bindings[39]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 39);
+                if (desc) {
+                    last_pd_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+
+            // Parse subnet specific option.
+            if (!out_bindings[52]->amNull() &&
+                (last_option_id < out_bindings[52]->getInteger<uint64_t>())) {
+                last_option_id = out_bindings[52]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 52);
+                if (desc) {
+                    last_subnet->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+
+        });
+    }
+
+    /// @brief Sends query to retrieve single subnet by id.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Subnet identifier.
+    ///
+    /// @return Pointer to the returned subnet or NULL if such subnet
+    /// doesn't exist.
+    Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
+                          const SubnetID& subnet_id) {
+        Subnet6Collection subnets;
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createInteger<uint32_t>(subnet_id)
+            };
+
+            getSubnets6(GET_SUBNET6_ID, in_bindings, subnets);
+        }
+
+        return (subnets.empty() ? Subnet6Ptr() : *subnets.begin());
+    }
+
+    /// @brief Sends query to retrieve single subnet by prefix.
+    ///
+    /// The prefix should be in the following format: "2001:db8:1::/64".
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Subnet identifier.
+    ///
+    /// @return Pointer to the returned subnet or NULL if such subnet
+    /// doesn't exist.
+    Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
+                          const std::string& subnet_prefix) {
+        Subnet6Collection subnets;
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createString(subnet_prefix)
+            };
+
+            getSubnets6(GET_SUBNET6_PREFIX, in_bindings, subnets);
+        }
+
+        return (subnets.empty() ? Subnet6Ptr() : *subnets.begin());
+    }
+
+    /// @brief Sends query to retrieve all subnets.
+    ///
+    /// @param server_selector Server selector.
+    /// @param [out] subnets Reference to the subnet collection structure where
+    /// subnets should be inserted.
+    void getAllSubnets6(const ServerSelector& server_selector,
+                        Subnet6Collection& subnets) {
+        auto tags = getServerTags(server_selector);
+
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag)
+            };
+
+            getSubnets6(GET_ALL_SUBNETS6, in_bindings, subnets);
+        }
+    }
+
+    /// @brief Sends query to retrieve modified subnets.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_ts Lower bound modification timestamp.
+    /// @param [out] subnets Reference to the subnet collection structure where
+    /// subnets should be inserted.
+    void getModifiedSubnets6(const ServerSelector& server_selector,
+                             const boost::posix_time::ptime& modification_ts,
+                             Subnet6Collection& subnets) {
+        auto tags = getServerTags(server_selector);
+
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createTimestamp(modification_ts)
+            };
+
+            getSubnets6(GET_MODIFIED_SUBNETS6, in_bindings, subnets);
+        }
+    }
+
+    /// @brief Sends query to retrieve multiple pools.
+    ///
+    /// Query should order pools by id.
+    ///
+    /// @param index Index of the query to be used.
+    /// @param in_bindings Input bindings specifying selection criteria. The
+    /// size of the bindings collection must match the number of placeholders
+    /// in the prepared statement. The input bindings collection must be empty
+    /// if the query contains no WHERE clause.
+    /// @param [out] pools Reference to the container where fetched pools
+    /// will be inserted.
+    /// @param [out] pool_ids Identifiers of the pools returned in @c pools
+    /// argument.
+    void getPools(const StatementIndex& index,
+                  const MySqlBindingCollection& in_bindings,
+                  PoolCollection& pools,
+                  std::vector<uint64_t>& pool_ids) {
+        MySqlBindingCollection out_bindings = {
+            MySqlBinding::createInteger<uint64_t>(), // pool: id
+            MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: start_address
+            MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: end_address
+            MySqlBinding::createInteger<uint32_t>(), // pool: dhcp6_subnet_id
+            MySqlBinding::createTimestamp(), // pool: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+            MySqlBinding::createInteger<uint16_t>(), // pool option: code
+            MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pool option: value
+            MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pool option: formatted_value
+            MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pool option: space
+            MySqlBinding::createInteger<uint8_t>(), // pool option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp6_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // pool option: scope_id
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pool option: user_context
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pool option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pool_id
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pd_pool_id
+            MySqlBinding::createTimestamp(), //pool option: modification_ts
+        };
+
+        uint64_t last_pool_id = 0;
+        uint64_t last_pool_option_id = 0;
+        Pool6Ptr last_pool;
+
+        conn_.selectQuery(index, in_bindings, out_bindings,
+                          [this, &last_pool_id, &last_pool_option_id, &last_pool,
+                           &pools, &pool_ids]
+                          (MySqlBindingCollection& out_bindings) {
+            if (out_bindings[0]->getInteger<uint64_t>() > last_pool_id) {
+
+                last_pool_id = out_bindings[0]->getInteger<uint64_t>();
+
+                last_pool.reset(new Pool6(Lease::TYPE_NA,
+                                          IOAddress(out_bindings[1]->getString()),
+                                          IOAddress(out_bindings[2]->getString())));
+                pools.push_back(last_pool);
+                pool_ids.push_back(last_pool_id);
+            }
+
+            // Parse pool specific option.
+            if (last_pool && !out_bindings[5]->amNull() &&
+                (last_pool_option_id < out_bindings[5]->getInteger<uint64_t>())) {
+                last_pool_option_id = out_bindings[5]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 5);
+                if (desc) {
+                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
+    }
+
+    /// @brief Sends query to retrieve multiple pd pools.
+    ///
+    /// Query should order pools by id.
+    ///
+    /// @param index Index of the query to be used.
+    /// @param in_bindings Input bindings specifying selection criteria. The
+    /// size of the bindings collection must match the number of placeholders
+    /// in the prepared statement. The input bindings collection must be empty
+    /// if the query contains no WHERE clause.
+    /// @param [out] pools Reference to the container where fetched pools
+    /// will be inserted.
+    /// @param [out] pool_ids Identifiers of the pools returned in @c pools
+    /// argument.
+    void getPdPools(const StatementIndex& index,
+                    const MySqlBindingCollection& in_bindings,
+                    PoolCollection& pools,
+                    std::vector<uint64_t>& pool_ids) {
+        MySqlBindingCollection out_bindings = {
+            MySqlBinding::createInteger<uint64_t>(), // pool: id
+            MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: prefix
+            MySqlBinding::createInteger<uint8_t>(), // pool: prefix_length
+            MySqlBinding::createInteger<uint8_t>(), // pool: delegated_prefix_length
+            MySqlBinding::createInteger<uint32_t>(), // pool: dhcp6_subnet_id
+            MySqlBinding::createTimestamp(), // pool: modification_ts
+            MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+            MySqlBinding::createInteger<uint16_t>(), // pool option: code
+            MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pool option: value
+            MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pool option: formatted_value
+            MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pool option: space
+            MySqlBinding::createInteger<uint8_t>(), // pool option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp6_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // pool option: scope_id
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pool option: user_context
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pool option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pool_id
+            MySqlBinding::createInteger<uint64_t>(), // pool option: pd_pool_id
+            MySqlBinding::createTimestamp(), //pool option: modification_ts
+        };
+
+        uint64_t last_pool_id = 0;
+        uint64_t last_pool_option_id = 0;
+        Pool6Ptr last_pool;
+
+        conn_.selectQuery(index, in_bindings, out_bindings,
+                          [this, &last_pool_id, &last_pool_option_id, &last_pool,
+                           &pools, &pool_ids]
+                          (MySqlBindingCollection& out_bindings) {
+            if (out_bindings[0]->getInteger<uint64_t>() > last_pool_id) {
+
+                last_pool_id = out_bindings[0]->getInteger<uint64_t>();
+
+                last_pool.reset(new Pool6(Lease::TYPE_PD,
+                                          IOAddress(out_bindings[1]->getString()),
+                                          out_bindings[2]->getInteger<uint8_t>(),
+                                          out_bindings[3]->getInteger<uint8_t>()));
+                pools.push_back(last_pool);
+                pool_ids.push_back(last_pool_id);
+            }
+
+            // Parse pool specific option.
+            if (last_pool && !out_bindings[6]->amNull() &&
+                (last_pool_option_id < out_bindings[6]->getInteger<uint64_t>())) {
+                last_pool_option_id = out_bindings[6]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 6);
+                if (desc) {
+                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
+    }
+
+    /// @brief Sends query to retrieve single pool by address range.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool_start_address Lower bound pool address.
+    /// @param pool_end_address Upper bound pool address.
+    /// @param pool_id Pool identifier for the returned pool.
+    /// @return Pointer to the pool or null if no such pool found.
+    Pool6Ptr getPool6(const ServerSelector& /* server_selector */,
+                      const IOAddress& pool_start_address,
+                      const IOAddress& pool_end_address,
+                      uint64_t& pool_id) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(pool_start_address.toText()),
+            MySqlBinding::createString(pool_end_address.toText())
+        };
+
+        PoolCollection pools;
+        std::vector<uint64_t> pool_ids;
+        getPools(GET_POOL6_RANGE, in_bindings, pools, pool_ids);
+
+        if (!pools.empty()) {
+            pool_id = pool_ids[0];
+            return (boost::dynamic_pointer_cast<Pool6>(*pools.begin()));
+        }
+
+        pool_id = 0;
+
+        return (Pool6Ptr());
+    }
+
+    /// @brief Sends query to retrieve single pd pool.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pd_pool_prefix Address part of the pd pool prefix.
+    /// @param pd_pool_prefix_length Length of the pd pool prefix.
+    /// @param pool_end_address Upper bound pool address.
+    /// @param pool_id Pool identifier for the returned pool.
+    /// @return Pointer to the pool or null if no such pool found.
+    Pool6Ptr getPdPool6(const ServerSelector& /* server_selector */,
+                        const asiolink::IOAddress& pd_pool_prefix,
+                        const uint8_t pd_pool_prefix_length,
+                        uint64_t& pool_id) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(pd_pool_prefix.toText()),
+            MySqlBinding::createInteger<uint8_t>(pd_pool_prefix_length)
+        };
+
+        PoolCollection pools;
+        std::vector<uint64_t> pool_ids;
+        getPdPools(GET_PD_POOL, in_bindings, pools, pool_ids);
+
+        if (!pools.empty()) {
+            pool_id = pool_ids[0];
+            return (boost::dynamic_pointer_cast<Pool6>(*pools.begin()));
+        }
+
+        pool_id = 0;
+
+        return (Pool6Ptr());
+    }
+
+    /// @brief Sends query to insert or update subnet.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet Pointer to the subnet to be inserted or updated.
+    void createUpdateSubnet6(const ServerSelector& server_selector,
+                             const Subnet6Ptr& subnet) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "creating or updating subnet");
+
+        // Create JSON list of required classes.
+        ElementPtr required_classes_element = Element::createList();
+        const auto& required_classes = subnet->getRequiredClasses();
+        for (auto required_class = required_classes.cbegin();
+             required_class != required_classes.cend();
+             ++required_class) {
+            required_classes_element->add(Element::create(*required_class));
+        }
+
+        // Create binding with shared network name if the subnet belongs to a
+        // shared network.
+        SharedNetwork6Ptr shared_network;
+        subnet->getSharedNetwork(shared_network);
+        MySqlBindingPtr shared_network_binding =
+            (shared_network ? MySqlBinding::createString(shared_network->getName()) :
+             MySqlBinding::createNull());
+
+        // Create input bindings.
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint32_t>(subnet->getID()),
+            MySqlBinding::createString(subnet->toText()),
+            MySqlBinding::condCreateString(subnet->getClientClass()),
+            MySqlBinding::condCreateString(subnet->getIface()),
+            MySqlBinding::createTimestamp(subnet->getModificationTime()),
+            MySqlBinding::createInteger<uint32_t>(subnet->getPreferred()),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getRapidCommit())),
+            MySqlBinding::createInteger<uint32_t>(subnet->getT2()),
+            createInputRelayBinding(subnet),
+            MySqlBinding::createInteger<uint32_t>(subnet->getT1()),
+            createInputRequiredClassesBinding(subnet),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getHostReservationMode())),
+            shared_network_binding,
+            createInputContextBinding(subnet),
+            MySqlBinding::createInteger<uint32_t>(subnet->getValid())
+        };
+
+        MySqlTransaction transaction(conn_);
+
+        try {
+            // Try to insert subnet. If this duplicates primary key, i.e. this
+            // subnet already exists it will throw DuplicateEntry exception in
+            // which case we'll try an update.
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6,
+                              in_bindings);
+
+            // Create bindings for inserting the association into
+            // dhcp6_subnet_server table.
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createInteger<uint32_t>(subnet->getID()), // subnet_id
+                MySqlBinding::createString(tag), // tag used to obtain server_id
+                MySqlBinding::createTimestamp(subnet->getModificationTime()), // modification_ts
+            };
+
+            // Insert association.
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER,
+                              in_server_bindings);
+
+
+        } catch (const DuplicateEntry&) {
+            deletePools6(subnet);
+            deletePdPools6(subnet);
+            deleteOptions6(server_selector, subnet);
+
+            // Need to add one more binding for WHERE clause.
+            in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(subnet->getID()));
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6,
+                                    in_bindings);
+        }
+
+        // (Re)create pools.
+        for (auto pool : subnet->getPools(Lease::TYPE_NA)) {
+            createPool6(server_selector, boost::dynamic_pointer_cast<Pool6>(pool),
+                        subnet);
+        }
+
+        // (Re)create pd pools.
+        for (auto pool : subnet->getPools(Lease::TYPE_PD)) {
+            createPdPool6(server_selector,
+                          boost::dynamic_pointer_cast<Pool6>(pool),
+                          subnet);
+        }
+
+        // (Re)create options.
+        auto option_spaces = subnet->getCfgOption()->getOptionSpaceNames();
+        for (auto option_space : option_spaces) {
+            OptionContainerPtr options = subnet->getCfgOption()->getAll(option_space);
+            for (auto desc = options->begin(); desc != options->end(); ++desc) {
+                OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, subnet->getID(), desc_copy);
+            }
+        }
+
+        transaction.commit();
+    }
+
+    /// @brief Inserts new IPv6 pool to the database.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool Pointer to the pool to be inserted.
+    /// @param subnet Pointer to the subnet that this pool belongs to.
+    void createPool6(const ServerSelector& server_selector, const Pool6Ptr& pool,
+                     const Subnet6Ptr& subnet) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(pool->getFirstAddress().toText()),
+            MySqlBinding::createString(pool->getLastAddress().toText()),
+            MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet->getID())),
+            MySqlBinding::createTimestamp(subnet->getModificationTime())
+        };
+
+        // Run INSERT.
+        conn_.insertQuery(INSERT_POOL6, in_bindings);
+
+        uint64_t pool_id = mysql_insert_id(conn_.mysql_);
+        auto option_spaces = pool->getCfgOption()->getOptionSpaceNames();
+        for (auto option_space : option_spaces) {
+            OptionContainerPtr options = pool->getCfgOption()->getAll(option_space);
+            for (auto desc = options->begin(); desc != options->end(); ++desc) {
+                OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, Lease::TYPE_NA,
+                                    pool_id, desc_copy);
+            }
+        }
+    }
+
+    /// @brief Inserts new IPv6 pd pool to the database.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool Pointer to the pd pool to be inserted.
+    /// @param subnet Pointer to the subnet that this pool belongs to.
+    void createPdPool6(const ServerSelector& server_selector,
+                       const Pool6Ptr& pool,
+                       const Subnet6Ptr& subnet) {
+        int plen = prefixLengthFromRange(pool->getFirstAddress(),
+                                         pool->getLastAddress());
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(pool->getFirstAddress().toText()),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(plen)),
+            MySqlBinding::createInteger<uint8_t>(pool->getLength()),
+            MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet->getID())),
+            MySqlBinding::createTimestamp(subnet->getModificationTime())
+        };
+
+        // Run INSERT.
+        conn_.insertQuery(INSERT_PD_POOL, in_bindings);
+
+        uint64_t pool_id = mysql_insert_id(conn_.mysql_);
+        auto option_spaces = pool->getCfgOption()->getOptionSpaceNames();
+        for (auto option_space : option_spaces) {
+            OptionContainerPtr options = pool->getCfgOption()->getAll(option_space);
+            for (auto desc = options->begin(); desc != options->end(); ++desc) {
+                OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, Lease::TYPE_PD,
+                                    pool_id, desc_copy);
+            }
+        }
+    }
+
+    /// @brief Sends query to delete subnet by id.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Identifier of the subnet to be deleted.
+    /// @return Number of deleted subnets.
+    uint64_t deleteSubnet6(const ServerSelector& server_selector,
+                           const SubnetID& subnet_id) {
+        return (deleteFromTable<uint32_t>(DELETE_SUBNET6_ID, server_selector,
+                                          "deleting a subnet",
+                                          subnet_id));
+    }
+
+    /// @brief Deletes pools belonging to a subnet from the database.
+    ///
+    /// @param subnet Pointer to the subnet for which pools should be
+    /// deleted.
+    uint64_t deletePools6(const Subnet6Ptr& subnet) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint32_t>(subnet->getID())
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_POOLS6_SUBNET_ID, in_bindings));
+    }
+
+    /// @brief Deletes pd pools belonging to a subnet from the database.
+    ///
+    /// @param subnet Pointer to the subnet for which pd pools should be
+    /// deleted.
+    uint64_t deletePdPools6(const Subnet6Ptr& subnet) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint32_t>(subnet->getID())
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_PD_POOLS_SUBNET_ID, in_bindings));
+    }
+
+    /// @brief Sends query to the database to retrieve multiple shared
+    /// networks.
+    ///
+    /// Query should order shared networks by id.
+    ///
+    /// @param index Index of the query to be used.
+    /// @param in_bindings Input bindings specifying selection criteria. The
+    /// size of the bindings collection must match the number of placeholders
+    /// in the prepared statement. The input bindings collection must be empty
+    /// if the query contains no WHERE clause.
+    /// @param [out] shared_networks Reference to the container where fetched
+    /// shared networks will be inserted.
+    void getSharedNetworks6(const StatementIndex& index,
+                            const MySqlBindingCollection& in_bindings,
+                            SharedNetwork6Collection& shared_networks) {
+        // Create output bindings. The order must match that in the prepared
+        // statement.
+        MySqlBindingCollection out_bindings = {
+            MySqlBinding::createInteger<uint64_t>(), // id
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // name
+            MySqlBinding::createString(CLIENT_CLASS_BUF_LENGTH), // client_class
+            MySqlBinding::createString(INTERFACE_BUF_LENGTH), // interface
+            MySqlBinding::createTimestamp(), // modification_ts
+            MySqlBinding::createInteger<uint32_t>(), // preferred_lifetime
+            MySqlBinding::createInteger<uint8_t>(), // rapid_commit
+            MySqlBinding::createInteger<uint32_t>(), // rebind_timer
+            MySqlBinding::createString(RELAY_BUF_LENGTH), // relay
+            MySqlBinding::createInteger<uint32_t>(), // renew_timer
+            MySqlBinding::createString(REQUIRE_CLIENT_CLASSES_BUF_LENGTH), // require_client_classes
+            MySqlBinding::createInteger<uint8_t>(), // reservation_mode
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context
+            MySqlBinding::createInteger<uint32_t>(), // valid_lifetime
+            MySqlBinding::createInteger<uint64_t>(), // option: option_id
+            MySqlBinding::createInteger<uint16_t>(), // option: code
+            MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // option: value
+            MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // option: formatted_value
+            MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // option: space
+            MySqlBinding::createInteger<uint8_t>(), // option: persistent
+            MySqlBinding::createInteger<uint32_t>(), // option: dhcp6_subnet_id
+            MySqlBinding::createInteger<uint8_t>(), // option: scope_id
+            MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // option: user_context
+            MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // option: shared_network_name
+            MySqlBinding::createInteger<uint64_t>(), // option: pool_id
+            MySqlBinding::createInteger<uint64_t>(), // option: pd_pool_id
+            MySqlBinding::createTimestamp() //option: modification_ts
+        };
+
+        uint64_t last_network_id = 0;
+        uint64_t last_option_id = 0;
+
+        conn_.selectQuery(index, in_bindings, out_bindings,
+                          [this, &shared_networks, &last_network_id, &last_option_id]
+                          (MySqlBindingCollection& out_bindings) {
+            SharedNetwork6Ptr last_network;
+            if (!shared_networks.empty()) {
+                last_network = *shared_networks.rbegin();
+            }
+
+            // If this is the first shared network or the shared network id in this
+            // row points to the next shared network we use the data in the
+            // row to create the new shared network instance.
+            if (last_network_id != out_bindings[0]->getInteger<uint64_t>()) {
+
+                last_network_id = out_bindings[0]->getInteger<uint64_t>();
+                last_network.reset(new SharedNetwork6(out_bindings[1]->getString()));
+
+                // client_class
+                if (!out_bindings[2]->amNull()) {
+                    last_network->allowClientClass(out_bindings[2]->getString());
+                }
+
+                // interface
+                last_network->setIface(out_bindings[3]->getStringOrDefault(""));
+
+                // modification_ts
+                last_network->setModificationTime(out_bindings[4]->getTimestamp());
+
+                // preferred_lifetime
+                if (!out_bindings[5]->amNull()) {
+                    last_network->setPreferred(out_bindings[5]->getInteger<uint32_t>());
+                }
+
+                // rapid_commit
+                last_network->setRapidCommit(static_cast<bool>
+                                             (out_bindings[6]->getIntegerOrDefault<uint8_t>(0)));
+
+                // rebind_timer
+                if (!out_bindings[7]->amNull()) {
+                    last_network->setT2(out_bindings[7]->getInteger<uint32_t>());
+                }
+
+                // relay
+                ElementPtr relay_element = out_bindings[8]->getJSON();
+                if (relay_element) {
+                    if (relay_element->getType() != Element::list) {
+                        isc_throw(BadValue, "invalid relay value "
+                                  << out_bindings[8]->getString());
+                    }
+                    for (auto i = 0; i < relay_element->size(); ++i) {
+                        auto relay_address_element = relay_element->get(i);
+                        if (relay_address_element->getType() != Element::string) {
+                            isc_throw(BadValue, "relay address must be a string");
+                        }
+                        last_network->addRelayAddress(IOAddress(relay_element->get(i)->stringValue()));
+                    }
+                }
+
+                // renew_timer
+                if (!out_bindings[9]->amNull()) {
+                    last_network->setT1(out_bindings[9]->getInteger<uint32_t>());
+                }
+
+                // require_client_classes
+                ElementPtr require_element = out_bindings[10]->getJSON();
+                if (require_element) {
+                    if (require_element->getType() != Element::list) {
+                        isc_throw(BadValue, "invalid require_client_classes value "
+                              << out_bindings[10]->getString());
+                    }
+                    for (auto i = 0; i < require_element->size(); ++i) {
+                        auto require_item = require_element->get(i);
+                        if (require_item->getType() != Element::string) {
+                            isc_throw(BadValue, "elements of require_client_classes list must"
+                                      "be valid strings");
+                        }
+                        last_network->requireClientClass(require_item->stringValue());
+                    }
+                }
+
+                // reservation_mode
+                last_network->setHostReservationMode(static_cast<Subnet6::HRMode>
+                    (out_bindings[11]->getIntegerOrDefault<uint8_t>(Subnet6::HR_ALL)));
+
+                // user_context
+                ElementPtr user_context = out_bindings[12]->getJSON();
+                if (user_context) {
+                    last_network->setContext(user_context);
+                }
+
+                // valid_lifetime
+                if (!out_bindings[13]->amNull()) {
+                    last_network->setValid(out_bindings[13]->getInteger<uint32_t>());
+                }
+
+                shared_networks.push_back(last_network);
+            }
+
+            // Parse option.
+            if (!out_bindings[14]->amNull() &&
+                (last_option_id < out_bindings[14]->getInteger<uint64_t>())) {
+                last_option_id = out_bindings[14]->getInteger<uint64_t>();
+
+                OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 14);
+                if (desc) {
+                    last_network->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
+    }
+
+    /// @brief Sends query to retrieve single shared network by name.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Shared network name.
+    ///
+    /// @return Pointer to the returned shared network or NULL if such shared
+    /// network doesn't exist.
+    SharedNetwork6Ptr getSharedNetwork6(const ServerSelector& server_selector,
+                                        const std::string& name) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "fetching shared network");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createString(name)
+        };
+
+        SharedNetwork6Collection shared_networks;
+        getSharedNetworks6(GET_SHARED_NETWORK6_NAME, in_bindings, shared_networks);
+
+        return (shared_networks.empty() ? SharedNetwork6Ptr() : *shared_networks.begin());
+    }
+
+    /// @brief Sends query to retrieve all shared networks.
+    ///
+    /// @param server_selector Server selector.
+    /// @param [out] shared_networks Reference to the shared networks collection
+    /// structure where shared networks should be inserted.
+    void getAllSharedNetworks6(const ServerSelector& server_selector,
+                               SharedNetwork6Collection& shared_networks) {
+        auto tags = getServerTags(server_selector);
+
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag)
+            };
+
+            getSharedNetworks6(GET_ALL_SHARED_NETWORKS6, in_bindings, shared_networks);
+        }
+    }
+
+    /// @brief Sends query to retrieve modified shared networks.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_ts Lower bound modification timestamp.
+    /// @param [out] shared_networks Reference to the shared networks collection
+    /// structure where shared networks should be inserted.
+    void getModifiedSharedNetworks6(const ServerSelector& server_selector,
+                                    const boost::posix_time::ptime& modification_ts,
+                                    SharedNetwork6Collection& shared_networks) {
+        auto tags = getServerTags(server_selector);
+
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createTimestamp(modification_ts)
+            };
+
+            getSharedNetworks6(GET_MODIFIED_SHARED_NETWORKS6, in_bindings,
+                               shared_networks);
+        }
+    }
+
+    /// @brief Sends query to insert or update shared network.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet Pointer to the shared network to be inserted or updated.
+    void createUpdateSharedNetwork6(const ServerSelector& server_selector,
+                                    const SharedNetwork6Ptr& shared_network) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "creating or updating shared network");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(shared_network->getName()),
+            MySqlBinding::condCreateString(shared_network->getClientClass()),
+            MySqlBinding::condCreateString(shared_network->getIface()),
+            MySqlBinding::createTimestamp(shared_network->getModificationTime()),
+            MySqlBinding::createInteger<uint32_t>(shared_network->getPreferred()),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(shared_network->getRapidCommit())),
+            MySqlBinding::condCreateInteger<uint32_t>(shared_network->getT2()),
+            createInputRelayBinding(shared_network),
+            MySqlBinding::condCreateInteger<uint32_t>(shared_network->getT1()),
+            createInputRequiredClassesBinding(shared_network),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
+                                                 (shared_network->getHostReservationMode())),
+            createInputContextBinding(shared_network),
+            MySqlBinding::condCreateInteger<uint32_t>(shared_network->getValid())
+        };
+
+        MySqlTransaction transaction(conn_);
+
+        try {
+            // Try to insert shared network. The shared network name must be unique,
+            // so if inserting fails with DuplicateEntry exception we'll need to
+            // update existing shared network entry.
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6,
+                              in_bindings);
+
+            // Create bindings for inserting association into dhcp6_shared_network_server
+            // table.
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createString(shared_network->getName()), // shared network name
+                MySqlBinding::createString(tag), // server tag
+                MySqlBinding::createTimestamp(shared_network->getModificationTime()), // modification_ts
+            };
+
+            // Insert association.
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER,
+                              in_server_bindings);
+
+
+        } catch (const DuplicateEntry&) {
+            deleteOptions6(server_selector, shared_network);
+
+            // Need to add one more binding for WHERE clause.
+            in_bindings.push_back(MySqlBinding::createString(shared_network->getName()));
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_SHARED_NETWORK6,
+                                    in_bindings);
+        }
+
+        // (Re)create options.
+        auto option_spaces = shared_network->getCfgOption()->getOptionSpaceNames();
+        for (auto option_space : option_spaces) {
+            OptionContainerPtr options = shared_network->getCfgOption()->getAll(option_space);
+            for (auto desc = options->begin(); desc != options->end(); ++desc) {
+                OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, shared_network->getName(),
+                                    desc_copy);
+            }
+        }
+
+        transaction.commit();
+    }
+
+
+    /// @brief Sends query to insert DHCP option.
+    ///
+    /// This method expects that the server selector contains exactly one
+    /// server tag.
+    ///
+    /// @param server_selector Server selector.
+    /// @param in_bindings Collection of bindings representing an option.
+    void insertOption6(const ServerSelector& server_selector,
+                       const MySqlBindingCollection& in_bindings) {
+        conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6,
+                          in_bindings);
+
+        // Fetch unique identifier of the inserted option.
+        uint64_t id = mysql_insert_id(conn_.mysql_);
+
+        // Create bindings needed to insert association of that option with
+        // a server into the dhcp6_options_server table.
+        MySqlBindingCollection in_server_bindings = {
+            MySqlBinding::createInteger<uint64_t>(id), // option_id
+            MySqlBinding::createString(*getServerTags(server_selector).begin()), // server_tag
+            in_bindings[12] // copy modification timestamp from option
+        };
+
+        conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER,
+                          in_server_bindings);
+    }
+
+    /// @brief Sends query to insert or update global DHCP option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param option Pointer to the option descriptor encapsulating the option.
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const OptionDescriptorPtr& option) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "creating or updating global option");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
+            createOptionValueBinding(option),
+            MySqlBinding::condCreateString(option->formatted_value_),
+            MySqlBinding::condCreateString(option->space_name_),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createInteger<uint8_t>(0),
+            createInputContextBinding(option),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createTimestamp(option->getModificationTime())
+        };
+
+        MySqlTransaction transaction(conn_);
+        OptionDescriptorPtr existing_option = getOption6(server_selector,
+                                                         option->option_->getType(),
+                                                         option->space_name_);
+        if (existing_option) {
+            in_bindings.push_back(MySqlBinding::createString(tag));
+            in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(option->option_->getType()));
+            in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_));
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
+                                    in_bindings);
+
+        } else {
+            insertOption6(server_selector, in_bindings);
+
+        }
+
+        transaction.commit();
+    }
+
+    /// @brief Sends query to insert or update DHCP option in a subnet.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Identifier of the subnet the option belongs to.
+    /// @param option Pointer to the option descriptor encapsulating the option.
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const SubnetID& subnet_id,
+                             const OptionDescriptorPtr& option) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector,
+                                "creating or updating subnet level option");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
+            createOptionValueBinding(option),
+            MySqlBinding::condCreateString(option->formatted_value_),
+            MySqlBinding::condCreateString(option->space_name_),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createNull(),
+            MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
+            MySqlBinding::createInteger<uint8_t>(1),
+            createInputContextBinding(option),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createTimestamp(option->getModificationTime())
+        };
+
+
+        MySqlTransaction transaction(conn_);
+
+        OptionDescriptorPtr existing_option = getOption6(server_selector, subnet_id,
+                                                         option->option_->getType(),
+                                                         option->space_name_);
+        if (existing_option) {
+            in_bindings.push_back(MySqlBinding::createString(tag));
+            in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)));
+            in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(option->option_->getType()));
+            in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_));
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID,
+                                    in_bindings);
+
+        } else {
+            insertOption6(server_selector, in_bindings);
+        }
+
+        transaction.commit();
+    }
+
+    /// @brief Sends query to insert or update DHCP option in a pool.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool_start_address Lower bound address of the pool.
+    /// @param pool_end_address Upper bound address of the pool.
+    /// @param option Pointer to the option descriptor encapsulating the option.
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const IOAddress& pool_start_address,
+                             const IOAddress& pool_end_address,
+                             const OptionDescriptorPtr& option) {
+        uint64_t pool_id = 0;
+        Pool6Ptr pool = getPool6(server_selector, pool_start_address, pool_end_address,
+                                 pool_id);
+        if (!pool) {
+            isc_throw(BadValue, "no pool found for range of "
+                      << pool_start_address << " : "
+                      << pool_end_address);
+        }
+
+        createUpdateOption6(server_selector, Lease::TYPE_NA, pool_id, option);
+    }
+
+    /// @brief Sends query to insert or update DHCP option in a pd pool.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pd_pool_prefix Address part of the pd pool prefix.
+    /// @param pd_pool_prefix_length Length of the pd pool prefix.
+    /// @param option Pointer to the option descriptor encapsulating the option.
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const asiolink::IOAddress& pd_pool_prefix,
+                             const uint8_t pd_pool_prefix_length,
+                             const OptionDescriptorPtr& option) {
+        uint64_t pool_id = 0;
+        Pool6Ptr pool = getPdPool6(server_selector, pd_pool_prefix,
+                                   pd_pool_prefix_length, pool_id);
+        if (!pool) {
+            isc_throw(BadValue, "no pd pool found for prefix of "
+                      << pd_pool_prefix << "/"
+                      << static_cast<unsigned>(pd_pool_prefix_length));
+        }
+
+        createUpdateOption6(server_selector, Lease::TYPE_PD, pool_id, option);
+    }
+
+    /// @brief Sends query to insert or update DHCP option in a pool.
+    ///
+    /// @param selector Server selector.
+    /// @param pool_type Pool type (Lease::TYPE_NA or Lease::TYPE_PD).
+    /// @param pool_id Identifier of the pool the option belongs to.
+    /// @param option Pointer to the option descriptor encapsulating the option.
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             Lease::Type pool_type,
+                             const uint64_t pool_id,
+                             const OptionDescriptorPtr& option) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector,
+                                "creating or updating pool level option");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
+            createOptionValueBinding(option),
+            MySqlBinding::condCreateString(option->formatted_value_),
+            MySqlBinding::condCreateString(option->space_name_),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createInteger<uint8_t>(
+                pool_type == Lease::TYPE_NA ? 5 : 6),
+            createInputContextBinding(option),
+            MySqlBinding::createNull(),
+            pool_type == Lease::TYPE_NA ?
+                MySqlBinding::createInteger<uint64_t>(pool_id) :
+                MySqlBinding::createNull(),
+            pool_type == Lease::TYPE_PD ?
+                MySqlBinding::createInteger<uint64_t>(pool_id) :
+                MySqlBinding::createNull(),
+            MySqlBinding::createTimestamp(option->getModificationTime())
+        };
+
+        MySqlTransaction transaction(conn_);
+        OptionDescriptorPtr existing_option = getOption6(server_selector,
+                                                         pool_type,
+                                                         pool_id,
+                                                         option->option_->getType(),
+                                                         option->space_name_);
+        if (existing_option) {
+            in_bindings.push_back(MySqlBinding::createString(tag));
+            in_bindings.push_back(MySqlBinding::createInteger<uint64_t>(pool_id));
+            in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(option->option_->getType()));
+            in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_));
+            StatementIndex stmt_idx =
+                Lease::TYPE_NA ?
+                    MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID :
+                    MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID;
+            conn_.updateDeleteQuery(stmt_idx, in_bindings);
+
+        } else {
+            insertOption6(server_selector, in_bindings);
+        }
+
+        transaction.commit();
+    }
+
+    /// @brief Sends query to insert or update DHCP option in a shared network.
+    ///
+    /// @param selector Server selector.
+    /// @param shared_network_name Name of the shared network the option
+    /// belongs to.
+    /// @param option Pointer to the option descriptor encapsulating the option.
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const std::string& shared_network_name,
+                             const OptionDescriptorPtr& option) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "creating or updating shared"
+                                " network level option");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
+            createOptionValueBinding(option),
+            MySqlBinding::condCreateString(option->formatted_value_),
+            MySqlBinding::condCreateString(option->space_name_),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createInteger<uint8_t>(4),
+            createInputContextBinding(option),
+            MySqlBinding::createString(shared_network_name),
+            MySqlBinding::createNull(),
+            MySqlBinding::createNull(),
+            MySqlBinding::createTimestamp(option->getModificationTime())
+        };
+
+        MySqlTransaction transaction(conn_);
+
+        OptionDescriptorPtr existing_option = getOption6(server_selector, shared_network_name,
+                                                         option->option_->getType(),
+                                                         option->space_name_);
+        if (existing_option) {
+            in_bindings.push_back(MySqlBinding::createString(tag));
+            in_bindings.push_back(MySqlBinding::createString(shared_network_name));
+            in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(option->option_->getType()));
+            in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_));
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
+                                    UPDATE_OPTION6_SHARED_NETWORK,
+                                    in_bindings);
+        } else {
+            insertOption6(server_selector, in_bindings);
+        }
+
+        transaction.commit();
+    }
+
+    /// @brief Sends query to retrieve single option definition by code and
+    /// option space.
+    ///
+    /// @param server_selector Server selector.
+    /// @param code Option code.
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the returned option definition or NULL if such
+    /// option definition doesn't exist.
+    OptionDefinitionPtr getOptionDef6(const ServerSelector& server_selector,
+                                      const uint16_t code,
+                                      const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "fetching option definition");
+
+        OptionDefContainer option_defs;
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+        getOptionDefs(GET_OPTION_DEF6_CODE_SPACE, in_bindings, option_defs);
+        return (option_defs.empty() ? OptionDefinitionPtr() : *option_defs.begin());
+    }
+
+    /// @brief Sends query to retrieve all option definitions.
+    ///
+    /// @param server_selector Server selector.
+    /// @param [out] option_defs Reference to the container where option
+    /// definitions are to be stored.
+    void
+    getAllOptionDefs6(const ServerSelector& server_selector,
+                      OptionDefContainer& option_defs) {
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag)
+            };
+            getOptionDefs(MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTION_DEFS6,
+                          in_bindings, option_defs);
+        }
+    }
+
+    /// @brief Sends query to retrieve option definitions with modification
+    /// time later than specified timestamp.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_time Lower bound subnet modification time.
+    /// @param [out] option_defs Reference to the container where option
+    /// definitions are to be stored.
+    void
+    getModifiedOptionDefs6(const ServerSelector& server_selector,
+                           const boost::posix_time::ptime& modification_time,
+                           OptionDefContainer& option_defs) {
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createTimestamp(modification_time)
+            };
+            getOptionDefs(MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTION_DEFS6,
+                          in_bindings, option_defs);
+        }
+    }
+
+    /// @brief Sends query to retrieve single global option by code and
+    /// option space.
+    ///
+    /// @param server_selector Server selector.
+    /// @param code Option code.
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the returned option or NULL if such option
+    /// doesn't exist.
+    OptionDescriptorPtr
+    getOption6(const ServerSelector& server_selector, const uint16_t code,
+               const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "fetching global option");
+
+        OptionContainer options;
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+        getOptions6(GET_OPTION6_CODE_SPACE, in_bindings, Option::V6, options);
+        return (options.empty() ? OptionDescriptorPtr() :
+                OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
+    }
+
+    /// @brief Sends query to retrieve all global options.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Container holding returned options.
+    OptionContainer
+    getAllOptions6(const ServerSelector& server_selector) {
+        OptionContainer options;
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag)
+            };
+            getOptions6(MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTIONS6,
+                        in_bindings, Option::V6, options);
+        }
+
+        return (options);
+    }
+
+    /// @brief Sends query to retrieve global options with modification
+    /// time later than specified timestamp.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Container holding returned options.
+    OptionContainer
+    getModifiedOptions6(const ServerSelector& server_selector,
+                        const boost::posix_time::ptime& modification_time) {
+        OptionContainer options;
+
+        auto tags = getServerTags(server_selector);
+        for (auto tag : tags) {
+            MySqlBindingCollection in_bindings = {
+                MySqlBinding::createString(tag),
+                MySqlBinding::createTimestamp(modification_time)
+            };
+            getOptions6(MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTIONS6,
+                        in_bindings, Option::V6, options);
+        }
+
+        return (options);
+    }
+
+    /// @brief Sends query to retrieve single option by code and option space
+    /// for a giben subnet id.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Subnet identifier.
+    /// @param code Option code.
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the returned option descriptor or NULL if such
+    /// option doesn't exist.
+    OptionDescriptorPtr getOption6(const ServerSelector& server_selector,
+                                   const SubnetID& subnet_id,
+                                   const uint16_t code,
+                                   const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "fetching subnet level option");
+
+        OptionContainer options;
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+        getOptions6(GET_OPTION6_SUBNET_ID_CODE_SPACE, in_bindings, Option::V6,
+                    options);
+        return (options.empty() ? OptionDescriptorPtr() :
+                OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
+    }
+
+    /// @brief Sends query to retrieve single option by code and option space
+    /// for a given pool id.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool_type Pool type (Lease::TYPE_NA or Lease::TYPE_PD).
+    /// @param pool_id Pool identifier in the database.
+    /// @param code Option code.
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the returned option descriptor or NULL if such
+    /// option doesn't exist.
+    OptionDescriptorPtr getOption6(const ServerSelector& server_selector,
+                                   Lease::Type pool_type,
+                                   const uint64_t pool_id,
+                                   const uint16_t code,
+                                   const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "fetching pool level option");
+
+        OptionContainer options;
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint64_t>(pool_id),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+        StatementIndex stmt_idx =
+            pool_type == Lease::TYPE_NA ?
+                GET_OPTION6_POOL_ID_CODE_SPACE :
+                GET_OPTION6_PD_POOL_ID_CODE_SPACE;
+        getOptions6(stmt_idx, in_bindings, Option::V6, options);
+        return (options.empty() ? OptionDescriptorPtr() :
+                OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
+    }
+
+    /// @brief Sends query to retrieve single option by code and option space
+    /// for a given shared network.
+    ///
+    /// @param server_selector Server selector.
+    /// @param shared_network_name Shared network name.
+    /// @param code Option code.
+    /// @param space Option space name.
+    ///
+    /// @return Pointer to the returned option descriptor or NULL if such
+    /// option doesn't exist.
+    OptionDescriptorPtr getOption6(const ServerSelector& server_selector,
+                                   const std::string& shared_network_name,
+                                   const uint16_t code,
+                                   const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "fetching shared network"
+                                " level option");
+
+        OptionContainer options;
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createString(shared_network_name),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+        getOptions6(GET_OPTION6_SHARED_NETWORK_CODE_SPACE, in_bindings,
+                    Option::V6, options);
+        return (options.empty() ? OptionDescriptorPtr() :
+                OptionDescriptorPtr(new OptionDescriptor(*options.begin())));
+    }
+
+    /// @brief Sends query to insert or update option definition.
+    ///
+    /// @param server_selector Server selector.
+    /// @param option_def Pointer to the option definition to be inserted or updated.
+    void createUpdateOptionDef6(const ServerSelector& server_selector,
+                                const OptionDefinitionPtr& option_def) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "creating or updating option definition");
+
+        ElementPtr record_types = Element::createList();
+        for (auto field : option_def->getRecordFields()) {
+            record_types->add(Element::create(static_cast<int>(field)));
+        }
+        MySqlBindingPtr record_types_binding = record_types->empty() ?
+            MySqlBinding::createNull() : MySqlBinding::createString(record_types->str());
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createInteger<uint16_t>(option_def->getCode()),
+            MySqlBinding::createString(option_def->getName()),
+            MySqlBinding::createString(option_def->getOptionSpaceName().empty() ?
+                                       "dhcp6" : option_def->getOptionSpaceName()),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getType())),
+            MySqlBinding::createTimestamp(option_def->getModificationTime()),
+            MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getArrayType())),
+            MySqlBinding::createString(option_def->getEncapsulatedSpace()),
+            record_types_binding,
+            createInputContextBinding(option_def)
+        };
+
+        MySqlTransaction transaction(conn_);
+
+        // Need to check if this definition already exists. We can't follow
+        // the same pattern as for shared networks and subnets, to try to insert
+        // the definition first and fall back to update if the DuplicateEntry
+        // exception is thrown, because the option code/space is not unique
+        // within the dhcp6_option_def table. Inserting another option definition
+        // with existing option code/name would not violate the key and the
+        // option definition instance would be inserted successfully. Therefore,
+        // we first fetch the option definition for the given server, code and
+        // space name. If it exists, we simply update it.
+        OptionDefinitionPtr existing_definition = getOptionDef6(server_selector,
+                                                                option_def->getCode(),
+                                                                option_def->getOptionSpaceName());
+        if (existing_definition) {
+            // Need to add three more bindings for WHERE clause.
+            in_bindings.push_back(MySqlBinding::createString(tag));
+            in_bindings.push_back(MySqlBinding::createInteger<uint16_t>(existing_definition->getCode()));
+            in_bindings.push_back(MySqlBinding::createString(existing_definition->getOptionSpaceName()));
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6,
+                                    in_bindings);
+
+        } else {
+            // If the option definition doesn't exist, let's insert it.
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6,
+                              in_bindings);
+
+            // Fetch unique identifier of the inserted option definition and use it
+            // as input to the next query.
+            uint64_t id = mysql_insert_id(conn_.mysql_);
+
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createInteger<uint64_t>(id), // option_def_id
+                MySqlBinding::createString(tag), // tag used to obtain server_id
+                MySqlBinding::createTimestamp(option_def->getModificationTime()), // modification_ts
+            };
+
+            // Insert association.
+            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_SERVER,
+                              in_server_bindings);
+        }
+
+        transaction.commit();
+    }
+
+    /// @brief Sends query to delete option definition by code and
+    /// option space name.
+    ///
+    /// @param server_selector Server selector.
+    /// @param code Option code.
+    /// @param name Option name.
+    /// @return Number of deleted option definitions.
+    uint64_t deleteOptionDef6(const ServerSelector& server_selector,
+                              const uint16_t code,
+                              const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting option definition");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTION_DEF6_CODE_NAME, in_bindings));
+    }
+
+    /// @brief Deletes global option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    uint64_t deleteOption6(const ServerSelector& server_selector,
+                           const uint16_t code,
+                           const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting global option");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTION6, in_bindings));
+    }
+
+    /// @brief Deletes subnet level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Identifier of the subnet to which deleted option
+    /// belongs.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    uint64_t deleteOption6(const ServerSelector& server_selector,
+                           const SubnetID& subnet_id,
+                           const uint16_t code,
+                           const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting option for a subnet");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTION6_SUBNET_ID, in_bindings));
+    }
+
+    /// @brief Deletes pool level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool_start_address Lower bound pool address.
+    /// @param pool_end_address  Upper bound pool address.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    uint64_t deleteOption6(const db::ServerSelector& server_selector,
+                           const IOAddress& pool_start_address,
+                           const IOAddress& pool_end_address,
+                           const uint16_t code,
+                           const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting option for a pool");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space),
+            MySqlBinding::createString(pool_start_address.toText()),
+            MySqlBinding::createString(pool_end_address.toText())
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTION6_POOL_RANGE,
+                                        in_bindings));
+    }
+
+    /// @brief Deletes pd pool level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pd_pool_prefix Address part of the pd pool prefix.
+    /// @param pd_pool_prefix_length Length of the pd pool prefix.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    uint64_t deleteOption6(const db::ServerSelector& server_selector,
+                           const asiolink::IOAddress& pd_pool_prefix,
+                           const uint8_t pd_pool_prefix_length,
+                           const uint16_t code,
+                           const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting option for a pd pool");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space),
+            MySqlBinding::createString(pd_pool_prefix.toText()),
+            MySqlBinding::createInteger<uint8_t>(pd_pool_prefix_length)
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTION6_PD_POOL, in_bindings));
+    }
+
+    /// @brief Deletes shared network level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param shared_network_name Name of the shared network which deleted
+    /// option belongs to
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    uint64_t deleteOption6(const db::ServerSelector& server_selector,
+                           const std::string& shared_network_name,
+                           const uint16_t code,
+                           const std::string& space) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting option for a shared network");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createString(shared_network_name),
+            MySqlBinding::createInteger<uint16_t>(code),
+            MySqlBinding::createString(space)
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTION6_SHARED_NETWORK,
+                                        in_bindings));
+    }
+
+    /// @brief Deletes options belonging to a subnet from the database.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet Pointer to the subnet for which options should be
+    /// deleted.
+    /// @return Number of deleted options.
+    uint64_t deleteOptions6(const ServerSelector& server_selector,
+                            const Subnet6Ptr& subnet) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting options for a subnet");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createInteger<uint32_t>(subnet->getID())
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTIONS6_SUBNET_ID,
+                                        in_bindings));
+    }
+
+    /// @brief Deletes options belonging to a shared network from the database.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet Pointer to the subnet for which options should be
+    /// deleted.
+    /// @return Number of deleted options.
+    uint64_t deleteOptions6(const ServerSelector& server_selector,
+                            const SharedNetwork6Ptr& shared_network) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        auto tag = getServerTag(server_selector, "deleting options for a shared network");
+
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createString(shared_network->getName())
+        };
+
+        // Run DELETE.
+        return (conn_.updateDeleteQuery(DELETE_OPTIONS6_SHARED_NETWORK,
+                                        in_bindings));
+    }
+};
+
+/// @brief Array of tagged statements.
+typedef std::array<TaggedStatement, MySqlConfigBackendDHCPv6Impl::NUM_STATEMENTS>
+TaggedStatementArray;
+
+/// @brief Prepared MySQL statements used by the backend to insert and
+/// retrieve data from the database.
+TaggedStatementArray tagged_statements6 = { {
+    // Select global parameter by name.
+    { MySqlConfigBackendDHCPv6Impl::GET_GLOBAL_PARAMETER6,
+      MYSQL_GET_GLOBAL_PARAMETER(dhcp6, AND g.name = ?)
+    },
+
+    // Select all global parameters.
+    { MySqlConfigBackendDHCPv6Impl::GET_ALL_GLOBAL_PARAMETERS6,
+      MYSQL_GET_GLOBAL_PARAMETER(dhcp6)
+    },
+
+    // Select modified global parameters.
+    { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_GLOBAL_PARAMETERS6,
+      MYSQL_GET_GLOBAL_PARAMETER(dhcp6, AND g.modification_ts > ?)
+    },
+
+    // Select subnet by id.
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID,
+      MYSQL_GET_SUBNET6(AND s.subnet_id = ?)
+    },
+
+    // Select subnet by prefix.
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX,
+      MYSQL_GET_SUBNET6(AND s.subnet_prefix = ?)
+    },
+
+    // Select all subnets.
+    { MySqlConfigBackendDHCPv6Impl::GET_ALL_SUBNETS6,
+      MYSQL_GET_SUBNET6()
+    },
+
+    // Select subnets having modification time later than X.
+    { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SUBNETS6,
+      MYSQL_GET_SUBNET6(AND s.modification_ts > ?)
+    },
+
+    // Select pool by address range.
+    { MySqlConfigBackendDHCPv6Impl::GET_POOL6_RANGE,
+      "SELECT"
+      "  p.id,"
+      "  p.start_address,"
+      "  p.end_address,"
+      "  p.dhcp6_subnet_id,"
+      "  p.modification_ts,"
+      "  x.option_id,"
+      "  x.code,"
+      "  x.value,"
+      "  x.formatted_value,"
+      "  x.space,"
+      "  x.persistent,"
+      "  x.dhcp6_subnet_id,"
+      "  x.scope_id,"
+      "  x.user_context,"
+      "  x.shared_network_name,"
+      "  x.pool_id,"
+      "  x.pd_pool_id,"
+      "  x.modification_ts "
+      "FROM dhcp6_pool AS p "
+      "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id "
+      "WHERE p.start_address = ? AND p.end_address = ? "
+      "ORDER BY p.id, x.option_id"
+    },
+
+    // Select pd pool.
+    { MySqlConfigBackendDHCPv6Impl::GET_PD_POOL,
+      "SELECT"
+      "  p.id,"
+      "  p.prefix,"
+      "  p.prefix_length,"
+      "  p.delegated_prefix_length,"
+      "  p.dhcp6_subnet_id,"
+      "  p.modification_ts,"
+      "  x.option_id,"
+      "  x.code,"
+      "  x.value,"
+      "  x.formatted_value,"
+      "  x.space,"
+      "  x.persistent,"
+      "  x.dhcp6_subnet_id,"
+      "  x.scope_id,"
+      "  x.user_context,"
+      "  x.shared_network_name,"
+      "  x.pool_id,"
+      "  x.pd_pool_id,"
+      "  x.modification_ts "
+      "FROM dhcp6_pd_pool AS p "
+      "LEFT JOIN dhcp6_options AS x ON x.scope_id = 6 AND p.id = x.pd_pool_id "
+      "WHERE p.prefix = ? AND p.prefix_length = ? "
+      "ORDER BY p.id, x.option_id"
+    },
+
+    // Select shared network by name.
+    { MySqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME,
+      MYSQL_GET_SHARED_NETWORK6(AND n.name = ?)
+    },
+
+    // Select all shared networks.
+    { MySqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6,
+      MYSQL_GET_SHARED_NETWORK6()
+    },
+
+    // Select modified shared networks.
+    { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6,
+      MYSQL_GET_SHARED_NETWORK6(AND n.modification_ts > ?)
+    },
+
+    // Retrieves option definition by code and space.
+    { MySqlConfigBackendDHCPv6Impl::GET_OPTION_DEF6_CODE_SPACE,
+      MYSQL_GET_OPTION_DEF(dhcp6, AND d.code = ? AND d.space = ?)
+    },
+
+    // Retrieves all option definitions.
+    { MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTION_DEFS6,
+      MYSQL_GET_OPTION_DEF(dhcp6)
+    },
+
+    // Retrieves modified option definitions.
+    { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTION_DEFS6,
+      MYSQL_GET_OPTION_DEF(dhcp6, AND d.modification_ts > ?)
+    },
+
+    // Retrieves global option by code and space.
+    { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_CODE_SPACE,
+      MYSQL_GET_OPTION6(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+    },
+
+    // Retrieves all global options.
+    { MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTIONS6,
+      MYSQL_GET_OPTION6(AND o.scope_id = 0)
+    },
+
+    // Retrieves modified options.
+    { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTIONS6,
+      MYSQL_GET_OPTION6(AND o.scope_id = 0 AND o.modification_ts > ?)
+    },
+
+    // Retrieves an option for a given subnet, option code and space.
+    { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_SUBNET_ID_CODE_SPACE,
+      MYSQL_GET_OPTION6(AND o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Retrieves an option for a given pool, option code and space.
+    { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_POOL_ID_CODE_SPACE,
+      MYSQL_GET_OPTION6(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Retrieves an option for a given pd pool, option code and space.
+    { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_PD_POOL_ID_CODE_SPACE,
+      MYSQL_GET_OPTION6(AND o.scope_id = 6 AND o.pd_pool_id = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Retrieves an option for a given shared network, option code and space.
+    { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_SHARED_NETWORK_CODE_SPACE,
+      MYSQL_GET_OPTION6(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Insert global parameter.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6,
+      MYSQL_INSERT_GLOBAL_PARAMETER(dhcp6)
+    },
+
+    // Insert association of the global parameter with a server.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6_SERVER,
+      MYSQL_INSERT_GLOBAL_PARAMETER_SERVER(dhcp6)
+    },
+
+    // Insert a subnet.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6,
+      "INSERT INTO dhcp6_subnet("
+      "  subnet_id,"
+      "  subnet_prefix,"
+      "  client_class,"
+      "  interface,"
+      "  modification_ts,"
+      "  preferred_lifetime,"
+      "  rapid_commit,"
+      "  rebind_timer,"
+      "  relay,"
+      "  renew_timer,"
+      "  require_client_classes,"
+      "  reservation_mode,"
+      "  shared_network_name,"
+      "  user_context,"
+      "  valid_lifetime"
+      ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" },
+
+    // Insert association of the subnet with a server.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER,
+      MYSQL_INSERT_SUBNET_SERVER(dhcp6)
+    },
+
+    // Insert pool for a subnet.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_POOL6,
+      MYSQL_INSERT_POOL6()
+    },
+
+    // Insert pd pool for a subnet.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_PD_POOL,
+      MYSQL_INSERT_PD_POOL()
+    },
+
+    // Insert a shared network.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6,
+      "INSERT INTO dhcp6_shared_network("
+      "  name,"
+      "  client_class,"
+      "  interface,"
+      "  modification_ts,"
+      "  preferred_lifetime,"
+      "  rapid_commit,"
+      "  rebind_timer,"
+      "  relay,"
+      "  renew_timer,"
+      "  require_client_classes,"
+      "  reservation_mode,"
+      "  user_context,"
+      "  valid_lifetime"
+      ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" },
+
+    // Insert association of the shared network with a server.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER,
+      MYSQL_INSERT_SHARED_NETWORK_SERVER(dhcp6)
+    },
+
+    // Insert option definition.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6,
+      MYSQL_INSERT_OPTION_DEF(dhcp6)
+    },
+
+    // Insert association of the option definition with a server.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_SERVER,
+      MYSQL_INSERT_OPTION_DEF_SERVER(dhcp6)
+    },
+
+    // Insert subnet specific option.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6,
+      MYSQL_INSERT_OPTION6()
+    },
+
+    // Insert association of the DHCP option with a server.
+    { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER,
+      MYSQL_INSERT_OPTION_SERVER(dhcp6)
+    },
+
+    // Update existing global parameter.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_GLOBAL_PARAMETER6,
+      MYSQL_UPDATE_GLOBAL_PARAMETER(dhcp6)
+    },
+
+    // Update existing subnet.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6,
+      "UPDATE dhcp6_subnet SET"
+      "  subnet_id = ?,"
+      "  subnet_prefix = ?,"
+      "  client_class = ?,"
+      "  interface = ?,"
+      "  modification_ts = ?,"
+      "  preferred_lifetime = ?,"
+      "  rapid_commit = ?,"
+      "  rebind_timer = ?,"
+      "  relay = ?,"
+      "  renew_timer = ?,"
+      "  require_client_classes = ?,"
+      "  reservation_mode = ?,"
+      "  shared_network_name = ?,"
+      "  user_context = ?,"
+      "  valid_lifetime = ? "
+      "WHERE subnet_id = ?" },
+
+    // Update existing shared network.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_SHARED_NETWORK6,
+      "UPDATE dhcp6_shared_network SET"
+      "  name = ?,"
+      "  client_class = ?,"
+      "  interface = ?,"
+      "  modification_ts = ?,"
+      "  preferred_lifetime = ?,"
+      "  rapid_commit = ?,"
+      "  rebind_timer = ?,"
+      "  relay = ?,"
+      "  renew_timer = ?,"
+      "  require_client_classes = ?,"
+      "  reservation_mode = ?,"
+      "  user_context = ?,"
+      "  valid_lifetime = ? "
+      "WHERE name = ?" },
+
+    // Update existing option definition.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6,
+      MYSQL_UPDATE_OPTION_DEF(dhcp6)
+    },
+
+    // Update existing global option.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
+      MYSQL_UPDATE_OPTION6(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+    },
+
+    // Update existing subnet level option.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID,
+      MYSQL_UPDATE_OPTION6(AND o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Update existing pool level option.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID,
+      MYSQL_UPDATE_OPTION6(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Update existing pd pool level option.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID,
+      MYSQL_UPDATE_OPTION6(AND o.scope_id = 6 AND o.pd_pool_id = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Update existing shared network level option.
+    { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SHARED_NETWORK,
+      MYSQL_UPDATE_OPTION6(AND o.scope_id = 6 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Delete global parameter by name.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_GLOBAL_PARAMETER6,
+      MYSQL_DELETE_GLOBAL_PARAMETER(dhcp6, AND g.name = ?)
+    },
+
+    // Delete all global parameters.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_PARAMETERS6,
+      MYSQL_DELETE_GLOBAL_PARAMETER(dhcp6)
+    },
+
+    // Delete subnet by id.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID,
+      MYSQL_DELETE_SUBNET(dhcp6, AND s.subnet_id = ?)
+    },
+
+    // Delete subnet by prefix.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX,
+      MYSQL_DELETE_SUBNET(dhcp6, AND s.subnet_prefix = ?)
+    },
+
+    // Delete all subnets.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6,
+      MYSQL_DELETE_SUBNET(dhcp6)
+    },
+
+    // Delete pools for a subnet.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_POOLS6_SUBNET_ID,
+      MYSQL_DELETE_POOLS6()
+    },
+
+    // Delete pools for a subnet.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_PD_POOLS_SUBNET_ID,
+      MYSQL_DELETE_PD_POOLS()
+    },
+
+    // Delete shared network by name.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME,
+      MYSQL_DELETE_SHARED_NETWORK(dhcp6, AND n.name = ?)
+    },
+
+    // Delete all shared networks.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6,
+      MYSQL_DELETE_SHARED_NETWORK(dhcp6)
+    },
+
+    // Delete option definition.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION_DEF6_CODE_NAME,
+      MYSQL_DELETE_OPTION_DEF(dhcp6, AND code = ? AND space = ?)
+    },
+
+    // Delete all option definitions.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6,
+      MYSQL_DELETE_OPTION_DEF(dhcp6)
+    },
+
+    // Delete single global option.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6,
+      MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 0  AND o.code = ? AND o.space = ?)
+    },
+
+    // Delete single option from a subnet.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SUBNET_ID,
+      MYSQL_DELETE_OPTION(dhcp6,
+                          AND o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Delete single option from a pool.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_POOL_RANGE,
+      MYSQL_DELETE_OPTION_POOL_RANGE(dhcp6, AND o.scope_id = 5 AND o.code = ? AND o.space = ?)
+    },
+
+    // Delete single option from a pd pool.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_PD_POOL,
+      MYSQL_DELETE_OPTION_PD_POOL(AND o.scope_id = 6 AND o.code = ? AND o.space = ?)
+    },
+
+    // Delete single option from a shared network.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SHARED_NETWORK,
+      MYSQL_DELETE_OPTION(dhcp6,
+                          AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+    },
+
+    // Delete options belonging to a subnet.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_SUBNET_ID,
+      MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 1 AND o.dhcp6_subnet_id = ?)
+    },
+
+    // Delete options belonging to a shared_network.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_SHARED_NETWORK,
+      MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 4 AND o.shared_network_name = ?)
+    }
+}
+};
+
+MySqlConfigBackendDHCPv6Impl::
+MySqlConfigBackendDHCPv6Impl(const DatabaseConnection::ParameterMap& parameters)
+    : MySqlConfigBackendImpl(parameters) {
+    // Prepare query statements. Those are will be only used to retrieve
+    // information from the database, so they can be used even if the
+    // database is read only for the current user.
+    conn_.prepareStatements(tagged_statements6.begin(),
+                            tagged_statements6.end());
+//                            tagged_statements6.begin() + WRITE_STMTS_BEGIN);
+}
+
+MySqlConfigBackendDHCPv6::
+MySqlConfigBackendDHCPv6(const DatabaseConnection::ParameterMap& parameters)
+    : impl_(new MySqlConfigBackendDHCPv6Impl(parameters)) {
+}
+
+Subnet6Ptr
+MySqlConfigBackendDHCPv6::getSubnet6(const ServerSelector& server_selector,
+                                     const std::string& subnet_prefix) const {
+    return (impl_->getSubnet6(server_selector, subnet_prefix));
+}
+
+Subnet6Ptr
+MySqlConfigBackendDHCPv6::getSubnet6(const ServerSelector& server_selector,
+                                     const SubnetID& subnet_id) const {
+    return (impl_->getSubnet6(server_selector, subnet_id));
+}
+
+Subnet6Collection
+MySqlConfigBackendDHCPv6::getAllSubnets6(const ServerSelector& server_selector) const {
+    Subnet6Collection subnets;
+    impl_->getAllSubnets6(server_selector, subnets);
+    return (subnets);
+}
+
+Subnet6Collection
+MySqlConfigBackendDHCPv6::getModifiedSubnets6(const ServerSelector& server_selector,
+                                              const boost::posix_time::ptime& modification_time) const {
+    Subnet6Collection subnets;
+    impl_->getModifiedSubnets6(server_selector, modification_time, subnets);
+    return (subnets);
+}
+
+SharedNetwork6Ptr
+MySqlConfigBackendDHCPv6::getSharedNetwork6(const ServerSelector& server_selector,
+                                            const std::string& name) const {
+    return (impl_->getSharedNetwork6(server_selector, name));
+}
+
+SharedNetwork6Collection
+MySqlConfigBackendDHCPv6::getAllSharedNetworks6(const ServerSelector& server_selector) const {
+    SharedNetwork6Collection shared_networks;
+    impl_->getAllSharedNetworks6(server_selector, shared_networks);
+    return (shared_networks);
+}
+
+SharedNetwork6Collection
+MySqlConfigBackendDHCPv6::
+getModifiedSharedNetworks6(const ServerSelector& server_selector,
+                           const boost::posix_time::ptime& modification_time) const {
+    SharedNetwork6Collection shared_networks;
+    impl_->getModifiedSharedNetworks6(server_selector, modification_time, shared_networks);
+    return (shared_networks);
+}
+
+OptionDefinitionPtr
+MySqlConfigBackendDHCPv6::getOptionDef6(const ServerSelector& server_selector,
+                                        const uint16_t code,
+                                        const std::string& space) const {
+    return (impl_->getOptionDef6(server_selector, code, space));
+}
+
+OptionDefContainer
+MySqlConfigBackendDHCPv6::getAllOptionDefs6(const ServerSelector& server_selector) const {
+    OptionDefContainer option_defs;
+    impl_->getAllOptionDefs6(server_selector, option_defs);
+    return (option_defs);
+}
+
+OptionDefContainer
+MySqlConfigBackendDHCPv6::
+getModifiedOptionDefs6(const ServerSelector& server_selector,
+                       const boost::posix_time::ptime& modification_time) const {
+    OptionDefContainer option_defs;
+    impl_->getModifiedOptionDefs6(server_selector, modification_time, option_defs);
+    return (option_defs);
+}
+
+OptionDescriptorPtr
+MySqlConfigBackendDHCPv6::getOption6(const ServerSelector& server_selector,
+                                     const uint16_t code,
+                                     const std::string& space) const {
+    return (impl_->getOption6(server_selector, code, space));
+}
+
+OptionContainer
+MySqlConfigBackendDHCPv6::getAllOptions6(const ServerSelector& server_selector) const {
+    return (impl_->getAllOptions6(server_selector));
+}
+
+OptionContainer
+MySqlConfigBackendDHCPv6::
+getModifiedOptions6(const ServerSelector& server_selector,
+                    const boost::posix_time::ptime& modification_time) const {
+    return (impl_->getModifiedOptions6(server_selector, modification_time));
+}
+
+StampedValuePtr
+MySqlConfigBackendDHCPv6::getGlobalParameter6(const ServerSelector& server_selector,
+                                              const std::string& name) const {
+    return (impl_->getGlobalParameter6(server_selector, name));
+}
+
+StampedValueCollection
+MySqlConfigBackendDHCPv6::getAllGlobalParameters6(const ServerSelector& server_selector) const {
+    StampedValueCollection parameters;
+
+    auto tags = impl_->getServerTags(server_selector);
+    for (auto tag : tags) {
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(tag) };
+        impl_->getGlobalParameters6(MySqlConfigBackendDHCPv6Impl::GET_ALL_GLOBAL_PARAMETERS6,
+                                    in_bindings, parameters);
+    }
+    return (parameters);
+}
+
+StampedValueCollection
+MySqlConfigBackendDHCPv6::
+getModifiedGlobalParameters6(const db::ServerSelector& server_selector,
+                             const boost::posix_time::ptime& modification_time) const {
+    StampedValueCollection parameters;
+
+    auto tags = impl_->getServerTags(server_selector);
+    for (auto tag : tags) {
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createString(tag),
+            MySqlBinding::createTimestamp(modification_time)
+        };
+        impl_->getGlobalParameters6(MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_GLOBAL_PARAMETERS6,
+                                    in_bindings, parameters);
+    }
+
+    return (parameters);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateSubnet6(const ServerSelector& server_selector,
+                                              const Subnet6Ptr& subnet) {
+    impl_->createUpdateSubnet6(server_selector, subnet);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateSharedNetwork6(const ServerSelector& server_selector,
+                                                     const SharedNetwork6Ptr& shared_network) {
+    impl_->createUpdateSharedNetwork6(server_selector, shared_network);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateOptionDef6(const ServerSelector& server_selector,
+                                                 const OptionDefinitionPtr& option_def) {
+    impl_->createUpdateOptionDef6(server_selector, option_def);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
+                                              const OptionDescriptorPtr& option) {
+    impl_->createUpdateOption6(server_selector, option);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateOption6(const db::ServerSelector& server_selector,
+                                              const std::string& shared_network_name,
+                                              const OptionDescriptorPtr& option) {
+    impl_->createUpdateOption6(server_selector, shared_network_name, option);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
+                                              const SubnetID& subnet_id,
+                                              const OptionDescriptorPtr& option) {
+    impl_->createUpdateOption6(server_selector, subnet_id, option);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
+                                              const asiolink::IOAddress& pool_start_address,
+                                              const asiolink::IOAddress& pool_end_address,
+                                              const OptionDescriptorPtr& option) {
+    impl_->createUpdateOption6(server_selector, pool_start_address, pool_end_address,
+                               option);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector,
+                                              const asiolink::IOAddress& pd_pool_prefix,
+                                              const uint8_t pd_pool_prefix_length,
+                                              const OptionDescriptorPtr& option) {
+    impl_->createUpdateOption6(server_selector, pd_pool_prefix,
+                               pd_pool_prefix_length, option);
+}
+
+void
+MySqlConfigBackendDHCPv6::createUpdateGlobalParameter6(const ServerSelector& server_selector,
+                                                       const StampedValuePtr& value) {
+    impl_->createUpdateGlobalParameter6(server_selector, value);
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector,
+                                        const std::string& subnet_prefix) {
+    return(impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX,
+                                  server_selector, "deleting a subnet by prefix",
+                                  subnet_prefix));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector,
+                                        const SubnetID& subnet_id) {
+    return (impl_->deleteSubnet6(server_selector, subnet_id));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteAllSubnets6(const ServerSelector& server_selector) {
+    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6,
+                                   server_selector, "deleting all subnets"));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteSharedNetwork6(const ServerSelector& server_selector,
+                                               const std::string& name) {
+    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME,
+                                   server_selector, "deleting a shared network",
+                                   name));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteAllSharedNetworks6(const ServerSelector& server_selector) {
+    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6,
+                                   server_selector, "deleting all shared networks"));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteOptionDef6(const ServerSelector& server_selector,
+                                           const uint16_t code,
+                                           const std::string& space) {
+    return (impl_->deleteOptionDef6(server_selector, code, space));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteAllOptionDefs6(const ServerSelector& server_selector) {
+    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6,
+                                   server_selector, "deleting all option definitions"));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+                                        const uint16_t code,
+                                        const std::string& space) {
+    return (impl_->deleteOption6(server_selector, code, space));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+                                        const std::string& shared_network_name,
+                                        const uint16_t code,
+                                        const std::string& space) {
+    return (impl_->deleteOption6(server_selector, shared_network_name,
+                                 code, space));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+                                        const SubnetID& subnet_id,
+                                        const uint16_t code,
+                                        const std::string& space) {
+    return (impl_->deleteOption6(server_selector, subnet_id, code, space));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+                                        const asiolink::IOAddress& pool_start_address,
+                                        const asiolink::IOAddress& pool_end_address,
+                                        const uint16_t code,
+                                        const std::string& space) {
+    return (impl_->deleteOption6(server_selector, pool_start_address, pool_end_address,
+                                 code, space));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+                                        const asiolink::IOAddress& pd_pool_prefix,
+                                        const uint8_t pd_pool_prefix_length,
+                                        const uint16_t code,
+                                        const std::string& space) {
+    return (impl_->deleteOption6(server_selector, pd_pool_prefix,
+                                 pd_pool_prefix_length, code, space));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteGlobalParameter6(const ServerSelector& server_selector,
+                                                 const std::string& name) {
+    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_GLOBAL_PARAMETER6,
+                                   server_selector, "deleting global parameter",
+                                   name));
+}
+
+uint64_t
+MySqlConfigBackendDHCPv6::deleteAllGlobalParameters6(const ServerSelector& server_selector) {
+    return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_PARAMETERS6,
+                                   server_selector, "deleting all global parameters"));
+}
+
+std::string
+MySqlConfigBackendDHCPv6::getType() const {
+    return (impl_->getType());
+}
+
+std::string
+MySqlConfigBackendDHCPv6::getHost() const {
+    return (impl_->getHost());
+}
+
+uint16_t
+MySqlConfigBackendDHCPv6::getPort() const {
+    return (impl_->getPort());
+}
+
+bool
+MySqlConfigBackendDHCPv6::registerBackendType() {
+    return (
+        dhcp::ConfigBackendDHCPv6Mgr::instance().registerBackendFactory("mysql",
+            [](const db::DatabaseConnection::ParameterMap& params) -> dhcp::ConfigBackendDHCPv6Ptr {
+            return (dhcp::MySqlConfigBackendDHCPv6Ptr(new dhcp::MySqlConfigBackendDHCPv6(params)));
+        })
+    );
+}
+
+void
+MySqlConfigBackendDHCPv6::unregisterBackendType() {
+    dhcp::ConfigBackendDHCPv6Mgr::instance().unregisterBackendFactory("mysql");
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h
new file mode 100644 (file)
index 0000000..b9c0581
--- /dev/null
@@ -0,0 +1,475 @@
+// Copyright (C) 2019 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/.
+
+#ifndef MYSQL_CONFIG_BACKEND_DHCP6_H
+#define MYSQL_CONFIG_BACKEND_DHCP6_H
+
+#include <database/database_connection.h>
+#include <dhcpsrv/config_backend_dhcp6.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+class MySqlConfigBackendDHCPv6Impl;
+
+/// @brief Implementation of the MySql Configuration Backend for
+/// Kea DHCPv6 server.
+class MySqlConfigBackendDHCPv6 : public ConfigBackendDHCPv6 {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param parameters A data structure relating keywords and values
+    /// concerned with the database.
+    explicit MySqlConfigBackendDHCPv6(const db::DatabaseConnection::ParameterMap& parameters);
+
+    /// @brief Retrieves a single subnet by subnet_prefix.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_prefix Prefix of the subnet to be retrieved.
+    /// @return Pointer to the retrieved subnet or NULL if not found.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual Subnet6Ptr
+    getSubnet6(const db::ServerSelector& server_selector,
+               const std::string& subnet_prefix) const;
+
+    /// @brief Retrieves a single subnet by subnet identifier.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Identifier of a subnet to be retrieved.
+    /// @return Pointer to the retrieved subnet or NULL if not found.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual Subnet6Ptr
+    getSubnet6(const db::ServerSelector& server_selector, const SubnetID& subnet_id) const;
+
+    /// @brief Retrieves all subnets.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Collection of subnets or empty collection if no subnet found.
+    virtual Subnet6Collection
+    getAllSubnets6(const db::ServerSelector& server_selector) const;
+
+    /// @brief Retrieves subnets modified after specified time.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_time Lower bound subnet modification time.
+    /// @return Collection of subnets or empty collection if no subnet found.
+    virtual Subnet6Collection
+    getModifiedSubnets6(const db::ServerSelector& server_selector,
+                        const boost::posix_time::ptime& modification_time) const;
+
+    /// @brief Retrieves shared network by name.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Name of the shared network to be retrieved.
+    /// @return Pointer to the shared network or NULL if not found.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual SharedNetwork6Ptr
+    getSharedNetwork6(const db::ServerSelector& server_selector,
+                      const std::string& name) const;
+
+    /// @brief Retrieves all shared networks.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Collection of shared network or empty collection if
+    /// no shared network found.
+    virtual SharedNetwork6Collection
+    getAllSharedNetworks6(const db::ServerSelector& server_selector) const;
+
+    /// @brief Retrieves shared networks modified after specified time.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_time Lower bound shared network modification time.
+    /// @return Collection of shared network or empty collection if
+    /// no shared network found.
+    virtual SharedNetwork6Collection
+    getModifiedSharedNetworks6(const db::ServerSelector& server_selector,
+                               const boost::posix_time::ptime& modification_time) const;
+
+    /// @brief Retrieves single option definition by code and space.
+    ///
+    /// @param server_selector Server selector.
+    /// @param code Code of the option to be retrieved.
+    /// @param space Option space of the option to be retrieved.
+    /// @return Pointer to the option definition or NULL if not found.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual OptionDefinitionPtr
+    getOptionDef6(const db::ServerSelector& server_selector, const uint16_t code,
+                  const std::string& space) const;
+
+    /// @brief Retrieves all option definitions.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Collection of option definitions or empty collection if
+    /// no option definition found.
+    virtual OptionDefContainer
+    getAllOptionDefs6(const db::ServerSelector& server_selector) const;
+
+    /// @brief Retrieves option definitions modified after specified time.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_time Lower bound option definition modification
+    /// time.
+    /// @return Collection of option definitions or empty collection if
+    /// no option definition found.
+    virtual OptionDefContainer
+    getModifiedOptionDefs6(const db::ServerSelector& server_selector,
+                           const boost::posix_time::ptime& modification_time) const;
+
+    /// @brief Retrieves single option by code and space.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Pointer to the retrieved option descriptor or null if
+    /// no option was found.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual OptionDescriptorPtr
+    getOption6(const db::ServerSelector& server_selector, const uint16_t code,
+               const std::string& space) const;
+
+    /// @brief Retrieves all global options.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Collection of global options or empty collection if no
+    /// option found.
+    virtual OptionContainer
+    getAllOptions6(const db::ServerSelector& server_selector) const;
+
+    /// @brief Retrieves option modified after specified time.
+    ///
+    /// @param server_selector Server selector.
+    /// @param modification_time Lower bound option modification time.
+    /// @return Collection of global options or empty collection if no
+    /// option found.
+    virtual OptionContainer
+    getModifiedOptions6(const db::ServerSelector& server_selector,
+                        const boost::posix_time::ptime& modification_time) const;
+
+    /// @brief Retrieves global parameter value.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Name of the global parameter to be retrieved.
+    /// @return Value of the global parameter.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual data::StampedValuePtr
+    getGlobalParameter6(const db::ServerSelector& server_selector,
+                        const std::string& name) const;
+
+    /// @brief Retrieves all global parameters.
+    ///
+    /// @param server_selector Server selector.
+    virtual data::StampedValueCollection
+    getAllGlobalParameters6(const db::ServerSelector& server_selector) const;
+
+    /// @brief Retrieves global parameters modified after specified time.
+    ///
+    /// @param modification_time Lower bound modification time.
+    /// @return Collection of modified global parameters.
+    virtual data::StampedValueCollection
+    getModifiedGlobalParameters6(const db::ServerSelector& server_selector,
+                                 const boost::posix_time::ptime& modification_time) const;
+
+    /// @brief Creates or updates a subnet.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet Subnet to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateSubnet6(const db::ServerSelector& server_selector,
+                        const Subnet6Ptr& subnet);
+
+    /// @brief Creates or updates a shared network.
+    ///
+    /// @param server_selector Server selector.
+    /// @param shared_network Shared network to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateSharedNetwork6(const db::ServerSelector& server_selector,
+                               const SharedNetwork6Ptr& shared_network);
+
+    /// @brief Creates or updates an option definition.
+    ///
+    /// @param server_selector Server selector.
+    /// @param option_def Option definition to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateOptionDef6(const db::ServerSelector& server_selector,
+                           const OptionDefinitionPtr& option_def);
+
+    /// @brief Creates or updates global option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param option Option to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateOption6(const db::ServerSelector& server_selector,
+                        const OptionDescriptorPtr& option);
+
+    /// @brief Creates or updates shared network level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param shared_network_name Name of a shared network to which option
+    /// belongs.
+    /// @param option Option to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateOption6(const db::ServerSelector& server_selector,
+                        const std::string& shared_network_name,
+                        const OptionDescriptorPtr& option);
+
+    /// @brief Creates or updates subnet level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Identifier of a subnet to which option belongs.
+    /// @param option Option to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateOption6(const db::ServerSelector& server_selector,
+                        const SubnetID& subnet_id,
+                        const OptionDescriptorPtr& option);
+
+    /// @brief Creates or updates pool level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool_start_address Lower bound address of the pool to which
+    /// the option belongs.
+    /// @param pool_end_address Upper bound address of the pool to which the
+    /// option belongs.
+    /// @param option Option to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateOption6(const db::ServerSelector& server_selector,
+                        const asiolink::IOAddress& pool_start_address,
+                        const asiolink::IOAddress& pool_end_address,
+                        const OptionDescriptorPtr& option);
+
+    /// @brief Creates or updates pool level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pd_pool_prefix Address part of the prefix of the pd pool
+    /// to which the the option belongs.
+    /// @param pd_pool_prefix_length Prefix length of the pd pool to which
+    /// the option belongs.
+    /// @param option Option to be added or updated.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateOption6(const db::ServerSelector& server_selector,
+                        const asiolink::IOAddress& pd_pool_prefix,
+                        const uint8_t pd_pool_prefix_length,
+                        const OptionDescriptorPtr& option);
+
+    /// @brief Creates or updates global parameter.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Name of the global parameter.
+    /// @param value Value of the global parameter.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual void
+    createUpdateGlobalParameter6(const db::ServerSelector& server_selector,
+                                 const data::StampedValuePtr& value);
+
+    /// @brief Deletes subnet by prefix.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_prefix Prefix of the subnet to be deleted.
+    /// @return Number of deleted subnets.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteSubnet6(const db::ServerSelector& server_selector,
+                  const std::string& subnet_prefix);
+
+    /// @brief Deletes subnet by identifier.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Identifier of the subnet to be deleted.
+    /// @return Number of deleted subnets.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteSubnet6(const db::ServerSelector& server_selector, const SubnetID& subnet_id);
+
+    /// @brief Deletes all subnets.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Number of deleted subnets.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteAllSubnets6(const db::ServerSelector& server_selector);
+
+    /// @brief Deletes shared network by name.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Name of the shared network to be deleted.
+    /// @return Number of deleted shared networks.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteSharedNetwork6(const db::ServerSelector& server_selector,
+                         const std::string& name);
+
+    /// @brief Deletes all shared networks.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Number of deleted shared networks.
+    virtual uint64_t
+    deleteAllSharedNetworks6(const db::ServerSelector& server_selector);
+
+    /// @brief Deletes option definition.
+    ///
+    /// @param server_selector Server selector.
+    /// @param code Code of the option to be deleted.
+    /// @param space Option space of the option to be deleted.
+    /// @return Number of deleted option definitions.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteOptionDef6(const db::ServerSelector& server_selector, const uint16_t code,
+                     const std::string& space);
+
+    /// @brief Deletes all option definitions.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Number of deleted option definitions.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteAllOptionDefs6(const db::ServerSelector& server_selector);
+
+    /// @brief Deletes global option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param code Code of the option to be deleted.
+    /// @param space Option space of the option to be deleted.
+    /// @return Number of deleted options.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteOption6(const db::ServerSelector& server_selector, const uint16_t code,
+                  const std::string& space);
+
+    /// @brief Deletes shared network level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param shared_network_name Name of the shared network which deleted
+    /// option belongs to
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteOption6(const db::ServerSelector& server_selector,
+                  const std::string& shared_network_name,
+                  const uint16_t code,
+                  const std::string& space);
+
+    /// @brief Deletes subnet level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param subnet_id Identifier of the subnet to which deleted option
+    /// belongs.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteOption6(const db::ServerSelector& server_selector, const SubnetID& subnet_id,
+                  const uint16_t code, const std::string& space);
+
+    /// @brief Deletes pool level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pool_start_address Lower bound address of the pool to which
+    /// deleted option belongs.
+    /// @param pool_end_address Upper bound address of the pool to which the
+    /// deleted option belongs.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteOption6(const db::ServerSelector& server_selector,
+                  const asiolink::IOAddress& pool_start_address,
+                  const asiolink::IOAddress& pool_end_address,
+                  const uint16_t code,
+                  const std::string& space);
+
+    /// @brief Deletes pool level option.
+    ///
+    /// @param server_selector Server selector.
+    /// @param pd_pool_prefix Address part of the prefix of the pd pool
+    /// to which the the option belongs.
+    /// @param pd_pool_prefix_length Prefix length of the pd pool to which
+    /// the option belongs.
+    /// @param code Code of the deleted option.
+    /// @param space Option space of the deleted option.
+    /// @return Number of deleted options.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteOption6(const db::ServerSelector& server_selector,
+                  const asiolink::IOAddress& pd_pool_prefix,
+                  const uint8_t pd_pool_prefix_length,
+                  const uint16_t code,
+                  const std::string& space);
+
+    /// @brief Deletes global parameter.
+    ///
+    /// @param server_selector Server selector.
+    /// @param name Name of the global parameter to be deleted.
+    /// @return Number of deleted global parameters.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteGlobalParameter6(const db::ServerSelector& server_selector,
+                           const std::string& name);
+
+    /// @brief Deletes all global parameters.
+    ///
+    /// @param server_selector Server selector.
+    /// @return Number of deleted global parameters.
+    /// @throw NotImplemented if server selector is "unassigned".
+    virtual uint64_t
+    deleteAllGlobalParameters6(const db::ServerSelector& server_selector);
+
+    /// @brief Returns backend type in the textual format.
+    ///
+    /// @return "mysql".
+    virtual std::string getType() const;
+
+    /// @brief Returns backend host.
+    ///
+    /// This is used by the @c BaseConfigBackendPool to select backend
+    /// when @c BackendSelector is specified.
+    ///
+    /// @return host on which the database is located.
+    virtual std::string getHost() const;
+
+    /// @brief Returns backend port number.
+    ///
+    /// This is used by the @c BaseConfigBackendPool to select backend
+    /// when @c BackendSelector is specified.
+    ///
+    /// @return Port number on which database service is available.
+    virtual uint16_t getPort() const;
+
+    /// @brief Registers the MySQL backend factory with backend config manager
+    ///
+    /// This should be called by the hook lib load() function.
+    /// @return True if the factory was registered successfully, false otherwise.
+    static bool registerBackendType();
+
+    /// @brief Unregisters the MySQL backend factory and discards MySQL backends
+    ///
+    /// This should be called by the hook lib unload() function.
+    static void unregisterBackendType();
+
+private:
+
+    /// @brief Pointer to the implementation of the @c MySqlConfigBackendDHCPv6
+    /// class.
+    boost::shared_ptr<MySqlConfigBackendDHCPv6Impl> impl_;
+
+};
+
+/// @brief Pointer to the @c MySqlConfigBackendDHCPv6 class.
+typedef boost::shared_ptr<MySqlConfigBackendDHCPv6> MySqlConfigBackendDHCPv6Ptr;
+
+} // end of namespace isc::cb
+} // end of namespace isc
+
+#endif // MYSQL_CONFIG_BACKEND_DHCP6_H
index 01ced7ad9f24c1a43aeab7efbe6246e9d5fd4844..69d026dff95473ffcc471fe8f4460e8c59c3ae9c 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 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
@@ -190,10 +190,10 @@ MySqlConfigBackendImpl::getOptionDefs(const int index,
 }
 
 void
-MySqlConfigBackendImpl::getOptions(const int index,
-                                   const db::MySqlBindingCollection& in_bindings,
-                                   const Option::Universe& universe,
-                                   OptionContainer& options) {
+MySqlConfigBackendImpl::getOptions4(const int index,
+                                    const db::MySqlBindingCollection& in_bindings,
+                                    const Option::Universe& universe,
+                                    OptionContainer& options) {
     // Create output bindings. The order must match that in the prepared
     // statement.
     MySqlBindingCollection out_bindings = {
@@ -222,7 +222,7 @@ MySqlConfigBackendImpl::getOptions(const int index,
              (last_option_id < out_bindings[0]->getInteger<uint64_t>()))) {
             last_option_id = out_bindings[0]->getInteger<uint64_t>();
 
-            OptionDescriptorPtr desc = processOptionRow(universe, out_bindings.begin());
+            OptionDescriptorPtr desc = processOptionRow4(universe, out_bindings.begin());
             if (desc) {
                 options.push_back(*desc);
             }
@@ -230,16 +230,61 @@ MySqlConfigBackendImpl::getOptions(const int index,
     });
 }
 
-OptionDescriptorPtr
-MySqlConfigBackendImpl::processOptionRow(const Option::Universe& universe,
-                                         MySqlBindingCollection::iterator first_binding) {
+void
+MySqlConfigBackendImpl::getOptions6(const int index,
+                                    const db::MySqlBindingCollection& in_bindings,
+                                    const Option::Universe& universe,
+                                    OptionContainer& options) {
+    // Create output bindings. The order must match that in the prepared
+    // statement.
+    MySqlBindingCollection out_bindings = {
+        MySqlBinding::createInteger<uint64_t>(), // option_id
+        MySqlBinding::createInteger<uint16_t>(), // code
+        MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // value
+        MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // formatted_value
+        MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // space
+        MySqlBinding::createInteger<uint8_t>(), // persistent
+        MySqlBinding::createInteger<uint32_t>(), // dhcp6_subnet_id
+        MySqlBinding::createInteger<uint8_t>(), // scope_id
+        MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context
+        MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // shared_network_name
+        MySqlBinding::createInteger<uint64_t>(), // pool_id
+        MySqlBinding::createInteger<uint64_t>(), // pd_pool_id
+        MySqlBinding::createTimestamp() //modification_ts
+    };
+
+    uint64_t last_option_id = 0;
+
+    conn_.selectQuery(index, in_bindings, out_bindings,
+                      [this, universe, &options, &last_option_id]
+                      (MySqlBindingCollection& out_bindings) {
+        // Parse option.
+        if (!out_bindings[0]->amNull() &&
+            ((last_option_id == 0) ||
+             (last_option_id < out_bindings[0]->getInteger<uint64_t>()))) {
+            last_option_id = out_bindings[0]->getInteger<uint64_t>();
+
+            OptionDescriptorPtr desc = processOptionRow6(universe, out_bindings.begin());
+            if (desc) {
+                options.push_back(*desc);
+            }
+        }
+    });
+}
+
+namespace {
+
+template<typename T> OptionDescriptorPtr
+processOptionRowCommon(const Option::Universe& universe,
+                       MySqlBindingCollection::iterator first_binding,
+                       const size_t tm_off) {
     // Some of the options have standard or custom definitions.
     // Depending whether the option has a definition or not a different
     // C++ class may be used to represent the option. Therefore, the
     // first thing to do is to see if there is a definition for our
     // parsed option. The option code and space is needed for it.
     std::string space = (*(first_binding + 4))->getString();
-    uint16_t code = (*(first_binding + 1))->getInteger<uint8_t>();
+    uint16_t code = (*(first_binding + 1))->getInteger<T>();
 
     // See if the option has standard definition.
     OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code);
@@ -296,11 +341,25 @@ MySqlConfigBackendImpl::processOptionRow(const Option::Universe& universe,
     // its option space and timestamp.
     OptionDescriptorPtr desc(new OptionDescriptor(option, persistent, formatted_value));
     desc->space_name_ = space;
-    desc->setModificationTime((*(first_binding + 11))->getTimestamp());
+    desc->setModificationTime((*(first_binding + tm_off))->getTimestamp());
 
     return (desc);
 }
 
+}
+
+OptionDescriptorPtr
+MySqlConfigBackendImpl::processOptionRow4(const Option::Universe& universe,
+                                          MySqlBindingCollection::iterator first_binding) {
+    return (processOptionRowCommon<uint8_t>(universe, first_binding, 11));
+}
+
+OptionDescriptorPtr
+MySqlConfigBackendImpl::processOptionRow6(const Option::Universe& universe,
+                                          MySqlBindingCollection::iterator first_binding) {
+    return (processOptionRowCommon<uint16_t>(universe, first_binding, 12));
+}
+
 MySqlBindingPtr
 MySqlConfigBackendImpl::createInputRelayBinding(const NetworkPtr& network) {
     ElementPtr relay_element = Element::createList();
index 235e6b18c941fe04406157cb65d84542b3d24849..ea403b3a02bbbd6268c8cba4e9749e42fb6434ab 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 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
@@ -188,12 +188,29 @@ public:
     /// @param universe Option universe, i.e. V4 or V6.
     /// @param [out] options Reference to the container where fetched options
     /// will be inserted.
-    void getOptions(const int index,
-                    const db::MySqlBindingCollection& in_bindings,
-                    const Option::Universe& universe,
-                    OptionContainer& options);
+    void getOptions4(const int index,
+                     const db::MySqlBindingCollection& in_bindings,
+                     const Option::Universe& universe,
+                     OptionContainer& options);
 
-    /// @brief Returns DHCP option instance from output bindings.
+    /// @brief Sends query to the database to retrieve multiple options.
+    ///
+    /// Query should order by option_id.
+    ///
+    /// @param index Index of the query to be used.
+    /// @param in_bindings Input bindings specifying selection criteria. The
+    /// size of the bindings collection must match the number of placeholders
+    /// in the prepared statement. The input bindings collection must be empty
+    /// if the query contains no WHERE clause.
+    /// @param universe Option universe, i.e. V4 or V6.
+    /// @param [out] options Reference to the container where fetched options
+    /// will be inserted.
+    void getOptions6(const int index,
+                     const db::MySqlBindingCollection& in_bindings,
+                     const Option::Universe& universe,
+                     OptionContainer& options);
+
+    /// @brief Returns DHCPv4 option instance from output bindings.
     ///
     /// The following is the expected order of columns specified in the SELECT
     /// query:
@@ -214,8 +231,33 @@ public:
     /// @param first_binding Iterator of the output binding containing
     /// option_id.
     OptionDescriptorPtr
-    processOptionRow(const Option::Universe& universe,
-                     db::MySqlBindingCollection::iterator first_binding);
+    processOptionRow4(const Option::Universe& universe,
+                      db::MySqlBindingCollection::iterator first_binding);
+
+    /// @brief Returns DHCPv6 option instance from output bindings.
+    ///
+    /// The following is the expected order of columns specified in the SELECT
+    /// query:
+    /// - option_id,
+    /// - code,
+    /// - value,
+    /// - formatted_value,
+    /// - space,
+    /// - persistent,
+    /// - dhcp6_subnet_id,
+    /// - scope_id,
+    /// - user_context,
+    /// - shared_network_name,
+    /// - pool_id,
+    /// - pd_pool_id,
+    /// - modification_ts
+    ///
+    /// @param universe V4 or V6.
+    /// @param first_binding Iterator of the output binding containing
+    /// option_id.
+    OptionDescriptorPtr
+    processOptionRow6(const Option::Universe& universe,
+                      db::MySqlBindingCollection::iterator first_binding);
 
     /// @brief Creates input binding for relay addresses.
     ///
index 896499a769873c6bdff8bdbcd56ea21645e811d1..d7d299ae8f6541c2f0551cef0f342a953186adf9 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 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
@@ -110,6 +110,88 @@ namespace {
     " ORDER BY s.subnet_id, p.id, x.option_id, o.option_id"
 #endif
 
+#ifndef MYSQL_GET_SUBNET6
+#define MYSQL_GET_SUBNET6(...) \
+    "SELECT" \
+    "  s.subnet_id," \
+    "  s.subnet_prefix," \
+    "  s.client_class," \
+    "  s.interface," \
+    "  s.modification_ts," \
+    "  s.preferred_lifetime," \
+    "  s.rapid_commit," \
+    "  s.rebind_timer," \
+    "  s.relay," \
+    "  s.renew_timer," \
+    "  s.require_client_classes," \
+    "  s.reservation_mode," \
+    "  s.shared_network_name," \
+    "  s.user_context," \
+    "  s.valid_lifetime," \
+    "  p.id," \
+    "  p.start_address," \
+    "  p.end_address," \
+    "  p.dhcp6_subnet_id," \
+    "  p.modification_ts," \
+    "  d.id," \
+    "  d.prefix," \
+    "  d.prefix_length," \
+    "  d.delegated_prefix_length," \
+    "  d.dhcp6_subnet_id," \
+    "  d.modification_ts," \
+    "  x.option_id," \
+    "  x.code," \
+    "  x.value," \
+    "  x.formatted_value," \
+    "  x.space," \
+    "  x.persistent," \
+    "  x.dhcp6_subnet_id," \
+    "  x.scope_id," \
+    "  x.user_context," \
+    "  x.shared_network_name," \
+    "  x.pool_id," \
+    "  x.pd_pool_id," \
+    "  x.modification_ts," \
+    "  y.option_id," \
+    "  y.code," \
+    "  y.value," \
+    "  y.formatted_value," \
+    "  y.space," \
+    "  y.persistent," \
+    "  y.dhcp6_subnet_id," \
+    "  y.scope_id," \
+    "  y.user_context," \
+    "  y.shared_network_name," \
+    "  y.pool_id," \
+    "  y.pd_pool_id," \
+    "  y.modification_ts," \
+    "  o.option_id," \
+    "  o.code," \
+    "  o.value," \
+    "  o.formatted_value," \
+    "  o.space," \
+    "  o.persistent," \
+    "  o.dhcp6_subnet_id," \
+    "  o.scope_id," \
+    "  o.user_context," \
+    "  o.shared_network_name," \
+    "  o.pool_id," \
+    "  o.pd_pool_id," \
+    "  o.modification_ts " \
+    "FROM dhcp6_subnet AS s " \
+    "INNER JOIN dhcp6_subnet_server AS a " \
+    "  ON s.subnet_id = a.subnet_id " \
+    "INNER JOIN dhcp6_server AS srv " \
+    "  ON (a.server_id = srv.id) OR (a.server_id = 1) " \
+    "LEFT JOIN dhcp6_pool AS p ON s.subnet_id = p.dhcp6_subnet_id " \
+    "LEFT JOIN dhcp6_pd_pool AS d ON s.subnet_id = d.dhcp6_subnet_id " \
+    "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \
+    "LEFT JOIN dhcp6_options AS y ON y.scope_id = 6 AND p.id = y.pd_pool_id " \
+    "LEFT JOIN dhcp6_options AS o ON o.scope_id = 1 AND s.subnet_id = o.dhcp6_subnet_id " \
+    "WHERE (srv.tag = ? OR srv.id = 1) " #__VA_ARGS__ \
+    " ORDER BY s.subnet_id, p.id, d.id, x.option_id, y.option_id, o.option_id"
+#endif
+
 #ifndef MYSQL_GET_SHARED_NETWORK4
 #define MYSQL_GET_SHARED_NETWORK4(...) \
     "SELECT" \
@@ -148,6 +230,46 @@ namespace {
     " ORDER BY n.id, o.option_id"
 #endif
 
+#ifndef MYSQL_GET_SHARED_NETWORK6
+#define MYSQL_GET_SHARED_NETWORK6(...) \
+    "SELECT" \
+    "  n.id," \
+    "  n.name," \
+    "  n.client_class," \
+    "  n.interface," \
+    "  n.modification_ts," \
+    "  n.preferred_lifetime," \
+    "  n.rapid_commit," \
+    "  n.rebind_timer," \
+    "  n.relay," \
+    "  n.renew_timer," \
+    "  n.require_client_classes," \
+    "  n.reservation_mode," \
+    "  n.user_context," \
+    "  n.valid_lifetime," \
+    "  o.option_id," \
+    "  o.code," \
+    "  o.value," \
+    "  o.formatted_value," \
+    "  o.space," \
+    "  o.persistent," \
+    "  o.dhcp6_subnet_id," \
+    "  o.scope_id," \
+    "  o.user_context," \
+    "  o.shared_network_name," \
+    "  o.pool_id," \
+    "  o.pd_pool_id," \
+    "  o.modification_ts " \
+    "FROM dhcp6_shared_network AS n " \
+    "INNER JOIN dhcp6_shared_network_server AS a " \
+    "  ON n.id = a.shared_network_id " \
+    "INNER JOIN dhcp6_server AS s " \
+    "  ON (a.server_id = s.id) OR (a.server_id = 1) " \
+    "LEFT JOIN dhcp6_options AS o ON o.scope_id = 4 AND n.name = o.shared_network_name " \
+    "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \
+    " ORDER BY n.id, o.option_id"
+#endif
+
 #ifndef MYSQL_GET_OPTION_DEF
 #define MYSQL_GET_OPTION_DEF(table_prefix, ...) \
     "SELECT" \
@@ -170,8 +292,8 @@ namespace {
     " ORDER BY d.id"
 #endif
 
-#ifndef MYSQL_GET_OPTION
-#define MYSQL_GET_OPTION(table_prefix, ...) \
+#ifndef MYSQL_GET_OPTION4
+#define MYSQL_GET_OPTION4(...) \
     "SELECT" \
     "  o.option_id," \
     "  o.code," \
@@ -185,10 +307,35 @@ namespace {
     "  o.shared_network_name," \
     "  o.pool_id," \
     "  o.modification_ts " \
-    "FROM " #table_prefix "_options AS o " \
-    "INNER JOIN " #table_prefix "_options_server AS a" \
+    "FROM dhcp4_options AS o " \
+    "INNER JOIN dhcp4_options_server AS a" \
     "  ON o.option_id = a.option_id " \
-    "INNER JOIN " #table_prefix "_server AS s" \
+    "INNER JOIN dhcp4_server AS s" \
+    "  ON a.server_id = s.id " \
+    "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \
+    " ORDER BY o.option_id"
+#endif
+
+#ifndef MYSQL_GET_OPTION6
+#define MYSQL_GET_OPTION6(...) \
+    "SELECT" \
+    "  o.option_id," \
+    "  o.code," \
+    "  o.value," \
+    "  o.formatted_value," \
+    "  o.space," \
+    "  o.persistent," \
+    "  o.dhcp6_subnet_id," \
+    "  o.scope_id," \
+    "  o.user_context," \
+    "  o.shared_network_name," \
+    "  o.pool_id," \
+    "  o.pd_pool_id," \
+    "  o.modification_ts " \
+    "FROM dhcp6_options AS o " \
+    "INNER JOIN dhcp6_options_server AS a" \
+    "  ON o.option_id = a.option_id " \
+    "INNER JOIN dhcp6_server AS s" \
     "  ON a.server_id = s.id " \
     "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \
     " ORDER BY o.option_id"
@@ -221,9 +368,9 @@ namespace {
     ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)"
 #endif
 
-#ifndef MYSQL_INSERT_POOL
-#define MYSQL_INSERT_POOL(table_prefix) \
-    "INSERT INTO " #table_prefix "_pool(" \
+#ifndef MYSQL_INSERT_POOL4
+#define MYSQL_INSERT_POOL4() \
+    "INSERT INTO dhcp4_pool(" \
     "  start_address," \
     "  end_address," \
     "  subnet_id," \
@@ -231,6 +378,27 @@ namespace {
     ") VALUES (?, ?, ?, ?)"
 #endif
 
+#ifndef MYSQL_INSERT_POOL6
+#define MYSQL_INSERT_POOL6() \
+    "INSERT INTO dhcp6_pool(" \
+    "  start_address," \
+    "  end_address," \
+    "  dhcp6_subnet_id," \
+    "  modification_ts" \
+    ") VALUES (?, ?, ?, ?)"
+#endif
+
+#ifndef MYSQL_INSERT_PD_POOL
+#define MYSQL_INSERT_PD_POOL() \
+    "INSERT INTO dhcp6_pd_pool(" \
+    " prefix," \
+    " prefix_length," \
+    " delegated_prefix_length," \
+    " dhcp6_subnet_id," \
+    " modification_ts" \
+    ") VALUES (?, ?, ?, ?, ?)"
+#endif
+
 #ifndef MYSQL_INSERT_SHARED_NETWORK_SERVER
 #define MYSQL_INSERT_SHARED_NETWORK_SERVER(table_prefix) \
     "INSERT INTO " #table_prefix "_shared_network_server(" \
@@ -267,16 +435,16 @@ namespace {
     ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)"
 #endif
 
-#ifndef MYSQL_INSERT_OPTION
-#define MYSQL_INSERT_OPTION(table_prefix) \
-    "INSERT INTO " #table_prefix "_options (" \
+#ifndef MYSQL_INSERT_OPTION4
+#define MYSQL_INSERT_OPTION4() \
+    "INSERT INTO dhcp4_options (" \
     "  code," \
     "  value," \
     "  formatted_value," \
     "  space," \
     "  persistent," \
     "  dhcp_client_class," \
-    " " #table_prefix "_subnet_id," \
+    "  dhcp4_subnet_id," \
     "  scope_id," \
     "  user_context," \
     "  shared_network_name," \
@@ -285,6 +453,25 @@ namespace {
     ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
 #endif
 
+#ifndef MYSQL_INSERT_OPTION6
+#define MYSQL_INSERT_OPTION6() \
+    "INSERT INTO dhcp6_options (" \
+    "  code," \
+    "  value," \
+    "  formatted_value," \
+    "  space," \
+    "  persistent," \
+    "  dhcp_client_class," \
+    "  dhcp6_subnet_id," \
+    "  scope_id," \
+    "  user_context," \
+    "  shared_network_name," \
+    "  pool_id," \
+    "  pd_pool_id," \
+    "  modification_ts" \
+    ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+#endif
+
 #ifndef MYSQL_INSERT_OPTION_SERVER
 #define MYSQL_INSERT_OPTION_SERVER(table_prefix) \
     "INSERT INTO " #table_prefix "_options_server (" \
@@ -328,12 +515,35 @@ namespace {
     "WHERE s.tag = ? AND d.code = ? AND d.space = ?"
 #endif
 
-#ifndef MYSQL_UPDATE_OPTION
-#define MYSQL_UPDATE_OPTION(table_prefix, ...) \
-    "UPDATE " #table_prefix "_options AS o " \
-    "INNER JOIN " #table_prefix "_options_server AS a" \
+#ifndef MYSQL_UPDATE_OPTION4
+#define MYSQL_UPDATE_OPTION4(...) \
+    "UPDATE dhcp4_options AS o " \
+    "INNER JOIN dhcp4_options_server AS a" \
     "  ON o.option_id = a.option_id " \
-    "INNER JOIN " #table_prefix "_server AS s" \
+    "INNER JOIN dhcp4_server AS s" \
+    "  ON a.server_id = s.id " \
+    "SET" \
+    "  o.code = ?," \
+    "  o.value = ?," \
+    "  o.formatted_value = ?," \
+    "  o.space = ?," \
+    "  o.persistent = ?," \
+    "  o.dhcp_client_class = ?," \
+    "  o.dhcp4_subnet_id = ?," \
+    "  o.scope_id = ?," \
+    "  o.user_context = ?," \
+    "  o.shared_network_name = ?," \
+    "  o.pool_id = ?," \
+    "  o.modification_ts = ? " \
+    "WHERE s.tag = ? " #__VA_ARGS__
+#endif
+
+#ifndef MYSQL_UPDATE_OPTION6
+#define MYSQL_UPDATE_OPTION6(...) \
+    "UPDATE dhcp6_options AS o " \
+    "INNER JOIN dhcp6_options_server AS a" \
+    "  ON o.option_id = a.option_id " \
+    "INNER JOIN dhcp6_server AS s" \
     "  ON a.server_id = s.id " \
     "SET" \
     "  o.code = ?," \
@@ -342,11 +552,12 @@ namespace {
     "  o.space = ?," \
     "  o.persistent = ?," \
     "  o.dhcp_client_class = ?," \
-    "  o." #table_prefix "_subnet_id = ?," \
+    "  o.dhcp6_subnet_id = ?," \
     "  o.scope_id = ?," \
     "  o.user_context = ?," \
     "  o.shared_network_name = ?," \
     "  o.pool_id = ?," \
+    "  o.pd_pool_id = ?," \
     "  o.modification_ts = ? " \
     "WHERE s.tag = ? " #__VA_ARGS__
 #endif
@@ -371,12 +582,24 @@ namespace {
     "WHERE srv.tag = ? " #__VA_ARGS__
 #endif
 
-#ifndef MYSQL_DELETE_POOLS
-#define MYSQL_DELETE_POOLS(table_prefix) \
-    "DELETE FROM " #table_prefix "_pool " \
+#ifndef MYSQL_DELETE_POOLS4
+#define MYSQL_DELETE_POOLS4() \
+    "DELETE FROM dhcp4_pool " \
     "WHERE subnet_id = ?"
 #endif
 
+#ifndef MYSQL_DELETE_POOLS6
+#define MYSQL_DELETE_POOLS6() \
+    "DELETE FROM dhcp6_pool " \
+    "WHERE dhcp6_subnet_id = ?"
+#endif
+
+#ifndef MYSQL_DELETE_PD_POOLS
+#define MYSQL_DELETE_PD_POOLS() \
+    "DELETE FROM dhcp6_pd_pool " \
+    "WHERE dhcp6_subnet_id = ?"
+#endif
+
 #ifndef MYSQL_DELETE_SHARED_NETWORK
 #define MYSQL_DELETE_SHARED_NETWORK(table_prefix, ...) \
     "DELETE n FROM " #table_prefix "_shared_network AS n " \
@@ -420,6 +643,19 @@ namespace {
     "   WHERE start_address = ? AND end_address = ?)"
 #endif
 
+#ifndef MYSQL_DELETE_OPTION_PD_POOL
+#define MYSQL_DELETE_OPTION_PD_POOL(...) \
+    "DELETE o FROM dhcp6_options AS o " \
+    "INNER JOIN dhcp6_options_server AS a" \
+    "  ON o.option_id = a.option_id " \
+    "INNER JOIN dhcp6_server AS s" \
+    "  ON a.server_id = s.id " \
+    "WHERE s.tag = ? " #__VA_ARGS__ \
+    "  AND o.pd_pool_id = " \
+    "  (SELECT id FROM dhcp6_pd_pool" \
+    "   WHERE prefix = ? AND prefix_length = ?)"
+#endif
+
 } // end of anonymous namespace
 
 } // end of namespace isc::dhcp
index 6ba01c6ecab5f67b80468c52f3138c67903da722..6e0f271facff39f11a5249e2ca42a194cd7364d6 100644 (file)
@@ -25,6 +25,8 @@ TESTS += mysql_cb_unittests
 
 mysql_cb_unittests_SOURCES  = mysql_cb_dhcp4_unittest.cc
 mysql_cb_unittests_SOURCES += mysql_cb_dhcp4_mgr_unittest.cc
+mysql_cb_unittests_SOURCES += mysql_cb_dhcp6_unittest.cc
+mysql_cb_unittests_SOURCES += mysql_cb_dhcp6_mgr_unittest.cc
 mysql_cb_unittests_SOURCES += run_unittests.cc
 
 mysql_cb_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
index 9643bdd7dbc66dbc78333fd50a4ac8f43e711d6b..bd5b83d447a466a506c0f7fc0c8c92225bad0caa 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018-2019 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
@@ -410,12 +410,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllGlobalParameters4) {
 
     // Fetch all parameters.
     auto parameters = cbptr_->getAllGlobalParameters4(ServerSelector::ALL());
-    ASSERT_EQ(3, parameters.size());
 
     // Verify their values.
-    EXPECT_EQ("value1", parameters[0]->getValue());
-    EXPECT_EQ(65, parameters[1]->getSignedIntegerValue());
-    EXPECT_EQ("value3", parameters[2]->getValue());
+    std::vector<StampedValuePtr> plist(parameters.begin(), parameters.end());
+    ASSERT_EQ(3, plist.size());
+    EXPECT_EQ("value1", plist[0]->getValue());
+    EXPECT_EQ(65, plist[1]->getSignedIntegerValue());
+    EXPECT_EQ("value3", plist[2]->getValue());
 
     // Should be able to fetct these parameters when explicitly providing
     // the server tag.
@@ -455,10 +456,9 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedGlobalParameters4) {
     auto parameters = cbptr_->getModifiedGlobalParameters4(ServerSelector::ALL(),
                                                            timestamps_["today"]);
 
-    // It should be the one modified "tomorrow". 
+    // It should be the one modified "tomorrow".
     ASSERT_EQ(1, parameters.size());
-    ASSERT_TRUE(parameters[0]);
-    EXPECT_EQ("value3", parameters[0]->getValue());
+    EXPECT_EQ("value3", (*parameters.begin())->getValue());
 
     // Should be able to fetct these parameters when explicitly providing
     // the server tag.
diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_mgr_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_mgr_unittest.cc
new file mode 100644 (file)
index 0000000..c4f8a48
--- /dev/null
@@ -0,0 +1,87 @@
+// Copyright (C) 2019 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 <config.h>
+#include <cc/stamped_value.h>
+#include <dhcpsrv/config_backend_dhcp6_mgr.h>
+#include <mysql_cb_dhcp6.h>
+#include <mysql/testutils/mysql_schema.h>
+#include <dhcpsrv/testutils/generic_backend_unittest.h>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::db;
+using namespace isc::db::test;
+
+namespace {
+
+/// @brief Test fixture class for @c MySqlConfigBackendDHCPv6Mgr.
+class MySqlConfigBackendDHCPv6MgrTest : public GenericBackendTest {
+public:
+    /// @brief Constructor.
+    MySqlConfigBackendDHCPv6MgrTest() {
+        // Recreate a fresh mgr.
+        ConfigBackendDHCPv6Mgr::create();
+
+        // Recreate database schema.
+        destroyMySQLSchema();
+        createMySQLSchema();
+    }
+
+    /// @brief Destructor.
+    virtual ~MySqlConfigBackendDHCPv6MgrTest() {
+        // Destroy the mgr.
+        ConfigBackendDHCPv6Mgr::destroy();
+        destroyMySQLSchema();
+    }
+};
+
+// This test verifies that MySQL backend can be registered with and
+// unregistered from the Config Backend Manager.
+TEST_F(MySqlConfigBackendDHCPv6MgrTest, factoryRegistration) {
+
+    // Get the mgr singleton.
+    ConfigBackendDHCPv6Mgr& mgr = ConfigBackendDHCPv6Mgr::instance();
+
+    // With no factory registered, attempting to add a MySQL db should fail.
+    ASSERT_THROW(mgr.addBackend(validMySQLConnectionString()), InvalidType);
+
+    // Now we'll register the MySQL factory.
+    ASSERT_NO_THROW(MySqlConfigBackendDHCPv6::registerBackendType());
+
+    // With the factory registered, attempting to add a MySQL db should succeed.
+    ASSERT_NO_THROW(mgr.addBackend(validMySQLConnectionString()));
+
+    // Create a MySQL backend selector for convenience.
+    BackendSelector mysql(BackendSelector::Type::MYSQL);
+
+    // Should be able to create a global parameter.
+    StampedValuePtr server_tag = StampedValue::create("server-tag", "whale");
+    ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(mysql, ServerSelector::ALL(),
+                                                                server_tag));
+    // Verify parameter can be fetched.
+    server_tag.reset();
+    ASSERT_NO_THROW(server_tag = mgr.getPool()->getGlobalParameter6(mysql, ServerSelector::ALL(),
+                                                                    "server-tag"));
+    ASSERT_TRUE(server_tag);
+    EXPECT_EQ("server-tag", server_tag->getName());
+    EXPECT_EQ("whale", server_tag->getValue());
+
+    // Now we'll unregister MySQL.
+    ASSERT_NO_THROW(MySqlConfigBackendDHCPv6::unregisterBackendType());
+
+    // With no factory registered, attempting to add a MySQL db should fail.
+    ASSERT_THROW(mgr.addBackend(validMySQLConnectionString()), InvalidType);
+
+    // Attempting to read the global parameter should fail.
+    ASSERT_THROW(mgr.getPool()->getGlobalParameter6(mysql, ServerSelector::ALL(), "server-tag"),
+                 NoSuchDatabase);
+}
+
+}
diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc
new file mode 100644 (file)
index 0000000..4a25db2
--- /dev/null
@@ -0,0 +1,1216 @@
+// Copyright (C) 2019 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 <config.h>
+#include <mysql_cb_dhcp6.h>
+#include <database/testutils/schema.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_space.h>
+#include <dhcp/option_string.h>
+#include <dhcpsrv/pool.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/testutils/generic_backend_unittest.h>
+#include <mysql/testutils/mysql_schema.h>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <map>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::db::test;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Test fixture class for @c MySqlConfigBackendDHCPv6.
+///
+/// @todo The tests we're providing here only test cases when the
+/// server selector is set to 'ALL' (configuration elements belong to
+/// all servers). Currently we have no API to insert servers into
+/// the database, and therefore we can't test the case when
+/// configuration elements are assigned to particular servers by
+/// server tags. We will have to expand existing tests when
+/// the API is extended allowing for inserting servers to the
+/// database.
+class MySqlConfigBackendDHCPv6Test : public GenericBackendTest {
+public:
+
+    /// @brief Constructor.
+    MySqlConfigBackendDHCPv6Test()
+        : test_subnets_(), test_networks_(), timestamps_() {
+        // Recreate database schema.
+        destroyMySQLSchema();
+        createMySQLSchema();
+
+        try {
+            // Create MySQL connection and use it to start the backend.
+            DatabaseConnection::ParameterMap params =
+                DatabaseConnection::parse(validMySQLConnectionString());
+            cbptr_.reset(new MySqlConfigBackendDHCPv6(params));
+
+        } catch (...) {
+            std::cerr << "*** ERROR: unable to open database. The test\n"
+                         "*** environment is broken and must be fixed before\n"
+                         "*** the MySQL tests will run correctly.\n"
+                         "*** The reason for the problem is described in the\n"
+                         "*** accompanying exception output.\n";
+            throw;
+        }
+
+        // Create test data.
+        initTestOptions();
+        initTestSubnets();
+        initTestSharedNetworks();
+        initTestOptionDefs();
+        initTimestamps();
+    }
+
+    /// @brief Destructor.
+    virtual ~MySqlConfigBackendDHCPv6Test() {
+        cbptr_.reset();
+        destroyMySQLSchema();
+    }
+
+    /// @brief Creates several subnets used in tests.
+    void initTestSubnets() {
+        // First subnet includes all parameters.
+        std::string interface_id_text = "whale";
+        OptionBuffer interface_id(interface_id_text.begin(), interface_id_text.end());
+        ElementPtr user_context = Element::createMap();
+        user_context->set("foo", Element::create("bar"));
+
+        Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 64, 30, 40, 50, 60, 1024));
+        subnet->allowClientClass("home");
+        subnet->setIface("eth1");
+        subnet->setT2(323212);
+        subnet->addRelayAddress(IOAddress("2001:db8:1::2"));
+        subnet->addRelayAddress(IOAddress("2001:db8:3::4"));
+        subnet->setT1(1234);
+        subnet->requireClientClass("required-class1");
+        subnet->requireClientClass("required-class2");
+        subnet->setHostReservationMode(Subnet4::HR_DISABLED);
+        subnet->setContext(user_context);
+        subnet->setValid(555555);
+
+        Pool6Ptr pool1(new Pool6(Lease::TYPE_NA,
+                                 IOAddress("2001:db8::10"),
+                                 IOAddress("2001:db8::20")));
+        subnet->addPool(pool1);
+
+        Pool6Ptr pool2(new Pool6(Lease::TYPE_NA,
+                                 IOAddress("2001:db8::50"),
+                                 IOAddress("2001:db8::60")));
+        subnet->addPool(pool2);
+
+        // Add several options to the subnet.
+        subnet->getCfgOption()->add(test_options_[0]->option_,
+                                    test_options_[0]->persistent_,
+                                    test_options_[0]->space_name_);
+
+        subnet->getCfgOption()->add(test_options_[1]->option_,
+                                    test_options_[1]->persistent_,
+                                    test_options_[1]->space_name_);
+
+        subnet->getCfgOption()->add(test_options_[2]->option_,
+                                    test_options_[2]->persistent_,
+                                    test_options_[2]->space_name_);
+
+        test_subnets_.push_back(subnet);
+
+        // Adding another subnet with the same subnet id to test
+        // cases that this second instance can override existing
+        // subnet instance.
+        subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 20, 30, 40, 50, 1024));
+
+        pool1.reset(new Pool6(Lease::TYPE_NA,
+                              IOAddress("2001:db8:1::10"),
+                              IOAddress("2001:db8:1::20")));
+        subnet->addPool(pool1);
+
+        pool1->getCfgOption()->add(test_options_[3]->option_,
+                                   test_options_[3]->persistent_,
+                                   test_options_[3]->space_name_);
+
+        pool1->getCfgOption()->add(test_options_[4]->option_,
+                                   test_options_[4]->persistent_,
+                                   test_options_[4]->space_name_);
+
+        pool2.reset(new Pool6(Lease::TYPE_NA,
+                              IOAddress("2001:db8:1::50"),
+                              IOAddress("2001:db8:1::60")));
+        subnet->addPool(pool2);
+
+        test_subnets_.push_back(subnet);
+
+        subnet.reset(new Subnet6(IOAddress("2001:db8:3::0"), 64, 20, 30, 40, 50, 2048));
+        test_subnets_.push_back(subnet);
+
+        subnet.reset(new Subnet6(IOAddress("2001:db8:4::0"), 64, 30, 40, 60, 4096));
+        test_subnets_.push_back(subnet);
+    }
+
+    /// @brief Creates several subnets used in tests.
+    void initTestSharedNetworks() {
+        ElementPtr user_context = Element::createMap();
+        user_context->set("foo", Element::create("bar"));
+
+        SharedNetwork6Ptr shared_network(new SharedNetwork6("level1"));
+        shared_network->allowClientClass("foo");
+        shared_network->setIface("eth1");
+        shared_network->setT2(323212);
+        shared_network->addRelayAddress(IOAddress("2001:db8:1::2"));
+        shared_network->addRelayAddress(IOAddress("2001:db8:3::4"));
+        shared_network->setT1(1234);
+        shared_network->requireClientClass("required-class1");
+        shared_network->requireClientClass("required-class2");
+        shared_network->setHostReservationMode(Subnet6::HR_DISABLED);
+        shared_network->setContext(user_context);
+        shared_network->setValid(5555);
+
+        // Add several options to the shared network.
+        shared_network->getCfgOption()->add(test_options_[2]->option_,
+                                            test_options_[2]->persistent_,
+                                            test_options_[2]->space_name_);
+
+        shared_network->getCfgOption()->add(test_options_[3]->option_,
+                                            test_options_[3]->persistent_,
+                                            test_options_[3]->space_name_);
+
+        shared_network->getCfgOption()->add(test_options_[4]->option_,
+                                            test_options_[4]->persistent_,
+                                            test_options_[4]->space_name_);
+
+        test_networks_.push_back(shared_network);
+
+        // Adding another shared network called "level1" to test
+        // cases that this second instance can override existing
+        // "level1" instance.
+        shared_network.reset(new SharedNetwork6("level1"));
+        test_networks_.push_back(shared_network);
+
+        // Add more shared networks.
+        shared_network.reset(new SharedNetwork6("level2"));
+        test_networks_.push_back(shared_network);
+
+        shared_network.reset(new SharedNetwork6("level3"));
+        test_networks_.push_back(shared_network);
+    }
+
+    /// @brief Creates several option definitions used in tests.
+    void initTestOptionDefs() {
+        ElementPtr user_context = Element::createMap();
+        user_context->set("foo", Element::create("bar"));
+
+        OptionDefinitionPtr option_def(new OptionDefinition("foo", 234, "string",
+                                                            "espace"));
+        option_def->setOptionSpaceName("dhcp6");
+        test_option_defs_.push_back(option_def);
+
+        option_def.reset(new OptionDefinition("bar", 234, "uint32", true));
+        option_def->setOptionSpaceName("dhcp6");
+        test_option_defs_.push_back(option_def);
+
+        option_def.reset(new OptionDefinition("fish", 235, "record", true));
+        option_def->setOptionSpaceName("dhcp6");
+        option_def->addRecordField("uint32");
+        option_def->addRecordField("string");
+        test_option_defs_.push_back(option_def);
+
+        option_def.reset(new OptionDefinition("whale", 236, "string"));
+        option_def->setOptionSpaceName("xyz");
+        test_option_defs_.push_back(option_def);
+    }
+
+    /// @brief Creates several DHCP options used in tests.
+    void initTestOptions() {
+        ElementPtr user_context = Element::createMap();
+        user_context->set("foo", Element::create("bar"));
+
+        OptionDefSpaceContainer defs;
+
+        OptionDescriptor desc =
+            createOption<OptionString>(Option::V6, D6O_NEW_POSIX_TIMEZONE,
+                                       true, false, "my-timezone");
+        desc.space_name_ = DHCP6_OPTION_SPACE;
+        desc.setContext(user_context);
+        test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+        desc = createOption<OptionUint8>(Option::V6, D6O_PREFERENCE,
+                                         false, true, 64);
+        desc.space_name_ = DHCP6_OPTION_SPACE;
+        test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+        desc = createOption<OptionUint32>(Option::V6, 1, false, false, 312131),
+        desc.space_name_ = "vendor-encapsulated-options";
+        test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+        desc = createAddressOption<Option6AddrLst>(254, true, true,
+                                                   "2001:db8::3");
+        desc.space_name_ = DHCP6_OPTION_SPACE;
+        test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+        desc = createEmptyOption(Option::V6, 1, true);
+        desc.space_name_ = "isc";
+        test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+        desc = createAddressOption<Option6AddrLst>(2, false, true, "2001:db8:1::5",
+                                                   "2001:db8:1::3", "2001:db8:4::4");
+        desc.space_name_ = "isc";
+        test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+        // Add definitions for DHCPv6 non-standard options.
+        defs.addItem(OptionDefinitionPtr(new OptionDefinition(
+                         "vendor-encapsulated-1", 1, "uint32")),
+                     "vendor-encapsulated-options");
+        defs.addItem(OptionDefinitionPtr(new OptionDefinition(
+                         "option-254", 254, "ipv6-address", true)),
+                     DHCP6_OPTION_SPACE);
+        defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-1", 1, "empty")), "isc");
+        defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-2", 2, "ipv6-address", true)),
+                     "isc");
+
+        // Register option definitions.
+        LibDHCP::setRuntimeOptionDefs(defs);
+    }
+
+    /// @brief Initialize posix time values used in tests.
+    void initTimestamps() {
+        // Current time minus 1 hour to make sure it is in the past.
+        timestamps_["today"] = boost::posix_time::second_clock::universal_time()
+            - boost::posix_time::hours(1);
+        // Yesterday.
+        timestamps_["yesterday"] = timestamps_["today"] - boost::posix_time::hours(24);
+        // Tomorrow.
+        timestamps_["tomorrow"] = timestamps_["today"] + boost::posix_time::hours(24);
+    }
+
+    /// @brief Holds pointers to subnets used in tests.
+    std::vector<Subnet6Ptr> test_subnets_;
+
+    /// @brief Holds pointers to shared networks used in tests.
+    std::vector<SharedNetwork6Ptr> test_networks_;
+
+    /// @brief Holds pointers to option definitions used in tests.
+    std::vector<OptionDefinitionPtr> test_option_defs_;
+
+    /// @brief Holds pointers to options used in tests.
+    std::vector<OptionDescriptorPtr> test_options_;
+
+    /// @brief Holds timestamp values used in tests.
+    std::map<std::string, boost::posix_time::ptime> timestamps_;
+
+    /// @brief Holds pointer to the backend.
+    boost::shared_ptr<ConfigBackendDHCPv6> cbptr_;
+};
+
+// This test verifies that the expected backend type is returned.
+TEST_F(MySqlConfigBackendDHCPv6Test, getType) {
+    DatabaseConnection::ParameterMap params;
+    params["name"] = "keatest";
+    params["password"] = "keatest";
+    params["user"] = "keatest";
+    ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params)));
+    EXPECT_EQ("mysql", cbptr_->getType());
+}
+
+// This test verifies that by default localhost is returned as MySQL connection
+// host.
+TEST_F(MySqlConfigBackendDHCPv6Test, getHost) {
+    DatabaseConnection::ParameterMap params;
+    params["name"] = "keatest";
+    params["password"] = "keatest";
+    params["user"] = "keatest";
+    ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params)));
+    EXPECT_EQ("localhost", cbptr_->getHost());
+}
+
+// This test verifies that by default port of 0 is returned as MySQL connection
+// port.
+TEST_F(MySqlConfigBackendDHCPv6Test, getPort) {
+    DatabaseConnection::ParameterMap params;
+    params["name"] = "keatest";
+    params["password"] = "keatest";
+    params["user"] = "keatest";
+    ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params)));
+    EXPECT_EQ(0, cbptr_->getPort());
+}
+
+// This test verifies that the global parameter can be added, updated and
+// deleted.
+TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteGlobalParameter6) {
+    StampedValuePtr global_parameter = StampedValue::create("global", "whale");
+
+    // Explicitly set modification time to make sure that the time
+    // returned from the database is correct.
+    global_parameter->setModificationTime(timestamps_["yesterday"]);
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         global_parameter);
+
+    // Verify returned parameter and the modification time.
+    StampedValuePtr returned_global_parameter =
+        cbptr_->getGlobalParameter6(ServerSelector::ALL(), "global");
+    ASSERT_TRUE(returned_global_parameter);
+    EXPECT_EQ("global", returned_global_parameter->getName());
+    EXPECT_EQ("whale", returned_global_parameter->getValue());
+    EXPECT_TRUE(returned_global_parameter->getModificationTime() ==
+                global_parameter->getModificationTime());
+
+    // Because we have added the global parameter for all servers, it
+    // should be also returned for the explicitly specified server.
+    returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ONE("server1"),
+                                                            "global");
+    ASSERT_TRUE(returned_global_parameter);
+    EXPECT_EQ("global", returned_global_parameter->getName());
+    EXPECT_EQ("whale", returned_global_parameter->getValue());
+    EXPECT_TRUE(returned_global_parameter->getModificationTime() ==
+                global_parameter->getModificationTime());
+
+    // Check that the parameter is udpated when selector is specified correctly.
+    global_parameter = StampedValue::create("global", "fish");
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         global_parameter);
+    returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ALL(),
+                                                            "global");
+    ASSERT_TRUE(returned_global_parameter);
+    EXPECT_EQ("global", returned_global_parameter->getName());
+    EXPECT_EQ("fish", returned_global_parameter->getValue());
+    EXPECT_TRUE(returned_global_parameter->getModificationTime() ==
+                global_parameter->getModificationTime());
+
+    // Should not delete parameter specified for all servers if explicit
+    // server name is provided.
+    EXPECT_EQ(0, cbptr_->deleteGlobalParameter6(ServerSelector::ONE("server1"),
+                                                "global"));
+
+    // Delete parameter and make sure it is gone.
+    cbptr_->deleteGlobalParameter6(ServerSelector::ALL(), "global");
+    returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ALL(),
+                                                            "global");
+    EXPECT_FALSE(returned_global_parameter);
+}
+
+// This test verifies that all global parameters can be retrieved and deleted.
+TEST_F(MySqlConfigBackendDHCPv6Test, getAllGlobalParameters6) {
+    // Create 3 parameters and put them into the database.
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         StampedValue::create("name1", "value1"));
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         StampedValue::create("name2", 65));
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         StampedValue::create("name3", "value3"));
+
+    // Fetch all parameters.
+    auto parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ALL());
+
+    // Verify their values.
+    std::vector<StampedValuePtr> plist(parameters.begin(), parameters.end());
+    ASSERT_EQ(3, plist.size());
+    EXPECT_EQ("value1", plist[0]->getValue());
+    EXPECT_EQ(65, plist[1]->getSignedIntegerValue());
+    EXPECT_EQ("value3", plist[2]->getValue());
+
+    // Should be able to fetct these parameters when explicitly providing
+    // the server tag.
+    parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ONE("server1"));
+    EXPECT_EQ(3, parameters.size());
+
+    // Deleting global parameters with non-matching server selector
+    // should fail.
+    EXPECT_EQ(0, cbptr_->deleteAllGlobalParameters6(ServerSelector::ONE("server1")));
+
+    // Delete all parameters and make sure they are gone.
+    EXPECT_EQ(3, cbptr_->deleteAllGlobalParameters6(ServerSelector::ALL()));
+    parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ALL());
+    EXPECT_TRUE(parameters.empty());
+}
+
+// This test verifies that modified global parameters can be retrieved.
+TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedGlobalParameters6) {
+    // Create 3 global parameters and assign modification times:
+    // "yesterday", "today" and "tomorrow" respectively.
+    StampedValuePtr value = StampedValue::create("name1", "value1");
+    value->setModificationTime(timestamps_["yesterday"]);
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         value);
+
+    value = StampedValue::create("name2", 65);
+    value->setModificationTime(timestamps_["today"]);
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         value);
+
+    value = StampedValue::create("name3", "value3");
+    value->setModificationTime(timestamps_["tomorrow"]);
+    cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(),
+                                         value);
+
+    // Get parameters modified after "today".
+    auto parameters = cbptr_->getModifiedGlobalParameters6(ServerSelector::ALL(),
+                                                           timestamps_["today"]);
+
+    // It should be the one modified "tomorrow".
+    ASSERT_EQ(1, parameters.size());
+    EXPECT_EQ("value3", (*parameters.begin())->getValue());
+
+    // Should be able to fetct these parameters when explicitly providing
+    // the server tag.
+    parameters = cbptr_->getModifiedGlobalParameters6(ServerSelector::ONE("server1"),
+                                                      timestamps_["today"]);
+    EXPECT_EQ(1, parameters.size());
+}
+
+// Test that subnet can be inserted, fetched, updated and then fetched again.
+TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
+    // Insert new subnet.
+    Subnet6Ptr subnet = test_subnets_[0];
+    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
+
+    // Fetch this subnet by subnet identifier.
+    Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                                    test_subnets_[0]->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    // The easiest way to verify whether the returned subnet matches the inserted
+    // subnet is to convert both to text.
+    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+
+    // Update the subnet in the database (both use the same ID).
+    Subnet6Ptr subnet2 = test_subnets_[1];
+    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet2);
+
+    // Fetch updated subnet and see if it matches.
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                         SubnetID(1024));
+    EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
+
+    // Fetching the subnet for an explicitly specified server tag should
+    // succeeed too.
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
+                                         SubnetID(1024));
+    EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
+}
+
+// Test that subnet can be associated with a shared network.
+TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6SharedNetwork) {
+    Subnet6Ptr subnet = test_subnets_[0];
+    SharedNetwork6Ptr shared_network = test_networks_[0];
+
+    // Add subnet to a shared network.
+    shared_network->add(subnet);
+
+    // Store shared network in the database.
+    cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
+                                       shared_network);
+
+    // Store subnet associated with the shared network in the database.
+    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
+
+    // Fetch this subnet by subnet identifier.
+    Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                                    test_subnets_[0]->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    // The easiest way to verify whether the returned subnet matches the inserted
+    // subnet is to convert both to text.
+    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+
+    // However, the check above doesn't verify whether shared network name was
+    // correctly returned from the database.
+    EXPECT_EQ(shared_network->getName(), returned_subnet->getSharedNetworkName());
+}
+
+// Test that subnet can be fetched by prefix.
+TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6ByPrefix) {
+    // Insert subnet to the database.
+    Subnet6Ptr subnet = test_subnets_[0];
+    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
+
+    // Fetch the subnet by prefix.
+    Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                                    "2001:db8::/64");
+    ASSERT_TRUE(returned_subnet);
+
+    // Verify subnet contents.
+    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+
+    // Fetching the subnet for an explicitly specified server tag should
+    // succeeed too.
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
+                                         "2001:db8::/64");
+    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+}
+
+// Test that all subnets can be fetched and then deleted.
+TEST_F(MySqlConfigBackendDHCPv6Test, getAllSubnets6) {
+    // Insert test subnets into the database. Note that the second subnet will
+    // overwrite the first subnet as they use the same ID.
+    for (auto subnet : test_subnets_) {
+        cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
+    }
+
+    // Fetch all subnets.
+    Subnet6Collection subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
+    ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
+
+    // All subnets should also be returned for explicitly specified server tag.
+    subnets = cbptr_->getAllSubnets6(ServerSelector::ONE("server1"));
+    ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
+
+    // See if the subnets are returned ok.
+    for (auto i = 0; i < subnets.size(); ++i) {
+        EXPECT_EQ(test_subnets_[i + 1]->toElement()->str(),
+                  subnets[i]->toElement()->str());
+    }
+
+    // Attempt to remove the non existing subnet should  return 0.
+    EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ALL(), 22));
+    EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ALL(),
+                                       "2001:db8:5::/64"));
+    // All subnets should be still there.
+    ASSERT_EQ(test_subnets_.size() - 1, subnets.size());
+
+    // Should not delete the subnet for explicit server tag because
+    // our subnet is for all servers.
+    EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ONE("server1"),
+                                       test_subnets_[1]->getID()));
+
+    // Also, verify that behavior when deleting by prefix.
+    EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ONE("server1"),
+                                       test_subnets_[2]->toText()));
+
+    // Same for all subnets.
+    EXPECT_EQ(0, cbptr_->deleteAllSubnets6(ServerSelector::ONE("server1")));
+
+    // Delete first subnet by id and verify that it is gone.
+    EXPECT_EQ(1, cbptr_->deleteSubnet6(ServerSelector::ALL(),
+                                       test_subnets_[1]->getID()));
+    subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
+    ASSERT_EQ(test_subnets_.size() - 2, subnets.size());
+
+    // Delete second subnet by prefix and verify it is gone.
+    EXPECT_EQ(1, cbptr_->deleteSubnet6(ServerSelector::ALL(),
+                                       test_subnets_[2]->toText()));
+    subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
+    ASSERT_EQ(test_subnets_.size() - 3, subnets.size());
+
+    // Delete all.
+    EXPECT_EQ(1, cbptr_->deleteAllSubnets6(ServerSelector::ALL()));
+    subnets = cbptr_->getAllSubnets6(ServerSelector::ALL());
+    ASSERT_TRUE(subnets.empty());
+}
+
+// Test that subnets modified after given time can be fetched.
+TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSubnets6) {
+    // Explicitly set timestamps of subnets. First subnet has a timestamp
+    // pointing to the future. Second subnet has timestamp pointing to the
+    // past (yesterday). Third subnet has a timestamp pointing to the
+    // past (an hour ago).
+    test_subnets_[1]->setModificationTime(timestamps_["tomorrow"]);
+    test_subnets_[2]->setModificationTime(timestamps_["yesterday"]);
+    test_subnets_[3]->setModificationTime(timestamps_["today"]);
+
+    // Insert subnets into the database.
+    for (int i = 1; i < test_subnets_.size(); ++i) {
+        cbptr_->createUpdateSubnet6(ServerSelector::ALL(),
+                                    test_subnets_[i]);
+    }
+
+    // Fetch subnets with timestamp later than today. Only one subnet
+    // should be returned.
+    Subnet6Collection
+        subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(),
+                                              timestamps_["today"]);
+    ASSERT_EQ(1, subnets.size());
+
+    // All subnets should also be returned for explicitly specified server tag.
+    subnets = cbptr_->getModifiedSubnets6(ServerSelector::ONE("server1"),
+                                          timestamps_["today"]);
+    ASSERT_EQ(1, subnets.size());
+
+    // Fetch subnets with timestamp later than yesterday. We should get
+    // two subnets.
+    subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(),
+                                          timestamps_["yesterday"]);
+    ASSERT_EQ(2, subnets.size());
+
+    // Fetch subnets with timestamp later than tomorrow. Nothing should
+    // be returned.
+    subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(),
+                                          timestamps_["tomorrow"]);
+    ASSERT_TRUE(subnets.empty());
+}
+
+// Test that shared network can be inserted, fetched, updated and then
+// fetched again.
+TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6) {
+    // Insert new shared network.
+    SharedNetwork6Ptr shared_network = test_networks_[0];
+    cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network);
+
+    // Fetch this shared network by name.
+    SharedNetwork6Ptr
+        returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
+                                                     test_networks_[0]->getName());
+    ASSERT_TRUE(returned_network);
+
+    // The easiest way to verify whether the returned shared network matches the
+    // inserted shared network is to convert both to text.
+    EXPECT_EQ(shared_network->toElement()->str(),
+              returned_network->toElement()->str());
+
+    // Update shared network in the database.
+    SharedNetwork6Ptr shared_network2 = test_networks_[1];
+    cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network2);
+
+    // Fetch updated subnet and see if it matches.
+    returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
+                                                 test_networks_[1]->getName());
+    EXPECT_EQ(shared_network2->toElement()->str(),
+              returned_network->toElement()->str());
+
+    // Fetching the shared network for an explicitly specified server tag should
+    // succeed too.
+    returned_network = cbptr_->getSharedNetwork6(ServerSelector::ONE("server1"),
+                                                 shared_network2->getName());
+    EXPECT_EQ(shared_network2->toElement()->str(),
+              returned_network->toElement()->str());
+}
+
+// Test that all shared networks can be fetched.
+TEST_F(MySqlConfigBackendDHCPv6Test, getAllSharedNetworks6) {
+    // Insert test shared networks into the database. Note that the second shared
+    // network will overwrite the first shared network as they use the same name.
+    for (auto network : test_networks_) {
+        cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), network);
+    }
+
+    // Fetch all shared networks.
+    SharedNetwork6Collection networks =
+        cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
+    ASSERT_EQ(test_networks_.size() - 1, networks.size());
+
+    // All shared networks should also be returned for explicitly specified
+    // server tag.
+    networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server1"));
+    ASSERT_EQ(test_networks_.size() - 1, networks.size());
+
+    // See if shared networks are returned ok.
+    for (auto i = 0; i < networks.size(); ++i) {
+        EXPECT_EQ(test_networks_[i + 1]->toElement()->str(),
+                  networks[i]->toElement()->str());
+    }
+
+    // Deleting non-existing shared network should return 0.
+    EXPECT_EQ(0, cbptr_->deleteSharedNetwork6(ServerSelector::ALL(),
+                                              "big-fish"));
+    // All shared networks should be still there.
+    ASSERT_EQ(test_networks_.size() - 1, networks.size());
+
+    // Should not delete the subnet for explicit server tag because
+    // our shared network is for all servers.
+    EXPECT_EQ(0, cbptr_->deleteSharedNetwork6(ServerSelector::ONE("server1"),
+                                              test_networks_[1]->getName()));
+
+    // Same for all shared networks.
+    EXPECT_EQ(0, cbptr_->deleteAllSharedNetworks6(ServerSelector::ONE("server1")));
+
+    // Delete first shared network and verify it is gone.
+    EXPECT_EQ(1, cbptr_->deleteSharedNetwork6(ServerSelector::ALL(),
+                                              test_networks_[1]->getName()));
+    networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
+    ASSERT_EQ(test_networks_.size() - 2, networks.size());
+
+    // Delete all.
+    EXPECT_EQ(2, cbptr_->deleteAllSharedNetworks6(ServerSelector::ALL()));
+    networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL());
+    ASSERT_TRUE(networks.empty());
+}
+
+// Test that shared networks modified after given time can be fetched.
+TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSharedNetworks6) {
+    // Explicitly set timestamps of shared networks. First shared
+    // network has a timestamp pointing to the future. Second shared
+    // network has timestamp pointing to the past (yesterday).
+    // Third shared network has a timestamp pointing to the
+    // past (an hour ago).
+    test_networks_[1]->setModificationTime(timestamps_["tomorrow"]);
+    test_networks_[2]->setModificationTime(timestamps_["yesterday"]);
+    test_networks_[3]->setModificationTime(timestamps_["today"]);
+
+    // Insert shared networks into the database.
+    for (int i = 1; i < test_networks_.size(); ++i) {
+        cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
+                                           test_networks_[i]);
+    }
+
+    // Fetch shared networks with timestamp later than today. Only one
+    // shared network  should be returned.
+    SharedNetwork6Collection
+        networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(),
+                                                      timestamps_["today"]);
+    ASSERT_EQ(1, networks.size());
+
+    // Fetch shared networks with timestamp later than yesterday. We
+    // should get two shared networks.
+    networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(),
+                                                 timestamps_["yesterday"]);
+    ASSERT_EQ(2, networks.size());
+
+    // Fetch shared networks with timestamp later than tomorrow. Nothing
+    // should be returned.
+    networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(),
+                                                  timestamps_["tomorrow"]);
+    ASSERT_TRUE(networks.empty());
+}
+
+// Test that option definition can be inserted, fetched, updated and then
+// fetched again.
+TEST_F(MySqlConfigBackendDHCPv6Test, getOptionDef6) {
+    // Insert new option definition.
+    OptionDefinitionPtr option_def = test_option_defs_[0];
+    cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def);
+
+    // Fetch this option_definition by subnet identifier.
+    OptionDefinitionPtr returned_option_def =
+        cbptr_->getOptionDef6(ServerSelector::ALL(),
+                              test_option_defs_[0]->getCode(),
+                              test_option_defs_[0]->getOptionSpaceName());
+    ASSERT_TRUE(returned_option_def);
+
+    EXPECT_TRUE(returned_option_def->equals(*option_def));
+
+    // Update the option definition in the database.
+    OptionDefinitionPtr option_def2 = test_option_defs_[1];
+    cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def2);
+
+    // Fetch updated option definition and see if it matches.
+    returned_option_def = cbptr_->getOptionDef6(ServerSelector::ALL(),
+                                                test_option_defs_[1]->getCode(),
+                                                test_option_defs_[1]->getOptionSpaceName());
+    EXPECT_TRUE(returned_option_def->equals(*option_def2));
+
+    // Fetching option definition for an explicitly specified server tag
+    // should succeed too.
+    returned_option_def = cbptr_->getOptionDef6(ServerSelector::ONE("server1"),
+                                                test_option_defs_[1]->getCode(),
+                                                test_option_defs_[1]->getOptionSpaceName());
+    EXPECT_TRUE(returned_option_def->equals(*option_def2));
+}
+
+// Test that all option definitions can be fetched.
+TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptionDefs6) {
+    // Insert test option definitions into the database. Note that the second
+    // option definition will overwrite the first option definition as they use
+    // the same code and space.
+    for (auto option_def : test_option_defs_) {
+        cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def);
+    }
+
+    // Fetch all option_definitions.
+    OptionDefContainer option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
+    ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+
+    // All option definitions should also be returned for explicitly specified
+    // server tag.
+    option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ONE("server1"));
+    ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+
+    // See if option definitions are returned ok.
+    for (auto def = option_defs.begin(); def != option_defs.end(); ++def) {
+        bool success = false;
+        for (auto i = 1; i < test_option_defs_.size(); ++i) {
+            if ((*def)->equals(*test_option_defs_[i])) {
+                success = true;
+            }
+        }
+        ASSERT_TRUE(success) << "failed for option definition " << (*def)->getCode()
+            << ", option space " << (*def)->getOptionSpaceName();
+    }
+
+    // Deleting non-existing option definition should return 0.
+    EXPECT_EQ(0, cbptr_->deleteOptionDef6(ServerSelector::ALL(),
+                                          99, "non-exiting-space"));
+    // All option definitions should be still there.
+    ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size());
+
+    // Should not delete option definition for explicit server tag
+    // because our option definition is for all servers.
+    EXPECT_EQ(0, cbptr_->deleteOptionDef6(ServerSelector::ONE("server1"),
+                                          test_option_defs_[1]->getCode(),
+                                          test_option_defs_[1]->getOptionSpaceName()));
+
+    // Same for all option definitions.
+    EXPECT_EQ(0, cbptr_->deleteAllOptionDefs6(ServerSelector::ONE("server1")));
+
+    // Delete one of the option definitions and see if it is gone.
+    EXPECT_EQ(1, cbptr_->deleteOptionDef6(ServerSelector::ALL(),
+                                          test_option_defs_[2]->getCode(),
+                                          test_option_defs_[2]->getOptionSpaceName()));
+    ASSERT_FALSE(cbptr_->getOptionDef6(ServerSelector::ALL(),
+                                       test_option_defs_[2]->getCode(),
+                                       test_option_defs_[2]->getOptionSpaceName()));
+
+    // Delete all remaining option definitions.
+    EXPECT_EQ(2, cbptr_->deleteAllOptionDefs6(ServerSelector::ALL()));
+    option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL());
+    ASSERT_TRUE(option_defs.empty());
+}
+
+// Test that option definitions modified after given time can be fetched.
+TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedOptionDefs6) {
+    // Explicitly set timestamps of option definitions. First option
+    // definition has a timestamp pointing to the future. Second option
+    // definition has timestamp pointing to the past (yesterday).
+    // Third option definitions has a timestamp pointing to the
+    // past (an hour ago).
+    test_option_defs_[1]->setModificationTime(timestamps_["tomorrow"]);
+    test_option_defs_[2]->setModificationTime(timestamps_["yesterday"]);
+    test_option_defs_[3]->setModificationTime(timestamps_["today"]);
+
+    // Insert option definitions into the database.
+    for (int i = 1; i < test_networks_.size(); ++i) {
+        cbptr_->createUpdateOptionDef6(ServerSelector::ALL(),
+                                       test_option_defs_[i]);
+    }
+
+    // Fetch option definitions with timestamp later than today. Only one
+    // option definition should be returned.
+    OptionDefContainer
+        option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(),
+                                                     timestamps_["today"]);
+    ASSERT_EQ(1, option_defs.size());
+
+    // Fetch option definitions with timestamp later than yesterday. We
+    // should get two option definitions.
+    option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(),
+                                                 timestamps_["yesterday"]);
+    ASSERT_EQ(2, option_defs.size());
+
+    // Fetch option definitions with timestamp later than tomorrow. Nothing
+    // should be returned.
+    option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(),
+                                              timestamps_["tomorrow"]);
+    ASSERT_TRUE(option_defs.empty());
+}
+
+// This test verifies that global option can be added, updated and deleted.
+TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteOption6) {
+    // Add option to the database.
+    OptionDescriptorPtr opt_boot_file_name = test_options_[0];
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                opt_boot_file_name);
+
+    // Make sure we can retrieve this option and that it is equal to the
+    // option we have inserted into the database.
+    OptionDescriptorPtr returned_opt_boot_file_name =
+        cbptr_->getOption6(ServerSelector::ALL(),
+                           opt_boot_file_name->option_->getType(),
+                           opt_boot_file_name->space_name_);
+    ASSERT_TRUE(returned_opt_boot_file_name);
+    EXPECT_TRUE(returned_opt_boot_file_name->equals(*opt_boot_file_name));
+
+    // Modify option and update it in the database.
+    opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                opt_boot_file_name);
+
+    // Retrieve the option again and make sure that updates were
+    // properly propagated to the database.
+    returned_opt_boot_file_name = cbptr_->getOption6(ServerSelector::ALL(),
+                                                     opt_boot_file_name->option_->getType(),
+                                                     opt_boot_file_name->space_name_);
+    ASSERT_TRUE(returned_opt_boot_file_name);
+    EXPECT_TRUE(returned_opt_boot_file_name->equals(*opt_boot_file_name));
+
+    // Deleting an option with explicitly specified server tag should fail.
+    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+
+    // Deleting option for all servers should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+
+    EXPECT_FALSE(cbptr_->getOption6(ServerSelector::ALL(),
+                                    opt_boot_file_name->option_->getType(),
+                                    opt_boot_file_name->space_name_));
+}
+
+// This test verifies that all global options can be retrieved.
+TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptions6) {
+    // Add three global options to the database.
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                test_options_[0]);
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                test_options_[1]);
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                test_options_[5]);
+
+    // Retrieve all these options.
+    OptionContainer returned_options = cbptr_->getAllOptions6(ServerSelector::ALL());
+    ASSERT_EQ(3, returned_options.size());
+
+    // Fetching global options with explicitly specified server tag should return
+    // the same result.
+    returned_options = cbptr_->getAllOptions6(ServerSelector::ONE("server1"));
+    ASSERT_EQ(3, returned_options.size());
+
+    // Get the container index used to search options by option code.
+    const OptionContainerTypeIndex& index = returned_options.get<1>();
+
+    // Verify that all options we put into the database were
+    // returned.
+    auto option0 = index.find(test_options_[0]->option_->getType());
+    ASSERT_FALSE(option0 == index.end());
+    EXPECT_TRUE(option0->equals(*test_options_[0]));
+
+    auto option1 = index.find(test_options_[1]->option_->getType());
+    ASSERT_FALSE(option1 == index.end());
+    EXPECT_TRUE(option1->equals(*test_options_[1]));
+
+    auto option5 = index.find(test_options_[5]->option_->getType());
+    ASSERT_FALSE(option5 == index.end());
+    EXPECT_TRUE(option5->equals(*test_options_[5]));
+}
+
+// This test verifies that modified global options can be retrieved.
+TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedOptions6) {
+    // Assign timestamps to the options we're going to store in the
+    // database.
+    test_options_[0]->setModificationTime(timestamps_["tomorrow"]);
+    test_options_[1]->setModificationTime(timestamps_["yesterday"]);
+    test_options_[5]->setModificationTime(timestamps_["today"]);
+
+    // Put options into the database.
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                test_options_[0]);
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                test_options_[1]);
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                test_options_[5]);
+
+    // Get options with the timestamp later than today. Only
+    // one option should be returned.
+    OptionContainer returned_options =
+        cbptr_->getModifiedOptions6(ServerSelector::ALL(),
+                                    timestamps_["today"]);
+    ASSERT_EQ(1, returned_options.size());
+
+    // Fetching modified options with explicitly specified server selector
+    // should return the same result.
+    returned_options = cbptr_->getModifiedOptions6(ServerSelector::ONE("server1"),
+                                                   timestamps_["today"]);
+    ASSERT_EQ(1, returned_options.size());
+
+    // The returned option should be the one with the timestamp
+    // set to tomorrow.
+    const OptionContainerTypeIndex& index = returned_options.get<1>();
+    auto option0 = index.find(test_options_[0]->option_->getType());
+    ASSERT_FALSE(option0 == index.end());
+    EXPECT_TRUE(option0->equals(*test_options_[0]));
+}
+
+// This test verifies that subnet level option can be added, updated and
+// deleted.
+TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSubnetOption6) {
+    // Insert new subnet.
+    Subnet6Ptr subnet = test_subnets_[1];
+    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
+
+    // Fetch this subnet by subnet identifier.
+    Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                                    subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    OptionDescriptorPtr opt_boot_file_name = test_options_[0];
+    cbptr_->createUpdateOption6(ServerSelector::ALL(), subnet->getID(),
+                                opt_boot_file_name);
+
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                         subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    OptionDescriptor returned_opt_boot_file_name =
+        returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
+    cbptr_->createUpdateOption6(ServerSelector::ALL(), subnet->getID(),
+                                opt_boot_file_name);
+
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                         subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+    returned_opt_boot_file_name =
+        returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    // Deleting an option with explicitly specified server tag should fail.
+    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
+                                       subnet->getID(),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+
+    // It should succeed for all servers.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(), subnet->getID(),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                         subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    EXPECT_FALSE(returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE).option_);
+}
+
+// This test verifies that option can be inserted, updated and deleted
+// from the pool.
+TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeletePoolOption6) {
+    // Insert new subnet.
+    Subnet6Ptr subnet = test_subnets_[1];
+    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
+
+    // Add an option into the pool.
+    const PoolPtr pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8::10"));
+    ASSERT_TRUE(pool);
+    OptionDescriptorPtr opt_boot_file_name = test_options_[0];
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                pool->getFirstAddress(),
+                                pool->getLastAddress(),
+                                opt_boot_file_name);
+
+    // Query for a subnet.
+    Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                                    subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+
+    // The returned subnet should include our pool.
+    const PoolPtr returned_pool = returned_subnet->getPool(Lease::TYPE_NA,
+                                                           IOAddress("2001:db8::10"));
+    ASSERT_TRUE(returned_pool);
+
+    // The pool should contain option we added earlier.
+    OptionDescriptor returned_opt_boot_file_name =
+        returned_pool->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    // Modify the option and update it in the database.
+    opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                pool->getFirstAddress(),
+                                pool->getLastAddress(),
+                                opt_boot_file_name);
+
+    // Fetch the subnet and the corresponding pool.
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                         subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+    const PoolPtr returned_pool1 = returned_subnet->getPool(Lease::TYPE_NA,
+                                                            IOAddress("2001:db8::10"));
+    ASSERT_TRUE(returned_pool1);
+
+    // Test that the option has been correctly updated in the database.
+    returned_opt_boot_file_name =
+        returned_pool1->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    // Deleting an option with explicitly specified server tag should fail.
+    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
+                                       pool->getFirstAddress(),
+                                       pool->getLastAddress(),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+
+    // Delete option for all servers should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(),
+                                       pool->getFirstAddress(),
+                                       pool->getLastAddress(),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+
+    // Fetch the subnet and the pool from the database again to make sure
+    // that the option is really gone.
+    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                         subnet->getID());
+    ASSERT_TRUE(returned_subnet);
+    const PoolPtr returned_pool2 = returned_subnet->getPool(Lease::TYPE_NA,
+                                                            IOAddress("2001:db8::10"));
+    ASSERT_TRUE(returned_pool2);
+
+    // Option should be gone.
+    EXPECT_FALSE(returned_pool2->getCfgOption()->get(DHCP6_OPTION_SPACE,
+                                                     D6O_NEW_POSIX_TIMEZONE).option_);
+}
+
+// This test verifies that shared network level option can be added,
+// updated and deleted.
+TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSharedNetworkOption6) {
+    // Insert new shared network.
+    SharedNetwork6Ptr shared_network = test_networks_[1];
+    cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
+                                       shared_network);
+
+    // Fetch this shared network by name.
+    SharedNetwork6Ptr returned_network =
+        cbptr_->getSharedNetwork6(ServerSelector::ALL(),
+                                  shared_network->getName());
+    ASSERT_TRUE(returned_network);
+
+    OptionDescriptorPtr opt_boot_file_name = test_options_[0];
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                shared_network->getName(),
+                                opt_boot_file_name);
+
+    returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
+                                                shared_network->getName());
+    ASSERT_TRUE(returned_network);
+
+    OptionDescriptor returned_opt_boot_file_name =
+        returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
+    cbptr_->createUpdateOption6(ServerSelector::ALL(),
+                                shared_network->getName(),
+                                opt_boot_file_name);
+
+    returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
+                                                 shared_network->getName());
+    ASSERT_TRUE(returned_network);
+    returned_opt_boot_file_name =
+        returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE);
+    ASSERT_TRUE(returned_opt_boot_file_name.option_);
+    EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+
+    // Deleting an option with explicitly specified server tag should fail.
+    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
+                                       shared_network->getName(),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+
+    // Deleting an option for all servers should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(),
+                                       shared_network->getName(),
+                                       opt_boot_file_name->option_->getType(),
+                                       opt_boot_file_name->space_name_));
+    returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
+                                                 shared_network->getName());
+    ASSERT_TRUE(returned_network);
+    EXPECT_FALSE(returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE).option_);
+}
+
+
+}
index 4468a98101b7259198538768811478a29d6c53b0..1f2210a4132db75b3b22b2938d3fd1c2232f0419 100644 (file)
@@ -1311,10 +1311,10 @@ CREATE TABLE IF NOT EXISTS dhcp6_options_server (
     KEY key_dhcp6_options_server_modification_ts (modification_ts),
     CONSTRAINT fk_dhcp6_options_server_option_id FOREIGN KEY (option_id)
         REFERENCES dhcp6_options (option_id)
-        ON DELETE NO ACTION ON UPDATE NO ACTION,
+        ON DELETE CASCADE ON UPDATE NO ACTION,
     CONSTRAINT fk_dhcp6_options_server_server_id FOREIGN KEY (server_id)
         REFERENCES dhcp6_server (id)
-        ON DELETE CASCADE ON UPDATE NO ACTION
+        ON DELETE NO ACTION ON UPDATE NO ACTION
 ) ENGINE=InnoDB;
 
 # Create trigger which removes pool specific options upon removal of