--- /dev/null
+// 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 <mysql_cb_dhcp6.h>
+#include <mysql_cb_impl.h>
+#include <mysql_query_macros_dhcp.h>
+#include <cc/data.h>
+#include <asiolink/addr_utilities.h>
+#include <config_backend/constants.h>
+#include <database/db_exceptions.h>
+#include <dhcp/classify.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_data_types.h>
+#include <dhcp/option_space.h>
+#include <dhcpsrv/config_backend_dhcp6_mgr.h>
+#include <dhcpsrv/network.h>
+#include <dhcpsrv/pool.h>
+#include <dhcpsrv/lease.h>
+#include <util/buffer.h>
+#include <mysql/mysql_connection.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/pointer_cast.hpp>
+#include <mysql.h>
+#include <mysqld_error.h>
+#include <array>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+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<uint64_t>(), // 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<uint64_t>(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<uint32_t>(), // 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<uint32_t>(), // preferred_lifetime
+ MySqlBinding::createInteger<uint8_t>(), // rapid_commit
+ MySqlBinding::createInteger<uint32_t>(), // rebind_timer
+ MySqlBinding::createString(RELAY_BUF_LENGTH), // relay
+ MySqlBinding::createInteger<uint32_t>(), // renew_timer
+ MySqlBinding::createString(REQUIRE_CLIENT_CLASSES_BUF_LENGTH), // require_client_classes
+ MySqlBinding::createInteger<uint8_t>(), // reservation_mode
+ MySqlBinding::createString(SHARED_NETWORK_NAME_BUF_LENGTH), // shared_network_name
+ MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context
+ MySqlBinding::createInteger<uint32_t>(), // valid_lifetime
+ MySqlBinding::createInteger<uint64_t>(), // pool: id
+ MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: start_address
+ MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: end_address
+ MySqlBinding::createInteger<uint32_t>(), // pool: dhcp6_subnet_id
+ MySqlBinding::createTimestamp(), // pool: modification_ts
+ MySqlBinding::createInteger<uint64_t>(), // pd pool: id
+ MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pd pool: prefix
+ MySqlBinding::createInteger<uint8_t>(), // pd pool: prefix_length
+ MySqlBinding::createInteger<uint8_t>(), // pd pool: delegated_prefix_length
+ MySqlBinding::createInteger<uint32_t>(), // pd pool: dhcp6_subnet_id
+ MySqlBinding::createTimestamp(), // pd pool: modification_ts
+ MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+ MySqlBinding::createInteger<uint16_t>(), // 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<uint8_t>(), // pool option: persistent
+ MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp6_subnet_id
+ MySqlBinding::createInteger<uint8_t>(), // 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<uint64_t>(), // pool option: pool_id
+ MySqlBinding::createInteger<uint64_t>(), // pool option: pd_pool_id
+ MySqlBinding::createTimestamp(), // pool option: modification_ts
+ MySqlBinding::createInteger<uint64_t>(), // pd pool option: option_id
+ MySqlBinding::createInteger<uint16_t>(), // 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<uint8_t>(), // pd pool option: persistent
+ MySqlBinding::createInteger<uint32_t>(), // pd pool option: dhcp6_subnet_id
+ MySqlBinding::createInteger<uint8_t>(), // 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<uint64_t>(), // pd pool option: pool_id
+ MySqlBinding::createInteger<uint64_t>(), // pd pool option: pd_pool_id
+ MySqlBinding::createTimestamp(), // pd pool option: modification_ts
+ MySqlBinding::createInteger<uint64_t>(), // option: option_id
+ MySqlBinding::createInteger<uint16_t>(), // 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<uint8_t>(), // option: persistent
+ MySqlBinding::createInteger<uint32_t>(), // option: dhcp6_subnet_id
+ MySqlBinding::createInteger<uint8_t>(), // 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<uint64_t>(), // option: pool_id
+ MySqlBinding::createInteger<uint64_t>(), // 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<uint32_t>())) {
+
+ // 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<uint32_t>());
+
+ // 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<uint32_t>(0);
+
+ // renew_timer
+ uint32_t renew_timer = out_bindings[9]->getIntegerOrDefault<uint32_t>(0);
+
+ // rebind_timer
+ uint32_t rebind_timer = out_bindings[7]->getIntegerOrDefault<uint32_t>(0);
+
+ // valid_lifetime
+ uint32_t valid_lifetime = out_bindings[14]->getIntegerOrDefault<uint32_t>(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<bool>
+ (out_bindings[6]->getIntegerOrDefault<uint8_t>(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<Subnet6::HRMode>
+ (out_bindings[11]->getIntegerOrDefault<uint8_t>(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<uint64_t>() > last_pool_id)) {
+ last_pool_id = out_bindings[15]->getInteger<uint64_t>();
+ 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<uint8_t>() != 0) &&
+ (out_bindings[23]->getInteger<uint8_t>() != 0) &&
+ (out_bindings[20]->getInteger<uint64_t>() > last_pd_pool_id)) {
+ last_pd_pool_id = out_bindings[20]->getInteger<uint64_t>();
+ last_pd_pool.reset(new Pool6(Lease::TYPE_PD,
+ IOAddress(out_bindings[21]->getString()),
+ out_bindings[22]->getInteger<uint8_t>(),
+ out_bindings[23]->getInteger<uint8_t>()));
+ 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<uint64_t>())) {
+ last_pool_option_id = out_bindings[26]->getInteger<uint64_t>();
+
+ 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<uint64_t>())) {
+ last_pd_pool_option_id = out_bindings[39]->getInteger<uint64_t>();
+
+ 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<uint64_t>())) {
+ last_option_id = out_bindings[52]->getInteger<uint64_t>();
+
+ 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<uint32_t>(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<uint64_t>& pool_ids) {
+ MySqlBindingCollection out_bindings = {
+ MySqlBinding::createInteger<uint64_t>(), // pool: id
+ MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: start_address
+ MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: end_address
+ MySqlBinding::createInteger<uint32_t>(), // pool: dhcp6_subnet_id
+ MySqlBinding::createTimestamp(), // pool: modification_ts
+ MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+ MySqlBinding::createInteger<uint16_t>(), // 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<uint8_t>(), // pool option: persistent
+ MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp6_subnet_id
+ MySqlBinding::createInteger<uint8_t>(), // 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<uint64_t>(), // pool option: pool_id
+ MySqlBinding::createInteger<uint64_t>(), // 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<uint64_t>() > last_pool_id) {
+
+ last_pool_id = out_bindings[0]->getInteger<uint64_t>();
+
+ 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<uint64_t>())) {
+ last_pool_option_id = out_bindings[5]->getInteger<uint64_t>();
+
+ 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<uint64_t>& pool_ids) {
+ MySqlBindingCollection out_bindings = {
+ MySqlBinding::createInteger<uint64_t>(), // pool: id
+ MySqlBinding::createString(POOL_ADDRESS6_BUF_LENGTH), // pool: prefix
+ MySqlBinding::createInteger<uint8_t>(), // pool: prefix_length
+ MySqlBinding::createInteger<uint8_t>(), // pool: delegated_prefix_length
+ MySqlBinding::createInteger<uint32_t>(), // pool: dhcp6_subnet_id
+ MySqlBinding::createTimestamp(), // pool: modification_ts
+ MySqlBinding::createInteger<uint64_t>(), // pool option: option_id
+ MySqlBinding::createInteger<uint16_t>(), // 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<uint8_t>(), // pool option: persistent
+ MySqlBinding::createInteger<uint32_t>(), // pool option: dhcp6_subnet_id
+ MySqlBinding::createInteger<uint8_t>(), // 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<uint64_t>(), // pool option: pool_id
+ MySqlBinding::createInteger<uint64_t>(), // 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<uint64_t>() > last_pool_id) {
+
+ last_pool_id = out_bindings[0]->getInteger<uint64_t>();
+
+ last_pool.reset(new Pool6(Lease::TYPE_PD,
+ IOAddress(out_bindings[1]->getString()),
+ out_bindings[2]->getInteger<uint8_t>(),
+ out_bindings[3]->getInteger<uint8_t>()));
+ 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<uint64_t>())) {
+ last_pool_option_id = out_bindings[6]->getInteger<uint64_t>();
+
+ 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<uint64_t> pool_ids;
+ getPools(GET_POOL6_RANGE, in_bindings, pools, pool_ids);
+
+ if (!pools.empty()) {
+ pool_id = pool_ids[0];
+ return (boost::dynamic_pointer_cast<Pool6>(*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<uint8_t>(pd_pool_prefix_length)
+ };
+
+ PoolCollection pools;
+ std::vector<uint64_t> pool_ids;
+ getPdPools(GET_PD_POOL, in_bindings, pools, pool_ids);
+
+ if (!pools.empty()) {
+ pool_id = pool_ids[0];
+ return (boost::dynamic_pointer_cast<Pool6>(*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<uint32_t>(subnet->getID()),
+ MySqlBinding::createString(subnet->toText()),
+ MySqlBinding::condCreateString(subnet->getClientClass()),
+ MySqlBinding::condCreateString(subnet->getIface()),
+ MySqlBinding::createTimestamp(subnet->getModificationTime()),
+ MySqlBinding::createInteger<uint32_t>(subnet->getPreferred()),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getRapidCommit())),
+ MySqlBinding::createInteger<uint32_t>(subnet->getT2()),
+ createInputRelayBinding(subnet),
+ MySqlBinding::createInteger<uint32_t>(subnet->getT1()),
+ createInputRequiredClassesBinding(subnet),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(subnet->getHostReservationMode())),
+ shared_network_binding,
+ createInputContextBinding(subnet),
+ MySqlBinding::createInteger<uint32_t>(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<uint32_t>(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<uint32_t>(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<Pool6>(pool),
+ subnet);
+ }
+
+ // (Re)create pd pools.
+ for (auto pool : subnet->getPools(Lease::TYPE_PD)) {
+ createPdPool6(server_selector,
+ boost::dynamic_pointer_cast<Pool6>(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<uint32_t>(static_cast<uint32_t>(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<uint8_t>(static_cast<uint8_t>(plen)),
+ MySqlBinding::createInteger<uint8_t>(pool->getLength()),
+ MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint64_t>(), // 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<uint32_t>(), // preferred_lifetime
+ MySqlBinding::createInteger<uint8_t>(), // rapid_commit
+ MySqlBinding::createInteger<uint32_t>(), // rebind_timer
+ MySqlBinding::createString(RELAY_BUF_LENGTH), // relay
+ MySqlBinding::createInteger<uint32_t>(), // renew_timer
+ MySqlBinding::createString(REQUIRE_CLIENT_CLASSES_BUF_LENGTH), // require_client_classes
+ MySqlBinding::createInteger<uint8_t>(), // reservation_mode
+ MySqlBinding::createString(USER_CONTEXT_BUF_LENGTH), // user_context
+ MySqlBinding::createInteger<uint32_t>(), // valid_lifetime
+ MySqlBinding::createInteger<uint64_t>(), // option: option_id
+ MySqlBinding::createInteger<uint16_t>(), // 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<uint8_t>(), // option: persistent
+ MySqlBinding::createInteger<uint32_t>(), // option: dhcp6_subnet_id
+ MySqlBinding::createInteger<uint8_t>(), // 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<uint64_t>(), // option: pool_id
+ MySqlBinding::createInteger<uint64_t>(), // 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<uint64_t>()) {
+
+ last_network_id = out_bindings[0]->getInteger<uint64_t>();
+ 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<uint32_t>());
+ }
+
+ // rapid_commit
+ last_network->setRapidCommit(static_cast<bool>
+ (out_bindings[6]->getIntegerOrDefault<uint8_t>(0)));
+
+ // rebind_timer
+ if (!out_bindings[7]->amNull()) {
+ last_network->setT2(out_bindings[7]->getInteger<uint32_t>());
+ }
+
+ // 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<uint32_t>());
+ }
+
+ // 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<Subnet6::HRMode>
+ (out_bindings[11]->getIntegerOrDefault<uint8_t>(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<uint32_t>());
+ }
+
+ shared_networks.push_back(last_network);
+ }
+
+ // Parse option.
+ if (!out_bindings[14]->amNull() &&
+ (last_option_id < out_bindings[14]->getInteger<uint64_t>())) {
+ last_option_id = out_bindings[14]->getInteger<uint64_t>();
+
+ 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<uint32_t>(shared_network->getPreferred()),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(shared_network->getRapidCommit())),
+ MySqlBinding::condCreateInteger<uint32_t>(shared_network->getT2()),
+ createInputRelayBinding(shared_network),
+ MySqlBinding::condCreateInteger<uint32_t>(shared_network->getT1()),
+ createInputRequiredClassesBinding(shared_network),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>
+ (shared_network->getHostReservationMode())),
+ createInputContextBinding(shared_network),
+ MySqlBinding::condCreateInteger<uint32_t>(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<uint64_t>(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<uint8_t>(option->option_->getType()),
+ createOptionValueBinding(option),
+ MySqlBinding::condCreateString(option->formatted_value_),
+ MySqlBinding::condCreateString(option->space_name_),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+ MySqlBinding::createNull(),
+ MySqlBinding::createNull(),
+ MySqlBinding::createInteger<uint8_t>(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<uint8_t>(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<uint8_t>(option->option_->getType()),
+ createOptionValueBinding(option),
+ MySqlBinding::condCreateString(option->formatted_value_),
+ MySqlBinding::condCreateString(option->space_name_),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+ MySqlBinding::createNull(),
+ MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)),
+ MySqlBinding::createInteger<uint8_t>(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<uint32_t>(static_cast<uint32_t>(subnet_id)));
+ in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(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<unsigned>(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<uint8_t>(option->option_->getType()),
+ createOptionValueBinding(option),
+ MySqlBinding::condCreateString(option->formatted_value_),
+ MySqlBinding::condCreateString(option->space_name_),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+ MySqlBinding::createNull(),
+ MySqlBinding::createNull(),
+ MySqlBinding::createInteger<uint8_t>(
+ pool_type == Lease::TYPE_NA ? 5 : 6),
+ createInputContextBinding(option),
+ MySqlBinding::createNull(),
+ pool_type == Lease::TYPE_NA ?
+ MySqlBinding::createInteger<uint64_t>(pool_id) :
+ MySqlBinding::createNull(),
+ pool_type == Lease::TYPE_PD ?
+ MySqlBinding::createInteger<uint64_t>(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<uint64_t>(pool_id));
+ in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(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<uint8_t>(option->option_->getType()),
+ createOptionValueBinding(option),
+ MySqlBinding::condCreateString(option->formatted_value_),
+ MySqlBinding::condCreateString(option->space_name_),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option->persistent_)),
+ MySqlBinding::createNull(),
+ MySqlBinding::createNull(),
+ MySqlBinding::createInteger<uint8_t>(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<uint8_t>(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<uint16_t>(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<uint16_t>(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<uint32_t>(static_cast<uint32_t>(subnet_id)),
+ MySqlBinding::createInteger<uint16_t>(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<uint64_t>(pool_id),
+ MySqlBinding::createInteger<uint16_t>(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<uint16_t>(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<int>(field)));
+ }
+ MySqlBindingPtr record_types_binding = record_types->empty() ?
+ MySqlBinding::createNull() : MySqlBinding::createString(record_types->str());
+
+ MySqlBindingCollection in_bindings = {
+ MySqlBinding::createInteger<uint16_t>(option_def->getCode()),
+ MySqlBinding::createString(option_def->getName()),
+ MySqlBinding::createString(option_def->getOptionSpaceName().empty() ?
+ "dhcp6" : option_def->getOptionSpaceName()),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(option_def->getType())),
+ MySqlBinding::createTimestamp(option_def->getModificationTime()),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(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<uint16_t>(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<uint64_t>(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<uint16_t>(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<uint16_t>(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<uint32_t>(static_cast<uint32_t>(subnet_id)),
+ MySqlBinding::createInteger<uint16_t>(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<uint16_t>(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<uint16_t>(code),
+ MySqlBinding::createString(space),
+ MySqlBinding::createString(pd_pool_prefix.toText()),
+ MySqlBinding::createInteger<uint8_t>(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<uint16_t>(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<uint32_t>(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<TaggedStatement, MySqlConfigBackendDHCPv6Impl::NUM_STATEMENTS>
+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
--- /dev/null
+// 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 <config.h>
+#include <mysql_cb_dhcp6.h>
+#include <database/testutils/schema.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_space.h>
+#include <dhcp/option_string.h>
+#include <dhcpsrv/pool.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/testutils/generic_backend_unittest.h>
+#include <mysql/testutils/mysql_schema.h>
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+#include <map>
+
+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<OptionString>(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<OptionUint8>(Option::V6, D6O_PREFERENCE,
+ false, true, 64);
+ desc.space_name_ = DHCP6_OPTION_SPACE;
+ test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+ desc = createOption<OptionUint32>(Option::V6, 1, false, false, 312131),
+ desc.space_name_ = "vendor-encapsulated-options";
+ test_options_.push_back(OptionDescriptorPtr(new OptionDescriptor(desc)));
+
+ desc = createAddressOption<Option6AddrLst>(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<Option6AddrLst>(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<Subnet6Ptr> test_subnets_;
+
+ /// @brief Holds pointers to shared networks used in tests.
+ std::vector<SharedNetwork6Ptr> test_networks_;
+
+ /// @brief Holds pointers to option definitions used in tests.
+ std::vector<OptionDefinitionPtr> test_option_defs_;
+
+ /// @brief Holds pointers to options used in tests.
+ std::vector<OptionDescriptorPtr> test_options_;
+
+ /// @brief Holds timestamp values used in tests.
+ std::map<std::string, boost::posix_time::ptime> timestamps_;
+
+ /// @brief Holds pointer to the backend.
+ boost::shared_ptr<ConfigBackendDHCPv6> 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<StampedValuePtr> 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_);
+}
+
+
+}