From: Francis Dupont Date: Sun, 20 Jan 2019 16:17:08 +0000 (+0100) Subject: [421-create-config-backend-for-dhcpv6] Began update of MySQL CB for DHCPv6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ba7664abbd6f3217d3d15ff40bdf513a44f90583;p=thirdparty%2Fkea.git [421-create-config-backend-for-dhcpv6] Began update of MySQL CB for DHCPv6 --- diff --git a/configure.ac b/configure.ac index a721b37dda..54181319d0 100644 --- a/configure.ac +++ b/configure.ac @@ -1544,6 +1544,8 @@ AC_CONFIG_FILES([Makefile src/hooks/dhcp/high_availability/tests/Makefile src/hooks/dhcp/lease_cmds/Makefile src/hooks/dhcp/lease_cmds/tests/Makefile + src/hooks/dhcp/mysql_cb/Makefile + src/hooks/dhcp/mysql_cb/tests/Makefile src/hooks/dhcp/user_chk/Makefile src/hooks/dhcp/user_chk/tests/Makefile src/hooks/dhcp/user_chk/tests/test_data_files_config.h diff --git a/src/hooks/dhcp/Makefile.am b/src/hooks/dhcp/Makefile.am index d8e33e26c8..902a3105ec 100644 --- a/src/hooks/dhcp/Makefile.am +++ b/src/hooks/dhcp/Makefile.am @@ -1 +1,5 @@ SUBDIRS = high_availability lease_cmds stat_cmds user_chk + +if HAVE_MYSQL +SUBDIRS += mysql_cb +endif diff --git a/src/hooks/dhcp/mysql_cb/Makefile.am b/src/hooks/dhcp/mysql_cb/Makefile.am index 9ebc47e7ac..f28d0c244b 100644 --- a/src/hooks/dhcp/mysql_cb/Makefile.am +++ b/src/hooks/dhcp/mysql_cb/Makefile.am @@ -26,6 +26,7 @@ noinst_LTLIBRARIES = libmysqlcb.la libmysqlcb_la_SOURCES = mysql_cb_callouts.cc libmysqlcb_la_SOURCES += mysql_cb_dhcp4.cc mysql_cb_dhcp4.h +libmysqlcb_la_SOURCES += mysql_cb_dhcp6.cc mysql_cb_dhcp6.h libmysqlcb_la_SOURCES += mysql_cb_impl.cc mysql_cb_impl.h libmysqlcb_la_SOURCES += mysql_query_macros_dhcp.h libmysqlcb_la_SOURCES += version.cc diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_callouts.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_callouts.cc index 5a23969a8e..34bcd5f18b 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_callouts.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_callouts.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,6 +11,7 @@ #include #include #include +#include using namespace isc::hooks; @@ -25,6 +26,7 @@ int load(LibraryHandle& /* handle */) { // Register MySQL CB factory with CB Manager isc::dhcp::MySqlConfigBackendDHCPv4::registerBackendType(); + isc::dhcp::MySqlConfigBackendDHCPv6::registerBackendType(); return (0); } @@ -35,6 +37,7 @@ int load(LibraryHandle& /* handle */) { int unload() { // Unregister the factory and remove MySQL backends + isc::dhcp::MySqlConfigBackendDHCPv6::unregisterBackendType(); isc::dhcp::MySqlConfigBackendDHCPv4::unregisterBackendType(); return (0); } diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index dfcff9c910..3f852e6763 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -148,7 +148,7 @@ public: StampedValuePtr stamped_value(new StampedValue(out_bindings[1]->getString(), out_bindings[2]->getString())); stamped_value->setModificationTime(out_bindings[3]->getTimestamp()); - parameters.push_back(stamped_value); + parameters.insert(stamped_value); } }); } @@ -252,7 +252,7 @@ public: // statement. MySqlBindingCollection out_bindings = { MySqlBinding::createInteger(), // subnet_id - MySqlBinding::createString(SUBNET_PREFIX_BUF_LENGTH), // subnet_prefix + MySqlBinding::createString(SUBNET4_PREFIX_BUF_LENGTH), // subnet_prefix MySqlBinding::createString(DHCP4O6_INTERFACE_BUF_LENGTH), // 4o6_interface MySqlBinding::createString(DHCP4O6_INTERFACE_ID_BUF_LENGTH), // 4o6_interface_id MySqlBinding::createString(DHCP4O6_SUBNET_BUF_LENGTH), // 4o6_subnet @@ -447,7 +447,7 @@ public: (last_pool_option_id < out_bindings[25]->getInteger())) { last_pool_option_id = out_bindings[25]->getInteger(); - OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 25); + OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 25); if (desc) { last_pool->getCfgOption()->add(*desc, desc->space_name_); } @@ -458,7 +458,7 @@ public: (last_option_id < out_bindings[37]->getInteger())) { last_option_id = out_bindings[37]->getInteger(); - OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 37); + OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 37); if (desc) { last_subnet->getCfgOption()->add(*desc, desc->space_name_); } @@ -616,7 +616,7 @@ public: (last_pool_option_id < out_bindings[5]->getInteger())) { last_pool_option_id = out_bindings[5]->getInteger(); - OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 5); + OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 5); if (desc) { last_pool->getCfgOption()->add(*desc, desc->space_name_); } @@ -977,7 +977,7 @@ public: (last_option_id < out_bindings[13]->getInteger())) { last_option_id = out_bindings[13]->getInteger(); - OptionDescriptorPtr desc = processOptionRow(Option::V4, out_bindings.begin() + 13); + OptionDescriptorPtr desc = processOptionRow4(Option::V4, out_bindings.begin() + 13); if (desc) { last_network->getCfgOption()->add(*desc, desc->space_name_); } @@ -1481,7 +1481,7 @@ public: MySqlBinding::createInteger(static_cast(code)), MySqlBinding::createString(space) }; - getOptions(GET_OPTION4_CODE_SPACE, in_bindings, Option::V4, options); + getOptions4(GET_OPTION4_CODE_SPACE, in_bindings, Option::V4, options); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); } @@ -1499,8 +1499,8 @@ public: MySqlBindingCollection in_bindings = { MySqlBinding::createString(tag) }; - getOptions(MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4, - in_bindings, Option::V4, options); + getOptions4(MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4, + in_bindings, Option::V4, options); } return (options); @@ -1522,8 +1522,8 @@ public: MySqlBinding::createString(tag), MySqlBinding::createTimestamp(modification_time) }; - getOptions(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4, - in_bindings, Option::V4, options); + getOptions4(MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4, + in_bindings, Option::V4, options); } return (options); @@ -1558,8 +1558,8 @@ public: MySqlBinding::createInteger(static_cast(code)), MySqlBinding::createString(space) }; - getOptions(GET_OPTION4_SUBNET_ID_CODE_SPACE, in_bindings, Option::V4, - options); + getOptions4(GET_OPTION4_SUBNET_ID_CODE_SPACE, in_bindings, Option::V4, + options); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); } @@ -1593,8 +1593,8 @@ public: MySqlBinding::createInteger(static_cast(code)), MySqlBinding::createString(space) }; - getOptions(GET_OPTION4_POOL_ID_CODE_SPACE, in_bindings, Option::V4, - options); + getOptions4(GET_OPTION4_POOL_ID_CODE_SPACE, in_bindings, Option::V4, + options); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); } @@ -1629,8 +1629,8 @@ public: MySqlBinding::createInteger(static_cast(code)), MySqlBinding::createString(space) }; - getOptions(GET_OPTION4_SHARED_NETWORK_CODE_SPACE, in_bindings, Option::V4, - options); + getOptions4(GET_OPTION4_SHARED_NETWORK_CODE_SPACE, in_bindings, + Option::V4, options); return (options.empty() ? OptionDescriptorPtr() : OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); } @@ -2019,33 +2019,32 @@ TaggedStatementArray tagged_statements = { { // Retrieves global option by code and space. { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_CODE_SPACE, - MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?) + MYSQL_GET_OPTION4(AND o.scope_id = 0 AND o.code = ? AND o.space = ?) }, // Retrieves all global options. { MySqlConfigBackendDHCPv4Impl::GET_ALL_OPTIONS4, - MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 0) + MYSQL_GET_OPTION4(AND o.scope_id = 0) }, // Retrieves modified options. { MySqlConfigBackendDHCPv4Impl::GET_MODIFIED_OPTIONS4, - MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 0 AND o.modification_ts > ?) + MYSQL_GET_OPTION4(AND o.scope_id = 0 AND o.modification_ts > ?) }, // Retrieves an option for a given subnet, option code and space. { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_SUBNET_ID_CODE_SPACE, - MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?) + MYSQL_GET_OPTION4(AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?) }, // Retrieves an option for a given pool, option code and space. { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_POOL_ID_CODE_SPACE, - MYSQL_GET_OPTION(dhcp4, AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?) + MYSQL_GET_OPTION4(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?) }, // Retrieves an option for a given shared network, option code and space. { MySqlConfigBackendDHCPv4Impl::GET_OPTION4_SHARED_NETWORK_CODE_SPACE, - MYSQL_GET_OPTION(dhcp4, - AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?) + MYSQL_GET_OPTION4(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?) }, // Insert global parameter. @@ -2091,7 +2090,7 @@ TaggedStatementArray tagged_statements = { { // Insert pool for a subnet. { MySqlConfigBackendDHCPv4Impl::INSERT_POOL4, - MYSQL_INSERT_POOL(dhcp4) + MYSQL_INSERT_POOL4() }, // Insert a shared network. @@ -2128,7 +2127,7 @@ TaggedStatementArray tagged_statements = { { // Insert subnet specific option. { MySqlConfigBackendDHCPv4Impl::INSERT_OPTION4, - MYSQL_INSERT_OPTION(dhcp4) + MYSQL_INSERT_OPTION4() }, // Insert association of the DHCP option with a server. @@ -2190,24 +2189,22 @@ TaggedStatementArray tagged_statements = { { // Update existing global option. { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4, - MYSQL_UPDATE_OPTION(dhcp4, AND o.scope_id = 0 AND o.code = ? AND o.space = ?) + MYSQL_UPDATE_OPTION4(AND o.scope_id = 0 AND o.code = ? AND o.space = ?) }, // Update existing subnet level option. { MySqlConfigBackendDHCPv4Impl::UPDATE_OPTION4_SUBNET_ID, - MYSQL_UPDATE_OPTION(dhcp4, - AND o.scope_id = 1 AND o.dhcp4_subnet_id = ? AND o.code = ? AND o.space = ?) + MYSQL_UPDATE_OPTION4(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_OPTION(dhcp4, AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?) + MYSQL_UPDATE_OPTION4(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_OPTION(dhcp4, - AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?) + MYSQL_UPDATE_OPTION4(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?) }, // Delete global parameter by name. @@ -2237,7 +2234,7 @@ TaggedStatementArray tagged_statements = { { // Delete pools for a subnet. { MySqlConfigBackendDHCPv4Impl::DELETE_POOLS4_SUBNET_ID, - MYSQL_DELETE_POOLS(dhcp4) + MYSQL_DELETE_POOLS4() }, // Delete shared network by name. diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc new file mode 100644 index 0000000000..14c8cf969a --- /dev/null +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc @@ -0,0 +1,2950 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc::cb; +using namespace isc::db; +using namespace isc::data; +using namespace isc::asiolink; +using namespace isc::util; + +namespace isc { +namespace dhcp { + +/// @brief Implementation of the MySQL Configuration Backend. +class MySqlConfigBackendDHCPv6Impl : public MySqlConfigBackendImpl { +public: + + /// @brief Statement tags. + /// + /// The contents of the enum are indexes into the list of SQL statements. + /// It is assumed that the order is such that the indices of statements + /// reading the database are less than those of statements modifying the + /// database. + enum StatementIndex { + GET_GLOBAL_PARAMETER6, + GET_ALL_GLOBAL_PARAMETERS6, + GET_MODIFIED_GLOBAL_PARAMETERS6, + GET_SUBNET6_ID, + GET_SUBNET6_PREFIX, + GET_ALL_SUBNETS6, + GET_MODIFIED_SUBNETS6, + GET_POOL6_RANGE, + GET_PD_POOL, + GET_SHARED_NETWORK6_NAME, + GET_ALL_SHARED_NETWORKS6, + GET_MODIFIED_SHARED_NETWORKS6, + GET_OPTION_DEF6_CODE_SPACE, + GET_ALL_OPTION_DEFS6, + GET_MODIFIED_OPTION_DEFS6, + GET_OPTION6_CODE_SPACE, + GET_ALL_OPTIONS6, + GET_MODIFIED_OPTIONS6, + GET_OPTION6_SUBNET_ID_CODE_SPACE, + GET_OPTION6_POOL_ID_CODE_SPACE, + GET_OPTION6_PD_POOL_ID_CODE_SPACE, + GET_OPTION6_SHARED_NETWORK_CODE_SPACE, + INSERT_GLOBAL_PARAMETER6, + INSERT_GLOBAL_PARAMETER6_SERVER, + INSERT_SUBNET6, + INSERT_SUBNET6_SERVER, + INSERT_POOL6, + INSERT_PD_POOL, + INSERT_SHARED_NETWORK6, + INSERT_SHARED_NETWORK6_SERVER, + INSERT_OPTION_DEF6, + INSERT_OPTION_DEF6_SERVER, + INSERT_OPTION6, + INSERT_OPTION6_SERVER, + UPDATE_GLOBAL_PARAMETER6, + UPDATE_SUBNET6, + UPDATE_SHARED_NETWORK6, + UPDATE_OPTION_DEF6, + UPDATE_OPTION6, + UPDATE_OPTION6_SUBNET_ID, + UPDATE_OPTION6_POOL_ID, + UPDATE_OPTION6_PD_POOL_ID, + UPDATE_OPTION6_SHARED_NETWORK, + DELETE_GLOBAL_PARAMETER6, + DELETE_ALL_GLOBAL_PARAMETERS6, + DELETE_SUBNET6_ID, + DELETE_SUBNET6_PREFIX, + DELETE_ALL_SUBNETS6, + DELETE_POOLS6_SUBNET_ID, + DELETE_PD_POOLS_SUBNET_ID, + DELETE_SHARED_NETWORK6_NAME, + DELETE_ALL_SHARED_NETWORKS6, + DELETE_OPTION_DEF6_CODE_NAME, + DELETE_ALL_OPTION_DEFS6, + DELETE_OPTION6, + DELETE_OPTION6_SUBNET_ID, + DELETE_OPTION6_POOL_RANGE, + DELETE_OPTION6_PD_POOL, + DELETE_OPTION6_SHARED_NETWORK, + DELETE_OPTIONS6_SUBNET_ID, + DELETE_OPTIONS6_SHARED_NETWORK, + NUM_STATEMENTS + }; + + /// @brief Constructor. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + explicit MySqlConfigBackendDHCPv6Impl(const DatabaseConnection::ParameterMap& + parameters); + + /// @brief Sends query to retrieve multiple global parameters. + /// + /// @param index Index of the query to be used. + /// @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 + /// if the query contains no WHERE clause. + /// @param [out] subnets Reference to the container where fetched parameters + /// will be inserted. + void getGlobalParameters6(const StatementIndex& index, + const MySqlBindingCollection& in_bindings, + StampedValueCollection& parameters) { + // The following parameters from the dhcp6_global_parameter table are + // returned: + // - id + // - name - parameter name + // - value - parameter value + // - modification_ts - modification timestamp. + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger(), // id + MySqlBinding::createString(GLOBAL_PARAMETER_NAME_BUF_LENGTH), // name + MySqlBinding::createString(GLOBAL_PARAMETER_VALUE_BUF_LENGTH), // value + MySqlBinding::createTimestamp() // modification_ts + }; + + conn_.selectQuery(index, in_bindings, out_bindings, + [¶meters] + (MySqlBindingCollection& out_bindings) { + if (!out_bindings[1]->getString().empty()) { + StampedValuePtr stamped_value(new StampedValue(out_bindings[1]->getString(), + out_bindings[2]->getString())); + stamped_value->setModificationTime(out_bindings[3]->getTimestamp()); + parameters.insert(stamped_value); + } + }); + } + + /// @brief Sends query to retrieve global parameter. + /// + /// @param server_selector Server selector. + /// @param name Name of the parameter to be retrieved. + /// + /// @return Pointer to the retrieved value or null if such parameter + /// doesn't exist. + StampedValuePtr getGlobalParameter6(const ServerSelector& server_selector, + const std::string& name) { + StampedValueCollection parameters; + + auto tags = getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createString(name) + }; + + getGlobalParameters6(GET_GLOBAL_PARAMETER6, in_bindings, parameters); + } + + return (parameters.empty() ? StampedValuePtr() : *parameters.begin()); + } + + /// @brief Sends query to insert or update global parameter. + /// + /// @param server_selector Server selector. + /// @param name Name of the global parameter. + /// @param value Value of the global parameter. + void createUpdateGlobalParameter6(const db::ServerSelector& server_selector, + const StampedValuePtr& value) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "creating or updating global parameter"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(value->getName()), + MySqlBinding::createString(value->getValue()), + MySqlBinding::createTimestamp(value->getModificationTime()), + MySqlBinding::createString(tag), + MySqlBinding::createString(value->getName()) + }; + + MySqlTransaction transaction(conn_); + + // Try to update the existing row. + if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_GLOBAL_PARAMETER6, + in_bindings) == 0) { + + // No such parameter found, so let's insert it. We have to adjust the + // bindings collection to match the prepared statement for insert. + in_bindings.pop_back(); + conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6, + 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 + // 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); + } + + transaction.commit(); + } + + /// @brief Sends query to the database to retrieve multiple subnets. + /// + /// Query should order subnets by subnet_id. + /// + /// @param index Index of the query to be used. + /// @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 + /// if the query contains no WHERE clause. + /// @param [out] subnets Reference to the container where fetched subnets + /// will be inserted. + void getSubnets6(const StatementIndex& index, + const MySqlBindingCollection& in_bindings, + Subnet6Collection& subnets) { + // Create output bindings. The order must match that in the prepared + // statement. + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger(), // subnet_id + MySqlBinding::createString(SUBNET6_PREFIX_BUF_LENGTH), // subnet_prefix + MySqlBinding::createString(CLIENT_CLASS_BUF_LENGTH), // client_class + MySqlBinding::createString(INTERFACE_BUF_LENGTH), // interface + MySqlBinding::createTimestamp(), // modification_ts + MySqlBinding::createInteger(), // preferred_lifetime + MySqlBinding::createInteger(), // rapid_commit + MySqlBinding::createInteger(), // rebind_timer + MySqlBinding::createString(RELAY_BUF_LENGTH), // relay + MySqlBinding::createInteger(), // renew_timer + MySqlBinding::createString(REQUIRE_CLIENT_CLASSES_BUF_LENGTH), // require_client_classes + MySqlBinding::createInteger(), // reservation_mode + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // shared_network_name + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context + MySqlBinding::createInteger(), // valid_lifetime + MySqlBinding::createInteger(), // pool: id + MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: start_address + MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: end_address + MySqlBinding::createInteger(), // pool: dhcp6_subnet_id + MySqlBinding::createTimestamp(), // pool: modification_ts + MySqlBinding::createInteger(), // pd pool: id + MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pd pool: prefix + MySqlBinding::createInteger(), // pd pool: prefix_length + MySqlBinding::createInteger(), // pd pool: delegated_prefix_length + MySqlBinding::createInteger(), // pd pool: dhcp6_subnet_id + MySqlBinding::createTimestamp(), // pd pool: modification_ts + MySqlBinding::createInteger(), // pool option: option_id + MySqlBinding::createInteger(), // pool option: code + MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pool option: value + MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pool option: formatted_value + MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pool option: space + MySqlBinding::createInteger(), // pool option: persistent + MySqlBinding::createInteger(), // pool option: dhcp6_subnet_id + MySqlBinding::createInteger(), // pool option: scope_id + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pool option: user_context + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pool option: shared_network_name + MySqlBinding::createInteger(), // pool option: pool_id + MySqlBinding::createInteger(), // pool option: pd_pool_id + MySqlBinding::createTimestamp(), // pool option: modification_ts + MySqlBinding::createInteger(), // pd pool option: option_id + MySqlBinding::createInteger(), // pd pool option: code + MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pd pool option: value + MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pd pool option: formatted_value + MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pd pool option: space + MySqlBinding::createInteger(), // pd pool option: persistent + MySqlBinding::createInteger(), // pd pool option: dhcp6_subnet_id + MySqlBinding::createInteger(), // pd pool option: scope_id + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pd pool option: user_context + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pd pool option: shared_network_name + MySqlBinding::createInteger(), // pd pool option: pool_id + MySqlBinding::createInteger(), // pd pool option: pd_pool_id + MySqlBinding::createTimestamp(), // pd pool option: modification_ts + MySqlBinding::createInteger(), // option: option_id + MySqlBinding::createInteger(), // option: code + MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // option: value + MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // option: formatted_value + MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // option: space + MySqlBinding::createInteger(), // option: persistent + MySqlBinding::createInteger(), // option: dhcp6_subnet_id + MySqlBinding::createInteger(), // option: scope_id + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // option: user_context + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // option: shared_network_name + MySqlBinding::createInteger(), // option: pool_id + MySqlBinding::createInteger(), // option: pd_pool_id + MySqlBinding::createTimestamp() // option: modification_ts + }; + + uint64_t last_pool_id = 0; + uint64_t last_pd_pool_id = 0; + 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; + + // 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] + (MySqlBindingCollection& out_bindings) { + // Get pointer to the last subnet in the collection. + Subnet6Ptr last_subnet; + if (!subnets.empty()) { + last_subnet = *subnets.rbegin(); + } + + // Subnet has been returned. Assuming that subnets are ordered by + // subnet identifier, if the subnet identifier of the current row + // is different than the subnet identifier of the previously returned + // row, it means that we have to construct new subnet object. + if (!last_subnet || (last_subnet->getID() != out_bindings[0]->getInteger())) { + + // Reset pool id, because current row defines new subnet. Subsequent + // rows will contain pool information. + last_pool_id = 0; + + // subnet_id + SubnetID subnet_id(out_bindings[0]->getInteger()); + + // subnet_prefix + std::string subnet_prefix = out_bindings[1]->getString(); + auto prefix_pair = Subnet6::parsePrefix(subnet_prefix); + + // preferred lifetime + uint32_t preferred_lifetime = out_bindings[5]->getIntegerOrDefault(0); + + // renew_timer + uint32_t renew_timer = out_bindings[9]->getIntegerOrDefault(0); + + // rebind_timer + uint32_t rebind_timer = out_bindings[7]->getIntegerOrDefault(0); + + // valid_lifetime + uint32_t valid_lifetime = out_bindings[14]->getIntegerOrDefault(0); + + // Create subnet with basic settings. + last_subnet.reset(new Subnet6(prefix_pair.first, + prefix_pair.second, + renew_timer, rebind_timer, + preferred_lifetime, + valid_lifetime, subnet_id)); + + // client_class + if (!out_bindings[2]->amNull()) { + last_subnet->allowClientClass(out_bindings[2]->getString()); + } + + // interface + last_subnet->setIface(out_bindings[3]->getStringOrDefault("")); + + // modification_ts + last_subnet->setModificationTime(out_bindings[4]->getTimestamp()); + // rapid_commit + last_subnet->setRapidCommit(static_cast + (out_bindings[6]->getIntegerOrDefault(0))); + + // relay + ElementPtr relay_element = out_bindings[8]->getJSON(); + if (relay_element) { + if (relay_element->getType() != Element::list) { + isc_throw(BadValue, "invalid relay value " + << out_bindings[8]->getString()); + } + for (auto i = 0; i < relay_element->size(); ++i) { + auto relay_address_element = relay_element->get(i); + if (relay_address_element->getType() != Element::string) { + isc_throw(BadValue, "relay address must be a string"); + } + last_subnet->addRelayAddress(IOAddress(relay_element->get(i)->stringValue())); + } + } + + // require_client_classes + ElementPtr require_element = out_bindings[10]->getJSON(); + if (require_element) { + if (require_element->getType() != Element::list) { + isc_throw(BadValue, "invalid require_client_classes value " + << out_bindings[10]->getString()); + } + for (auto i = 0; i < require_element->size(); ++i) { + auto require_item = require_element->get(i); + if (require_item->getType() != Element::string) { + isc_throw(BadValue, "elements of require_client_classes list must" + "be valid strings"); + } + last_subnet->requireClientClass(require_item->stringValue()); + } + } + + // reservation_mode + last_subnet->setHostReservationMode(static_cast + (out_bindings[11]->getIntegerOrDefault(Subnet6::HR_ALL))); + + // shared_network_name + last_subnet->setSharedNetworkName(out_bindings[12]->getStringOrDefault("")); + + // user_context + ElementPtr user_context = out_bindings[13]->getJSON(); + if (user_context) { + last_subnet->setContext(user_context); + } + + // Subnet ready. Add it to the list. + subnets.push_back(last_subnet); + } + + // 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. + if (!out_bindings[15]->amNull() && + !out_bindings[16]->getString().empty() && + !out_bindings[17]->getString().empty() && + (out_bindings[15]->getInteger() > last_pool_id)) { + last_pool_id = out_bindings[15]->getInteger(); + last_pool.reset(new Pool6(Lease::TYPE_NA, + IOAddress(out_bindings[16]->getString()), + IOAddress(out_bindings[17]->getString()))); + last_subnet->addPool(last_pool); + } + + // If the row contains information about the pd pool and it + // appears to be new pd pool entry (checked by comparing pd pool + // id), let's create the new pd pool and add it to the subnet. + if (!out_bindings[20]->amNull() && + !out_bindings[21]->getString().empty() && + (out_bindings[22]->getInteger() != 0) && + (out_bindings[23]->getInteger() != 0) && + (out_bindings[20]->getInteger() > last_pd_pool_id)) { + last_pd_pool_id = out_bindings[20]->getInteger(); + last_pd_pool.reset(new Pool6(Lease::TYPE_PD, + IOAddress(out_bindings[21]->getString()), + out_bindings[22]->getInteger(), + out_bindings[23]->getInteger())); + last_subnet->addPool(last_pd_pool); + } + + // Parse pool specific option. + if (last_pool && !out_bindings[26]->amNull() && + (last_pool_option_id < out_bindings[26]->getInteger())) { + last_pool_option_id = out_bindings[26]->getInteger(); + + OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 26); + if (desc) { + last_pool->getCfgOption()->add(*desc, desc->space_name_); + } + } + + // Parse pd pool specific option. + if (last_pd_pool && !out_bindings[39]->amNull() && + (last_pd_pool_option_id < out_bindings[39]->getInteger())) { + last_pd_pool_option_id = out_bindings[39]->getInteger(); + + OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 39); + if (desc) { + last_pd_pool->getCfgOption()->add(*desc, desc->space_name_); + } + } + + // Parse subnet specific option. + if (!out_bindings[52]->amNull() && + (last_option_id < out_bindings[52]->getInteger())) { + last_option_id = out_bindings[52]->getInteger(); + + OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 52); + if (desc) { + last_subnet->getCfgOption()->add(*desc, desc->space_name_); + } + } + + }); + } + + /// @brief Sends query to retrieve single subnet by id. + /// + /// @param server_selector Server selector. + /// @param subnet_id Subnet identifier. + /// + /// @return Pointer to the returned subnet or NULL if such subnet + /// doesn't exist. + Subnet6Ptr getSubnet6(const ServerSelector& server_selector, + const SubnetID& subnet_id) { + Subnet6Collection subnets; + + auto tags = getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(subnet_id) + }; + + getSubnets6(GET_SUBNET6_ID, in_bindings, subnets); + } + + return (subnets.empty() ? Subnet6Ptr() : *subnets.begin()); + } + + /// @brief Sends query to retrieve single subnet by prefix. + /// + /// The prefix should be in the following format: "2001:db8:1::/64". + /// + /// @param server_selector Server selector. + /// @param subnet_id Subnet identifier. + /// + /// @return Pointer to the returned subnet or NULL if such subnet + /// doesn't exist. + Subnet6Ptr getSubnet6(const ServerSelector& server_selector, + const std::string& subnet_prefix) { + Subnet6Collection subnets; + + auto tags = getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createString(subnet_prefix) + }; + + getSubnets6(GET_SUBNET6_PREFIX, in_bindings, subnets); + } + + return (subnets.empty() ? Subnet6Ptr() : *subnets.begin()); + } + + /// @brief Sends query to retrieve all subnets. + /// + /// @param server_selector Server selector. + /// @param [out] subnets Reference to the subnet collection structure where + /// subnets should be inserted. + void getAllSubnets6(const ServerSelector& server_selector, + Subnet6Collection& subnets) { + auto tags = getServerTags(server_selector); + + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag) + }; + + getSubnets6(GET_ALL_SUBNETS6, in_bindings, subnets); + } + } + + /// @brief Sends query to retrieve modified subnets. + /// + /// @param server_selector Server selector. + /// @param modification_ts Lower bound modification timestamp. + /// @param [out] subnets Reference to the subnet collection structure where + /// subnets should be inserted. + void getModifiedSubnets6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_ts, + Subnet6Collection& subnets) { + auto tags = getServerTags(server_selector); + + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createTimestamp(modification_ts) + }; + + getSubnets6(GET_MODIFIED_SUBNETS6, in_bindings, subnets); + } + } + + /// @brief Sends query to retrieve multiple pools. + /// + /// Query should order pools by id. + /// + /// @param index Index of the query to be used. + /// @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 + /// if the query contains no WHERE clause. + /// @param [out] pools Reference to the container where fetched pools + /// will be inserted. + /// @param [out] pool_ids Identifiers of the pools returned in @c pools + /// argument. + void getPools(const StatementIndex& index, + const MySqlBindingCollection& in_bindings, + PoolCollection& pools, + std::vector& pool_ids) { + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger(), // pool: id + MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: start_address + MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: end_address + MySqlBinding::createInteger(), // pool: dhcp6_subnet_id + MySqlBinding::createTimestamp(), // pool: modification_ts + MySqlBinding::createInteger(), // pool option: option_id + MySqlBinding::createInteger(), // pool option: code + MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pool option: value + MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pool option: formatted_value + MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pool option: space + MySqlBinding::createInteger(), // pool option: persistent + MySqlBinding::createInteger(), // pool option: dhcp6_subnet_id + MySqlBinding::createInteger(), // pool option: scope_id + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pool option: user_context + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pool option: shared_network_name + MySqlBinding::createInteger(), // pool option: pool_id + MySqlBinding::createInteger(), // pool option: pd_pool_id + MySqlBinding::createTimestamp(), //pool option: modification_ts + }; + + uint64_t last_pool_id = 0; + uint64_t last_pool_option_id = 0; + Pool6Ptr last_pool; + + conn_.selectQuery(index, in_bindings, out_bindings, + [this, &last_pool_id, &last_pool_option_id, &last_pool, + &pools, &pool_ids] + (MySqlBindingCollection& out_bindings) { + if (out_bindings[0]->getInteger() > last_pool_id) { + + last_pool_id = out_bindings[0]->getInteger(); + + last_pool.reset(new Pool6(Lease::TYPE_NA, + IOAddress(out_bindings[1]->getString()), + IOAddress(out_bindings[2]->getString()))); + pools.push_back(last_pool); + pool_ids.push_back(last_pool_id); + } + + // Parse pool specific option. + if (last_pool && !out_bindings[5]->amNull() && + (last_pool_option_id < out_bindings[5]->getInteger())) { + last_pool_option_id = out_bindings[5]->getInteger(); + + OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 5); + if (desc) { + last_pool->getCfgOption()->add(*desc, desc->space_name_); + } + } + }); + } + + /// @brief Sends query to retrieve multiple pd pools. + /// + /// Query should order pools by id. + /// + /// @param index Index of the query to be used. + /// @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 + /// if the query contains no WHERE clause. + /// @param [out] pools Reference to the container where fetched pools + /// will be inserted. + /// @param [out] pool_ids Identifiers of the pools returned in @c pools + /// argument. + void getPdPools(const StatementIndex& index, + const MySqlBindingCollection& in_bindings, + PoolCollection& pools, + std::vector& pool_ids) { + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger(), // pool: id + MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: prefix + MySqlBinding::createInteger(), // pool: prefix_length + MySqlBinding::createInteger(), // pool: delegated_prefix_length + MySqlBinding::createInteger(), // pool: dhcp6_subnet_id + MySqlBinding::createTimestamp(), // pool: modification_ts + MySqlBinding::createInteger(), // pool option: option_id + MySqlBinding::createInteger(), // pool option: code + MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // pool option: value + MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // pool option: formatted_value + MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // pool option: space + MySqlBinding::createInteger(), // pool option: persistent + MySqlBinding::createInteger(), // pool option: dhcp6_subnet_id + MySqlBinding::createInteger(), // pool option: scope_id + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // pool option: user_context + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // pool option: shared_network_name + MySqlBinding::createInteger(), // pool option: pool_id + MySqlBinding::createInteger(), // pool option: pd_pool_id + MySqlBinding::createTimestamp(), //pool option: modification_ts + }; + + uint64_t last_pool_id = 0; + uint64_t last_pool_option_id = 0; + Pool6Ptr last_pool; + + conn_.selectQuery(index, in_bindings, out_bindings, + [this, &last_pool_id, &last_pool_option_id, &last_pool, + &pools, &pool_ids] + (MySqlBindingCollection& out_bindings) { + if (out_bindings[0]->getInteger() > last_pool_id) { + + last_pool_id = out_bindings[0]->getInteger(); + + last_pool.reset(new Pool6(Lease::TYPE_PD, + IOAddress(out_bindings[1]->getString()), + out_bindings[2]->getInteger(), + out_bindings[3]->getInteger())); + pools.push_back(last_pool); + pool_ids.push_back(last_pool_id); + } + + // Parse pool specific option. + if (last_pool && !out_bindings[6]->amNull() && + (last_pool_option_id < out_bindings[6]->getInteger())) { + last_pool_option_id = out_bindings[6]->getInteger(); + + OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 6); + if (desc) { + last_pool->getCfgOption()->add(*desc, desc->space_name_); + } + } + }); + } + + /// @brief Sends query to retrieve single pool by address range. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound pool address. + /// @param pool_end_address Upper bound pool address. + /// @param pool_id Pool identifier for the returned pool. + /// @return Pointer to the pool or null if no such pool found. + Pool6Ptr getPool6(const ServerSelector& /* server_selector */, + const IOAddress& pool_start_address, + const IOAddress& pool_end_address, + uint64_t& pool_id) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(pool_start_address.toText()), + MySqlBinding::createString(pool_end_address.toText()) + }; + + PoolCollection pools; + std::vector pool_ids; + getPools(GET_POOL6_RANGE, in_bindings, pools, pool_ids); + + if (!pools.empty()) { + pool_id = pool_ids[0]; + return (boost::dynamic_pointer_cast(*pools.begin())); + } + + pool_id = 0; + + return (Pool6Ptr()); + } + + /// @brief Sends query to retrieve single pd pool. + /// + /// @param server_selector Server selector. + /// @param pd_pool_prefix Address part of the pd pool prefix. + /// @param pd_pool_prefix_length Length of the pd pool prefix. + /// @param pool_end_address Upper bound pool address. + /// @param pool_id Pool identifier for the returned pool. + /// @return Pointer to the pool or null if no such pool found. + Pool6Ptr getPdPool6(const ServerSelector& /* server_selector */, + const asiolink::IOAddress& pd_pool_prefix, + const uint8_t pd_pool_prefix_length, + uint64_t& pool_id) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(pd_pool_prefix.toText()), + MySqlBinding::createInteger(pd_pool_prefix_length) + }; + + PoolCollection pools; + std::vector pool_ids; + getPdPools(GET_PD_POOL, in_bindings, pools, pool_ids); + + if (!pools.empty()) { + pool_id = pool_ids[0]; + return (boost::dynamic_pointer_cast(*pools.begin())); + } + + pool_id = 0; + + return (Pool6Ptr()); + } + + /// @brief Sends query to insert or update subnet. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the subnet to be inserted or updated. + void createUpdateSubnet6(const ServerSelector& server_selector, + const Subnet6Ptr& subnet) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (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(); + for (auto required_class = required_classes.cbegin(); + required_class != required_classes.cend(); + ++required_class) { + required_classes_element->add(Element::create(*required_class)); + } + + // Create binding with shared network name if the subnet belongs to a + // shared network. + SharedNetwork6Ptr shared_network; + subnet->getSharedNetwork(shared_network); + MySqlBindingPtr shared_network_binding = + (shared_network ? MySqlBinding::createString(shared_network->getName()) : + MySqlBinding::createNull()); + + // Create input bindings. + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger(subnet->getID()), + MySqlBinding::createString(subnet->toText()), + MySqlBinding::condCreateString(subnet->getClientClass()), + MySqlBinding::condCreateString(subnet->getIface()), + MySqlBinding::createTimestamp(subnet->getModificationTime()), + MySqlBinding::createInteger(subnet->getPreferred()), + MySqlBinding::createInteger(static_cast(subnet->getRapidCommit())), + MySqlBinding::createInteger(subnet->getT2()), + createInputRelayBinding(subnet), + MySqlBinding::createInteger(subnet->getT1()), + createInputRequiredClassesBinding(subnet), + MySqlBinding::createInteger(static_cast(subnet->getHostReservationMode())), + shared_network_binding, + createInputContextBinding(subnet), + MySqlBinding::createInteger(subnet->getValid()) + }; + + MySqlTransaction transaction(conn_); + + try { + // Try to insert subnet. If this duplicates primary key, i.e. this + // subnet already exists it will throw DuplicateEntry exception in + // which case we'll try an update. + 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); + + + } catch (const DuplicateEntry&) { + deletePools6(subnet); + deletePdPools6(subnet); + deleteOptions6(server_selector, subnet); + + // Need to add one more binding for WHERE clause. + in_bindings.push_back(MySqlBinding::createInteger(subnet->getID())); + conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6, + in_bindings); + } + + // (Re)create pools. + for (auto pool : subnet->getPools(Lease::TYPE_NA)) { + createPool6(server_selector, boost::dynamic_pointer_cast(pool), + subnet); + } + + // (Re)create pd pools. + for (auto pool : subnet->getPools(Lease::TYPE_PD)) { + createPdPool6(server_selector, + boost::dynamic_pointer_cast(pool), + subnet); + } + + // (Re)create options. + auto option_spaces = subnet->getCfgOption()->getOptionSpaceNames(); + for (auto option_space : option_spaces) { + OptionContainerPtr options = subnet->getCfgOption()->getAll(option_space); + for (auto desc = options->begin(); desc != options->end(); ++desc) { + OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc)); + desc_copy->space_name_ = option_space; + createUpdateOption6(server_selector, subnet->getID(), desc_copy); + } + } + + transaction.commit(); + } + + /// @brief Inserts new IPv6 pool to the database. + /// + /// @param server_selector Server selector. + /// @param pool Pointer to the pool to be inserted. + /// @param subnet Pointer to the subnet that this pool belongs to. + void createPool6(const ServerSelector& server_selector, const Pool6Ptr& pool, + const Subnet6Ptr& subnet) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(pool->getFirstAddress().toText()), + MySqlBinding::createString(pool->getLastAddress().toText()), + MySqlBinding::createInteger(static_cast(subnet->getID())), + MySqlBinding::createTimestamp(subnet->getModificationTime()) + }; + + // Run INSERT. + conn_.insertQuery(INSERT_POOL6, in_bindings); + + uint64_t pool_id = mysql_insert_id(conn_.mysql_); + auto option_spaces = pool->getCfgOption()->getOptionSpaceNames(); + for (auto option_space : option_spaces) { + OptionContainerPtr options = pool->getCfgOption()->getAll(option_space); + for (auto desc = options->begin(); desc != options->end(); ++desc) { + OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc)); + desc_copy->space_name_ = option_space; + createUpdateOption6(server_selector, Lease::TYPE_NA, + pool_id, desc_copy); + } + } + } + + /// @brief Inserts new IPv6 pd pool to the database. + /// + /// @param server_selector Server selector. + /// @param pool Pointer to the pd pool to be inserted. + /// @param subnet Pointer to the subnet that this pool belongs to. + void createPdPool6(const ServerSelector& server_selector, + const Pool6Ptr& pool, + const Subnet6Ptr& subnet) { + int plen = prefixLengthFromRange(pool->getFirstAddress(), + pool->getLastAddress()); + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(pool->getFirstAddress().toText()), + MySqlBinding::createInteger(static_cast(plen)), + MySqlBinding::createInteger(pool->getLength()), + MySqlBinding::createInteger(static_cast(subnet->getID())), + MySqlBinding::createTimestamp(subnet->getModificationTime()) + }; + + // Run INSERT. + conn_.insertQuery(INSERT_PD_POOL, in_bindings); + + uint64_t pool_id = mysql_insert_id(conn_.mysql_); + auto option_spaces = pool->getCfgOption()->getOptionSpaceNames(); + for (auto option_space : option_spaces) { + OptionContainerPtr options = pool->getCfgOption()->getAll(option_space); + for (auto desc = options->begin(); desc != options->end(); ++desc) { + OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc)); + desc_copy->space_name_ = option_space; + createUpdateOption6(server_selector, Lease::TYPE_PD, + pool_id, desc_copy); + } + } + } + + /// @brief Sends query to delete subnet by id. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to be deleted. + /// @return Number of deleted subnets. + uint64_t deleteSubnet6(const ServerSelector& server_selector, + const SubnetID& subnet_id) { + return (deleteFromTable(DELETE_SUBNET6_ID, server_selector, + "deleting a subnet", + subnet_id)); + } + + /// @brief Deletes pools belonging to a subnet from the database. + /// + /// @param subnet Pointer to the subnet for which pools should be + /// deleted. + uint64_t deletePools6(const Subnet6Ptr& subnet) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger(subnet->getID()) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_POOLS6_SUBNET_ID, in_bindings)); + } + + /// @brief Deletes pd pools belonging to a subnet from the database. + /// + /// @param subnet Pointer to the subnet for which pd pools should be + /// deleted. + uint64_t deletePdPools6(const Subnet6Ptr& subnet) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger(subnet->getID()) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_PD_POOLS_SUBNET_ID, in_bindings)); + } + + /// @brief Sends query to the database to retrieve multiple shared + /// networks. + /// + /// Query should order shared networks by id. + /// + /// @param index Index of the query to be used. + /// @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 + /// if the query contains no WHERE clause. + /// @param [out] shared_networks Reference to the container where fetched + /// shared networks will be inserted. + void getSharedNetworks6(const StatementIndex& index, + const MySqlBindingCollection& in_bindings, + SharedNetwork6Collection& shared_networks) { + // Create output bindings. The order must match that in the prepared + // statement. + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger(), // id + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // name + MySqlBinding::createString(CLIENT_CLASS_BUF_LENGTH), // client_class + MySqlBinding::createString(INTERFACE_BUF_LENGTH), // interface + MySqlBinding::createTimestamp(), // modification_ts + MySqlBinding::createInteger(), // preferred_lifetime + MySqlBinding::createInteger(), // rapid_commit + MySqlBinding::createInteger(), // rebind_timer + MySqlBinding::createString(RELAY_BUF_LENGTH), // relay + MySqlBinding::createInteger(), // renew_timer + MySqlBinding::createString(REQUIRE_CLIENT_CLASSES_BUF_LENGTH), // require_client_classes + MySqlBinding::createInteger(), // reservation_mode + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context + MySqlBinding::createInteger(), // valid_lifetime + MySqlBinding::createInteger(), // option: option_id + MySqlBinding::createInteger(), // option: code + MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // option: value + MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // option: formatted_value + MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // option: space + MySqlBinding::createInteger(), // option: persistent + MySqlBinding::createInteger(), // option: dhcp6_subnet_id + MySqlBinding::createInteger(), // option: scope_id + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // option: user_context + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // option: shared_network_name + MySqlBinding::createInteger(), // option: pool_id + MySqlBinding::createInteger(), // option: pd_pool_id + MySqlBinding::createTimestamp() //option: modification_ts + }; + + uint64_t last_network_id = 0; + uint64_t last_option_id = 0; + + conn_.selectQuery(index, in_bindings, out_bindings, + [this, &shared_networks, &last_network_id, &last_option_id] + (MySqlBindingCollection& out_bindings) { + SharedNetwork6Ptr last_network; + if (!shared_networks.empty()) { + last_network = *shared_networks.rbegin(); + } + + // If this is the first shared network or the shared network id in this + // row points to the next shared network we use the data in the + // row to create the new shared network instance. + if (last_network_id != out_bindings[0]->getInteger()) { + + last_network_id = out_bindings[0]->getInteger(); + last_network.reset(new SharedNetwork6(out_bindings[1]->getString())); + + // client_class + if (!out_bindings[2]->amNull()) { + last_network->allowClientClass(out_bindings[2]->getString()); + } + + // interface + last_network->setIface(out_bindings[3]->getStringOrDefault("")); + + // modification_ts + last_network->setModificationTime(out_bindings[4]->getTimestamp()); + + // preferred_lifetime + if (!out_bindings[5]->amNull()) { + last_network->setPreferred(out_bindings[5]->getInteger()); + } + + // rapid_commit + last_network->setRapidCommit(static_cast + (out_bindings[6]->getIntegerOrDefault(0))); + + // rebind_timer + if (!out_bindings[7]->amNull()) { + last_network->setT2(out_bindings[7]->getInteger()); + } + + // relay + ElementPtr relay_element = out_bindings[8]->getJSON(); + if (relay_element) { + if (relay_element->getType() != Element::list) { + isc_throw(BadValue, "invalid relay value " + << out_bindings[8]->getString()); + } + for (auto i = 0; i < relay_element->size(); ++i) { + auto relay_address_element = relay_element->get(i); + if (relay_address_element->getType() != Element::string) { + isc_throw(BadValue, "relay address must be a string"); + } + last_network->addRelayAddress(IOAddress(relay_element->get(i)->stringValue())); + } + } + + // renew_timer + if (!out_bindings[9]->amNull()) { + last_network->setT1(out_bindings[9]->getInteger()); + } + + // require_client_classes + ElementPtr require_element = out_bindings[10]->getJSON(); + if (require_element) { + if (require_element->getType() != Element::list) { + isc_throw(BadValue, "invalid require_client_classes value " + << out_bindings[10]->getString()); + } + for (auto i = 0; i < require_element->size(); ++i) { + auto require_item = require_element->get(i); + if (require_item->getType() != Element::string) { + isc_throw(BadValue, "elements of require_client_classes list must" + "be valid strings"); + } + last_network->requireClientClass(require_item->stringValue()); + } + } + + // reservation_mode + last_network->setHostReservationMode(static_cast + (out_bindings[11]->getIntegerOrDefault(Subnet6::HR_ALL))); + + // user_context + ElementPtr user_context = out_bindings[12]->getJSON(); + if (user_context) { + last_network->setContext(user_context); + } + + // valid_lifetime + if (!out_bindings[13]->amNull()) { + last_network->setValid(out_bindings[13]->getInteger()); + } + + shared_networks.push_back(last_network); + } + + // Parse option. + if (!out_bindings[14]->amNull() && + (last_option_id < out_bindings[14]->getInteger())) { + last_option_id = out_bindings[14]->getInteger(); + + OptionDescriptorPtr desc = processOptionRow6(Option::V6, out_bindings.begin() + 14); + if (desc) { + last_network->getCfgOption()->add(*desc, desc->space_name_); + } + } + }); + } + + /// @brief Sends query to retrieve single shared network by name. + /// + /// @param server_selector Server selector. + /// @param name Shared network name. + /// + /// @return Pointer to the returned shared network or NULL if such shared + /// network doesn't exist. + SharedNetwork6Ptr getSharedNetwork6(const ServerSelector& server_selector, + const std::string& name) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "fetching shared network"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createString(name) + }; + + SharedNetwork6Collection shared_networks; + getSharedNetworks6(GET_SHARED_NETWORK6_NAME, in_bindings, shared_networks); + + return (shared_networks.empty() ? SharedNetwork6Ptr() : *shared_networks.begin()); + } + + /// @brief Sends query to retrieve all shared networks. + /// + /// @param server_selector Server selector. + /// @param [out] shared_networks Reference to the shared networks collection + /// structure where shared networks should be inserted. + void getAllSharedNetworks6(const ServerSelector& server_selector, + SharedNetwork6Collection& shared_networks) { + auto tags = getServerTags(server_selector); + + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag) + }; + + getSharedNetworks6(GET_ALL_SHARED_NETWORKS6, in_bindings, shared_networks); + } + } + + /// @brief Sends query to retrieve modified shared networks. + /// + /// @param server_selector Server selector. + /// @param modification_ts Lower bound modification timestamp. + /// @param [out] shared_networks Reference to the shared networks collection + /// structure where shared networks should be inserted. + void getModifiedSharedNetworks6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_ts, + SharedNetwork6Collection& shared_networks) { + auto tags = getServerTags(server_selector); + + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createTimestamp(modification_ts) + }; + + getSharedNetworks6(GET_MODIFIED_SHARED_NETWORKS6, in_bindings, + shared_networks); + } + } + + /// @brief Sends query to insert or update shared network. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the shared network to be inserted or updated. + void createUpdateSharedNetwork6(const ServerSelector& server_selector, + const SharedNetwork6Ptr& shared_network) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "creating or updating shared network"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(shared_network->getName()), + MySqlBinding::condCreateString(shared_network->getClientClass()), + MySqlBinding::condCreateString(shared_network->getIface()), + MySqlBinding::createTimestamp(shared_network->getModificationTime()), + MySqlBinding::createInteger(shared_network->getPreferred()), + MySqlBinding::createInteger(static_cast(shared_network->getRapidCommit())), + MySqlBinding::condCreateInteger(shared_network->getT2()), + createInputRelayBinding(shared_network), + MySqlBinding::condCreateInteger(shared_network->getT1()), + createInputRequiredClassesBinding(shared_network), + MySqlBinding::createInteger(static_cast + (shared_network->getHostReservationMode())), + createInputContextBinding(shared_network), + MySqlBinding::condCreateInteger(shared_network->getValid()) + }; + + MySqlTransaction transaction(conn_); + + try { + // Try to insert shared network. The shared network name must be unique, + // so if inserting fails with DuplicateEntry exception we'll need to + // update existing shared network entry. + 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); + + // 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); + } + + // (Re)create options. + auto option_spaces = shared_network->getCfgOption()->getOptionSpaceNames(); + for (auto option_space : option_spaces) { + OptionContainerPtr options = shared_network->getCfgOption()->getAll(option_space); + for (auto desc = options->begin(); desc != options->end(); ++desc) { + OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc)); + desc_copy->space_name_ = option_space; + createUpdateOption6(server_selector, shared_network->getName(), + desc_copy); + } + } + + transaction.commit(); + } + + + /// @brief Sends query to insert DHCP option. + /// + /// This method expects that the server selector contains exactly one + /// server tag. + /// + /// @param server_selector Server selector. + /// @param in_bindings Collection of bindings representing an option. + void insertOption6(const ServerSelector& server_selector, + const MySqlBindingCollection& in_bindings) { + 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(*getServerTags(server_selector).begin()), // server_tag + in_bindings[12] // copy modification timestamp from option + }; + + conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER, + in_server_bindings); + } + + /// @brief Sends query to insert or update global DHCP option. + /// + /// @param server_selector Server selector. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption6(const ServerSelector& server_selector, + const OptionDescriptorPtr& option) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "creating or updating global option"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger(option->option_->getType()), + createOptionValueBinding(option), + MySqlBinding::condCreateString(option->formatted_value_), + MySqlBinding::condCreateString(option->space_name_), + MySqlBinding::createInteger(static_cast(option->persistent_)), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createInteger(0), + createInputContextBinding(option), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createTimestamp(option->getModificationTime()) + }; + + MySqlTransaction transaction(conn_); + OptionDescriptorPtr existing_option = getOption6(server_selector, + option->option_->getType(), + option->space_name_); + if (existing_option) { + in_bindings.push_back(MySqlBinding::createString(tag)); + in_bindings.push_back(MySqlBinding::createInteger(option->option_->getType())); + in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_)); + conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6, + in_bindings); + + } else { + insertOption6(server_selector, in_bindings); + + } + + transaction.commit(); + } + + /// @brief Sends query to insert or update DHCP option in a subnet. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet the option belongs to. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption6(const ServerSelector& server_selector, + const SubnetID& subnet_id, + const OptionDescriptorPtr& option) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (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), + MySqlBinding::condCreateString(option->formatted_value_), + MySqlBinding::condCreateString(option->space_name_), + MySqlBinding::createInteger(static_cast(option->persistent_)), + MySqlBinding::createNull(), + MySqlBinding::createInteger(static_cast(subnet_id)), + MySqlBinding::createInteger(1), + createInputContextBinding(option), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createTimestamp(option->getModificationTime()) + }; + + + MySqlTransaction transaction(conn_); + + OptionDescriptorPtr existing_option = getOption6(server_selector, subnet_id, + option->option_->getType(), + option->space_name_); + if (existing_option) { + in_bindings.push_back(MySqlBinding::createString(tag)); + in_bindings.push_back(MySqlBinding::createInteger(static_cast(subnet_id))); + in_bindings.push_back(MySqlBinding::createInteger(option->option_->getType())); + in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_)); + conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SUBNET_ID, + in_bindings); + + } else { + insertOption6(server_selector, in_bindings); + } + + transaction.commit(); + } + + /// @brief Sends query to insert or update DHCP option in a pool. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool. + /// @param pool_end_address Upper bound address of the pool. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption6(const ServerSelector& server_selector, + const IOAddress& pool_start_address, + const IOAddress& pool_end_address, + const OptionDescriptorPtr& option) { + uint64_t pool_id = 0; + Pool6Ptr pool = getPool6(server_selector, pool_start_address, pool_end_address, + pool_id); + if (!pool) { + isc_throw(BadValue, "no pool found for range of " + << pool_start_address << " : " + << pool_end_address); + } + + createUpdateOption6(server_selector, Lease::TYPE_NA, pool_id, option); + } + + /// @brief Sends query to insert or update DHCP option in a pd pool. + /// + /// @param server_selector Server selector. + /// @param pd_pool_prefix Address part of the pd pool prefix. + /// @param pd_pool_prefix_length Length of the pd pool prefix. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption6(const ServerSelector& server_selector, + const asiolink::IOAddress& pd_pool_prefix, + const uint8_t pd_pool_prefix_length, + const OptionDescriptorPtr& option) { + uint64_t pool_id = 0; + Pool6Ptr pool = getPdPool6(server_selector, pd_pool_prefix, + pd_pool_prefix_length, pool_id); + if (!pool) { + isc_throw(BadValue, "no pd pool found for prefix of " + << pd_pool_prefix << "/" + << static_cast(pd_pool_prefix_length)); + } + + createUpdateOption6(server_selector, Lease::TYPE_PD, pool_id, option); + } + + /// @brief Sends query to insert or update DHCP option in a pool. + /// + /// @param selector Server selector. + /// @param pool_type Pool type (Lease::TYPE_NA or Lease::TYPE_PD). + /// @param pool_id Identifier of the pool the option belongs to. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption6(const ServerSelector& server_selector, + Lease::Type pool_type, + const uint64_t pool_id, + const OptionDescriptorPtr& option) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, + "creating or updating pool level option"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger(option->option_->getType()), + createOptionValueBinding(option), + MySqlBinding::condCreateString(option->formatted_value_), + MySqlBinding::condCreateString(option->space_name_), + MySqlBinding::createInteger(static_cast(option->persistent_)), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createInteger( + pool_type == Lease::TYPE_NA ? 5 : 6), + createInputContextBinding(option), + MySqlBinding::createNull(), + pool_type == Lease::TYPE_NA ? + MySqlBinding::createInteger(pool_id) : + MySqlBinding::createNull(), + pool_type == Lease::TYPE_PD ? + MySqlBinding::createInteger(pool_id) : + MySqlBinding::createNull(), + MySqlBinding::createTimestamp(option->getModificationTime()) + }; + + MySqlTransaction transaction(conn_); + OptionDescriptorPtr existing_option = getOption6(server_selector, + pool_type, + pool_id, + option->option_->getType(), + option->space_name_); + if (existing_option) { + in_bindings.push_back(MySqlBinding::createString(tag)); + in_bindings.push_back(MySqlBinding::createInteger(pool_id)); + in_bindings.push_back(MySqlBinding::createInteger(option->option_->getType())); + in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_)); + StatementIndex stmt_idx = + Lease::TYPE_NA ? + MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_POOL_ID : + MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_PD_POOL_ID; + conn_.updateDeleteQuery(stmt_idx, in_bindings); + + } else { + insertOption6(server_selector, in_bindings); + } + + transaction.commit(); + } + + /// @brief Sends query to insert or update DHCP option in a shared network. + /// + /// @param selector Server selector. + /// @param shared_network_name Name of the shared network the option + /// belongs to. + /// @param option Pointer to the option descriptor encapsulating the option. + void createUpdateOption6(const ServerSelector& server_selector, + const std::string& shared_network_name, + const OptionDescriptorPtr& option) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (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), + MySqlBinding::condCreateString(option->formatted_value_), + MySqlBinding::condCreateString(option->space_name_), + MySqlBinding::createInteger(static_cast(option->persistent_)), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createInteger(4), + createInputContextBinding(option), + MySqlBinding::createString(shared_network_name), + MySqlBinding::createNull(), + MySqlBinding::createNull(), + MySqlBinding::createTimestamp(option->getModificationTime()) + }; + + MySqlTransaction transaction(conn_); + + OptionDescriptorPtr existing_option = getOption6(server_selector, shared_network_name, + option->option_->getType(), + option->space_name_); + if (existing_option) { + in_bindings.push_back(MySqlBinding::createString(tag)); + in_bindings.push_back(MySqlBinding::createString(shared_network_name)); + in_bindings.push_back(MySqlBinding::createInteger(option->option_->getType())); + in_bindings.push_back(MySqlBinding::condCreateString(option->space_name_)); + conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl:: + UPDATE_OPTION6_SHARED_NETWORK, + in_bindings); + } else { + insertOption6(server_selector, in_bindings); + } + + transaction.commit(); + } + + /// @brief Sends query to retrieve single option definition by code and + /// option space. + /// + /// @param server_selector Server selector. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option definition or NULL if such + /// option definition doesn't exist. + OptionDefinitionPtr getOptionDef6(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "fetching option definition"); + + OptionDefContainer option_defs; + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + getOptionDefs(GET_OPTION_DEF6_CODE_SPACE, in_bindings, option_defs); + return (option_defs.empty() ? OptionDefinitionPtr() : *option_defs.begin()); + } + + /// @brief Sends query to retrieve all option definitions. + /// + /// @param server_selector Server selector. + /// @param [out] option_defs Reference to the container where option + /// definitions are to be stored. + void + getAllOptionDefs6(const ServerSelector& server_selector, + OptionDefContainer& option_defs) { + auto tags = getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag) + }; + getOptionDefs(MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTION_DEFS6, + in_bindings, option_defs); + } + } + + /// @brief Sends query to retrieve option definitions with modification + /// time later than specified timestamp. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound subnet modification time. + /// @param [out] option_defs Reference to the container where option + /// definitions are to be stored. + void + getModifiedOptionDefs6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time, + OptionDefContainer& option_defs) { + auto tags = getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createTimestamp(modification_time) + }; + getOptionDefs(MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTION_DEFS6, + in_bindings, option_defs); + } + } + + /// @brief Sends query to retrieve single global option by code and + /// option space. + /// + /// @param server_selector Server selector. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option or NULL if such option + /// doesn't exist. + OptionDescriptorPtr + getOption6(const ServerSelector& server_selector, const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "fetching global option"); + + OptionContainer options; + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + getOptions6(GET_OPTION6_CODE_SPACE, in_bindings, Option::V6, options); + return (options.empty() ? OptionDescriptorPtr() : + OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); + } + + /// @brief Sends query to retrieve all global options. + /// + /// @param server_selector Server selector. + /// @return Container holding returned options. + OptionContainer + getAllOptions6(const ServerSelector& server_selector) { + OptionContainer options; + + auto tags = getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag) + }; + getOptions6(MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTIONS6, + in_bindings, Option::V6, options); + } + + return (options); + } + + /// @brief Sends query to retrieve global options with modification + /// time later than specified timestamp. + /// + /// @param server_selector Server selector. + /// @return Container holding returned options. + OptionContainer + getModifiedOptions6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) { + OptionContainer options; + + auto tags = getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createTimestamp(modification_time) + }; + getOptions6(MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTIONS6, + in_bindings, Option::V6, options); + } + + return (options); + } + + /// @brief Sends query to retrieve single option by code and option space + /// for a giben subnet id. + /// + /// @param server_selector Server selector. + /// @param subnet_id Subnet identifier. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option descriptor or NULL if such + /// option doesn't exist. + OptionDescriptorPtr getOption6(const ServerSelector& server_selector, + const SubnetID& subnet_id, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "fetching subnet level option"); + + OptionContainer options; + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(static_cast(subnet_id)), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + getOptions6(GET_OPTION6_SUBNET_ID_CODE_SPACE, in_bindings, Option::V6, + options); + return (options.empty() ? OptionDescriptorPtr() : + OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); + } + + /// @brief Sends query to retrieve single option by code and option space + /// for a given pool id. + /// + /// @param server_selector Server selector. + /// @param pool_type Pool type (Lease::TYPE_NA or Lease::TYPE_PD). + /// @param pool_id Pool identifier in the database. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option descriptor or NULL if such + /// option doesn't exist. + OptionDescriptorPtr getOption6(const ServerSelector& server_selector, + Lease::Type pool_type, + const uint64_t pool_id, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "fetching pool level option"); + + OptionContainer options; + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(pool_id), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + StatementIndex stmt_idx = + pool_type == Lease::TYPE_NA ? + GET_OPTION6_POOL_ID_CODE_SPACE : + GET_OPTION6_PD_POOL_ID_CODE_SPACE; + getOptions6(stmt_idx, in_bindings, Option::V6, options); + return (options.empty() ? OptionDescriptorPtr() : + OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); + } + + /// @brief Sends query to retrieve single option by code and option space + /// for a given shared network. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Shared network name. + /// @param code Option code. + /// @param space Option space name. + /// + /// @return Pointer to the returned option descriptor or NULL if such + /// option doesn't exist. + OptionDescriptorPtr getOption6(const ServerSelector& server_selector, + const std::string& shared_network_name, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "fetching shared network" + " level option"); + + OptionContainer options; + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createString(shared_network_name), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + getOptions6(GET_OPTION6_SHARED_NETWORK_CODE_SPACE, in_bindings, + Option::V6, options); + return (options.empty() ? OptionDescriptorPtr() : + OptionDescriptorPtr(new OptionDescriptor(*options.begin()))); + } + + /// @brief Sends query to insert or update option definition. + /// + /// @param server_selector Server selector. + /// @param option_def Pointer to the option definition to be inserted or updated. + void createUpdateOptionDef6(const ServerSelector& server_selector, + const OptionDefinitionPtr& option_def) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "creating or updating option definition"); + + ElementPtr record_types = Element::createList(); + for (auto field : option_def->getRecordFields()) { + record_types->add(Element::create(static_cast(field))); + } + MySqlBindingPtr record_types_binding = record_types->empty() ? + MySqlBinding::createNull() : MySqlBinding::createString(record_types->str()); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createInteger(option_def->getCode()), + MySqlBinding::createString(option_def->getName()), + MySqlBinding::createString(option_def->getOptionSpaceName().empty() ? + "dhcp6" : option_def->getOptionSpaceName()), + MySqlBinding::createInteger(static_cast(option_def->getType())), + MySqlBinding::createTimestamp(option_def->getModificationTime()), + MySqlBinding::createInteger(static_cast(option_def->getArrayType())), + MySqlBinding::createString(option_def->getEncapsulatedSpace()), + record_types_binding, + createInputContextBinding(option_def) + }; + + MySqlTransaction transaction(conn_); + + // Need to check if this definition already exists. We can't follow + // the same pattern as for shared networks and subnets, to try to insert + // the definition first and fall back to update if the DuplicateEntry + // exception is thrown, because the option code/space is not unique + // within the dhcp6_option_def table. Inserting another option definition + // with existing option code/name would not violate the key and the + // option definition instance would be inserted successfully. Therefore, + // we first fetch the option definition for the given server, code and + // space name. If it exists, we simply update it. + OptionDefinitionPtr existing_definition = getOptionDef6(server_selector, + option_def->getCode(), + option_def->getOptionSpaceName()); + if (existing_definition) { + // Need to add three more bindings for WHERE clause. + in_bindings.push_back(MySqlBinding::createString(tag)); + in_bindings.push_back(MySqlBinding::createInteger(existing_definition->getCode())); + in_bindings.push_back(MySqlBinding::createString(existing_definition->getOptionSpaceName())); + conn_.updateDeleteQuery(MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6, + in_bindings); + + } else { + // If the option definition doesn't exist, let's insert it. + conn_.insertQuery(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6, + in_bindings); + + // Fetch unique identifier of the inserted option definition and use it + // 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(MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_SERVER, + in_server_bindings); + } + + transaction.commit(); + } + + /// @brief Sends query to delete option definition by code and + /// option space name. + /// + /// @param server_selector Server selector. + /// @param code Option code. + /// @param name Option name. + /// @return Number of deleted option definitions. + uint64_t deleteOptionDef6(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting option definition"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTION_DEF6_CODE_NAME, in_bindings)); + } + + /// @brief Deletes global option. + /// + /// @param server_selector Server selector. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption6(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting global option"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTION6, in_bindings)); + } + + /// @brief Deletes subnet level option. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to which deleted option + /// belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption6(const ServerSelector& server_selector, + const SubnetID& subnet_id, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting option for a subnet"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(static_cast(subnet_id)), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTION6_SUBNET_ID, in_bindings)); + } + + /// @brief Deletes pool level option. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound pool address. + /// @param pool_end_address Upper bound pool address. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption6(const db::ServerSelector& server_selector, + const IOAddress& pool_start_address, + const IOAddress& pool_end_address, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting option for a pool"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space), + MySqlBinding::createString(pool_start_address.toText()), + MySqlBinding::createString(pool_end_address.toText()) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTION6_POOL_RANGE, + in_bindings)); + } + + /// @brief Deletes pd pool level option. + /// + /// @param server_selector Server selector. + /// @param pd_pool_prefix Address part of the pd pool prefix. + /// @param pd_pool_prefix_length Length of the pd pool prefix. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption6(const db::ServerSelector& server_selector, + const asiolink::IOAddress& pd_pool_prefix, + const uint8_t pd_pool_prefix_length, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting option for a pd pool"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space), + MySqlBinding::createString(pd_pool_prefix.toText()), + MySqlBinding::createInteger(pd_pool_prefix_length) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTION6_PD_POOL, in_bindings)); + } + + /// @brief Deletes shared network level option. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of the shared network which deleted + /// option belongs to + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + uint64_t deleteOption6(const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const uint16_t code, + const std::string& space) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting option for a shared network"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createString(shared_network_name), + MySqlBinding::createInteger(code), + MySqlBinding::createString(space) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTION6_SHARED_NETWORK, + in_bindings)); + } + + /// @brief Deletes options belonging to a subnet from the database. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the subnet for which options should be + /// deleted. + /// @return Number of deleted options. + uint64_t deleteOptions6(const ServerSelector& server_selector, + const Subnet6Ptr& subnet) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting options for a subnet"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createInteger(subnet->getID()) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTIONS6_SUBNET_ID, + in_bindings)); + } + + /// @brief Deletes options belonging to a shared network from the database. + /// + /// @param server_selector Server selector. + /// @param subnet Pointer to the subnet for which options should be + /// deleted. + /// @return Number of deleted options. + uint64_t deleteOptions6(const ServerSelector& server_selector, + const SharedNetwork6Ptr& shared_network) { + + if (server_selector.amUnassigned()) { + isc_throw(NotImplemented, "managing configuration for no particular server" + " (unassigned) is unsupported at the moment"); + } + + auto tag = getServerTag(server_selector, "deleting options for a shared network"); + + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createString(shared_network->getName()) + }; + + // Run DELETE. + return (conn_.updateDeleteQuery(DELETE_OPTIONS6_SHARED_NETWORK, + in_bindings)); + } +}; + +/// @brief Array of tagged statements. +typedef std::array +TaggedStatementArray; + +/// @brief Prepared MySQL statements used by the backend to insert and +/// retrieve data from the database. +TaggedStatementArray tagged_statements6 = { { + // Select global parameter by name. + { MySqlConfigBackendDHCPv6Impl::GET_GLOBAL_PARAMETER6, + MYSQL_GET_GLOBAL_PARAMETER(dhcp6, AND g.name = ?) + }, + + // Select all global parameters. + { MySqlConfigBackendDHCPv6Impl::GET_ALL_GLOBAL_PARAMETERS6, + MYSQL_GET_GLOBAL_PARAMETER(dhcp6) + }, + + // Select modified global parameters. + { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_GLOBAL_PARAMETERS6, + MYSQL_GET_GLOBAL_PARAMETER(dhcp6, AND g.modification_ts > ?) + }, + + // Select subnet by id. + { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_ID, + MYSQL_GET_SUBNET6(AND s.subnet_id = ?) + }, + + // Select subnet by prefix. + { MySqlConfigBackendDHCPv6Impl::GET_SUBNET6_PREFIX, + MYSQL_GET_SUBNET6(AND s.subnet_prefix = ?) + }, + + // Select all subnets. + { MySqlConfigBackendDHCPv6Impl::GET_ALL_SUBNETS6, + MYSQL_GET_SUBNET6() + }, + + // Select subnets having modification time later than X. + { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SUBNETS6, + MYSQL_GET_SUBNET6(AND s.modification_ts > ?) + }, + + // Select pool by address range. + { MySqlConfigBackendDHCPv6Impl::GET_POOL6_RANGE, + "SELECT" + " p.id," + " p.start_address," + " p.end_address," + " p.dhcp6_subnet_id," + " p.modification_ts," + " x.option_id," + " x.code," + " x.value," + " x.formatted_value," + " x.space," + " x.persistent," + " x.dhcp6_subnet_id," + " x.scope_id," + " x.user_context," + " x.shared_network_name," + " x.pool_id," + " x.pd_pool_id," + " x.modification_ts " + "FROM dhcp6_pool AS p " + "LEFT JOIN dhcp6_options AS x ON x.scope_id = 5 AND p.id = x.pool_id " + "WHERE p.start_address = ? AND p.end_address = ? " + "ORDER BY p.id, x.option_id" + }, + + // Select pd pool. + { MySqlConfigBackendDHCPv6Impl::GET_PD_POOL, + "SELECT" + " p.id," + " p.prefix," + " p.prefix_length," + " p.delegated_prefix_length," + " p.dhcp6_subnet_id," + " p.modification_ts," + " x.option_id," + " x.code," + " x.value," + " x.formatted_value," + " x.space," + " x.persistent," + " x.dhcp6_subnet_id," + " x.scope_id," + " x.user_context," + " x.shared_network_name," + " x.pool_id," + " x.pd_pool_id," + " x.modification_ts " + "FROM dhcp6_pd_pool AS p " + "LEFT JOIN dhcp6_options AS x ON x.scope_id = 6 AND p.id = x.pd_pool_id " + "WHERE p.prefix = ? AND p.prefix_length = ? " + "ORDER BY p.id, x.option_id" + }, + + // Select shared network by name. + { MySqlConfigBackendDHCPv6Impl::GET_SHARED_NETWORK6_NAME, + MYSQL_GET_SHARED_NETWORK6(AND n.name = ?) + }, + + // Select all shared networks. + { MySqlConfigBackendDHCPv6Impl::GET_ALL_SHARED_NETWORKS6, + MYSQL_GET_SHARED_NETWORK6() + }, + + // Select modified shared networks. + { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_SHARED_NETWORKS6, + MYSQL_GET_SHARED_NETWORK6(AND n.modification_ts > ?) + }, + + // Retrieves option definition by code and space. + { MySqlConfigBackendDHCPv6Impl::GET_OPTION_DEF6_CODE_SPACE, + MYSQL_GET_OPTION_DEF(dhcp6, AND d.code = ? AND d.space = ?) + }, + + // Retrieves all option definitions. + { MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTION_DEFS6, + MYSQL_GET_OPTION_DEF(dhcp6) + }, + + // Retrieves modified option definitions. + { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTION_DEFS6, + MYSQL_GET_OPTION_DEF(dhcp6, AND d.modification_ts > ?) + }, + + // Retrieves global option by code and space. + { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_CODE_SPACE, + MYSQL_GET_OPTION6(AND o.scope_id = 0 AND o.code = ? AND o.space = ?) + }, + + // Retrieves all global options. + { MySqlConfigBackendDHCPv6Impl::GET_ALL_OPTIONS6, + MYSQL_GET_OPTION6(AND o.scope_id = 0) + }, + + // Retrieves modified options. + { MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_OPTIONS6, + MYSQL_GET_OPTION6(AND o.scope_id = 0 AND o.modification_ts > ?) + }, + + // Retrieves an option for a given subnet, option code and space. + { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_SUBNET_ID_CODE_SPACE, + MYSQL_GET_OPTION6(AND o.scope_id = 1 AND o.dhcp6_subnet_id = ? AND o.code = ? AND o.space = ?) + }, + + // Retrieves an option for a given pool, option code and space. + { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_POOL_ID_CODE_SPACE, + MYSQL_GET_OPTION6(AND o.scope_id = 5 AND o.pool_id = ? AND o.code = ? AND o.space = ?) + }, + + // Retrieves an option for a given pd pool, option code and space. + { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_PD_POOL_ID_CODE_SPACE, + MYSQL_GET_OPTION6(AND o.scope_id = 6 AND o.pd_pool_id = ? AND o.code = ? AND o.space = ?) + }, + + // Retrieves an option for a given shared network, option code and space. + { MySqlConfigBackendDHCPv6Impl::GET_OPTION6_SHARED_NETWORK_CODE_SPACE, + MYSQL_GET_OPTION6(AND o.scope_id = 4 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?) + }, + + // Insert global parameter. + { MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6, + MYSQL_INSERT_GLOBAL_PARAMETER(dhcp6) + }, + + // Insert association of the global parameter with a server. + { MySqlConfigBackendDHCPv6Impl::INSERT_GLOBAL_PARAMETER6_SERVER, + MYSQL_INSERT_GLOBAL_PARAMETER_SERVER(dhcp6) + }, + + // Insert a subnet. + { MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6, + "INSERT INTO dhcp6_subnet(" + " subnet_id," + " subnet_prefix," + " client_class," + " interface," + " modification_ts," + " preferred_lifetime," + " rapid_commit," + " rebind_timer," + " relay," + " renew_timer," + " require_client_classes," + " reservation_mode," + " shared_network_name," + " user_context," + " valid_lifetime" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" }, + + // Insert association of the subnet with a server. + { MySqlConfigBackendDHCPv6Impl::INSERT_SUBNET6_SERVER, + MYSQL_INSERT_SUBNET_SERVER(dhcp6) + }, + + // Insert pool for a subnet. + { MySqlConfigBackendDHCPv6Impl::INSERT_POOL6, + MYSQL_INSERT_POOL6() + }, + + // Insert pd pool for a subnet. + { MySqlConfigBackendDHCPv6Impl::INSERT_PD_POOL, + MYSQL_INSERT_PD_POOL() + }, + + // Insert a shared network. + { MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6, + "INSERT INTO dhcp6_shared_network(" + " name," + " client_class," + " interface," + " modification_ts," + " preferred_lifetime," + " rapid_commit," + " rebind_timer," + " relay," + " renew_timer," + " require_client_classes," + " reservation_mode," + " user_context," + " valid_lifetime" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" }, + + // Insert association of the shared network with a server. + { MySqlConfigBackendDHCPv6Impl::INSERT_SHARED_NETWORK6_SERVER, + MYSQL_INSERT_SHARED_NETWORK_SERVER(dhcp6) + }, + + // Insert option definition. + { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6, + MYSQL_INSERT_OPTION_DEF(dhcp6) + }, + + // Insert association of the option definition with a server. + { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION_DEF6_SERVER, + MYSQL_INSERT_OPTION_DEF_SERVER(dhcp6) + }, + + // Insert subnet specific option. + { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6, + MYSQL_INSERT_OPTION6() + }, + + // Insert association of the DHCP option with a server. + { MySqlConfigBackendDHCPv6Impl::INSERT_OPTION6_SERVER, + MYSQL_INSERT_OPTION_SERVER(dhcp6) + }, + + // Update existing global parameter. + { MySqlConfigBackendDHCPv6Impl::UPDATE_GLOBAL_PARAMETER6, + MYSQL_UPDATE_GLOBAL_PARAMETER(dhcp6) + }, + + // Update existing subnet. + { MySqlConfigBackendDHCPv6Impl::UPDATE_SUBNET6, + "UPDATE dhcp6_subnet SET" + " subnet_id = ?," + " subnet_prefix = ?," + " client_class = ?," + " interface = ?," + " modification_ts = ?," + " preferred_lifetime = ?," + " rapid_commit = ?," + " rebind_timer = ?," + " relay = ?," + " renew_timer = ?," + " require_client_classes = ?," + " reservation_mode = ?," + " shared_network_name = ?," + " user_context = ?," + " valid_lifetime = ? " + "WHERE subnet_id = ?" }, + + // Update existing shared network. + { MySqlConfigBackendDHCPv6Impl::UPDATE_SHARED_NETWORK6, + "UPDATE dhcp6_shared_network SET" + " name = ?," + " client_class = ?," + " interface = ?," + " modification_ts = ?," + " preferred_lifetime = ?," + " rapid_commit = ?," + " rebind_timer = ?," + " relay = ?," + " renew_timer = ?," + " require_client_classes = ?," + " reservation_mode = ?," + " user_context = ?," + " valid_lifetime = ? " + "WHERE name = ?" }, + + // Update existing option definition. + { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION_DEF6, + MYSQL_UPDATE_OPTION_DEF(dhcp6) + }, + + // Update existing global option. + { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6, + MYSQL_UPDATE_OPTION6(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 = ?) + }, + + // 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 = ?) + }, + + // 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 = ?) + }, + + // Update existing shared network level option. + { MySqlConfigBackendDHCPv6Impl::UPDATE_OPTION6_SHARED_NETWORK, + MYSQL_UPDATE_OPTION6(AND o.scope_id = 6 AND o.shared_network_name = ? AND o.code = ? AND o.space = ?) + }, + + // Delete global parameter by name. + { MySqlConfigBackendDHCPv6Impl::DELETE_GLOBAL_PARAMETER6, + MYSQL_DELETE_GLOBAL_PARAMETER(dhcp6, AND g.name = ?) + }, + + // Delete all global parameters. + { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_PARAMETERS6, + MYSQL_DELETE_GLOBAL_PARAMETER(dhcp6) + }, + + // Delete subnet by id. + { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID, + MYSQL_DELETE_SUBNET(dhcp6, AND s.subnet_id = ?) + }, + + // Delete subnet by prefix. + { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX, + MYSQL_DELETE_SUBNET(dhcp6, AND s.subnet_prefix = ?) + }, + + // Delete all subnets. + { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6, + MYSQL_DELETE_SUBNET(dhcp6) + }, + + // Delete pools for a subnet. + { MySqlConfigBackendDHCPv6Impl::DELETE_POOLS6_SUBNET_ID, + MYSQL_DELETE_POOLS6() + }, + + // Delete pools for a subnet. + { MySqlConfigBackendDHCPv6Impl::DELETE_PD_POOLS_SUBNET_ID, + MYSQL_DELETE_PD_POOLS() + }, + + // Delete shared network by name. + { MySqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME, + MYSQL_DELETE_SHARED_NETWORK(dhcp6, AND n.name = ?) + }, + + // Delete all shared networks. + { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6, + MYSQL_DELETE_SHARED_NETWORK(dhcp6) + }, + + // Delete option definition. + { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION_DEF6_CODE_NAME, + MYSQL_DELETE_OPTION_DEF(dhcp6, AND code = ? AND space = ?) + }, + + // Delete all option definitions. + { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6, + MYSQL_DELETE_OPTION_DEF(dhcp6) + }, + + // Delete single global option. + { MySqlConfigBackendDHCPv6Impl::DELETE_OPTION6, + MYSQL_DELETE_OPTION(dhcp6, AND o.scope_id = 0 AND o.code = ? AND o.space = ?) + }, + + // 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 = ?) + }, + + // 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 = ?) + }, + + // 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 = ?) + }, + + // 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 = ?) + }, + + // 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 = ?) + }, + + // 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 = ?) + } +} +}; + +MySqlConfigBackendDHCPv6Impl:: +MySqlConfigBackendDHCPv6Impl(const DatabaseConnection::ParameterMap& parameters) + : MySqlConfigBackendImpl(parameters) { + // Prepare query statements. Those are will be only used to retrieve + // information from the database, so they can be used even if the + // database is read only for the current user. + conn_.prepareStatements(tagged_statements6.begin(), + tagged_statements6.end()); +// tagged_statements6.begin() + WRITE_STMTS_BEGIN); +} + +MySqlConfigBackendDHCPv6:: +MySqlConfigBackendDHCPv6(const DatabaseConnection::ParameterMap& parameters) + : impl_(new MySqlConfigBackendDHCPv6Impl(parameters)) { +} + +Subnet6Ptr +MySqlConfigBackendDHCPv6::getSubnet6(const ServerSelector& server_selector, + const std::string& subnet_prefix) const { + return (impl_->getSubnet6(server_selector, subnet_prefix)); +} + +Subnet6Ptr +MySqlConfigBackendDHCPv6::getSubnet6(const ServerSelector& server_selector, + const SubnetID& subnet_id) const { + return (impl_->getSubnet6(server_selector, subnet_id)); +} + +Subnet6Collection +MySqlConfigBackendDHCPv6::getAllSubnets6(const ServerSelector& server_selector) const { + Subnet6Collection subnets; + impl_->getAllSubnets6(server_selector, subnets); + return (subnets); +} + +Subnet6Collection +MySqlConfigBackendDHCPv6::getModifiedSubnets6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + Subnet6Collection subnets; + impl_->getModifiedSubnets6(server_selector, modification_time, subnets); + return (subnets); +} + +SharedNetwork6Ptr +MySqlConfigBackendDHCPv6::getSharedNetwork6(const ServerSelector& server_selector, + const std::string& name) const { + return (impl_->getSharedNetwork6(server_selector, name)); +} + +SharedNetwork6Collection +MySqlConfigBackendDHCPv6::getAllSharedNetworks6(const ServerSelector& server_selector) const { + SharedNetwork6Collection shared_networks; + impl_->getAllSharedNetworks6(server_selector, shared_networks); + return (shared_networks); +} + +SharedNetwork6Collection +MySqlConfigBackendDHCPv6:: +getModifiedSharedNetworks6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + SharedNetwork6Collection shared_networks; + impl_->getModifiedSharedNetworks6(server_selector, modification_time, shared_networks); + return (shared_networks); +} + +OptionDefinitionPtr +MySqlConfigBackendDHCPv6::getOptionDef6(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) const { + return (impl_->getOptionDef6(server_selector, code, space)); +} + +OptionDefContainer +MySqlConfigBackendDHCPv6::getAllOptionDefs6(const ServerSelector& server_selector) const { + OptionDefContainer option_defs; + impl_->getAllOptionDefs6(server_selector, option_defs); + return (option_defs); +} + +OptionDefContainer +MySqlConfigBackendDHCPv6:: +getModifiedOptionDefs6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + OptionDefContainer option_defs; + impl_->getModifiedOptionDefs6(server_selector, modification_time, option_defs); + return (option_defs); +} + +OptionDescriptorPtr +MySqlConfigBackendDHCPv6::getOption6(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) const { + return (impl_->getOption6(server_selector, code, space)); +} + +OptionContainer +MySqlConfigBackendDHCPv6::getAllOptions6(const ServerSelector& server_selector) const { + return (impl_->getAllOptions6(server_selector)); +} + +OptionContainer +MySqlConfigBackendDHCPv6:: +getModifiedOptions6(const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + return (impl_->getModifiedOptions6(server_selector, modification_time)); +} + +StampedValuePtr +MySqlConfigBackendDHCPv6::getGlobalParameter6(const ServerSelector& server_selector, + const std::string& name) const { + return (impl_->getGlobalParameter6(server_selector, name)); +} + +StampedValueCollection +MySqlConfigBackendDHCPv6::getAllGlobalParameters6(const ServerSelector& server_selector) const { + StampedValueCollection parameters; + + auto tags = impl_->getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { MySqlBinding::createString(tag) }; + impl_->getGlobalParameters6(MySqlConfigBackendDHCPv6Impl::GET_ALL_GLOBAL_PARAMETERS6, + in_bindings, parameters); + } + return (parameters); +} + +StampedValueCollection +MySqlConfigBackendDHCPv6:: +getModifiedGlobalParameters6(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + StampedValueCollection parameters; + + auto tags = impl_->getServerTags(server_selector); + for (auto tag : tags) { + MySqlBindingCollection in_bindings = { + MySqlBinding::createString(tag), + MySqlBinding::createTimestamp(modification_time) + }; + impl_->getGlobalParameters6(MySqlConfigBackendDHCPv6Impl::GET_MODIFIED_GLOBAL_PARAMETERS6, + in_bindings, parameters); + } + + return (parameters); +} + +void +MySqlConfigBackendDHCPv6::createUpdateSubnet6(const ServerSelector& server_selector, + const Subnet6Ptr& subnet) { + impl_->createUpdateSubnet6(server_selector, subnet); +} + +void +MySqlConfigBackendDHCPv6::createUpdateSharedNetwork6(const ServerSelector& server_selector, + const SharedNetwork6Ptr& shared_network) { + impl_->createUpdateSharedNetwork6(server_selector, shared_network); +} + +void +MySqlConfigBackendDHCPv6::createUpdateOptionDef6(const ServerSelector& server_selector, + const OptionDefinitionPtr& option_def) { + impl_->createUpdateOptionDef6(server_selector, option_def); +} + +void +MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector, + const OptionDescriptorPtr& option) { + impl_->createUpdateOption6(server_selector, option); +} + +void +MySqlConfigBackendDHCPv6::createUpdateOption6(const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const OptionDescriptorPtr& option) { + impl_->createUpdateOption6(server_selector, shared_network_name, option); +} + +void +MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector, + const SubnetID& subnet_id, + const OptionDescriptorPtr& option) { + impl_->createUpdateOption6(server_selector, subnet_id, option); +} + +void +MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const OptionDescriptorPtr& option) { + impl_->createUpdateOption6(server_selector, pool_start_address, pool_end_address, + option); +} + +void +MySqlConfigBackendDHCPv6::createUpdateOption6(const ServerSelector& server_selector, + const asiolink::IOAddress& pd_pool_prefix, + const uint8_t pd_pool_prefix_length, + const OptionDescriptorPtr& option) { + impl_->createUpdateOption6(server_selector, pd_pool_prefix, + pd_pool_prefix_length, option); +} + +void +MySqlConfigBackendDHCPv6::createUpdateGlobalParameter6(const ServerSelector& server_selector, + const StampedValuePtr& value) { + impl_->createUpdateGlobalParameter6(server_selector, value); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector, + const std::string& subnet_prefix) { + return(impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX, + server_selector, "deleting a subnet by prefix", + subnet_prefix)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector, + const SubnetID& subnet_id) { + return (impl_->deleteSubnet6(server_selector, subnet_id)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteAllSubnets6(const ServerSelector& server_selector) { + return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6, + server_selector, "deleting all subnets")); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteSharedNetwork6(const ServerSelector& server_selector, + const std::string& name) { + return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_SHARED_NETWORK6_NAME, + server_selector, "deleting a shared network", + name)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteAllSharedNetworks6(const ServerSelector& server_selector) { + return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SHARED_NETWORKS6, + server_selector, "deleting all shared networks")); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteOptionDef6(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + return (impl_->deleteOptionDef6(server_selector, code, space)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteAllOptionDefs6(const ServerSelector& server_selector) { + return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_OPTION_DEFS6, + server_selector, "deleting all option definitions")); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + return (impl_->deleteOption6(server_selector, code, space)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector, + const std::string& shared_network_name, + const uint16_t code, + const std::string& space) { + return (impl_->deleteOption6(server_selector, shared_network_name, + code, space)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteOption6(const ServerSelector& server_selector, + const SubnetID& subnet_id, + const uint16_t code, + const std::string& space) { + return (impl_->deleteOption6(server_selector, subnet_id, code, space)); +} + +uint64_t +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) { + return (impl_->deleteOption6(server_selector, pool_start_address, pool_end_address, + code, space)); +} + +uint64_t +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) { + return (impl_->deleteOption6(server_selector, pd_pool_prefix, + pd_pool_prefix_length, code, space)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteGlobalParameter6(const ServerSelector& server_selector, + const std::string& name) { + return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_GLOBAL_PARAMETER6, + server_selector, "deleting global parameter", + name)); +} + +uint64_t +MySqlConfigBackendDHCPv6::deleteAllGlobalParameters6(const ServerSelector& server_selector) { + return (impl_->deleteFromTable(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_GLOBAL_PARAMETERS6, + server_selector, "deleting all global parameters")); +} + +std::string +MySqlConfigBackendDHCPv6::getType() const { + return (impl_->getType()); +} + +std::string +MySqlConfigBackendDHCPv6::getHost() const { + return (impl_->getHost()); +} + +uint16_t +MySqlConfigBackendDHCPv6::getPort() const { + return (impl_->getPort()); +} + +bool +MySqlConfigBackendDHCPv6::registerBackendType() { + return ( + dhcp::ConfigBackendDHCPv6Mgr::instance().registerBackendFactory("mysql", + [](const db::DatabaseConnection::ParameterMap& params) -> dhcp::ConfigBackendDHCPv6Ptr { + return (dhcp::MySqlConfigBackendDHCPv6Ptr(new dhcp::MySqlConfigBackendDHCPv6(params))); + }) + ); +} + +void +MySqlConfigBackendDHCPv6::unregisterBackendType() { + dhcp::ConfigBackendDHCPv6Mgr::instance().unregisterBackendFactory("mysql"); +} + +} // end of namespace isc::dhcp +} // end of namespace isc diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h new file mode 100644 index 0000000000..b9c0581d6c --- /dev/null +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h @@ -0,0 +1,475 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef MYSQL_CONFIG_BACKEND_DHCP6_H +#define MYSQL_CONFIG_BACKEND_DHCP6_H + +#include +#include +#include + +namespace isc { +namespace dhcp { + +class MySqlConfigBackendDHCPv6Impl; + +/// @brief Implementation of the MySql Configuration Backend for +/// Kea DHCPv6 server. +class MySqlConfigBackendDHCPv6 : public ConfigBackendDHCPv6 { +public: + + /// @brief Constructor. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + explicit MySqlConfigBackendDHCPv6(const db::DatabaseConnection::ParameterMap& parameters); + + /// @brief Retrieves a single subnet by subnet_prefix. + /// + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + /// @throw NotImplemented if server selector is "unassigned". + virtual Subnet6Ptr + getSubnet6(const db::ServerSelector& server_selector, + const std::string& subnet_prefix) const; + + /// @brief Retrieves a single subnet by subnet identifier. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of a subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + /// @throw NotImplemented if server selector is "unassigned". + virtual Subnet6Ptr + getSubnet6(const db::ServerSelector& server_selector, const SubnetID& subnet_id) const; + + /// @brief Retrieves all subnets. + /// + /// @param server_selector Server selector. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet6Collection + getAllSubnets6(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves subnets modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound subnet modification time. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet6Collection + getModifiedSubnets6(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves shared network by name. + /// + /// @param server_selector Server selector. + /// @param name Name of the shared network to be retrieved. + /// @return Pointer to the shared network or NULL if not found. + /// @throw NotImplemented if server selector is "unassigned". + virtual SharedNetwork6Ptr + getSharedNetwork6(const db::ServerSelector& server_selector, + const std::string& name) const; + + /// @brief Retrieves all shared networks. + /// + /// @param server_selector Server selector. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork6Collection + getAllSharedNetworks6(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves shared networks modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound shared network modification time. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork6Collection + getModifiedSharedNetworks6(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves single option definition by code and space. + /// + /// @param server_selector Server selector. + /// @param code Code of the option to be retrieved. + /// @param space Option space of the option to be retrieved. + /// @return Pointer to the option definition or NULL if not found. + /// @throw NotImplemented if server selector is "unassigned". + virtual OptionDefinitionPtr + getOptionDef6(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space) const; + + /// @brief Retrieves all option definitions. + /// + /// @param server_selector Server selector. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getAllOptionDefs6(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves option definitions modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound option definition modification + /// time. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getModifiedOptionDefs6(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves single option by code and space. + /// + /// @param server_selector Server selector. + /// @return Pointer to the retrieved option descriptor or null if + /// no option was found. + /// @throw NotImplemented if server selector is "unassigned". + virtual OptionDescriptorPtr + getOption6(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space) const; + + /// @brief Retrieves all global options. + /// + /// @param server_selector Server selector. + /// @return Collection of global options or empty collection if no + /// option found. + virtual OptionContainer + getAllOptions6(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves option modified after specified time. + /// + /// @param server_selector Server selector. + /// @param modification_time Lower bound option modification time. + /// @return Collection of global options or empty collection if no + /// option found. + virtual OptionContainer + getModifiedOptions6(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves global parameter value. + /// + /// @param server_selector Server selector. + /// @param name Name of the global parameter to be retrieved. + /// @return Value of the global parameter. + /// @throw NotImplemented if server selector is "unassigned". + virtual data::StampedValuePtr + getGlobalParameter6(const db::ServerSelector& server_selector, + const std::string& name) const; + + /// @brief Retrieves all global parameters. + /// + /// @param server_selector Server selector. + virtual data::StampedValueCollection + getAllGlobalParameters6(const db::ServerSelector& server_selector) const; + + /// @brief Retrieves global parameters modified after specified time. + /// + /// @param modification_time Lower bound modification time. + /// @return Collection of modified global parameters. + virtual data::StampedValueCollection + getModifiedGlobalParameters6(const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Creates or updates a subnet. + /// + /// @param server_selector Server selector. + /// @param subnet Subnet to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateSubnet6(const db::ServerSelector& server_selector, + const Subnet6Ptr& subnet); + + /// @brief Creates or updates a shared network. + /// + /// @param server_selector Server selector. + /// @param shared_network Shared network to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateSharedNetwork6(const db::ServerSelector& server_selector, + const SharedNetwork6Ptr& shared_network); + + /// @brief Creates or updates an option definition. + /// + /// @param server_selector Server selector. + /// @param option_def Option definition to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOptionDef6(const db::ServerSelector& server_selector, + const OptionDefinitionPtr& option_def); + + /// @brief Creates or updates global option. + /// + /// @param server_selector Server selector. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption6(const db::ServerSelector& server_selector, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates shared network level option. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of a shared network to which option + /// belongs. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption6(const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates subnet level option. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of a subnet to which option belongs. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption6(const db::ServerSelector& server_selector, + const SubnetID& subnet_id, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates pool level option. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// the option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// option belongs. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption6(const db::ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates pool level option. + /// + /// @param server_selector Server selector. + /// @param pd_pool_prefix Address part of the prefix of the pd pool + /// to which the the option belongs. + /// @param pd_pool_prefix_length Prefix length of the pd pool to which + /// the option belongs. + /// @param option Option to be added or updated. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateOption6(const db::ServerSelector& server_selector, + const asiolink::IOAddress& pd_pool_prefix, + const uint8_t pd_pool_prefix_length, + const OptionDescriptorPtr& option); + + /// @brief Creates or updates global parameter. + /// + /// @param server_selector Server selector. + /// @param name Name of the global parameter. + /// @param value Value of the global parameter. + /// @throw NotImplemented if server selector is "unassigned". + virtual void + createUpdateGlobalParameter6(const db::ServerSelector& server_selector, + const data::StampedValuePtr& value); + + /// @brief Deletes subnet by prefix. + /// + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be deleted. + /// @return Number of deleted subnets. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteSubnet6(const db::ServerSelector& server_selector, + const std::string& subnet_prefix); + + /// @brief Deletes subnet by identifier. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to be deleted. + /// @return Number of deleted subnets. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteSubnet6(const db::ServerSelector& server_selector, const SubnetID& subnet_id); + + /// @brief Deletes all subnets. + /// + /// @param server_selector Server selector. + /// @return Number of deleted subnets. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteAllSubnets6(const db::ServerSelector& server_selector); + + /// @brief Deletes shared network by name. + /// + /// @param server_selector Server selector. + /// @param name Name of the shared network to be deleted. + /// @return Number of deleted shared networks. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteSharedNetwork6(const db::ServerSelector& server_selector, + const std::string& name); + + /// @brief Deletes all shared networks. + /// + /// @param server_selector Server selector. + /// @return Number of deleted shared networks. + virtual uint64_t + deleteAllSharedNetworks6(const db::ServerSelector& server_selector); + + /// @brief Deletes option definition. + /// + /// @param server_selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + /// @return Number of deleted option definitions. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOptionDef6(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space); + + /// @brief Deletes all option definitions. + /// + /// @param server_selector Server selector. + /// @return Number of deleted option definitions. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteAllOptionDefs6(const db::ServerSelector& server_selector); + + /// @brief Deletes global option. + /// + /// @param server_selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + /// @return Number of deleted options. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption6(const db::ServerSelector& server_selector, const uint16_t code, + const std::string& space); + + /// @brief Deletes shared network level option. + /// + /// @param server_selector Server selector. + /// @param shared_network_name Name of the shared network which deleted + /// option belongs to + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption6(const db::ServerSelector& server_selector, + const std::string& shared_network_name, + const uint16_t code, + const std::string& space); + + /// @brief Deletes subnet level option. + /// + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to which deleted option + /// belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption6(const db::ServerSelector& server_selector, const SubnetID& subnet_id, + const uint16_t code, const std::string& space); + + /// @brief Deletes pool level option. + /// + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// deleted option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// deleted option belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption6(const db::ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const uint16_t code, + const std::string& space); + + /// @brief Deletes pool level option. + /// + /// @param server_selector Server selector. + /// @param pd_pool_prefix Address part of the prefix of the pd pool + /// to which the the option belongs. + /// @param pd_pool_prefix_length Prefix length of the pd pool to which + /// the option belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + /// @return Number of deleted options. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteOption6(const db::ServerSelector& server_selector, + const asiolink::IOAddress& pd_pool_prefix, + const uint8_t pd_pool_prefix_length, + const uint16_t code, + const std::string& space); + + /// @brief Deletes global parameter. + /// + /// @param server_selector Server selector. + /// @param name Name of the global parameter to be deleted. + /// @return Number of deleted global parameters. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteGlobalParameter6(const db::ServerSelector& server_selector, + const std::string& name); + + /// @brief Deletes all global parameters. + /// + /// @param server_selector Server selector. + /// @return Number of deleted global parameters. + /// @throw NotImplemented if server selector is "unassigned". + virtual uint64_t + deleteAllGlobalParameters6(const db::ServerSelector& server_selector); + + /// @brief Returns backend type in the textual format. + /// + /// @return "mysql". + virtual std::string getType() const; + + /// @brief Returns backend host. + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return host on which the database is located. + virtual std::string getHost() const; + + /// @brief Returns backend port number. + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return Port number on which database service is available. + virtual uint16_t getPort() const; + + /// @brief Registers the MySQL backend factory with backend config manager + /// + /// This should be called by the hook lib load() function. + /// @return True if the factory was registered successfully, false otherwise. + static bool registerBackendType(); + + /// @brief Unregisters the MySQL backend factory and discards MySQL backends + /// + /// This should be called by the hook lib unload() function. + static void unregisterBackendType(); + +private: + + /// @brief Pointer to the implementation of the @c MySqlConfigBackendDHCPv6 + /// class. + boost::shared_ptr impl_; + +}; + +/// @brief Pointer to the @c MySqlConfigBackendDHCPv6 class. +typedef boost::shared_ptr MySqlConfigBackendDHCPv6Ptr; + +} // end of namespace isc::cb +} // end of namespace isc + +#endif // MYSQL_CONFIG_BACKEND_DHCP6_H diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc index 01ced7ad9f..69d026dff9 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -190,10 +190,10 @@ MySqlConfigBackendImpl::getOptionDefs(const int index, } void -MySqlConfigBackendImpl::getOptions(const int index, - const db::MySqlBindingCollection& in_bindings, - const Option::Universe& universe, - OptionContainer& options) { +MySqlConfigBackendImpl::getOptions4(const int index, + const db::MySqlBindingCollection& in_bindings, + const Option::Universe& universe, + OptionContainer& options) { // Create output bindings. The order must match that in the prepared // statement. MySqlBindingCollection out_bindings = { @@ -222,7 +222,7 @@ MySqlConfigBackendImpl::getOptions(const int index, (last_option_id < out_bindings[0]->getInteger()))) { last_option_id = out_bindings[0]->getInteger(); - OptionDescriptorPtr desc = processOptionRow(universe, out_bindings.begin()); + OptionDescriptorPtr desc = processOptionRow4(universe, out_bindings.begin()); if (desc) { options.push_back(*desc); } @@ -230,16 +230,61 @@ MySqlConfigBackendImpl::getOptions(const int index, }); } -OptionDescriptorPtr -MySqlConfigBackendImpl::processOptionRow(const Option::Universe& universe, - MySqlBindingCollection::iterator first_binding) { +void +MySqlConfigBackendImpl::getOptions6(const int index, + const db::MySqlBindingCollection& in_bindings, + const Option::Universe& universe, + OptionContainer& options) { + // Create output bindings. The order must match that in the prepared + // statement. + MySqlBindingCollection out_bindings = { + MySqlBinding::createInteger(), // option_id + MySqlBinding::createInteger(), // code + MySqlBinding::createBlob(OPTION_VALUE_BUF_LENGTH), // value + MySqlBinding::createString(FORMATTED_OPTION_VALUE_BUF_LENGTH), // formatted_value + MySqlBinding::createString(OPTION_SPACE_BUF_LENGTH), // space + MySqlBinding::createInteger(), // persistent + MySqlBinding::createInteger(), // dhcp6_subnet_id + MySqlBinding::createInteger(), // scope_id + MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context + MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // shared_network_name + MySqlBinding::createInteger(), // pool_id + MySqlBinding::createInteger(), // pd_pool_id + MySqlBinding::createTimestamp() //modification_ts + }; + + uint64_t last_option_id = 0; + + conn_.selectQuery(index, in_bindings, out_bindings, + [this, universe, &options, &last_option_id] + (MySqlBindingCollection& out_bindings) { + // Parse option. + if (!out_bindings[0]->amNull() && + ((last_option_id == 0) || + (last_option_id < out_bindings[0]->getInteger()))) { + last_option_id = out_bindings[0]->getInteger(); + + OptionDescriptorPtr desc = processOptionRow6(universe, out_bindings.begin()); + if (desc) { + options.push_back(*desc); + } + } + }); +} + +namespace { + +template OptionDescriptorPtr +processOptionRowCommon(const Option::Universe& universe, + MySqlBindingCollection::iterator first_binding, + const size_t tm_off) { // Some of the options have standard or custom definitions. // Depending whether the option has a definition or not a different // C++ class may be used to represent the option. Therefore, the // first thing to do is to see if there is a definition for our // parsed option. The option code and space is needed for it. std::string space = (*(first_binding + 4))->getString(); - uint16_t code = (*(first_binding + 1))->getInteger(); + uint16_t code = (*(first_binding + 1))->getInteger(); // See if the option has standard definition. OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code); @@ -296,11 +341,25 @@ MySqlConfigBackendImpl::processOptionRow(const Option::Universe& universe, // its option space and timestamp. OptionDescriptorPtr desc(new OptionDescriptor(option, persistent, formatted_value)); desc->space_name_ = space; - desc->setModificationTime((*(first_binding + 11))->getTimestamp()); + desc->setModificationTime((*(first_binding + tm_off))->getTimestamp()); return (desc); } +} + +OptionDescriptorPtr +MySqlConfigBackendImpl::processOptionRow4(const Option::Universe& universe, + MySqlBindingCollection::iterator first_binding) { + return (processOptionRowCommon(universe, first_binding, 11)); +} + +OptionDescriptorPtr +MySqlConfigBackendImpl::processOptionRow6(const Option::Universe& universe, + MySqlBindingCollection::iterator first_binding) { + return (processOptionRowCommon(universe, first_binding, 12)); +} + 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 235e6b18c9..ea403b3a02 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -188,12 +188,29 @@ public: /// @param universe Option universe, i.e. V4 or V6. /// @param [out] options Reference to the container where fetched options /// will be inserted. - void getOptions(const int index, - const db::MySqlBindingCollection& in_bindings, - const Option::Universe& universe, - OptionContainer& options); + void getOptions4(const int index, + const db::MySqlBindingCollection& in_bindings, + const Option::Universe& universe, + OptionContainer& options); - /// @brief Returns DHCP option instance from output bindings. + /// @brief Sends query to the database to retrieve multiple options. + /// + /// Query should order by option_id. + /// + /// @param index Index of the query to be used. + /// @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 + /// if the query contains no WHERE clause. + /// @param universe Option universe, i.e. V4 or V6. + /// @param [out] options Reference to the container where fetched options + /// will be inserted. + void getOptions6(const int index, + const db::MySqlBindingCollection& in_bindings, + const Option::Universe& universe, + OptionContainer& options); + + /// @brief Returns DHCPv4 option instance from output bindings. /// /// The following is the expected order of columns specified in the SELECT /// query: @@ -214,8 +231,33 @@ public: /// @param first_binding Iterator of the output binding containing /// option_id. OptionDescriptorPtr - processOptionRow(const Option::Universe& universe, - db::MySqlBindingCollection::iterator first_binding); + processOptionRow4(const Option::Universe& universe, + db::MySqlBindingCollection::iterator first_binding); + + /// @brief Returns DHCPv6 option instance from output bindings. + /// + /// The following is the expected order of columns specified in the SELECT + /// query: + /// - option_id, + /// - code, + /// - value, + /// - formatted_value, + /// - space, + /// - persistent, + /// - dhcp6_subnet_id, + /// - scope_id, + /// - user_context, + /// - shared_network_name, + /// - pool_id, + /// - pd_pool_id, + /// - modification_ts + /// + /// @param universe V4 or V6. + /// @param first_binding Iterator of the output binding containing + /// option_id. + OptionDescriptorPtr + processOptionRow6(const Option::Universe& universe, + db::MySqlBindingCollection::iterator first_binding); /// @brief Creates input binding for relay addresses. /// 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 896499a769..d7d299ae8f 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h +++ b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -110,6 +110,88 @@ namespace { " ORDER BY s.subnet_id, p.id, x.option_id, o.option_id" #endif +#ifndef MYSQL_GET_SUBNET6 +#define MYSQL_GET_SUBNET6(...) \ + "SELECT" \ + " s.subnet_id," \ + " s.subnet_prefix," \ + " s.client_class," \ + " s.interface," \ + " s.modification_ts," \ + " s.preferred_lifetime," \ + " s.rapid_commit," \ + " s.rebind_timer," \ + " s.relay," \ + " s.renew_timer," \ + " s.require_client_classes," \ + " s.reservation_mode," \ + " s.shared_network_name," \ + " s.user_context," \ + " s.valid_lifetime," \ + " p.id," \ + " p.start_address," \ + " p.end_address," \ + " p.dhcp6_subnet_id," \ + " p.modification_ts," \ + " d.id," \ + " d.prefix," \ + " d.prefix_length," \ + " d.delegated_prefix_length," \ + " d.dhcp6_subnet_id," \ + " d.modification_ts," \ + " x.option_id," \ + " x.code," \ + " x.value," \ + " x.formatted_value," \ + " x.space," \ + " x.persistent," \ + " x.dhcp6_subnet_id," \ + " x.scope_id," \ + " x.user_context," \ + " x.shared_network_name," \ + " x.pool_id," \ + " x.pd_pool_id," \ + " x.modification_ts," \ + " y.option_id," \ + " y.code," \ + " y.value," \ + " y.formatted_value," \ + " y.space," \ + " y.persistent," \ + " y.dhcp6_subnet_id," \ + " y.scope_id," \ + " y.user_context," \ + " y.shared_network_name," \ + " y.pool_id," \ + " y.pd_pool_id," \ + " y.modification_ts," \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o.dhcp6_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " o.pd_pool_id," \ + " o.modification_ts " \ + "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) " \ + "LEFT JOIN dhcp6_pool AS p ON s.subnet_id = p.dhcp6_subnet_id " \ + "LEFT JOIN dhcp6_pd_pool AS d ON s.subnet_id = d.dhcp6_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 p.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__ \ + " ORDER BY s.subnet_id, p.id, d.id, x.option_id, y.option_id, o.option_id" +#endif + #ifndef MYSQL_GET_SHARED_NETWORK4 #define MYSQL_GET_SHARED_NETWORK4(...) \ "SELECT" \ @@ -148,6 +230,46 @@ namespace { " ORDER BY n.id, o.option_id" #endif +#ifndef MYSQL_GET_SHARED_NETWORK6 +#define MYSQL_GET_SHARED_NETWORK6(...) \ + "SELECT" \ + " n.id," \ + " n.name," \ + " n.client_class," \ + " n.interface," \ + " n.modification_ts," \ + " n.preferred_lifetime," \ + " n.rapid_commit," \ + " n.rebind_timer," \ + " n.relay," \ + " n.renew_timer," \ + " n.require_client_classes," \ + " n.reservation_mode," \ + " n.user_context," \ + " n.valid_lifetime," \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o.dhcp6_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " o.pd_pool_id," \ + " o.modification_ts " \ + "FROM dhcp6_shared_network AS n " \ + "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) " \ + "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" +#endif + #ifndef MYSQL_GET_OPTION_DEF #define MYSQL_GET_OPTION_DEF(table_prefix, ...) \ "SELECT" \ @@ -170,8 +292,8 @@ namespace { " ORDER BY d.id" #endif -#ifndef MYSQL_GET_OPTION -#define MYSQL_GET_OPTION(table_prefix, ...) \ +#ifndef MYSQL_GET_OPTION4 +#define MYSQL_GET_OPTION4(...) \ "SELECT" \ " o.option_id," \ " o.code," \ @@ -185,10 +307,35 @@ namespace { " o.shared_network_name," \ " o.pool_id," \ " o.modification_ts " \ - "FROM " #table_prefix "_options AS o " \ - "INNER JOIN " #table_prefix "_options_server AS a" \ + "FROM dhcp4_options AS o " \ + "INNER JOIN dhcp4_options_server AS a" \ " ON o.option_id = a.option_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ + "INNER JOIN dhcp4_server AS s" \ + " ON a.server_id = s.id " \ + "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \ + " ORDER BY o.option_id" +#endif + +#ifndef MYSQL_GET_OPTION6 +#define MYSQL_GET_OPTION6(...) \ + "SELECT" \ + " o.option_id," \ + " o.code," \ + " o.value," \ + " o.formatted_value," \ + " o.space," \ + " o.persistent," \ + " o.dhcp6_subnet_id," \ + " o.scope_id," \ + " o.user_context," \ + " o.shared_network_name," \ + " o.pool_id," \ + " o.pd_pool_id," \ + " o.modification_ts " \ + "FROM dhcp6_options AS o " \ + "INNER JOIN dhcp6_options_server AS a" \ + " ON o.option_id = a.option_id " \ + "INNER JOIN dhcp6_server AS s" \ " ON a.server_id = s.id " \ "WHERE (s.tag = ? OR s.id = 1) " #__VA_ARGS__ \ " ORDER BY o.option_id" @@ -221,9 +368,9 @@ namespace { ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)" #endif -#ifndef MYSQL_INSERT_POOL -#define MYSQL_INSERT_POOL(table_prefix) \ - "INSERT INTO " #table_prefix "_pool(" \ +#ifndef MYSQL_INSERT_POOL4 +#define MYSQL_INSERT_POOL4() \ + "INSERT INTO dhcp4_pool(" \ " start_address," \ " end_address," \ " subnet_id," \ @@ -231,6 +378,27 @@ namespace { ") VALUES (?, ?, ?, ?)" #endif +#ifndef MYSQL_INSERT_POOL6 +#define MYSQL_INSERT_POOL6() \ + "INSERT INTO dhcp6_pool(" \ + " start_address," \ + " end_address," \ + " dhcp6_subnet_id," \ + " modification_ts" \ + ") VALUES (?, ?, ?, ?)" +#endif + +#ifndef MYSQL_INSERT_PD_POOL +#define MYSQL_INSERT_PD_POOL() \ + "INSERT INTO dhcp6_pd_pool(" \ + " prefix," \ + " prefix_length," \ + " delegated_prefix_length," \ + " dhcp6_subnet_id," \ + " modification_ts" \ + ") VALUES (?, ?, ?, ?, ?)" +#endif + #ifndef MYSQL_INSERT_SHARED_NETWORK_SERVER #define MYSQL_INSERT_SHARED_NETWORK_SERVER(table_prefix) \ "INSERT INTO " #table_prefix "_shared_network_server(" \ @@ -267,16 +435,16 @@ namespace { ") VALUES (?, (SELECT id FROM " #table_prefix "_server WHERE tag = ?), ?)" #endif -#ifndef MYSQL_INSERT_OPTION -#define MYSQL_INSERT_OPTION(table_prefix) \ - "INSERT INTO " #table_prefix "_options (" \ +#ifndef MYSQL_INSERT_OPTION4 +#define MYSQL_INSERT_OPTION4() \ + "INSERT INTO dhcp4_options (" \ " code," \ " value," \ " formatted_value," \ " space," \ " persistent," \ " dhcp_client_class," \ - " " #table_prefix "_subnet_id," \ + " dhcp4_subnet_id," \ " scope_id," \ " user_context," \ " shared_network_name," \ @@ -285,6 +453,25 @@ namespace { ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" #endif +#ifndef MYSQL_INSERT_OPTION6 +#define MYSQL_INSERT_OPTION6() \ + "INSERT INTO dhcp6_options (" \ + " code," \ + " value," \ + " formatted_value," \ + " space," \ + " persistent," \ + " dhcp_client_class," \ + " dhcp6_subnet_id," \ + " scope_id," \ + " user_context," \ + " shared_network_name," \ + " pool_id," \ + " pd_pool_id," \ + " modification_ts" \ + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" +#endif + #ifndef MYSQL_INSERT_OPTION_SERVER #define MYSQL_INSERT_OPTION_SERVER(table_prefix) \ "INSERT INTO " #table_prefix "_options_server (" \ @@ -328,12 +515,35 @@ namespace { "WHERE s.tag = ? AND d.code = ? AND d.space = ?" #endif -#ifndef MYSQL_UPDATE_OPTION -#define MYSQL_UPDATE_OPTION(table_prefix, ...) \ - "UPDATE " #table_prefix "_options AS o " \ - "INNER JOIN " #table_prefix "_options_server AS a" \ +#ifndef MYSQL_UPDATE_OPTION4 +#define MYSQL_UPDATE_OPTION4(...) \ + "UPDATE dhcp4_options AS o " \ + "INNER JOIN dhcp4_options_server AS a" \ " ON o.option_id = a.option_id " \ - "INNER JOIN " #table_prefix "_server AS s" \ + "INNER JOIN dhcp4_server AS s" \ + " ON a.server_id = s.id " \ + "SET" \ + " o.code = ?," \ + " o.value = ?," \ + " o.formatted_value = ?," \ + " o.space = ?," \ + " o.persistent = ?," \ + " o.dhcp_client_class = ?," \ + " o.dhcp4_subnet_id = ?," \ + " o.scope_id = ?," \ + " o.user_context = ?," \ + " o.shared_network_name = ?," \ + " o.pool_id = ?," \ + " o.modification_ts = ? " \ + "WHERE s.tag = ? " #__VA_ARGS__ +#endif + +#ifndef MYSQL_UPDATE_OPTION6 +#define MYSQL_UPDATE_OPTION6(...) \ + "UPDATE dhcp6_options AS o " \ + "INNER JOIN dhcp6_options_server AS a" \ + " ON o.option_id = a.option_id " \ + "INNER JOIN dhcp6_server AS s" \ " ON a.server_id = s.id " \ "SET" \ " o.code = ?," \ @@ -342,11 +552,12 @@ namespace { " o.space = ?," \ " o.persistent = ?," \ " o.dhcp_client_class = ?," \ - " o." #table_prefix "_subnet_id = ?," \ + " o.dhcp6_subnet_id = ?," \ " o.scope_id = ?," \ " o.user_context = ?," \ " o.shared_network_name = ?," \ " o.pool_id = ?," \ + " o.pd_pool_id = ?," \ " o.modification_ts = ? " \ "WHERE s.tag = ? " #__VA_ARGS__ #endif @@ -371,12 +582,24 @@ namespace { "WHERE srv.tag = ? " #__VA_ARGS__ #endif -#ifndef MYSQL_DELETE_POOLS -#define MYSQL_DELETE_POOLS(table_prefix) \ - "DELETE FROM " #table_prefix "_pool " \ +#ifndef MYSQL_DELETE_POOLS4 +#define MYSQL_DELETE_POOLS4() \ + "DELETE FROM dhcp4_pool " \ "WHERE subnet_id = ?" #endif +#ifndef MYSQL_DELETE_POOLS6 +#define MYSQL_DELETE_POOLS6() \ + "DELETE FROM dhcp6_pool " \ + "WHERE dhcp6_subnet_id = ?" +#endif + +#ifndef MYSQL_DELETE_PD_POOLS +#define MYSQL_DELETE_PD_POOLS() \ + "DELETE FROM dhcp6_pd_pool " \ + "WHERE dhcp6_subnet_id = ?" +#endif + #ifndef MYSQL_DELETE_SHARED_NETWORK #define MYSQL_DELETE_SHARED_NETWORK(table_prefix, ...) \ "DELETE n FROM " #table_prefix "_shared_network AS n " \ @@ -420,6 +643,19 @@ namespace { " WHERE start_address = ? AND end_address = ?)" #endif +#ifndef MYSQL_DELETE_OPTION_PD_POOL +#define MYSQL_DELETE_OPTION_PD_POOL(...) \ + "DELETE o FROM dhcp6_options AS o " \ + "INNER JOIN dhcp6_options_server AS a" \ + " ON o.option_id = a.option_id " \ + "INNER JOIN dhcp6_server AS s" \ + " ON a.server_id = s.id " \ + "WHERE s.tag = ? " #__VA_ARGS__ \ + " AND o.pd_pool_id = " \ + " (SELECT id FROM dhcp6_pd_pool" \ + " WHERE prefix = ? AND prefix_length = ?)" +#endif + } // end of anonymous namespace } // end of namespace isc::dhcp diff --git a/src/hooks/dhcp/mysql_cb/tests/Makefile.am b/src/hooks/dhcp/mysql_cb/tests/Makefile.am index 6ba01c6eca..6e0f271fac 100644 --- a/src/hooks/dhcp/mysql_cb/tests/Makefile.am +++ b/src/hooks/dhcp/mysql_cb/tests/Makefile.am @@ -25,6 +25,8 @@ TESTS += mysql_cb_unittests mysql_cb_unittests_SOURCES = mysql_cb_dhcp4_unittest.cc mysql_cb_unittests_SOURCES += mysql_cb_dhcp4_mgr_unittest.cc +mysql_cb_unittests_SOURCES += mysql_cb_dhcp6_unittest.cc +mysql_cb_unittests_SOURCES += mysql_cb_dhcp6_mgr_unittest.cc mysql_cb_unittests_SOURCES += run_unittests.cc mysql_cb_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) 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 9643bdd7db..bd5b83d447 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 @@ -1,4 +1,4 @@ -// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -410,12 +410,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllGlobalParameters4) { // Fetch all parameters. auto parameters = cbptr_->getAllGlobalParameters4(ServerSelector::ALL()); - ASSERT_EQ(3, parameters.size()); // Verify their values. - EXPECT_EQ("value1", parameters[0]->getValue()); - EXPECT_EQ(65, parameters[1]->getSignedIntegerValue()); - EXPECT_EQ("value3", parameters[2]->getValue()); + std::vector plist(parameters.begin(), parameters.end()); + ASSERT_EQ(3, plist.size()); + EXPECT_EQ("value1", plist[0]->getValue()); + EXPECT_EQ(65, plist[1]->getSignedIntegerValue()); + EXPECT_EQ("value3", plist[2]->getValue()); // Should be able to fetct these parameters when explicitly providing // the server tag. @@ -455,10 +456,9 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedGlobalParameters4) { auto parameters = cbptr_->getModifiedGlobalParameters4(ServerSelector::ALL(), timestamps_["today"]); - // It should be the one modified "tomorrow". + // It should be the one modified "tomorrow". ASSERT_EQ(1, parameters.size()); - ASSERT_TRUE(parameters[0]); - EXPECT_EQ("value3", parameters[0]->getValue()); + EXPECT_EQ("value3", (*parameters.begin())->getValue()); // Should be able to fetct these parameters when explicitly providing // the server tag. diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_mgr_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_mgr_unittest.cc new file mode 100644 index 0000000000..c4f8a48a1e --- /dev/null +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_mgr_unittest.cc @@ -0,0 +1,87 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::dhcp::test; +using namespace isc::db; +using namespace isc::db::test; + +namespace { + +/// @brief Test fixture class for @c MySqlConfigBackendDHCPv6Mgr. +class MySqlConfigBackendDHCPv6MgrTest : public GenericBackendTest { +public: + /// @brief Constructor. + MySqlConfigBackendDHCPv6MgrTest() { + // Recreate a fresh mgr. + ConfigBackendDHCPv6Mgr::create(); + + // Recreate database schema. + destroyMySQLSchema(); + createMySQLSchema(); + } + + /// @brief Destructor. + virtual ~MySqlConfigBackendDHCPv6MgrTest() { + // Destroy the mgr. + ConfigBackendDHCPv6Mgr::destroy(); + destroyMySQLSchema(); + } +}; + +// This test verifies that MySQL backend can be registered with and +// unregistered from the Config Backend Manager. +TEST_F(MySqlConfigBackendDHCPv6MgrTest, factoryRegistration) { + + // Get the mgr singleton. + ConfigBackendDHCPv6Mgr& mgr = ConfigBackendDHCPv6Mgr::instance(); + + // With no factory registered, attempting to add a MySQL db should fail. + ASSERT_THROW(mgr.addBackend(validMySQLConnectionString()), InvalidType); + + // Now we'll register the MySQL factory. + ASSERT_NO_THROW(MySqlConfigBackendDHCPv6::registerBackendType()); + + // With the factory registered, attempting to add a MySQL db should succeed. + ASSERT_NO_THROW(mgr.addBackend(validMySQLConnectionString())); + + // Create a MySQL backend selector for convenience. + BackendSelector mysql(BackendSelector::Type::MYSQL); + + // Should be able to create a global parameter. + StampedValuePtr server_tag = StampedValue::create("server-tag", "whale"); + ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(mysql, ServerSelector::ALL(), + server_tag)); + // Verify parameter can be fetched. + server_tag.reset(); + ASSERT_NO_THROW(server_tag = mgr.getPool()->getGlobalParameter6(mysql, ServerSelector::ALL(), + "server-tag")); + ASSERT_TRUE(server_tag); + EXPECT_EQ("server-tag", server_tag->getName()); + EXPECT_EQ("whale", server_tag->getValue()); + + // Now we'll unregister MySQL. + ASSERT_NO_THROW(MySqlConfigBackendDHCPv6::unregisterBackendType()); + + // With no factory registered, attempting to add a MySQL db should fail. + ASSERT_THROW(mgr.addBackend(validMySQLConnectionString()), InvalidType); + + // Attempting to read the global parameter should fail. + ASSERT_THROW(mgr.getPool()->getGlobalParameter6(mysql, ServerSelector::ALL(), "server-tag"), + NoSuchDatabase); +} + +} 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 new file mode 100644 index 0000000000..4a25db265c --- /dev/null +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc @@ -0,0 +1,1216 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc::asiolink; +using namespace isc::db; +using namespace isc::db::test; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::dhcp::test; + +namespace { + +/// @brief Test fixture class for @c MySqlConfigBackendDHCPv6. +/// +/// @todo The tests we're providing here only test cases when the +/// server selector is set to 'ALL' (configuration elements belong to +/// all servers). Currently we have no API to insert servers into +/// the database, and therefore we can't test the case when +/// configuration elements are assigned to particular servers by +/// server tags. We will have to expand existing tests when +/// the API is extended allowing for inserting servers to the +/// database. +class MySqlConfigBackendDHCPv6Test : public GenericBackendTest { +public: + + /// @brief Constructor. + MySqlConfigBackendDHCPv6Test() + : test_subnets_(), test_networks_(), timestamps_() { + // Recreate database schema. + destroyMySQLSchema(); + createMySQLSchema(); + + try { + // Create MySQL connection and use it to start the backend. + DatabaseConnection::ParameterMap params = + DatabaseConnection::parse(validMySQLConnectionString()); + cbptr_.reset(new MySqlConfigBackendDHCPv6(params)); + + } catch (...) { + std::cerr << "*** ERROR: unable to open database. The test\n" + "*** environment is broken and must be fixed before\n" + "*** the MySQL tests will run correctly.\n" + "*** The reason for the problem is described in the\n" + "*** accompanying exception output.\n"; + throw; + } + + // Create test data. + initTestOptions(); + initTestSubnets(); + initTestSharedNetworks(); + initTestOptionDefs(); + initTimestamps(); + } + + /// @brief Destructor. + virtual ~MySqlConfigBackendDHCPv6Test() { + cbptr_.reset(); + destroyMySQLSchema(); + } + + /// @brief Creates several subnets used in tests. + void initTestSubnets() { + // First subnet includes all parameters. + std::string interface_id_text = "whale"; + OptionBuffer interface_id(interface_id_text.begin(), interface_id_text.end()); + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 64, 30, 40, 50, 60, 1024)); + subnet->allowClientClass("home"); + subnet->setIface("eth1"); + subnet->setT2(323212); + subnet->addRelayAddress(IOAddress("2001:db8:1::2")); + subnet->addRelayAddress(IOAddress("2001:db8:3::4")); + subnet->setT1(1234); + subnet->requireClientClass("required-class1"); + subnet->requireClientClass("required-class2"); + subnet->setHostReservationMode(Subnet4::HR_DISABLED); + subnet->setContext(user_context); + subnet->setValid(555555); + + Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, + IOAddress("2001:db8::10"), + IOAddress("2001:db8::20"))); + subnet->addPool(pool1); + + Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, + IOAddress("2001:db8::50"), + IOAddress("2001:db8::60"))); + subnet->addPool(pool2); + + // Add several options to the subnet. + subnet->getCfgOption()->add(test_options_[0]->option_, + test_options_[0]->persistent_, + test_options_[0]->space_name_); + + subnet->getCfgOption()->add(test_options_[1]->option_, + test_options_[1]->persistent_, + test_options_[1]->space_name_); + + subnet->getCfgOption()->add(test_options_[2]->option_, + test_options_[2]->persistent_, + test_options_[2]->space_name_); + + test_subnets_.push_back(subnet); + + // Adding another subnet with the same subnet id to test + // cases that this second instance can override existing + // subnet instance. + subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 20, 30, 40, 50, 1024)); + + pool1.reset(new Pool6(Lease::TYPE_NA, + IOAddress("2001:db8:1::10"), + IOAddress("2001:db8:1::20"))); + subnet->addPool(pool1); + + pool1->getCfgOption()->add(test_options_[3]->option_, + test_options_[3]->persistent_, + test_options_[3]->space_name_); + + pool1->getCfgOption()->add(test_options_[4]->option_, + test_options_[4]->persistent_, + test_options_[4]->space_name_); + + pool2.reset(new Pool6(Lease::TYPE_NA, + IOAddress("2001:db8:1::50"), + IOAddress("2001:db8:1::60"))); + subnet->addPool(pool2); + + test_subnets_.push_back(subnet); + + subnet.reset(new Subnet6(IOAddress("2001:db8:3::0"), 64, 20, 30, 40, 50, 2048)); + test_subnets_.push_back(subnet); + + subnet.reset(new Subnet6(IOAddress("2001:db8:4::0"), 64, 30, 40, 60, 4096)); + test_subnets_.push_back(subnet); + } + + /// @brief Creates several subnets used in tests. + void initTestSharedNetworks() { + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + SharedNetwork6Ptr shared_network(new SharedNetwork6("level1")); + shared_network->allowClientClass("foo"); + shared_network->setIface("eth1"); + shared_network->setT2(323212); + shared_network->addRelayAddress(IOAddress("2001:db8:1::2")); + shared_network->addRelayAddress(IOAddress("2001:db8:3::4")); + shared_network->setT1(1234); + shared_network->requireClientClass("required-class1"); + shared_network->requireClientClass("required-class2"); + shared_network->setHostReservationMode(Subnet6::HR_DISABLED); + shared_network->setContext(user_context); + shared_network->setValid(5555); + + // Add several options to the shared network. + shared_network->getCfgOption()->add(test_options_[2]->option_, + test_options_[2]->persistent_, + test_options_[2]->space_name_); + + shared_network->getCfgOption()->add(test_options_[3]->option_, + test_options_[3]->persistent_, + test_options_[3]->space_name_); + + shared_network->getCfgOption()->add(test_options_[4]->option_, + test_options_[4]->persistent_, + test_options_[4]->space_name_); + + test_networks_.push_back(shared_network); + + // Adding another shared network called "level1" to test + // cases that this second instance can override existing + // "level1" instance. + shared_network.reset(new SharedNetwork6("level1")); + test_networks_.push_back(shared_network); + + // Add more shared networks. + shared_network.reset(new SharedNetwork6("level2")); + test_networks_.push_back(shared_network); + + shared_network.reset(new SharedNetwork6("level3")); + test_networks_.push_back(shared_network); + } + + /// @brief Creates several option definitions used in tests. + void initTestOptionDefs() { + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + OptionDefinitionPtr option_def(new OptionDefinition("foo", 234, "string", + "espace")); + option_def->setOptionSpaceName("dhcp6"); + test_option_defs_.push_back(option_def); + + option_def.reset(new OptionDefinition("bar", 234, "uint32", true)); + option_def->setOptionSpaceName("dhcp6"); + test_option_defs_.push_back(option_def); + + option_def.reset(new OptionDefinition("fish", 235, "record", true)); + option_def->setOptionSpaceName("dhcp6"); + option_def->addRecordField("uint32"); + option_def->addRecordField("string"); + test_option_defs_.push_back(option_def); + + option_def.reset(new OptionDefinition("whale", 236, "string")); + option_def->setOptionSpaceName("xyz"); + test_option_defs_.push_back(option_def); + } + + /// @brief Creates several DHCP options used in tests. + void initTestOptions() { + ElementPtr user_context = Element::createMap(); + user_context->set("foo", Element::create("bar")); + + OptionDefSpaceContainer defs; + + OptionDescriptor desc = + createOption(Option::V6, D6O_NEW_POSIX_TIMEZONE, + true, false, "my-timezone"); + desc.space_name_ = DHCP6_OPTION_SPACE; + desc.setContext(user_context); + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createOption(Option::V6, D6O_PREFERENCE, + false, true, 64); + desc.space_name_ = DHCP6_OPTION_SPACE; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createOption(Option::V6, 1, false, false, 312131), + desc.space_name_ = "vendor-encapsulated-options"; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createAddressOption(254, true, true, + "2001:db8::3"); + desc.space_name_ = DHCP6_OPTION_SPACE; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createEmptyOption(Option::V6, 1, true); + desc.space_name_ = "isc"; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + desc = createAddressOption(2, false, true, "2001:db8:1::5", + "2001:db8:1::3", "2001:db8:4::4"); + desc.space_name_ = "isc"; + test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc))); + + // Add definitions for DHCPv6 non-standard options. + defs.addItem(OptionDefinitionPtr(new OptionDefinition( + "vendor-encapsulated-1", 1, "uint32")), + "vendor-encapsulated-options"); + defs.addItem(OptionDefinitionPtr(new OptionDefinition( + "option-254", 254, "ipv6-address", true)), + DHCP6_OPTION_SPACE); + defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-1", 1, "empty")), "isc"); + defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-2", 2, "ipv6-address", true)), + "isc"); + + // Register option definitions. + LibDHCP::setRuntimeOptionDefs(defs); + } + + /// @brief Initialize posix time values used in tests. + void initTimestamps() { + // Current time minus 1 hour to make sure it is in the past. + timestamps_["today"] = boost::posix_time::second_clock::universal_time() + - boost::posix_time::hours(1); + // Yesterday. + timestamps_["yesterday"] = timestamps_["today"] - boost::posix_time::hours(24); + // Tomorrow. + timestamps_["tomorrow"] = timestamps_["today"] + boost::posix_time::hours(24); + } + + /// @brief Holds pointers to subnets used in tests. + std::vector test_subnets_; + + /// @brief Holds pointers to shared networks used in tests. + std::vector test_networks_; + + /// @brief Holds pointers to option definitions used in tests. + std::vector test_option_defs_; + + /// @brief Holds pointers to options used in tests. + std::vector test_options_; + + /// @brief Holds timestamp values used in tests. + std::map timestamps_; + + /// @brief Holds pointer to the backend. + boost::shared_ptr cbptr_; +}; + +// This test verifies that the expected backend type is returned. +TEST_F(MySqlConfigBackendDHCPv6Test, getType) { + DatabaseConnection::ParameterMap params; + params["name"] = "keatest"; + params["password"] = "keatest"; + params["user"] = "keatest"; + ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params))); + EXPECT_EQ("mysql", cbptr_->getType()); +} + +// This test verifies that by default localhost is returned as MySQL connection +// host. +TEST_F(MySqlConfigBackendDHCPv6Test, getHost) { + DatabaseConnection::ParameterMap params; + params["name"] = "keatest"; + params["password"] = "keatest"; + params["user"] = "keatest"; + ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params))); + EXPECT_EQ("localhost", cbptr_->getHost()); +} + +// This test verifies that by default port of 0 is returned as MySQL connection +// port. +TEST_F(MySqlConfigBackendDHCPv6Test, getPort) { + DatabaseConnection::ParameterMap params; + params["name"] = "keatest"; + params["password"] = "keatest"; + params["user"] = "keatest"; + ASSERT_NO_THROW(cbptr_.reset(new MySqlConfigBackendDHCPv6(params))); + EXPECT_EQ(0, cbptr_->getPort()); +} + +// This test verifies that the global parameter can be added, updated and +// deleted. +TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteGlobalParameter6) { + StampedValuePtr global_parameter = StampedValue::create("global", "whale"); + + // Explicitly set modification time to make sure that the time + // returned from the database is correct. + global_parameter->setModificationTime(timestamps_["yesterday"]); + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + global_parameter); + + // Verify returned parameter and the modification time. + StampedValuePtr returned_global_parameter = + cbptr_->getGlobalParameter6(ServerSelector::ALL(), "global"); + ASSERT_TRUE(returned_global_parameter); + EXPECT_EQ("global", returned_global_parameter->getName()); + EXPECT_EQ("whale", returned_global_parameter->getValue()); + EXPECT_TRUE(returned_global_parameter->getModificationTime() == + global_parameter->getModificationTime()); + + // Because we have added the global parameter for all servers, it + // should be also returned for the explicitly specified server. + returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ONE("server1"), + "global"); + ASSERT_TRUE(returned_global_parameter); + EXPECT_EQ("global", returned_global_parameter->getName()); + EXPECT_EQ("whale", returned_global_parameter->getValue()); + EXPECT_TRUE(returned_global_parameter->getModificationTime() == + global_parameter->getModificationTime()); + + // Check that the parameter is udpated when selector is specified correctly. + global_parameter = StampedValue::create("global", "fish"); + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + global_parameter); + returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ALL(), + "global"); + ASSERT_TRUE(returned_global_parameter); + EXPECT_EQ("global", returned_global_parameter->getName()); + EXPECT_EQ("fish", returned_global_parameter->getValue()); + EXPECT_TRUE(returned_global_parameter->getModificationTime() == + global_parameter->getModificationTime()); + + // Should not delete parameter specified for all servers if explicit + // server name is provided. + EXPECT_EQ(0, cbptr_->deleteGlobalParameter6(ServerSelector::ONE("server1"), + "global")); + + // Delete parameter and make sure it is gone. + cbptr_->deleteGlobalParameter6(ServerSelector::ALL(), "global"); + returned_global_parameter = cbptr_->getGlobalParameter6(ServerSelector::ALL(), + "global"); + EXPECT_FALSE(returned_global_parameter); +} + +// This test verifies that all global parameters can be retrieved and deleted. +TEST_F(MySqlConfigBackendDHCPv6Test, getAllGlobalParameters6) { + // Create 3 parameters and put them into the database. + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + StampedValue::create("name1", "value1")); + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + StampedValue::create("name2", 65)); + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + StampedValue::create("name3", "value3")); + + // Fetch all parameters. + auto parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ALL()); + + // Verify their values. + std::vector plist(parameters.begin(), parameters.end()); + ASSERT_EQ(3, plist.size()); + EXPECT_EQ("value1", plist[0]->getValue()); + EXPECT_EQ(65, plist[1]->getSignedIntegerValue()); + EXPECT_EQ("value3", plist[2]->getValue()); + + // Should be able to fetct these parameters when explicitly providing + // the server tag. + parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ONE("server1")); + EXPECT_EQ(3, parameters.size()); + + // Deleting global parameters with non-matching server selector + // should fail. + EXPECT_EQ(0, cbptr_->deleteAllGlobalParameters6(ServerSelector::ONE("server1"))); + + // Delete all parameters and make sure they are gone. + EXPECT_EQ(3, cbptr_->deleteAllGlobalParameters6(ServerSelector::ALL())); + parameters = cbptr_->getAllGlobalParameters6(ServerSelector::ALL()); + EXPECT_TRUE(parameters.empty()); +} + +// This test verifies that modified global parameters can be retrieved. +TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedGlobalParameters6) { + // Create 3 global parameters and assign modification times: + // "yesterday", "today" and "tomorrow" respectively. + StampedValuePtr value = StampedValue::create("name1", "value1"); + value->setModificationTime(timestamps_["yesterday"]); + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + value); + + value = StampedValue::create("name2", 65); + value->setModificationTime(timestamps_["today"]); + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + value); + + value = StampedValue::create("name3", "value3"); + value->setModificationTime(timestamps_["tomorrow"]); + cbptr_->createUpdateGlobalParameter6(ServerSelector::ALL(), + value); + + // Get parameters modified after "today". + auto parameters = cbptr_->getModifiedGlobalParameters6(ServerSelector::ALL(), + timestamps_["today"]); + + // It should be the one modified "tomorrow". + ASSERT_EQ(1, parameters.size()); + EXPECT_EQ("value3", (*parameters.begin())->getValue()); + + // Should be able to fetct these parameters when explicitly providing + // the server tag. + parameters = cbptr_->getModifiedGlobalParameters6(ServerSelector::ONE("server1"), + timestamps_["today"]); + EXPECT_EQ(1, parameters.size()); +} + +// 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); + + // 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()); + + // 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()); + + // Fetching the subnet for an explicitly specified server tag should + // succeeed too. + returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"), + SubnetID(1024)); + EXPECT_EQ(subnet2->toElement()->str(), returned_subnet->toElement()->str()); +} + +// Test that subnet can be associated with a shared network. +TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6SharedNetwork) { + Subnet6Ptr subnet = test_subnets_[0]; + SharedNetwork6Ptr shared_network = test_networks_[0]; + + // Add subnet to a shared network. + shared_network->add(subnet); + + // Store shared network in the database. + cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), + shared_network); + + // Store subnet associated with the shared network in the database. + 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); + + // 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()); + + // However, the check above doesn't verify whether shared network name was + // correctly returned from the database. + EXPECT_EQ(shared_network->getName(), returned_subnet->getSharedNetworkName()); +} + +// Test that subnet can be fetched by prefix. +TEST_F(MySqlConfigBackendDHCPv6Test, getSubnet6ByPrefix) { + // Insert subnet to the database. + Subnet6Ptr subnet = test_subnets_[0]; + cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet); + + // Fetch the subnet by prefix. + Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + "2001:db8::/64"); + ASSERT_TRUE(returned_subnet); + + // Verify subnet contents. + EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str()); + + // Fetching the subnet for an explicitly specified server tag should + // succeeed too. + returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"), + "2001:db8::/64"); + EXPECT_EQ(subnet->toElement()->str(), returned_subnet->toElement()->str()); +} + +// Test that all subnets can be fetched and then deleted. +TEST_F(MySqlConfigBackendDHCPv6Test, getAllSubnets6) { + // Insert test subnets into the database. Note that the second subnet will + // overwrite the first subnet as they use the same ID. + for (auto subnet : test_subnets_) { + cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet); + } + + // Fetch all subnets. + Subnet6Collection subnets = cbptr_->getAllSubnets6(ServerSelector::ALL()); + ASSERT_EQ(test_subnets_.size() - 1, subnets.size()); + + // All subnets should also be returned for explicitly specified server tag. + subnets = cbptr_->getAllSubnets6(ServerSelector::ONE("server1")); + ASSERT_EQ(test_subnets_.size() - 1, subnets.size()); + + // See if the subnets are returned ok. + for (auto i = 0; i < subnets.size(); ++i) { + EXPECT_EQ(test_subnets_[i + 1]->toElement()->str(), + subnets[i]->toElement()->str()); + } + + // Attempt to remove the non existing subnet should return 0. + EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ALL(), 22)); + EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ALL(), + "2001:db8:5::/64")); + // All subnets should be still there. + ASSERT_EQ(test_subnets_.size() - 1, subnets.size()); + + // Should not delete the subnet for explicit server tag because + // our subnet is for all servers. + EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ONE("server1"), + test_subnets_[1]->getID())); + + // Also, verify that behavior when deleting by prefix. + EXPECT_EQ(0, cbptr_->deleteSubnet6(ServerSelector::ONE("server1"), + test_subnets_[2]->toText())); + + // Same for all subnets. + EXPECT_EQ(0, cbptr_->deleteAllSubnets6(ServerSelector::ONE("server1"))); + + // Delete first subnet by id and verify that it is gone. + EXPECT_EQ(1, cbptr_->deleteSubnet6(ServerSelector::ALL(), + test_subnets_[1]->getID())); + subnets = cbptr_->getAllSubnets6(ServerSelector::ALL()); + ASSERT_EQ(test_subnets_.size() - 2, subnets.size()); + + // Delete second subnet by prefix and verify it is gone. + EXPECT_EQ(1, cbptr_->deleteSubnet6(ServerSelector::ALL(), + test_subnets_[2]->toText())); + subnets = cbptr_->getAllSubnets6(ServerSelector::ALL()); + ASSERT_EQ(test_subnets_.size() - 3, subnets.size()); + + // Delete all. + EXPECT_EQ(1, cbptr_->deleteAllSubnets6(ServerSelector::ALL())); + subnets = cbptr_->getAllSubnets6(ServerSelector::ALL()); + ASSERT_TRUE(subnets.empty()); +} + +// Test that subnets modified after given time can be fetched. +TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSubnets6) { + // Explicitly set timestamps of subnets. First subnet has a timestamp + // pointing to the future. Second subnet has timestamp pointing to the + // past (yesterday). Third subnet has a timestamp pointing to the + // past (an hour ago). + test_subnets_[1]->setModificationTime(timestamps_["tomorrow"]); + test_subnets_[2]->setModificationTime(timestamps_["yesterday"]); + test_subnets_[3]->setModificationTime(timestamps_["today"]); + + // Insert subnets into the database. + for (int i = 1; i < test_subnets_.size(); ++i) { + cbptr_->createUpdateSubnet6(ServerSelector::ALL(), + test_subnets_[i]); + } + + // Fetch subnets with timestamp later than today. Only one subnet + // should be returned. + Subnet6Collection + subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(), + timestamps_["today"]); + ASSERT_EQ(1, subnets.size()); + + // All subnets should also be returned for explicitly specified server tag. + subnets = cbptr_->getModifiedSubnets6(ServerSelector::ONE("server1"), + timestamps_["today"]); + ASSERT_EQ(1, subnets.size()); + + // Fetch subnets with timestamp later than yesterday. We should get + // two subnets. + subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(), + timestamps_["yesterday"]); + ASSERT_EQ(2, subnets.size()); + + // Fetch subnets with timestamp later than tomorrow. Nothing should + // be returned. + subnets = cbptr_->getModifiedSubnets6(ServerSelector::ALL(), + timestamps_["tomorrow"]); + ASSERT_TRUE(subnets.empty()); +} + +// 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); + + // Fetch this shared network by name. + SharedNetwork6Ptr + returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(), + test_networks_[0]->getName()); + ASSERT_TRUE(returned_network); + + // 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()); + + // Update shared network in the database. + SharedNetwork6Ptr shared_network2 = test_networks_[1]; + cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), shared_network2); + + // Fetch updated subnet 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()); + + // 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()); +} + +// Test that all shared networks can be fetched. +TEST_F(MySqlConfigBackendDHCPv6Test, getAllSharedNetworks6) { + // Insert test shared networks into the database. Note that the second shared + // network will overwrite the first shared network as they use the same name. + for (auto network : test_networks_) { + cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), network); + } + + // Fetch all shared networks. + SharedNetwork6Collection networks = + cbptr_->getAllSharedNetworks6(ServerSelector::ALL()); + ASSERT_EQ(test_networks_.size() - 1, networks.size()); + + // All shared networks should also be returned for explicitly specified + // server tag. + networks = cbptr_->getAllSharedNetworks6(ServerSelector::ONE("server1")); + ASSERT_EQ(test_networks_.size() - 1, networks.size()); + + // See if shared networks are returned ok. + for (auto i = 0; i < networks.size(); ++i) { + EXPECT_EQ(test_networks_[i + 1]->toElement()->str(), + networks[i]->toElement()->str()); + } + + // Deleting non-existing shared network should return 0. + EXPECT_EQ(0, cbptr_->deleteSharedNetwork6(ServerSelector::ALL(), + "big-fish")); + // All shared networks should be still there. + ASSERT_EQ(test_networks_.size() - 1, networks.size()); + + // Should not delete the subnet for explicit server tag because + // our shared network is for all servers. + EXPECT_EQ(0, cbptr_->deleteSharedNetwork6(ServerSelector::ONE("server1"), + test_networks_[1]->getName())); + + // Same for all shared networks. + EXPECT_EQ(0, cbptr_->deleteAllSharedNetworks6(ServerSelector::ONE("server1"))); + + // Delete first shared network and verify it is gone. + EXPECT_EQ(1, cbptr_->deleteSharedNetwork6(ServerSelector::ALL(), + test_networks_[1]->getName())); + networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL()); + ASSERT_EQ(test_networks_.size() - 2, networks.size()); + + // Delete all. + EXPECT_EQ(2, cbptr_->deleteAllSharedNetworks6(ServerSelector::ALL())); + networks = cbptr_->getAllSharedNetworks6(ServerSelector::ALL()); + ASSERT_TRUE(networks.empty()); +} + +// Test that shared networks modified after given time can be fetched. +TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSharedNetworks6) { + // Explicitly set timestamps of shared networks. First shared + // network has a timestamp pointing to the future. Second shared + // network has timestamp pointing to the past (yesterday). + // Third shared network has a timestamp pointing to the + // past (an hour ago). + test_networks_[1]->setModificationTime(timestamps_["tomorrow"]); + test_networks_[2]->setModificationTime(timestamps_["yesterday"]); + test_networks_[3]->setModificationTime(timestamps_["today"]); + + // Insert shared networks into the database. + for (int i = 1; i < test_networks_.size(); ++i) { + cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), + test_networks_[i]); + } + + // Fetch shared networks with timestamp later than today. Only one + // shared network should be returned. + SharedNetwork6Collection + networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(), + timestamps_["today"]); + ASSERT_EQ(1, networks.size()); + + // Fetch shared networks with timestamp later than yesterday. We + // should get two shared networks. + networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(), + timestamps_["yesterday"]); + ASSERT_EQ(2, networks.size()); + + // Fetch shared networks with timestamp later than tomorrow. Nothing + // should be returned. + networks = cbptr_->getModifiedSharedNetworks6(ServerSelector::ALL(), + timestamps_["tomorrow"]); + ASSERT_TRUE(networks.empty()); +} + +// Test that option definition can be inserted, fetched, updated and then +// fetched again. +TEST_F(MySqlConfigBackendDHCPv6Test, getOptionDef6) { + // Insert new option definition. + OptionDefinitionPtr option_def = test_option_defs_[0]; + cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def); + + // Fetch this option_definition by subnet identifier. + OptionDefinitionPtr returned_option_def = + cbptr_->getOptionDef6(ServerSelector::ALL(), + test_option_defs_[0]->getCode(), + test_option_defs_[0]->getOptionSpaceName()); + ASSERT_TRUE(returned_option_def); + + EXPECT_TRUE(returned_option_def->equals(*option_def)); + + // Update the option definition in the database. + OptionDefinitionPtr option_def2 = test_option_defs_[1]; + cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def2); + + // Fetch updated option definition and see if it matches. + returned_option_def = cbptr_->getOptionDef6(ServerSelector::ALL(), + test_option_defs_[1]->getCode(), + test_option_defs_[1]->getOptionSpaceName()); + EXPECT_TRUE(returned_option_def->equals(*option_def2)); + + // Fetching option definition for an explicitly specified server tag + // should succeed too. + returned_option_def = cbptr_->getOptionDef6(ServerSelector::ONE("server1"), + test_option_defs_[1]->getCode(), + test_option_defs_[1]->getOptionSpaceName()); + EXPECT_TRUE(returned_option_def->equals(*option_def2)); +} + +// Test that all option definitions can be fetched. +TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptionDefs6) { + // Insert test option definitions into the database. Note that the second + // option definition will overwrite the first option definition as they use + // the same code and space. + for (auto option_def : test_option_defs_) { + cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), option_def); + } + + // Fetch all option_definitions. + OptionDefContainer option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL()); + ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size()); + + // All option definitions should also be returned for explicitly specified + // server tag. + option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ONE("server1")); + ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size()); + + // See if option definitions are returned ok. + for (auto def = option_defs.begin(); def != option_defs.end(); ++def) { + bool success = false; + for (auto i = 1; i < test_option_defs_.size(); ++i) { + if ((*def)->equals(*test_option_defs_[i])) { + success = true; + } + } + ASSERT_TRUE(success) << "failed for option definition " << (*def)->getCode() + << ", option space " << (*def)->getOptionSpaceName(); + } + + // Deleting non-existing option definition should return 0. + EXPECT_EQ(0, cbptr_->deleteOptionDef6(ServerSelector::ALL(), + 99, "non-exiting-space")); + // All option definitions should be still there. + ASSERT_EQ(test_option_defs_.size() - 1, option_defs.size()); + + // Should not delete option definition for explicit server tag + // because our option definition is for all servers. + EXPECT_EQ(0, cbptr_->deleteOptionDef6(ServerSelector::ONE("server1"), + test_option_defs_[1]->getCode(), + test_option_defs_[1]->getOptionSpaceName())); + + // Same for all option definitions. + EXPECT_EQ(0, cbptr_->deleteAllOptionDefs6(ServerSelector::ONE("server1"))); + + // Delete one of the option definitions and see if it is gone. + EXPECT_EQ(1, cbptr_->deleteOptionDef6(ServerSelector::ALL(), + test_option_defs_[2]->getCode(), + test_option_defs_[2]->getOptionSpaceName())); + ASSERT_FALSE(cbptr_->getOptionDef6(ServerSelector::ALL(), + test_option_defs_[2]->getCode(), + test_option_defs_[2]->getOptionSpaceName())); + + // Delete all remaining option definitions. + EXPECT_EQ(2, cbptr_->deleteAllOptionDefs6(ServerSelector::ALL())); + option_defs = cbptr_->getAllOptionDefs6(ServerSelector::ALL()); + ASSERT_TRUE(option_defs.empty()); +} + +// Test that option definitions modified after given time can be fetched. +TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedOptionDefs6) { + // Explicitly set timestamps of option definitions. First option + // definition has a timestamp pointing to the future. Second option + // definition has timestamp pointing to the past (yesterday). + // Third option definitions has a timestamp pointing to the + // past (an hour ago). + test_option_defs_[1]->setModificationTime(timestamps_["tomorrow"]); + test_option_defs_[2]->setModificationTime(timestamps_["yesterday"]); + test_option_defs_[3]->setModificationTime(timestamps_["today"]); + + // Insert option definitions into the database. + for (int i = 1; i < test_networks_.size(); ++i) { + cbptr_->createUpdateOptionDef6(ServerSelector::ALL(), + test_option_defs_[i]); + } + + // Fetch option definitions with timestamp later than today. Only one + // option definition should be returned. + OptionDefContainer + option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(), + timestamps_["today"]); + ASSERT_EQ(1, option_defs.size()); + + // Fetch option definitions with timestamp later than yesterday. We + // should get two option definitions. + option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(), + timestamps_["yesterday"]); + ASSERT_EQ(2, option_defs.size()); + + // Fetch option definitions with timestamp later than tomorrow. Nothing + // should be returned. + option_defs = cbptr_->getModifiedOptionDefs6(ServerSelector::ALL(), + timestamps_["tomorrow"]); + ASSERT_TRUE(option_defs.empty()); +} + +// This test verifies that global option can be added, updated and deleted. +TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteOption6) { + // Add option to the database. + OptionDescriptorPtr opt_boot_file_name = test_options_[0]; + cbptr_->createUpdateOption6(ServerSelector::ALL(), + opt_boot_file_name); + + // Make sure we can retrieve this option and that it is equal to the + // option we have inserted into the database. + OptionDescriptorPtr returned_opt_boot_file_name = + cbptr_->getOption6(ServerSelector::ALL(), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_); + ASSERT_TRUE(returned_opt_boot_file_name); + EXPECT_TRUE(returned_opt_boot_file_name->equals(*opt_boot_file_name)); + + // Modify option and update it in the database. + opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_; + cbptr_->createUpdateOption6(ServerSelector::ALL(), + opt_boot_file_name); + + // Retrieve the option again and make sure that updates were + // properly propagated to the database. + returned_opt_boot_file_name = cbptr_->getOption6(ServerSelector::ALL(), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_); + ASSERT_TRUE(returned_opt_boot_file_name); + EXPECT_TRUE(returned_opt_boot_file_name->equals(*opt_boot_file_name)); + + // Deleting an option with explicitly specified server tag should fail. + EXPECT_EQ(0, cbptr_->deleteOption6(ServerSelector::ONE("server1"), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_)); + + // Deleting option for all servers should succeed. + EXPECT_EQ(1, cbptr_->deleteOption6(ServerSelector::ALL(), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_)); + + EXPECT_FALSE(cbptr_->getOption6(ServerSelector::ALL(), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_)); +} + +// This test verifies that all global options can be retrieved. +TEST_F(MySqlConfigBackendDHCPv6Test, getAllOptions6) { + // Add three global options to the database. + cbptr_->createUpdateOption6(ServerSelector::ALL(), + test_options_[0]); + cbptr_->createUpdateOption6(ServerSelector::ALL(), + test_options_[1]); + cbptr_->createUpdateOption6(ServerSelector::ALL(), + test_options_[5]); + + // Retrieve all these options. + OptionContainer returned_options = cbptr_->getAllOptions6(ServerSelector::ALL()); + ASSERT_EQ(3, returned_options.size()); + + // Fetching global options with explicitly specified server tag should return + // the same result. + returned_options = cbptr_->getAllOptions6(ServerSelector::ONE("server1")); + ASSERT_EQ(3, returned_options.size()); + + // Get the container index used to search options by option code. + const OptionContainerTypeIndex& index = returned_options.get<1>(); + + // Verify that all options we put into the database were + // returned. + auto option0 = index.find(test_options_[0]->option_->getType()); + ASSERT_FALSE(option0 == index.end()); + EXPECT_TRUE(option0->equals(*test_options_[0])); + + auto option1 = index.find(test_options_[1]->option_->getType()); + ASSERT_FALSE(option1 == index.end()); + EXPECT_TRUE(option1->equals(*test_options_[1])); + + auto option5 = index.find(test_options_[5]->option_->getType()); + ASSERT_FALSE(option5 == index.end()); + EXPECT_TRUE(option5->equals(*test_options_[5])); +} + +// This test verifies that modified global options can be retrieved. +TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedOptions6) { + // Assign timestamps to the options we're going to store in the + // database. + test_options_[0]->setModificationTime(timestamps_["tomorrow"]); + test_options_[1]->setModificationTime(timestamps_["yesterday"]); + test_options_[5]->setModificationTime(timestamps_["today"]); + + // Put options into the database. + cbptr_->createUpdateOption6(ServerSelector::ALL(), + test_options_[0]); + cbptr_->createUpdateOption6(ServerSelector::ALL(), + test_options_[1]); + cbptr_->createUpdateOption6(ServerSelector::ALL(), + test_options_[5]); + + // Get options with the timestamp later than today. Only + // one option should be returned. + OptionContainer returned_options = + cbptr_->getModifiedOptions6(ServerSelector::ALL(), + timestamps_["today"]); + ASSERT_EQ(1, returned_options.size()); + + // Fetching modified options with explicitly specified server selector + // should return the same result. + returned_options = cbptr_->getModifiedOptions6(ServerSelector::ONE("server1"), + timestamps_["today"]); + ASSERT_EQ(1, returned_options.size()); + + // The returned option should be the one with the timestamp + // set to tomorrow. + const OptionContainerTypeIndex& index = returned_options.get<1>(); + auto option0 = index.find(test_options_[0]->option_->getType()); + ASSERT_FALSE(option0 == index.end()); + EXPECT_TRUE(option0->equals(*test_options_[0])); +} + +// This test verifies that subnet level option can be added, updated and +// deleted. +TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSubnetOption6) { + // Insert new subnet. + Subnet6Ptr subnet = test_subnets_[1]; + cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet); + + // Fetch this subnet by subnet identifier. + Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + subnet->getID()); + ASSERT_TRUE(returned_subnet); + + OptionDescriptorPtr opt_boot_file_name = test_options_[0]; + cbptr_->createUpdateOption6(ServerSelector::ALL(), subnet->getID(), + opt_boot_file_name); + + returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + subnet->getID()); + ASSERT_TRUE(returned_subnet); + + OptionDescriptor returned_opt_boot_file_name = + returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(returned_opt_boot_file_name.option_); + EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name)); + + opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_; + cbptr_->createUpdateOption6(ServerSelector::ALL(), subnet->getID(), + opt_boot_file_name); + + returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + subnet->getID()); + ASSERT_TRUE(returned_subnet); + returned_opt_boot_file_name = + returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(returned_opt_boot_file_name.option_); + EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name)); + + // Deleting an option with explicitly specified server tag should fail. + EXPECT_EQ(0, cbptr_->deleteOption6(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_->deleteOption6(ServerSelector::ALL(), subnet->getID(), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_)); + + returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + subnet->getID()); + ASSERT_TRUE(returned_subnet); + + EXPECT_FALSE(returned_subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE).option_); +} + +// This test verifies that option can be inserted, updated and deleted +// from the pool. +TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeletePoolOption6) { + // Insert new subnet. + Subnet6Ptr subnet = test_subnets_[1]; + cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet); + + // Add an option into the pool. + const PoolPtr pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8::10")); + ASSERT_TRUE(pool); + OptionDescriptorPtr opt_boot_file_name = test_options_[0]; + cbptr_->createUpdateOption6(ServerSelector::ALL(), + pool->getFirstAddress(), + pool->getLastAddress(), + opt_boot_file_name); + + // Query for a subnet. + Subnet6Ptr returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + subnet->getID()); + ASSERT_TRUE(returned_subnet); + + // The returned subnet should include our pool. + const PoolPtr returned_pool = returned_subnet->getPool(Lease::TYPE_NA, + IOAddress("2001:db8::10")); + ASSERT_TRUE(returned_pool); + + // The pool should contain option we added earlier. + OptionDescriptor returned_opt_boot_file_name = + returned_pool->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(returned_opt_boot_file_name.option_); + EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name)); + + // Modify the option and update it in the database. + opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_; + cbptr_->createUpdateOption6(ServerSelector::ALL(), + pool->getFirstAddress(), + pool->getLastAddress(), + opt_boot_file_name); + + // Fetch the subnet and the corresponding pool. + returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + subnet->getID()); + ASSERT_TRUE(returned_subnet); + const PoolPtr returned_pool1 = returned_subnet->getPool(Lease::TYPE_NA, + IOAddress("2001:db8::10")); + ASSERT_TRUE(returned_pool1); + + // Test that the option has been correctly updated in the database. + returned_opt_boot_file_name = + returned_pool1->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(returned_opt_boot_file_name.option_); + EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name)); + + // Deleting an option with explicitly specified server tag should fail. + EXPECT_EQ(0, cbptr_->deleteOption6(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_->deleteOption6(ServerSelector::ALL(), + pool->getFirstAddress(), + pool->getLastAddress(), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_)); + + // Fetch the subnet and the pool from the database again to make sure + // that the option is really gone. + returned_subnet = cbptr_->getSubnet6(ServerSelector::ALL(), + subnet->getID()); + ASSERT_TRUE(returned_subnet); + const PoolPtr returned_pool2 = returned_subnet->getPool(Lease::TYPE_NA, + IOAddress("2001:db8::10")); + ASSERT_TRUE(returned_pool2); + + // Option should be gone. + EXPECT_FALSE(returned_pool2->getCfgOption()->get(DHCP6_OPTION_SPACE, + D6O_NEW_POSIX_TIMEZONE).option_); +} + +// This test verifies that shared network level option can be added, +// updated and deleted. +TEST_F(MySqlConfigBackendDHCPv6Test, createUpdateDeleteSharedNetworkOption6) { + // Insert new shared network. + SharedNetwork6Ptr shared_network = test_networks_[1]; + cbptr_->createUpdateSharedNetwork6(ServerSelector::ALL(), + shared_network); + + // Fetch this shared network by name. + SharedNetwork6Ptr returned_network = + cbptr_->getSharedNetwork6(ServerSelector::ALL(), + shared_network->getName()); + ASSERT_TRUE(returned_network); + + OptionDescriptorPtr opt_boot_file_name = test_options_[0]; + cbptr_->createUpdateOption6(ServerSelector::ALL(), + shared_network->getName(), + opt_boot_file_name); + + returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(), + shared_network->getName()); + ASSERT_TRUE(returned_network); + + OptionDescriptor returned_opt_boot_file_name = + returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(returned_opt_boot_file_name.option_); + EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name)); + + opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_; + cbptr_->createUpdateOption6(ServerSelector::ALL(), + shared_network->getName(), + opt_boot_file_name); + + returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(), + shared_network->getName()); + ASSERT_TRUE(returned_network); + returned_opt_boot_file_name = + returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE); + ASSERT_TRUE(returned_opt_boot_file_name.option_); + EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name)); + + // Deleting an option with explicitly specified server tag should fail. + EXPECT_EQ(0, cbptr_->deleteOption6(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_->deleteOption6(ServerSelector::ALL(), + shared_network->getName(), + opt_boot_file_name->option_->getType(), + opt_boot_file_name->space_name_)); + returned_network = cbptr_->getSharedNetwork6(ServerSelector::ALL(), + shared_network->getName()); + ASSERT_TRUE(returned_network); + EXPECT_FALSE(returned_network->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_NEW_POSIX_TIMEZONE).option_); +} + + +} diff --git a/src/share/database/scripts/mysql/dhcpdb_create.mysql b/src/share/database/scripts/mysql/dhcpdb_create.mysql index 4468a98101..1f2210a413 100644 --- a/src/share/database/scripts/mysql/dhcpdb_create.mysql +++ b/src/share/database/scripts/mysql/dhcpdb_create.mysql @@ -1311,10 +1311,10 @@ CREATE TABLE IF NOT EXISTS dhcp6_options_server ( KEY key_dhcp6_options_server_modification_ts (modification_ts), CONSTRAINT fk_dhcp6_options_server_option_id FOREIGN KEY (option_id) REFERENCES dhcp6_options (option_id) - ON DELETE NO ACTION ON UPDATE NO ACTION, + ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT fk_dhcp6_options_server_server_id FOREIGN KEY (server_id) REFERENCES dhcp6_server (id) - ON DELETE CASCADE ON UPDATE NO ACTION + ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB; # Create trigger which removes pool specific options upon removal of