From: Marcin Siodelski Date: Mon, 8 Jul 2019 19:42:50 +0000 (+0200) Subject: [#717] Associate subnets with server tags. X-Git-Tag: Kea-1.6.0-beta2~73 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a4791908bcc1c897996ea95badabef0427361419;p=thirdparty%2Fkea.git [#717] Associate subnets with server tags. --- diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index cb95b16946..d12a8d125d 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -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()); // 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(); + 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(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(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(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(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(subnet->getID()), + MySqlBinding::createTimestamp(subnet->getModificationTime())); + // (Re)create pools. for (auto pool : subnet->getPools(Lease::TYPE_V4)) { createPool4(server_selector, boost::dynamic_pointer_cast(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(option->option_->getType()), createOptionValueBinding(option), @@ -1519,7 +1547,6 @@ public: MySqlBinding::createNull(), MySqlBinding::createNull(), MySqlBinding::createTimestamp(option->getModificationTime()), - MySqlBinding::createString(tag), MySqlBinding::createInteger(static_cast(subnet_id)), MySqlBinding::createInteger(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) diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc index c65e2cd8d8..53444d2e5d 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc @@ -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()); @@ -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(); + 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(subnet_id) - }; + MySqlBindingCollection in_bindings = { MySqlBinding::createInteger(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(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(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(subnet->getID()), + MySqlBinding::createTimestamp(subnet->getModificationTime())); + // (Re)create pools. for (auto pool : subnet->getPools(Lease::TYPE_NA)) { createPool6(server_selector, boost::dynamic_pointer_cast(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) 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 d37860eedc..f00cb778bd 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h +++ b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h @@ -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 " \ 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 ee96023ddb..4f793a26a9 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 @@ -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); } 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 b1be84ac5a..fbd186e31d 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 @@ -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); }