From: Marcin Siodelski Date: Mon, 8 Jul 2019 13:10:27 +0000 (+0200) Subject: [#716,!412] Multiple tags are returned with shared networks. X-Git-Tag: Kea-1.6.0-beta2~88 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed777dbd8f03d1f716968500d7b5b23b5153062c;p=thirdparty%2Fkea.git [#716,!412] Multiple tags are returned with shared networks. --- diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index 1d532f6d80..1d2d932e7b 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -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(); + 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. diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc index de1503ce1d..f7412ad252 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc @@ -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(); + 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. diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h index 64ddd45fb6..a8749582da 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h @@ -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 + 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". diff --git a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h index 9f5dc3fe83..a5e5e1158b 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h +++ b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h @@ -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__) diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc index d8db0ebd8f..5abd2c4982 100644 --- a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc @@ -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. diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc index ed91ce5b11..a9af75113b 100644 --- a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc @@ -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 diff --git a/src/lib/database/server_selector.h b/src/lib/database/server_selector.h index 205bfdcd09..39ff19d8c4 100644 --- a/src/lib/database/server_selector.h +++ b/src/lib/database/server_selector.h @@ -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. diff --git a/src/lib/database/tests/server_selector_unittest.cc b/src/lib/database/tests/server_selector_unittest.cc index b3b530db99..418ab98a47 100644 --- a/src/lib/database/tests/server_selector_unittest.cc +++ b/src/lib/database/tests/server_selector_unittest.cc @@ -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()); } diff --git a/src/lib/dhcpsrv/shared_network.h b/src/lib/dhcpsrv/shared_network.h index d793c71a60..d12bb62339 100644 --- a/src/lib/dhcpsrv/shared_network.h +++ b/src/lib/dhcpsrv/shared_network.h @@ -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 +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(); + 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; + +/// @brief Type of the @c SharedNetworkFetcher used for IPv6. +using SharedNetworkFetcher6 = SharedNetworkFetcher; + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/tests/shared_network_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_unittest.cc index f068c8675c..75e44215f3 100644 --- a/src/lib/dhcpsrv/tests/shared_network_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_unittest.cc @@ -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