]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#717] Associate subnets with server tags.
authorMarcin Siodelski <marcin@isc.org>
Mon, 8 Jul 2019 19:42:50 +0000 (21:42 +0200)
committerMarcin Siodelski <marcin@isc.org>
Fri, 12 Jul 2019 15:58:16 +0000 (11:58 -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_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

index cb95b16946bca4ebfea247d0a27ad3e966fac4f8..d12a8d125d630100c86038658a7591e8e0f5372c 100644 (file)
@@ -60,10 +60,16 @@ public:
         GET_GLOBAL_PARAMETER4,
         GET_ALL_GLOBAL_PARAMETERS4,
         GET_MODIFIED_GLOBAL_PARAMETERS4,
-        GET_SUBNET4_ID,
-        GET_SUBNET4_PREFIX,
+        GET_SUBNET4_ID_NO_TAG,
+        GET_SUBNET4_ID_ANY,
+        GET_SUBNET4_ID_UNASSIGNED,
+        GET_SUBNET4_PREFIX_NO_TAG,
+        GET_SUBNET4_PREFIX_ANY,
+        GET_SUBNET4_PREFIX_UNASSIGNED,
         GET_ALL_SUBNETS4,
+        GET_ALL_SUBNETS4_UNASSIGNED,
         GET_MODIFIED_SUBNETS4,
+        GET_MODIFIED_SUBNETS4_UNASSIGNED,
         GET_SHARED_NETWORK_SUBNETS4,
         GET_POOL4_RANGE,
         GET_SHARED_NETWORK4_NAME_NO_TAG,
@@ -113,6 +119,7 @@ public:
         DELETE_SUBNET4_PREFIX,
         DELETE_ALL_SUBNETS4,
         DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME,
+        DELETE_SUBNET4_SERVER,
         DELETE_POOLS4_SUBNET_ID,
         DELETE_SHARED_NETWORK4_NAME_WITH_TAG,
         DELETE_SHARED_NETWORK4_NAME_ANY,
@@ -227,6 +234,7 @@ public:
     /// Query should order subnets by subnet_id.
     ///
     /// @param index Index of the query to be used.
+    /// @param server_selector Server selector.
     /// @param in_bindings Input bindings specifying selection criteria. The
     /// size of the bindings collection must match the number of placeholders
     /// in the prepared statement. The input bindings collection must be empty
@@ -234,6 +242,7 @@ public:
     /// @param [out] subnets Reference to the container where fetched subnets
     /// will be inserted.
     void getSubnets4(const StatementIndex& index,
+                     const ServerSelector& server_selector,
                      const MySqlBindingCollection& in_bindings,
                      Subnet4Collection& subnets) {
         // Create output bindings. The order must match that in the prepared
@@ -300,13 +309,14 @@ public:
         uint64_t last_pool_id = 0;
         uint64_t last_pool_option_id = 0;
         uint64_t last_option_id = 0;
-
         Pool4Ptr last_pool;
+        std::string last_tag;
 
         // Execute actual query.
         conn_.selectQuery(index, in_bindings, out_bindings,
                           [this, &subnets, &last_pool, &last_pool_id,
-                           &last_pool_option_id, &last_option_id]
+                           &last_pool_option_id, &last_option_id,
+                           &last_tag]
                           (MySqlBindingCollection& out_bindings) {
             // Get pointer to the last subnet in the collection.
             Subnet4Ptr last_subnet;
@@ -324,6 +334,9 @@ public:
                 // rows will contain pool information.
                 last_pool_id = 0;
 
+                // Reset last server tag as we're now starting to process new subnet.
+                last_tag.clear();
+
                 // subnet_id
                 SubnetID subnet_id(out_bindings[0]->getInteger<uint32_t>());
                 // subnet_prefix
@@ -466,9 +479,6 @@ public:
 
                 // {min,max}_valid_lifetime
 
-                // server_tag
-                last_subnet->setServerTag(out_bindings[55]->getString());
-
                 // Subnet ready. Add it to the list.
                 auto ret = subnets.push_back(last_subnet);
 
@@ -480,6 +490,15 @@ public:
                 }
             }
 
+            // Check for new server tags.
+            if (!out_bindings[55]->amNull() &&
+                (last_tag != out_bindings[55]->getString())) {
+                last_tag = out_bindings[55]->getString();
+                if (!last_tag.empty() && !last_subnet->hasServerTag(ServerTag(last_tag))) {
+                    last_subnet->setServerTag(last_tag);
+                }
+            }
+
             // 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.
@@ -514,8 +533,14 @@ public:
                     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<SubnetRandomAccessIndexTag>();
+        tossNonMatchingElements(server_selector, subnet_index);
     }
 
     /// @brief Sends query to retrieve single subnet by id.
@@ -527,18 +552,27 @@ public:
     /// doesn't exist.
     Subnet4Ptr getSubnet4(const ServerSelector& server_selector,
                           const SubnetID& subnet_id) {
-        Subnet4Collection subnets;
 
-        auto tags = server_selector.getTags();
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createInteger<uint32_t>(subnet_id)
-            };
+        if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a subnet. Got: "
+                      << getServerTagsAsText(server_selector));
+        }
+
+        MySqlBindingCollection in_bindings = { MySqlBinding::createInteger<uint32_t>(subnet_id) };
+
+        auto index = GET_SUBNET4_ID_NO_TAG;
 
-            getSubnets4(GET_SUBNET4_ID, in_bindings, subnets);
+        if (server_selector.amUnassigned()) {
+            index = GET_SUBNET4_ID_UNASSIGNED;
+
+        } else if (server_selector.amAny()) {
+            index = GET_SUBNET4_ID_ANY;
         }
 
+        Subnet4Collection subnets;
+        getSubnets4(index, server_selector, in_bindings, subnets);
+
         return (subnets.empty() ? Subnet4Ptr() : *subnets.begin());
     }
 
@@ -553,18 +587,26 @@ public:
     /// doesn't exist.
     Subnet4Ptr getSubnet4(const ServerSelector& server_selector,
                           const std::string& subnet_prefix) {
-        Subnet4Collection subnets;
+        if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a subnet. Got: "
+                      << getServerTagsAsText(server_selector));
+        }
 
-        auto tags = server_selector.getTags();
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createString(subnet_prefix)
-            };
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(subnet_prefix) };
 
-            getSubnets4(GET_SUBNET4_PREFIX, in_bindings, subnets);
+        auto index = GET_SUBNET4_PREFIX_NO_TAG;
+
+        if (server_selector.amUnassigned()) {
+            index = GET_SUBNET4_PREFIX_UNASSIGNED;
+
+        } else if (server_selector.amAny()) {
+            index = GET_SUBNET4_PREFIX_ANY;
         }
 
+        Subnet4Collection subnets;
+        getSubnets4(index, server_selector, in_bindings, subnets);
+
         return (subnets.empty() ? Subnet4Ptr() : *subnets.begin());
     }
 
@@ -575,15 +617,10 @@ public:
     /// subnets should be inserted.
     void getAllSubnets4(const ServerSelector& server_selector,
                         Subnet4Collection& subnets) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get())
-            };
-
-            getSubnets4(GET_ALL_SUBNETS4, in_bindings, subnets);
-        }
+        auto index = (server_selector.amUnassigned() ? GET_ALL_SUBNETS4_UNASSIGNED :
+                      GET_ALL_SUBNETS4);
+        MySqlBindingCollection in_bindings;
+        getSubnets4(index, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve modified subnets.
@@ -595,16 +632,13 @@ public:
     void getModifiedSubnets4(const ServerSelector& server_selector,
                              const boost::posix_time::ptime& modification_ts,
                              Subnet4Collection& subnets) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createTimestamp(modification_ts)
-            };
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createTimestamp(modification_ts)
+        };
 
-            getSubnets4(GET_MODIFIED_SUBNETS4, in_bindings, subnets);
-        }
+        auto index = (server_selector.amUnassigned() ? GET_MODIFIED_SUBNETS4_UNASSIGNED :
+                      GET_MODIFIED_SUBNETS4);
+        getSubnets4(index, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve all subnets belonging to a shared network.
@@ -617,16 +651,8 @@ public:
     void getSharedNetworkSubnets4(const ServerSelector& server_selector,
                                   const std::string& shared_network_name,
                                   Subnet4Collection& subnets) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createString(shared_network_name)
-            };
-
-            getSubnets4(GET_SHARED_NETWORK_SUBNETS4, in_bindings, subnets);
-        }
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(shared_network_name) };
+        getSubnets4(GET_SHARED_NETWORK_SUBNETS4, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve multiple pools.
@@ -739,8 +765,6 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "creating or updating subnet");
-
         // Convert DHCPv4o6 interface id to text.
         OptionPtr dhcp4o6_interface_id = subnet->get4o6().getInterfaceId();
         MySqlBindingPtr dhcp4o6_interface_id_binding;
@@ -856,12 +880,6 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::INSERT_SUBNET4,
                               in_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(ServerSelector::ANY(), subnet);
@@ -871,8 +889,21 @@ public:
             in_bindings.push_back(MySqlBinding::createString(subnet->toText()));
             conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_SUBNET4,
                                     in_bindings);
+
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createInteger<uint32_t>(subnet->getID())
+            };
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::DELETE_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()));
+
         // (Re)create pools.
         for (auto pool : subnet->getPools(Lease::TYPE_V4)) {
             createPool4(server_selector, boost::dynamic_pointer_cast<Pool4>(pool),
@@ -1503,9 +1534,6 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector,
-                                "creating or updating subnet level option");
-
         MySqlBindingCollection in_bindings = {
             MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
             createOptionValueBinding(option),
@@ -1519,7 +1547,6 @@ public:
             MySqlBinding::createNull(),
             MySqlBinding::createNull(),
             MySqlBinding::createTimestamp(option->getModificationTime()),
-            MySqlBinding::createString(tag),
             MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
             MySqlBinding::createInteger<uint8_t>(option->option_->getType()),
             MySqlBinding::condCreateString(option->space_name_)
@@ -1543,8 +1570,8 @@ public:
 
         if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
                                     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);
         }
 
@@ -2004,28 +2031,58 @@ TaggedStatementArray tagged_statements = { {
     },
 
     // Select subnet by id.
-    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_ID,
-      MYSQL_GET_SUBNET4(AND s.subnet_id = ?)
+    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_ID_NO_TAG,
+      MYSQL_GET_SUBNET4_NO_TAG(WHERE s.subnet_id = ?)
+    },
+
+    // Select subnet by id without specifying server tags.
+    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_ID_ANY,
+      MYSQL_GET_SUBNET4_ANY(WHERE s.subnet_id = ?)
+    },
+
+    // Select unassigned subnet by id.
+    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_ID_UNASSIGNED,
+      MYSQL_GET_SUBNET4_UNASSIGNED(AND s.subnet_id = ?)
     },
 
     // Select subnet by prefix.
-    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX,
-      MYSQL_GET_SUBNET4(AND s.subnet_prefix = ?)
+    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX_NO_TAG,
+      MYSQL_GET_SUBNET4_NO_TAG(WHERE s.subnet_prefix = ?)
+    },
+
+    // Select subnet by prefix without specifying server tags.
+    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX_ANY,
+      MYSQL_GET_SUBNET4_ANY(WHERE s.subnet_prefix = ?)
+    },
+
+    // Select unassigned subnet by prefix.
+    { MySqlConfigBackendDHCPv4Impl::GET_SUBNET4_PREFIX_UNASSIGNED,
+      MYSQL_GET_SUBNET4_UNASSIGNED(AND s.subnet_prefix = ?)
     },
 
     // Select all subnets.
     { MySqlConfigBackendDHCPv4Impl::GET_ALL_SUBNETS4,
-      MYSQL_GET_SUBNET4()
+      MYSQL_GET_SUBNET4_NO_TAG()
+    },
+
+    // Select all unassigned subnets.
+    { MySqlConfigBackendDHCPv4Impl::GET_ALL_SUBNETS4_UNASSIGNED,
+      MYSQL_GET_SUBNET4_UNASSIGNED()
     },
 
     // Select subnets having modification time later than X.
     { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_SUBNETS4,
-      MYSQL_GET_SUBNET4(AND s.modification_ts > ?)
+      MYSQL_GET_SUBNET4_NO_TAG(WHERE s.modification_ts > ?)
+    },
+
+    // Select modified and unassigned subnets.
+    { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_SUBNETS4_UNASSIGNED,
+      MYSQL_GET_SUBNET4_UNASSIGNED(AND s.modification_ts > ?)
     },
 
     // Select subnets belonging to a shared network.
     { MySqlConfigBackendDHCPv4Impl::GET_SHARED_NETWORK_SUBNETS4,
-      MYSQL_GET_SUBNET4(AND s.shared_network_name = ?)
+      MYSQL_GET_SUBNET4_NO_TAG(WHERE s.shared_network_name = ?)
     },
 
     // Select pool by address range.
@@ -2332,7 +2389,7 @@ TaggedStatementArray tagged_statements = { {
 
     // Update existing subnet level option.
     { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID,
-      MYSQL_UPDATE_OPTION4_WITH_TAG(AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
+      MYSQL_UPDATE_OPTION4_NO_TAG(o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?)
     },
 
     // Update existing pool level option.
@@ -2385,6 +2442,11 @@ TaggedStatementArray tagged_statements = { {
       MYSQL_DELETE_SUBNET(dhcp4, AND s.shared_network_name = ?)
     },
 
+    // Delete associations of a subnet with server.
+    { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_SERVER,
+      MYSQL_DELETE_SUBNET_SERVER(dhcp4),
+    },
+
     // Delete pools for a subnet.
     { MySqlConfigBackendDHCPv4Impl::DELETE_POOLS4_SUBNET_ID,
       MYSQL_DELETE_POOLS(dhcp4)
index c65e2cd8d8c3c9aa7d2dac597c05969085acd89d..53444d2e5d9efdee4d77ec994e01abc3c2f1c977 100644 (file)
@@ -61,10 +61,16 @@ public:
         GET_GLOBAL_PARAMETER6,
         GET_ALL_GLOBAL_PARAMETERS6,
         GET_MODIFIED_GLOBAL_PARAMETERS6,
-        GET_SUBNET6_ID,
-        GET_SUBNET6_PREFIX,
+        GET_SUBNET6_ID_NO_TAG,
+        GET_SUBNET6_ID_ANY,
+        GET_SUBNET6_ID_UNASSIGNED,
+        GET_SUBNET6_PREFIX_NO_TAG,
+        GET_SUBNET6_PREFIX_ANY,
+        GET_SUBNET6_PREFIX_UNASSIGNED,
         GET_ALL_SUBNETS6,
+        GET_ALL_SUBNETS6_UNASSIGNED,
         GET_MODIFIED_SUBNETS6,
+        GET_MODIFIED_SUBNETS6_UNASSIGNED,
         GET_SHARED_NETWORK_SUBNETS6,
         GET_POOL6_RANGE,
         GET_PD_POOL,
@@ -118,6 +124,7 @@ public:
         DELETE_SUBNET6_PREFIX,
         DELETE_ALL_SUBNETS6,
         DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME,
+        DELETE_SUBNET6_SERVER,
         DELETE_POOLS6_SUBNET_ID,
         DELETE_PD_POOLS_SUBNET_ID,
         DELETE_SHARED_NETWORK6_NAME_WITH_TAG,
@@ -237,6 +244,7 @@ public:
     /// Query should order subnets by subnet_id.
     ///
     /// @param index Index of the query to be used.
+    /// @param server_selector Server selector.
     /// @param in_bindings Input bindings specifying selection criteria. The
     /// size of the bindings collection must match the number of placeholders
     /// in the prepared statement. The input bindings collection must be empty
@@ -244,6 +252,7 @@ public:
     /// @param [out] subnets Reference to the container where fetched subnets
     /// will be inserted.
     void getSubnets6(const StatementIndex& index,
+                     const ServerSelector& server_selector,
                      const MySqlBindingCollection& in_bindings,
                      Subnet6Collection& subnets) {
         // Create output bindings. The order must match that in the prepared
@@ -330,16 +339,16 @@ public:
         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.
         conn_.selectQuery(index, in_bindings, out_bindings,
                           [this, &subnets, &last_pool,  &last_pd_pool,
                            &last_pool_id, &last_pd_pool_id,
                            &last_pool_option_id, &last_pd_pool_option_id,
-                           &last_option_id]
+                           &last_option_id, &last_tag]
                           (MySqlBindingCollection& out_bindings) {
             // Get pointer to the last subnet in the collection.
             Subnet6Ptr last_subnet;
@@ -358,6 +367,9 @@ public:
                 last_pool_id = 0;
                 last_pd_pool_id = 0;
 
+                // Reset last server tag as we're now starting to process new subnet.
+                last_tag.clear();
+
                 // subnet_id
                 SubnetID subnet_id(out_bindings[0]->getInteger<uint32_t>());
 
@@ -491,9 +503,6 @@ public:
 
                 // 71 and 72 are {min,max}_valid_lifetime
 
-                // server_tag
-                last_subnet->setServerTag(out_bindings[73]->getString());
-
                 // Subnet ready. Add it to the list.
                 auto ret = subnets.push_back(last_subnet);
 
@@ -505,6 +514,15 @@ public:
                 }
             }
 
+            // Check for new server tags.
+            if (!out_bindings[73]->amNull() &&
+                (last_tag != out_bindings[73]->getString())) {
+                last_tag = out_bindings[73]->getString();
+                if (!last_tag.empty() && !last_subnet->hasServerTag(ServerTag(last_tag))) {
+                    last_subnet->setServerTag(last_tag);
+                }
+            }
+
             // Pool is between 15 and 19
 
             // If the row contains information about the pool and it
@@ -573,8 +591,14 @@ public:
                     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<SubnetRandomAccessIndexTag>();
+        tossNonMatchingElements(server_selector, subnet_index);
     }
 
     /// @brief Sends query to retrieve single subnet by id.
@@ -586,18 +610,26 @@ public:
     /// doesn't exist.
     Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
                           const SubnetID& subnet_id) {
-        Subnet6Collection subnets;
+        if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a subnet. Got: "
+                      << getServerTagsAsText(server_selector));
+        }
 
-        auto tags = server_selector.getTags();
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createInteger<uint32_t>(subnet_id)
-            };
+        MySqlBindingCollection in_bindings = { MySqlBinding::createInteger<uint32_t>(subnet_id) };
 
-            getSubnets6(GET_SUBNET6_ID, in_bindings, subnets);
+        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());
     }
 
@@ -612,18 +644,26 @@ public:
     /// doesn't exist.
     Subnet6Ptr getSubnet6(const ServerSelector& server_selector,
                           const std::string& subnet_prefix) {
-        Subnet6Collection subnets;
+        if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a subnet. Got: "
+                      << getServerTagsAsText(server_selector));
+        }
 
-        auto tags = server_selector.getTags();
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createString(subnet_prefix)
-            };
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(subnet_prefix) };
+
+        auto index = GET_SUBNET6_PREFIX_NO_TAG;
+
+        if (server_selector.amUnassigned()) {
+            index = GET_SUBNET6_PREFIX_UNASSIGNED;
 
-            getSubnets6(GET_SUBNET6_PREFIX, in_bindings, subnets);
+        } 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());
     }
 
@@ -634,15 +674,10 @@ public:
     /// subnets should be inserted.
     void getAllSubnets6(const ServerSelector& server_selector,
                         Subnet6Collection& subnets) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get())
-            };
-
-            getSubnets6(GET_ALL_SUBNETS6, in_bindings, subnets);
-        }
+        auto index = (server_selector.amUnassigned() ? GET_ALL_SUBNETS6_UNASSIGNED :
+                      GET_ALL_SUBNETS6);
+        MySqlBindingCollection in_bindings;
+        getSubnets6(index, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve modified subnets.
@@ -654,16 +689,13 @@ public:
     void getModifiedSubnets6(const ServerSelector& server_selector,
                              const boost::posix_time::ptime& modification_ts,
                              Subnet6Collection& subnets) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createTimestamp(modification_ts)
-            };
+        MySqlBindingCollection in_bindings = {
+            MySqlBinding::createTimestamp(modification_ts)
+        };
 
-            getSubnets6(GET_MODIFIED_SUBNETS6, in_bindings, subnets);
-        }
+        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.
@@ -676,16 +708,8 @@ public:
     void getSharedNetworkSubnets6(const ServerSelector& server_selector,
                                   const std::string& shared_network_name,
                                   Subnet6Collection& subnets) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get()),
-                MySqlBinding::createString(shared_network_name)
-            };
-
-            getSubnets6(GET_SHARED_NETWORK_SUBNETS6, in_bindings, subnets);
-        }
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(shared_network_name) };
+        getSubnets6(GET_SHARED_NETWORK_SUBNETS6, server_selector, in_bindings, subnets);
     }
 
     /// @brief Sends query to retrieve multiple pools.
@@ -902,8 +926,6 @@ public:
                       " (unassigned) is unsupported at the moment");
         }
 
-        auto tag = getServerTag(server_selector, "creating or updating subnet");
-
         // Create JSON list of required classes.
         ElementPtr required_classes_element = Element::createList();
         const auto& required_classes = subnet->getRequiredClasses();
@@ -1004,12 +1026,6 @@ public:
             conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6,
                               in_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);
@@ -1020,8 +1036,20 @@ public:
             in_bindings.push_back(MySqlBinding::createString(subnet->toText()));
             conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6,
                                     in_bindings);
+
+            MySqlBindingCollection in_server_bindings = {
+                MySqlBinding::createInteger<uint32_t>(subnet->getID())
+            };
+            conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::DELETE_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()));
+
         // (Re)create pools.
         for (auto pool : subnet->getPools(Lease::TYPE_NA)) {
             createPool6(server_selector, boost::dynamic_pointer_cast<Pool6>(pool),
@@ -2328,28 +2356,58 @@ TaggedStatementArray tagged_statements = { {
     },
 
     // Select subnet by id.
-    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID,
-      MYSQL_GET_SUBNET6(AND s.subnet_id = ?)
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID_NO_TAG,
+      MYSQL_GET_SUBNET6_NO_TAG(WHERE s.subnet_id = ?)
+    },
+
+    // Select subnet by id without specifying server tags.
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID_ANY,
+      MYSQL_GET_SUBNET6_ANY(WHERE s.subnet_id = ?)
+    },
+
+    // Select unassigned subnet by id.
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID_UNASSIGNED,
+      MYSQL_GET_SUBNET6_UNASSIGNED(AND s.subnet_id = ?)
     },
 
     // Select subnet by prefix.
-    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX,
-      MYSQL_GET_SUBNET6(AND s.subnet_prefix = ?)
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX_NO_TAG,
+      MYSQL_GET_SUBNET6_NO_TAG(WHERE s.subnet_prefix = ?)
+    },
+
+    // Select subnet by prefix without specifying server tags.
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX_ANY,
+      MYSQL_GET_SUBNET6_ANY(WHERE s.subnet_prefix = ?)
+    },
+
+    // Select unassigned subnet by prefix.
+    { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX_UNASSIGNED,
+      MYSQL_GET_SUBNET6_UNASSIGNED(AND s.subnet_prefix = ?)
     },
 
     // Select all subnets.
     { MySqlConfigBackendDHCPv6Impl::GET_ALL_SUBNETS6,
-      MYSQL_GET_SUBNET6()
+      MYSQL_GET_SUBNET6_NO_TAG()
+    },
+
+    // Select all unassigned subnets.
+    { MySqlConfigBackendDHCPv6Impl::GET_ALL_SUBNETS6_UNASSIGNED,
+      MYSQL_GET_SUBNET6_UNASSIGNED()
     },
 
     // Select subnets having modification time later than X.
     { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SUBNETS6,
-      MYSQL_GET_SUBNET6(AND s.modification_ts > ?)
+      MYSQL_GET_SUBNET6_NO_TAG(WHERE s.modification_ts > ?)
+    },
+
+    // Select modified and unassigned subnets.
+    { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SUBNETS6_UNASSIGNED,
+      MYSQL_GET_SUBNET6_UNASSIGNED(AND s.modification_ts > ?)
     },
 
     // Select subnets belonging to a shared network.
     { MySqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK_SUBNETS6,
-      MYSQL_GET_SUBNET6(AND s.shared_network_name = ?)
+      MYSQL_GET_SUBNET6_NO_TAG(WHERE s.shared_network_name = ?)
     },
 
     // Select pool by address range.
@@ -2742,6 +2800,11 @@ TaggedStatementArray tagged_statements = { {
       MYSQL_DELETE_SUBNET(dhcp6, AND s.shared_network_name = ?)
     },
 
+    // Delete associations of a subnet with server.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_SERVER,
+      MYSQL_DELETE_SUBNET_SERVER(dhcp6),
+    },
+
     // Delete pools for a subnet.
     { MySqlConfigBackendDHCPv6Impl::DELETE_POOLS6_SUBNET_ID,
       MYSQL_DELETE_POOLS(dhcp6)
index d37860eedcbda0ea13b8b71b9d9174f6a6408bfc..f00cb778bd90c09dd002a7b0721898207c37f00d 100644 (file)
@@ -49,7 +49,7 @@ namespace {
 #endif
 
 #ifndef MYSQL_GET_SUBNET4
-#define MYSQL_GET_SUBNET4(...) \
+#define MYSQL_GET_SUBNET4_COMMON(server_join, ...) \
     "SELECT" \
     "  s.subnet_id," \
     "  s.subnet_prefix," \
@@ -108,19 +108,41 @@ namespace {
     "  s.max_valid_lifetime," \
     "  srv.tag " \
     "FROM dhcp4_subnet AS s " \
-    "INNER JOIN dhcp4_subnet_server AS a " \
-    "  ON s.subnet_id = a.subnet_id " \
-    "INNER JOIN dhcp4_server AS srv " \
-    "  ON (a.server_id = srv.id) OR (a.server_id = 1) " \
+    server_join \
     "LEFT JOIN dhcp4_pool AS p ON s.subnet_id = p.subnet_id " \
     "LEFT JOIN dhcp4_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \
     "LEFT JOIN dhcp4_options AS o ON o.scope_id = 1 AND s.subnet_id = o.dhcp4_subnet_id " \
-    "WHERE (srv.tag = ? OR srv.id = 1) " #__VA_ARGS__ \
+    #__VA_ARGS__ \
     " ORDER BY s.subnet_id, p.id, x.option_id, o.option_id"
+
+#define MYSQL_GET_SUBNET4_NO_TAG(...) \
+    MYSQL_GET_SUBNET4_COMMON( \
+    "INNER JOIN dhcp4_subnet_server AS a " \
+    "  ON s.subnet_id = a.subnet_id " \
+    "INNER JOIN dhcp4_server AS srv " \
+    "  ON (a.server_id = srv.id) ", \
+    __VA_ARGS__)
+
+#define MYSQL_GET_SUBNET4_ANY(...) \
+    MYSQL_GET_SUBNET4_COMMON( \
+    "LEFT JOIN dhcp4_subnet_server AS a "\
+    "  ON s.subnet_id = a.subnet_id " \
+    "LEFT JOIN dhcp4_server AS srv " \
+    "  ON a.server_id = srv.id ", \
+    __VA_ARGS__)
+
+#define MYSQL_GET_SUBNET4_UNASSIGNED(...) \
+    MYSQL_GET_SUBNET4_COMMON( \
+    "LEFT JOIN dhcp4_subnet_server AS a "\
+    "  ON s.subnet_id = a.subnet_id " \
+    "LEFT JOIN dhcp4_server AS srv " \
+    "  ON a.server_id = srv.id ", \
+    WHERE a.subnet_id IS NULL __VA_ARGS__)
+
 #endif
 
 #ifndef MYSQL_GET_SUBNET6
-#define MYSQL_GET_SUBNET6(...) \
+#define MYSQL_GET_SUBNET6_COMMON(server_join, ...) \
     "SELECT" \
     "  s.subnet_id," \
     "  s.subnet_prefix," \
@@ -197,17 +219,39 @@ namespace {
     "  s.max_valid_lifetime," \
     "  srv.tag " \
     "FROM dhcp6_subnet AS s " \
-    "INNER JOIN dhcp6_subnet_server AS a " \
-    "  ON s.subnet_id = a.subnet_id " \
-    "INNER JOIN dhcp6_server AS srv " \
-    "  ON (a.server_id = srv.id) OR (a.server_id = 1) " \
+    server_join \
     "LEFT JOIN dhcp6_pool AS p ON s.subnet_id = p.subnet_id " \
     "LEFT JOIN dhcp6_pd_pool AS d ON s.subnet_id = d.subnet_id " \
     "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " \
     "LEFT JOIN dhcp6_options AS y ON y.scope_id = 6 AND d.id = y.pd_pool_id " \
     "LEFT JOIN dhcp6_options AS o ON o.scope_id = 1 AND s.subnet_id = o.dhcp6_subnet_id " \
-    "WHERE (srv.tag = ? OR srv.id = 1) " #__VA_ARGS__ \
+    #__VA_ARGS__                                                        \
     " ORDER BY s.subnet_id, p.id, d.id, x.option_id, o.option_id"
+
+#define MYSQL_GET_SUBNET6_NO_TAG(...) \
+    MYSQL_GET_SUBNET6_COMMON( \
+    "INNER JOIN dhcp6_subnet_server AS a " \
+    "  ON s.subnet_id = a.subnet_id " \
+    "INNER JOIN dhcp6_server AS srv " \
+    "  ON (a.server_id = srv.id) ", \
+    __VA_ARGS__)
+
+#define MYSQL_GET_SUBNET6_ANY(...) \
+    MYSQL_GET_SUBNET6_COMMON( \
+    "LEFT JOIN dhcp6_subnet_server AS a "\
+    "  ON s.subnet_id = a.subnet_id " \
+    "LEFT JOIN dhcp6_server AS srv " \
+    "  ON a.server_id = srv.id ", \
+    __VA_ARGS__)
+
+#define MYSQL_GET_SUBNET6_UNASSIGNED(...) \
+    MYSQL_GET_SUBNET6_COMMON( \
+    "LEFT JOIN dhcp6_subnet_server AS a "\
+    "  ON s.subnet_id = a.subnet_id " \
+    "LEFT JOIN dhcp6_server AS srv " \
+    "  ON a.server_id = srv.id ", \
+    WHERE a.subnet_id IS NULL __VA_ARGS__)
+
 #endif
 
 #ifndef MYSQL_GET_SHARED_NETWORK4
@@ -676,6 +720,12 @@ namespace {
     "WHERE srv.tag = ? " #__VA_ARGS__
 #endif
 
+#ifndef MYSQL_DELETE_SUBNET_SERVER
+#define MYSQL_DELETE_SUBNET_SERVER(table_prefix) \
+    "DELETE FROM " #table_prefix "_subnet_server " \
+    "WHERE subnet_id = ?"
+#endif
+
 #ifndef MYSQL_DELETE_POOLS
 #define MYSQL_DELETE_POOLS(table_prefix) \
     "DELETE FROM " #table_prefix "_pool " \
index ee96023ddb6f1dcdf352379adbca1efafac7849d..4f793a26a9d339024fc2f7d90fabaa7a647966fb 100644 (file)
@@ -1005,21 +1005,20 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedGlobalParameters4) {
 
 // Test that subnet can be inserted, fetched, updated and then fetched again.
 TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
-    // Insert new subnet.
-    Subnet4Ptr subnet = test_subnets_[0];
-    cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet);
-
-    // Fetch this subnet by subnet identifier.
-    Subnet4Ptr returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
-                                                    test_subnets_[0]->getID());
-    ASSERT_TRUE(returned_subnet);
-    ASSERT_EQ(1, returned_subnet->getServerTags().size());
-    EXPECT_EQ("all", returned_subnet->getServerTags().begin()->get());
+    // 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");
+    }
 
-    // The easiest way to verify whether the returned subnet matches the inserted
-    // subnet is to convert both to text.
-    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+    auto subnet = test_subnets_[0];
+    auto subnet2 = test_subnets_[2];
 
+    // Insert two subnets, one for all servers and one for server2.
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet));
     {
         SCOPED_TRACE("CREATE audit entry for the subnet");
         testNewAuditEntry("dhcp4_subnet",
@@ -1027,44 +1026,97 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
                           "subnet set");
     }
 
-    // Update the subnet in the database (both use the same ID).
-    Subnet4Ptr subnet2 = test_subnets_[1];
-    cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet2);
 
-    // Fetch updated subnet and see if it matches.
-    returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
-                                         SubnetID(1024));
-    EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ONE("server2"), subnet2));
+    {
+        SCOPED_TRACE("CREATE audit entry for the subnet");
+        testNewAuditEntry("dhcp4_subnet",
+                          AuditEntry::ModificationType::CREATE,
+                          "subnet set", ServerSelector::ONE("subnet2"),
+                          2, 1);
+    }
 
-    // Fetching the subnet for an explicitly specified server tag should
-    // succeed too.
-    returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server1"),
-                                         SubnetID(1024));
-    EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
+    // We are not going to support selection of a single entry for multiple servers.
+    EXPECT_THROW(cbptr_->getSubnet4(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                    subnet->getID()),
+                 isc::InvalidOperation);
+
+    EXPECT_THROW(cbptr_->getSubnet4(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                    subnet->toText()),
+                 isc::InvalidOperation);
+
+    // Test that this subnet will be fetched for various server selectors.
+    auto test_get_subnet = [this, &subnet] (const std::string& test_case_name,
+                                            const ServerSelector& server_selector,
+                                            const std::string& expected_tag = ServerTag::ALL) {
+        SCOPED_TRACE(test_case_name);
+
+        // Test fetching subnet by id.
+        Subnet4Ptr returned_subnet;
+        ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet4(server_selector, subnet->getID()));
+        ASSERT_TRUE(returned_subnet);
+
+        ASSERT_EQ(1, returned_subnet->getServerTags().size());
+        EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
+
+        EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+
+        // Test fetching subnet by prefix.
+        ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet4(server_selector,
+                                                             subnet->toText()));
+        ASSERT_TRUE(returned_subnet);
+
+        ASSERT_EQ(1, returned_subnet->getServerTags().size());
+        EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
+
+        EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+    };
 
     {
-        SCOPED_TRACE("UPDATE audit entry for the subnet");
+        SCOPED_TRACE("testing various server selectors before update");
+        test_get_subnet("all servers", ServerSelector::ALL());
+        test_get_subnet("one server", ServerSelector::ONE("server1"));
+        test_get_subnet("any server", ServerSelector::ANY());
+    }
+
+    subnet = subnet2;
+    {
+        SCOPED_TRACE("testing server selectors for another server");
+        test_get_subnet("one server", ServerSelector::ONE("server2"), "server2");
+        test_get_subnet("any server", ServerSelector::ANY(), "server2");
+    }
+
+    // Update the subnet in the database (both use the same ID).
+    subnet = test_subnets_[1];
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet));
+    {
+        SCOPED_TRACE("CREATE audit entry for the subnet");
         testNewAuditEntry("dhcp4_subnet",
                           AuditEntry::ModificationType::UPDATE,
                           "subnet set");
     }
 
-    // Insert another subnet.
-    cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[2]);
+    {
+        SCOPED_TRACE("testing various server selectors after update");
+        test_get_subnet("all servers", ServerSelector::ALL());
+        test_get_subnet("one server", ServerSelector::ONE("server1"));
+        test_get_subnet("any server", ServerSelector::ANY());
+    }
 
-    // Fetch this subnet by prefix and verify it matches.
-    returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
-                                         test_subnets_[2]->toText());
-    ASSERT_TRUE(returned_subnet);
-    EXPECT_EQ(test_subnets_[2]->toElement()->str(), returned_subnet->toElement()->str());
+    // The server2 specific subnet should not be returned if the server selector
+    // is not matching.
+    EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ALL(), subnet2->getID()));
+    EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ALL(), subnet2->toText()));
+    EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ONE("server1"), subnet2->getID()));
+    EXPECT_FALSE(cbptr_->getSubnet4(ServerSelector::ONE("server1"), subnet2->toText()));
 
     // Update the subnet in the database (both use the same prefix).
     subnet2.reset(new Subnet4(IOAddress("192.0.3.0"), 24, 30, 40, 60, 8192));
-    cbptr_->createUpdateSubnet4(ServerSelector::ALL(),  subnet2);
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ONE("server2"),  subnet2));
 
     // Fetch again and verify.
-    returned_subnet = cbptr_->getSubnet4(ServerSelector::ALL(),
-                                         test_subnets_[2]->toText());
+    auto returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server2"),
+                                              subnet2->toText());
     ASSERT_TRUE(returned_subnet);
     EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
 
@@ -1072,7 +1124,7 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSubnet4) {
     // with different subnets. This should throw.
     // Subnets are 10.0.0.0/8 id 1024 and 192.0.3.0/24 id 8192
     subnet2.reset(new Subnet4(IOAddress("10.0.0.0"), 8, 30, 40, 60, 8192));
-    EXPECT_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(),  subnet2),
+    EXPECT_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ONE("server2"),  subnet2),
                  DuplicateEntry);
 }
 
index b1be84ac5a05068416c212f4874e52ac605318a0..fbd186e31d3c1f4dbff89641ad71491ade254157 100644 (file)
@@ -1037,21 +1037,20 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedGlobalParameters6) {
 
 // Test that subnet can be inserted, fetched, updated and then fetched again.
 TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
-    // Insert new subnet.
-    Subnet6Ptr subnet = test_subnets_[0];
-    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet);
-
-    // Fetch this subnet by subnet identifier.
-    Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
-                                                    test_subnets_[0]->getID());
-    ASSERT_TRUE(returned_subnet);
-    ASSERT_EQ(1, returned_subnet->getServerTags().size());
-    EXPECT_EQ("all", returned_subnet->getServerTags().begin()->get());
+    // 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");
+    }
 
-    // The easiest way to verify whether the returned subnet matches the inserted
-    // subnet is to convert both to text.
-    EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+    auto subnet = test_subnets_[0];
+    auto subnet2 = test_subnets_[2];
 
+    // Insert two subnets, one for all servers and one for server2.
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
     {
         SCOPED_TRACE("CREATE audit entry for the subnet");
         testNewAuditEntry("dhcp6_subnet",
@@ -1059,45 +1058,98 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
                           "subnet set");
     }
 
-    // Update the subnet in the database (both use the same ID).
-    Subnet6Ptr subnet2 = test_subnets_[1];
-    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet2);
 
-    // Fetch updated subnet and see if it matches.
-    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
-                                         SubnetID(1024));
-    EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2));
+    {
+        SCOPED_TRACE("CREATE audit entry for the subnet");
+        testNewAuditEntry("dhcp6_subnet",
+                          AuditEntry::ModificationType::CREATE,
+                          "subnet set", ServerSelector::ONE("subnet2"),
+                          2, 1);
+    }
 
-    // Fetching the subnet for an explicitly specified server tag should
-    // succeed too.
-    returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
-                                         SubnetID(1024));
-    EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
+    // We are not going to support selection of a single entry for multiple servers.
+    EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                    subnet->getID()),
+                 isc::InvalidOperation);
+
+    EXPECT_THROW(cbptr_->getSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                    subnet->toText()),
+                 isc::InvalidOperation);
+
+    // Test that this subnet will be fetched for various server selectors.
+    auto test_get_subnet = [this, &subnet] (const std::string& test_case_name,
+                                            const ServerSelector& server_selector,
+                                            const std::string& expected_tag = ServerTag::ALL) {
+        SCOPED_TRACE(test_case_name);
+
+        // Test fetching subnet by id.
+        Subnet6Ptr returned_subnet;
+        ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet6(server_selector, subnet->getID()));
+        ASSERT_TRUE(returned_subnet);
+
+        ASSERT_EQ(1, returned_subnet->getServerTags().size());
+        EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
+
+        EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+
+        // Test fetching subnet by prefix.
+        ASSERT_NO_THROW(returned_subnet = cbptr_->getSubnet6(server_selector,
+                                                             subnet->toText()));
+        ASSERT_TRUE(returned_subnet);
+
+        ASSERT_EQ(1, returned_subnet->getServerTags().size());
+        EXPECT_TRUE(returned_subnet->hasServerTag(ServerTag(expected_tag)));
+
+        EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str());
+    };
 
     {
-        SCOPED_TRACE("UPDATE audit entry for the subnet");
+        SCOPED_TRACE("testing various server selectors before update");
+        test_get_subnet("all servers", ServerSelector::ALL());
+        test_get_subnet("one server", ServerSelector::ONE("server1"));
+        test_get_subnet("any server", ServerSelector::ANY());
+    }
+
+    subnet = subnet2;
+    {
+        SCOPED_TRACE("testing server selectors for another server");
+        test_get_subnet("one server", ServerSelector::ONE("server2"), "server2");
+        test_get_subnet("any server", ServerSelector::ANY(), "server2");
+    }
+
+    // Update the subnet in the database (both use the same ID).
+    subnet = test_subnets_[1];
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet));
+    {
+        SCOPED_TRACE("CREATE audit entry for the subnet");
         testNewAuditEntry("dhcp6_subnet",
                           AuditEntry::ModificationType::UPDATE,
                           "subnet set");
     }
 
-    // Insert another subnet.
-    cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[2]);
+    {
+        SCOPED_TRACE("testing various server selectors after update");
+        test_get_subnet("all servers", ServerSelector::ALL());
+        test_get_subnet("one server", ServerSelector::ONE("server1"));
+        test_get_subnet("any server", ServerSelector::ANY());
+    }
 
-    // Fetch this subnet by prefix and verify it matches.
-    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
-                                         test_subnets_[2]->toText());
-    ASSERT_TRUE(returned_subnet);
-    EXPECT_EQ(test_subnets_[2]->toElement()->str(), returned_subnet->toElement()->str());
+    // The server2 specific subnet should not be returned if the server selector
+    // is not matching.
+    EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ALL(), subnet2->getID()));
+    EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ALL(), subnet2->toText()));
+    EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ONE("server1"), subnet2->getID()));
+    EXPECT_FALSE(cbptr_->getSubnet6(ServerSelector::ONE("server1"), subnet2->toText()));
 
     // Update the subnet in the database (both use the same prefix).
     subnet2.reset(new Subnet6(IOAddress("2001:db8:3::"),
                               64, 30, 40, 50, 80, 8192));
-    cbptr_->createUpdateSubnet6(ServerSelector::ALL(),  subnet2);
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"),  subnet2));
 
     // Fetch again and verify.
-    returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(),
-                                         test_subnets_[2]->toText());
+    auto returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server2"),
+                                              subnet2->toText());
     ASSERT_TRUE(returned_subnet);
     EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str());
 
@@ -1106,7 +1158,7 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6) {
     // 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::ALL(),  subnet2),
+    EXPECT_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"),  subnet2),
                  DuplicateEntry);
 }