Commit 417beb8b authored by Marcin Siodelski's avatar Marcin Siodelski

[#28] Added libkea-cb library.

parent e3f3adaf
......@@ -1534,6 +1534,8 @@ AC_CONFIG_FILES([Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/data_def_unittests_config.h
src/lib/config/tests/testdata/Makefile
src/lib/config_backend/Makefile
src/lib/config_backend/tests/Makefile
src/lib/cryptolink/Makefile
src/lib/cryptolink/tests/Makefile
src/lib/database/Makefile
......
......@@ -13,7 +13,7 @@ if HAVE_CQL
SUBDIRS += cql
endif
SUBDIRS += testutils hooks dhcp config stats
SUBDIRS += config_backend testutils hooks dhcp config stats
if HAVE_SYSREPO
SUBDIRS += yang
......
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(KEA_CXXFLAGS)
lib_LTLIBRARIES = libkea-cb.la
libkea_cb_la_SOURCES = base_config_backend.h
libkea_cb_la_SOURCES += base_config_backend_mgr.h
libkea_cb_la_SOURCES += base_config_backend_pool.cc base_config_backend_pool.h
libkea_cb_la_LIBADD = $(top_builddir)/src/lib/database/libkea-database.la
libkea_cb_la_LIBADD = $(top_builddir)/src/lib/util/libkea-util.la
libkea_cb_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libkea_cb_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libkea_cb_la_LIBADD += $(BOOST_LIBS)
libkea_cb_la_LDFLAGS = -no-undefined -version-info 0:0:0
# The message file should be in the distribution.
#EXTRA_DIST = config_backend.dox
CLEANFILES = *.gcno *.gcda
# Specify the headers for copying into the installation directory tree.
#libkea_cb_includedir = $(pkgincludedir)/config
#libkea_cb_include_HEADERS =
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef BASE_CONFIG_BACKEND_H
#define BASE_CONFIG_BACKEND_H
#include <boost/shared_ptr.hpp>
#include <string>
namespace isc {
namespace cb {
class BaseConfigBackend {
public:
virtual ~BaseConfigBackend() {
}
virtual std::string getType() const = 0;
virtual std::string getHost() const = 0;
virtual uint16_t getPort() const = 0;
};
typedef boost::shared_ptr<BaseConfigBackend> BaseConfigBackendPtr;
} // end of namespace isc::cb
} // end of namespace isc
#endif // BASE_CONFIG_BACKEND_H
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef BASE_CONFIG_BACKEND_MGR_H
#define BASE_CONFIG_BACKEND_MGR_H
#include <database/database_connection.h>
#include <config_backend/base_config_backend.h>
#include <exceptions/exceptions.h>
#include <boost/shared_ptr.hpp>
#include <functional>
#include <map>
#include <string>
namespace isc {
namespace cb {
template<typename ConfigBackendPoolType>
class BaseConfigBackendMgr {
public:
typedef boost::shared_ptr<ConfigBackendPoolType> ConfigBackendPoolPtr;
typedef std::function<typename ConfigBackendPoolType::ConfigBackendTypePtr
(const db::DatabaseConnection::ParameterMap&)> Factory;
BaseConfigBackendMgr()
: factories_(), backends_(new ConfigBackendPoolType()) {
}
bool registerBackendFactory(const std::string& db_type,
const Factory& factory) {
if (factories_.count(db_type)) {
return (false);
}
factories_.insert(std::make_pair(db_type, factory));
return (true);
}
void addBackend(const std::string& dbaccess) {
// Parse the access string and create a redacted string for logging.
db::DatabaseConnection::ParameterMap parameters =
db::DatabaseConnection::parse(dbaccess);
// Get the database type and open the corresponding database
db::DatabaseConnection::ParameterMap::iterator it = parameters.find("type");
if (it == parameters.end()) {
isc_throw(InvalidParameter, "Host database configuration does not "
"contain the 'type' keyword");
}
std::string db_type = it->second;
auto index = factories_.find(db_type);
// No match?
if (index == factories_.end()) {
isc_throw(db::InvalidType, "The type of host backend: '" <<
db_type << "' is not currently supported");
}
// Call the factory and push the pointer on sources.
auto backend = index->second(parameters);
if (!backend) {
isc_throw(Unexpected, "Config database " << db_type <<
" factory returned NULL");
}
backends_->addBackend(backend);
}
void delAllBackends() {
backends_->delAllBackends();
}
ConfigBackendPoolPtr getPool() const {
return (backends_);
}
protected:
std::map<std::string, Factory> factories_;
ConfigBackendPoolPtr backends_;
};
} // end of namespace isc::cb
} // end of namespace isc
#endif // BASE_CONFIG_BACKEND_MGR_H
// 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_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
} // end of namespace isc
This diff is collapsed.
// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef CONFIG_BACKEND_MGR_H
#define CONFIG_BACKEND_MGR_H
namespace isc {
namespace cb {
class ConfigBackendMgr {
public:
};
} // end of namespace isc::cb
} // end of namespace isc
#endif // CONFIG_BACKEND_MGR_H
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\"
AM_CXXFLAGS = $(KEA_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
CLEANFILES = *.gcno *.gcda
TESTS_ENVIRONMENT = \
$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
TESTS =
if HAVE_GTEST
TESTS += libcb_unittests
libcb_unittests_SOURCES = config_backend_mgr_unittest.cc
libcb_unittests_SOURCES += config_backend_selector_unittest.cc
libcb_unittests_SOURCES += run_unittests.cc
libcb_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
libcb_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
libcb_unittests_LDADD = $(top_builddir)/src/lib/database/libkea-database.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/config_backend/libkea-cb.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
libcb_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
libcb_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)
// 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_mgr.h>
#include <config_backend/base_config_backend_pool.h>
#include <config_backend/base_config_backend.h>
#include <database/database_connection.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <list>
#include <string>
#include <utility>
using namespace isc;
using namespace isc::cb;
using namespace isc::db;
namespace {
/// @brief Defines list of properties retrieved by @c TestConfigBackend.
///
/// A single property is a name/value pair, where value is an integer.
typedef std::list<std::pair<std::string, int> > PropertiesList;
/// @brief Implements configuration backend used in tests.
///
/// @c BaseConfigBackend is an abstract class that must be implemented
/// in order to allow us to test mechanisms implemented in
/// @c BaseConfigBackendMgr and @c BaseConfigBackendPool.
///
/// Normally, a class derived directly from the @c BaseConfigBackend
/// will merely provide an interface for server specific operations,
/// e.g. DHCPv4 specific operations, and the database specific classes
/// will implement this interface. However, for the test purposes it
/// is convenient to implement them here and let the derivations
/// only provide the implementations of the @c getType, @c getHost
/// and @c getPort functions. That way, the logic for adding and
/// retrieving the data from the backend is implemented only once and
/// is common accross all test backends.
///
/// This class provides a logic for managing test data being a collection
/// of name/value pairs, i.e. "properties". It contains a list of
/// properties and the functions for adding the properties, retrieving
/// a single property and retrieving a collection of properties.
class TestConfigBackend : public BaseConfigBackend {
public:
/// @brief Retrieves first property having a given name.
///
/// @param property_name Name of the property to be retrieved.
/// @return Value of the property or 0 if property doesn't exist.
virtual int getProperty(const std::string& property_name) const {
for (auto property : properties_) {
if (property.first == property_name) {
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 {
PropertiesList properties;
for (auto property : properties_) {
if (property.first == property_name) {
properties.push_back(property);
}
}
return (properties);
}
/// @brief Retrieves all properties.
///
/// @return List of all properties held in the backend.
virtual PropertiesList getAllProperties() const {
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) {
properties_.push_back(new_property);
}
protected:
/// @brief Holds list of properties (simulates database).
PropertiesList properties_;
};
/// @brief Shared pointer to the @c TestConfigBackend.
typedef boost::shared_ptr<TestConfigBackend> TestConfigBackendPtr;
/// @brief First implementation of the test config backend.
///
/// It simulates being a MySQL backend installed on the
/// "mysql-host" host and running on port 2345.
class TestConfigBackendImpl1 : public TestConfigBackend {
public:
/// @brief Returns backend type.
///
/// @return "mysql".
virtual std::string getType() const {
return (std::string("mysql"));
}
/// @brief Returns backend host.
///
/// @return "mysql-host".
virtual std::string getHost() const {
return (std::string("mysql-host"));
}
/// @brief Returns backend port.
///
/// @return Port number 2345.
virtual uint16_t getPort() const {
return (2345);
}
};
/// @brief Shared pointer to the @c TestConfigBackendImpl1.
typedef boost::shared_ptr<TestConfigBackendImpl1> TestConfigBackendImpl1Ptr;
/// @brief Second implementation of the test config backend.
///
/// It simulates being a Postgres backend installed on the
/// "pgsql-host" host and running on port 1234.
class TestConfigBackendImpl2 : public TestConfigBackend {
public:
/// @brief Returns backend type.
///
/// @return "pgsql".
virtual std::string getType() const {
return (std::string("pgsql"));
}
/// @brief Returns backend host.
///
/// @return "pgsql-host".
virtual std::string getHost() const {
return (std::string("pgsql-host"));
}
/// @brief Returns backend port.
///
/// @return Port number 1234.
virtual uint16_t getPort() const {
return (1234);
}
};
/// @brief Shared pointer to the @c TestConfigBackendImpl2.
typedef boost::shared_ptr<TestConfigBackendImpl2> TestConfigBackendImpl2Ptr;
/// @brief Implements test pool of configuration backends.
///
/// @c BaseConfigBackendPool template provides mechanics for managing the data
/// stored in multiple backends. Server specific pools must extend this class
/// with methods for managing the data appropriate for the server types.
/// This class provides an example pool implementation for managing the
/// "properties" being name/value pairs. It extends the base class with
/// new methods to retrieve a single property and multiple properties. It
/// also adds a method to create new property. Those methods correspond to
/// the ones implemented in the @c TestConfigBackend, but also each of
/// them includes a "database selector" used to indicate the backend to
/// be used.
class TestConfigBackendPool : public BaseConfigBackendPool<TestConfigBackend> {
public:
/// @brief Retrieves a value of the property.
///
/// @param property_name Name of the property which value should be returned.
/// @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.
virtual int getProperty(const std::string& property_name,
const BackendSelector& selector = BackendSelector::UNSPEC()) const {
int property;
// If the selector is specified, this method will pick the appropriate
// backend and will call getProperty method on this backend. If the
// selector is not specified, this method will iterate over existing
// backends and call getProperty on them. It will return after finding
// the first non-zero value of the property. For example, if the first
// backend contains a non-zero value this value will be returned and
// the value held in the second backend (if any) won't be fetched.
// The template arguments specify the returned value type and the
// argument of the getProperty method.
getPropertyPtrConst<int,
const std::string&,
&TestConfigBackend::getProperty>
(property, property_name, selector);
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.
virtual PropertiesList getProperties(const std::string& property_name,
const BackendSelector& selector =
BackendSelector::UNSPEC()) const {
PropertiesList properties;
// If the selector is specified, this method will pick the appropriate
// backend and will call getProperties method on this backend. If the
// selector is not specified, this method will iterate over existing
// backends and call getProperties on them. It will return after finding
// the first non-empty list of properties in one of the backends.
// The template arguments specify the type of the list of properties
// and the argument of the getProperties method.
getMultiplePropertiesConst<PropertiesList, const std::string&,
&TestConfigBackend::getProperties>
(properties, property_name, selector);
return (properties);
}
/// @brief Retrieves all properties.
///
/// @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.
virtual PropertiesList getAllProperties(const BackendSelector& selector =
BackendSelector::UNSPEC()) 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);
return (properties);
}
/// @brief Creates new property in a selected backend.
///
/// @param new_property New property to be added to a backend.
/// @param selector Backend selector. It has no default value.
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);
}
};
using TestConfigBackendMgr = BaseConfigBackendMgr<TestConfigBackendPool>;
/// @brief Test fixture class for testing @c ConfigBackendMgr.
class ConfigBackendMgrTest : public ::testing::Test {
public:
/// @brief Constructor.
ConfigBackendMgrTest()