1 // Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC") 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 #ifndef BASE_CONFIG_BACKEND_MGR_H 8 #define BASE_CONFIG_BACKEND_MGR_H 9 10 #include <config_backend/base_config_backend.h> 11 #include <database/database_connection.h> 12 #include <database/backend_selector.h> 13 #include <exceptions/exceptions.h> 14 #include <boost/shared_ptr.hpp> 15 #include <functional> 16 #include <map> 17 #include <string> 18 19 namespace isc { 20 namespace cb { 21 22 /// @brief Base class for Configuration Backend Managers (CBM). 23 /// 24 /// Each Kea server supporting Configuration Backend feature implements 25 /// a "manager" class which holds information about supported and 26 /// configured backends and provides access to the backends. This is 27 /// similar to @c HostMgr and @c LeaseMgr singletons being used by the 28 /// DHCP servers. 29 /// 30 /// The Config Backend Managers are typically implemented as singletons 31 /// which can be accessed from any place within the server code. This 32 /// includes server configuration, data fetching during normal server 33 /// operation and data management, including processing of control 34 /// commands implemented within hooks libraries. 35 /// 36 /// The @c BaseConfigBackendMgr is a base class for all CBMs implemented 37 /// for respective Kea servers. It includes mechanisms to register config 38 /// backend factory functions and to create instances of the backends using 39 /// those factory functions as a result of server configuration. The mechanism 40 /// of factory functions registration is useful in cases when the config 41 /// backend is implemented within the hook library. Such hook library 42 /// registers factory function in its @c load function and the server 43 /// simply calls this function to create the instance of this backend when 44 /// instructed to do so via configuration. Similar mechanism exists in 45 /// DHCP @c HostMgr. 46 /// 47 /// Unlike @c HostMgr, the CBMs do not directly expose API to fetch and 48 /// manipulate the data in the database. This is done via, so called, 49 /// Configuration Backends Pools. See @c BaseConfigBackendPool for 50 /// details. The @c BaseConfigBackendMgr is provided with the pool type 51 /// via class template parameter. Respective CBM implementations 52 /// use their own pools, which provide APIs appropriate for those 53 /// implementations. 54 /// 55 /// @tparam ConfgBackendPoolType Type of the configuration backend pool 56 /// to be used by the manager. It must derive from @c BaseConfigBackendPool 57 /// template class. 58 template<typename ConfigBackendPoolType> 59 class BaseConfigBackendMgr { 60 public: 61 62 /// @brief Pointer to the configuration backend pool. 63 typedef boost::shared_ptr<ConfigBackendPoolType> ConfigBackendPoolPtr; 64 65 /// @brief Type of the backend factory function. 66 /// 67 /// Factory function returns a pointer to the instance of the configuration 68 /// backend created. 69 typedef std::function<typename ConfigBackendPoolType::ConfigBackendTypePtr 70 (const db::DatabaseConnection::ParameterMap&)> Factory; 71 72 /// @brief Constructor. BaseConfigBackendMgr()73 BaseConfigBackendMgr() 74 : factories_(), pool_(new ConfigBackendPoolType()) { 75 } 76 77 /// @brief Registers new backend factory function for a given backend type. 78 /// 79 /// The typical usage of this function is to make the CBM aware of a 80 /// configuration backend implementation. This implementation may exist 81 /// in a hooks library. In such case, this function should be called from 82 /// the @c load function in this library. When the backend is registered, 83 /// the server will use it when required by the configuration, i.e. a 84 /// user includes configuration backend of that type in the 85 /// "config-databases" list. 86 /// 87 /// If the backend of the given type has already been registered, perhaps 88 /// by another hooks library, the CBM will refuse to register another 89 /// backend of the same type. 90 /// 91 /// @param db_type Backend type, e.g. "mysql". 92 /// @param factory Pointer to the backend factory function. 93 /// 94 /// @return true if the backend has been successfully registered, false 95 /// if another backend of this type already exists. registerBackendFactory(const std::string & db_type,const Factory & factory)96 bool registerBackendFactory(const std::string& db_type, 97 const Factory& factory) { 98 // Check if this backend has been already registered. 99 if (factories_.count(db_type)) { 100 return (false); 101 } 102 103 // Register the new backend. 104 factories_.insert(std::make_pair(db_type, factory)); 105 return (true); 106 } 107 108 /// @brief Unregisters the backend factory function for a given backend type. 109 /// 110 /// This function is used to remove the factory function and all backend instances 111 /// for a given backend type. Typically, it would be called when unloading the 112 /// a config backend hook library, and thus called by the library's @c unload 113 /// function. 114 /// 115 /// @param db_type Backend type, e.g. "mysql". 116 /// 117 /// @return false if no factory for the given type was unregistered, true 118 /// if the factory was removed. unregisterBackendFactory(const std::string & db_type)119 bool unregisterBackendFactory(const std::string& db_type) { 120 // Look for it. 121 auto index = factories_.find(db_type); 122 123 // If it's there remove it 124 if (index != factories_.end()) { 125 factories_.erase(index); 126 pool_->delAllBackends(db_type); 127 return (true); 128 129 } 130 131 return (false); 132 } 133 134 /// @brief Create an instance of a configuration backend. 135 /// 136 /// This method uses provided @c dbaccess string representing database 137 /// connection information to create an instance of the database 138 /// backend. If the specified backend type is not supported, i.e. there 139 /// is no relevant factory function registered, an exception is thrown. 140 /// 141 /// @param dbaccess Database access string being a collection of 142 /// key=value pairs. 143 /// 144 /// @throw InvalidParameter if access string lacks database type value. 145 /// @throw db::InvalidType if the type of the database backend is not 146 /// supported. 147 /// @throw Unexpected if the backend factory function returned NULL. addBackend(const std::string & dbaccess)148 void addBackend(const std::string& dbaccess) { 149 // Parse the access string into a map of parameters. 150 db::DatabaseConnection::ParameterMap parameters = 151 db::DatabaseConnection::parse(dbaccess); 152 153 // Get the database type to locate a factory function. 154 db::DatabaseConnection::ParameterMap::iterator it = parameters.find("type"); 155 if (it == parameters.end()) { 156 isc_throw(InvalidParameter, "Config backend specification lacks the " 157 "'type' keyword"); 158 } 159 160 std::string db_type = it->second; 161 auto index = factories_.find(db_type); 162 163 // No match? 164 if (index == factories_.end()) { 165 isc_throw(db::InvalidType, "The type of the configuration backend: '" << 166 db_type << "' is not supported"); 167 } 168 169 // Call the factory and push the pointer on sources. 170 auto backend = index->second(parameters); 171 if (!backend) { 172 isc_throw(Unexpected, "Config database " << db_type << 173 " factory returned NULL"); 174 } 175 176 // Backend instance created successfully. 177 pool_->addBackend(backend); 178 } 179 180 /// @brief Removes all backends from the pool. delAllBackends()181 void delAllBackends() { 182 pool_->delAllBackends(); 183 } 184 185 /// @brief Delete a config backend manager. 186 /// 187 /// Delete the first instance of a config database manager which matches 188 /// specific parameters. 189 /// This should have the effect of closing the database connection. 190 /// 191 /// @param db_type Backend to remove. 192 /// @param dbaccess Database access string being a collection of 193 /// key=value pairs. 194 /// @param if_unusable Flag which indicates if the config backend should be 195 /// deleted only if it is unusable. 196 /// @return false when not removed because it is not found or because it is 197 /// still usable (if_unusable is true), true otherwise. delBackend(const std::string & db_type,const std::string & dbaccess,bool if_unusable)198 bool delBackend(const std::string& db_type, const std::string& dbaccess, 199 bool if_unusable) { 200 return (pool_->del(db_type, dbaccess, if_unusable)); 201 } 202 203 /// @brief Returns underlying config backend pool. getPool()204 ConfigBackendPoolPtr getPool() const { 205 return (pool_); 206 } 207 208 protected: 209 210 /// @brief A map holding registered backend factory functions. 211 std::map<std::string, Factory> factories_; 212 213 /// @brief Pointer to the configuration backends pool. 214 ConfigBackendPoolPtr pool_; 215 }; 216 217 } // end of namespace isc::cb 218 } // end of namespace isc 219 220 #endif // BASE_CONFIG_BACKEND_MGR_H 221