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