]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#716,!412] Multiple tags are returned with shared networks.
authorMarcin Siodelski <marcin@isc.org>
Mon, 8 Jul 2019 13:10:27 +0000 (15:10 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 9 Jul 2019 18:37:29 +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.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
src/lib/dhcpsrv/shared_network.h
src/lib/dhcpsrv/tests/shared_network_unittest.cc

index 1d532f6d80cc244c96f3e0c9e6133561c090b9e6..1d2d932e7baa963dd7f90368b4762312d05fda7b 100644 (file)
@@ -66,7 +66,6 @@ public:
         GET_MODIFIED_SUBNETS4,
         GET_SHARED_NETWORK_SUBNETS4,
         GET_POOL4_RANGE,
-        GET_SHARED_NETWORK4_NAME_WITH_TAG,
         GET_SHARED_NETWORK4_NAME_NO_TAG,
         GET_ALL_SHARED_NETWORKS4,
         GET_MODIFIED_SHARED_NETWORKS4,
@@ -995,6 +994,7 @@ public:
     /// Query should order shared networks by 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
@@ -1002,6 +1002,7 @@ public:
     /// @param [out] shared_networks Reference to the container where fetched
     /// shared networks will be inserted.
     void getSharedNetworks4(const StatementIndex& index,
+                            const ServerSelector& server_selector,
                             const MySqlBindingCollection& in_bindings,
                             SharedNetwork4Collection& shared_networks) {
         // Create output bindings. The order must match that in the prepared
@@ -1218,6 +1219,13 @@ public:
                 }
             }
         });
+
+        // Now that we're done fetching the whole network, we have to
+        // check if it has matching server tags and toss it if it
+        // doesn't. We skip matching the server tags if we're asking
+        // for ANY shared network.
+        auto& sn_index = shared_networks.get<SharedNetworkRandomAccessIndexTag>();
+        tossNonMatchingElements(server_selector, sn_index);
     }
 
     /// @brief Sends query to retrieve single shared network by name.
@@ -1233,22 +1241,19 @@ public:
         if (server_selector.amUnassigned()) {
             isc_throw(NotImplemented, "managing configuration for no particular server"
                       " (unassigned) is unsupported at the moment");
-        }
-
-        MySqlBindingCollection in_bindings;
-        auto index = GET_SHARED_NETWORK4_NAME_NO_TAG;
 
-        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;
+        } else if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a shared network. Got: "
+                      << getServerTagsAsText(server_selector));
         }
 
-        in_bindings.push_back(MySqlBinding::createString(name));
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(name) };
+        auto index = GET_SHARED_NETWORK4_NAME_NO_TAG;
 
         SharedNetwork4Collection shared_networks;
-        getSharedNetworks4(index, in_bindings, shared_networks);
+        getSharedNetworks4(GET_SHARED_NETWORK4_NAME_NO_TAG, server_selector, in_bindings,
+                           shared_networks);
 
         return (shared_networks.empty() ? SharedNetwork4Ptr() : *shared_networks.begin());
     }
@@ -1260,15 +1265,9 @@ public:
     /// structure where shared networks should be inserted.
     void getAllSharedNetworks4(const ServerSelector& server_selector,
                                SharedNetwork4Collection& shared_networks) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get())
-            };
-
-            getSharedNetworks4(GET_ALL_SHARED_NETWORKS4, in_bindings, shared_networks);
-        }
+        MySqlBindingCollection in_bindings;
+        getSharedNetworks4(GET_ALL_SHARED_NETWORKS4, server_selector, in_bindings,
+                           shared_networks);
     }
 
     /// @brief Sends query to retrieve modified shared networks.
@@ -1280,17 +1279,12 @@ public:
     void getModifiedSharedNetworks4(const ServerSelector& server_selector,
                                     const boost::posix_time::ptime& modification_ts,
                                     SharedNetwork4Collection& shared_networks) {
-        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)
+        };
 
-            getSharedNetworks4(GET_MODIFIED_SHARED_NETWORKS4, in_bindings,
-                               shared_networks);
-        }
+        getSharedNetworks4(GET_MODIFIED_SHARED_NETWORKS4, server_selector, in_bindings,
+                           shared_networks);
     }
 
     /// @brief Sends query to insert or update shared network.
@@ -2030,11 +2024,6 @@ TaggedStatementArray tagged_statements = { {
       "ORDER BY p.id, x.option_id"
     },
 
-    // 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 = ?)
@@ -2042,12 +2031,12 @@ TaggedStatementArray tagged_statements = { {
 
     // Select all shared networks.
     { MySqlConfigBackendDHCPv4Impl::GET_ALL_SHARED_NETWORKS4,
-      MYSQL_GET_SHARED_NETWORK4_WITH_TAG()
+      MYSQL_GET_SHARED_NETWORK4_NO_TAG()
     },
 
     // Select modified shared networks.
     { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_SHARED_NETWORKS4,
-      MYSQL_GET_SHARED_NETWORK4_WITH_TAG(AND n.modification_ts > ?)
+      MYSQL_GET_SHARED_NETWORK4_NO_TAG(WHERE n.modification_ts > ?)
     },
 
     // Retrieves option definition by code and space.
index de1503ce1d951408d182e3ca233a0aaf29200220..f7412ad2526c9bf92101650ee0c706d442cf6604 100644 (file)
@@ -68,7 +68,6 @@ public:
         GET_SHARED_NETWORK_SUBNETS6,
         GET_POOL6_RANGE,
         GET_PD_POOL,
-        GET_SHARED_NETWORK6_NAME_WITH_TAG,
         GET_SHARED_NETWORK6_NAME_NO_TAG,
         GET_ALL_SHARED_NETWORKS6,
         GET_MODIFIED_SHARED_NETWORKS6,
@@ -1197,6 +1196,7 @@ public:
     /// Query should order shared networks by 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
@@ -1204,6 +1204,7 @@ public:
     /// @param [out] shared_networks Reference to the container where fetched
     /// shared networks will be inserted.
     void getSharedNetworks6(const StatementIndex& index,
+                            const ServerSelector& server_selector,
                             const MySqlBindingCollection& in_bindings,
                             SharedNetwork6Collection& shared_networks) {
         // Create output bindings. The order must match that in the prepared
@@ -1420,6 +1421,13 @@ public:
                 }
             }
         });
+
+        // Now that we're done fetching the whole network, we have to
+        // check if it has matching server tags and toss it if it
+        // doesn't. We skip matching the server tags if we're asking
+        // for ANY shared network.
+        auto& sn_index = shared_networks.get<SharedNetworkRandomAccessIndexTag>();
+        tossNonMatchingElements(server_selector, sn_index);
     }
 
     /// @brief Sends query to retrieve single shared network by name.
@@ -1435,22 +1443,18 @@ public:
         if (server_selector.amUnassigned()) {
             isc_throw(NotImplemented, "managing configuration for no particular server"
                       " (unassigned) is unsupported at the moment");
-        }
-
-        MySqlBindingCollection in_bindings;
-        auto index = GET_SHARED_NETWORK6_NAME_NO_TAG;
 
-        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;
+        } else if (server_selector.hasMultipleTags()) {
+            isc_throw(InvalidOperation, "expected one server tag to be specified"
+                      " while fetching a shared network. Got: "
+                      << getServerTagsAsText(server_selector));
         }
 
-        in_bindings.push_back(MySqlBinding::createString(name));
+        MySqlBindingCollection in_bindings = { MySqlBinding::createString(name) };
+        auto index = GET_SHARED_NETWORK6_NAME_NO_TAG;
 
         SharedNetwork6Collection shared_networks;
-        getSharedNetworks6(index, in_bindings, shared_networks);
+        getSharedNetworks6(index, server_selector, in_bindings, shared_networks);
 
         return (shared_networks.empty() ? SharedNetwork6Ptr() : *shared_networks.begin());
     }
@@ -1462,15 +1466,9 @@ public:
     /// structure where shared networks should be inserted.
     void getAllSharedNetworks6(const ServerSelector& server_selector,
                                SharedNetwork6Collection& shared_networks) {
-        auto tags = server_selector.getTags();
-
-        for (auto tag : tags) {
-            MySqlBindingCollection in_bindings = {
-                MySqlBinding::createString(tag.get())
-            };
-
-            getSharedNetworks6(GET_ALL_SHARED_NETWORKS6, in_bindings, shared_networks);
-        }
+        MySqlBindingCollection in_bindings;
+        getSharedNetworks6(GET_ALL_SHARED_NETWORKS6, server_selector, in_bindings,
+                           shared_networks);
     }
 
     /// @brief Sends query to retrieve modified shared networks.
@@ -1482,17 +1480,12 @@ public:
     void getModifiedSharedNetworks6(const ServerSelector& server_selector,
                                     const boost::posix_time::ptime& modification_ts,
                                     SharedNetwork6Collection& shared_networks) {
-        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)
+        };
 
-            getSharedNetworks6(GET_MODIFIED_SHARED_NETWORKS6, in_bindings,
-                               shared_networks);
-        }
+        getSharedNetworks6(GET_MODIFIED_SHARED_NETWORKS6, server_selector, in_bindings,
+                           shared_networks);
     }
 
     /// @brief Sends query to insert or update shared network.
@@ -2382,11 +2375,6 @@ TaggedStatementArray tagged_statements = { {
       "ORDER BY p.id, x.option_id"
     },
 
-    // Select shared network by 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 = ?)
@@ -2394,12 +2382,12 @@ TaggedStatementArray tagged_statements = { {
 
     // Select all shared networks.
     { MySqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6,
-      MYSQL_GET_SHARED_NETWORK6_WITH_TAG()
+      MYSQL_GET_SHARED_NETWORK6_NO_TAG()
     },
 
     // Select modified shared networks.
     { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6,
-      MYSQL_GET_SHARED_NETWORK6_WITH_TAG(AND n.modification_ts > ?)
+      MYSQL_GET_SHARED_NETWORK6_NO_TAG(WHERE n.modification_ts > ?)
     },
 
     // Retrieves option definition by code and space.
index 64ddd45fb64389df293d2c0d5009523b9b35ae6e..a8749582da38674f314b145cc4a297ead2953fe6 100644 (file)
@@ -669,6 +669,71 @@ public:
         }
     }
 
+    /// @brief Removes configuration elements from the index which don't match
+    /// the specified server selector.
+    ///
+    /// This is a generic function which removes configuration elements which
+    /// don't match the specified selector. In order to fetch all server tags
+    /// for the returned configuration element, the query must not limit the
+    /// results to the given server tag. Instead, it must post process the
+    /// result to eliminate those configuration elements for which the desired
+    /// server tag wasn't found.
+    ///
+    /// If the server selector is set to ANY, this method is no-op.
+    ///
+    /// @tparam CollectionIndex Type of the collection to be processed.
+    /// @param server_selector Server selector.
+    /// @param index Reference to the index holding the returned configuration
+    /// elements to be processed.
+    template<typename CollectionIndex>
+    void tossNonMatchingElements(const db::ServerSelector& server_selector,
+                                 CollectionIndex& index) {
+        // Don't filter the matching server tags if the server selector is
+        // set to ANY.
+        if (server_selector.amAny()) {
+            return;
+        }
+
+        // Go over the collection of elements.
+        for (auto elem = index.begin(); elem != index.end(); ) {
+
+            // If we're asking for shared networks matching all servers,
+            // we have to make sure that the fetched element has "all"
+            // server tag.
+            if (server_selector.amAll()) {
+                if (!(*elem)->hasAllServerTag()) {
+                    // It doesn't so let's remove it.
+                    elem = index.erase(elem);
+                    continue;
+                }
+
+            } else {
+                // Server selector contains explicit server tags, so
+                // let's see if the returned elements includes any of
+                // them.
+                auto tags = server_selector.getTags();
+                bool tag_found = false;
+                for (auto tag : tags) {
+                    if ((*elem)->hasServerTag(tag) ||
+                        (*elem)->hasAllServerTag()) {
+                        tag_found = true;
+                        break;
+                    }
+                }
+                if (!tag_found) {
+                    // Tag not matching, so toss the element.
+                    elem = index.erase(elem);
+                    continue;
+                }
+            }
+
+            // Go to the next element if we didn't toss the current one.
+            // Otherwise, the erase() function should have already taken
+            // us to the next one.
+            ++elem;
+        }
+    }
+
     /// @brief Returns backend type in the textual format.
     ///
     /// @return "mysql".
index 9f5dc3fe838fd27b0be3ad125659303ee11880bc..a5e5e1158b9f26a4bb3a9d3e1208ce9b6556b8ae 100644 (file)
@@ -257,9 +257,6 @@ namespace {
     #__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__)
 
@@ -313,9 +310,6 @@ namespace {
     #__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__)
 
index d8db0ebd8f8ca58f59a2087725514900082d540d..5abd2c4982e8b765b1732992bb2b8601afe94920 100644 (file)
@@ -1764,6 +1764,78 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllSharedNetworks4) {
     EXPECT_TRUE(subnet->getSharedNetworkName().empty());
 }
 
+// Test that shared networks with different server associations are returned.
+TEST_F(MySqlConfigBackendDHCPv4Test, getAllSharedNetworks4WithServerTags) {
+    auto shared_network1 = test_networks_[0];
+    auto shared_network2 = test_networks_[2];
+    auto shared_network3 = test_networks_[3];
+
+    EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[0]));
+    EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[2]));
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(),
+                                                       shared_network1));
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::ONE("server1"),
+                                                       shared_network2));
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork4(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                                       shared_network3));
+
+    SharedNetwork4Collection networks;
+
+    // All three networks are associated with the server1.
+    EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks4(ServerSelector::ONE("server1")));
+    EXPECT_EQ(3, networks.size());
+
+    // First network is associated with all servers.
+    auto returned_network = SharedNetworkFetcher4::get(networks, "level1");
+    ASSERT_TRUE(returned_network);
+    EXPECT_TRUE(returned_network->hasAllServerTag());
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Second network is only associated with the server1.
+    returned_network = SharedNetworkFetcher4::get(networks, "level2");
+    ASSERT_TRUE(returned_network);
+    EXPECT_FALSE(returned_network->hasAllServerTag());
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Third network is associated with both server1 and server2.
+    returned_network = SharedNetworkFetcher4::get(networks, "level3");
+    ASSERT_TRUE(returned_network);
+    EXPECT_FALSE(returned_network->hasAllServerTag());
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // For server2 we should only get two shared networks, i.e. first and last.
+    EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks4(ServerSelector::ONE("server2")));
+    EXPECT_EQ(2, networks.size());
+
+    // First shared network is associated with all servers.
+    returned_network = SharedNetworkFetcher4::get(networks, "level1");
+    ASSERT_TRUE(returned_network);
+    EXPECT_TRUE(returned_network->hasAllServerTag());
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Last shared network is associated with server1 and server2.
+    returned_network = SharedNetworkFetcher4::get(networks, "level3");
+    ASSERT_TRUE(returned_network);
+    EXPECT_FALSE(returned_network->hasAllServerTag());
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Only the first shared network is associated with all servers.
+    EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks4(ServerSelector::ALL()));
+    EXPECT_EQ(1, networks.size());
+
+    returned_network = SharedNetworkFetcher4::get(networks, "level1");
+    ASSERT_TRUE(returned_network);
+    EXPECT_TRUE(returned_network->hasAllServerTag());
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+}
+
 // Test that shared networks modified after given time can be fetched.
 TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedSharedNetworks4) {
     // Explicitly set timestamps of shared networks. First shared
@@ -1882,13 +1954,12 @@ TEST_F(MySqlConfigBackendDHCPv4Test, deleteSharedNetwork4) {
         EXPECT_EQ(1, deleted_count);
 
         EXPECT_FALSE(cbptr_->getSharedNetwork4(server_selector,
-                                                  shared_network->getName()));
+                                               shared_network->getName()));
     };
 
     test_delete("all servers", ServerSelector::ALL(), shared_network1);
     test_delete("any server", ServerSelector::ANY(), shared_network2);
     test_delete("one server", ServerSelector::ONE("server1"), shared_network3);
-
 }
 
 // Test that lifetimes in shared networks are handled as expected.
index ed91ce5b11493a13bfd5a30a04dad4e59494815d..a9af75113be8a0120eac36bb8074ca797d5a0a90 100644 (file)
@@ -1779,6 +1779,78 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getAllSharedNetworks6) {
     EXPECT_TRUE(subnet->getSharedNetworkName().empty());
 }
 
+// Test that shared networks with different server associations are returned.
+TEST_F(MySqlConfigBackendDHCPv6Test, getAllSharedNetworks6WithServerTags) {
+    auto shared_network1 = test_networks_[0];
+    auto shared_network2 = test_networks_[2];
+    auto shared_network3 = test_networks_[3];
+
+    EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
+    EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2]));
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(),
+                                                       shared_network1));
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::ONE("server1"),
+                                                       shared_network2));
+    EXPECT_NO_THROW(cbptr_->createUpdateSharedNetwork6(ServerSelector::MULTIPLE({ "server1", "server2" }),
+                                                       shared_network3));
+
+    SharedNetwork6Collection networks;
+
+    // All three networks are associated with the server1.
+    EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server1")));
+    EXPECT_EQ(3, networks.size());
+
+    // First network is associated with all servers.
+    auto returned_network = SharedNetworkFetcher6::get(networks, "level1");
+    ASSERT_TRUE(returned_network);
+    EXPECT_TRUE(returned_network->hasAllServerTag());
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Second network is only associated with the server1.
+    returned_network = SharedNetworkFetcher6::get(networks, "level2");
+    ASSERT_TRUE(returned_network);
+    EXPECT_FALSE(returned_network->hasAllServerTag());
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Third network is associated with both server1 and server2.
+    returned_network = SharedNetworkFetcher6::get(networks, "level3");
+    ASSERT_TRUE(returned_network);
+    EXPECT_FALSE(returned_network->hasAllServerTag());
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // For server2 we should only get two shared networks, i.e. first and last.
+    EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server2")));
+    EXPECT_EQ(2, networks.size());
+
+    // First shared network is associated with all servers.
+    returned_network = SharedNetworkFetcher6::get(networks, "level1");
+    ASSERT_TRUE(returned_network);
+    EXPECT_TRUE(returned_network->hasAllServerTag());
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Last shared network is associated with server1 and server2.
+    returned_network = SharedNetworkFetcher6::get(networks, "level3");
+    ASSERT_TRUE(returned_network);
+    EXPECT_FALSE(returned_network->hasAllServerTag());
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_TRUE(returned_network->hasServerTag(ServerTag("server2")));
+
+    // Only the first shared network is associated with all servers.
+    EXPECT_NO_THROW(networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL()));
+    EXPECT_EQ(1, networks.size());
+
+    returned_network = SharedNetworkFetcher6::get(networks, "level1");
+    ASSERT_TRUE(returned_network);
+    EXPECT_TRUE(returned_network->hasAllServerTag());
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server1")));
+    EXPECT_FALSE(returned_network->hasServerTag(ServerTag("server2")));
+}
+
 // Test that shared networks modified after given time can be fetched.
 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSharedNetworks6) {
     // Explicitly set timestamps of shared networks. First shared
index 205bfdcd091d6471e48102f0bf35637c2d282571..39ff19d8c4c36e922af50b4cb977c491e4c599c1 100644 (file)
@@ -106,6 +106,13 @@ public:
         return (getType() == Type::UNASSIGNED);
     }
 
+    /// @brief Convenience method checking if the server selector is "all".
+    ///
+    /// @return true if the selector is "all", false otherwise.
+    bool amAll() const {
+        return (getType() == Type::ALL);
+    }
+
     /// @brief Convenience method checking if the server selector is "any".
     ///
     /// @return true if the selector is "any", false otherwise.
index b3b530db99e220ae7741d601dcac52743fe681e1..418ab98a475789fa1cff7675008ded0aacd6feba 100644 (file)
@@ -32,6 +32,7 @@ TEST(ServerSelectorTest, all) {
     auto tags = selector.getTags();
     EXPECT_EQ(1, tags.size());
     EXPECT_EQ(1, tags.count(ServerTag("all")));
+    EXPECT_TRUE(selector.amAll());
     EXPECT_FALSE(selector.hasMultipleTags());
 }
 
index d793c71a6085619e3cac1da545cb029f5d818206..d12bb623392acf2ee4ba96fd6bffc98e9d35f298 100644 (file)
@@ -455,6 +455,41 @@ typedef boost::multi_index_container<
     >
 > SharedNetwork6Collection;
 
+/// @brief A class containing static convenience methods to fetch the shared
+/// networks from the containers.
+///
+/// @tparam ReturnPtrType Type of the returned object, i.e. @c SharedNetwork4Ptr
+/// or @c SharedNetwork6Ptr.
+/// @tparam CollectionType One of the @c SharedNetwork4Collection or
+/// @c SharedNetwork6Collection.
+template<typename ReturnPtrType, typename CollectionType>
+class SharedNetworkFetcher {
+public:
+
+    /// @brief Fetches shared network by name.
+    ///
+    /// @param collection Const reference to the collection from which the shared
+    /// network is to be fetched.
+    /// @param name Name of the shared network to be fetched.
+    /// @return Pointer to the fetched shared network or null if no such shared
+    /// network could be found.
+    static ReturnPtrType get(const CollectionType& collection, const std::string& name) {
+        auto& index = collection.template get<SharedNetworkNameIndexTag>();
+        auto sn = index.find(name);
+        if (sn != index.end()) {
+            return (*sn);
+        }
+        // No network found. Return null pointer.
+        return (ReturnPtrType());
+    }
+};
+
+/// @brief Type of the @c SharedNetworkFetcher used for IPv4.
+using SharedNetworkFetcher4 = SharedNetworkFetcher<SharedNetwork4Ptr, SharedNetwork4Collection>;
+
+/// @brief Type of the @c SharedNetworkFetcher used for IPv6.
+using SharedNetworkFetcher6 = SharedNetworkFetcher<SharedNetwork6Ptr, SharedNetwork6Collection>;
+
 } // end of namespace isc::dhcp
 } // end of namespace isc
 
index f068c8675c210b7fa9ffc6fdbc0e5ba7f32a79bf..75e44215f3e7f5821d8f69e497016a1ab29aec55 100644 (file)
@@ -1225,5 +1225,53 @@ TEST(SharedNetwork6Test, delAll) {
     ASSERT_EQ(0, network->getAllSubnets()->size());
 }
 
+// This test verifies that the IPv4 shared network can be fetched by name.
+TEST(SharedNetworkFetcherTest, getSharedNetwork4ByName) {
+    SharedNetwork4Collection collection;
+
+    // Shared network hasn't been added to the collection. A null pointer should
+    // be returned.
+    auto network = SharedNetworkFetcher4::get(collection, "network1");
+    EXPECT_FALSE(network);
+
+    network.reset(new SharedNetwork4("network1"));
+    EXPECT_NO_THROW(collection.push_back(network));
+
+    network.reset(new SharedNetwork4("network2"));
+    EXPECT_NO_THROW(collection.push_back(network));
+
+    network = SharedNetworkFetcher4::get(collection, "network1");
+    ASSERT_TRUE(network);
+    EXPECT_EQ("network1", network->getName());
+
+    network = SharedNetworkFetcher4::get(collection, "network2");
+    ASSERT_TRUE(network);
+    EXPECT_EQ("network2", network->getName());
+}
+
+// This test verifies that the IPv6 shared network can be fetched by name.
+TEST(SharedNetworkFetcherTest, getSharedNetwork6ByName) {
+    SharedNetwork6Collection collection;
+
+    // Shared network hasn't been added to the collection. A null pointer should
+    // be returned.
+    auto network = SharedNetworkFetcher6::get(collection, "network1");
+    EXPECT_FALSE(network);
+
+    network.reset(new SharedNetwork6("network1"));
+    EXPECT_NO_THROW(collection.push_back(network));
+
+    network.reset(new SharedNetwork6("network2"));
+    EXPECT_NO_THROW(collection.push_back(network));
+
+    network = SharedNetworkFetcher6::get(collection, "network1");
+    ASSERT_TRUE(network);
+    EXPECT_EQ("network1", network->getName());
+
+    network = SharedNetworkFetcher6::get(collection, "network2");
+    ASSERT_TRUE(network);
+    EXPECT_EQ("network2", network->getName());
+}
+
 
 } // end of anonymous namespace