]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#716,!412] Added support for associating networks with servers.
authorMarcin Siodelski <marcin@isc.org>
Sat, 6 Jul 2019 17:42:34 +0000 (19:42 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 9 Jul 2019 18:37:28 +0000 (14:37 -0400)
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc
src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc
src/hooks/dhcp/mysql_cb/mysql_cb_impl.h
src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc
src/lib/database/server_selector.h
src/lib/database/tests/server_selector_unittest.cc

index 201ab51fc0662a8712b61518deb7de928afce4a3..9c8ce4e9bbe2ec02ebaf5ad7e32cdf428f66af53 100644 (file)
@@ -66,7 +66,8 @@ public:
         GET_MODIFIED_SUBNETS4,
         GET_SHARED_NETWORK_SUBNETS4,
         GET_POOL4_RANGE,
-        GET_SHARED_NETWORK4_NAME,
+        GET_SHARED_NETWORK4_NAME_WITH_TAG,
+        GET_SHARED_NETWORK4_NAME_NO_TAG,
         GET_ALL_SHARED_NETWORKS4,
         GET_MODIFIED_SHARED_NETWORKS4,
         GET_OPTION_DEF4_CODE_SPACE,
@@ -112,6 +113,7 @@ public:
         DELETE_POOLS4_SUBNET_ID,
         DELETE_SHARED_NETWORK4_NAME,
         DELETE_ALL_SHARED_NETWORKS4,
+        DELETE_SHARED_NETWORK4_SERVER,
         DELETE_OPTION_DEF4_CODE_NAME,
         DELETE_ALL_OPTION_DEFS4,
         DELETE_ALL_OPTION_DEFS4_UNASSIGNED,
@@ -122,6 +124,7 @@ public:
         DELETE_OPTION4_SHARED_NETWORK,
         DELETE_OPTIONS4_SUBNET_ID,
         DELETE_OPTIONS4_SHARED_NETWORK,
+        DELETE_OPTION4_SERVER,
         DELETE_SERVER4,
         DELETE_ALL_SERVERS4,
         NUM_STATEMENTS
@@ -201,24 +204,15 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4,
                               in_bindings);
 
-            // Successfully inserted global parameter. Now, we have to associate it
-            // with the server tag.
-
             // Let's first get the primary key of the global parameter.
             uint64_t id = mysql_insert_id(conn_.mysql_);
 
-            // Create bindings for inserting the association into
-            // dhcp4_global_parameter_server table.
-            MySqlBindingCollection in_server_bindings = {
-                MySqlBinding::createInteger<uint64_t>(id), // parameter_id
-                MySqlBinding::createString(tag), // tag used to obtain server_id
-                MySqlBinding::createTimestamp(value->getModificationTime()), // modification_ts
-            };
-
-            // Insert association.
-            conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4_SERVER,
-                              in_server_bindings);
-
+            // Successfully inserted global parameter. Now, we have to associate it
+            // with the server tag.
+            attachElementToServers(MySqlConfigBackendDHCPv4Impl::INSERT_GLOBAL_PARAMETER4_SERVER,
+                                   server_selector,
+                                   MySqlBinding::createInteger<uint64_t>(id),
+                                   MySqlBinding::createTimestamp(value->getModificationTime()));
         }
 
         transaction.commit();
@@ -858,21 +852,15 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4,
                               in_bindings);
 
-            // Create bindings for inserting the association into
-            // dhcp4_subnet_server table.
-            MySqlBindingCollection in_server_bindings = {
-                MySqlBinding::createInteger<uint32_t>(subnet->getID()), // subnet_id
-                MySqlBinding::createString(tag), // tag used to obtain server_id
-                MySqlBinding::createTimestamp(subnet->getModificationTime()), // modification_ts
-            };
-
-            // Insert association.
-            conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4_SERVER,
-                              in_server_bindings);
+            // Insert associations with the servers.
+            attachElementToServers(MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4_SERVER,
+                                   server_selector,
+                                   MySqlBinding::createInteger<uint32_t>(subnet->getID()),
+                                   MySqlBinding::createTimestamp(subnet->getModificationTime()));
 
         } catch (const DuplicateEntry&) {
             deletePools4(subnet);
-            deleteOptions4(server_selector, subnet);
+            deleteOptions4(ServerSelector::ANY(), subnet);
 
             // Need to add two more bindings for WHERE clause.
             in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(subnet->getID()));
@@ -1057,9 +1045,11 @@ public:
 
         uint64_t last_network_id = 0;
         uint64_t last_option_id = 0;
+        std::string last_tag;
 
         conn_.selectQuery(index, in_bindings, out_bindings,
-                          [this, &shared_networks, &last_network_id, &last_option_id]
+                          [this, &shared_networks, &last_network_id, &last_option_id,
+                           &last_tag]
                           (MySqlBindingCollection& out_bindings) {
             SharedNetwork4Ptr last_network;
             if (!shared_networks.empty()) {
@@ -1071,6 +1061,10 @@ public:
             // row to create the new shared network instance.
             if (last_network_id != out_bindings[0]->getInteger<uint64_t>()) {
 
+                // Reset last server tag as we're now starting to process new
+                // shared network.
+                last_tag.clear();
+
                 last_network_id = out_bindings[0]->getInteger<uint64_t>();
                 last_network = SharedNetwork4::create(out_bindings[1]->getString());
                 last_network->setId(last_network_id);
@@ -1192,9 +1186,6 @@ public:
 
                 // {min,max}_valid_lifetime
 
-                // server_tag
-                last_network->setServerTag(out_bindings[34]->getString());
-
                 // Add the shared network.
                 auto ret = shared_networks.push_back(last_network);
 
@@ -1206,6 +1197,15 @@ public:
                 }
             }
 
+            // Check for new server tags.
+            if (!out_bindings[34]->amNull() &&
+                (last_tag != out_bindings[34]->getString())) {
+                last_tag = out_bindings[34]->getString();
+                if (!last_tag.empty() && !last_network->hasServerTag(ServerTag(last_tag))) {
+                    last_network->setServerTag(last_tag);
+                }
+            }
+
             // Parse option.
             if (!out_bindings[13]->amNull() &&
                 (last_option_id < out_bindings[13]->getInteger<uint64_t>())) {
@@ -1234,15 +1234,20 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "fetching shared network");
+        MySqlBindingCollection in_bindings;
+        auto index = GET_SHARED_NETWORK4_NAME_NO_TAG;
 
-        MySqlBindingCollection in_bindings = {
-            MySqlBinding::createString(tag),
-            MySqlBinding::createString(name)
-        };
+        if (!server_selector.amAny()) {
+            auto tag = getServerTag(server_selector, "fetching shared network");
+            in_bindings.push_back(MySqlBinding::createString(tag));
+
+            index = GET_SHARED_NETWORK4_NAME_WITH_TAG;
+        }
+
+        in_bindings.push_back(MySqlBinding::createString(name));
 
         SharedNetwork4Collection shared_networks;
-        getSharedNetworks4(GET_SHARED_NETWORK4_NAME, in_bindings, shared_networks);
+        getSharedNetworks4(index, in_bindings, shared_networks);
 
         return (shared_networks.empty() ? SharedNetwork4Ptr() : *shared_networks.begin());
     }
@@ -1299,8 +1304,6 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "creating or updating shared network");
-
         // Create binding for host reservation mode.
         MySqlBindingPtr hr_mode_binding;
         auto hr_mode = shared_network->getHostReservationMode(Network::Inheritance::NONE);
@@ -1352,28 +1355,27 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SHARED_NETWORK4,
                               in_bindings);
 
-            // Create bindings for inserting association into dhcp4_shared_network_server
-            // table.
-            MySqlBindingCollection in_server_bindings = {
-                MySqlBinding::createString(shared_network->getName()), // shared network name
-                MySqlBinding::createString(tag), // server tag
-                MySqlBinding::createTimestamp(shared_network->getModificationTime()), // modification_ts
-            };
-
-            // Insert association.
-            conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SHARED_NETWORK4_SERVER,
-                              in_server_bindings);
-
-
         } catch (const DuplicateEntry&) {
-            deleteOptions4(server_selector, shared_network);
+            deleteOptions4(ServerSelector::ANY(), shared_network);
 
             // Need to add one more binding for WHERE clause.
             in_bindings.push_back(MySqlBinding::createString(shared_network->getName()));
             conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_SHARED_NETWORK4,
                                     in_bindings);
+
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createString(shared_network->getName())
+            };
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::DELETE_SHARED_NETWORK4_SERVER,
+                                    in_server_bindings);
         }
 
+        // Associate the shared network with the servers.
+        attachElementToServers(MySqlConfigBackendDHCPv4Impl::INSERT_SHARED_NETWORK4_SERVER,
+                               server_selector,
+                               MySqlBinding::createString(shared_network->getName()),
+                               MySqlBinding::createTimestamp(shared_network->getModificationTime()));
+
         // (Re)create options.
         auto option_spaces = shared_network->getCfgOption()->getOptionSpaceNames();
         for (auto option_space : option_spaces) {
@@ -1402,19 +1404,11 @@ public:
         conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_OPTION4,
                           in_bindings);
 
-        // Fetch unique identifier of the inserted option.
-        uint64_t id = mysql_insert_id(conn_.mysql_);
-
-        // Create bindings needed to insert association of that option with
-        // a server into the dhcp4_options_server table.
-        MySqlBindingCollection in_server_bindings = {
-            MySqlBinding::createInteger<uint64_t>(id), // option_id
-            MySqlBinding::createString(server_selector.getTags().begin()->get()), // server_tag
-            in_bindings[11] // copy modification timestamp from option
-        };
-
-        conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_OPTION4_SERVER,
-                          in_server_bindings);
+        // Associate the option with the servers.
+        attachElementToServers(MySqlConfigBackendDHCPv4Impl::INSERT_OPTION4_SERVER,
+                               server_selector,
+                               MySqlBinding::createInteger<uint64_t>(mysql_insert_id(conn_.mysql_)),
+                               in_bindings[11]);
     }
 
     /// @brief Sends query to insert or update global DHCP option.
@@ -1635,9 +1629,6 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "creating or updating shared"
-                                " network level option");
-
         MySqlBindingCollection in_bindings = {
             MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
             createOptionValueBinding(option),
@@ -1651,7 +1642,6 @@ public:
             MySqlBinding::createString(shared_network_name),
             MySqlBinding::createNull(),
             MySqlBinding::createTimestamp(option->getModificationTime()),
-            MySqlBinding::createString(tag),
             MySqlBinding::createString(shared_network_name),
             MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
             MySqlBinding::condCreateString(option->space_name_)
@@ -1676,8 +1666,8 @@ public:
         if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::
                                     UPDATE_OPTION4_SHARED_NETWORK,
                                     in_bindings) == 0) {
-            // Remove the 4 bindings used only in case of update.
-            in_bindings.resize(in_bindings.size() - 4);
+            // Remove the 3 bindings used only in case of update.
+            in_bindings.resize(in_bindings.size() - 3);
             insertOption4(server_selector, in_bindings);
         }
 
@@ -2040,19 +2030,24 @@ TaggedStatementArray tagged_statements = { {
       "ORDER BY p.id, x.option_id"
     },
 
-    // Select shared network by name.
-    { MySqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK4_NAME,
-      MYSQL_GET_SHARED_NETWORK4(AND n.name = ?)
+    // Select shared network by name and filter by server tag.
+    { MySqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK4_NAME_WITH_TAG,
+      MYSQL_GET_SHARED_NETWORK4_WITH_TAG(AND n.name = ?)
+    },
+
+    // Select shared network by name without filtering by server tag.
+    { MySqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK4_NAME_NO_TAG,
+      MYSQL_GET_SHARED_NETWORK4_NO_TAG(WHERE n.name = ?)
     },
 
     // Select all shared networks.
     { MySqlConfigBackendDHCPv4Impl::GET_ALL_SHARED_NETWORKS4,
-      MYSQL_GET_SHARED_NETWORK4()
+      MYSQL_GET_SHARED_NETWORK4_WITH_TAG()
     },
 
     // Select modified shared networks.
     { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_SHARED_NETWORKS4,
-      MYSQL_GET_SHARED_NETWORK4(AND n.modification_ts > ?)
+      MYSQL_GET_SHARED_NETWORK4_WITH_TAG(AND n.modification_ts > ?)
     },
 
     // Retrieves option definition by code and space.
@@ -2293,22 +2288,22 @@ TaggedStatementArray tagged_statements = { {
 
     // Update existing global option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4,
-      MYSQL_UPDATE_OPTION4(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
     },
 
     // Update existing subnet level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
-      MYSQL_UPDATE_OPTION4(AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing pool level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_POOL_ID,
-      MYSQL_UPDATE_OPTION4(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing shared network level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SHARED_NETWORK,
-      MYSQL_UPDATE_OPTION4(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing server, e.g. server description.
@@ -2366,6 +2361,11 @@ TaggedStatementArray tagged_statements = { {
       MYSQL_DELETE_SHARED_NETWORK(dhcp4)
     },
 
+    // Delete associations of a shared network with server.
+    { MySqlConfigBackendDHCPv4Impl::DELETE_SHARED_NETWORK4_SERVER,
+      MYSQL_DELETE_SHARED_NETWORK_SERVER(dhcp4)
+    },
+
     // Delete option definition.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION_DEF4_CODE_NAME,
       MYSQL_DELETE_OPTION_DEF(dhcp4, AND code = ? AND space = ?)
@@ -2383,7 +2383,7 @@ TaggedStatementArray tagged_statements = { {
 
     // Delete single global option.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4,
-      MYSQL_DELETE_OPTION(dhcp4, AND o.scope_id = 0  AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_WITH_TAG(dhcp4, AND o.scope_id = 0  AND o.code = ? AND o.space = ?)
     },
 
     // Delete all global options which are unassigned to any servers.
@@ -2393,29 +2393,34 @@ TaggedStatementArray tagged_statements = { {
 
     // Delete single option from a subnet.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SUBNET_ID,
-      MYSQL_DELETE_OPTION(dhcp4,
-                          AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp4,
+                          WHERE o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Delete single option from a pool.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_POOL_RANGE,
-      MYSQL_DELETE_OPTION_POOL_RANGE(dhcp4, AND o.scope_id = 5 AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_POOL_RANGE(dhcp4, o.scope_id = 5 AND o.code = ? AND o.space = ?)
     },
 
     // Delete single option from a shared network.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SHARED_NETWORK,
-      MYSQL_DELETE_OPTION(dhcp4,
-                          AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp4,
+                          WHERE o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
     },
 
     // Delete options belonging to a subnet.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTIONS4_SUBNET_ID,
-      MYSQL_DELETE_OPTION(dhcp4, AND o.scope_id = 1 AND o.dhcp4_subnet_id = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp4, WHERE o.scope_id = 1 AND o.dhcp4_subnet_id = ?)
     },
 
     // Delete options belonging to a shared_network.
     { MySqlConfigBackendDHCPv4Impl::DELETE_OPTIONS4_SHARED_NETWORK,
-      MYSQL_DELETE_OPTION(dhcp4, AND o.scope_id = 4 AND o.shared_network_name = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp4, WHERE o.scope_id = 4 AND o.shared_network_name = ?)
+    },
+
+    // Delete associations of an option with the server.
+    { MySqlConfigBackendDHCPv4Impl::DELETE_OPTION4_SERVER,
+      MYSQL_DELETE_OPTION_SERVER(dhcp4)
     },
 
     // Delete a server by tag.
@@ -2856,13 +2861,16 @@ MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
 }
 
 uint64_t
-MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
+MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
                                         const std::string& shared_network_name,
                                         const uint16_t code,
                                         const std::string& space) {
+    /// @todo In the future we might use the server selector to make sure that the
+    /// option is only deleted if the pool belongs to a given server. For now, we
+    /// just delete it when there is a match with the parent object.
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_SHARED_NETWORK_OPTION4)
         .arg(shared_network_name).arg(code).arg(space);
-    uint64_t result = impl_->deleteOption4(server_selector, shared_network_name,
+    uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), shared_network_name,
                                            code, space);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_SHARED_NETWORK_OPTION4_RESULT)
         .arg(result);
@@ -2870,28 +2878,34 @@ MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
 }
 
 uint64_t
-MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
+MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
                                         const SubnetID& subnet_id,
                                         const uint16_t code,
                                         const std::string& space) {
+    /// @todo In the future we might use the server selector to make sure that the
+    /// option is only deleted if the pool belongs to a given server. For now, we
+    /// just delete it when there is a match with the parent object.
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_SUBNET_ID_OPTION4)
         .arg(subnet_id).arg(code).arg(space);
-    uint64_t result = impl_->deleteOption4(server_selector, subnet_id, code, space);
+    uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), subnet_id, code, space);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_SUBNET_ID_OPTION4_RESULT)
         .arg(result);
     return (result);
 }
 
 uint64_t
-MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& server_selector,
+MySqlConfigBackendDHCPv4::deleteOption4(const ServerSelector& /* server_selector */,
                                         const asiolink::IOAddress& pool_start_address,
                                         const asiolink::IOAddress& pool_end_address,
                                         const uint16_t code,
                                         const std::string& space) {
+    /// @todo In the future we might use the server selector to make sure that the
+    /// option is only deleted if the pool belongs to a given server. For now, we
+    /// just delete it when there is a match with the parent object.
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_OPTION4)
         .arg(pool_start_address.toText()).arg(pool_end_address.toText()).arg(code).arg(space);
-    uint64_t result = impl_->deleteOption4(server_selector, pool_start_address, pool_end_address,
-                                           code, space);
+    uint64_t result = impl_->deleteOption4(ServerSelector::ANY(), pool_start_address,
+                                           pool_end_address, code, space);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_OPTION4_RESULT)
         .arg(result);
     return (result);
index 0f1ce65424032b0d64561e7692fcf2913ffef83f..00f82bc16f918e0481b8b6edb003d96cad0951db 100644 (file)
@@ -68,7 +68,8 @@ public:
         GET_SHARED_NETWORK_SUBNETS6,
         GET_POOL6_RANGE,
         GET_PD_POOL,
-        GET_SHARED_NETWORK6_NAME,
+        GET_SHARED_NETWORK6_NAME_WITH_TAG,
+        GET_SHARED_NETWORK6_NAME_NO_TAG,
         GET_ALL_SHARED_NETWORKS6,
         GET_MODIFIED_SHARED_NETWORKS6,
         GET_OPTION_DEF6_CODE_SPACE,
@@ -118,6 +119,7 @@ public:
         DELETE_PD_POOLS_SUBNET_ID,
         DELETE_SHARED_NETWORK6_NAME,
         DELETE_ALL_SHARED_NETWORKS6,
+        DELETE_SHARED_NETWORK6_SERVER,
         DELETE_OPTION_DEF6_CODE_NAME,
         DELETE_ALL_OPTION_DEFS6,
         DELETE_ALL_OPTION_DEFS6_UNASSIGNED,
@@ -214,18 +216,12 @@ public:
             // Let's first get the primary key of the global parameter.
             uint64_t id = mysql_insert_id(conn_.mysql_);
 
-            // Create bindings for inserting the association into
-            // dhcp6_global_parameter_server table.
-            MySqlBindingCollection in_server_bindings = {
-                MySqlBinding::createInteger<uint64_t>(id), // parameter_id
-                MySqlBinding::createString(tag), // tag used to obtain server_id
-                MySqlBinding::createTimestamp(value->getModificationTime()), // modification_ts
-            };
-
-            // Insert association.
-            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6_SERVER,
-                              in_server_bindings);
-
+            // Successfully inserted global parameter. Now, we have to associate it
+            // with the server tag.
+            attachElementToServers(MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6_SERVER,
+                                   server_selector,
+                                   MySqlBinding::createInteger<uint64_t>(id),
+                                   MySqlBinding::createTimestamp(value->getModificationTime()));
         }
 
         transaction.commit();
@@ -1003,22 +999,16 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6,
                               in_bindings);
 
-            // Create bindings for inserting the association into
-            // dhcp6_subnet_server table.
-            MySqlBindingCollection in_server_bindings = {
-                MySqlBinding::createInteger<uint32_t>(subnet->getID()), // subnet_id
-                MySqlBinding::createString(tag), // tag used to obtain server_id
-                MySqlBinding::createTimestamp(subnet->getModificationTime()), // modification_ts
-            };
-
-            // Insert association.
-            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER,
-                              in_server_bindings);
+            // Insert associations with the servers.
+            attachElementToServers(MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER,
+                                   server_selector,
+                                   MySqlBinding::createInteger<uint32_t>(subnet->getID()),
+                                   MySqlBinding::createTimestamp(subnet->getModificationTime()));
 
         } catch (const DuplicateEntry&) {
             deletePools6(subnet);
             deletePdPools6(subnet);
-            deleteOptions6(server_selector, subnet);
+            deleteOptions6(ServerSelector::ANY(), subnet);
 
             // Need to add two more bindings for WHERE clause.
             in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(subnet->getID()));
@@ -1258,9 +1248,11 @@ public:
 
         uint64_t last_network_id = 0;
         uint64_t last_option_id = 0;
+        std::string last_tag;
 
         conn_.selectQuery(index, in_bindings, out_bindings,
-                          [this, &shared_networks, &last_network_id, &last_option_id]
+                          [this, &shared_networks, &last_network_id, &last_option_id,
+                           &last_tag]
                           (MySqlBindingCollection& out_bindings) {
             SharedNetwork6Ptr last_network;
             if (!shared_networks.empty()) {
@@ -1272,6 +1264,10 @@ public:
             // row to create the new shared network instance.
             if (last_network_id != out_bindings[0]->getInteger<uint64_t>()) {
 
+                // Reset last server tag as we're now starting to process new
+                // shared network.
+                last_tag.clear();
+
                 last_network_id = out_bindings[0]->getInteger<uint64_t>();
                 last_network = SharedNetwork6::create(out_bindings[1]->getString());
                 last_network->setId(last_network_id);
@@ -1392,9 +1388,6 @@ public:
 
                 // {min,max)_valid_lifetime
 
-                // server_tag
-                last_network->setServerTag(out_bindings[35]->getString());
-
                 // Add the shared network.
                 auto ret = shared_networks.push_back(last_network);
 
@@ -1416,6 +1409,15 @@ public:
                     last_network->getCfgOption()->add(*desc, desc->space_name_);
                 }
             }
+
+            // Check for new server tags.
+            if (!out_bindings[35]->amNull() &&
+                (last_tag != out_bindings[35]->getString())) {
+                last_tag = out_bindings[35]->getString();
+                if (!last_tag.empty() && !last_network->hasServerTag(ServerTag(last_tag))) {
+                    last_network->setServerTag(last_tag);
+                }
+            }
         });
     }
 
@@ -1434,15 +1436,20 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "fetching shared network");
+        MySqlBindingCollection in_bindings;
+        auto index = GET_SHARED_NETWORK6_NAME_NO_TAG;
 
-        MySqlBindingCollection in_bindings = {
-            MySqlBinding::createString(tag),
-            MySqlBinding::createString(name)
-        };
+        if (!server_selector.amAny()) {
+            auto tag = getServerTag(server_selector, "fetching shared network");
+            in_bindings.push_back(MySqlBinding::createString(tag));
+
+            index = GET_SHARED_NETWORK6_NAME_WITH_TAG;
+        }
+
+        in_bindings.push_back(MySqlBinding::createString(name));
 
         SharedNetwork6Collection shared_networks;
-        getSharedNetworks6(GET_SHARED_NETWORK6_NAME, in_bindings, shared_networks);
+        getSharedNetworks6(index, in_bindings, shared_networks);
 
         return (shared_networks.empty() ? SharedNetwork6Ptr() : *shared_networks.begin());
     }
@@ -1499,8 +1506,6 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "creating or updating shared network");
-
         // Create binding for host reservation mode.
         MySqlBindingPtr hr_mode_binding;
         auto hr_mode = shared_network->getHostReservationMode(Network::Inheritance::NONE);
@@ -1563,28 +1568,27 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6,
                               in_bindings);
 
-            // Create bindings for inserting association into dhcp6_shared_network_server
-            // table.
-            MySqlBindingCollection in_server_bindings = {
-                MySqlBinding::createString(shared_network->getName()), // shared network name
-                MySqlBinding::createString(tag), // server tag
-                MySqlBinding::createTimestamp(shared_network->getModificationTime()), // modification_ts
-            };
-
-            // Insert association.
-            conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER,
-                              in_server_bindings);
-
-
         } catch (const DuplicateEntry&) {
-            deleteOptions6(server_selector, shared_network);
+            deleteOptions6(ServerSelector::ANY(), shared_network);
 
             // Need to add one more binding for WHERE clause.
             in_bindings.push_back(MySqlBinding::createString(shared_network->getName()));
             conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_SHARED_NETWORK6,
                                     in_bindings);
+
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createString(shared_network->getName())
+            };
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_SERVER,
+                                    in_server_bindings);
+
         }
 
+        attachElementToServers(MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER,
+                               server_selector,
+                               MySqlBinding::createString(shared_network->getName()),
+                               MySqlBinding::createTimestamp(shared_network->getModificationTime()));
+
         // (Re)create options.
         auto option_spaces = shared_network->getCfgOption()->getOptionSpaceNames();
         for (auto option_space : option_spaces) {
@@ -1613,19 +1617,11 @@ public:
         conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6,
                           in_bindings);
 
-        // Fetch unique identifier of the inserted option.
-        uint64_t id = mysql_insert_id(conn_.mysql_);
-
-        // Create bindings needed to insert association of that option with
-        // a server into the dhcp6_options_server table.
-        MySqlBindingCollection in_server_bindings = {
-            MySqlBinding::createInteger<uint64_t>(id), // option_id
-            MySqlBinding::createString(server_selector.getTags().begin()->get()), // server_tag
-            in_bindings[11] // copy modification timestamp from option
-        };
-
-        conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER,
-                          in_server_bindings);
+        // Associate the option with the servers.
+        attachElementToServers(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER,
+                               server_selector,
+                               MySqlBinding::createInteger<uint64_t>(mysql_insert_id(conn_.mysql_)),
+                               in_bindings[11]);
     }
 
     /// @brief Sends query to insert or update global DHCP option.
@@ -1920,9 +1916,6 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "creating or updating shared"
-                                " network level option");
-
         MySqlBindingCollection in_bindings = {
             MySqlBinding::createInteger<uint16_t>(option->option_->getType()),
             createOptionValueBinding(option),
@@ -1937,7 +1930,6 @@ public:
             MySqlBinding::createNull(),
             MySqlBinding::createTimestamp(option->getModificationTime()),
             MySqlBinding::createNull(),
-            MySqlBinding::createString(tag),
             MySqlBinding::createString(shared_network_name),
             MySqlBinding::createInteger<uint16_t>(option->option_->getType()),
             MySqlBinding::condCreateString(option->space_name_)
@@ -1962,8 +1954,8 @@ public:
         if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::
                                     UPDATE_OPTION6_SHARED_NETWORK,
                                     in_bindings) == 0) {
-            // Remove the 4 bindings used only in case of update.
-            in_bindings.resize(in_bindings.size() - 4);
+            // Remove the 3 bindings used only in case of update.
+            in_bindings.resize(in_bindings.size() - 3);
             insertOption6(server_selector, in_bindings);
         }
 
@@ -2390,18 +2382,23 @@ TaggedStatementArray tagged_statements = { {
     },
 
     // Select shared network by name.
-    { MySqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME,
-      MYSQL_GET_SHARED_NETWORK6(AND n.name = ?)
+    { MySqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_WITH_TAG,
+      MYSQL_GET_SHARED_NETWORK6_WITH_TAG(AND n.name = ?)
+    },
+
+    // Select shared network by name without filtering by server tag.
+    { MySqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME_NO_TAG,
+      MYSQL_GET_SHARED_NETWORK6_NO_TAG(WHERE n.name = ?)
     },
 
     // Select all shared networks.
     { MySqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6,
-      MYSQL_GET_SHARED_NETWORK6()
+      MYSQL_GET_SHARED_NETWORK6_WITH_TAG()
     },
 
     // Select modified shared networks.
     { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6,
-      MYSQL_GET_SHARED_NETWORK6(AND n.modification_ts > ?)
+      MYSQL_GET_SHARED_NETWORK6_WITH_TAG(AND n.modification_ts > ?)
     },
 
     // Retrieves option definition by code and space.
@@ -2646,27 +2643,27 @@ TaggedStatementArray tagged_statements = { {
 
     // Update existing global option.
     { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6,
-      MYSQL_UPDATE_OPTION6(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION6_WITH_TAG(AND o.scope_id = 0 AND o.code = ? AND o.space = ?)
     },
 
     // Update existing subnet level option.
     { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID,
-      MYSQL_UPDATE_OPTION6(AND o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION6_WITH_TAG(AND o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing pool level option.
     { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID,
-      MYSQL_UPDATE_OPTION6(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION6_WITH_TAG(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing pd pool level option.
     { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID,
-      MYSQL_UPDATE_OPTION6(AND o.scope_id = 6 AND o.pd_pool_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION6_WITH_TAG(AND o.scope_id = 6 AND o.pd_pool_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing shared network level option.
     { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SHARED_NETWORK,
-      MYSQL_UPDATE_OPTION6(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION6_NO_TAG(o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing server, e.g. server description.
@@ -2724,6 +2721,11 @@ TaggedStatementArray tagged_statements = { {
       MYSQL_DELETE_SHARED_NETWORK(dhcp6)
     },
 
+    // Delete associations of a shared network with server.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_SERVER,
+      MYSQL_DELETE_SHARED_NETWORK_SERVER(dhcp6)
+    },
+
     // Delete option definition.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION_DEF6_CODE_NAME,
       MYSQL_DELETE_OPTION_DEF(dhcp6, AND code = ? AND space = ?)
@@ -2741,7 +2743,7 @@ TaggedStatementArray tagged_statements = { {
 
     // Delete single global option.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6,
-      MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 0  AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_WITH_TAG(dhcp6, AND o.scope_id = 0  AND o.code = ? AND o.space = ?)
     },
 
     // Delete all global options which are unassigned to any servers.
@@ -2751,34 +2753,34 @@ TaggedStatementArray tagged_statements = { {
 
     // Delete single option from a subnet.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SUBNET_ID,
-      MYSQL_DELETE_OPTION(dhcp6,
-                          AND o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp6,
+                          WHERE o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Delete single option from a pool.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_POOL_RANGE,
-      MYSQL_DELETE_OPTION_POOL_RANGE(dhcp6, AND o.scope_id = 5 AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_POOL_RANGE(dhcp6, o.scope_id = 5 AND o.code = ? AND o.space = ?)
     },
 
     // Delete single option from a pd pool.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_PD_POOL,
-      MYSQL_DELETE_OPTION_PD_POOL(AND o.scope_id = 6 AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_PD_POOL(o.scope_id = 6 AND o.code = ? AND o.space = ?)
     },
 
     // Delete single option from a shared network.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6_SHARED_NETWORK,
-      MYSQL_DELETE_OPTION(dhcp6,
-                          AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp6,
+                          WHERE o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?)
     },
 
     // Delete options belonging to a subnet.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_SUBNET_ID,
-      MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 1 AND o.dhcp6_subnet_id = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp6, WHERE o.scope_id = 1 AND o.dhcp6_subnet_id = ?)
     },
 
     // Delete options belonging to a shared_network.
     { MySqlConfigBackendDHCPv6Impl::DELETE_OPTIONS6_SHARED_NETWORK,
-      MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 4 AND o.shared_network_name = ?)
+      MYSQL_DELETE_OPTION_NO_TAG(dhcp6, WHERE o.scope_id = 4 AND o.shared_network_name = ?)
     },
 
     // Delete a server by tag.
@@ -3234,9 +3236,12 @@ MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
                                         const std::string& shared_network_name,
                                         const uint16_t code,
                                         const std::string& space) {
+    /// @todo In the future we might use the server selector to make sure that the
+    /// option is only deleted if the pool belongs to a given server. For now, we
+    /// just delete it when there is a match with the parent object.
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_SHARED_NETWORK_OPTION6)
         .arg(shared_network_name).arg(code).arg(space);
-    uint64_t result = impl_->deleteOption6(server_selector, shared_network_name,
+    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), shared_network_name,
                                            code, space);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_SHARED_NETWORK_OPTION6_RESULT)
         .arg(result);
@@ -3244,27 +3249,33 @@ MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
 }
 
 uint64_t
-MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& /* server_selector */,
                                         const SubnetID& subnet_id,
                                         const uint16_t code,
                                         const std::string& space) {
+    /// @todo In the future we might use the server selector to make sure that the
+    /// option is only deleted if the pool belongs to a given server. For now, we
+    /// just delete it when there is a match with the parent object.
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_SUBNET_ID_OPTION6)
         .arg(subnet_id).arg(code).arg(space);
-    uint64_t result = impl_->deleteOption6(server_selector, subnet_id, code, space);
+    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), subnet_id, code, space);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_SUBNET_ID_OPTION6_RESULT)
         .arg(result);
     return (result);
 }
 
 uint64_t
-MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& /* server_selector */,
                                         const asiolink::IOAddress& pool_start_address,
                                         const asiolink::IOAddress& pool_end_address,
                                         const uint16_t code,
                                         const std::string& space) {
+    /// @todo In the future we might use the server selector to make sure that the
+    /// option is only deleted if the pool belongs to a given server. For now, we
+    /// just delete it when there is a match with the parent object.
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_OPTION6)
         .arg(pool_start_address.toText()).arg(pool_end_address.toText()).arg(code).arg(space);
-    uint64_t result = impl_->deleteOption6(server_selector, pool_start_address, pool_end_address,
+    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), pool_start_address, pool_end_address,
                                            code, space);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_OPTION6_RESULT)
         .arg(result);
@@ -3272,14 +3283,14 @@ MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
 }
 
 uint64_t
-MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector,
+MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& /* server_selector */,
                                         const asiolink::IOAddress& pd_pool_prefix,
                                         const uint8_t pd_pool_prefix_length,
                                         const uint16_t code,
                                         const std::string& space) {
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_PREFIX_OPTION6)
         .arg(pd_pool_prefix.toText()).arg(pd_pool_prefix_length).arg(code).arg(space);
-    uint64_t result = impl_->deleteOption6(server_selector, pd_pool_prefix,
+    uint64_t result = impl_->deleteOption6(ServerSelector::ANY(), pd_pool_prefix,
                                            pd_pool_prefix_length, code, space);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_POOL_PREFIX_OPTION6_RESULT)
         .arg(result);
index 17baf3fbd00ab70d83f1f615d865501bf298407c..ea7f66e1f98960ececbc1958a398d24ae9a4d3f0 100644 (file)
@@ -160,8 +160,17 @@ MySqlConfigBackendImpl::createAuditRevision(const int index,
         return;
     }
 
-    auto tag = getServerTag(server_selector, "creating new configuration "
-                            "audit revision");
+    /// @todo The audit trail is not really well prepared to handle multiple server
+    /// tags or no server tags. Therefore, if the server selector appears to be
+    /// pointing to multiple servers, no servers or any server we simply associate the
+    /// audit revision with all servers. The only case when we create a dedicated
+    /// audit entry is when there is a single server tag, i.e. "all" or explicit
+    /// server name. In fact, these are the most common two cases.
+    std::string tag = ServerTag::ALL;
+    auto tags = server_selector.getTags();
+    if (tags.size() == 1) {
+        tag = tags.begin()->get();
+    }
 
     MySqlBindingCollection in_bindings = {
         MySqlBinding::createTimestamp(audit_ts),
@@ -557,14 +566,11 @@ MySqlConfigBackendImpl::createUpdateOptionDef(const db::ServerSelector& server_s
         // as input to the next query.
         uint64_t id = mysql_insert_id(conn_.mysql_);
 
-        MySqlBindingCollection in_server_bindings = {
-            MySqlBinding::createInteger<uint64_t>(id), // option_def_id
-            MySqlBinding::createString(tag), // tag used to obtain server_id
-            MySqlBinding::createTimestamp(option_def->getModificationTime()), // modification_ts
-        };
-
-        // Insert association.
-        conn_.insertQuery(insert_option_def_server, in_server_bindings);
+        // Insert associations of the option definition with servers.
+        attachElementToServers(insert_option_def_server,
+                               server_selector,
+                               MySqlBinding::createInteger<uint64_t>(id),
+                               MySqlBinding::createTimestamp(option_def->getModificationTime()));
     }
 
     transaction.commit();
@@ -885,6 +891,20 @@ MySqlConfigBackendImpl::processOptionRow(const Option::Universe& universe,
     return (desc);
 }
 
+void
+MySqlConfigBackendImpl::attachElementToServers(const int index,
+                                               const ServerSelector& server_selector,
+                                               const MySqlBindingPtr& first_binding,
+                                               const MySqlBindingPtr& in_bindings...) {
+    // Create the vector from the parameter pack.
+    MySqlBindingCollection in_server_bindings = { first_binding, in_bindings };
+    for (auto tag : server_selector.getTags()) {
+        in_server_bindings.push_back(MySqlBinding::createString(tag.get()));
+        conn_.insertQuery(index, in_server_bindings);
+        in_server_bindings.pop_back();
+    }
+}
+
 MySqlBindingPtr
 MySqlConfigBackendImpl::createInputRelayBinding(const NetworkPtr& network) {
     ElementPtr relay_element = Element::createList();
index e858ce7078a21d51345c1e783622b2aab691d56e..64ddd45fb64389df293d2c0d5009523b9b35ae6e 100644 (file)
@@ -266,9 +266,11 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, operation);
-
-        in_bindings.insert(in_bindings.begin(), db::MySqlBinding::createString(tag));
+        // For ANY server, we use queries that lack server tag.
+        if (!server_selector.amAny()) {
+            auto tag = getServerTag(server_selector, operation);
+            in_bindings.insert(in_bindings.begin(), db::MySqlBinding::createString(tag));
+        }
 
         return (conn_.updateDeleteQuery(index, in_bindings));
     }
@@ -548,6 +550,19 @@ public:
     processOptionRow(const Option::Universe& universe,
                      db::MySqlBindingCollection::iterator first_binding);
 
+    /// @brief Associates a configuration element with multiple servers.
+    ///
+    /// @param index Query index.
+    /// @param server_selector Server selector, perhaps with multiple server tags.
+    /// @param first_binding First binding to be used in the query.
+    /// @param in_bindings Parameter pack holding bindings for the query. Note that
+    /// the server tag (or server id) must be the last binding in the prepared
+    /// statement. The caller must not include this binding in the parameter pack.
+    void attachElementToServers(const int index,
+                                const db::ServerSelector& server_selector,
+                                const db::MySqlBindingPtr& first_binding,
+                                const db::MySqlBindingPtr& in_bindings...);
+
     /// @brief Creates input binding for relay addresses.
     ///
     /// @param network Pointer to a shared network or subnet for which binding
index 96f24cd74b09803152c0563a570614628ab0db04..855022cb4e37471b1d2aaee2d679b6a84bc50a45 100644 (file)
@@ -211,7 +211,7 @@ namespace {
 #endif
 
 #ifndef MYSQL_GET_SHARED_NETWORK4
-#define MYSQL_GET_SHARED_NETWORK4(...) \
+#define MYSQL_GET_SHARED_NETWORK4_COMMON(...) \
     "SELECT" \
     "  n.id," \
     "  n.name," \
@@ -252,14 +252,21 @@ namespace {
     "INNER JOIN dhcp4_shared_network_server AS a " \
     "  ON n.id = a.shared_network_id " \
     "INNER JOIN dhcp4_server AS s " \
-    "  ON (a.server_id = s.id) OR (a.server_id = 1) " \
+    "  ON (a.server_id = s.id) " \
     "LEFT JOIN dhcp4_options AS o ON o.scope_id = 4 AND n.name = o.shared_network_name " \
-    "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \
-    " ORDER BY n.id, o.option_id"
+    #__VA_ARGS__ \
+    " ORDER BY n.id, s.id, o.option_id"
+
+#define MYSQL_GET_SHARED_NETWORK4_WITH_TAG(...) \
+    MYSQL_GET_SHARED_NETWORK4_COMMON(WHERE (s.tag = ? OR s.id = 1) __VA_ARGS__)
+
+#define MYSQL_GET_SHARED_NETWORK4_NO_TAG(...) \
+    MYSQL_GET_SHARED_NETWORK4_COMMON(__VA_ARGS__)
+
 #endif
 
 #ifndef MYSQL_GET_SHARED_NETWORK6
-#define MYSQL_GET_SHARED_NETWORK6(...) \
+#define MYSQL_GET_SHARED_NETWORK6_COMMON(...) \
     "SELECT" \
     "  n.id," \
     "  n.name," \
@@ -301,10 +308,17 @@ namespace {
     "INNER JOIN dhcp6_shared_network_server AS a " \
     "  ON n.id = a.shared_network_id " \
     "INNER JOIN dhcp6_server AS s " \
-    "  ON (a.server_id = s.id) OR (a.server_id = 1) " \
+    "  ON (a.server_id = s.id) " \
     "LEFT JOIN dhcp6_options AS o ON o.scope_id = 4 AND n.name = o.shared_network_name " \
-    "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \
-    " ORDER BY n.id, o.option_id"
+    #__VA_ARGS__ \
+    " ORDER BY n.id, s.id, o.option_id"
+
+#define MYSQL_GET_SHARED_NETWORK6_WITH_TAG(...) \
+    MYSQL_GET_SHARED_NETWORK6_COMMON(WHERE (s.tag = ? OR s.id = 1) __VA_ARGS__)
+
+#define MYSQL_GET_SHARED_NETWORK6_NO_TAG(...) \
+    MYSQL_GET_SHARED_NETWORK6_COMMON(__VA_ARGS__)
+
 #endif
 
 #ifndef MYSQL_GET_OPTION_DEF
@@ -410,18 +424,18 @@ namespace {
 #define MYSQL_INSERT_GLOBAL_PARAMETER_SERVER(table_prefix) \
     "INSERT INTO " #table_prefix "_global_parameter_server(" \
     "  parameter_id," \
-    "  server_id," \
-    "  modification_ts" \
-    ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)"
+    "  modification_ts," \
+    "  server_id" \
+    ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))"
 #endif
 
 #ifndef MYSQL_INSERT_SUBNET_SERVER
 #define MYSQL_INSERT_SUBNET_SERVER(table_prefix) \
     "INSERT INTO " #table_prefix "_subnet_server(" \
     "  subnet_id," \
-    "  server_id," \
-    "  modification_ts" \
-    ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)"
+    "  modification_ts," \
+    "  server_id" \
+    ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))"
 #endif
 
 #ifndef MYSQL_INSERT_POOL
@@ -449,11 +463,11 @@ namespace {
 #define MYSQL_INSERT_SHARED_NETWORK_SERVER(table_prefix) \
     "INSERT INTO " #table_prefix "_shared_network_server(" \
     "  shared_network_id," \
-    "  server_id," \
-    "  modification_ts" \
+    "  modification_ts," \
+    "  server_id" \
     ") VALUES (" \
-    "    (SELECT id FROM " #table_prefix "_shared_network WHERE name = ?)," \
-    "    (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?" \
+    "    (SELECT id FROM " #table_prefix "_shared_network WHERE name = ?), ?," \
+    "    (SELECT id FROM " #table_prefix "_server WHERE tag = ?)" \
     ")"
 #endif
 
@@ -476,9 +490,9 @@ namespace {
 #define MYSQL_INSERT_OPTION_DEF_SERVER(table_prefix) \
     "INSERT INTO " #table_prefix "_option_def_server(" \
     "  option_def_id," \
-    "  server_id," \
-    "  modification_ts" \
-    ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)"
+    "  modification_ts," \
+    "  server_id" \
+    ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))"
 #endif
 
 #ifndef MYSQL_INSERT_OPTION_COMMON
@@ -509,9 +523,9 @@ namespace {
 #define MYSQL_INSERT_OPTION_SERVER(table_prefix) \
     "INSERT INTO " #table_prefix "_options_server (" \
     "  option_id," \
-    "  server_id," \
-    "  modification_ts" \
-    ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)"
+    "  modification_ts," \
+    "  server_id" \
+    ") VALUES (?, ?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?))"
 #endif
 
 #ifndef MYSQL_INSERT_SERVER
@@ -566,6 +580,7 @@ namespace {
     "INNER JOIN " #table_prefix "_server AS s" \
     "  ON a.server_id = s.id " \
     "SET" \
+    "  o.option_id = LAST_INSERT_ID(o.option_id)," \
     "  o.code = ?," \
     "  o.value = ?," \
     "  o.formatted_value = ?," \
@@ -579,11 +594,18 @@ namespace {
     "  o.pool_id = ?," \
     "  o.modification_ts = ? " \
     pd_pool_id \
-    "WHERE s.tag = ? " #__VA_ARGS__
+    "WHERE " #__VA_ARGS__
 
-#define MYSQL_UPDATE_OPTION4(...) \
+#define MYSQL_UPDATE_OPTION4_WITH_TAG(...) \
+    MYSQL_UPDATE_OPTION_COMMON(dhcp4, "", s.tag = ? __VA_ARGS__)
+
+#define MYSQL_UPDATE_OPTION4_NO_TAG(...) \
     MYSQL_UPDATE_OPTION_COMMON(dhcp4, "", __VA_ARGS__)
-#define MYSQL_UPDATE_OPTION6(...) \
+
+#define MYSQL_UPDATE_OPTION6_WITH_TAG(...) \
+    MYSQL_UPDATE_OPTION_COMMON(dhcp6, ", o.pd_pool_id = ? ", s.tag = ? __VA_ARGS__)
+
+#define MYSQL_UPDATE_OPTION6_NO_TAG(...) \
     MYSQL_UPDATE_OPTION_COMMON(dhcp6, ", o.pd_pool_id = ? ", __VA_ARGS__)
 #endif
 
@@ -647,6 +669,13 @@ namespace {
     "WHERE s.tag = ? " #__VA_ARGS__
 #endif
 
+#ifndef MYSQL_DELETE_SHARED_NETWORK_SERVER
+#define MYSQL_DELETE_SHARED_NETWORK_SERVER(table_prefix) \
+    "DELETE FROM " #table_prefix "_shared_network_server " \
+    "WHERE shared_network_id = " \
+    "(SELECT id FROM " #table_prefix "_shared_network WHERE name = ?)"
+#endif
+
 #ifndef MYSQL_DELETE_OPTION_DEF
 #define MYSQL_DELETE_OPTION_DEF(table_prefix, ...) \
     "DELETE d FROM " #table_prefix "_option_def AS d " \
@@ -666,13 +695,19 @@ namespace {
 #endif
 
 #ifndef MYSQL_DELETE_OPTION
-#define MYSQL_DELETE_OPTION(table_prefix, ...) \
+#define MYSQL_DELETE_OPTION_COMMON(table_prefix, ...) \
     "DELETE o FROM " #table_prefix "_options AS o " \
     "INNER JOIN " #table_prefix "_options_server AS a" \
     "  ON o.option_id = a.option_id " \
     "INNER JOIN " #table_prefix "_server AS s" \
     "  ON a.server_id = s.id " \
-    "WHERE s.tag = ? " #__VA_ARGS__
+    #__VA_ARGS__
+
+#define MYSQL_DELETE_OPTION_WITH_TAG(table_prefix, ...) \
+    MYSQL_DELETE_OPTION_COMMON(table_prefix, WHERE s.tag = ? __VA_ARGS__)
+
+#define MYSQL_DELETE_OPTION_NO_TAG(table_prefix, ...) \
+    MYSQL_DELETE_OPTION_COMMON(table_prefix, __VA_ARGS__)
 #endif
 
 #ifndef MYSQL_DELETE_OPTION_UNASSIGNED
@@ -690,7 +725,7 @@ namespace {
     "  ON o.option_id = a.option_id " \
     "INNER JOIN " #table_prefix "_server AS s" \
     "  ON a.server_id = s.id " \
-    "WHERE s.tag = ? " #__VA_ARGS__ \
+    "WHERE " #__VA_ARGS__ \
     "  AND o.pool_id = " \
     "  (SELECT id FROM " #table_prefix "_pool" \
     "   WHERE start_address = ? AND end_address = ?)"
@@ -703,12 +738,24 @@ namespace {
     "  ON o.option_id = a.option_id " \
     "INNER JOIN dhcp6_server AS s" \
     "  ON a.server_id = s.id " \
-    "WHERE s.tag = ? " #__VA_ARGS__ \
+    "WHERE " #__VA_ARGS__ \
     "  AND o.pd_pool_id = " \
     "  (SELECT id FROM dhcp6_pd_pool" \
     "   WHERE prefix = ? AND prefix_length = ?)"
 #endif
 
+#ifndef MYSQL_DELETE_OPTION_SERVER
+#define MYSQL_DELETE_OPTION_SERVER(table_prefix) \
+    "DELETE os FROM " #table_prefix "_options_server AS os " \
+    "WHERE os.option_id = " \
+    "  (SELECT o.option_id FROM " #table_prefix "_options AS o" \
+    "   INNER JOIN " #table_prefix "_options_server AS a" \
+    "      ON o.option_id = a.option_id " \
+    "   INNER JOIN " #table_prefix "_server AS s" \
+    "      ON a.server_id = s.id " \
+    "   WHERE s.tag = ? AND o.scope_id = ? AND o.code = ? AND o.space = ?)"
+#endif
+
 #ifndef MYSQL_DELETE_SERVER
 #define MYSQL_DELETE_SERVER(table_prefix) \
     "DELETE FROM " #table_prefix "_server " \
index 513279f77c0bcb3ce27b8b583680a34456eb31e3..971e15d984d07e103e117dd30c862967a0dd1e05 100644 (file)
@@ -1438,24 +1438,60 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetworkSubnets4) {
 // Test that shared network can be inserted, fetched, updated and then
 // fetched again.
 TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4) {
-    // Insert new shared network.
-    SharedNetwork4Ptr shared_network = test_networks_[0];
-    cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(), shared_network);
+    // Insert the server2 into the database.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[2]));
+    {
+        SCOPED_TRACE("CREATE audit entry for server");
+        testNewAuditEntry("dhcp4_server",
+                          AuditEntry::ModificationType::CREATE,
+                          "server set");
+    }
 
-    // Fetch this shared network by name.
-    SharedNetwork4Ptr
-        returned_network = cbptr_->getSharedNetwork4(ServerSelector::ALL(),
-                                                     test_networks_[0]->getName());
-    ASSERT_TRUE(returned_network);
+    auto shared_network = test_networks_[0];
+    auto shared_network2 = test_networks_[2];
 
-    EXPECT_GT(returned_network->getId(), 0);
-    ASSERT_EQ(1, returned_network->getServerTags().size());
-    EXPECT_EQ("all", returned_network->getServerTags()[0].get());
+    // Insert two shared networks, one for all servers, and one for server2.
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(),
+                                                       shared_network));
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::ONE("server2"),
+                                                       shared_network2));
 
-    // The easiest way to verify whether the returned shared network matches the
-    // inserted shared network is to convert both to text.
-    EXPECT_EQ(shared_network->toElement()->str(),
-              returned_network->toElement()->str());
+    // We are not going to support selection of a single entry for multiple servers.
+    EXPECT_THROW(cbptr_->getSharedNetwork4(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                           test_networks_[0]->getName()),
+                 isc::InvalidOperation);
+    // We currently don't support fetching a shared network which is assigned
+    // to no servers.
+    EXPECT_THROW(cbptr_->getSharedNetwork4(ServerSelector::UNASSIGNED(),
+                                           test_networks_[0]->getName()),
+                 isc::NotImplemented);
+
+
+    // Test that this shared network will be fetched for various server selectors.
+    auto test_get_network = [this, &shared_network] (const std::string& test_case_name,
+                                                     const ServerSelector& server_selector,
+                                                     const std::string& expected_tag = ServerTag::ALL) {
+        SCOPED_TRACE(test_case_name);
+        SharedNetwork4Ptr network;
+        ASSERT_NO_THROW(network = cbptr_->getSharedNetwork4(server_selector,
+                                                            shared_network->getName()));
+        ASSERT_TRUE(network);
+
+        EXPECT_GT(network->getId(), 0);
+        ASSERT_EQ(1, network->getServerTags().size());
+        EXPECT_EQ(expected_tag, network->getServerTags()[0].get());
+
+        // The easiest way to verify whether the returned shared network matches the
+        // inserted shared network is to convert both to text.
+        EXPECT_EQ(shared_network->toElement()->str(), network->toElement()->str());
+    };
+
+    {
+        SCOPED_TRACE("testing various server selectors before update");
+        test_get_network("all servers", ServerSelector::ALL());
+        test_get_network("one server", ServerSelector::ONE("server1"));
+        test_get_network("any server", ServerSelector::ANY());
+    }
 
     {
         SCOPED_TRACE("CREATE audit entry for a shared network");
@@ -1465,14 +1501,16 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4) {
     }
 
     // Update shared network in the database.
-    SharedNetwork4Ptr shared_network2 = test_networks_[1];
-    cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(), shared_network2);
+    shared_network = test_networks_[1];
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(),
+                                                       shared_network));
 
-    // Fetch updated shared network and see if it matches.
-    returned_network = cbptr_->getSharedNetwork4(ServerSelector::ALL(),
-                                                 test_networks_[1]->getName());
-    EXPECT_EQ(shared_network2->toElement()->str(),
-              returned_network->toElement()->str());
+    {
+        SCOPED_TRACE("testing various server selectors after update");
+        test_get_network("all servers after update", ServerSelector::ALL());
+        test_get_network("one server after update", ServerSelector::ONE("server1"));
+        test_get_network("any server after update", ServerSelector::ANY());
+    }
 
     {
         SCOPED_TRACE("UPDATE audit entry for a shared network");
@@ -1481,12 +1519,69 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4) {
                           "shared network set");
     }
 
-    // Fetching the shared network for an explicitly specified server tag should
-    // succeed too.
-    returned_network = cbptr_->getSharedNetwork4(ServerSelector::ONE("server1"),
-                                                 shared_network2->getName());
-    EXPECT_EQ(shared_network2->toElement()->str(),
-              returned_network->toElement()->str());
+    // The server2 specific shared network should not be returned if the
+    // server selector is not matching.
+    EXPECT_FALSE(cbptr_->getSharedNetwork4(ServerSelector::ALL(),
+                                           shared_network2->getName()));
+    EXPECT_FALSE(cbptr_->getSharedNetwork4(ServerSelector::ONE("server1"),
+                                           shared_network2->getName()));
+
+    {
+        SCOPED_TRACE("testing selectors for server2 specific shared network");
+        shared_network = shared_network2;
+        test_get_network("one server", ServerSelector::ONE("server2"), "server2");
+        test_get_network("any server", ServerSelector::ANY(), "server2");
+    }
+}
+
+// Test that shared network may be created and updated and the server tags
+// are properly assigned to it.
+TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateSharedNetwork4) {
+    // Insert the server1 into the database.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[0]));
+    {
+        SCOPED_TRACE("CREATE audit entry for server");
+        testNewAuditEntry("dhcp4_server",
+                          AuditEntry::ModificationType::CREATE,
+                          "server set");
+    }
+
+    // Insert the server2 into the database.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[2]));
+    {
+        SCOPED_TRACE("CREATE audit entry for server");
+        testNewAuditEntry("dhcp4_server",
+                          AuditEntry::ModificationType::CREATE,
+                          "server set");
+    }
+
+    auto shared_network = test_networks_[0];
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(),
+                                                       shared_network));
+    {
+        SCOPED_TRACE("CREATE audit entry for shared network and ALL servers");
+        testNewAuditEntry("dhcp4_shared_network",
+                          AuditEntry::ModificationType::CREATE,
+                          "shared network set");
+    }
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                                       shared_network));
+    {
+        SCOPED_TRACE("CREATE audit entry for shared network and MULTIPLE servers");
+        testNewAuditEntry("dhcp4_shared_network",
+                          AuditEntry::ModificationType::UPDATE,
+                          "shared network set");
+    }
+
+    SharedNetwork4Ptr network;
+    ASSERT_NO_THROW(network = cbptr_->getSharedNetwork4(ServerSelector::ANY(),
+                                                        shared_network->getName()));
+    ASSERT_TRUE(network);
+    EXPECT_TRUE(network->hasServerTag(ServerTag("server1")));
+    EXPECT_TRUE(network->hasServerTag(ServerTag("server2")));
+    EXPECT_FALSE(network->hasServerTag(ServerTag()));
 }
 
 // Test that the information about unspecified optional parameters gets
@@ -1575,6 +1670,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllSharedNetworks4) {
     for (auto i = 0; i < networks.size(); ++i) {
         EXPECT_EQ(test_networks_[i + 1]->toElement()->str(),
                   networks[i]->toElement()->str());
+        ASSERT_EQ(1, networks[i]->getServerTags().size());
+        EXPECT_EQ("all", networks[i]->getServerTags()[0].get());
     }
 
     // Add some subnets.
@@ -2528,14 +2625,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSubnetOption4) {
                           "subnet specific option set");
     }
 
-    // Deleting an option with explicitly specified server tag should fail.
-    EXPECT_EQ(0, cbptr_->deleteOption4(ServerSelector::ONE("server1"),
-                                       subnet->getID(),
-                                       opt_boot_file_name->option_->getType(),
-                                       opt_boot_file_name->space_name_));
-
-    // It should succeed for all servers.
-    EXPECT_EQ(1, cbptr_->deleteOption4(ServerSelector::ALL(), subnet->getID(),
+    // It should succeed for any server.
+    EXPECT_EQ(1, cbptr_->deleteOption4(ServerSelector::ANY(), subnet->getID(),
                                        opt_boot_file_name->option_->getType(),
                                        opt_boot_file_name->space_name_));
 
@@ -2639,15 +2730,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeletePoolOption4) {
                           "pool specific option set");
     }
 
-    // Deleting an option with explicitly specified server tag should fail.
-    EXPECT_EQ(0, cbptr_->deleteOption4(ServerSelector::ONE("server1"),
-                                       pool->getFirstAddress(),
-                                       pool->getLastAddress(),
-                                       opt_boot_file_name->option_->getType(),
-                                       opt_boot_file_name->space_name_));
-
-    // Delete option for all servers should succeed.
-    EXPECT_EQ(1, cbptr_->deleteOption4(ServerSelector::ALL(),
+    // Delete option for any server should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption4(ServerSelector::ANY(),
                                        pool->getFirstAddress(),
                                        pool->getLastAddress(),
                                        opt_boot_file_name->option_->getType(),
@@ -2750,14 +2834,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSharedNetworkOption4) {
                           "shared network specific option set");
     }
 
-    // Deleting an option with explicitly specified server tag should fail.
-    EXPECT_EQ(0, cbptr_->deleteOption4(ServerSelector::ONE("server1"),
-                                       shared_network->getName(),
-                                       opt_boot_file_name->option_->getType(),
-                                       opt_boot_file_name->space_name_));
-
-    // Deleting an option for all servers should succeed.
-    EXPECT_EQ(1, cbptr_->deleteOption4(ServerSelector::ALL(),
+    // Deleting an option for any server should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption4(ServerSelector::ANY(),
                                        shared_network->getName(),
                                        opt_boot_file_name->option_->getType(),
                                        opt_boot_file_name->space_name_));
index 95789f2145d89cc93b01a89e82cd3324e0be2c1d..e54164a56014e73dc4c9dfb19a470dbb4ac6de11 100644 (file)
@@ -1453,24 +1453,59 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetworkSubnets6) {
 // Test that shared network can be inserted, fetched, updated and then
 // fetched again.
 TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6) {
-    // Insert new shared network.
-    SharedNetwork6Ptr shared_network = test_networks_[0];
-    cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network);
+    // Insert the server2 into the database.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
+    {
+        SCOPED_TRACE("CREATE audit entry for server");
+        testNewAuditEntry("dhcp6_server",
+                          AuditEntry::ModificationType::CREATE,
+                          "server set");
+    }
 
-    // Fetch this shared network by name.
-    SharedNetwork6Ptr
-        returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
-                                                     test_networks_[0]->getName());
-    ASSERT_TRUE(returned_network);
+    auto shared_network = test_networks_[0];
+    auto shared_network2 = test_networks_[2];
 
-    EXPECT_GT(returned_network->getId(), 0);
-    ASSERT_EQ(1, returned_network->getServerTags().size());
-    EXPECT_EQ("all", returned_network->getServerTags()[0].get());
+    // Insert two shared networks, one for all servers, and one for server2.
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
+                                                       shared_network));
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server2"),
+                                                       shared_network2));
 
-    // The easiest way to verify whether the returned shared network matches the
-    // inserted shared network is to convert both to text.
-    EXPECT_EQ(shared_network->toElement()->str(),
-              returned_network->toElement()->str());
+    // We are not going to support selection of a single entry for multiple servers.
+    EXPECT_THROW(cbptr_->getSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                           test_networks_[0]->getName()),
+                 isc::InvalidOperation);
+    // We currently don't support fetching a shared network which is assigned
+    // to no servers.
+    EXPECT_THROW(cbptr_->getSharedNetwork6(ServerSelector::UNASSIGNED(),
+                                           test_networks_[0]->getName()),
+                 isc::NotImplemented);
+
+    // Test that this shared network will be fetched for various server selectors.
+    auto test_get_network = [this, &shared_network] (const std::string& test_case_name,
+                                                     const ServerSelector& server_selector,
+                                                     const std::string& expected_tag = ServerTag::ALL) {
+        SCOPED_TRACE(test_case_name);
+        SharedNetwork6Ptr network;
+        ASSERT_NO_THROW(network = cbptr_->getSharedNetwork6(server_selector,
+                                                            shared_network->getName()));
+        ASSERT_TRUE(network);
+
+        EXPECT_GT(network->getId(), 0);
+        ASSERT_EQ(1, network->getServerTags().size());
+        EXPECT_EQ(expected_tag, network->getServerTags()[0].get());
+
+        // The easiest way to verify whether the returned shared network matches the
+        // inserted shared network is to convert both to text.
+        EXPECT_EQ(shared_network->toElement()->str(), network->toElement()->str());
+    };
+
+    {
+        SCOPED_TRACE("testing various server selectors before update");
+        test_get_network("all servers", ServerSelector::ALL());
+        test_get_network("one server", ServerSelector::ONE("server1"));
+        test_get_network("any server", ServerSelector::ANY());
+    }
 
     {
         SCOPED_TRACE("CREATE audit entry for a shared network");
@@ -1480,14 +1515,16 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6) {
     }
 
     // Update shared network in the database.
-    SharedNetwork6Ptr shared_network2 = test_networks_[1];
-    cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network2);
+    shared_network = test_networks_[1];
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
+                                                       shared_network));
 
-    // Fetch updated shared network and see if it matches.
-    returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(),
-                                                 test_networks_[1]->getName());
-    EXPECT_EQ(shared_network2->toElement()->str(),
-              returned_network->toElement()->str());
+    {
+        SCOPED_TRACE("testing various server selectors after update");
+        test_get_network("all servers after update", ServerSelector::ALL());
+        test_get_network("one server after update", ServerSelector::ONE("server1"));
+        test_get_network("any server after update", ServerSelector::ANY());
+    }
 
     {
         SCOPED_TRACE("UPDATE audit entry for a shared network");
@@ -1496,12 +1533,69 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6) {
                           "shared network set");
     }
 
-    // Fetching the shared network for an explicitly specified server tag should
-    // succeed too.
-    returned_network = cbptr_->getSharedNetwork6(ServerSelector::ONE("server1"),
-                                                 shared_network2->getName());
-    EXPECT_EQ(shared_network2->toElement()->str(),
-              returned_network->toElement()->str());
+    // The server2 specific shared network should not be returned if the
+    // server selector is not matching.
+    EXPECT_FALSE(cbptr_->getSharedNetwork6(ServerSelector::ALL(),
+                                           shared_network2->getName()));
+    EXPECT_FALSE(cbptr_->getSharedNetwork6(ServerSelector::ONE("server1"),
+                                           shared_network2->getName()));
+
+    {
+        SCOPED_TRACE("testing selectors for server2 specific shared network");
+        shared_network = shared_network2;
+        test_get_network("one server", ServerSelector::ONE("server2"), "server2");
+        test_get_network("any server", ServerSelector::ANY(), "server2");
+    }
+}
+
+// Test that shared network may be created and updated and the server tags
+// are properly assigned to it.
+TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateSharedNetwork6) {
+    // Insert the server1 into the database.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
+    {
+        SCOPED_TRACE("CREATE audit entry for server");
+        testNewAuditEntry("dhcp6_server",
+                          AuditEntry::ModificationType::CREATE,
+                          "server set");
+    }
+
+    // Insert the server2 into the database.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
+    {
+        SCOPED_TRACE("CREATE audit entry for server");
+        testNewAuditEntry("dhcp6_server",
+                          AuditEntry::ModificationType::CREATE,
+                          "server set");
+    }
+
+    auto shared_network = test_networks_[0];
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
+                                                       shared_network));
+    {
+        SCOPED_TRACE("CREATE audit entry for shared network and ALL servers");
+        testNewAuditEntry("dhcp6_shared_network",
+                          AuditEntry::ModificationType::CREATE,
+                          "shared network set");
+    }
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                                       shared_network));
+    {
+        SCOPED_TRACE("UPDATE audit entry for shared network and MULTIPLE servers");
+        testNewAuditEntry("dhcp6_shared_network",
+                          AuditEntry::ModificationType::UPDATE,
+                          "shared network set");
+    }
+
+    SharedNetwork6Ptr network;
+    ASSERT_NO_THROW(network = cbptr_->getSharedNetwork6(ServerSelector::ANY(),
+                                                        shared_network->getName()));
+    ASSERT_TRUE(network);
+    EXPECT_TRUE(network->hasServerTag(ServerTag("server1")));
+    EXPECT_TRUE(network->hasServerTag(ServerTag("server2")));
+    EXPECT_FALSE(network->hasServerTag(ServerTag()));
 }
 
 // Test that the information about unspecified optional parameters gets
@@ -2552,14 +2646,8 @@ TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSubnetOption6) {
                           "subnet specific option set");
     }
 
-    // Deleting an option with explicitly specified server tag should fail.
-    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
-                                       subnet->getID(),
-                                       opt_posix_timezone->option_->getType(),
-                                       opt_posix_timezone->space_name_));
-
-    // It should succeed for all servers.
-    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(), subnet->getID(),
+    // It should succeed for any server.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(), subnet->getID(),
                                        opt_posix_timezone->option_->getType(),
                                        opt_posix_timezone->space_name_));
 
@@ -2664,15 +2752,8 @@ TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeletePoolOption6) {
                           "address pool specific option set");
     }
 
-    // Deleting an option with explicitly specified server tag should fail.
-    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
-                                       pool->getFirstAddress(),
-                                       pool->getLastAddress(),
-                                       opt_posix_timezone->option_->getType(),
-                                       opt_posix_timezone->space_name_));
-
-    // Delete option for all servers should succeed.
-    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(),
+    // Delete option for any server should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(),
                                        pool->getFirstAddress(),
                                        pool->getLastAddress(),
                                        opt_posix_timezone->option_->getType(),
@@ -2791,15 +2872,8 @@ TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeletePdPoolOption6) {
                           "prefix delegation pool specific option set");
     }
 
-    // Deleting an option with explicitly specified server tag should fail.
-    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
-                                       pd_pool->getFirstAddress(),
-                                       static_cast<uint8_t>(pd_pool_len),
-                                       opt_posix_timezone->option_->getType(),
-                                       opt_posix_timezone->space_name_));
-
-    // Delete option for all servers should succeed.
-    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(),
+    // Delete option for any server should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(),
                                        pd_pool->getFirstAddress(),
                                        static_cast<uint8_t>(pd_pool_len),
                                        opt_posix_timezone->option_->getType(),
@@ -2902,14 +2976,8 @@ TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSharedNetworkOption6) {
                           "shared network specific option set");
     }
 
-    // Deleting an option with explicitly specified server tag should fail.
-    EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"),
-                                       shared_network->getName(),
-                                       opt_posix_timezone->option_->getType(),
-                                       opt_posix_timezone->space_name_));
-
-    // Deleting an option for all servers should succeed.
-    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(),
+    // Deleting an option for any server should succeed.
+    EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ANY(),
                                        shared_network->getName(),
                                        opt_posix_timezone->option_->getType(),
                                        opt_posix_timezone->space_name_));
index 574be35f590a6707fd9b7135212b98a0528c3734..205bfdcd091d6471e48102f0bf35637c2d282571 100644 (file)
@@ -50,7 +50,8 @@ public:
     enum class Type {
         UNASSIGNED,
         ALL,
-        SUBSET
+        SUBSET,
+        ANY
     };
 
     /// @brief Factory returning "unassigned" server selector.
@@ -79,6 +80,12 @@ public:
     /// @throw InvalidOperation if no server tags provided.
     static ServerSelector MULTIPLE(const std::set<std::string>& server_tags);
 
+    /// @brief Factory returning "any server" selector.
+    static ServerSelector ANY() {
+        ServerSelector selector(Type::ANY);
+        return (selector);
+    }
+
     /// @brief Returns type of the selector.
     Type getType() const {
         return (type_);
@@ -99,6 +106,13 @@ public:
         return (getType() == Type::UNASSIGNED);
     }
 
+    /// @brief Convenience method checking if the server selector is "any".
+    ///
+    /// @return true if the selector is "any", false otherwise.
+    bool amAny() const {
+        return (getType() == Type::ANY);
+    }
+
     /// @brief Convenience method checking if the server selector has multiple tags.
     ///
     /// @return true if it has multiple tags, false otherwise.
index 2eaed14bb10f87735c998bb8de8d63e0666357bf..b3b530db99e220ae7741d601dcac52743fe681e1 100644 (file)
@@ -61,4 +61,14 @@ TEST(ServerSelectorTest, multiple) {
     EXPECT_TRUE(selector.hasMultipleTags());
 }
 
+// Check that server selector can be set to ALL.
+TEST(ServerSelectorTest, any) {
+    ServerSelector selector = ServerSelector::ANY();
+    EXPECT_EQ(ServerSelector::Type::ANY, selector.getType());
+    EXPECT_FALSE(selector.amUnassigned());
+
+    auto tags = selector.getTags();
+    EXPECT_EQ(0, tags.size());
+}
+
 }