]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#28,!20] Moved backend and server selector classes to database library.
authorMarcin Siodelski <marcin@isc.org>
Thu, 6 Sep 2018 15:34:51 +0000 (17:34 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 18 Sep 2018 05:06:26 +0000 (07:06 +0200)
src/lib/config_backend/base_config_backend.h
src/lib/config_backend/base_config_backend_pool.cc
src/lib/config_backend/base_config_backend_pool.h
src/lib/config_backend/tests/Makefile.am
src/lib/database/Makefile.am
src/lib/database/backend_selector.cc [new file with mode: 0644]
src/lib/database/backend_selector.h [new file with mode: 0644]
src/lib/database/server_selector.h [new file with mode: 0644]
src/lib/database/tests/Makefile.am
src/lib/database/tests/backend_selector_unittest.cc [moved from src/lib/config_backend/tests/config_backend_selector_unittest.cc with 87% similarity]
src/lib/database/tests/server_selector_unittest.cc [moved from src/lib/config_backend/tests/server_selector_unittest.cc with 95% similarity]

index d35734eb1d02a13a734f64c3ec7ca6c7762bf5d1..d34bc6f06f764a5d431af233af1291faa7003c24 100644 (file)
 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<std::string>& 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<std::string> 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<std::string>& server_tags)
-        : type_(Type::SUBSET), tags_(server_tags) {
-    }
-
-    /// @brief Selection type used.
-    Type type_;
-
-    /// @brief Holds tags of explicitly selected servers.
-    std::set<std::string> tags_;
-};
-
 /// @brief Base class for server specific configuration backends.
 class BaseConfigBackend {
 public:
index 34175fa6a35720fa6b96b6ce2461d7462418cf35..46420e73d8f42ace8045a6c1127842faaa8ded97 100644 (file)
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <config_backend/base_config_backend_pool.h>
-#include <exceptions/exceptions.h>
-#include <climits>
-#include <sstream>
-
-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<uint16_t>::max())) {
-            isc_throw(BadValue, "'port' parameter must be a number in range from 0 "
-                      "to " << std::numeric_limits<uint16_t>::max());
-        }
-        port_ = static_cast<uint16_t>(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
index 2dd5a9d03fa8aff39bbf398c58310ff7a2bc0a88..8989ba1a8bcd88a4e7110dbf04eda40d9c7f8c6b 100644 (file)
@@ -9,8 +9,9 @@
 
 #include <cc/data.h>
 #include <config_backend/base_config_backend.h>
+#include <database/backend_selector.h>
 #include <database/db_exceptions.h>
-#include <cstdint>
+#include <database/server_selector.h>
 #include <functional>
 #include <list>
 #include <string>
 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<typename PropertyType, typename... InputType>
     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<typename PropertyCollectionType, typename... InputType>
     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<typename PropertyCollectionType>
     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<typename... InputType>
     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<ConfigBackendTypePtr> selectBackends(const BackendSelector& selector) const {
+    std::list<ConfigBackendTypePtr>
+    selectBackends(const db::BackendSelector& selector) const {
 
         std::list<ConfigBackendTypePtr> 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;
             }
 
index 1c2b76d80f031872f62b4c0efa2ef7238ac7afc2..23574e4e09bbe103d68805612a7a04f6b733c6e3 100644 (file)
@@ -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)
index b39015f8db86238a0a639d23fc231e8ba4e8fb2e..ad8d1c6db8a81a91a665f2038da66561f87eaa19 100644 (file)
@@ -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 (file)
index 0000000..c7431d2
--- /dev/null
@@ -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 <database/backend_selector.h>
+#include <exceptions/exceptions.h>
+#include <climits>
+#include <sstream>
+
+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<uint16_t>::max())) {
+            isc_throw(BadValue, "'port' parameter must be a number in range from 0 "
+                      "to " << std::numeric_limits<uint16_t>::max());
+        }
+        port_ = static_cast<uint16_t>(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 (file)
index 0000000..7d256a0
--- /dev/null
@@ -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 <cc/data.h>
+#include <cstdint>
+#include <string>
+
+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 (file)
index 0000000..ed05ce9
--- /dev/null
@@ -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 <set>
+#include <string>
+
+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<std::string>& 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<std::string> 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<std::string>& server_tags)
+        : type_(Type::SUBSET), tags_(server_tags) {
+    }
+
+    /// @brief Selection type used.
+    Type type_;
+
+    /// @brief Holds tags of explicitly selected servers.
+    std::set<std::string> tags_;
+};
+
+
+} // end of namespace isc::db
+} // end of namespace isc
+
+#endif
index 7a0b74d984f40f0c9a8aeae41152d0ca64aba423..4e1609d18f7342c6618da130e17399ab1894ed6b 100644 (file)
@@ -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)
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 0fded79d30a1bf5301cd0e557c62d21573698e3f..3665d50972f916616754a00569658e9d7af86076 100644 (file)
@@ -6,16 +6,17 @@
 
 
 #include <config.h>
-#include <config_backend/base_config_backend_pool.h>
+#include <database/backend_selector.h>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 
 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<BackendSelector> 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<BackendSelector> 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<BackendSelector> 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<BackendSelector> 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));
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 bf532771b9e0727d0a5f5594bb3774376d59f611..1f2ab09c0349f62da55fe84e582aca865b7d2d5a 100644 (file)
@@ -5,10 +5,10 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <config.h>
-#include <config_backend/base_config_backend.h>
+#include <database/server_selector.h>
 #include <gtest/gtest.h>
 
-using namespace isc::cb;
+using namespace isc::db;
 
 namespace {