From: Marcin Siodelski Date: Thu, 6 Sep 2018 15:34:51 +0000 (+0200) Subject: [#28,!20] Moved backend and server selector classes to database library. X-Git-Tag: 134-bugs--xcode-10_base~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3bc730d19a3eba1467766e4cfb209da4b2530784;p=thirdparty%2Fkea.git [#28,!20] Moved backend and server selector classes to database library. --- diff --git a/src/lib/config_backend/base_config_backend.h b/src/lib/config_backend/base_config_backend.h index d35734eb1d..d34bc6f06f 100644 --- a/src/lib/config_backend/base_config_backend.h +++ b/src/lib/config_backend/base_config_backend.h @@ -15,112 +15,6 @@ namespace isc { namespace cb { -/// @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_; -}; - /// @brief Base class for server specific configuration backends. class BaseConfigBackend { public: diff --git a/src/lib/config_backend/base_config_backend_pool.cc b/src/lib/config_backend/base_config_backend_pool.cc index 34175fa6a3..46420e73d8 100644 --- a/src/lib/config_backend/base_config_backend_pool.cc +++ b/src/lib/config_backend/base_config_backend_pool.cc @@ -5,148 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include -#include -#include -#include - -using namespace isc::data; namespace isc { namespace cb { -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::cb diff --git a/src/lib/config_backend/base_config_backend_pool.h b/src/lib/config_backend/base_config_backend_pool.h index 2dd5a9d03f..8989ba1a8b 100644 --- a/src/lib/config_backend/base_config_backend_pool.h +++ b/src/lib/config_backend/base_config_backend_pool.h @@ -9,8 +9,9 @@ #include #include +#include #include -#include +#include #include #include #include @@ -18,192 +19,6 @@ namespace isc { namespace cb { -/// @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_; -}; /// @brief Base class for configuration backend pools. /// @@ -293,9 +108,9 @@ protected: /// was found. template void getPropertyPtrConst(PropertyType (ConfigBackendType::*MethodPointer) - (const ServerSelector&, InputType...) const, - const BackendSelector& selector, - const ServerSelector& server_selector, + (const db::ServerSelector&, InputType...) const, + const db::BackendSelector& selector, + const db::ServerSelector& server_selector, PropertyType& property, InputType... input) const { @@ -372,9 +187,9 @@ protected: /// was found. template void getMultiplePropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer) - (const ServerSelector&, InputType...) const, - const BackendSelector& selector, - const ServerSelector& server_selector, + (const db::ServerSelector&, InputType...) const, + const db::BackendSelector& selector, + const db::ServerSelector& server_selector, PropertyCollectionType& properties, InputType... input) const { if (selector.amUnspecified()) { @@ -443,9 +258,9 @@ protected: /// was found. template void getAllPropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer) - (const ServerSelector&) const, - const BackendSelector& selector, - const ServerSelector& server_selector, + (const db::ServerSelector&) const, + const db::BackendSelector& selector, + const db::ServerSelector& server_selector, PropertyCollectionType& properties) const { if (selector.amUnspecified()) { for (auto backend : backends_) { @@ -517,9 +332,9 @@ protected: /// were found. template void createUpdateDeleteProperty(void (ConfigBackendType::*MethodPointer) - (const ServerSelector&, InputType...), - const BackendSelector& selector, - const ServerSelector& server_selector, + (const db::ServerSelector&, InputType...), + const db::BackendSelector& selector, + const db::ServerSelector& server_selector, InputType... input) { auto backends = selectBackends(selector); if (backends.empty()) { @@ -540,7 +355,8 @@ protected: /// "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 BackendSelector& selector) const { + std::list + selectBackends(const db::BackendSelector& selector) const { std::list selected; @@ -560,9 +376,9 @@ protected: for (auto backend : backends_) { // If backend type is specified and it is not matching, // do not select this backend. - if ((selector.getBackendType() != BackendSelector::Type::UNSPEC) && + if ((selector.getBackendType() != db::BackendSelector::Type::UNSPEC) && (selector.getBackendType() != - BackendSelector::stringToBackendType(backend->getType()))) { + db::BackendSelector::stringToBackendType(backend->getType()))) { continue; } diff --git a/src/lib/config_backend/tests/Makefile.am b/src/lib/config_backend/tests/Makefile.am index 1c2b76d80f..23574e4e09 100644 --- a/src/lib/config_backend/tests/Makefile.am +++ b/src/lib/config_backend/tests/Makefile.am @@ -20,9 +20,7 @@ if HAVE_GTEST TESTS += libcb_unittests libcb_unittests_SOURCES = config_backend_mgr_unittest.cc -libcb_unittests_SOURCES += config_backend_selector_unittest.cc libcb_unittests_SOURCES += run_unittests.cc -libcb_unittests_SOURCES += server_selector_unittest.cc libcb_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) libcb_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/database/Makefile.am b/src/lib/database/Makefile.am index b39015f8db..ad8d1c6db8 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 0000000000..c7431d2c21 --- /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 0000000000..7d256a037a --- /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/server_selector.h b/src/lib/database/server_selector.h new file mode 100644 index 0000000000..ed05ce9811 --- /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 7a0b74d984..4e1609d18f 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/config_backend/tests/config_backend_selector_unittest.cc b/src/lib/database/tests/backend_selector_unittest.cc similarity index 87% rename from src/lib/config_backend/tests/config_backend_selector_unittest.cc rename to src/lib/database/tests/backend_selector_unittest.cc index 0fded79d30..3665d50972 100644 --- a/src/lib/config_backend/tests/config_backend_selector_unittest.cc +++ b/src/lib/database/tests/backend_selector_unittest.cc @@ -6,16 +6,17 @@ #include -#include +#include #include #include using namespace isc; -using namespace isc::cb; +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()); @@ -25,6 +26,7 @@ TEST(BackendSelectorTest, defaults) { 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()); @@ -34,6 +36,7 @@ TEST(BackendSelectorTest, unspec) { 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( @@ -46,6 +49,7 @@ TEST(BackendSelectorTest, backendTypeSpec) { 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( @@ -58,6 +62,7 @@ TEST(BackendSelectorTest, backendHostPortSpec) { 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( @@ -70,6 +75,8 @@ TEST(BackendSelectorTest, backendHostSpec) { 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; @@ -83,6 +90,7 @@ TEST(BackendSelectorTest, accessMapTypeUnSpec) { 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")); @@ -97,6 +105,8 @@ TEST(BackendSelectorTest, accessMapTypeSpec) { 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")); @@ -112,6 +122,8 @@ TEST(BackendSelectorTest, accessMapHostPortSpec) { 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")); @@ -126,6 +138,7 @@ TEST(BackendSelectorTest, accessMapHostSpec) { 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))); @@ -134,6 +147,7 @@ TEST(BackendSelectorTest, accessMapPortSpec) { BadValue); } +// Tests conversions of strings to backend types. TEST(BackendSelectorTest, stringToBackendType) { EXPECT_EQ(BackendSelector::Type::MYSQL, BackendSelector::stringToBackendType("mysql")); @@ -145,6 +159,7 @@ TEST(BackendSelectorTest, stringToBackendType) { BadValue); } +// Tests conversions of backend types to strings. TEST(BackendSelectorTest, backendTypeToString) { EXPECT_EQ("mysql", BackendSelector::backendTypeToString(BackendSelector::Type::MYSQL)); diff --git a/src/lib/config_backend/tests/server_selector_unittest.cc b/src/lib/database/tests/server_selector_unittest.cc similarity index 95% rename from src/lib/config_backend/tests/server_selector_unittest.cc rename to src/lib/database/tests/server_selector_unittest.cc index bf532771b9..1f2ab09c03 100644 --- a/src/lib/config_backend/tests/server_selector_unittest.cc +++ b/src/lib/database/tests/server_selector_unittest.cc @@ -5,10 +5,10 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include -#include +#include #include -using namespace isc::cb; +using namespace isc::db; namespace {