diff --git a/configure.ac b/configure.ac index e9f2b0b486a725f7262edfa1fbe06ca2bb179d1e..b784dfc19a970b5b0ea1dd7cd33d160bb6f886fc 100644 --- a/configure.ac +++ b/configure.ac @@ -1534,6 +1534,8 @@ AC_CONFIG_FILES([Makefile src/lib/config/tests/Makefile src/lib/config/tests/data_def_unittests_config.h src/lib/config/tests/testdata/Makefile + src/lib/config_backend/Makefile + src/lib/config_backend/tests/Makefile src/lib/cryptolink/Makefile src/lib/cryptolink/tests/Makefile src/lib/database/Makefile diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 319c99549062345c26e13faa1a294bb3d777abe4..211aafe810839806ae6bb763bd5a28e144643e1f 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -13,7 +13,7 @@ if HAVE_CQL SUBDIRS += cql endif -SUBDIRS += testutils hooks dhcp config stats +SUBDIRS += config_backend testutils hooks dhcp config stats if HAVE_SYSREPO SUBDIRS += yang diff --git a/src/lib/config_backend/Makefile.am b/src/lib/config_backend/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..163d378799eeefc17bbfb8c38df554eb5da5dadb --- /dev/null +++ b/src/lib/config_backend/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +EXTRA_DIST = base_config_backend.h +EXTRA_DIST += base_config_backend_mgr.h +EXTRA_DIST += base_config_backend_pool.h + +# The message file should be in the distribution. +#EXTRA_DIST += config_backend.dox + +CLEANFILES = *.gcno *.gcda + +# Specify the headers for copying into the installation directory tree. +libkea_cb_includedir = $(pkgincludedir)/config_backend +libkea_cb_include_HEADERS = \ + base_config_backend.h \ + base_config_backend_mgr.h \ + base_config_backend_pool.h + diff --git a/src/lib/config_backend/base_config_backend.h b/src/lib/config_backend/base_config_backend.h new file mode 100644 index 0000000000000000000000000000000000000000..b9b040529745ef4784811c4004c6a23aca1ba301 --- /dev/null +++ b/src/lib/config_backend/base_config_backend.h @@ -0,0 +1,67 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BASE_CONFIG_BACKEND_H +#define BASE_CONFIG_BACKEND_H + +#include +#include +#include +#include + +namespace isc { +namespace cb { + +/// @brief Interface for Kea server specific configuration backend +/// implementations. +/// +/// Each Kea server (e.g. DHCPv4 server) needs to implement its own +/// interface to store and fetch its configuration from the databases. +/// This is because each Kea server uses a different set of +/// configuration information. This is a base interface which should +/// be implemented (and extended) by respective Kea servers to provide +/// API to store and fetch configuration information from a database. +/// Such implementation is called configuration backend. Each +/// configuration backend faciliates a single database type, e.g. MySQL +/// database. In order to support multiple database types, i.e. MySQL, +/// Posrgres, Cassandra, each Kea server will have to implement +/// 3 separate configuration backends, one for each database type. +class BaseConfigBackend { +public: + + /// @brief Virtual destructor. + virtual ~BaseConfigBackend() { } + + /// @brief Returns backend type in the textual format. + /// + /// @return Name of the storage for configurations, e.g. "mysql", + /// "pgsql" and so forth. + virtual std::string getType() const = 0; + + /// @brief Returns backend host. + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return host on which the database is located. + virtual std::string getHost() const = 0; + + /// @brief Returns backend port number. + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return Port number on which database service is available. + virtual uint16_t getPort() const = 0; +}; + +/// @brief Shared pointer to the @c BaseConfigBackend. +typedef boost::shared_ptr BaseConfigBackendPtr; + +} // end of namespace isc::cb +} // end of namespace isc + +#endif // BASE_CONFIG_BACKEND_H diff --git a/src/lib/config_backend/base_config_backend_mgr.h b/src/lib/config_backend/base_config_backend_mgr.h new file mode 100644 index 0000000000000000000000000000000000000000..62684ac4c384cab12d07934667403385c7ed7446 --- /dev/null +++ b/src/lib/config_backend/base_config_backend_mgr.h @@ -0,0 +1,175 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BASE_CONFIG_BACKEND_MGR_H +#define BASE_CONFIG_BACKEND_MGR_H + +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace cb { + +/// @brief Base class for Configuration Backend Managers (CBM). +/// +/// Each Kea server supporting Configuration Backend feature implements +/// a "manager" class which holds information about supported and +/// configured backends and provides access to the backends. This is +/// similar to @c HostMgr and @c LeaseMgr singletons being used by the +/// DHCP servers. +/// +/// The Config Backend Managers are typically implemented as singletons +/// which can be accessed from any place within the server code. This +/// includes server configuration, data fetching during normal server +/// operation and data management, including processing of control +/// commands implemented within hooks libraries. +/// +/// The @c BaseConfigBackendMgr is a base class for all CBMs implemented +/// for respective Kea servers. It includes mechanisms to register config +/// backend factory functions and to create instances of the backends using +/// those factory functions as a result of server configuration. The mechanism +/// of factory functions registration is useful in cases when the config +/// backend is implemented within the hook library. Such hook library +/// registers factory function in its @c load function and the server +/// simply calls this function to create the instance of this backend when +/// instructed to do so via configuration. Similar mechanism exists in +/// DHCP @c HostMgr. +/// +/// Unlike @c HostMgr, the CBMs do not directly expose API to fetch and +/// manipulate the data in the database. This is done via, so called, +/// Configuration Backends Pools. See @c BaseConfigBackendPool for +/// details. The @c BaseConfigBackendMgr is provided with the pool type +/// via class template parameter. Respective CBM implementations +/// use their own pools, which provide APIs appropriate for those +/// implementation. +/// +/// @tparam ConfgBackendPoolType Type of the configuration backend pool +/// to be used by the manager. It must derive from @c BaseConfigBackendPool +/// template class. +template +class BaseConfigBackendMgr { +public: + + /// @brief Pointer to the configuration backend pool. + typedef boost::shared_ptr ConfigBackendPoolPtr; + + /// @brief Type of the backend factory function. + /// + /// Factory function returns a pointer to the instance of the configuration + /// backend created. + typedef std::function Factory; + + /// @brief Constructor. + BaseConfigBackendMgr() + : factories_(), pool_(new ConfigBackendPoolType()) { + } + + /// @brief Registers new backend factory function for a given backend type. + /// + /// The typical usage of this function is to make the CBM aware of a + /// configuration backend implementation. This implementation may exist + /// in a hooks library. In such case, this function should be called from + /// the @c load function in this library. When the backend is registered, + /// the server will use it when required by the configuration, i.e. a + /// user includes configuration backend of that type in the + /// "config-databases" list. + /// + /// If the backend of the given type has already been registered, perhaps + /// by another hooks library, the CBM will refuse to register another + /// backend of the same type. + /// + /// @param db_type Backend type, e.g. "mysql". + /// @param factory Pointer to the backend factory function. + /// + /// @return true if the backend has been successfully registered, false + /// if another backend of this type already exists. + bool registerBackendFactory(const std::string& db_type, + const Factory& factory) { + // Check if this backend has been already registered. + if (factories_.count(db_type)) { + return (false); + } + + // Register the new backend. + factories_.insert(std::make_pair(db_type, factory)); + return (true); + } + + /// @brief Create an instance of a configuration backend. + /// + /// This method uses provided @c dbaccess string representing database + /// connection information to create an instance of the database + /// backend. If the specified backend type is not supported, i.e. there + /// is no relevant factory function registered, an exception is thrown. + /// + /// @param dbaccess Database access string being a collection of + /// key=value pairs. + /// + /// @throw InvalidParameter if access string lacks database type value. + /// @throw db::InvalidType if the type of the database backend is not + /// supported. + /// @throw Unexpected if the backend factory function returned NULL. + void addBackend(const std::string& dbaccess) { + // Parse the access string into a map of parameters. + db::DatabaseConnection::ParameterMap parameters = + db::DatabaseConnection::parse(dbaccess); + + // Get the database type to locate a factory function. + db::DatabaseConnection::ParameterMap::iterator it = parameters.find("type"); + if (it == parameters.end()) { + isc_throw(InvalidParameter, "Config backend specification lacks the " + "'type' keyword"); + } + + std::string db_type = it->second; + auto index = factories_.find(db_type); + + // No match? + if (index == factories_.end()) { + isc_throw(db::InvalidType, "The type of the configuration backend: '" << + db_type << "' is not supported"); + } + + // Call the factory and push the pointer on sources. + auto backend = index->second(parameters); + if (!backend) { + isc_throw(Unexpected, "Config database " << db_type << + " factory returned NULL"); + } + + // Backend instance created successfully. + pool_->addBackend(backend); + } + + /// @brief Removes all backends from the pool. + void delAllBackends() { + pool_->delAllBackends(); + } + + /// @brief Returns underlying config backend pool. + ConfigBackendPoolPtr getPool() const { + return (pool_); + } + +protected: + + /// @brief A map holding registered backend factory functions. + std::map factories_; + + /// @brief Pointer to the configuration backends pool. + ConfigBackendPoolPtr pool_; +}; + +} // end of namespace isc::cb +} // end of namespace isc + +#endif // BASE_CONFIG_BACKEND_MGR_H diff --git a/src/lib/config_backend/base_config_backend_pool.h b/src/lib/config_backend/base_config_backend_pool.h new file mode 100644 index 0000000000000000000000000000000000000000..b3d1c81623dc097272550d9a9b0ecb405257f20a --- /dev/null +++ b/src/lib/config_backend/base_config_backend_pool.h @@ -0,0 +1,504 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BASE_CONFIG_BACKEND_POOL_H +#define BASE_CONFIG_BACKEND_POOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace cb { + + +/// @brief Base class for configuration backend pools. +/// +/// Each Kea server supporting databases as a configuration repository can +/// use multiple database instances simultaneously. A pool is a collection +/// of database backends used by a particular server. Different Kea servers +/// use different pools because they store and fetch different configuration +/// information. For example: DHCPv4 server stores and fetches IPv4 subnets, +/// and DHCPv6 server stores and fetches IPv6 subnets. Therefore, each pool +/// type will expose a different API calls. +/// +/// This template class is a base class for all pools used by various servers. +/// It implements mechanisms for managing multiple backends and for forwarding +/// API calls to one or many database backends depending on the selections +/// made via @c BackendSelector class. +/// +/// @tparam ConfigBackendType Type of the configuration backend. This must +/// be a class deriving from @c BaseConfigBackend class. It is a class +/// dedicated to a particular server type, e.g. DHCPv4 server, and from +/// which database specific backends derive. +template +class BaseConfigBackendPool { +public: + + /// @brief Shared pointer to the Configuration Backend used. + typedef boost::shared_ptr ConfigBackendTypePtr; + + /// @brief Virtual destructor. + virtual ~BaseConfigBackendPool() { } + + /// @brief Adds a backend to the pool. + /// + /// @param backend Pointer to a backend to be added. + void addBackend(ConfigBackendTypePtr backend) { + backends_.push_back(backend); + } + + /// @brief Deletes all backends from the pool. + void delAllBackends() { + backends_.clear(); + } + +protected: + + /// @brief Retrieve a single configuration property from the pool. + /// + /// This is common method for retrieving a single configuration property + /// from the databases. The server specific backends call this method to + /// retrieve a single object. For example, the DHCPv4 configuration backend + /// pool may use this function to implement a @c getSubnet4 method: + /// + /// @code + /// Subnet4Ptr getSubnet4(const SubnetID& subnet_id, + /// const BackendSelector& backend_selector, + /// const ServerSelector& server_selector) const { + /// Subnet4Ptr subnet; + /// getPropertyPtrConst + /// (&ConfigBackendDHCPv4::getSubnet4, backend_selector, + /// server_selector, subnet, subnet_id); + /// return (subnet); + /// } + /// @endcode + /// + /// where @c ConfigBackendDHCPv4::getSubnet4 has the following signature: + /// + /// @code + /// Subnet4Ptr getSubnet4(const ServerSelector&, const SubnetID&) const; + /// @endcode + /// + /// If the backend selector is set to "unspecified", this method will iterate + /// over the existing backends and call the @c MethodPointer method on each + /// backend. It will return the first non-null (or non-zero) value returned + /// by this call. For example: if the first backend returns non-null value, + /// this value is returned via @c property argument and the calls for the + /// rest of the backends are skipped. + /// + /// @tparam PropertyType Type of the object returned by the backend call. + /// @tparam FnPtrArgs Parameter pack holding argument types of the backend + /// method to be invoked. + /// @tparam Args Parameter pack holding types of the arguments provided + /// in the call to this method. + /// + /// @param MethodPointer Pointer to the backend method to be called. + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param [out] property Reference to the shared pointer where retrieved + /// property should be assigned. + /// @param input Values to be used as input to the backend call. + /// + /// @throw db::NoSuchDatabase if no database matching the given selector + /// was found. + template + void getPropertyPtrConst(PropertyType (ConfigBackendType::*MethodPointer) + (const db::ServerSelector&, FnPtrArgs...) const, + const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + PropertyType& property, + Args... input) const { + + // If no particular backend is selected, call each backend and return + // the first non-null (non zero) value. + if (backend_selector.amUnspecified()) { + for (auto backend : backends_) { + property = ((*backend).*MethodPointer)(server_selector, input...); + if (property) { + break; + } + } + + } else { + // Backend selected, find the one that matches selection. + auto backends = selectBackends(backend_selector); + if (!backends.empty()) { + for (auto backend : backends) { + property = ((*backend).*MethodPointer)(server_selector, input...); + if (property) { + break; + } + } + + } else { + isc_throw(db::NoSuchDatabase, "no such database found for selector: " + << backend_selector.toText()); + } + } + } + + /// @brief Retrieve a single value encapsulated in the @c OptionalValue + /// template. + /// + /// This is common method for retrieving a single configuration property + /// from the databases. The property is encapsulated in the @c OptionalValue + /// class. The value is set to "unspecified" if it is null in the database. + /// The following is the example implementation of the method retrieving + /// global conifguration value: + /// + /// @code + /// OptionalValue + /// getGlobalParameter4(const std::string& parameter_name, + /// const BackendSelector& backend_selector, + /// const ServerSelector& server_selector) const { + /// std::string parameter; + /// getOptionalPropertyConst + /// (&ConfigBackendDHCPv4::getGlobalParameter4, backend_selector, + /// server_selector, parameter, parameter_name); + /// return (parameter); + /// } + /// @endcode + /// + /// where @c ConfigBackendDHCPv4::getGlobalParameter has the following signature: + /// + /// @code + /// std::string getGlobalParameter4(const ServerSelector&, const std::string&) const; + /// @endcode + /// + /// + /// @tparam PropertyType Type of the object returned by the backend call. + /// @tparam FnPtrArgs Parameter pack holding argument types of the backend + /// method to be invoked. + /// @tparam Args Parameter pack holding types of the arguments provided + /// in the call to this method. + /// + /// @param MethodPointer Pointer to the backend method to be called. + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param [out] property Reference to the shared pointer where retrieved + /// property should be assigned. + /// @param input Values to be used as input to the backend call. + /// + /// @throw db::NoSuchDatabase if no database matching the given selector + /// was found. + template + void getOptionalPropertyConst(util::OptionalValue + (ConfigBackendType::*MethodPointer) + (const db::ServerSelector&, FnPtrArgs...) const, + const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + util::OptionalValue& property, + Args... input) const { + + // If no particular backend is selected, call each backend and return + // the first non-null (non zero) value. + if (backend_selector.amUnspecified()) { + for (auto backend : backends_) { + property = ((*backend).*MethodPointer)(server_selector, input...); + if (property.isSpecified()) { + break; + } + } + + } else { + // Backend selected, find the one that matches selection. + auto backends = selectBackends(backend_selector); + if (!backends.empty()) { + for (auto backend : backends) { + property = ((*backend).*MethodPointer)(server_selector, input...); + if (property.isSpecified()) { + break; + } + } + + } else { + isc_throw(db::NoSuchDatabase, "no such database found for selector: " + << backend_selector.toText()); + } + } + } + + /// @brief Retrieve multiple configuration properties from the pool. + /// + /// This is a common method for retrieving multiple configuration properties + /// from the databases. The server specific backends call this method to + /// retrieve multiple objects of the same type. For example, the DHCPv6 + /// configuration backend pool may use this function to implement a + /// @c getSubnets6 method: + /// + /// @code + /// Subnet6Collection getModifiedSubnets6(const BackendSelector& backend_selector, + /// const ServerSelector& server_selector, + /// const ptime& modification_time) const { + /// Subnet6Collection subnets; + /// getMultiplePropertiesConst + /// (&ConfigBackendDHCPv6::getSubnets6, backend_selector, server_selector, + /// subnets, modification_time); + /// return (subnets); + /// } + /// @endcode + /// + /// where @c ConfigBackendDHCPv6::getSubnets6 has the following signature: + /// + /// @code + /// Subnet6Collection getSubnets6(const ServerSelector&, const ptime&) const; + /// @endcode + /// + /// If the backend selector is set to "unspecified", this method will iterate + /// over existing backends and call the @c MethodPointer method on each + /// backend. It will return the first non-empty list returned by one of the + /// backends. + /// + /// @tparam PropertyCollectionType Type of the container into which the + /// properties are stored. + /// @tparam FnPtrArgs Parameter pack holding argument types of the backend + /// method to be invoked. + /// @tparam Args Parameter pack holding types of the arguments provided + /// in the call to this method. + /// + /// @param MethodPointer Pointer to the backend method to be called. + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param [out] properties Reference to the collection of retrieved properties. + /// @param input Values to be used as input to the backend call. + /// + /// @throw db::NoSuchDatabase if no database matching the given selector + /// was found. + template + void getMultiplePropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer) + (const db::ServerSelector&, FnPtrArgs...) const, + const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + PropertyCollectionType& properties, + Args... input) const { + if (backend_selector.amUnspecified()) { + for (auto backend : backends_) { + properties = ((*backend).*MethodPointer)(server_selector, input...); + if (!properties.empty()) { + break; + } + } + + } else { + auto backends = selectBackends(backend_selector); + if (!backends.empty()) { + for (auto backend : backends) { + properties = ((*backend).*MethodPointer)(server_selector, input...); + if (!properties.empty()) { + break; + } + } + + } else { + isc_throw(db::NoSuchDatabase, "no database found for selector: " + << backend_selector.toText()); + } + } + } + + /// @brief Retrieve all configuration properties from the pool. + /// + /// This is a common method for retrieving all configuration properties + /// from the databases. The server specific backends call this method + /// to retrieve all objects of the same type. For example, the DHCPv4 + /// configuration backend pool may use this function to implement a + /// @c getAllSubnets4 method: + /// + /// @code + /// Subnet4Collection getAllSubnets4(const BackendSelector&, const ServerSelector&) const { + /// Subnet4Collection subnets; + /// getAllPropertiesConst + /// (&ConfigBackendDHCPv4::getAllSubnets4, subnets, backend_selector, + /// server_selector); + /// return (subnets); + /// } + /// @endcode + /// + /// where @c ConfigBackendDHCPv4::getAllSubnets4 has the following signature: + /// + /// @code + /// Subnet4Collection getAllSubnets4(const ServerSelector&) const; + /// @endcode + /// + /// If the backend selector is set to "unspecified", this method will iterate + /// over existing backends and call the @c MethodPointer method on each + /// backend. It will return the first non-empty list returned by one of the + /// backends. + /// + /// @tparam PropertyCollectionType Type of the container into which the + /// properties are stored. + /// + /// @param MethodPointer Pointer to the backend method to be called. + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param [out] properties Reference to the collection of retrieved properties. + /// + /// @throw db::NoSuchDatabase if no database matching the given selector + /// was found. + template + void getAllPropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer) + (const db::ServerSelector&) const, + const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + PropertyCollectionType& properties) const { + if (backend_selector.amUnspecified()) { + for (auto backend : backends_) { + properties = ((*backend).*MethodPointer)(server_selector); + if (!properties.empty()) { + break; + } + } + + } else { + auto backends = selectBackends(backend_selector); + if (!backends.empty()) { + for (auto backend : backends) { + properties = ((*backend).*MethodPointer)(server_selector); + if (!properties.empty()) { + break; + } + } + + } else { + isc_throw(db::NoSuchDatabase, "no database found for selector: " + << backend_selector.toText()); + } + } + } + + + /// @brief Add, update or delete property from the backend. + /// + /// This is a common method for storing a single configuration property in + /// a database, updating an existing property or deleting the property. + /// The server specific backends call this method. For example, + /// the DHCPv6 configuration backend pool may use this function to implement + /// a @c createUpdateSubnet6 method: + /// + /// @code + /// void createUpdateSubnet6(const Subnet6Ptr& subnet, + /// const BackendSelector& backend_selector, + /// const ServerSelector& server_selector) { + /// createUpdateDeleteProperty + /// (&ConfigBackendDHCPv6::createUpdateSubnet6, backend_selector, + /// server_selector, subnet, selector); + /// } + /// @endcode + /// + /// where @c ConfigBackendDHCPv6::createUpdateSubnet6 has the following + /// signature: + /// + /// @code + /// void createUpdateSubnet6(const ServerSelector&, const Subnet6Ptr&); + /// @endcode + /// + /// The backend selector must point to exactly one backend. If more than one + /// backend is selected, an exception is thrown. If no backend is selected + /// an exception is thrown either. + /// + /// @tparam FnPtrArgs Parameter pack holding argument types of the backend + /// method to be invoked. + /// @tparam Args Parameter pack holding types of the arguments provided + /// in the call to this method. + /// + /// @param MethodPointer Pointer to the backend method to be called. + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param input Objects used as arguments to the backend method to be + /// called. + /// + /// @throw db::NoSuchDatabase if no database matching the given selector + /// was found. + /// @throw db::AmbiguousDatabase if multiple databases matching the selector + /// were found. + template + void createUpdateDeleteProperty(void (ConfigBackendType::*MethodPointer) + (const db::ServerSelector&, FnPtrArgs...), + const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + Args... input) { + auto backends = selectBackends(backend_selector); + if (backends.empty()) { + isc_throw(db::NoSuchDatabase, "no database found for selector: " + << backend_selector.toText()); + + } else if (backends.size() > 1) { + isc_throw(db::AmbiguousDatabase, "more than 1 database found for " + "selector: " << backend_selector.toText()); + } + + (*(*(backends.begin())).*MethodPointer)(server_selector, input...); + } + + /// @brief Selects existing backends matching the selector. + /// + /// This method selects backends matching the selector. If the selector is + /// "unspecified" or there is no backend in the pool, an empty list is returned. + /// + /// @param selector Selector for which matching backends should be selected. + std::list + selectBackends(const db::BackendSelector& backend_selector) const { + + std::list selected; + + // In case there is only one backend and the caller hasn't specified + // any particular backend, simply return it. + if ((backends_.size() == 1) && backend_selector.amUnspecified()) { + selected.push_back(*backends_.begin()); + return (selected); + } + + // For other cases we return empty list. + if (backends_.empty() || backend_selector.amUnspecified()) { + return (selected); + } + + // Go over all backends. + for (auto backend : backends_) { + // If backend type is specified and it is not matching, + // do not select this backend. + if ((backend_selector.getBackendType() != db::BackendSelector::Type::UNSPEC) && + (backend_selector.getBackendType() != + db::BackendSelector::stringToBackendType(backend->getType()))) { + continue; + } + + // If the host has been specified by the backend's host is not + // matching, do not select this backend. + if ((!backend_selector.getBackendHost().empty()) && + (backend_selector.getBackendHost() != backend->getHost())) { + continue; + } + + // If the port has been specified by the backend's port is not + // matching, do not select this backend. + if ((backend_selector.getBackendPort() != 0) && + (backend_selector.getBackendPort() != backend->getPort())) { + continue; + } + + // Passed all checks, so the backend is matching. Add it to the list. + selected.push_back(backend); + } + + return (selected); + } + + /// @brief Holds configuration backends belonging to the pool. + std::list backends_; +}; + +} // end of namespace isc::cb +} // end of namespace isc + +#endif // BASE_CONFIG_BACKEND_POOL_H diff --git a/src/lib/config_backend/tests/.gitignore b/src/lib/config_backend/tests/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c38b41f478d85a8892ab2fc49135c1da6af65072 --- /dev/null +++ b/src/lib/config_backend/tests/.gitignore @@ -0,0 +1 @@ +/libcb_unittests diff --git a/src/lib/config_backend/tests/Makefile.am b/src/lib/config_backend/tests/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..0ab44250c33b1e09aeb93cd7e953686c8b9cbaf4 --- /dev/null +++ b/src/lib/config_backend/tests/Makefile.am @@ -0,0 +1,39 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += libcb_unittests + +libcb_unittests_SOURCES = config_backend_mgr_unittest.cc +libcb_unittests_SOURCES += run_unittests.cc + +libcb_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +libcb_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) + +libcb_unittests_LDADD = $(top_builddir)/src/lib/database/libkea-database.la +libcb_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libcb_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libcb_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +libcb_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la +libcb_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +libcb_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libcb_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD) + +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/config_backend/tests/config_backend_mgr_unittest.cc b/src/lib/config_backend/tests/config_backend_mgr_unittest.cc new file mode 100644 index 0000000000000000000000000000000000000000..040d1048a23fc1f1d133799c24ed73f8c40f7358 --- /dev/null +++ b/src/lib/config_backend/tests/config_backend_mgr_unittest.cc @@ -0,0 +1,529 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::cb; +using namespace isc::db; + +namespace { + +/// @brief Defines list of properties retrieved by @c TestConfigBackend. +/// +/// A single property is a name/value pair, where value is an integer. +typedef std::list > PropertiesList; + +/// @brief Implements configuration backend used in tests. +/// +/// @c BaseConfigBackend is an abstract class that must be implemented +/// in order to allow us to test mechanisms implemented in +/// @c BaseConfigBackendMgr and @c BaseConfigBackendPool. +/// +/// Normally, a class derived directly from the @c BaseConfigBackend +/// will merely provide an interface for server specific operations, +/// e.g. DHCPv4 specific operations, and the database specific classes +/// will implement this interface. However, for the test purposes it +/// is convenient to implement them here and let the derivations +/// only provide the implementations of the @c getType, @c getHost +/// and @c getPort functions. That way, the logic for adding and +/// retrieving the data from the backend is implemented only once and +/// is common accross all test backends. +/// +/// This class provides a logic for managing test data being a collection +/// of name/value pairs, i.e. "properties". It contains a list of +/// properties and the functions for adding the properties, retrieving +/// a single property and retrieving a collection of properties. +class TestConfigBackend : public BaseConfigBackend { +public: + + /// @brief Retrieves first property having a given name. + /// + /// @param property_name Name of the property to be retrieved. + /// @return Value of the property or 0 if property doesn't exist. + virtual int getProperty(const ServerSelector&, + const std::string& property_name) const { + for (auto property : properties_) { + if (property.first == property_name) { + return (property.second); + } + } + return (0); + } + + /// @brief Retrieves first property matching the name and value. + /// + /// @param property_name Name of the property to be retrieved. + /// @param property_value Value of the property to be retrieved. + /// @return Value of the property or 0 if the property doesn't exist. + virtual int getProperty(const ServerSelector&, + const std::string& property_name, + const int property_value) const { + for (auto property : properties_) { + if ((property.first == property_name) && + (property.second == property_value)) { + return (property.second); + } + } + return (0); + } + + /// @brief Retrieves all properties having a given name. + /// + /// @param property_name Name of the properties to be retrieved. + /// @return List of the properties having a given name. This list is + /// empty if no property was found. + virtual PropertiesList getProperties(const ServerSelector&, + const std::string& property_name) const { + PropertiesList properties; + for (auto property : properties_) { + if (property.first == property_name) { + properties.push_back(property); + } + } + return (properties); + } + + /// @brief Retrieves all properties. + /// + /// @return List of all properties held in the backend. + virtual PropertiesList getAllProperties(const ServerSelector&) const { + return (properties_); + } + + /// @brief Creates new property. + /// + /// @param new_property Property to be added to the backend. + virtual void createProperty(const ServerSelector&, + const std::pair& new_property) { + properties_.push_back(new_property); + } + +protected: + + /// @brief Holds list of properties (simulates database). + PropertiesList properties_; + +}; + +/// @brief Shared pointer to the @c TestConfigBackend. +typedef boost::shared_ptr TestConfigBackendPtr; + +/// @brief First implementation of the test config backend. +/// +/// It simulates being a MySQL backend installed on the +/// "mysql-host" host and running on port 2345. +class TestConfigBackendImpl1 : public TestConfigBackend { +public: + + /// @brief Returns backend type. + /// + /// @return "mysql". + virtual std::string getType() const { + return (std::string("mysql")); + } + + /// @brief Returns backend host. + /// + /// @return "mysql-host". + virtual std::string getHost() const { + return (std::string("mysql-host")); + } + + /// @brief Returns backend port. + /// + /// @return Port number 2345. + virtual uint16_t getPort() const { + return (2345); + } + +}; + +/// @brief Shared pointer to the @c TestConfigBackendImpl1. +typedef boost::shared_ptr TestConfigBackendImpl1Ptr; + +/// @brief Second implementation of the test config backend. +/// +/// It simulates being a Postgres backend installed on the +/// "pgsql-host" host and running on port 1234. +class TestConfigBackendImpl2 : public TestConfigBackend { +public: + + /// @brief Returns backend type. + /// + /// @return "pgsql". + virtual std::string getType() const { + return (std::string("pgsql")); + } + + /// @brief Returns backend host. + /// + /// @return "pgsql-host". + virtual std::string getHost() const { + return (std::string("pgsql-host")); + } + + /// @brief Returns backend port. + /// + /// @return Port number 1234. + virtual uint16_t getPort() const { + return (1234); + } +}; + +/// @brief Shared pointer to the @c TestConfigBackendImpl2. +typedef boost::shared_ptr TestConfigBackendImpl2Ptr; + +/// @brief Implements test pool of configuration backends. +/// +/// @c BaseConfigBackendPool template provides mechanics for managing the data +/// stored in multiple backends. Server specific pools must extend this class +/// with methods for managing the data appropriate for the server types. +/// This class provides an example pool implementation for managing the +/// "properties" being name/value pairs. It extends the base class with +/// new methods to retrieve a single property and multiple properties. It +/// also adds a method to create new property. Those methods correspond to +/// the ones implemented in the @c TestConfigBackend, but also each of +/// them includes a "database selector" used to indicate the backend to +/// be used. +class TestConfigBackendPool : public BaseConfigBackendPool { +public: + + /// @brief Retrieves a value of the property. + /// + /// @param property_name Name of the property which value should be returned. + /// @param backend_selector Backend selector. The default value of the selector + /// is @c UNSPEC which means that the property will be searched in all backends + /// and the first value found will be returned. + /// @param server_selector Server selector. The default value is set to @c ALL, + /// which means that the property for all servers will be returned. + virtual int getProperty(const std::string& property_name, + const BackendSelector& backend_selector = + BackendSelector::UNSPEC(), + const ServerSelector& server_selector = + ServerSelector::ALL()) const { + int property; + + // If the selector is specified, this method will pick the appropriate + // backend and will call getProperty method on this backend. If the + // selector is not specified, this method will iterate over existing + // backends and call getProperty on them. It will return after finding + // the first non-zero value of the property. For example, if the first + // backend contains a non-zero value this value will be returned and + // the value held in the second backend (if any) won't be fetched. + // The template arguments specify the returned value type and the + // argument of the getProperty method. + getPropertyPtrConst + (&TestConfigBackend::getProperty, backend_selector, server_selector, + property, property_name); + return (property); + } + + /// @brief Retrieves value of the property. + /// + /// @param property_name Name of the property which value should be returned. + /// @param property_value Value of the property to be retrieved. + /// @param backend_selector Backend selector. The default value of the selector + /// is @c UNSPEC which means that the property will be searched in all backends + /// and the first value found will be returned. + /// @param server_selector Server selector. The default value is set to @c ALL, + /// which means that the property for all servers will be returned. + virtual int getProperty(const std::string& property_name, + const int property_value, + const BackendSelector& backend_selector = + BackendSelector::UNSPEC(), + const ServerSelector& server_selector = + ServerSelector::ALL()) const { + int property; + getPropertyPtrConst + (&TestConfigBackend::getProperty, backend_selector, server_selector, + property, property_name, property_value); + return (property); + } + + + /// @brief Retrieves multiple properties. + /// + /// @param property_name Name of the properties which should be retrieved. + /// @param backend_selector Backend selector. The default value of the selector + /// is @c UNSPEC which means that the properties will be searched in all + /// backends and the first non-empty list will be returned. + /// @param server_selector Server selector. The default value is set to @c ALL, + /// which means that the properties for all servers will be returned. + virtual PropertiesList getProperties(const std::string& property_name, + const BackendSelector& backend_selector = + BackendSelector::UNSPEC(), + const ServerSelector& server_selector = + ServerSelector::ALL()) const { + PropertiesList properties; + + // If the selector is specified, this method will pick the appropriate + // backend and will call getProperties method on this backend. If the + // selector is not specified, this method will iterate over existing + // backends and call getProperties on them. It will return after finding + // the first non-empty list of properties in one of the backends. + // The template arguments specify the type of the list of properties + // and the argument of the getProperties method. + getMultiplePropertiesConst + (&TestConfigBackend::getProperties, backend_selector, server_selector, + properties, property_name); + return (properties); + } + + /// @brief Retrieves all properties. + /// + /// @param backend_selector Backend selector. The default value of the selector + /// is @c UNSPEC which means that the properties will be searched in all + /// backends and the first non-empty list will be returned. + /// @param server_selector Server selector. The default value is set to @c ALL, + /// which means that the properties for all servers will be returned. + virtual PropertiesList getAllProperties(const BackendSelector& backend_selector = + BackendSelector::UNSPEC(), + const ServerSelector& server_selector = + ServerSelector::ALL()) const { + PropertiesList properties; + + // This method is similar to getMultiplePropertiesConst but it lacks + // an argument and it simply returns all properties. + getAllPropertiesConst + (&TestConfigBackend::getAllProperties, backend_selector, server_selector, + properties); + return (properties); + } + + /// @brief Creates new property in a selected backend. + /// + /// @param new_property New property to be added to a backend. + /// @param backend_selector Backend selector. It has no default value. + /// @param server_selector The default value is @c ALL which means that + /// new property is going to be shared by all servers. + virtual void createProperty(const std::pair& new_property, + const BackendSelector& backend_selector, + const ServerSelector& server_selector = + ServerSelector::ALL()) { + createUpdateDeleteProperty&> + (&TestConfigBackend::createProperty, backend_selector, server_selector, + new_property); + } +}; + +using TestConfigBackendMgr = BaseConfigBackendMgr; + +/// @brief Test fixture class for testing @c ConfigBackendMgr. +class ConfigBackendMgrTest : public ::testing::Test { +public: + + /// @brief Constructor. + ConfigBackendMgrTest() + : config_mgr_() { + } + + /// @brief Destructor. + /// + /// Removes backend instances. + ~ConfigBackendMgrTest() { + config_mgr_.delAllBackends(); + } + + /// @brief Creates example database backend called "mysql". + /// + /// It uses @c TestConfigBackendImpl1. + void addTestMySQLBackend() { + config_mgr_.registerBackendFactory("mysql", [](const DatabaseConnection::ParameterMap&) + -> TestConfigBackendPtr { + return (TestConfigBackendImpl1Ptr(new TestConfigBackendImpl1())); + }); + + config_mgr_.addBackend("type=mysql"); + } + + /// @brief Creates example database backend called "pgsql". + /// + /// It uses @c TestConfigBackendImpl2. + void addTestPgSQLBackend() { + config_mgr_.registerBackendFactory("pgsql", [](const DatabaseConnection::ParameterMap&) + -> TestConfigBackendPtr { + return (TestConfigBackendImpl2Ptr(new TestConfigBackendImpl2())); + }); + + // Actually create the backends. + config_mgr_.addBackend("type=pgsql"); + } + + /// @brief Creates two example database backends called "mysql" and "pgsql". + /// + /// It uses @c TestConfigBackendImpl1 and @c TestConfigBackendImpl2. + void addTestBackends() { + addTestMySQLBackend(); + addTestPgSQLBackend(); + } + + /// @brief Adds test data into the backends. + void addTestData() { + // Add two properties with different names into the first backend. + config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1), + BackendSelector(BackendSelector::Type::MYSQL)); + config_mgr_.getPool()->createProperty(std::make_pair("wolves", 3), + BackendSelector(BackendSelector::Type::MYSQL)); + + // Add two properties into the second backend. Both properties share the + // name so as we can test retrieving multiple records from the same backend. + config_mgr_.getPool()->createProperty(std::make_pair("cats", 2), + BackendSelector(BackendSelector::Type::PGSQL)); + config_mgr_.getPool()->createProperty(std::make_pair("cats", 4), + BackendSelector(BackendSelector::Type::PGSQL)); + } + + /// Instance of the test configuration manager. + TestConfigBackendMgr config_mgr_; +}; + +// Test that selector can be left "unspecified" if there is only one backend, +// when manipulating the data. +TEST_F(ConfigBackendMgrTest, createPropertySingleBackendUnspec) { + addTestMySQLBackend(); + + ASSERT_NO_THROW( + config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1), + BackendSelector(BackendSelector::Type::UNSPEC)) + ); + + // We should be able to retrieve stored value without having to specify + // the backend. + EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs")); +} + +// Test that selector must be specified if there is more than one backend, +// when manipulating the data. +TEST_F(ConfigBackendMgrTest, createPropertyTwoBackendsUnspec) { + addTestBackends(); + + // Backend must be selected if there is more than one backend present. + EXPECT_THROW( + config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1), + BackendSelector(BackendSelector::Type::UNSPEC)), + NoSuchDatabase + ); +} + +// Test that exception is thrown when multiple backends are selected. +TEST_F(ConfigBackendMgrTest, createPropertyAmbiguousSelection) { + addTestBackends(); + + // Add another MySQL backend to cause the selection to give ambiguous + // result. + config_mgr_.addBackend("type=mysql"); + + EXPECT_THROW( + config_mgr_.getPool()->createProperty(std::make_pair("dogs", 1), + BackendSelector(BackendSelector::Type::MYSQL)), + AmbiguousDatabase + ); +} + +// Test that a single property can be retrieved from a selected backend. +TEST_F(ConfigBackendMgrTest, getSingleProperty) { + + addTestBackends(); + addTestData(); + + // Backend is not specified, so it should find the dogs in first one and + // cats in the second one. + EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs")); + EXPECT_EQ(2, config_mgr_.getPool()->getProperty("cats")); + + // No dogs in the pgsql backend and no cats in mysql backend. + EXPECT_EQ(0, config_mgr_.getPool()->getProperty("dogs", + BackendSelector(BackendSelector::Type::PGSQL))); + EXPECT_EQ(0, config_mgr_.getPool()->getProperty("cats", + BackendSelector(BackendSelector::Type::MYSQL))); + + // If the selectors are pointing to the right databases, the dogs and cats + // should be returned properly. + EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs", + BackendSelector(BackendSelector::Type::MYSQL))); + EXPECT_EQ(2, config_mgr_.getPool()->getProperty("cats", + BackendSelector(BackendSelector::Type::PGSQL))); + + // Also make sure that the variant of getProperty function taking two arguments + // would return the value. + EXPECT_EQ(1, config_mgr_.getPool()->getProperty("dogs", 1, + BackendSelector(BackendSelector::Type::MYSQL))); + + // If the value is not matching it should return 0. + EXPECT_EQ(0, config_mgr_.getPool()->getProperty("dogs", 2, + BackendSelector(BackendSelector::Type::MYSQL))); + + // Try to use the backend that is not present. + EXPECT_THROW(config_mgr_.getPool()->getProperty("cats", + BackendSelector(BackendSelector::Type::CQL)), + NoSuchDatabase); +} + +// Test that multiple properties can be retrieved with filtering. +TEST_F(ConfigBackendMgrTest, getMultipleProperties) { + + addTestBackends(); + addTestData(); + + // There is one dogs entry in mysql. + PropertiesList mysql_list = + config_mgr_.getPool()->getProperties("dogs", + BackendSelector(BackendSelector::Type::MYSQL)); + ASSERT_EQ(1, mysql_list.size()); + + // There is also one wolves entry in mysql. + mysql_list = config_mgr_.getPool()->getProperties("wolves", + BackendSelector(BackendSelector::Type::MYSQL)); + ASSERT_EQ(1, mysql_list.size()); + + // There are two cats entries in pgsql. + PropertiesList pgsql_list = + config_mgr_.getPool()->getProperties("cats", + BackendSelector(BackendSelector::Type::PGSQL)); + ASSERT_EQ(2, pgsql_list.size()); + + // Try to use the backend that is not present. + EXPECT_THROW(config_mgr_.getPool()->getProperties("cats", + BackendSelector(BackendSelector::Type::CQL)), + NoSuchDatabase); + +} + +// Test that all properties can be retrieved from each backend. +TEST_F(ConfigBackendMgrTest, getAllProperties) { + + addTestBackends(); + addTestData(); + + // The mysql backend holds two entries. + PropertiesList mysql_list = + config_mgr_.getPool()->getAllProperties(BackendSelector(BackendSelector::Type::MYSQL)); + ASSERT_EQ(2, mysql_list.size()); + + // The pgsql backends also holds two entries. + PropertiesList pgsql_list = + config_mgr_.getPool()->getAllProperties(BackendSelector(BackendSelector::Type::PGSQL)); + ASSERT_EQ(2, pgsql_list.size()); + + // Try to use the backend that is not present. + EXPECT_THROW(config_mgr_.getPool()->getProperties("cats", + BackendSelector(BackendSelector::Type::CQL)), + NoSuchDatabase); +} + +} + diff --git a/src/lib/config_backend/tests/run_unittests.cc b/src/lib/config_backend/tests/run_unittests.cc new file mode 100644 index 0000000000000000000000000000000000000000..6754d66eb20dab39be3f50352fd6c5b99ad7f4d9 --- /dev/null +++ b/src/lib/config_backend/tests/run_unittests.cc @@ -0,0 +1,19 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/lib/database/Makefile.am b/src/lib/database/Makefile.am index b39015f8db86238a0a639d23fc231e8ba4e8fb2e..ad8d1c6db8a81a91a665f2038da66561f87eaa19 100644 --- a/src/lib/database/Makefile.am +++ b/src/lib/database/Makefile.am @@ -23,10 +23,12 @@ EXTRA_DIST = db_messages.mes CLEANFILES = *.gcno *.gcda db_messages.h db_messages.cc s-messages lib_LTLIBRARIES = libkea-database.la -libkea_database_la_SOURCES = database_connection.cc database_connection.h +libkea_database_la_SOURCES = backend_selector.cc backend_selector.h +libkea_database_la_SOURCES += database_connection.cc database_connection.h libkea_database_la_SOURCES += dbaccess_parser.h dbaccess_parser.cc libkea_database_la_SOURCES += db_exceptions.h libkea_database_la_SOURCES += db_log.cc db_log.h +libkea_database_la_SOURCES += server_selector.h nodist_libkea_database_la_SOURCES = db_messages.cc db_messages.h @@ -43,6 +45,7 @@ libkea_database_la_LDFLAGS = -no-undefined -version-info 0:0:0 # Specify the headers for copying into the installation directory tree. libkea_database_includedir = $(pkgincludedir)/database libkea_database_include_HEADERS = \ + backend_selector.h \ database_connection.h \ db_exceptions.h \ db_log.h diff --git a/src/lib/database/backend_selector.cc b/src/lib/database/backend_selector.cc new file mode 100644 index 0000000000000000000000000000000000000000..c7431d2c2106b20948129ee8fcdc04656c9b2e2a --- /dev/null +++ b/src/lib/database/backend_selector.cc @@ -0,0 +1,152 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include + +using namespace isc::data; + +namespace isc { +namespace db { + +BackendSelector::BackendSelector() + : backend_type_(BackendSelector::Type::UNSPEC), + host_(), port_(0) { +} + +BackendSelector::BackendSelector(const Type& backend_type) + : backend_type_(backend_type), + host_(), port_(0) { +} + +BackendSelector::BackendSelector(const std::string& host, + const uint16_t port) + : backend_type_(BackendSelector::Type::UNSPEC), + host_(host), port_(port) { + validate(); +} + +BackendSelector::BackendSelector(const data::ConstElementPtr& access_map) + : backend_type_(BackendSelector::Type::UNSPEC), + host_(), port_(0) { + if (access_map->getType() != Element::map) { + isc_throw(BadValue, "database access information must be a map"); + } + + ConstElementPtr t = access_map->get("type"); + if (t) { + if (t->getType() != Element::string) { + isc_throw(BadValue, "'type' parameter must be a string"); + } + backend_type_ = stringToBackendType(t->stringValue()); + } + + ConstElementPtr h = access_map->get("host"); + if (h) { + if (h->getType() != Element::string) { + isc_throw(BadValue, "'host' parameter must be a string"); + } + host_ = h->stringValue(); + } + + ConstElementPtr p = access_map->get("port"); + if (p) { + if ((p->getType() != Element::integer) || + (p->intValue() < 0) || + (p->intValue() > std::numeric_limits::max())) { + isc_throw(BadValue, "'port' parameter must be a number in range from 0 " + "to " << std::numeric_limits::max()); + } + port_ = static_cast(p->intValue()); + } + + validate(); +} + +const BackendSelector& +BackendSelector::BackendSelector::UNSPEC() { + static BackendSelector selector; + return (selector); +} + +bool +BackendSelector::amUnspecified() const { + return ((backend_type_ == BackendSelector::Type::UNSPEC) && + (host_.empty()) && + (port_ == 0)); +} + +std::string +BackendSelector::toText() const { + std::ostringstream s; + if (amUnspecified()) { + s << "unspecified"; + + } else { + if (backend_type_ != BackendSelector::Type::UNSPEC) { + s << "type=" << backendTypeToString(backend_type_) << ","; + } + + if (!host_.empty()) { + s << "host=" << host_ << ","; + + if (port_ > 0) { + s << "port=" << port_ << ","; + } + } + } + + std::string text = s.str(); + if ((!text.empty() && (text.back() == ','))) { + text.pop_back(); + } + + return (text); +} + +BackendSelector::Type +BackendSelector::stringToBackendType(const std::string& type) { + if (type == "mysql") { + return (BackendSelector::Type::MYSQL); + + } else if (type == "pgsql") { + return (BackendSelector::Type::PGSQL); + + } else if (type == "cql") { + return (BackendSelector::Type::CQL); + + } else { + isc_throw(BadValue, "unsupported configuration backend type '" << type << "'"); + } +} + +std::string +BackendSelector::backendTypeToString(const BackendSelector::Type& type) { + switch (type) { + case BackendSelector::Type::MYSQL: + return ("mysql"); + case BackendSelector::Type::PGSQL: + return ("pgsql"); + case BackendSelector::Type::CQL: + return ("cql"); + default: + ; + } + + return (std::string()); +} + +void +BackendSelector::validate() const { + if ((port_ != 0) && (host_.empty())) { + isc_throw(BadValue, "'host' must be specified along with 'port' parameter"); + } +} + +} // end of namespace isc::db +} // end of namespace isc diff --git a/src/lib/database/backend_selector.h b/src/lib/database/backend_selector.h new file mode 100644 index 0000000000000000000000000000000000000000..7d256a037a6e827747c73bb2557a6ade19635578 --- /dev/null +++ b/src/lib/database/backend_selector.h @@ -0,0 +1,208 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BACKEND_SELECTOR_H +#define BACKEND_SELECTOR_H + +#include +#include +#include + +namespace isc { +namespace db { + +/// @brief Config Backend selector. +/// +/// Each Kea server using database as a configuration respository +/// may use multiple configuration backends simultaneously. The most +/// typical case is to use a single configuration backend per server, +/// but there are use cases when configuration information is distributed +/// accross multiple database instances. In the future, there may be +/// also caching mechanisms implemented, which will allow for storing +/// results of certain database queries in memory. +/// +/// From the server perspective, the most common use of the configuration +/// backend is to fetch entire configuration information from the +/// databases (upon startup) or fetch the latest updates to the +/// configuration, e.g. new subnet added, DHCP option modified etc. +/// In those cases, it is not so important from the server which backend +/// this data come from. Therefore, the server would fetch this information +/// from all available backends. +/// +/// When the server administrator wants to insert some new data into +/// the database, modify existing data or simply wants to check the +/// contents of one of the database instance, he would specify which +/// database backend he wants to direct queries to. +/// +/// The @c BackendSelector class provides means to specify whether +/// the queries should be directed to any backend (see server case +/// above) or to a specific backend (data insertion case above). +/// In addition, the @c BackendSelector allows for using various +/// criteria for selecting a backend to use. Currently those criteria +/// are: database type (e.g. mysql), database host and database port. +/// In order to use a specific port, the database host must also be +/// specified. Note that in a general case multiple backends of the +/// same type can be used simultaneously, e.g. multiple MySQL backends. +/// In that case, it may be necessary to specify host (and port) to +/// issue a query to the right one. +/// +/// The @c BackendSelector class may be extended in the future to provide +/// additional backend selection criteria. +class BackendSelector { +public: + + /// @brief Supported database types. + /// + /// The @c UNSPEC indicates that the database type is not specified + /// as selection criteria. + enum class Type { + MYSQL, + PGSQL, + CQL, + UNSPEC + }; + + /// @brief Default constructor. + /// + /// It sets the selector to "unspecified". When this selector is used + /// the backend pool will use "any" backend. This has a different meaning + /// for each type of query. See the @c BaseConfigBackendPool for details. + explicit BackendSelector(); + + /// @brief Constructor specifying backend type as a selection criteria. + /// + /// @param backend_type Type of the backend to be selected. + explicit BackendSelector(const Type& backend_type); + + /// @brief Constructor for specifying host and optionally port as a + /// selection criteria. + /// + /// @param host Hostname to be used for selecting a backend. + /// @param port Port number to be used for selecting a backend. This value + /// is optional and is ignored when set to 0. It must be used on conjuction + /// with hostname. + explicit BackendSelector(const std::string& host, const uint16_t port = 0); + + /// @brief Constructor for selecting a backend using JSON access map. + /// + /// The provided access map must have the same structure as an element + /// of the "config-databases" configuration parameter. However, it merely + /// takes into account: "type", "host" and "port" parameters. In addition, + /// these parameters are optional. The following are valid combinations: + /// + /// @code + /// { + /// "type": "mysql" + /// } + /// @endcode + /// + /// @code + /// { + /// "host": "somehost.example.org" + /// } + /// @endcode + /// + /// @code + /// { + /// "host": "somehost.example.org", + /// "port": 1234 + /// } + /// @endcode + /// + /// @code + /// { + /// "type": "mysql" + /// "host": "somehost.example.org", + /// } + /// @endcode + /// + /// @code + /// { + /// "type": "mysql" + /// "host": "somehost.example.org", + /// "port": 1234 + /// } + /// @endcode + /// + /// where "type" can be any of the supported backend types. + /// + /// This constructor is useful for creating backend selectors from the + /// received control commands. + /// + /// @param access_map Access map as provided above. + explicit BackendSelector(const data::ConstElementPtr& access_map); + + /// @brief Returns instance of the "unspecified" backend selector. + static const BackendSelector& UNSPEC(); + + /// @brief Checks if selector is "unspecified". + /// + /// @return true if backend type is @c UNSPEC, hostname is empty and + /// port number 0, false otherwise. + bool amUnspecified() const; + + /// @brief Returns backend type selected. + Type getBackendType() const { + return (backend_type_); + } + + /// @brief Returns host selected. + /// + /// @return host if specified or empty string if host is not + /// specified. + std::string getBackendHost() const { + return (host_); + } + + /// @brief Returns port selected. + /// + /// @return port number of the selected backend or 0 if port number + /// is not specified. + uint16_t getBackendPort() const { + return (port_); + } + + /// @brief Returns selections as text. + /// + /// @return Collection of comma separated selections, e.g. + /// "type=mysql,host=somehost.example.org,port=1234". + std::string toText() const; + + /// @brief Converts string to backend type. + /// + /// @param type Backend type as string. + static Type stringToBackendType(const std::string& type); + + /// @brief Converts backend type to string. + /// + /// @param type Backend type to be converted. + static std::string backendTypeToString(const Type& type); + + +private: + + /// @brief Checks if the specified selector is valid. + /// + /// It checks if the port number is specified in conjuction with + /// host. + /// @throw BadValue if selector validation fails. + void validate() const; + + /// @brief Backend type selected. + Type backend_type_; + + /// @brief Host selected. + std::string host_; + + /// @brief Port number selected. + uint16_t port_; +}; + + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/database/db_exceptions.h b/src/lib/database/db_exceptions.h index 60e10900d9057e57bb3867e44cbc427b66f4a2fc..21dd8fa5a6dc7ff0fdedbb523a8119b435453b1c 100644 --- a/src/lib/database/db_exceptions.h +++ b/src/lib/database/db_exceptions.h @@ -74,6 +74,22 @@ public: isc::Exception(file, line, what) {} }; +/// @brief Error when specified database could not be found in the server +/// configuration. +class NoSuchDatabase : public Exception { +public: + NoSuchDatabase(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Specification of the database backend to be used yields +/// multiple results. +class AmbiguousDatabase : public Exception { +public: + AmbiguousDatabase(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + } // namespace isc } // namespace db diff --git a/src/lib/database/server_selector.h b/src/lib/database/server_selector.h new file mode 100644 index 0000000000000000000000000000000000000000..ed05ce9811cc508502f130a8c518bedde86a4ee3 --- /dev/null +++ b/src/lib/database/server_selector.h @@ -0,0 +1,126 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef SERVER_SELECTOR_H +#define SERVER_SELECTOR_H + +#include +#include + +namespace isc { +namespace db { + +/// @brief Server selector for associating objects in a database with +/// specific servers. +/// +/// Configuration information stored in the configuration backends can be +/// associated with selected servers, all servers or no particular server. +/// For example: a particular subnet definition in the database may be +/// associated with one server or can be shared by multiple servers. +/// In the latter case, a subnet may be associated with a subset of +/// servers or all servers. An administrator may also add the +/// configuration data into the database and do not associate this data +/// with any patrticular server. +/// +/// When fetching the configuration data from a databse or when storing +/// data in the database there is a need to specify which servers this +/// data is associated with. The @c ServerSelector class represents +/// such associations. +/// +/// It includes three modes of selection: UNASSIGNED, ALL and SUBSET and +/// several factory functions making associations described above. +/// +/// The @c ServerSelector class should be used in objects derived from +/// @c BaseConfigBackendPool and in objects derived from +/// @c BaseConfigBackend to indicate which servers the specific calls +/// exposed by these objects refer to. +class ServerSelector { +public: + + /// @brief Type of the server selection. + enum class Type { + UNASSIGNED, + ALL, + SUBSET + }; + + /// @brief Factory returning "unassigned" server selector. + static ServerSelector& UNASSIGNED() { + static ServerSelector selector(Type::UNASSIGNED); + return (selector); + } + + /// @brief Factory returning "all servers" selector. + static ServerSelector& ALL() { + static ServerSelector selector(Type::ALL); + return (selector); + } + + /// @brief Factory returning selector of one server. + /// + /// @param server_tag tag of the single server to be selected. + static ServerSelector& ONE(const std::string& server_tag) { + static ServerSelector selector(server_tag); + return (selector); + } + + /// @brief Factory returning "multiple servers" selector. + /// + /// @param server_tags set of server tags to be selected. + static ServerSelector& MULTIPLE(const std::set& server_tags) { + static ServerSelector selector(server_tags); + return (selector); + } + + /// @brief Returns type of the selector. + Type getType() const { + return (type_); + } + + /// @brief Returns tags associated with the selector. + /// + /// @return server tags for mutliple selections and for one server, + /// empty set for all servers and and unassigned. + std::set getTags() const { + return (tags_); + } + +private: + + /// @brief Constructor used for "unassigned" and "all" slection types. + /// + /// @param type selector type. + explicit ServerSelector(const Type& type) + : type_(type), tags_() { + } + + /// @brief Constructor used for selecting a single server. + /// + /// @param server_tag tag of the server to be selected. + explicit ServerSelector(const std::string& server_tag) + : type_(Type::SUBSET), tags_() { + tags_.insert(server_tag); + } + + /// @brief Constructor used for selecting multiple servers. + /// + /// @param server_tags set of server tags. + explicit ServerSelector(const std::set& server_tags) + : type_(Type::SUBSET), tags_(server_tags) { + } + + /// @brief Selection type used. + Type type_; + + /// @brief Holds tags of explicitly selected servers. + std::set tags_; +}; + + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/database/tests/Makefile.am b/src/lib/database/tests/Makefile.am index 7a0b74d984f40f0c9a8aeae41152d0ca64aba423..4e1609d18f7342c6618da130e17399ab1894ed6b 100644 --- a/src/lib/database/tests/Makefile.am +++ b/src/lib/database/tests/Makefile.am @@ -19,9 +19,11 @@ TESTS = if HAVE_GTEST TESTS += libdatabase_unittests -libdatabase_unittests_SOURCES = database_connection_unittest.cc +libdatabase_unittests_SOURCES = backend_selector_unittest.cc +libdatabase_unittests_SOURCES += database_connection_unittest.cc libdatabase_unittests_SOURCES += dbaccess_parser_unittest.cc libdatabase_unittests_SOURCES += run_unittests.cc +libdatabase_unittests_SOURCES += server_selector_unittest.cc libdatabase_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) libdatabase_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/database/tests/backend_selector_unittest.cc b/src/lib/database/tests/backend_selector_unittest.cc new file mode 100644 index 0000000000000000000000000000000000000000..3665d50972f916616754a00569658e9d7af86076 --- /dev/null +++ b/src/lib/database/tests/backend_selector_unittest.cc @@ -0,0 +1,174 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#include +#include +#include +#include + +using namespace isc; +using namespace isc::db; +using namespace isc::data; + +namespace { + +// Verifies defaults of the backend selector. +TEST(BackendSelectorTest, defaults) { + BackendSelector sel; + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel.getBackendType()); + EXPECT_TRUE(sel.getBackendHost().empty()); + EXPECT_EQ(0, sel.getBackendPort()); + EXPECT_TRUE(sel.amUnspecified()); + EXPECT_EQ("unspecified", sel.toText()); +} + +// Verifies that the backend selector can be set to "unspecified". +TEST(BackendSelectorTest, unspec) { + BackendSelector sel = BackendSelector::UNSPEC(); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel.getBackendType()); + EXPECT_TRUE(sel.getBackendHost().empty()); + EXPECT_EQ(0, sel.getBackendPort()); + EXPECT_TRUE(sel.amUnspecified()); + EXPECT_EQ("unspecified", sel.toText()); +} + +// Verifies that it is possible to select backend by type. +TEST(BackendSelectorTest, backendTypeSpec) { + boost::scoped_ptr sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(BackendSelector::Type::MYSQL)) + ); + EXPECT_EQ(BackendSelector::Type::MYSQL, sel->getBackendType()); + EXPECT_TRUE(sel->getBackendHost().empty()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("type=mysql", sel->toText()); +} + +// Verifies that backend can be selected by host and port. +TEST(BackendSelectorTest, backendHostPortSpec) { + boost::scoped_ptr sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector("myhost", 1234)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("myhost", sel->getBackendHost()); + EXPECT_EQ(1234, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=myhost,port=1234", sel->toText()); +} + +// Verifies that backend can be selected by host. +TEST(BackendSelectorTest, backendHostSpec) { + boost::scoped_ptr sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector("otherhost")) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("otherhost", sel->getBackendHost()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=otherhost", sel->toText()); +} + +// Verifies that backend becomes unspecified if the access +// map is empty. +TEST(BackendSelectorTest, accessMapTypeUnSpec) { + ElementPtr access_map = Element::createMap(); + boost::scoped_ptr sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_TRUE(sel->getBackendHost().empty()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_TRUE(sel->amUnspecified()); + EXPECT_EQ("unspecified", sel->toText()); +} + +// Verifies that backend can be selected by type using access map. +TEST(BackendSelectorTest, accessMapTypeSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("type", Element::create("mysql")); + boost::scoped_ptr sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::MYSQL, sel->getBackendType()); + EXPECT_TRUE(sel->getBackendHost().empty()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("type=mysql", sel->toText()); +} + +// Verifies that backend can be selected by host and port using +// access map. +TEST(BackendSelectorTest, accessMapHostPortSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("host", Element::create("myhost")); + access_map->set("port", Element::create(int64_t(1234))); + boost::scoped_ptr sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("myhost", sel->getBackendHost()); + EXPECT_EQ(1234, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=myhost,port=1234", sel->toText()); +} + +// Verifies that the backend can be selected by host using access +// map. +TEST(BackendSelectorTest, accessMapHostSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("host", Element::create("myhost")); + boost::scoped_ptr sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("myhost", sel->getBackendHost()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=myhost", sel->toText()); +} + +// Verifies that selecting backend by port only is not possible. +TEST(BackendSelectorTest, accessMapPortSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("port", Element::create(int64_t(1234))); + boost::scoped_ptr sel; + EXPECT_THROW(sel.reset(new BackendSelector(access_map)), + BadValue); +} + +// Tests conversions of strings to backend types. +TEST(BackendSelectorTest, stringToBackendType) { + EXPECT_EQ(BackendSelector::Type::MYSQL, + BackendSelector::stringToBackendType("mysql")); + EXPECT_EQ(BackendSelector::Type::PGSQL, + BackendSelector::stringToBackendType("pgsql")); + EXPECT_EQ(BackendSelector::Type::CQL, + BackendSelector::stringToBackendType("cql")); + EXPECT_THROW(BackendSelector::stringToBackendType("unsupported"), + BadValue); +} + +// Tests conversions of backend types to strings. +TEST(BackendSelectorTest, backendTypeToString) { + EXPECT_EQ("mysql", + BackendSelector::backendTypeToString(BackendSelector::Type::MYSQL)); + EXPECT_EQ("pgsql", + BackendSelector::backendTypeToString(BackendSelector::Type::PGSQL)); + EXPECT_EQ("cql", + BackendSelector::backendTypeToString(BackendSelector::Type::CQL)); +} + + +} + diff --git a/src/lib/database/tests/server_selector_unittest.cc b/src/lib/database/tests/server_selector_unittest.cc new file mode 100644 index 0000000000000000000000000000000000000000..1f2ab09c0349f62da55fe84e582aca865b7d2d5a --- /dev/null +++ b/src/lib/database/tests/server_selector_unittest.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include + +using namespace isc::db; + +namespace { + +// Check that server selector can be set to UNASSIGNED. +TEST(ServerSelectorTest, unassigned) { + ServerSelector selector = ServerSelector::UNASSIGNED(); + EXPECT_EQ(ServerSelector::Type::UNASSIGNED, selector.getType()); + EXPECT_TRUE(selector.getTags().empty()); +} + +// Check that server selector can be set to ALL. +TEST(ServerSelectorTest, all) { + ServerSelector selector = ServerSelector::ALL(); + EXPECT_EQ(ServerSelector::Type::ALL, selector.getType()); + EXPECT_TRUE(selector.getTags().empty()); +} + +// Check that a single server can be selected. +TEST(ServerSelectorTest, one) { + ServerSelector selector = ServerSelector::ONE("some-tag"); + EXPECT_EQ(ServerSelector::Type::SUBSET, selector.getType()); + + std::set tags = selector.getTags(); + ASSERT_EQ(1, tags.size()); + EXPECT_EQ(1, tags.count("some-tag")); +} + +// Check that multiple servers can be selected. +TEST(ServerSelectorTest, multiple) { + ServerSelector selector = ServerSelector::MULTIPLE({ "tag1", "tag2", "tag3" }); + EXPECT_EQ(ServerSelector::Type::SUBSET, selector.getType()); + + std::set tags = selector.getTags(); + ASSERT_EQ(3, tags.size()); + EXPECT_EQ(1, tags.count("tag1")); + EXPECT_EQ(1, tags.count("tag2")); + EXPECT_EQ(1, tags.count("tag3")); +} + +} diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 3243f20377353baab8dc2cb25a7b35027a5c6a28..23f83d48fdb7ada1d9d9835b7b500dae69c0a426 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -108,6 +108,8 @@ libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h libkea_dhcpsrv_la_SOURCES += client_class_def.cc client_class_def.h +libkea_dhcpsrv_la_SOURCES += config_backend_dhcp4.h +libkea_dhcpsrv_la_SOURCES += config_backend_pool_dhcp4.cc config_backend_pool_dhcp4.h libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h @@ -270,6 +272,8 @@ libkea_dhcpsrv_include_HEADERS = \ cfg_subnets6.h \ cfgmgr.h \ client_class_def.h \ + config_backend_dhcp4.h \ + config_backend_pool_dhcp4.h \ csv_lease_file4.h \ csv_lease_file6.h \ dhcpsrv_exceptions.h \ diff --git a/src/lib/dhcpsrv/config_backend_dhcp4.h b/src/lib/dhcpsrv/config_backend_dhcp4.h new file mode 100644 index 0000000000000000000000000000000000000000..c6324846ce3fd0fdda93685d5a54fccc7f8e8633 --- /dev/null +++ b/src/lib/dhcpsrv/config_backend_dhcp4.h @@ -0,0 +1,323 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CONFIG_BACKEND_DHCP4_H +#define CONFIG_BACKEND_DHCP4_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Interface implemented by DHCPv4 configuration backends. +class ConfigBackendDHCPv4 : public cb::BaseConfigBackend { +public: + + /// @brief Virtual destructor. + virtual ~ConfigBackendDHCPv4() { } + + /// @brief Retrieves a single subnet by subnet_prefix. + /// + /// @param selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + virtual Subnet4Ptr + getSubnet4(const db::ServerSelector& selector, + const std::string& subnet_prefix) const = 0; + + /// @brief Retrieves a single subnet by subnet identifier. + /// + /// @param selector Server selector. + /// @param subnet_id Identifier of a subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + virtual Subnet4Ptr + getSubnet4(const db::ServerSelector& selector, const SubnetID& subnet_id) const = 0; + + /// @brief Retrieves all subnets. + /// + /// @param selector Server selector. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet4Collection + getAllSubnets4(const db::ServerSelector& selector) const = 0; + + /// @brief Retrieves subnets modified after specified time. + /// + /// @param selector Server selector. + /// @param modification_time Lower bound subnet modification time. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet4Collection + getModifiedSubnets4(const db::ServerSelector& selector, + const boost::posix_time::ptime& modification_time) const = 0; + + /// @brief Retrieves shared network by name. + /// + /// @param selector Server selector. + /// @param name Name of the shared network to be retrieved. + /// @return Pointer to the shared network or NULL if not found. + virtual SharedNetwork4Ptr + getSharedNetwork4(const db::ServerSelector& selector, + const std::string& name) const = 0; + + /// @brief Retrieves all shared networks. + /// + /// @param selector Server selector. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork4Collection + getAllSharedNetworks4(const db::ServerSelector& selector) const = 0; + + /// @brief Retrieves shared networks modified after specified time. + /// + /// @param selector Server selector. + /// @param modification_time Lower bound shared network modification time. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork4Collection + getModifiedSharedNetworks4(const db::ServerSelector& selector, + const boost::posix_time::ptime& modification_time) const = 0; + + /// @brief Retrieves single option definition by code and space. + /// + /// @param selector Server selector. + /// @param code Code of the option to be retrieved. + /// @param space Option space of the option to be retrieved. + /// @return Pointer to the option definition or NULL if not found. + virtual OptionDefinitionPtr + getOptionDef4(const db::ServerSelector& selector, const uint16_t code, + const std::string& space) const = 0; + + /// @brief Retrieves all option definitions. + /// + /// @param selector Server selector. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getAllOptionDefs4(const db::ServerSelector& selector) const = 0; + + /// @brief Retrieves option definitions modified after specified time. + /// + /// @param selector Server selector. + /// @param modification_time Lower bound option definition modification + /// time. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getModifiedOptionDefs4(const db::ServerSelector& selector, + const boost::posix_time::ptime& modification_time) const = 0; + + /// @brief Retrieves global string parameter value. + /// + /// @param selector Server selector. + /// @param name Name of the global parameter to be retrieved. + /// @return Value of the global string parameter. + virtual util::OptionalValue + getGlobalStringParameter4(const db::ServerSelector& selector, + const std::string& name) const = 0; + + /// @brief Retrieves global number parameter. + /// + /// @param selector Server selector. + /// @param name Name of the parameter to be retrieved. + virtual util::OptionalValue + getGlobalNumberParameter4(const db::ServerSelector& selector, + const std::string& name) const = 0; + + /// @brief Retrieves all global parameters as strings. + /// + /// @param selector Server selector. + virtual std::map + getAllGlobalParameters4(const db::ServerSelector& selector) const = 0; + + /// @brief Creates or updates a subnet. + /// + /// @param selector Server selector. + /// @param subnet Subnet to be added or updated. + virtual void + createUpdateSubnet4(const db::ServerSelector& selector, + const Subnet4Ptr& subnet) = 0; + + /// @brief Creates or updates a shared network. + /// + /// @param selector Server selector. + /// @param shared_network Shared network to be added or updated. + virtual void + createUpdateSharedNetwork4(const db::ServerSelector& selector, + const SharedNetwork4Ptr& shared_network) = 0; + + /// @brief Creates or updates an option definition. + /// + /// @param selector Server selector. + /// @param option_def Option definition to be added or updated. + virtual void + createUpdateOptionDef4(const db::ServerSelector& selector, + const OptionDefinitionPtr& option_def) = 0; + + /// @brief Creates or updates global option. + /// + /// @param selector Server selector. + /// @param option Option to be added or updated. + virtual void + createUpdateOption4(const db::ServerSelector& selector, + const OptionPtr& option) = 0; + + /// @brief Creates or updates subnet level option. + /// + /// @param selector Server selector. + /// @param subnet_id Identifier of a subnet to which option belongs. + /// @param option Option to be added or updated. + virtual void + createUpdateOption4(const db::ServerSelector& selector, + const SubnetID& subnet_id, + const OptionPtr& option) = 0; + + /// @brief Creates or updates pool level option. + /// + /// @param selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// the option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// option belongs. + /// @param option Option to be added or updated. + virtual void + createUpdateOption4(const db::ServerSelector& selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const OptionPtr& option) = 0; + + /// @brief Creates or updates global string parameter. + /// + /// @param selector Server selector. + /// @param name Name of the global parameter. + /// @param value Value of the global parameter. + virtual void + createUpdateGlobalParameter4(const db::ServerSelector& selector, + const std::string& name, + const std::string& value) = 0; + + /// @brief Creates or updates global number parameter. + /// + /// @param selector Server selector. + /// @param name Name of the global parameter. + /// @param value Value of the global parameter. + virtual void + createUpdateGlobalParameter4(const db::ServerSelector& selector, + const std::string& name, + const int64_t value) = 0; + + /// @brief Deletes subnet by prefix. + /// + /// @param selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be deleted. + virtual void + deleteSubnet4(const db::ServerSelector& selector, + const std::string& subnet_prefix) = 0; + + /// @brief Deletes subnet by identifier. + /// + /// @param selector Server selector. + /// @param subnet_id Identifier of the subnet to be deleted. + virtual void + deleteSubnet4(const db::ServerSelector& selector, const SubnetID& subnet_id) = 0; + + /// @brief Deletes all subnets. + /// + /// @param selector Server selector. + virtual void + deleteAllSubnets4(const db::ServerSelector& selector) = 0; + + /// @brief Deletes shared network by name. + /// + /// @param selector Server selector. + /// @param name Name of the shared network to be deleted. + virtual void + deleteSharedNetwork4(const db::ServerSelector& selector, + const std::string& name) = 0; + + /// @brief Deletes all shared networks. + /// + /// @param selector Server selector. + virtual void + deleteAllSharedNetworks4(const db::ServerSelector& selector) = 0; + + /// @brief Deletes option definition. + /// + /// @param selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + virtual void + deleteOptionDef4(const db::ServerSelector& selector, const uint16_t code, + const std::string& space) = 0; + + /// @brief Deletes all option definitions. + /// + /// @param selector Server selector. + virtual void + deleteAllOptionDefs4(const db::ServerSelector& selector) = 0; + + /// @brief Deletes global option. + /// + /// @param selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + virtual void + deleteOption4(const db::ServerSelector& selector, const uint16_t code, + const std::string& space) = 0; + + /// @brief Deletes subnet level option. + /// + /// @param 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. + virtual void + deleteOption4(const db::ServerSelector& selector, const SubnetID& subnet_id, + const uint16_t code, const std::string& space) = 0; + + /// @brief Deletes pool level option. + /// + /// @param selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// deleted option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// deleted option belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + virtual void + deleteOption4(const db::ServerSelector& selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const uint16_t code, + const std::string& space) = 0; + + /// @brief Deletes global parameter. + /// + /// @param selector Server selector. + /// @param name Name of the global parameter to be deleted. + virtual void + deleteGlobalParameter4(const db::ServerSelector& selector, + const std::string& name) = 0; + + /// @brief Deletes all global parameters. + /// + /// @param selector Server selector. + virtual void + deleteAllGlobalParameters4(const db::ServerSelector& selector) = 0; +}; + +} // end of namespace isc::dhcp +} // end of namespace isc + +#endif // CONFIG_BACKEND_DHCP4_H diff --git a/src/lib/dhcpsrv/config_backend_pool_dhcp4.cc b/src/lib/dhcpsrv/config_backend_pool_dhcp4.cc new file mode 100644 index 0000000000000000000000000000000000000000..d3782737814b4ef5144c56d03e7f7164a6a8c945 --- /dev/null +++ b/src/lib/dhcpsrv/config_backend_pool_dhcp4.cc @@ -0,0 +1,346 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include + +using namespace isc::asiolink; +using namespace isc::db; +using namespace isc::util; + +namespace isc { +namespace dhcp { + +Subnet4Ptr +ConfigBackendPoolDHCPv4::getSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& subnet_prefix) const { + Subnet4Ptr subnet; + getPropertyPtrConst + (&ConfigBackendDHCPv4::getSubnet4, backend_selector, server_selector, + subnet, subnet_prefix); + return (subnet); +} + +Subnet4Ptr +ConfigBackendPoolDHCPv4::getSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id) const { + Subnet4Ptr subnet; + getPropertyPtrConst + (&ConfigBackendDHCPv4::getSubnet4, backend_selector, server_selector, + subnet, subnet_id); + return (subnet); +} + +Subnet4Collection +ConfigBackendPoolDHCPv4::getAllSubnets4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const { + Subnet4Collection subnets; + getAllPropertiesConst + (&ConfigBackendDHCPv4::getAllSubnets4, backend_selector, server_selector, + subnets); + return (subnets); +} + +Subnet4Collection +ConfigBackendPoolDHCPv4::getModifiedSubnets4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + Subnet4Collection subnets; + getMultiplePropertiesConst + (&ConfigBackendDHCPv4::getModifiedSubnets4, backend_selector, server_selector, + subnets, modification_time); + return (subnets); +} + +SharedNetwork4Ptr +ConfigBackendPoolDHCPv4::getSharedNetwork4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) const { + SharedNetwork4Ptr shared_network; + getPropertyPtrConst + (&ConfigBackendDHCPv4::getSharedNetwork4, backend_selector, server_selector, + shared_network, name); + return (shared_network); +} + +SharedNetwork4Collection +ConfigBackendPoolDHCPv4::getAllSharedNetworks4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const { + SharedNetwork4Collection shared_networks; + getAllPropertiesConst + (&ConfigBackendDHCPv4::getAllSharedNetworks4, backend_selector, server_selector, + shared_networks); + return (shared_networks); +} + +SharedNetwork4Collection +ConfigBackendPoolDHCPv4:: +getModifiedSharedNetworks4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + SharedNetwork4Collection shared_networks; + getMultiplePropertiesConst + (&ConfigBackendDHCPv4::getModifiedSharedNetworks4, backend_selector, server_selector, + shared_networks, modification_time); + return (shared_networks); +} + +OptionDefinitionPtr +ConfigBackendPoolDHCPv4::getOptionDef4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space) const { + OptionDefinitionPtr option_def; + getPropertyPtrConst + (&ConfigBackendDHCPv4::getOptionDef4, backend_selector, server_selector, + option_def, code, space); + return (option_def); +} + +OptionDefContainer +ConfigBackendPoolDHCPv4::getAllOptionDefs4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const { + OptionDefContainer option_defs; + getAllPropertiesConst + (&ConfigBackendDHCPv4::getAllOptionDefs4, backend_selector, server_selector, + option_defs); + return (option_defs); +} + +OptionDefContainer +ConfigBackendPoolDHCPv4::getModifiedOptionDefs4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const { + OptionDefContainer option_defs; + getMultiplePropertiesConst + (&ConfigBackendDHCPv4::getModifiedOptionDefs4, backend_selector, server_selector, + option_defs, modification_time); + return (option_defs); +} + +util::OptionalValue +ConfigBackendPoolDHCPv4::getGlobalStringParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) const { + OptionalValue parameter; + getOptionalPropertyConst + (&ConfigBackendDHCPv4::getGlobalStringParameter4, backend_selector, + server_selector, parameter, name); + return (parameter); +} + +util::OptionalValue +ConfigBackendPoolDHCPv4::getGlobalNumberParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) const { + OptionalValue parameter; + getOptionalPropertyConst + (&ConfigBackendDHCPv4::getGlobalNumberParameter4, backend_selector, + server_selector, parameter, name); + return (parameter); +} + +std::map +ConfigBackendPoolDHCPv4::getAllGlobalParameters4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const { + std::map parameters; + getAllPropertiesConst > + (&ConfigBackendDHCPv4::getAllGlobalParameters4, backend_selector, + server_selector, parameters); + return (parameters); +} + +void +ConfigBackendPoolDHCPv4::createUpdateSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const Subnet4Ptr& subnet) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateSubnet4, backend_selector, + server_selector, subnet); +} + +void +ConfigBackendPoolDHCPv4::createUpdateSharedNetwork4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SharedNetwork4Ptr& shared_network) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateSharedNetwork4, backend_selector, + server_selector, shared_network); +} + +void +ConfigBackendPoolDHCPv4::createUpdateOptionDef4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const OptionDefinitionPtr& option_def) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateOptionDef4, backend_selector, + server_selector, option_def); +} + +void +ConfigBackendPoolDHCPv4::createUpdateOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const OptionPtr& option) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateOption4, backend_selector, + server_selector, option); +} + +void +ConfigBackendPoolDHCPv4::createUpdateOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id, + const OptionPtr& option) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateOption4, backend_selector, + server_selector, subnet_id, option); +} + +void +ConfigBackendPoolDHCPv4::createUpdateOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const IOAddress& pool_start_address, + const IOAddress& pool_end_address, + const OptionPtr& option) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateOption4, backend_selector, + server_selector, pool_start_address, pool_end_address, option); +} + +void +ConfigBackendPoolDHCPv4::createUpdateGlobalParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name, + const std::string& value) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateGlobalParameter4, backend_selector, + server_selector, name, value); +} + +void +ConfigBackendPoolDHCPv4::createUpdateGlobalParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name, + const int64_t value) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::createUpdateGlobalParameter4, backend_selector, + server_selector, name, value); +} + +void +ConfigBackendPoolDHCPv4::deleteSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& subnet_prefix) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteSubnet4, backend_selector, server_selector, + subnet_prefix); +} + +void +ConfigBackendPoolDHCPv4::deleteSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteSubnet4, backend_selector, server_selector, + subnet_id); +} + +void +ConfigBackendPoolDHCPv4::deleteAllSubnets4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) { + createUpdateDeleteProperty<> + (&ConfigBackendDHCPv4::deleteAllSubnets4, backend_selector, server_selector); +} + +void +ConfigBackendPoolDHCPv4::deleteSharedNetwork4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteSharedNetwork4, backend_selector, + server_selector, name); +} + +void +ConfigBackendPoolDHCPv4::deleteAllSharedNetworks4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) { + createUpdateDeleteProperty<> + (&ConfigBackendDHCPv4::deleteAllSharedNetworks4, backend_selector, server_selector); +} + +void +ConfigBackendPoolDHCPv4::deleteOptionDef4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteOptionDef4, backend_selector, + server_selector, code, space); +} + +void +ConfigBackendPoolDHCPv4::deleteAllOptionDefs4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) { + createUpdateDeleteProperty<> + (&ConfigBackendDHCPv4::deleteAllOptionDefs4, backend_selector, server_selector); +} + +void +ConfigBackendPoolDHCPv4::deleteOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector, + code, space); +} + +void +ConfigBackendPoolDHCPv4::deleteOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id, + const uint16_t code, + const std::string& space) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector, + subnet_id, code, space); +} + +void +ConfigBackendPoolDHCPv4::deleteOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const uint16_t code, + const std::string& space) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteOption4, backend_selector, server_selector, + pool_start_address, pool_end_address, code, space); +} + +void +ConfigBackendPoolDHCPv4::deleteGlobalParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) { + createUpdateDeleteProperty + (&ConfigBackendDHCPv4::deleteGlobalParameter4, backend_selector, + server_selector, name); +} + +void +ConfigBackendPoolDHCPv4::deleteAllGlobalParameters4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) { + createUpdateDeleteProperty<> + (&ConfigBackendDHCPv4::deleteAllGlobalParameters4, backend_selector, + server_selector); +} + + +} // end of namespace isc::dhcp +} // end of namespace isc diff --git a/src/lib/dhcpsrv/config_backend_pool_dhcp4.h b/src/lib/dhcpsrv/config_backend_pool_dhcp4.h new file mode 100644 index 0000000000000000000000000000000000000000..271e5cc462081d59b630380ee147c81033423f70 --- /dev/null +++ b/src/lib/dhcpsrv/config_backend_pool_dhcp4.h @@ -0,0 +1,394 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CONFIG_BACKEND_POOL_DHCP4_H +#define CONFIG_BACKEND_POOL_DHCP4_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Implementation of the Configuration Backend Pool for DHCPv4. +class ConfigBackendPoolDHCPv4 : cb::BaseConfigBackendPool { +public: + + /// @brief Retrieves a single subnet by subnet_prefix. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + virtual Subnet4Ptr + getSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& subnet_prefix) const; + + /// @brief Retrieves a single subnet by subnet identifier. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param subnet_id Identifier of a subnet to be retrieved. + /// @return Pointer to the retrieved subnet or NULL if not found. + virtual Subnet4Ptr + getSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id) const; + + /// @brief Retrieves all subnets. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet4Collection + getAllSubnets4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const; + + /// @brief Retrieves subnets modified after specified time. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param modification_time Lower bound subnet modification time. + /// @return Collection of subnets or empty collection if no subnet found. + virtual Subnet4Collection + getModifiedSubnets4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves shared network by name. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param name Name of the shared network to be retrieved. + /// @return Pointer to the shared network or NULL if not found. + virtual SharedNetwork4Ptr + getSharedNetwork4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) const; + + /// @brief Retrieves all shared networks. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork4Collection + getAllSharedNetworks4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const; + + /// @brief Retrieves shared networks modified after specified time. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param modification_time Lower bound shared network modification time. + /// @return Collection of shared network or empty collection if + /// no shared network found. + virtual SharedNetwork4Collection + getModifiedSharedNetworks4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves single option definition by code and space. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param code Code of the option to be retrieved. + /// @param space Option space of the option to be retrieved. + /// @return Pointer to the option definition or NULL if not found. + virtual OptionDefinitionPtr + getOptionDef4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space) const; + + /// @brief Retrieves all option definitions. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getAllOptionDefs4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const; + + /// @brief Retrieves option definitions modified after specified time. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param modification_time Lower bound option definition modification + /// time. + /// @return Collection of option definitions or empty collection if + /// no option definition found. + virtual OptionDefContainer + getModifiedOptionDefs4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time) const; + + /// @brief Retrieves global string parameter value. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param name Name of the global parameter to be retrieved. + /// @return Value of the global string parameter. + virtual util::OptionalValue + getGlobalStringParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) const; + + /// @brief Retrieves global number parameter. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param name Name of the parameter to be retrieved. + virtual util::OptionalValue + getGlobalNumberParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name) const; + + /// @brief Retrieves all global parameters as strings. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + virtual std::map + getAllGlobalParameters4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector) const; + + /// @brief Creates or updates a subnet. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param subnet Subnet to be added or updated. + virtual void + createUpdateSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const Subnet4Ptr& subnet); + + /// @brief Creates or updates a shared network. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param shared_network Shared network to be added or updated. + virtual void + createUpdateSharedNetwork4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SharedNetwork4Ptr& shared_network); + + /// @brief Creates or updates an option definition. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param option_def Option definition to be added or updated. + virtual void + createUpdateOptionDef4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const OptionDefinitionPtr& option_def); + + /// @brief Creates or updates global option. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param option Option to be added or updated. + virtual void + createUpdateOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const OptionPtr& option); + + /// @brief Creates or updates subnet level option. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param subnet_id Identifier of a subnet to which option belongs. + /// @param option Option to be added or updated. + virtual void + createUpdateOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id, + const OptionPtr& option); + + /// @brief Creates or updates pool level option. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// the option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// option belongs. + /// @param option Option to be added or updated. + virtual void + createUpdateOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const OptionPtr& option); + + /// @brief Creates or updates global string parameter. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param name Name of the global parameter. + /// @param value Value of the global parameter. + virtual void + createUpdateGlobalParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name, + const std::string& value); + + /// @brief Creates or updates global number parameter. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param name Name of the global parameter. + /// @param value Value of the global parameter. + virtual void + createUpdateGlobalParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name, + const int64_t value); + + /// @brief Deletes subnet by prefix. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be deleted. + virtual void + deleteSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& subnet_prefix); + + /// @brief Deletes subnet by identifier. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param subnet_id Identifier of the subnet to be deleted. + virtual void + deleteSubnet4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id); + + /// @brief Deletes all subnets. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + virtual void + deleteAllSubnets4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector); + + /// @brief Deletes shared network by name. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param name Name of the shared network to be deleted. + virtual void + deleteSharedNetwork4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name); + + /// @brief Deletes all shared networks. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + virtual void + deleteAllSharedNetworks4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector); + + /// @brief Deletes option definition. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + virtual void + deleteOptionDef4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space); + + /// @brief Deletes all option definitions. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + virtual void + deleteAllOptionDefs4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector); + + /// @brief Deletes global option. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param code Code of the option to be deleted. + /// @param space Option space of the option to be deleted. + virtual void + deleteOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const uint16_t code, + const std::string& space); + + /// @brief Deletes subnet level option. + /// + /// @param backend_selector Backend selector. + /// @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. + virtual void + deleteOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const SubnetID& subnet_id, + const uint16_t code, const std::string& space); + + /// @brief Deletes pool level option. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param pool_start_address Lower bound address of the pool to which + /// deleted option belongs. + /// @param pool_end_address Upper bound address of the pool to which the + /// deleted option belongs. + /// @param code Code of the deleted option. + /// @param space Option space of the deleted option. + virtual void + deleteOption4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const asiolink::IOAddress& pool_start_address, + const asiolink::IOAddress& pool_end_address, + const uint16_t code, + const std::string& space); + + /// @brief Deletes global parameter. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param name Name of the global parameter to be deleted. + virtual void + deleteGlobalParameter4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const std::string& name); + + /// @brief Deletes all global parameters. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + virtual void + deleteAllGlobalParameters4(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector); +}; + + +} // end of namespace isc::dhcp +} // end of namespace isc + +#endif // CONFIG_BACKEND_POOL_DHCP4_H