]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#28,!20] Added server selector to the config_backend.
authorMarcin Siodelski <marcin@isc.org>
Wed, 5 Sep 2018 15:43:13 +0000 (17:43 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 18 Sep 2018 05:06:26 +0000 (07:06 +0200)
Also, modfied config backend pool functions to support variable number
of parameters.

src/lib/config_backend/base_config_backend.h
src/lib/config_backend/base_config_backend_pool.h
src/lib/config_backend/tests/Makefile.am
src/lib/config_backend/tests/config_backend_mgr_unittest.cc
src/lib/config_backend/tests/server_selector_unittest.cc [new file with mode: 0644]

index b92d2079be6996fc1f3c0fc0e315613487cbac47..d35734eb1d02a13a734f64c3ec7ca6c7762bf5d1 100644 (file)
 
 #include <boost/shared_ptr.hpp>
 #include <cstdint>
+#include <set>
 #include <string>
 
 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 ea392ecf1f37f3ec067cba883bd99283e636ae82..2dd5a9d03fa8aff39bbf398c58310ff7a2bc0a88 100644 (file)
@@ -216,7 +216,7 @@ private:
 /// 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 routing
+/// 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.
 ///
@@ -250,17 +250,18 @@ protected:
 
     /// @brief Retrieve a single configuration property from the pool.
     ///
-    /// This is common method for retrieving a single configuration property
+    /// 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& selector) const {
+    ///                       const BackendSelector& selector,
+    ///                       const ServerSelector& server_selector) const {
     ///     Subnet4Ptr subnet;
     ///     getPropertyPtrConst<Subnet4Ptr, const SubnetID&, ConfigBackendDHCPv4::getSubnet4>
-    ///         (subnet, subnet_id, selector);
+    ///         (&ConfigBackendDHCPv4::getSubnet4, selector, subnet, subnet_id);
     ///     return (subnet);
     /// }
     /// @endcode
@@ -268,7 +269,7 @@ protected:
     /// where @c ConfigBackendDHCPv4::getSubnet4 has the following signature:
     ///
     /// @code
-    /// Subnet4Ptr getSubnet4(const SubnetID& subnet_id) const;
+    /// Subnet4Ptr getSubnet4(const ServerSelector&, const SubnetID&) const;
     /// @endcode
     ///
     /// If the backend selector is set to "unspecified", this method will iterate
@@ -279,28 +280,30 @@ protected:
     /// rest of the backends are skipped.
     ///
     /// @tparam PropertyType Type of the object returned by the backend call.
-    /// @tparam InputType Type of the object used as input to the backend call.
-    /// @tparam MethodPointer Type of the pointer to the backend method to be
-    /// called.
+    /// @tparam InputType Type of the objects used as input to the backend call.
     ///
+    /// @param MethodPointer Pointer to the backend method to be called.
+    /// @param selector Backend selector.
+    /// @param server_selector Server selector.
     /// @param [out] property Reference to the shared pointer where retrieved
     /// property should be assigned.
-    /// @param input Value to be used as input to the backend call.
-    /// @param selector Backend selector. By default it is unspecified.
+    /// @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<typename PropertyType, typename InputType,
-             PropertyType (ConfigBackendType::*MethodPointer)(InputType) const>
-    void getPropertyPtrConst(PropertyType& property, InputType input,
-                             const BackendSelector& selector =
-                             BackendSelector::UNSPEC()) const {
+    template<typename PropertyType, typename... InputType>
+    void getPropertyPtrConst(PropertyType (ConfigBackendType::*MethodPointer)
+                             (const ServerSelector&, InputType...) const,
+                             const BackendSelector& selector,
+                             const ServerSelector& server_selector,
+                             PropertyType& property,
+                             InputType... input) const {
 
         // If no particular backend is selected, call each backend and return
         // the first non-null (non zero) value.
         if (selector.amUnspecified()) {
             for (auto backend : backends_) {
-                property = ((*backend).*MethodPointer)(input);
+                property = ((*backend).*MethodPointer)(server_selector, input...);
                 if (property) {
                     break;
                 }
@@ -311,7 +314,7 @@ protected:
             auto backends = selectBackends(selector);
             if (!backends.empty()) {
                 for (auto backend : backends) {
-                    property = ((*backend).*MethodPointer)(input);
+                    property = ((*backend).*MethodPointer)(server_selector, input...);
                     if (property) {
                         break;
                     }
@@ -333,20 +336,21 @@ protected:
     /// @c getSubnets6 method:
     ///
     /// @code
-    /// Subnet6Collection getModifiedSubnets6(const ptime& modification_time,
-    ///                                       const BackendSelector& selector) const {
+    /// Subnet6Collection getModifiedSubnets6(const BackendSelector& selector,
+    ///                                       const ServerSelector& server_selector,
+    ///                                       const ptime& modification_time) const {
     ///     Subnet6Collection subnets;
-    ///     getMultiplePropertiesConst<Subnet6Collection, const ptime&,
-    ///                                ConfigBackendDHCPv6::getSubnets6>
-    ///         (subnets, modification_time, selector);
-    ///     return (subnets); 
+    ///     getMultiplePropertiesConst<Subnet6Collection, const ptime&>
+    ///         (&ConfigBackendDHCPv6::getSubnets6, selector, subnets,
+    ///          modification_time);
+    ///     return (subnets);
     /// }
     /// @endcode
     ///
     /// where @c ConfigBackendDHCPv6::getSubnets6 has the following signature:
     ///
     /// @code
-    /// Subnet6Collection getSubnets6(const ptime& modification_time) const;
+    /// Subnet6Collection getSubnets6(const ServerSelector&, const ptime&) const;
     /// @endcode
     ///
     /// If the backend selector is set to "unspecified", this method will iterate
@@ -356,25 +360,26 @@ protected:
     ///
     /// @tparam PropertyCollectionType Type of the container into which the
     /// properties are stored.
-    /// @tparam InputType type of the object used as input to the backend call.
-    /// @tparam MethodPointer Type of the pointer to the backend method to be
-    /// called.
+    /// @tparam InputType Type of the objects used as input to the backend call.
     ///
+    /// @param MethodPointer Pointer to the backend method to be called.
+    /// @param selector Backend selector.
+    /// @param server_selector Server selector.
     /// @param [out] properties Reference to the collection of retrieved properties.
-    /// @param inputValue to be used as input to the backend call.
-    /// @param selector Backend selector. By default it is unspecified.
+    /// @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<typename PropertyCollectionType, typename InputType,
-             PropertyCollectionType (ConfigBackendType::*MethodPointer)(InputType) const>
-    void getMultiplePropertiesConst(PropertyCollectionType& properties,
-                                    InputType input,
-                                    const BackendSelector& selector =
-                                    BackendSelector::UNSPEC()) const {
+    template<typename PropertyCollectionType, typename... InputType>
+    void getMultiplePropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer)
+                                    (const ServerSelector&, InputType...) const,
+                                    const BackendSelector& selector,
+                                    const ServerSelector& server_selector,
+                                    PropertyCollectionType& properties,
+                                    InputType... input) const {
         if (selector.amUnspecified()) {
             for (auto backend : backends_) {
-                properties = ((*backend).*MethodPointer)(input);
+                properties = ((*backend).*MethodPointer)(server_selector, input...);
                 if (!properties.empty()) {
                     break;
                 }
@@ -384,7 +389,7 @@ protected:
             auto backends = selectBackends(selector);
             if (!backends.empty()) {
                 for (auto backend : backends) {
-                    properties = ((*backend).*MethodPointer)(input);
+                    properties = ((*backend).*MethodPointer)(server_selector, input...);
                     if (!properties.empty()) {
                         break;
                     }
@@ -406,10 +411,11 @@ protected:
     /// @c getAllSubnets4 method:
     ///
     /// @code
-    /// Subnet4Collection getAllSubnets4(const BackendSelector& selector) const {
+    /// Subnet4Collection getAllSubnets4(const BackendSelector&, const ServerSelector&) const {
     ///     Subnet4Collection subnets;
-    ///     getAllPropertiesConst<Subnet6Collection, ConfigBackendDHCPv4::getAllSubnets4>
-    ///         (subnets, selector);
+    ///     getAllPropertiesConst<Subnet6Collection>
+    ///         (&ConfigBackendDHCPv4::getAllSubnets4, subnets, selector,
+    ///          server_selector);
     ///     return (subnets);
     /// }
     /// @endcode
@@ -417,7 +423,7 @@ protected:
     /// where @c ConfigBackendDHCPv4::getAllSubnets4 has the following signature:
     ///
     /// @code
-    /// Subnet4Collection getAllSubnets4() const;
+    /// Subnet4Collection getAllSubnets4(const ServerSelector&) const;
     /// @endcode
     ///
     /// If the backend selector is set to "unspecified", this method will iterate
@@ -427,22 +433,23 @@ protected:
     ///
     /// @tparam PropertyCollectionType Type of the container into which the
     /// properties are stored.
-    /// @tparam MethodPointer Type of the pointer to the backend method to be
-    /// called.
     ///
+    /// @param MethodPointer Pointer to the backend method to be called.
+    /// @param selector Backend selector.
+    /// @param server_selector Server selector.
     /// @param [out] properties Reference to the collection of retrieved properties.
-    /// @param selector Backend selector. By default it is unspecified.
     ///
     /// @throw db::NoSuchDatabase if no database matching the given selector
     /// was found.
-    template<typename PropertyCollectionType,
-             PropertyCollectionType (ConfigBackendType::*MethodPointer)() const>
-    void getAllPropertiesConst(PropertyCollectionType& properties,
-                               const BackendSelector& selector =
-                               BackendSelector::UNSPEC()) const {
+    template<typename PropertyCollectionType>
+    void getAllPropertiesConst(PropertyCollectionType (ConfigBackendType::*MethodPointer)
+                               (const ServerSelector&) const,
+                               const BackendSelector& selector,
+                               const ServerSelector& server_selector,
+                               PropertyCollectionType& properties) const {
         if (selector.amUnspecified()) {
             for (auto backend : backends_) {
-                properties = ((*backend).*MethodPointer)();
+                properties = ((*backend).*MethodPointer)(server_selector);
                 if (!properties.empty()) {
                     break;
                 }
@@ -452,7 +459,7 @@ protected:
             auto backends = selectBackends(selector);
             if (!backends.empty()) {
                 for (auto backend : backends) {
-                    properties = ((*backend).*MethodPointer)();
+                    properties = ((*backend).*MethodPointer)(server_selector);
                     if (!properties.empty()) {
                         break;
                     }
@@ -476,10 +483,11 @@ protected:
     ///
     /// @code
     /// void createUpdateSubnet6(const Subnet6Ptr& subnet,
-    ///                          const BackendSelector& selector) {
-    ///     createUpdateDeleteProperty<const Subnet6Ptr&,
-    ///                                ConfigBackendDHCPv6::createUpdateSubnet6>
-    ///         (subnet, selector);
+    ///                          const BackendSelector& selector,
+    ///                          const ServerSelector& server_selector) {
+    ///     createUpdateDeleteProperty<const Subnet6Ptr&>
+    ///         (&ConfigBackendDHCPv6::createUpdateSubnet6, selector,
+    ///          server_selector, subnet, selector);
     /// }
     /// @endcode
     ///
@@ -487,29 +495,32 @@ protected:
     /// signature:
     ///
     /// @code
-    /// void createUpdateSubnet6(const Subnet6Ptr& subnet);
+    /// 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 InputType Type of the object being a new property to be added
-    /// or updated, or an identifier of the object to be deleted.
-    /// @tparam MethodPointer Type of the pointer to the backend method to be
-    /// called.
+    /// @tparam InputType Type of the objects being used as arguments of the
+    /// backend method, e.g. new property to be added, updated or deleted.
     ///
-    /// @param input Object being a new property to be added or updated, or an
-    /// identifier of the object to be deleted.
+    /// @param MethodPointer Pointer to the backend method to be called.
     /// @param 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<typename InputType,
-             void (ConfigBackendType::*MethodPointer)(InputType)>
-    void createUpdateDeleteProperty(InputType input, const BackendSelector& selector) {
+    template<typename... InputType>
+    void createUpdateDeleteProperty(void (ConfigBackendType::*MethodPointer)
+                                    (const ServerSelector&, InputType...),
+                                    const BackendSelector& selector,
+                                    const ServerSelector& server_selector,
+                                    InputType... input) {
         auto backends = selectBackends(selector);
         if (backends.empty()) {
             isc_throw(db::NoSuchDatabase, "no database found for selector: "
@@ -520,7 +531,7 @@ protected:
                       "selector: " << selector.toText());
         }
 
-        (*(*(backends.begin())).*MethodPointer)(input);
+        (*(*(backends.begin())).*MethodPointer)(server_selector, input...);
     }
 
     /// @brief Selects existing backends matching the selector.
index 29472adfa44db6705434736f68223057c3d49cb3..1c2b76d80f031872f62b4c0efa2ef7238ac7afc2 100644 (file)
@@ -22,6 +22,7 @@ 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 57f4aad45c44f829a6f1965e9565e294ca07a279..a2568b62939e3ab74fbe450761d960437903e44f 100644 (file)
@@ -54,7 +54,8 @@ public:
     ///
     /// @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 std::string& property_name) const {
+    virtual int getProperty(const ServerSelector&,
+                            const std::string& property_name) const {
         for (auto property : properties_) {
             if (property.first == property_name) {
                 return (property.second);
@@ -63,12 +64,30 @@ public:
         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 std::string& property_name) const {
+    virtual PropertiesList getProperties(const ServerSelector&,
+                                         const std::string& property_name) const {
         PropertiesList properties;
         for (auto property : properties_) {
             if (property.first == property_name) {
@@ -81,14 +100,15 @@ public:
     /// @brief Retrieves all properties.
     ///
     /// @return List of all properties held in the backend.
-    virtual PropertiesList getAllProperties() const {
+    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 std::pair<std::string, int>& new_property) {
+    virtual void createProperty(const ServerSelector&,
+                                const std::pair<std::string, int>& new_property) {
         properties_.push_back(new_property);
     }
 
@@ -188,8 +208,13 @@ public:
     /// @param 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& selector = BackendSelector::UNSPEC()) const {
+                            const BackendSelector& selector =
+                            BackendSelector::UNSPEC(),
+                            const ServerSelector& server_selector =
+                            ServerSelector::ALL()) const {
         int property;
 
         // If the selector is specified, this method will pick the appropriate
@@ -201,22 +226,48 @@ public:
         // 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<int,
-                            const std::string&,
-                            &TestConfigBackend::getProperty>
-            (property, property_name, selector);
+        getPropertyPtrConst<int, const std::string&>
+            (&TestConfigBackend::getProperty, 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 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& selector =
+                            BackendSelector::UNSPEC(),
+                            const ServerSelector& server_selector =
+                            ServerSelector::ALL()) const {
+        int property;
+        getPropertyPtrConst<int, const std::string&, const int>
+            (&TestConfigBackend::getProperty, 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 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& selector =
-                                         BackendSelector::UNSPEC()) const {
+                                         BackendSelector::UNSPEC(),
+                                         const ServerSelector& server_selector =
+                                         ServerSelector::ALL()) const {
         PropertiesList properties;
 
         // If the selector is specified, this method will pick the appropriate
@@ -226,9 +277,9 @@ public:
         // 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<PropertiesList, const std::string&,
-                                   &TestConfigBackend::getProperties>
-            (properties, property_name, selector);
+        getMultiplePropertiesConst<PropertiesList, const std::string&>
+            (&TestConfigBackend::getProperties, selector, server_selector,
+             properties, property_name);
         return (properties);
     }
 
@@ -237,14 +288,19 @@ public:
     /// @param 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& selector =
-                                            BackendSelector::UNSPEC()) const {
+                                            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<PropertiesList, &TestConfigBackend::getAllProperties>
-            (properties, selector);
+        getAllPropertiesConst<PropertiesList>
+            (&TestConfigBackend::getAllProperties, selector, server_selector,
+             properties);
         return (properties);
     }
 
@@ -252,13 +308,16 @@ public:
     ///
     /// @param new_property New property to be added to a backend.
     /// @param 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<std::string, int>& new_property,
-                                const BackendSelector& selector) {
-        createUpdateDeleteProperty<const std::pair<std::string, int>&,
-                                   &TestConfigBackend::createProperty>
-            (new_property, selector);
+                                const BackendSelector& selector,
+                                const ServerSelector& server_selector =
+                                ServerSelector::ALL()) {
+        createUpdateDeleteProperty<const std::pair<std::string, int>&>
+            (&TestConfigBackend::createProperty, selector, server_selector,
+             new_property);
     }
-
 };
 
 using TestConfigBackendMgr = BaseConfigBackendMgr<TestConfigBackendPool>;
@@ -399,6 +458,15 @@ TEST_F(ConfigBackendMgrTest, getSingleProperty) {
     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)),
diff --git a/src/lib/config_backend/tests/server_selector_unittest.cc b/src/lib/config_backend/tests/server_selector_unittest.cc
new file mode 100644 (file)
index 0000000..bf53277
--- /dev/null
@@ -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 <config.h>
+#include <config_backend/base_config_backend.h>
+#include <gtest/gtest.h>
+
+using namespace isc::cb;
+
+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<std::string> 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<std::string> 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"));
+}
+
+}