]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2349] PgSql CB V6 shared-networks, subnets, pools
authorThomas Markwalder <tmark@isc.org>
Thu, 10 Mar 2022 20:59:29 +0000 (15:59 -0500)
committerThomas Markwalder <tmark@isc.org>
Thu, 10 Mar 2022 20:59:29 +0000 (15:59 -0500)
configure.ac
    Added src/share/database/scripts/pgsql/upgrade_010_to_011.sh

src/bin/admin/tests/pgsql_tests.sh.in
    Updated for upgrade from 10 to 11.

src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.cc
    Make use of PgSqlConfigBackendImpl::addDdnsReplaceClientNameBinding()
    Add universe paramter to createUpdateOptionDef() calls

src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp6.cc
    PgSqlConfigBackendDHCPv6Impl - implemented methods for
    shared-networks, subnets, pools, subordinate option defs and options

    getInterfaceId()
    addInterfaceIdBinding()- new convenience functions for reading and
    writing interface id options

src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.*
    PgSqlConfigBackendImpl::createUpdateOptionDef() - added universe
    parameter to replace incorrect use of space name

    PgSqlConfigBackendImpl::addDdnsReplaceClientNameBinding()
    - new convenience function

src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_dhcp6_unittest.cc
    Added tests for networks, subnets, pools

src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc
    Replaced EXPECT_THROWs with ASSER_THROWs - simplifies
    debugging tremendously

src/lib/pgsql/pgsql_connection.h
    Updated schema version to 11

src/share/database/scripts/pgsql/Makefile.am
src/share/database/scripts/pgsql/.gitignore
    Added upgrade_010_to_011.sh

src/share/database/scripts/pgsql/dhcpdb_create.pgsql
    Added replacement of createOptionAuditDHCP6() to correct
    assocation of options to PD pools

src/share/database/scripts/pgsql/upgrade_010_to_011.sh.in - new file

14 files changed:
configure.ac
src/bin/admin/tests/pgsql_tests.sh.in
src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp4.cc
src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp6.cc
src/hooks/dhcp/pgsql_cb/pgsql_cb_dhcp6.h
src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.cc
src/hooks/dhcp/pgsql_cb/pgsql_cb_impl.h
src/hooks/dhcp/pgsql_cb/tests/pgsql_cb_dhcp6_unittest.cc
src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc
src/lib/pgsql/pgsql_connection.h
src/share/database/scripts/pgsql/.gitignore
src/share/database/scripts/pgsql/Makefile.am
src/share/database/scripts/pgsql/dhcpdb_create.pgsql
src/share/database/scripts/pgsql/upgrade_010_to_011.sh.in [new file with mode: 0644]

index 2cc5484cf20506d1741de815adecc43b15a92d70..9c340a9633e73494fcc3ab7bb51c4022aaa3de83 100644 (file)
@@ -1846,6 +1846,8 @@ AC_CONFIG_FILES([src/share/database/scripts/pgsql/upgrade_008_to_009.sh],
                 [chmod +x src/share/database/scripts/pgsql/upgrade_008_to_009.sh])
 AC_CONFIG_FILES([src/share/database/scripts/pgsql/upgrade_009_to_010.sh],
                 [chmod +x src/share/database/scripts/pgsql/upgrade_009_to_010.sh])
+AC_CONFIG_FILES([src/share/database/scripts/pgsql/upgrade_010_to_011.sh],
+                [chmod +x src/share/database/scripts/pgsql/upgrade_010_to_011.sh])
 AC_CONFIG_FILES([src/share/database/scripts/pgsql/wipe_data.sh],
                 [chmod +x src/share/database/scripts/pgsql/wipe_data.sh])
 AC_CONFIG_FILES([src/share/yang/Makefile])
index 136edfeb439af3cb50c84a774c1402326898824e..2e786a9788d02400bb359b7a6462bde7c35172f7 100644 (file)
@@ -143,7 +143,7 @@ pgsql_db_version_test() {
     run_command \
         "${kea_admin}" db-version pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}"
     version="${OUTPUT}"
-    assert_str_eq "10.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
+    assert_str_eq "11.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
 
     # Let's wipe the whole database
     pgsql_wipe
@@ -445,6 +445,22 @@ pgsql_upgrade_9_0_to_10_0() {
     assert_eq 1 "${count}" "func_dhcp6_client_class_check_dependency_BINS is missing RETURN NEW. (expected count %d, returned %d)"
 }
 
+pgsql_upgrade_10_0_to_11_0() {
+    run_command \
+        pgsql_execute "$session_sql"
+
+    # Get function source code so we can check that it returns NEW.
+    # Function name must be lower case for WHERE clause.
+    run_command \
+         pgsql_execute "select proname,prosrc from pg_proc where proname='createoptionauditdhcp6'"
+
+    assert_eq 0 "${EXIT_CODE}" "function createOptionAuditDHCP6() broken or missing. (expected status code %d, returned %d)"
+
+    count=$(echo "${OUTPUT}" | grep -Eci 'SELECT dhcp6_pd_pool.subnet_id INTO sid FROM dhcp6_pd_pool WHERE id = pd_pool_id') || true
+    assert_eq 1 "${count}" "func_dhcp6_client_class_check_dependency_BINS is missing changed line. (expected count %d, returned %d)"
+}
+
+
 pgsql_upgrade_test() {
     test_start "pgsql.upgrade-test"
 
@@ -460,9 +476,9 @@ pgsql_upgrade_test() {
         "${kea_admin}" db-upgrade pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}"
     assert_eq 0 "${EXIT_CODE}" "db-upgrade failed, expected exit code: %d, actual: %d"
 
-    # Verify upgraded schema reports version 10.0.
+    # Verify upgraded schema reports version 11.0.
     version=$("${kea_admin}" db-version pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}")
-    assert_str_eq "10.0" "${version}" 'Expected kea-admin to return %s, returned value was %s'
+    assert_str_eq "11.0" "${version}" 'Expected kea-admin to return %s, returned value was %s'
 
     # Check 1.0 to 2.0 upgrade
     pgsql_upgrade_1_0_to_2_0
@@ -488,6 +504,9 @@ pgsql_upgrade_test() {
     # Check 9.0 to 10.0 upgrade
     pgsql_upgrade_9_0_to_10_0
 
+    # Check 10.0 to 11.0 upgrade
+    pgsql_upgrade_10_0_to_11_0
+
     # Let's wipe the whole database
     pgsql_wipe
 
index 236b4b3946e7154049193542cd4bff9a479efdaa..b6b2c51446175e029b129bc3ed8c3a3451367afa 100644 (file)
@@ -925,14 +925,7 @@ public:
         in_bindings.addOptional(subnet->getDdnsSendUpdates(Network::Inheritance::NONE));
         in_bindings.addOptional(subnet->getDdnsOverrideNoUpdate(Network::Inheritance::NONE));
         in_bindings.addOptional(subnet->getDdnsOverrideClientUpdate(Network::Inheritance::NONE));
-
-        auto ddns_rcn_mode = subnet->getDdnsReplaceClientNameMode(Network::Inheritance::NONE);
-        if (!ddns_rcn_mode.unspecified()) {
-            in_bindings.add(static_cast<uint8_t>(ddns_rcn_mode.get()));
-        } else {
-            in_bindings.addNull();
-        }
-
+        addDdnsReplaceClientNameBinding(in_bindings, subnet);
         in_bindings.addOptional(subnet->getDdnsGeneratedPrefix(Network::Inheritance::NONE));
         in_bindings.addOptional(subnet->getDdnsQualifyingSuffix(Network::Inheritance::NONE));
         in_bindings.addOptional(subnet->getReservationsInSubnet(Network::Inheritance::NONE));
@@ -1493,14 +1486,7 @@ public:
         in_bindings.addOptional(shared_network->getDdnsSendUpdates(Network::Inheritance::NONE));
         in_bindings.addOptional(shared_network->getDdnsOverrideNoUpdate(Network::Inheritance::NONE));
         in_bindings.addOptional(shared_network->getDdnsOverrideClientUpdate(Network::Inheritance::NONE));
-
-        auto ddns_rcn_mode = shared_network->getDdnsReplaceClientNameMode(Network::Inheritance::NONE);
-        if (!ddns_rcn_mode.unspecified()) {
-            in_bindings.add(static_cast<uint8_t>(ddns_rcn_mode.get()));
-        } else {
-            in_bindings.addNull();
-        }
-
+        addDdnsReplaceClientNameBinding(in_bindings, shared_network);
         in_bindings.addOptional(shared_network->getDdnsGeneratedPrefix(Network::Inheritance::NONE));
         in_bindings.addOptional(shared_network->getDdnsQualifyingSuffix(Network::Inheritance::NONE));
         in_bindings.addOptional(shared_network->getReservationsInSubnet(Network::Inheritance::NONE));
@@ -1950,7 +1936,7 @@ public:
     /// @param option_def Pointer to the option definition to be inserted or updated.
     void createUpdateOptionDef4(const ServerSelector& server_selector,
                                 const OptionDefinitionPtr& option_def) {
-        createUpdateOptionDef(server_selector, option_def, DHCP4_OPTION_SPACE,
+        createUpdateOptionDef(server_selector, Option::V4, option_def, DHCP4_OPTION_SPACE,
                               PgSqlConfigBackendDHCPv4Impl::GET_OPTION_DEF4_CODE_SPACE,
                               PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION_DEF4,
                               PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION_DEF4,
@@ -1967,7 +1953,7 @@ public:
     void createUpdateOptionDef4(const ServerSelector& server_selector,
                                 const OptionDefinitionPtr& option_def,
                                 const std::string& client_class_name) {
-        createUpdateOptionDef(server_selector, option_def, DHCP4_OPTION_SPACE,
+        createUpdateOptionDef(server_selector, Option::V4, option_def, DHCP4_OPTION_SPACE,
                               PgSqlConfigBackendDHCPv4Impl::GET_OPTION_DEF4_CODE_SPACE,
                               PgSqlConfigBackendDHCPv4Impl::INSERT_OPTION_DEF4_CLIENT_CLASS,
                               PgSqlConfigBackendDHCPv4Impl::UPDATE_OPTION_DEF4_CLIENT_CLASS,
index 11c923b18de62201bc96d72c83ac4cbff5e6a4a8..d40731e910cae9b8f246c95e4612275ca1d6b6fd 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <config.h>
 
+#include <asiolink/addr_utilities.h>
 #include <pgsql_cb_dhcp6.h>
 #include <pgsql_cb_impl.h>
 #include <pgsql_query_macros_dhcp.h>
@@ -281,11 +282,379 @@ public:
     /// 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 ServerSelector& /* server_selector */,
-                     const PsqlBindArray& /* in_bindings */,
-                     Subnet6Collection& /* subnets */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getSubnets6(const StatementIndex& index,
+                     const ServerSelector& server_selector,
+                     const PsqlBindArray& in_bindings,
+                     Subnet6Collection& subnets) {
+        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;
+        std::string last_tag;
+
+        // Execute actual query.
+        selectQuery(index, in_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, &last_tag](PgSqlResult& r, int row) {
+            // Create a convenience worker for the row.
+            PgSqlResultRowWorker worker(r, row);
+
+            // Get pointer to the last subnet in the collection.
+            Subnet6Ptr last_subnet;
+            if (!subnets.empty()) {
+                last_subnet = *subnets.rbegin();
+            }
+            // Subnet_id is column 0.
+            SubnetID subnet_id = worker.getInt(0) ;
+
+            // 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() != subnet_id)) {
+                // Reset per subnet component tracking and server tag because
+                // we're now starting to process a new subnet.
+                last_pool_id = 0;
+                last_pd_pool_id = 0;
+                last_pool_option_id = 0;
+                last_pd_pool_option_id = 0;
+                last_option_id = 0;
+                last_pool.reset();
+                last_pd_pool.reset();
+                last_tag.clear();
+
+                // Get subnet parameters required by the constructor first.
+
+                // subnet_prefix at 1.
+                std::string subnet_prefix = worker.getString(1);
+                auto prefix_pair = Subnet6::parsePrefix(subnet_prefix);
+
+                // preferred_lifetime (5)
+                // min_preferred_lifetime (69)
+                // max_preferred_lifetime (70)
+                auto preferred_lifetime = worker.getTriplet(5, 69, 70);
+
+                // renew_timer at 9.
+                auto renew_timer = worker.getTriplet(9);
+
+                // rebind_timer at 7.
+                auto rebind_timer = worker.getTriplet(7);
+
+                // valid_lifetime at 14.
+                // min_valid_lifetime at 71.
+                // max_valid_lifetime at 72.
+                auto valid_lifetime = worker.getTriplet(14, 71, 72);
+
+                // Create subnet with basic settings.
+                last_subnet = Subnet6::create(prefix_pair.first, prefix_pair.second,
+                                              renew_timer, rebind_timer,
+                                              preferred_lifetime,
+                                              valid_lifetime, subnet_id);
+
+                // 0 and 1 are subnet_id and subnet_prefix
+
+                // client_class at 2.
+                if (!worker.isColumnNull(2)) {
+                    last_subnet->allowClientClass(worker.getString(2));
+                }
+
+                // interface at 3.
+                if (!worker.isColumnNull(3)) {
+                    last_subnet->setIface(worker.getString(3));
+                }
+
+                // modification_ts at 4.
+                last_subnet->setModificationTime(worker.getTimestamp(4));
+
+                // preferred_lifetime is 5.
+
+                if (!worker.isColumnNull(6)) {
+                    last_subnet->setRapidCommit(worker.getBool(6));
+                }
+
+                // rebind_timer is 7.
+
+                // Relay addresses at 8.
+                setRelays(worker, 8, *last_subnet);
+
+                // renew_timer is 9.
+
+                // require_client_classes at 10.
+                setRequiredClasses(worker, 10, [&last_subnet](const std::string& class_name) {
+                    last_subnet->requireClientClass(class_name);
+                });
+
+                // reservations_global at 11.
+                if (!worker.isColumnNull(11)) {
+                    last_subnet->setReservationsGlobal(worker.getBool(11));
+                }
+
+                // shared_network_name at 12.
+                if (!worker.isColumnNull(12)) {
+                    last_subnet->setSharedNetworkName(worker.getString(12));
+                }
+
+                // user_context at 13.
+                if (!worker.isColumnNull(13)) {
+                    last_subnet->setContext(worker.getJSON(13));
+                }
+
+                // valid_lifetime at 14.
+
+                // 15 to 19 are pool
+                // 20 to 25 are pd pool
+                // 26 to 38 are pool option
+                // 39 to 51 are pd pool option
+                // 52 to 64 are option
+
+                // calculate_tee_times at 65.
+                if (!worker.isColumnNull(65)) {
+                    last_subnet->setCalculateTeeTimes(worker.getBool(65));
+                }
+
+                // t1_percent at 66.
+                if (!worker.isColumnNull(66)) {
+                    last_subnet->setT1Percent(worker.getDouble(66));
+                }
+
+                // t2_percent at 67.
+                if (!worker.isColumnNull(67)) {
+                    last_subnet->setT2Percent(worker.getDouble(67));
+                }
+
+                // interface_id at 68.
+                setInterfaceId(*last_subnet, worker, 68);
+
+                // 69 and 70 are {min,max}_preferred_lifetime
+
+                // 71 and 72 are {min,max}_valid_lifetime
+
+                // 73 is pool client_class
+                // 74 is pool require_client_classes
+                // 75 is pool user_context
+                // 76 is pd pool excluded_prefix
+                // 77 is pd pool excluded_prefix_length
+                // 78 is pd pool client_class
+                // 79 is pd pool require_client_classes
+                // 80 is pd pool user_context
+
+                // ddns_send_updates at 81.
+                if (!worker.isColumnNull(81)) {
+                    last_subnet->setDdnsSendUpdates(worker.getBool(81));
+                }
+
+                // ddns_override_no_update at 82.
+                if (!worker.isColumnNull(82)) {
+                    last_subnet->setDdnsOverrideNoUpdate(worker.getBool(82));
+                }
+
+                // ddns_override_client_update at 83.
+                if (!worker.isColumnNull(83)) {
+                    last_subnet->setDdnsOverrideClientUpdate(worker.getBool(83));
+                }
+
+                // ddns_replace_client_name at 84.
+                if (!worker.isColumnNull(84)) {
+                    last_subnet->setDdnsReplaceClientNameMode(
+                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(84)));
+                }
+
+                // ddns_generated_prefix at 85.
+                if (!worker.isColumnNull(85)) {
+                    last_subnet->setDdnsGeneratedPrefix(worker.getString(85));
+                }
+
+                // ddns_qualifying_suffix at 86.
+                if (!worker.isColumnNull(86)) {
+                    last_subnet->setDdnsQualifyingSuffix(worker.getString(86));
+                }
+
+                // reservations_in_subnet at 87.
+                if (!worker.isColumnNull(87)) {
+                    last_subnet->setReservationsInSubnet(worker.getBool(87));
+                }
+
+                // reservations_out_of_pool at 88.
+                if (!worker.isColumnNull(88)) {
+                    last_subnet->setReservationsOutOfPool(worker.getBool(88));
+                }
+
+                // cache_threshold at 89.
+                if (!worker.isColumnNull(89)) {
+                    last_subnet->setCacheThreshold(worker.getDouble(89));
+                }
+
+                // cache_max_age at 90.
+                if (!worker.isColumnNull(90)) {
+                    last_subnet->setCacheMaxAge(worker.getInt(90));
+                }
+
+                // server_tag at 91.
+
+                // Subnet ready. Add it to the list.
+                auto ret = subnets.insert(last_subnet);
+
+                // subnets is a multi index container with unique indexes
+                // but these indexes are unique too in the database,
+                // so this is for sanity only.
+                if (!ret.second) {
+                    isc_throw(Unexpected, "add subnet failed");
+                }
+            }
+
+            // Check for new server tags at 91.
+            if (!worker.isColumnNull(91)) {
+                std::string new_tag = worker.getString(91);
+                if (last_tag != new_tag) {
+                    if (!new_tag.empty() && !last_subnet->hasServerTag(ServerTag(new_tag))) {
+                        last_subnet->setServerTag(new_tag);
+                    }
+
+                    last_tag = new_tag;
+                }
+            }
+
+            // Pool is between 15 and 19 with extra between 73 and 75
+
+            // 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.
+            // pool id (15)
+            // pool start_address (16)
+            // pool end_address (17)
+            if (!worker.isColumnNull(15) &&
+                (worker.getInet6(16) != 0) &&
+                (worker.getInet6(17) != 0) &&
+                (worker.getBigInt(15) > last_pool_id)) {
+
+                last_pool_id = worker.getBigInt(15);
+                last_pool = Pool6::create(Lease::TYPE_NA,
+                                          IOAddress(worker.getInet6(16)),
+                                          IOAddress(worker.getInet6(17)));
+
+                // pool subnet_id at 18 (ignored)
+                // pool modification_ts at 19 (ignored)
+
+                // pool client_class at 73.
+                if (!worker.isColumnNull(73)) {
+                    last_pool->allowClientClass(worker.getString(73));
+                }
+
+                // pool require_client_classes at 74.
+                setRequiredClasses(worker, 74, [&last_pool](const std::string& class_name) {
+                    last_pool->requireClientClass(class_name);
+                });
+
+                // pool user_context at 75.
+                if (!worker.isColumnNull(75)) {
+                    ElementPtr user_context = worker.getJSON(75);
+                    if (user_context) {
+                        last_pool->setContext(user_context);
+                    }
+                }
+
+                last_subnet->addPool(last_pool);
+            }
+
+            // Pd Pool is between 20 and 25 with extra between 76 and 80
+
+            // 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.
+            // pd pool id (20)
+            // pd pool prefix (21)
+            // pd pool prefix_length (22)
+            // pd pool delegated_prefix_length (23)
+            if (!worker.isColumnNull(20) &&
+                (!worker.getString(21).empty()) &&
+                (worker.getSmallInt(22) != 0) &&
+                (worker.getSmallInt(23) != 0) &&
+                (worker.getBigInt(20) > last_pd_pool_id)) {
+
+                // 24 is pd pool subnet_id (ignored)
+                // 25 is pd pool modification_ts (ignored)
+
+                // excluded_prefix (76) and excluded_prefix_length (77)
+                IOAddress excluded_prefix = IOAddress::IPV6_ZERO_ADDRESS();
+                if (!worker.isColumnNull(76)) {
+                    excluded_prefix = worker.getInet6(76);
+                }
+
+                last_pd_pool_id = worker.getBigInt(20);
+                last_pd_pool = Pool6::create(worker.getInet6(21),
+                                             static_cast<uint8_t>(worker.getSmallInt(22)),
+                                             static_cast<uint8_t>(worker.getSmallInt(23)),
+                                             excluded_prefix,
+                                             static_cast<uint8_t>(worker.getSmallInt(77)));
+
+                // pd pool client_class (78)
+                if (!worker.isColumnNull(78)) {
+                    last_pd_pool->allowClientClass(worker.getString(78));
+                }
+
+                // pd pool require_client_classes at 79.
+                setRequiredClasses(worker, 79, [&last_pd_pool](const std::string& class_name) {
+                    last_pd_pool->requireClientClass(class_name);
+                });
+
+                // pd pool user_context at 80.
+                if (!worker.isColumnNull(80)) {
+                    ElementPtr user_context = worker.getJSON(80);
+                    if (user_context) {
+                        last_pd_pool->setContext(user_context);
+                    }
+                }
+
+                last_subnet->addPool(last_pd_pool);
+            }
+
+            // Parse pool-specific option from 26 to 38.
+            if (last_pool && !worker.isColumnNull(26) &&
+                (last_pool_option_id < worker.getBigInt(26))) {
+                last_pool_option_id = worker.getBigInt(26);
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 26);
+                if (desc) {
+                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+
+            // Parse pd pool-specific option from 39 to 51.
+            if (last_pd_pool && !worker.isColumnNull(39) &&
+                (last_pd_pool_option_id < worker.getBigInt(39))) {
+                last_pd_pool_option_id = worker.getBigInt(39);
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 39);
+                if (desc) {
+                    last_pd_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+
+            // Parse subnet-specific option from 52 to 64.
+            if (!worker.isColumnNull(52) &&
+                (last_option_id < worker.getBigInt(52))) {
+                last_option_id = worker.getBigInt(52);
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 52);
+                if (desc) {
+                    last_subnet->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
+
+        // Now that we're done fetching the whole subnet, we have to
+        // check if it has matching server tags and toss it if it
+        // doesn't. We skip matching the server tags if we're asking
+        // for ANY subnet.
+        auto& subnet_index = subnets.get<SubnetSubnetIdIndexTag>();
+        tossNonMatchingElements(server_selector, subnet_index);
     }
 
     /// @brief Sends query to retrieve single subnet by id.
@@ -295,9 +664,29 @@ public:
     ///
     /// @return Pointer to the returned subnet or NULL if such subnet
     /// doesn't exist.
-    Subnet6Ptr getSubnet6(const ServerSelector& /* server_selector */,
-                          const SubnetID& /* subnet_id */)  {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
+                          const SubnetID& subnet_id)  {
+
+        if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a subnet. Got: "
+                      << getServerTagsAsText(server_selector));
+        }
+
+        PsqlBindArray in_bindings;
+        in_bindings.add(subnet_id);
+
+        auto index = GET_SUBNET6_ID_NO_TAG;
+        if (server_selector.amUnassigned()) {
+            index = GET_SUBNET6_ID_UNASSIGNED;
+        } else if (server_selector.amAny()) {
+            index = GET_SUBNET6_ID_ANY;
+        }
+
+        Subnet6Collection subnets;
+        getSubnets6(index, server_selector, in_bindings, subnets);
+
+        return (subnets.empty() ? Subnet6Ptr() : *subnets.begin());
     }
 
     /// @brief Sends query to retrieve single subnet by prefix.
@@ -309,9 +698,28 @@ public:
     ///
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
+                          const std::string& subnet_prefix) {
+        if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a subnet. Got: "
+                      << getServerTagsAsText(server_selector));
+        }
+
+        PsqlBindArray in_bindings;
+        in_bindings.add(subnet_prefix);
+
+        auto index = GET_SUBNET6_PREFIX_NO_TAG;
+        if (server_selector.amUnassigned()) {
+            index = GET_SUBNET6_PREFIX_UNASSIGNED;
+        } else if (server_selector.amAny()) {
+            index = GET_SUBNET6_PREFIX_ANY;
+        }
+
+        Subnet6Collection subnets;
+        getSubnets6(index, server_selector, in_bindings, subnets);
+
+        return (subnets.empty() ? Subnet6Ptr() : *subnets.begin());
     }
 
     /// @brief Sends query to retrieve all subnets.
@@ -319,9 +727,18 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getAllSubnets6(const ServerSelector& server_selector,
+                        Subnet6Collection& subnets) {
+        if (server_selector.amAny()) {
+            isc_throw(InvalidOperation, "fetching all subnets for ANY "
+                      "server is not supported");
+        }
+
+        PsqlBindArray in_bindings;
+
+        auto index = (server_selector.amUnassigned() ? GET_ALL_SUBNETS6_UNASSIGNED :
+                      GET_ALL_SUBNETS6);
+        getSubnets6(index, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve modified subnets.
@@ -330,10 +747,20 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getModifiedSubnets6(const ServerSelector& server_selector,
+                             const boost::posix_time::ptime& modification_ts,
+                             Subnet6Collection& subnets) {
+        if (server_selector.amAny()) {
+            isc_throw(InvalidOperation, "fetching modified subnets for ANY "
+                      "server is not supported");
+        }
+
+        PsqlBindArray in_bindings;
+        in_bindings.addTimestamp(modification_ts);
+
+        auto index = (server_selector.amUnassigned() ? GET_MODIFIED_SUBNETS6_UNASSIGNED :
+                      GET_MODIFIED_SUBNETS6);
+        getSubnets6(index, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve all subnets belonging to a shared network.
@@ -343,10 +770,12 @@ public:
     /// subnets should be retrieved.
     /// @param [out] subnets Reference to the subnet collection structure where
     /// subnets should be inserted.
-    void getSharedNetworkSubnets6(const ServerSelector& /* server_selector */,
-                                  const std::string& /* shared_network_name */,
-                                  Subnet6Collection& /* subnets */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getSharedNetworkSubnets6(const ServerSelector& server_selector,
+                                  const std::string& shared_network_name,
+                                  Subnet6Collection& subnets) {
+        PsqlBindArray in_bindings;
+        in_bindings.add(shared_network_name);
+        getSubnets6(GET_SHARED_NETWORK_SUBNETS6, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve multiple pools.
@@ -362,11 +791,68 @@ public:
     /// will be inserted.
     /// @param [out] pool_ids Identifiers of the pools returned in @c pools
     /// argument.
-    void getPools(const StatementIndex& /* index */,
-                  const PsqlBindArray& /* in_bindings */,
-                  PoolCollection& /* pools */,
-                  std::vector<uint64_t>& /* pool_ids */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getPools(const StatementIndex& index,
+                  const PsqlBindArray& in_bindings,
+                  PoolCollection& pools,
+                  std::vector<uint64_t>& pool_ids) {
+        uint64_t last_pool_id = 0;
+        uint64_t last_pool_option_id = 0;
+        Pool6Ptr last_pool;
+
+        selectQuery(index, in_bindings,
+                    [this, &last_pool_id, &last_pool_option_id, &last_pool, &pools, &pool_ids]
+                    (PgSqlResult& r, int row) {
+            // Create a convenience worker for the row.
+            PgSqlResultRowWorker worker(r, row);
+
+            // Pool id is column 0.
+            auto id = worker.getBigInt(0) ;
+            if (id > last_pool_id) {
+                // pool start_address (1)
+                // pool end_address (2)
+                last_pool_id = id;
+
+                // pool start_address (1)
+                // pool end_address (2)
+                last_pool = Pool6::create(Lease::TYPE_NA, worker.getInet6(1), worker.getInet6(2));
+
+                // pool subnet_id (3) (ignored)
+
+                // pool client_class (4)
+                if (!worker.isColumnNull(4)) {
+                    last_pool->allowClientClass(worker.getString(4));
+                }
+
+                // pool require_client_classes (5)
+                setRequiredClasses(worker, 5, [&last_pool](const std::string& class_name) {
+                    last_pool->requireClientClass(class_name);
+                });
+
+                // pool user_context (6)
+                if (!worker.isColumnNull(6)) {
+                    ElementPtr user_context = worker.getJSON(6);
+                    if (user_context) {
+                        last_pool->setContext(user_context);
+                    }
+                }
+
+                // pool: modification_ts (7) (ignored)
+
+                pools.push_back(last_pool);
+                pool_ids.push_back(last_pool_id);
+            }
+
+            // Parse pool specific option (8).
+            if (last_pool && !worker.isColumnNull(8) &&
+                (last_pool_option_id < worker.getBigInt(8))) {
+                last_pool_option_id = worker.getBigInt(8);
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 8);
+                if (desc) {
+                    last_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
     }
 
     /// @brief Sends query to retrieve multiple pd pools.
@@ -382,11 +868,79 @@ public:
     /// will be inserted.
     /// @param [out] pd_pool_ids Identifiers of the pd pools returned in
     /// @c pd_pools argument.
-    void getPdPools(const StatementIndex& /* index */,
-                    const PsqlBindArray& /* in_bindings */,
-                    PoolCollection& /* pd_pools */,
-                    std::vector<uint64_t>& /* pd_pool_ids */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getPdPools(const StatementIndex& index,
+                    const PsqlBindArray& in_bindings,
+                    PoolCollection& pd_pools,
+                    std::vector<uint64_t>& pd_pool_ids) {
+        uint64_t last_pd_pool_id = 0;
+        uint64_t last_pd_pool_option_id = 0;
+        Pool6Ptr last_pd_pool;
+
+        selectQuery(index, in_bindings,
+                    [this, &last_pd_pool_id, &last_pd_pool_option_id,
+                     &last_pd_pool, &pd_pools, &pd_pool_ids]
+                    (PgSqlResult& r, int row) {
+            // Create a convenience worker for the row.
+            PgSqlResultRowWorker worker(r, row);
+
+            // Pool id is column 0.
+            auto id = worker.getBigInt(0) ;
+            if (id > last_pd_pool_id) {
+                last_pd_pool_id = id;
+
+                // pd pool prefix (1)
+                // pd pool prefix_length (2)
+                // pd pool delegated_prefix_length (3)
+
+                // excluded_prefix (5) and excluded_prefix_length (6)
+                IOAddress excluded_prefix = IOAddress::IPV6_ZERO_ADDRESS();
+                if (!worker.isColumnNull(5)) {
+                    excluded_prefix = worker.getInet6(5);
+                }
+
+                last_pd_pool = Pool6::create(worker.getInet6(1),
+                                             static_cast<uint8_t>(worker.getSmallInt(2)),
+                                             static_cast<uint8_t>(worker.getSmallInt(3)),
+                                             excluded_prefix,
+                                             static_cast<uint8_t>(worker.getSmallInt(6)));
+
+                // pd pool subnet_id (4) (ignored)
+
+                // pool client_class (7)
+                if (!worker.isColumnNull(7)) {
+                    last_pd_pool->allowClientClass(worker.getString(7));
+                }
+
+                // pool require_client_classes (8)
+                setRequiredClasses(worker, 8, [&last_pd_pool](const std::string& class_name) {
+                    last_pd_pool->requireClientClass(class_name);
+                });
+
+                // pool user_context (9)
+                if (!worker.isColumnNull(9)) {
+                    ElementPtr user_context = worker.getJSON(9);
+                    if (user_context) {
+                        last_pd_pool->setContext(user_context);
+                    }
+                }
+
+                // pd pool modification_ts (10) (ignored)
+
+                pd_pools.push_back(last_pd_pool);
+                pd_pool_ids.push_back(last_pd_pool_id);
+            }
+
+            // Parse pd pool specific option between 11 and 24.
+            if (last_pd_pool && !worker.isColumnNull(11) &&
+                (last_pd_pool_option_id < worker.getBigInt(11))) {
+                last_pd_pool_option_id = worker.getBigInt(11);
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 11);
+                if (desc) {
+                    last_pd_pool->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
     }
 
     /// @brief Sends query to retrieve single pool by address range.
@@ -396,11 +950,39 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    Pool6Ptr getPool6(const ServerSelector& server_selector,
+                      const IOAddress& pool_start_address,
+                      const IOAddress& pool_end_address,
+                      uint64_t& pool_id) {
+        PoolCollection pools;
+        std::vector<uint64_t> pool_ids;
+
+        if (server_selector.amAny()) {
+            PsqlBindArray in_bindings;
+            in_bindings.addInet6(pool_start_address);
+            in_bindings.addInet6(pool_end_address);
+            getPools(GET_POOL6_RANGE_ANY, in_bindings, pools, pool_ids);
+        } else {
+            auto const& tags = server_selector.getTags();
+            for (auto const& tag : tags) {
+                PsqlBindArray in_bindings;
+                in_bindings.addTempString(tag.get());
+                in_bindings.addInet6(pool_start_address);
+                in_bindings.addInet6(pool_end_address);
+
+                getPools(GET_POOL6_RANGE, in_bindings, pools, pool_ids);
+                // Break if something is found?
+            }
+        }
+
+        // Return upon the first pool found.
+        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.
@@ -410,20 +992,194 @@ public:
     /// @param pd_pool_prefix_length Length of the pd pool prefix.
     /// @param pd_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& /* pd_pool_id */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    Pool6Ptr getPdPool6(const ServerSelector& server_selector,
+                        const asiolink::IOAddress& pd_pool_prefix,
+                        const uint8_t pd_pool_prefix_length,
+                        uint64_t& pd_pool_id) {
+        PoolCollection pd_pools;
+        std::vector<uint64_t> pd_pool_ids;
+
+        if (server_selector.amAny()) {
+            PsqlBindArray in_bindings;
+            in_bindings.addTempString(pd_pool_prefix.toText());
+            in_bindings.add(pd_pool_prefix_length);
+            getPdPools(GET_PD_POOL_ANY, in_bindings, pd_pools, pd_pool_ids);
+        } else {
+            auto const& tags = server_selector.getTags();
+            for (auto const& tag : tags) {
+                PsqlBindArray in_bindings;
+                in_bindings.addTempString(tag.get());
+                in_bindings.addTempString(pd_pool_prefix.toText());
+                in_bindings.add(pd_pool_prefix_length);
+                getPdPools(GET_PD_POOL, in_bindings, pd_pools, pd_pool_ids);
+            }
+        }
+
+        if (!pd_pools.empty()) {
+            pd_pool_id = pd_pool_ids[0];
+            return (boost::dynamic_pointer_cast<Pool6>(*pd_pools.begin()));
+        }
+
+        pd_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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void createUpdateSubnet6(const ServerSelector& server_selector,
+                             const Subnet6Ptr& subnet) {
+
+        if (server_selector.amAny()) {
+            isc_throw(InvalidOperation, "creating or updating a subnet for ANY"
+                      " server is not supported");
+
+        } else if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        // Create input bindings.
+        PsqlBindArray in_bindings;
+        in_bindings.add(subnet->getID());
+        in_bindings.addTempString(subnet->toText());
+        in_bindings.addOptional(subnet->getClientClass(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getIface(Network::Inheritance::NONE));
+        in_bindings.addTimestamp(subnet->getModificationTime());
+        in_bindings.add(subnet->getPreferred(Network::Inheritance::NONE));
+        in_bindings.addMin(subnet->getPreferred(Network::Inheritance::NONE));
+        in_bindings.addMax(subnet->getPreferred(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getRapidCommit(Network::Inheritance::NONE));
+        in_bindings.add(subnet->getT2(Network::Inheritance::NONE));
+        addRelayBinding(in_bindings, subnet);
+        in_bindings.add(subnet->getT1(Network::Inheritance::NONE));
+        addRequiredClassesBinding(in_bindings, subnet);
+        in_bindings.addOptional(subnet->getReservationsGlobal(Network::Inheritance::NONE));
+
+        // Add shared network.
+        // If the subnet is associated with a shared network instance.
+        // If it is, create the binding using the name of the shared network.
+        SharedNetwork6Ptr shared_network;
+        subnet->getSharedNetwork(shared_network);
+        if (shared_network) {
+            in_bindings.addTempString(shared_network->getName());
+
+        // If the subnet is associated with a shared network by name (no
+        // shared network instance), use this name to create the binding.
+        // This may be the case if the subnet is added as a result of
+        // receiving a control command that merely specifies shared
+        // network name. In that case, it is expected that the shared
+        // network data is already stored in the database.
+        } else if (!subnet->getSharedNetworkName().empty()) {
+            in_bindings.addTempString(subnet->getSharedNetworkName());
+
+        // If the subnet is not associated with a shared network, create
+        // null binding.
+        } else {
+             in_bindings.addNull();
+        }
+
+        in_bindings.add(subnet->getContext());
+        in_bindings.add(subnet->getValid(Network::Inheritance::NONE));
+        in_bindings.addMin(subnet->getValid(Network::Inheritance::NONE));
+        in_bindings.addMax(subnet->getValid(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getCalculateTeeTimes(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getT1Percent(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getT2Percent(Network::Inheritance::NONE));
+        addInterfaceIdBinding(in_bindings, *subnet);
+        in_bindings.addOptional(subnet->getDdnsSendUpdates(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getDdnsOverrideNoUpdate(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getDdnsOverrideClientUpdate(Network::Inheritance::NONE));
+        addDdnsReplaceClientNameBinding(in_bindings, subnet);
+        in_bindings.addOptional(subnet->getDdnsGeneratedPrefix(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getDdnsQualifyingSuffix(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getReservationsInSubnet(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getReservationsOutOfPool(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getCacheThreshold(Network::Inheritance::NONE));
+        in_bindings.addOptional(subnet->getCacheMaxAge(Network::Inheritance::NONE));
+
+        // Start transaction.
+        PgSqlTransaction transaction(conn_);
+
+        // Create scoped audit revision. As long as this instance exists
+        // no new audit revisions are created in any subsequent calls.
+        ScopedAuditRevision audit_revision(this,
+                                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
+                                           server_selector, "subnet set", true);
+
+        // Create a savepoint in case we are called as part of larger
+        // transaction.
+        conn_.createSavepoint("createUpdateSubnet6");
+
+        try {
+            insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_SUBNET6, in_bindings);
+        } catch (const DuplicateEntry&) {
+            // It already exists, rollback to the savepoint to preserve
+            // any prior work.
+            conn_.rollbackToSavepoint("createUpdateSubnet6");
+
+            // We're updating, so we need to remove any existing pools and options.
+            deletePools6(subnet);
+            deletePdPools6(subnet);
+            deleteOptions6(ServerSelector::ANY(), subnet);
+
+            // Now we need to add two more bindings for WHERE clause.
+            in_bindings.add(subnet->getID());
+            in_bindings.addTempString(subnet->toText());
+
+            // Attempt the update.
+            auto cnt = updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6,
+                                         in_bindings);
+            if (!cnt) {
+                // Possible only if someone deleted it since we tried to insert it
+                // or the query is broken.
+                isc_throw(Unexpected, "Update subnet failed to find subnet id: "
+                          << subnet->getID() << ", prefix: " << subnet->toText());
+            }
+
+            // Remove existing server assocation.
+            PsqlBindArray server_bindings;
+            server_bindings.add(subnet->getID());
+            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_SERVER,
+                              server_bindings);
+        }
+
+        // Subnet was successfully created/updated.
+
+        // Insert associations with the servers.
+        PsqlBindArray attach_bindings;
+        attach_bindings.add(subnet->getID());
+        attach_bindings.addTimestamp(subnet->getModificationTime());
+        attachElementToServers(PgSqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER,
+                               server_selector, attach_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 pd_pool : subnet->getPools(Lease::TYPE_PD)) {
+            createPdPool6(server_selector, boost::dynamic_pointer_cast<Pool6>(pd_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 = OptionDescriptor::create(*desc);
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, subnet->getID(), desc_copy,
+                                    true);
+            }
+        }
+
+        // Commit the work.
+        transaction.commit();
     }
 
     /// @brief Inserts new IPv6 pool to the database.
@@ -431,10 +1187,37 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void createPool6(const ServerSelector& server_selector,
+                     const Pool6Ptr& pool,
+                     const Subnet6Ptr& subnet) {
+
+        // Create the input bindings.
+        PsqlBindArray in_bindings;
+        in_bindings.addInet6(pool->getFirstAddress());
+        in_bindings.addInet6(pool->getLastAddress());
+        in_bindings.add(subnet->getID());
+        in_bindings.addOptional(pool->getClientClass());
+        addRequiredClassesBinding(in_bindings, pool);
+        in_bindings.add(pool->getContext());
+        in_bindings.addTimestamp(subnet->getModificationTime());
+
+        // Attempt to INSERT the pool.
+        insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_POOL6, in_bindings);
+
+        // Get the id of the newly inserted pool.
+        uint64_t pool_id = getLastInsertId("dhcp6_pool", "id");
+
+        // Add the pool's options.
+        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 = OptionDescriptor::create(*desc);
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, Lease::TYPE_NA,
+                                    pool_id, desc_copy, true);
+            }
+        }
     }
 
     /// @brief Inserts new IPv6 pd pool to the database.
@@ -442,10 +1225,54 @@ public:
     /// @param server_selector Server selector.
     /// @param pd_pool Pointer to the pd pool to be inserted.
     /// @param subnet Pointer to the subnet that this pd pool belongs to.
-    void createPdPool6(const ServerSelector& /* server_selector */,
-                       const Pool6Ptr& /* pd_pool */,
-                       const Subnet6Ptr& /* subnet */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void createPdPool6(const ServerSelector& server_selector,
+                       const Pool6Ptr& pd_pool,
+                       const Subnet6Ptr& subnet) {
+
+        int plen = prefixLengthFromRange(pd_pool->getFirstAddress(),
+                                         pd_pool->getLastAddress());
+
+        // Extract excluded prefix components.
+        Optional<std::string> xprefix_txt;
+        uint8_t xlen = 0;
+        const Option6PDExcludePtr& xopt = pd_pool->getPrefixExcludeOption();
+        if (xopt) {
+            const IOAddress& prefix = pd_pool->getFirstAddress();
+            const IOAddress& xprefix = xopt->getExcludedPrefix(prefix, pd_pool->getLength());
+            xprefix_txt = xprefix.toText();
+            xlen = xopt->getExcludedPrefixLength();
+        }
+
+        // Create the input bindings.
+        PsqlBindArray in_bindings;
+        in_bindings.addInet6(pd_pool->getFirstAddress());
+        in_bindings.add(plen);
+        in_bindings.add(pd_pool->getLength());
+        in_bindings.add(subnet->getID());
+        in_bindings.addOptional(xprefix_txt);
+        in_bindings.add(xlen);
+        in_bindings.addOptional(pd_pool->getClientClass());
+        addRequiredClassesBinding(in_bindings, pd_pool);
+        in_bindings.add(pd_pool->getContext());
+        in_bindings.addTimestamp(subnet->getModificationTime());
+
+        // Attempt to INSERT the pool.
+        insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_PD_POOL, in_bindings);
+
+        // Get the id of the newly inserted pool.
+        uint64_t pd_pool_id = getLastInsertId("dhcp6_pd_pool", "id");
+
+        // Add the pool's options.
+        auto option_spaces = pd_pool->getCfgOption()->getOptionSpaceNames();
+        for (auto option_space : option_spaces) {
+            OptionContainerPtr options = pd_pool->getCfgOption()->getAll(option_space);
+            for (auto desc = options->begin(); desc != options->end(); ++desc) {
+                OptionDescriptorPtr desc_copy = OptionDescriptor::create(*desc);
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, Lease::TYPE_PD,
+                                    pd_pool_id, desc_copy, true);
+            }
+        }
     }
 
     /// @brief Sends a query to delete data from a table.
@@ -494,9 +1321,14 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deleteSubnet6(const ServerSelector& server_selector,
+                           const SubnetID& subnet_id) {
+        int index = (server_selector.amAny() ?
+                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_ANY :
+                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_WITH_TAG);
+        return (deleteTransactional(index, server_selector,
+                                    "deleting a subnet", "subnet deleted",
+                                    true, static_cast<uint32_t>(subnet_id)));
     }
 
     /// @brief Sends query to delete subnet by id.
@@ -504,9 +1336,15 @@ public:
     /// @param server_selector Server selector.
     /// @param subnet_prefix Prefix of the subnet to be deleted.
     /// @return Number of deleted subnets.
-    uint64_t deleteSubnet6(const ServerSelector& /* server_selector */,
-                           const std::string& /* subnet_prefix */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deleteSubnet6(const ServerSelector& server_selector,
+                           const std::string& subnet_prefix) {
+        int index = (server_selector.amAny() ?
+                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_ANY :
+                     PgSqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_WITH_TAG);
+        return (deleteTransactional(index, server_selector,
+                                    "deleting a subnet", "subnet deleted",
+                                    true, subnet_prefix));
+
     }
 
     /// @brief Deletes pools belonging to a subnet from the database.
@@ -515,8 +1353,14 @@ public:
     /// identifier or prefix.
     /// @param subnet Pointer to the subnet for which pools should be
     /// deleted.
-    uint64_t deletePools6(const Subnet6Ptr& /* subnet */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deletePools6(const Subnet6Ptr& subnet) {
+        PsqlBindArray in_bindings;
+        in_bindings.add(subnet->getID());
+        in_bindings.addTempString(subnet->toText());
+
+        // Run DELETE.
+        return (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_POOLS6,
+                                  in_bindings));
     }
 
     /// @brief Deletes prefix delegation pools belonging to a subnet from
@@ -526,8 +1370,14 @@ public:
     /// identifier or prefix.
     /// @param subnet Pointer to the subnet for which pd pools should be
     /// deleted.
-    uint64_t deletePdPools6(const Subnet6Ptr& /* subnet */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deletePdPools6(const Subnet6Ptr& subnet) {
+        PsqlBindArray in_bindings;
+        in_bindings.add(subnet->getID());
+        in_bindings.addTempString(subnet->toText());
+
+        // Run DELETE.
+        return (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_PD_POOLS,
+                                  in_bindings));
     }
 
     /// @brief Sends query to the database to retrieve multiple shared
@@ -543,11 +1393,220 @@ public:
     /// 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 ServerSelector& /* server_selector */,
-                            const PsqlBindArray& /* in_bindings */,
-                            SharedNetwork6Collection& /* shared_networks */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getSharedNetworks6(const StatementIndex& index,
+                            const ServerSelector& server_selector,
+                            const PsqlBindArray& in_bindings,
+                            SharedNetwork6Collection& shared_networks) {
+        uint64_t last_network_id = 0;
+        uint64_t last_option_id = 0;
+        std::string last_tag;
+
+        selectQuery(index, in_bindings,
+                    [this, &shared_networks, &last_network_id, &last_option_id, &last_tag]
+                    (PgSqlResult& r, int row) {
+            // Create a convenience worker for the row.
+            PgSqlResultRowWorker worker(r, row);
+
+            SharedNetwork6Ptr last_network;
+            if (!shared_networks.empty()) {
+                last_network = *shared_networks.rbegin();
+            }
+
+            // Network id is column 0.
+            auto network_id = worker.getBigInt(0) ;
+
+            // 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 != network_id) {
+                last_network_id = network_id;
+
+                // Reset per shared network subnet component tracking and server tag because
+                // we're now starting to process a new shared network.
+                last_option_id = 0;
+                last_tag.clear();
+
+                // name at 1.
+                last_network = SharedNetwork6::create(worker.getString(1));
+                last_network->setId(network_id);
+
+                // client_class at 2.
+                if (!worker.isColumnNull(2)) {
+                    last_network->allowClientClass(worker.getString(2));
+                }
+
+                // interface at 3.
+                if (!worker.isColumnNull(3)) {
+                    last_network->setIface(worker.getString(3));
+                }
+
+                // modification_ts at 4.
+                last_network->setModificationTime(worker.getTimestamp(4));
+
+                // preferred_lifetime (5)
+                // min_preferred_lifetime (31)
+                // max_preferred_lifetime (32)
+                last_network->setPreferred(worker.getTriplet(5, 31, 32));
+
+                // rapid_commit at 6.
+                if (!worker.isColumnNull(6)) {
+                    last_network->setRapidCommit(worker.getBool(6));
+                }
+
+                // rebind_timer at 7.
+                if (!worker.isColumnNull(7)) {
+                    last_network->setT2(worker.getTriplet(7));
+                }
+
+                // Relay addresses at 8.
+                setRelays(worker, 8, *last_network);
+
+                // renew_timer at 9.
+                if (!worker.isColumnNull(9)) {
+                    last_network->setT1(worker.getTriplet(9));
+                }
+
+                // require_client_classes at 10.
+                setRequiredClasses(worker, 10, [&last_network](const std::string& class_name) {
+                    last_network->requireClientClass(class_name);
+                });
+
+                // reservations_global at 11.
+                if (!worker.isColumnNull(11)) {
+                    last_network->setReservationsGlobal(worker.getBool(11));
+                }
+
+                // user_context at 12.
+                if (!worker.isColumnNull(12)) {
+                    last_network->setContext(worker.getJSON(12));
+                }
+
+                // valid_lifetime at 13.
+                // min_valid_lifetime at 33.
+                // max_valid_lifetime at 34.
+                if (!worker.isColumnNull(13)) {
+                    last_network->setValid(worker.getTriplet(13, 33, 34));
+                }
+
+                // option from 14 to 26.
+
+                // calculate_tee_times at 27.
+                if (!worker.isColumnNull(27)) {
+                    last_network->setCalculateTeeTimes(worker.getBool(27));
+                }
+
+                // t1_percent at 28.
+                if (!worker.isColumnNull(28)) {
+                    last_network->setT1Percent(worker.getDouble(28));
+                }
+
+                // t2_percent at 29.
+                if (!worker.isColumnNull(29)) {
+                    last_network->setT2Percent(worker.getDouble(29));
+                }
+
+                // interface_id at 30.
+                setInterfaceId(*last_network, worker, 30);
+
+                // min_preferred_lifetime at 31.
+                // max_preferred_lifetime at 32.
+                // min_valid_lifetime at 33.
+                // max_valid_lifetime at 34.
+
+                // ddns_send_updates at 35.
+                if (!worker.isColumnNull(35)) {
+                    last_network->setDdnsSendUpdates(worker.getBool(35));
+                }
+
+                // ddns_override_no_update at 36.
+                if (!worker.isColumnNull(36)) {
+                    last_network->setDdnsOverrideNoUpdate(worker.getBool(36));
+                }
+
+                // ddns_override_client_update at 37.
+                if (!worker.isColumnNull(37)) {
+                    last_network->setDdnsOverrideClientUpdate(worker.getBool(37));
+                }
+
+                // ddns_replace_client_name at 38.
+                if (!worker.isColumnNull(38)) {
+                    last_network->setDdnsReplaceClientNameMode(
+                        static_cast<D2ClientConfig::ReplaceClientNameMode>(worker.getSmallInt(38)));
+                }
+
+                // ddns_generated_prefix at 39.
+                if (!worker.isColumnNull(39)) {
+                    last_network->setDdnsGeneratedPrefix(worker.getString(39));
+                }
+
+                // ddns_qualifying_suffix at 40.
+                if (!worker.isColumnNull(40)) {
+                    last_network->setDdnsQualifyingSuffix(worker.getString(40));
+                }
+
+                // reservations_in_subnet at 41.
+                if (!worker.isColumnNull(41)) {
+                    last_network->setReservationsInSubnet(worker.getBool(41));
+                }
+
+                // reservations_in_subnet at 42.
+                if (!worker.isColumnNull(42)) {
+                    last_network->setReservationsOutOfPool(worker.getBool(42));
+                }
+
+                // cache_threshold at 43.
+                if (!worker.isColumnNull(43)) {
+                    last_network->setCacheThreshold(worker.getDouble(43));
+                }
+
+                // cache_max_age at 44.
+                if (!worker.isColumnNull(44)) {
+                    last_network->setCacheMaxAge(worker.getInt(44));
+                }
+
+                // server_tag at 45.
+
+                // Add the shared network.
+                auto ret = shared_networks.push_back(last_network);
+
+                // shared_networks is a multi index container with an unique
+                // index but this index is unique too in the database,
+                // so this is for sanity only.
+                if (!ret.second) {
+                    isc_throw(Unexpected, "add shared network failed");
+                }
+            }
+
+            // Check for new server tags.
+            if (!worker.isColumnNull(45)) {
+                std::string new_tag = worker.getString(45);
+                if (last_tag != new_tag) {
+                    if (!new_tag.empty() && !last_network->hasServerTag(ServerTag(new_tag))) {
+                        last_network->setServerTag(new_tag);
+                    }
+
+                    last_tag = new_tag;
+                }
+            }
+
+            // Parse network-specific option from 14 to 26.
+            if (!worker.isColumnNull(14) &&
+                (last_option_id < worker.getBigInt(14))) {
+                last_option_id = worker.getBigInt(14);
+
+                OptionDescriptorPtr desc = processOptionRow(Option::V6, worker, 14);
+                if (desc) {
+                    last_network->getCfgOption()->add(*desc, desc->space_name_);
+                }
+            }
+        });
+
+        // Now that we're done fetching the whole network, we have to
+        // check if it has matching server tags and toss it if it
+        // doesn't. We skip matching the server tags if we're asking
+        // for ANY shared network.
+        auto& sn_index = shared_networks.get<SharedNetworkRandomAccessIndexTag>();
+        tossNonMatchingElements(server_selector, sn_index);
     }
 
     /// @brief Sends query to retrieve single shared network by name.
@@ -557,9 +1616,28 @@ public:
     ///
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    SharedNetwork6Ptr getSharedNetwork6(const ServerSelector& server_selector,
+                                        const std::string& name) {
+        if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a shared network. Got: "
+                      << getServerTagsAsText(server_selector));
+        }
+
+        PsqlBindArray in_bindings;
+        in_bindings.add(name);
+
+        auto index = PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_NO_TAG;
+        if (server_selector.amUnassigned()) {
+            index = PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_UNASSIGNED;
+        } else if (server_selector.amAny()) {
+            index = PgSqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_ANY;
+        }
+
+        SharedNetwork6Collection shared_networks;
+        getSharedNetworks6(index, server_selector, in_bindings, shared_networks);
+
+        return (shared_networks.empty() ? SharedNetwork6Ptr() : *shared_networks.begin());
     }
 
     /// @brief Sends query to retrieve all shared networks.
@@ -567,9 +1645,19 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getAllSharedNetworks6(const ServerSelector& server_selector,
+                               SharedNetwork6Collection& shared_networks) {
+        if (server_selector.amAny()) {
+            isc_throw(InvalidOperation, "fetching all shared networks for ANY "
+                      "server is not supported");
+        }
+
+        auto index = (server_selector.amUnassigned() ?
+                      PgSqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6_UNASSIGNED :
+                      PgSqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6);
+
+        PsqlBindArray in_bindings;
+        getSharedNetworks6(index, server_selector, in_bindings, shared_networks);
     }
 
     /// @brief Sends query to retrieve modified shared networks.
@@ -578,22 +1666,138 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void getModifiedSharedNetworks6(const ServerSelector& server_selector,
+                                    const boost::posix_time::ptime& modification_ts,
+                                    SharedNetwork6Collection& shared_networks) {
+        if (server_selector.amAny()) {
+            isc_throw(InvalidOperation, "fetching modified shared networks for ANY "
+                      "server is not supported");
+        }
+
+        PsqlBindArray in_bindings;
+        in_bindings.addTimestamp(modification_ts);
+
+        auto index = (server_selector.amUnassigned() ?
+                      PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6_UNASSIGNED :
+                      PgSqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6);
+        getSharedNetworks6(index, server_selector, 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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    /// @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.amAny()) {
+            isc_throw(InvalidOperation, "creating or updating a shared network for ANY"
+                      " server is not supported");
+
+        } else if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "creating or updating a shared network without"
+                      " assigning it to a server or all servers is not supported");
+        }
+
+        PsqlBindArray in_bindings;
+        in_bindings.addTempString(shared_network->getName());
+        in_bindings.addOptional(shared_network->getClientClass(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getIface(Network::Inheritance::NONE));
+        in_bindings.addTimestamp(shared_network->getModificationTime());
+        in_bindings.add(shared_network->getPreferred(Network::Inheritance::NONE));
+        in_bindings.addMin(shared_network->getPreferred(Network::Inheritance::NONE));
+        in_bindings.addMax(shared_network->getPreferred(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getRapidCommit(Network::Inheritance::NONE));
+        in_bindings.add(shared_network->getT2(Network::Inheritance::NONE));
+        addRelayBinding(in_bindings, shared_network);
+        in_bindings.add(shared_network->getT1(Network::Inheritance::NONE));
+        addRequiredClassesBinding(in_bindings, shared_network);
+        in_bindings.addOptional(shared_network->getReservationsGlobal(Network::Inheritance::NONE));
+        in_bindings.add(shared_network->getContext());
+        in_bindings.add(shared_network->getValid(Network::Inheritance::NONE));
+        in_bindings.addMin(shared_network->getValid(Network::Inheritance::NONE));
+        in_bindings.addMax(shared_network->getValid(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getCalculateTeeTimes(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getT1Percent(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getT2Percent(Network::Inheritance::NONE));
+        addInterfaceIdBinding(in_bindings, *shared_network);
+        in_bindings.addOptional(shared_network->getDdnsSendUpdates(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getDdnsOverrideNoUpdate(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getDdnsOverrideClientUpdate(Network::Inheritance::NONE));
+        addDdnsReplaceClientNameBinding(in_bindings, shared_network);
+        in_bindings.addOptional(shared_network->getDdnsGeneratedPrefix(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getDdnsQualifyingSuffix(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getReservationsInSubnet(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getReservationsOutOfPool(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getCacheThreshold(Network::Inheritance::NONE));
+        in_bindings.addOptional(shared_network->getCacheMaxAge(Network::Inheritance::NONE));
+
+        // Start transaction (if not already in one).
+        PgSqlTransaction transaction(conn_);
+
+        // Create scoped audit revision. As long as this instance exists
+        // no new audit revisions are created in any subsequent calls.
+        ScopedAuditRevision
+            audit_revision(this,
+                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
+                           server_selector, "shared network set", true);
+
+        // Create a savepoint in case we are called as part of larger
+        // transaction.
+        conn_.createSavepoint("createUpdateSharedNetwork6");
+
+        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.
+            insertQuery(PgSqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6,
+                        in_bindings);
+
+        } catch (const DuplicateEntry&) {
+            // It already exists, rollback to the savepoint to preserve
+            // any prior work.
+            conn_.rollbackToSavepoint("createUpdateSharedNetwork6");
+
+            // We're updating, so we need to remove any options.
+            deleteOptions6(ServerSelector::ANY(), shared_network);
+
+            // Need to add one more binding for WHERE clause.
+            in_bindings.addTempString(shared_network->getName());
+
+            // Try the update.
+            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_SHARED_NETWORK6,
+                              in_bindings);
+
+            // Remove existing server assocation.
+            PsqlBindArray server_bindings;
+            server_bindings.addTempString(shared_network->getName());
+            updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_SERVER,
+                              server_bindings);
+        }
+
+        // Associate the shared network with the servers.
+        PsqlBindArray attach_bindings;
+        attach_bindings.addTempString(shared_network->getName());
+        attach_bindings.addTimestamp(shared_network->getModificationTime());
+        attachElementToServers(PgSqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER,
+                               server_selector, attach_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 = OptionDescriptor::create(*desc);
+                desc_copy->space_name_ = option_space;
+                createUpdateOption6(server_selector, shared_network->getName(),
+                                    desc_copy, true);
+            }
+        }
+
+        // Commit the work.
+        transaction.commit();
     }
 
-
     /// @brief Sends query to insert DHCP option.
     ///
     /// This method expects that the server selector contains exactly one
@@ -692,11 +1896,66 @@ public:
     /// @param option Pointer to the option descriptor encapsulating the option.
     /// @param cascade_update Boolean value indicating whether the update is
     /// performed as part of the owning element, e.g. subnet.
-    void createUpdateOption6(const ServerSelector& /* server_selector */,
-                             const SubnetID& /* subnet_id */,
-                             const OptionDescriptorPtr& /* option */,
-                             const bool /* cascade_update */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const SubnetID& subnet_id,
+                             const OptionDescriptorPtr& option,
+                             const bool cascade_update) {
+
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        // Populate input bindings.
+        PsqlBindArray in_bindings;
+        in_bindings.add(option->option_->getType());
+        addOptionValueBinding(in_bindings, option);
+        in_bindings.addOptional(option->formatted_value_);
+        in_bindings.addOptional(option->space_name_);
+        in_bindings.add(option->persistent_);
+        in_bindings.addNull();
+        in_bindings.add(subnet_id);
+        in_bindings.add(1);
+        in_bindings.add(option->getContext());
+        in_bindings.addNull();
+        in_bindings.addNull();
+        in_bindings.addTimestamp(option->getModificationTime());
+        in_bindings.addNull();
+
+        // Remember the size before we added where clause arguments.
+        size_t pre_where_size = in_bindings.size();
+
+        // Now the add the update where clause parameters
+        in_bindings.add(subnet_id);
+        in_bindings.add(option->option_->getType());
+        in_bindings.addOptional(option->space_name_);
+
+        // Start a transaction.
+        PgSqlTransaction transaction(conn_);
+
+        // Create scoped audit revision. As long as this instance exists
+        // no new audit revisions are created in any subsequent calls.
+        ScopedAuditRevision
+            audit_revision(this,
+                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
+                           server_selector, "subnet specific option set",
+                           cascade_update);
+
+        // Try to update the subnet option.
+        if (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID,
+                              in_bindings) == 0) {
+            // The option doesn't exist, so we'll try to insert it.
+            // Remove the update where clause bindings.
+            while (in_bindings.size() > pre_where_size) {
+                in_bindings.popBack();
+            }
+
+            // Try to insert the option.
+            insertOption6(server_selector, in_bindings, option->getModificationTime());
+        }
+
+        // Commit the work.
+        transaction.commit();
     }
 
     /// @brief Sends query to insert or update DHCP option in a pool.
@@ -705,11 +1964,21 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    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, false);
     }
 
 
@@ -719,13 +1988,24 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
-    }
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const asiolink::IOAddress& pd_pool_prefix,
+                             const uint8_t pd_pool_prefix_length,
+                             const OptionDescriptorPtr& option) {
+        uint64_t pd_pool_id = 0;
+        Pool6Ptr pd_pool = getPdPool6(server_selector,
+                                      pd_pool_prefix,
+                                      pd_pool_prefix_length,
+                                      pd_pool_id);
+        if (!pd_pool) {
+            isc_throw(BadValue, "no prefix delegation pool found for prefix "
+                      << "of " << pd_pool_prefix << "/"
+                      << static_cast<unsigned>(pd_pool_prefix_length));
+        }
 
+        createUpdateOption6(server_selector, Lease::TYPE_PD,
+                            pd_pool_id, option, false);
+    }
 
     /// @brief Sends query to insert or update DHCP option in an address
     /// or prefix delegation pool.
@@ -737,12 +2017,103 @@ public:
     /// @param option Pointer to the option descriptor encapsulating the option.
     /// @param cascade_update Boolean value indicating whether the update is
     /// performed as part of the owning element, e.g. subnet.
-    void createUpdateOption6(const ServerSelector& /* server_selector */,
-                             const Lease::Type& /* pool_type */,
-                             const uint64_t /* pool_id */,
-                             const OptionDescriptorPtr& /* option */,
-                             const bool /* cascade_update */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const Lease::Type& pool_type,
+                             const uint64_t pool_id,
+                             const OptionDescriptorPtr& option,
+                             const bool cascade_update) {
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        std::string msg = "creating or updating ";
+        if (pool_type == Lease::TYPE_PD) {
+            msg += "prefix delegation";
+        } else {
+            msg += "address";
+        }
+        msg += " pool level option";
+
+        PsqlBindArray in_bindings;
+
+        in_bindings.add(option->option_->getType());
+        addOptionValueBinding(in_bindings, option);
+        in_bindings.addOptional(option->formatted_value_);
+        in_bindings.addOptional(option->space_name_);
+        in_bindings.add(option->persistent_);
+        in_bindings.addNull();
+        in_bindings.addNull();
+
+        // scope_id
+        if (pool_type == Lease::TYPE_NA) {
+            in_bindings.add(5);
+        } else {
+            in_bindings.add(6);
+        }
+
+        in_bindings.add(option->getContext());
+        in_bindings.addNull();
+
+        // pool_id
+        if (pool_type == Lease::TYPE_NA) {
+            in_bindings.add(pool_id);
+        } else {
+            in_bindings.addNull();
+        }
+
+        in_bindings.addTimestamp(option->getModificationTime());
+
+        // pd_pool_id
+        if (pool_type == Lease::TYPE_PD) {
+            in_bindings.add(pool_id);
+        } else {
+            in_bindings.addNull();
+        }
+
+        // Remember the size before we added where clause arguments.
+        size_t pre_where_size = in_bindings.size();
+
+        // Now the add the update where clause parameters
+        in_bindings.add(pool_id);
+        in_bindings.add(option->option_->getType());
+        in_bindings.addOptional(option->space_name_);
+
+        // Start transaction.
+        PgSqlTransaction transaction(conn_);
+
+        // Create scoped audit revision. As long as this instance exists
+        // no new audit revisions are created in any subsequent calls.
+        if (pool_type == Lease::TYPE_PD) {
+            msg = "prefix delegation";
+        } else {
+            msg = "address";
+        }
+
+        msg += " pool specific option set";
+        ScopedAuditRevision
+            audit_revision(this,
+                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
+                           server_selector, msg, cascade_update);
+
+        auto index = (pool_type == Lease::TYPE_NA ?
+                      PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID :
+                      PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID);
+
+        // Try to update the option.
+        if (updateDeleteQuery(index, in_bindings) == 0) {
+            // The option doesn't exist, so we'll try to insert it.
+            // Remove the update where clause bindings.
+            while (in_bindings.size() > pre_where_size) {
+                in_bindings.popBack();
+            }
+
+            // Try to insert the option.
+            insertOption6(server_selector, in_bindings, option->getModificationTime());
+        }
+
+        // Commit the work.
+        transaction.commit();
     }
 
     /// @brief Sends query to insert or update DHCP option in a shared network.
@@ -753,11 +2124,66 @@ public:
     /// @param option Pointer to the option descriptor encapsulating the option.
     /// @param cascade_update Boolean value indicating whether the update is
     /// performed as part of the owning element, e.g. shared network.
-    void createUpdateOption6(const ServerSelector& /* server_selector */,
-                             const std::string& /* shared_network_name */,
-                             const OptionDescriptorPtr& /* option */,
-                             const bool /* cascade_update */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    void createUpdateOption6(const ServerSelector& server_selector,
+                             const std::string& shared_network_name,
+                             const OptionDescriptorPtr& option,
+                             const bool cascade_update) {
+        if (server_selector.amUnassigned()) {
+            isc_throw(NotImplemented, "managing configuration for no particular server"
+                      " (unassigned) is unsupported at the moment");
+        }
+
+        // Populate input bindings.
+        PsqlBindArray in_bindings;
+        in_bindings.add(option->option_->getType());
+        addOptionValueBinding(in_bindings, option);
+        in_bindings.addOptional(option->formatted_value_);
+        in_bindings.addOptional(option->space_name_);
+        in_bindings.add(option->persistent_);
+        in_bindings.addNull();
+        in_bindings.addNull();
+        in_bindings.add(4);
+        in_bindings.add(option->getContext());
+        in_bindings.add(shared_network_name);
+        in_bindings.addNull();
+        in_bindings.addTimestamp(option->getModificationTime());
+        in_bindings.addNull();
+
+        // Remember the size before we added where clause arguments.
+        size_t pre_where_size = in_bindings.size();
+
+        // Now the add the update where clause parameters
+        in_bindings.add(shared_network_name);
+        in_bindings.add(option->option_->getType());
+        in_bindings.addOptional(option->space_name_);
+
+        // Start a transaction.
+        PgSqlTransaction transaction(conn_);
+
+        // Create scoped audit revision. As long as this instance exists
+        // no new audit revisions are created in any subsequent calls.
+        ScopedAuditRevision
+            audit_revision(this,
+                           PgSqlConfigBackendDHCPv6Impl::CREATE_AUDIT_REVISION,
+                           server_selector, "shared network specific option set",
+                           cascade_update);
+
+        // Try to update the subnet option.
+        if (updateDeleteQuery(PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SHARED_NETWORK,
+                              in_bindings) == 0) {
+            // The option doesn't exist, so we'll try to insert it.
+            // Remove the update where clause bindings.
+            while (in_bindings.size() > pre_where_size) {
+                in_bindings.popBack();
+            }
+
+            // Try to insert the option.
+            insertOption6(server_selector, in_bindings, option->getModificationTime());
+        }
+
+        // Commit the work.
+        transaction.commit();
+
     }
 
     /// @brief Sends query to insert or update DHCP option in a client class.
@@ -777,7 +2203,7 @@ public:
     /// @param option_def Pointer to the option definition to be inserted or updated.
     void createUpdateOptionDef6(const ServerSelector& server_selector,
                                 const OptionDefinitionPtr& option_def) {
-        createUpdateOptionDef(server_selector, option_def, DHCP6_OPTION_SPACE,
+        createUpdateOptionDef(server_selector, Option::V6, option_def, DHCP6_OPTION_SPACE,
                               PgSqlConfigBackendDHCPv6Impl::GET_OPTION_DEF6_CODE_SPACE,
                               PgSqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6,
                               PgSqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6,
@@ -861,11 +2287,22 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deleteOption6(const ServerSelector& server_selector,
+                           const SubnetID& subnet_id,
+                           const uint16_t code,
+                           const std::string& space) {
+        PsqlBindArray in_bindings;
+        in_bindings.add(subnet_id);
+        in_bindings.add(code);
+        in_bindings.add(space);
+
+        // Run DELETE.
+        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SUBNET_ID,
+                                    server_selector,
+                                    "deleting option for a subnet",
+                                    "subnet specific option deleted",
+                                    false,
+                                    in_bindings));
     }
 
     /// @brief Deletes pool level option.
@@ -876,12 +2313,24 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    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) {
+        PsqlBindArray in_bindings;
+        in_bindings.addInet6(pool_start_address);
+        in_bindings.addInet6(pool_end_address);
+        in_bindings.add(code);
+        in_bindings.add(space);
+
+        // Run DELETE.
+        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_POOL_RANGE,
+                                    server_selector,
+                                    "deleting option for a pool",
+                                    "address pool specific option deleted",
+                                    false,
+                                    in_bindings));
     }
 
     /// @brief Deletes pd pool level option.
@@ -892,12 +2341,24 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    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) {
+        PsqlBindArray in_bindings;
+        in_bindings.addTempString(pd_pool_prefix.toText());
+        in_bindings.add(pd_pool_prefix_length);
+        in_bindings.add(code);
+        in_bindings.add(space);
+
+        // Run DELETE.
+        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_PD_POOL,
+                                    server_selector,
+                                    "deleting option for a prefix delegation pool",
+                                    "prefix delegation pool specific option deleted",
+                                    false,
+                                    in_bindings));
     }
 
     /// @brief Deletes shared network level option.
@@ -908,11 +2369,22 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deleteOption6(const db::ServerSelector& server_selector,
+                           const std::string& shared_network_name,
+                           const uint16_t code,
+                           const std::string& space) {
+        PsqlBindArray in_bindings;
+        in_bindings.add(shared_network_name);
+        in_bindings.add(code);
+        in_bindings.add(space);
+
+        // Run DELETE.
+        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SHARED_NETWORK,
+                                    server_selector,
+                                    "deleting option for a shared network",
+                                    "shared network specific option deleted",
+                                    false,
+                                    in_bindings));
     }
 
     /// @brief Deletes options belonging to a subnet from the database.
@@ -921,9 +2393,17 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deleteOptions6(const ServerSelector& server_selector,
+                            const Subnet6Ptr& subnet) {
+        PsqlBindArray in_bindings;
+        in_bindings.add(subnet->getID());
+        in_bindings.addTempString(subnet->toText());
+
+        // Run DELETE.
+        return (deleteTransactional(DELETE_OPTIONS6_SUBNET_ID_PREFIX, server_selector,
+                                    "deleting options for a subnet",
+                                    "subnet specific options deleted",
+                                    true, in_bindings));
     }
 
     /// @brief Deletes options belonging to a shared network from the database.
@@ -932,9 +2412,17 @@ public:
     /// @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 */) {
-        isc_throw(NotImplemented, NOT_IMPL_STR);
+    uint64_t deleteOptions6(const ServerSelector& server_selector,
+                            const SharedNetwork6Ptr& shared_network) {
+        PsqlBindArray in_bindings;
+        in_bindings.addTempString(shared_network->getName());
+
+        // Run DELETE.
+        return (deleteTransactional(PgSqlConfigBackendDHCPv6Impl::
+                                    DELETE_OPTIONS6_SHARED_NETWORK, server_selector,
+                                    "deleting options for a shared network",
+                                    "shared network specific options deleted",
+                                    true, in_bindings));
     }
 
     /// @brief Deletes options belonging to a client class from the database.
@@ -1205,6 +2693,46 @@ public:
         return (true);
     }
 
+    /// @brief Sets a Network6 interface ID from result set col.
+    ///
+    /// Sets the interface id of the Network6 to the value carried in the
+    /// given column in a result set row.
+    ///
+    /// @param network shared network or subnet to receive the interface ID
+    /// @param worker result set row worker contain the database row
+    /// @param col column within the row from which to take the value
+    void setInterfaceId(Network6& network, PgSqlResultRowWorker& worker, size_t col) {
+        if (!worker.isColumnNull(col)) {
+            std::vector<uint8_t> iface_id_data;
+            worker.getBytes(col, iface_id_data);
+            if (!iface_id_data.empty()) {
+                OptionPtr opt_iface_id(new Option(Option::V6, D6O_INTERFACE_ID, iface_id_data));
+                network.setInterfaceId(opt_iface_id);
+            }
+        }
+    }
+
+    /// @brief Adds network interface ID to a bind array.
+    ///
+    /// Adds the interface id to end of the given bind array as a vector of bytes.
+    ///
+    /// @param bindings PsqlBindArray to which the ID should be added.
+    /// @param network Pointer to shared network or subnet for which ID binding
+    /// should be created.
+    void addInterfaceIdBinding(PsqlBindArray& bindings, const Network6& network) {
+        auto opt_iface_id = network.getInterfaceId(Network::Inheritance::NONE);
+        if (!opt_iface_id) {
+            bindings.addNull();
+        } else {
+            auto iface_id_data = opt_iface_id->getData();
+            if (iface_id_data.empty()) {
+                bindings.addNull();
+            } else {
+                bindings.addTempBinary(iface_id_data);
+            }
+        }
+    }
+
 };
 
 namespace {
@@ -2061,7 +3589,7 @@ TaggedStatementArray tagged_statements = { {
             OID_VARCHAR,    //  4 space
             OID_BOOL,       //  5 persistent
             OID_VARCHAR,    //  6 dhcp_client_class
-            OID_INT8,       //  7 dhcp4_subnet_id
+            OID_INT8,       //  7 dhcp6_subnet_id
             OID_INT2,       //  8 scope_id
             OID_TEXT,       //  9 user_context
             OID_VARCHAR,    // 10 shared_network_name
index c561fca10d3c1ad841b4ae294a568d3cc86d496a..ba2752e8b2d74a393a2c22f8ff9481ea76363e97 100644 (file)
@@ -600,7 +600,7 @@ public:
 
     /// @brief Returns backend type in the textual format.
     ///
-    /// @return "mysql".
+    /// @return "postgresql".
     virtual std::string getType() const;
 
     /// @brief Returns backend host.
@@ -619,13 +619,13 @@ public:
     /// @return Port number on which database service is available.
     virtual uint16_t getPort() const;
 
-    /// @brief Registers the MySQL backend factory with backend config manager
+    /// @brief Registers the PgSql 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
+    /// @brief Unregisters the PgSql backend factory and discards PgSql backends
     ///
     /// This should be called by the hook lib unload() function.
     static void unregisterBackendType();
index 7069723c7215a811eb26c92d6be0fa2bbf4c91e5..a88e86ce90421badc56b50de223eafc57865d896 100644 (file)
@@ -467,8 +467,9 @@ PgSqlConfigBackendImpl::getOptionDefs(const int index,
 
 void
 PgSqlConfigBackendImpl::createUpdateOptionDef(const db::ServerSelector& server_selector,
+                                              const Option::Universe& universe,
                                               const OptionDefinitionPtr& option_def,
-                                              const std::string& space,
+                                              const std::string& /* space */,
                                               const int& /*get_option_def_code_space*/,
                                               const int& insert_option_def,
                                               const int& update_option_def,
@@ -545,7 +546,7 @@ PgSqlConfigBackendImpl::createUpdateOptionDef(const db::ServerSelector& server_s
         // Successfully inserted the definition. Now, we have to associate it
         // with the server tag.
         PsqlBindArray attach_bindings;
-        uint64_t id = getLastInsertId((space == DHCP4_OPTION_SPACE ?
+        uint64_t id = getLastInsertId((universe == Option::V4 ?
                                        "dhcp4_option_def" : "dhcp6_option_def"), "id");
         attach_bindings.add(id);
         attach_bindings.addTimestamp(option_def->getModificationTime());
@@ -898,6 +899,7 @@ PgSqlConfigBackendImpl::attachElementToServers(const int index,
         server_bindings.popBack();
     }
 }
+
 ServerPtr
 PgSqlConfigBackendImpl::getServer(const int index, const ServerTag& server_tag) {
     ServerCollection servers;
@@ -1044,6 +1046,17 @@ PgSqlConfigBackendImpl::getPort() const {
     return (0);
 }
 
+void
+PgSqlConfigBackendImpl::addDdnsReplaceClientNameBinding(PsqlBindArray& bindings,
+                                                        const NetworkPtr& network) {
+    auto ddns_rcn_mode = network->getDdnsReplaceClientNameMode(Network::Inheritance::NONE);
+    if (!ddns_rcn_mode.unspecified()) {
+        bindings.add(static_cast<uint8_t>(ddns_rcn_mode.get()));
+    } else {
+        bindings.addNull();
+    }
+}
+
 void
 PgSqlConfigBackendImpl::addRelayBinding(PsqlBindArray& bindings,
                                         const NetworkPtr& network) {
index fc436488d370128468568aba015bba1abef14876..a2ce5e31d8a55b6d5fdc9f687a072a1b085c63b5 100644 (file)
@@ -351,6 +351,7 @@ public:
     /// @brief Creates or updates an option definition.
     ///
     /// @param server_selector Server selector.
+    /// @param universe Option universe, i.e. V4 or V6.
     /// @param option_def Option definition to be added or updated.
     /// @param space Default option space
     /// @param get_option_def_code_space Statement getting option
@@ -365,6 +366,7 @@ public:
     /// it is a global option definition.
     /// @throw NotImplemented if server selector is "unassigned".
     void createUpdateOptionDef(const db::ServerSelector& server_selector,
+                               const Option::Universe& universe,
                                const OptionDefinitionPtr& option_def,
                                const std::string& space,
                                const int& get_option_def_code_space,
@@ -553,6 +555,17 @@ public:
                                 const db::ServerSelector& server_selector,
                                 const db::PsqlBindArray& in_bindings);
 
+    /// @brief Adds network ddns-replace-client-name mode to a bind array.
+    ///
+    /// If network's value of ddsn-replace-client-name mode has been specified
+    /// it is added to the binding, otherwise a null is added to the binding.
+    ///
+    /// @param bindings PsqlBindArray to which the mode should be added.
+    /// @param network Pointer to shared network or subnet for which mode binding
+    /// should be created.
+    void addDdnsReplaceClientNameBinding(db::PsqlBindArray& bindings,
+                                         const NetworkPtr& network);
+
     /// @brief Adds network relays addresses to a bind array.
     ///
     /// Creates an Element tree of relay addresses add adds that to the end
index 0a44d23dcf95a7a394397ef2f07046193c436918..0d693a62819770ab111766122049c07afb44bea1 100644 (file)
@@ -157,12 +157,161 @@ TEST_F(PgSqlConfigBackendDHCPv6Test, nullKeyErrorTest) {
     nullKeyErrorTest();
 }
 
+TEST_F(PgSqlConfigBackendDHCPv6Test, createUpdateSubnet6SelectorsTest) {
+    createUpdateSubnet6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSubnet6Test) {
+    getSubnet6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSubnet6byIdSelectorsTest) {
+    getSubnet6byIdSelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSubnet6WithOptionalUnspecifiedTest) {
+    getSubnet6WithOptionalUnspecifiedTest();
+}
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSubnet6SharedNetworkTest) {
+    getSubnet6SharedNetworkTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSubnet6ByPrefixTest) {
+    getSubnet6ByPrefixTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSubnet6byPrefixSelectorsTest) {
+    getSubnet6byPrefixSelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getAllSubnets6Test) {
+    getAllSubnets6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getAllSubnets6SelectorsTest) {
+    getAllSubnets6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getAllSubnets6WithServerTagsTest) {
+    getAllSubnets6WithServerTagsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteSubnet6Test) {
+    deleteSubnet6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteSubnet6ByIdSelectorsTest) {
+    deleteSubnet6ByIdSelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteSubnet6ByPrefixSelectorsTest) {
+    deleteSubnet6ByPrefixSelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteAllSubnets6SelectorsTest) {
+    deleteAllSubnets6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, unassignedSubnet6Test) {
+    unassignedSubnet6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getModifiedSubnets6Test) {
+    getModifiedSubnets6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getModifiedSubnets6SelectorsTest) {
+    getModifiedSubnets6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, subnetLifetimeTest) {
+    subnetLifetimeTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSharedNetworkSubnets6Test) {
+    getSharedNetworkSubnets6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, subnetUpdatePoolsTest) {
+    subnetUpdatePoolsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, subnetOptionsTest) {
+    subnetOptionsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSharedNetwork6Test) {
+    getSharedNetwork6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSharedNetwork6SelectorsTest) {
+    getSharedNetwork6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, createUpdateSharedNetwork6Test) {
+    createUpdateSharedNetwork6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, createUpdateSharedNetwork6SelectorsTest) {
+    createUpdateSharedNetwork6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getSharedNetwork6WithOptionalUnspecifiedTest) {
+    getSharedNetwork6WithOptionalUnspecifiedTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteSharedNetworkSubnets6Test) {
+    deleteSharedNetworkSubnets6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getAllSharedNetworks6Test) {
+    getAllSharedNetworks6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getAllSharedNetworks6SelectorsTest) {
+    getAllSharedNetworks6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getAllSharedNetworks6WithServerTagsTest) {
+    getAllSharedNetworks6WithServerTagsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getModifiedSharedNetworks6Test) {
+    getModifiedSharedNetworks6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, getModifiedSharedNetworks6SelectorsTest) {
+    getModifiedSharedNetworks6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteSharedNetwork6Test) {
+    deleteSharedNetwork6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteSharedNetwork6SelectorsTest) {
+    deleteSharedNetwork6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, deleteAllSharedNetworks6SelectorsTest) {
+    deleteAllSharedNetworks6SelectorsTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, unassignedSharedNetworkTest) {
+    unassignedSharedNetworkTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, sharedNetworkLifetimeTest) {
+    sharedNetworkLifetimeTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, sharedNetworkOptionsTest) {
+    sharedNetworkOptionsTest();
+}
+
 TEST_F(PgSqlConfigBackendDHCPv6Test, getOptionDef6Test) {
     getOptionDef6Test();
 }
 
-// skipping shared-network and subnet tests for now
-
 TEST_F(PgSqlConfigBackendDHCPv6Test, optionDefs6WithServerTagsTest) {
     optionDefs6WithServerTagsTest();
 }
@@ -191,6 +340,34 @@ TEST_F(PgSqlConfigBackendDHCPv6Test, getModifiedOptions6Test) {
     getModifiedOptions6Test();
 }
 
+TEST_F(PgSqlConfigBackendDHCPv6Test, createUpdateDeleteSubnetOption6Test) {
+    createUpdateDeleteSubnetOption6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, createUpdateDeletePoolOption6Test) {
+    createUpdateDeletePoolOption6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, createUpdateDeletePdPoolOption6Test) {
+    createUpdateDeletePdPoolOption6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, createUpdateDeleteSharedNetworkOption6Test) {
+    createUpdateDeleteSharedNetworkOption6Test();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, subnetOptionIdOrderTest) {
+    subnetOptionIdOrderTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, sharedNetworkOptionIdOrderTest) {
+    sharedNetworkOptionIdOrderTest();
+}
+
+TEST_F(PgSqlConfigBackendDHCPv6Test, multipleAuditEntriesTest) {
+    multipleAuditEntriesTest();
+}
+
 /// @brief Test fixture for verifying database connection loss-recovery
 /// behavior.
 class PgSqlConfigBackendDHCPv6DbLostCallbackTest : public GenericConfigBackendDbLostCallbackTest {
index 762602a0c65c31200544a9bbaa92507415570cca..5c6e9e6e888903e57d89a99150d35cc8a8e4cbab 100644 (file)
@@ -481,7 +481,7 @@ GenericConfigBackendDHCPv6Test::createUpdateDeleteServerTest() {
     // It should not be possible to create a duplicate of the logical
     // server 'all'.
     auto all_server = Server::create(ServerTag("all"), "this is logical server all");
-    EXPECT_THROW(cbptr_->createUpdateServer6(all_server), isc::InvalidOperation);
+    ASSERT_THROW(cbptr_->createUpdateServer6(all_server), isc::InvalidOperation);
 
     ServerPtr returned_server;
 
@@ -525,7 +525,7 @@ GenericConfigBackendDHCPv6Test::createUpdateDeleteServerTest() {
     EXPECT_TRUE(returned_server);
 
     // Deleting logical server 'all' is not allowed.
-    EXPECT_THROW(cbptr_->deleteServer6(ServerTag()), isc::InvalidOperation);
+    ASSERT_THROW(cbptr_->deleteServer6(ServerTag()), isc::InvalidOperation);
 
     // Delete the existing server.
     ASSERT_NO_THROW_LOG(servers_deleted = cbptr_->deleteServer6(ServerTag("server1")));
@@ -670,7 +670,7 @@ GenericConfigBackendDHCPv6Test::globalParameters6WithServerTagsTest() {
 
     // Try to insert one of them and associate with non-existing server.
     // This should fail because the server must be inserted first.
-    EXPECT_THROW(cbptr_->createUpdateGlobalParameter6(ServerSelector::ONE("server1"),
+    ASSERT_THROW(cbptr_->createUpdateGlobalParameter6(ServerSelector::ONE("server1"),
                                                       global_parameter1),
                  NullKeyError);
 
@@ -1000,11 +1000,11 @@ GenericConfigBackendDHCPv6Test::createUpdateSubnet6SelectorsTest() {
                                                     subnet));
 
     // Not supported server selectors.
-    EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ANY(), subnet),
+    ASSERT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ANY(), subnet),
                  isc::InvalidOperation);
 
     // Not implemented server selectors.
-    EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::UNASSIGNED(),
+    ASSERT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::UNASSIGNED(),
                                              subnet),
                  isc::NotImplemented);
 }
@@ -1024,7 +1024,7 @@ GenericConfigBackendDHCPv6Test::getSubnet6Test() {
     auto subnet2 = test_subnets_[2];
 
     // An attempt to add a subnet to a non-existing server (server1) should fail.
-    EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                              subnet2),
                  NullKeyError);
 
@@ -1053,11 +1053,11 @@ GenericConfigBackendDHCPv6Test::getSubnet6Test() {
     }
 
     // We are not going to support selection of a single entry for multiple servers.
-    EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                     subnet->getID()),
                  isc::InvalidOperation);
 
-    EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                     subnet->toText()),
                  isc::InvalidOperation);
 
@@ -1141,7 +1141,7 @@ GenericConfigBackendDHCPv6Test::getSubnet6Test() {
     // Subnets are 2001:db8:1::/48 id 1024 and 2001:db8:3::/64 id 8192
     subnet2.reset(new Subnet6(IOAddress("2001:db8:1::"),
                               48, 30, 40, 50, 80, 8192));
-    EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2),
+    ASSERT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"),  subnet2),
                  DuplicateEntry);
 }
 
@@ -1154,7 +1154,7 @@ GenericConfigBackendDHCPv6Test::getSubnet6byIdSelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->getSubnet6(ServerSelector::ONE("server1"), SubnetID(1)));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                     SubnetID(1)),
                  isc::InvalidOperation);
 }
@@ -1175,8 +1175,9 @@ GenericConfigBackendDHCPv6Test::getSubnet6WithOptionalUnspecifiedTest() {
     ASSERT_NO_THROW_LOG(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
 
     // Fetch this subnet by subnet identifier.
-    Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
-                                                    subnet->getID());
+    Subnet6Ptr returned_subnet;
+    ASSERT_NO_THROW_LOG(returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
+                                                             subnet->getID()));
     ASSERT_TRUE(returned_subnet);
 
     EXPECT_TRUE(returned_subnet->getIface().unspecified());
@@ -1305,7 +1306,7 @@ GenericConfigBackendDHCPv6Test::getSubnet6byPrefixSelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->getSubnet6(ServerSelector::ONE("server1"), "192.0.2.0/26"));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                     "192.0.2.0/26"),
                  isc::InvalidOperation);
 }
@@ -1418,7 +1419,7 @@ GenericConfigBackendDHCPv6Test::getAllSubnets6SelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->getAllSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" })));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->getAllSubnets6(ServerSelector::ANY()), isc::InvalidOperation);
+    ASSERT_THROW(cbptr_->getAllSubnets6(ServerSelector::ANY()), isc::InvalidOperation);
 }
 
 void
@@ -1627,12 +1628,12 @@ GenericConfigBackendDHCPv6Test::deleteSubnet6ByIdSelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->deleteSubnet6(ServerSelector::ONE("server1"), SubnetID(1)));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->deleteSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                            SubnetID(1)),
                  isc::InvalidOperation);
 
     // Not implemented selectors.
-    EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::UNASSIGNED(), SubnetID(1)),
+    ASSERT_THROW(cbptr_->deleteSubnet6(ServerSelector::UNASSIGNED(), SubnetID(1)),
                  isc::NotImplemented);
 }
 
@@ -1644,12 +1645,12 @@ GenericConfigBackendDHCPv6Test::deleteSubnet6ByPrefixSelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->deleteSubnet6(ServerSelector::ONE("server1"), "192.0.2.0/26"));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->deleteSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                            "192.0.2.0/26"),
                  isc::InvalidOperation);
 
     // Not implemented selectors.
-    EXPECT_THROW(cbptr_->deleteSubnet6(ServerSelector::UNASSIGNED(), "192.0.2.0/26"),
+    ASSERT_THROW(cbptr_->deleteSubnet6(ServerSelector::UNASSIGNED(), "192.0.2.0/26"),
                  isc::NotImplemented);
 }
 
@@ -1661,9 +1662,9 @@ GenericConfigBackendDHCPv6Test::deleteAllSubnets6SelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->deleteAllSubnets6(ServerSelector::ONE("server1")));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->deleteAllSubnets6(ServerSelector::ANY()),
+    ASSERT_THROW(cbptr_->deleteAllSubnets6(ServerSelector::ANY()),
                  isc::InvalidOperation);
-    EXPECT_THROW(cbptr_->deleteAllSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" })),
+    ASSERT_THROW(cbptr_->deleteAllSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" })),
                  isc::InvalidOperation);
 }
 
@@ -2008,7 +2009,7 @@ GenericConfigBackendDHCPv6Test::getSharedNetwork6Test() {
                                                            shared_network2));
 
     // We are not going to support selection of a single entry for multiple servers.
-    EXPECT_THROW(cbptr_->getSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->getSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                            test_networks_[0]->getName()),
                  isc::InvalidOperation);
 
@@ -2088,7 +2089,7 @@ GenericConfigBackendDHCPv6Test::getSharedNetwork6SelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->getSharedNetwork6(ServerSelector::ONE("server1"), "level1"));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->getSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->getSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                            "level1"),
                  isc::InvalidOperation);
 }
@@ -2098,7 +2099,7 @@ GenericConfigBackendDHCPv6Test::createUpdateSharedNetwork6Test() {
     auto shared_network = test_networks_[0];
 
     // An attempt to insert the shared network for non-existing server should fail.
-    EXPECT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"),
+    ASSERT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"),
                                                     shared_network),
                  NullKeyError);
 
@@ -2164,11 +2165,11 @@ GenericConfigBackendDHCPv6Test::createUpdateSharedNetwork6SelectorsTest() {
                                                            shared_network));
 
     // Not supported server selectors.
-    EXPECT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ANY(), shared_network),
+    ASSERT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ANY(), shared_network),
                  isc::InvalidOperation);
 
     // Not implemented server selectors.
-    EXPECT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::UNASSIGNED(),
+    ASSERT_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::UNASSIGNED(),
                                                     shared_network),
                  isc::NotImplemented);
 }
@@ -2246,16 +2247,16 @@ GenericConfigBackendDHCPv6Test::getSharedNetwork6WithOptionalUnspecifiedTest() {
 
 void
 GenericConfigBackendDHCPv6Test::deleteSharedNetworkSubnets6Test() {
-    EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::UNASSIGNED(),
+    ASSERT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::UNASSIGNED(),
                                                      test_networks_[1]->getName()),
                  isc::InvalidOperation);
-    EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::ALL(),
+    ASSERT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::ALL(),
                                                      test_networks_[1]->getName()),
                  isc::InvalidOperation);
-    EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::ONE("server1"),
+    ASSERT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::ONE("server1"),
                                                      test_networks_[1]->getName()),
                  isc::InvalidOperation);
-    EXPECT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->deleteSharedNetworkSubnets6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                                      test_networks_[1]->getName()),
                  isc::InvalidOperation);
 }
@@ -2420,7 +2421,7 @@ GenericConfigBackendDHCPv6Test::getAllSharedNetworks6SelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->getAllSharedNetworks6(ServerSelector::MULTIPLE({ "server1", "server2" })));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->getAllSharedNetworks6(ServerSelector::ANY()),
+    ASSERT_THROW(cbptr_->getAllSharedNetworks6(ServerSelector::ANY()),
                  isc::InvalidOperation);
 }
 
@@ -2546,7 +2547,7 @@ GenericConfigBackendDHCPv6Test::getModifiedSharedNetworks6SelectorsTest() {
                                                            timestamps_["yesterday"]));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->getModifiedSharedNetworks6(ServerSelector::ANY(),
+    ASSERT_THROW(cbptr_->getModifiedSharedNetworks6(ServerSelector::ANY(),
                                                     timestamps_["yesterday"]),
                  isc::InvalidOperation);
 }
@@ -2610,13 +2611,13 @@ GenericConfigBackendDHCPv6Test::deleteSharedNetwork6Test() {
     }
 
     // We are not going to support deletion of a single entry for multiple servers.
-    EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+    ASSERT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
                                               shared_network3->getName()),
                  isc::InvalidOperation);
 
     // We currently don't support deleting a shared network with specifying
     // an unassigned server tag. Use ANY to delete any subnet instead.
-    EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::UNASSIGNED(),
+    ASSERT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::UNASSIGNED(),
                                               shared_network1->getName()),
                  isc::NotImplemented);
 
@@ -2649,12 +2650,12 @@ GenericConfigBackendDHCPv6Test::deleteSharedNetwork6SelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->deleteSharedNetwork6(ServerSelector::ONE("server1"), "level1"));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
-                                              "level1"),
+    ASSERT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                           "level1"),
                  isc::InvalidOperation);
 
     // Not implemented selectors.
-    EXPECT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::UNASSIGNED(), "level1"),
+    ASSERT_THROW(cbptr_->deleteSharedNetwork6(ServerSelector::UNASSIGNED(), "level1"),
                  isc::NotImplemented);
 }
 
@@ -2666,9 +2667,9 @@ GenericConfigBackendDHCPv6Test::deleteAllSharedNetworks6SelectorsTest() {
     ASSERT_NO_THROW_LOG(cbptr_->deleteAllSharedNetworks6(ServerSelector::ONE("server1")));
 
     // Not supported selectors.
-    EXPECT_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::ANY()),
+    ASSERT_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::ANY()),
                  isc::InvalidOperation);
-    EXPECT_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::MULTIPLE({ "server1", "server2" })),
+    ASSERT_THROW(cbptr_->deleteAllSharedNetworks6(ServerSelector::MULTIPLE({ "server1", "server2" })),
                  isc::InvalidOperation);
 }
 
@@ -2885,7 +2886,7 @@ GenericConfigBackendDHCPv6Test::optionDefs6WithServerTagsTest() {
 
     // An attempt to create option definition for non-existing server should
     // fail.
-    EXPECT_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server1"),
+    ASSERT_THROW(cbptr_->createUpdateOptionDef6(ServerSelector::ONE("server1"),
                                                 option1),
                  NullKeyError);
 
@@ -3278,7 +3279,7 @@ GenericConfigBackendDHCPv6Test::globalOptions6WithServerTagsTest() {
     OptionDescriptorPtr opt_timezone2 = test_options_[6];
     OptionDescriptorPtr opt_timezone3 = test_options_[7];
 
-    EXPECT_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server1"),
+    ASSERT_THROW(cbptr_->createUpdateOption6(ServerSelector::ONE("server1"),
                                              opt_timezone1),
                  NullKeyError);
 
@@ -3655,7 +3656,7 @@ void
 GenericConfigBackendDHCPv6Test::createUpdateDeletePoolOption6Test() {
     // Insert new subnet.
     Subnet6Ptr subnet = test_subnets_[1];
-    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
+    ASSERT_NO_THROW_LOG(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
 
     {
         SCOPED_TRACE("CREATE audit entry for a subnet");
@@ -3672,10 +3673,10 @@ GenericConfigBackendDHCPv6Test::createUpdateDeletePoolOption6Test() {
                                          IOAddress("2001:db8::10"));
     ASSERT_TRUE(pool);
     OptionDescriptorPtr opt_posix_timezone = test_options_[0];
-    cbptr_->createUpdateOption6(ServerSelector::ANY(),
-                                pool->getFirstAddress(),
-                                pool->getLastAddress(),
-                                opt_posix_timezone);
+    ASSERT_NO_THROW_LOG(cbptr_->createUpdateOption6(ServerSelector::ANY(),
+                                                    pool->getFirstAddress(),
+                                                    pool->getLastAddress(),
+                                                    opt_posix_timezone));
 
     // Query for a subnet.
     Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
@@ -4399,7 +4400,7 @@ GenericConfigBackendDHCPv6Test::getModifiedClientClasses6Test() {
     EXPECT_EQ(0, client_classes.getClasses()->size());
 
     // Getting modified client classes for any server is unsupported.
-    EXPECT_THROW(cbptr_->getModifiedClientClasses6(ServerSelector::ANY(),
+    ASSERT_THROW(cbptr_->getModifiedClientClasses6(ServerSelector::ANY(),
                                                    timestamps_["two days ago"]),
                  InvalidOperation);
 }
@@ -4635,7 +4636,7 @@ GenericConfigBackendDHCPv6Test::deleteAllClientClasses6Test() {
     EXPECT_EQ(0, result);
 
     // Deleting multiple objects using ANY server tag is unsupported.
-    EXPECT_THROW(cbptr_->deleteAllClientClasses6(ServerSelector::ANY()), InvalidOperation);
+    ASSERT_THROW(cbptr_->deleteAllClientClasses6(ServerSelector::ANY()), InvalidOperation);
 }
 
 void
@@ -4662,20 +4663,20 @@ GenericConfigBackendDHCPv6Test::clientClassDependencies6Test() {
 
     // An attempt to move the first class to the end of the class hierarchy should
     // fail because other classes depend on it.
-    EXPECT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, "bar"),
+    ASSERT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, "bar"),
                  DbOperationError);
 
     // Try to change the dependency of the first class. There are other classes
     // having indirect dependency on KNOWN class via this class. Therefore, the
     // update should be unsuccessful.
     class1->setTest("member('HA_server1')");
-    EXPECT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""),
+    ASSERT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class1, ""),
                  DbOperationError);
 
     // Try to change the dependency of the second class. This should result in
     // an error because the third class depends on it.
     class2->setTest("member('HA_server1')");
-    EXPECT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class2, ""),
+    ASSERT_THROW(cbptr_->createUpdateClientClass6(ServerSelector::ALL(), class2, ""),
                  DbOperationError);
 
     // Changing the indirect dependency of the third class should succeed, because
index 776e8b940da145e35098585bcaf243ddbc93f41a..f0b63e97d3b2240b028af727f2307eb05bbe9eae 100644 (file)
@@ -17,8 +17,8 @@
 namespace isc {
 namespace db {
 
-/// @brief Define PostgreSQL backend version: 10.0
-const uint32_t PGSQL_SCHEMA_VERSION_MAJOR = 10;
+/// @brief Define PostgreSQL backend version: 11.0
+const uint32_t PGSQL_SCHEMA_VERSION_MAJOR = 11;
 const uint32_t PGSQL_SCHEMA_VERSION_MINOR = 0;
 
 // Maximum number of parameters that can be used a statement
index e167a4c047b459e9a1932a86aea3fc3a65dc1e41..24ebb76b8d8d75046a8a0341c66a1730be48583c 100644 (file)
@@ -13,4 +13,5 @@
 /upgrade_007_to_008.sh
 /upgrade_008_to_009.sh
 /upgrade_009_to_010.sh
+/upgrade_010_to_011.sh
 /wipe_data.sh
index 071cba56054dc1bb452a3ee9c63203bbcfb9e67e..1205cd446d5de76465d1914547b57c7b3f2ab668 100644 (file)
@@ -24,6 +24,7 @@ pgsql_SCRIPTS += upgrade_006.2_to_007.0.sh
 pgsql_SCRIPTS += upgrade_007_to_008.sh
 pgsql_SCRIPTS += upgrade_008_to_009.sh
 pgsql_SCRIPTS += upgrade_009_to_010.sh
+pgsql_SCRIPTS += upgrade_010_to_011.sh
 pgsql_SCRIPTS += wipe_data.sh
 
 DISTCLEANFILES = ${pgsql_SCRIPTS}
index 6cb33ff1a72238288968d69ebd160ead755305fd..7ab2c6bcbc97ec47c24c50f8d6809c22568a4188 100644 (file)
@@ -4801,9 +4801,117 @@ END;
 $dhcp6_client_class_check_dependency_BINS$
 LANGUAGE plpgsql;
 
+-- Schema 10.0 specification ends here.
+
+-- This starts schema update to 11.0.
+
+-- Replace createOptionAuditDHCP6() with a version corrected
+-- where clause when scope is 6 (i.e. PD pool)
+--
+-- -----------------------------------------------------
+--
+-- Stored procedure which updates modification timestamp of
+-- a parent object when an option is modified.
+--
+-- The following parameters are passed to the procedure:
+-- - modification_type: "create", "update" or "delete"
+-- - scope_id: identifier of the option scope, e.g.
+--   global, subnet specific etc. See dhcp_option_scope
+--   for specific values.
+-- - option_id: identifier of the option.
+-- - p_subnet_id: identifier of the subnet if the option
+--   belongs to the subnet.
+-- - host_id: identifier of the host if the option
+-- - belongs to the host.
+-- - network_name: shared network name if the option
+--   belongs to the shared network.
+-- - pool_id: identifier of the pool if the option
+--   belongs to the pool.
+-- - pd_pool_id: identifier of the pool if the option
+--   belongs to the pd pool.
+-- - p_modification_ts: modification timestamp of the
+--   option.
+--   Some arguments are prefixed with "p_" to avoid ambiguity
+--   with column names in SQL statements. PostgreSQL does not
+--   allow table aliases to be used with column names in update
+--   set expressions.
+-- -----------------------------------------------------
+CREATE OR REPLACE FUNCTION createOptionAuditDHCP6(modification_type VARCHAR,
+                                                  scope_id SMALLINT,
+                                                  option_id INT,
+                                                  p_subnet_id BIGINT,
+                                                  host_id INT,
+                                                  network_name VARCHAR,
+                                                  pool_id BIGINT,
+                                                  pd_pool_id BIGINT,
+                                                  p_modification_ts TIMESTAMP WITH TIME ZONE)
+RETURNS VOID
+LANGUAGE plpgsql
+AS $$
+DECLARE
+    -- These variables will hold shared network id and subnet id that
+    -- we will select.
+    snid BIGINT;
+    sid BIGINT;
+    cascade_transaction BOOLEAN;
+BEGIN
+    -- Cascade transaction flag is set to true to prevent creation of
+    -- the audit entries for the options when the options are
+    -- created as part of the parent object creation or update.
+    -- For example: when the option is added as part of the subnet
+    -- addition, the cascade transaction flag is equal to true. If
+    -- the option is added into the existing subnet the cascade
+    -- transaction is equal to false. Note that depending on the option
+    -- scope the audit entry will contain the object_type value
+    -- of the parent object to cause the server to replace the
+    -- entire subnet. The only case when the object_type will be
+    -- set to 'dhcp6_options' is when a global option is added.
+    -- Global options do not have the owner.
+
+    cascade_transaction := get_session_boolean('kea.cascade_transaction');
+    IF cascade_transaction = false THEN
+        -- todo: host manager hasn't been updated to use audit
+        -- mechanisms so ignore host specific options for now.
+        IF scope_id = 0 THEN
+            -- If a global option is added or modified, create audit
+            -- entry for the 'dhcp6_options' table.
+            PERFORM createAuditEntryDHCP6('dhcp6_options', option_id, modification_type);
+        ELSEIF scope_id = 1 THEN
+            -- If subnet specific option is added or modified, update
+            -- the modification timestamp of this subnet to allow the
+            -- servers to refresh the subnet information. This will
+            -- also result in creating an audit entry for this subnet.
+            UPDATE dhcp6_subnet SET modification_ts = p_modification_ts
+                WHERE subnet_id = p_subnet_id;
+        ELSEIF scope_id = 4 THEN
+            -- If shared network specific option is added or modified,
+            -- update the modification timestamp of this shared network
+            -- to allow the servers to refresh the shared network
+            -- information. This will also result in creating an
+            -- audit entry for this shared network.
+           SELECT id INTO snid FROM dhcp6_shared_network WHERE name = network_name LIMIT 1;
+           UPDATE dhcp6_shared_network SET modification_ts = p_modification_ts
+                WHERE id = snid;
+        ELSEIF scope_id = 5 THEN
+            -- If pool specific option is added or modified, update
+            -- the modification timestamp of the owning subnet.
+            SELECT dhcp6_pool.subnet_id INTO sid FROM dhcp6_pool WHERE id = pool_id;
+            UPDATE dhcp6_subnet SET modification_ts = p_modification_ts
+                WHERE subnet_id = sid;
+        ELSEIF scope_id = 6 THEN
+            -- If pd pool specific option is added or modified, create
+            -- audit entry for the subnet which this pool belongs to.
+            SELECT dhcp6_pd_pool.subnet_id INTO sid FROM dhcp6_pd_pool WHERE id = pd_pool_id;
+            UPDATE dhcp6_subnet SET modification_ts = p_modification_ts
+                WHERE subnet_id = sid;
+        END IF;
+    END IF;
+    RETURN;
+END;$$;
+
 -- Update the schema version number.
 UPDATE schema_version
-    SET version = '10', minor = '0';
+    SET version = '11', minor = '0';
 
 -- Commit the script transaction.
 COMMIT;
diff --git a/src/share/database/scripts/pgsql/upgrade_010_to_011.sh.in b/src/share/database/scripts/pgsql/upgrade_010_to_011.sh.in
new file mode 100644 (file)
index 0000000..1e28c3b
--- /dev/null
@@ -0,0 +1,152 @@
+#!/bin/sh
+
+# Copyright (C) 2022 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/.
+
+# shellcheck disable=SC1091
+# SC1091: Not following: ... was not specified as input (see shellcheck -x).
+
+# Exit with error if commands exit with non-zero and if undefined variables are
+# used.
+set -eu
+
+# shellcheck disable=SC2034
+# SC2034: ... appears unused. Verify use (or export if used externally).
+prefix="@prefix@"
+
+# Include utilities. Use installed version if available and
+# use build version if it isn't.
+if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then
+    . "@datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh"
+else
+    . "@abs_top_builddir@/src/bin/admin/admin-utils.sh"
+fi
+
+VERSION=$(pgsql_version "$@")
+
+if [ "$VERSION" != "10.0" ]; then
+    printf 'This script upgrades 10.0 to 11.0. '
+    printf 'Reported version is %s. Skipping upgrade.\n' "${VERSION}"
+    exit 0
+fi
+
+psql "$@" >/dev/null <<EOF
+START TRANSACTION;
+
+-- This starts schema update to 11.0.
+
+-- Replace createOptionAuditDHCP6() with a version corrected
+-- where clause when scope is 6 (i.e. PD pool)
+-- 
+-- -----------------------------------------------------
+--
+-- Stored procedure which updates modification timestamp of
+-- a parent object when an option is modified.
+--
+-- The following parameters are passed to the procedure:
+-- - modification_type: "create", "update" or "delete"
+-- - scope_id: identifier of the option scope, e.g.
+--   global, subnet specific etc. See dhcp_option_scope
+--   for specific values.
+-- - option_id: identifier of the option.
+-- - p_subnet_id: identifier of the subnet if the option
+--   belongs to the subnet.
+-- - host_id: identifier of the host if the option
+-- - belongs to the host.
+-- - network_name: shared network name if the option
+--   belongs to the shared network.
+-- - pool_id: identifier of the pool if the option
+--   belongs to the pool.
+-- - pd_pool_id: identifier of the pool if the option
+--   belongs to the pd pool.
+-- - p_modification_ts: modification timestamp of the
+--   option.
+--   Some arguments are prefixed with "p_" to avoid ambiguity
+--   with column names in SQL statements. PostgreSQL does not
+--   allow table aliases to be used with column names in update
+--   set expressions.
+-- -----------------------------------------------------
+CREATE OR REPLACE FUNCTION createOptionAuditDHCP6(modification_type VARCHAR,
+                                                  scope_id SMALLINT,
+                                                  option_id INT,
+                                                  p_subnet_id BIGINT,
+                                                  host_id INT,
+                                                  network_name VARCHAR,
+                                                  pool_id BIGINT,
+                                                  pd_pool_id BIGINT,
+                                                  p_modification_ts TIMESTAMP WITH TIME ZONE)
+RETURNS VOID
+LANGUAGE plpgsql
+AS \$\$
+DECLARE
+    -- These variables will hold shared network id and subnet id that
+    -- we will select.
+    snid BIGINT;
+    sid BIGINT;
+    cascade_transaction BOOLEAN;
+BEGIN
+    -- Cascade transaction flag is set to true to prevent creation of
+    -- the audit entries for the options when the options are
+    -- created as part of the parent object creation or update.
+    -- For example: when the option is added as part of the subnet
+    -- addition, the cascade transaction flag is equal to true. If
+    -- the option is added into the existing subnet the cascade
+    -- transaction is equal to false. Note that depending on the option
+    -- scope the audit entry will contain the object_type value
+    -- of the parent object to cause the server to replace the
+    -- entire subnet. The only case when the object_type will be
+    -- set to 'dhcp6_options' is when a global option is added.
+    -- Global options do not have the owner.
+
+    cascade_transaction := get_session_boolean('kea.cascade_transaction');
+    IF cascade_transaction = false THEN
+        -- todo: host manager hasn't been updated to use audit
+        -- mechanisms so ignore host specific options for now.
+        IF scope_id = 0 THEN
+            -- If a global option is added or modified, create audit
+            -- entry for the 'dhcp6_options' table.
+            PERFORM createAuditEntryDHCP6('dhcp6_options', option_id, modification_type);
+        ELSEIF scope_id = 1 THEN
+            -- If subnet specific option is added or modified, update
+            -- the modification timestamp of this subnet to allow the
+            -- servers to refresh the subnet information. This will
+            -- also result in creating an audit entry for this subnet.
+            UPDATE dhcp6_subnet SET modification_ts = p_modification_ts
+                WHERE subnet_id = p_subnet_id;
+        ELSEIF scope_id = 4 THEN
+            -- If shared network specific option is added or modified,
+            -- update the modification timestamp of this shared network
+            -- to allow the servers to refresh the shared network
+            -- information. This will also result in creating an
+            -- audit entry for this shared network.
+           SELECT id INTO snid FROM dhcp6_shared_network WHERE name = network_name LIMIT 1;
+           UPDATE dhcp6_shared_network SET modification_ts = p_modification_ts
+                WHERE id = snid;
+        ELSEIF scope_id = 5 THEN
+            -- If pool specific option is added or modified, update
+            -- the modification timestamp of the owning subnet.
+            SELECT dhcp6_pool.subnet_id INTO sid FROM dhcp6_pool WHERE id = pool_id;
+            UPDATE dhcp6_subnet SET modification_ts = p_modification_ts
+                WHERE subnet_id = sid;
+        ELSEIF scope_id = 6 THEN
+            -- If pd pool specific option is added or modified, create
+            -- audit entry for the subnet which this pool belongs to.
+            SELECT dhcp6_pd_pool.subnet_id INTO sid FROM dhcp6_pd_pool WHERE id = pd_pool_id;
+            UPDATE dhcp6_subnet SET modification_ts = p_modification_ts
+                WHERE subnet_id = sid;
+        END IF;
+    END IF;
+    RETURN;
+END;\$\$;
+
+-- Update the schema version number.
+UPDATE schema_version
+    SET version = '11', minor = '0';
+
+-- Commit the script transaction.
+COMMIT;
+
+EOF