Commit 210105df authored by Thomas Markwalder's avatar Thomas Markwalder

[#101,!73] kea-dhcp4 can init CB backends based on config

src/bin/dhcp4/json_config_parser.*
    databaseConfigConnect(const SrvConfigPtr& srv_cfg)
    databaseConfigFetch(const SrvConfigPtr& srv_cfg, ElementPtr /*global_scope*/)
    - new functions

    configureDhcp4Server() - modified to call databaseConfigFetch() after
    loading hook libs

src/bin/dhcp4/tests/config_parser_unittest.cc
    TEST_F(Dhcp4ParserTest, configControlInfoNoFactory) - new test
    TEST_F(Dhcp4ParserTest, configControlInfo) - modified to register
    dummy backend

src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.*
    New files that implement DHPC4 dummy backend for testing
parent 8fb5a295
......@@ -10,11 +10,12 @@
#include <database/dbaccess_parser.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp4/json_config_parser.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp4/json_config_parser.h>
#include <dhcpsrv/config_backend_dhcp4_mgr.h>
#include <dhcpsrv/db_type.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
......@@ -50,6 +51,7 @@ using namespace isc::dhcp;
using namespace isc::data;
using namespace isc::asiolink;
using namespace isc::hooks;
using namespace isc::process;
namespace {
......@@ -318,9 +320,11 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
// the parsers. It is declared outside the loops so in case of an error,
// the name of the failing parser can be retrieved in the "catch" clause.
ConfigPair config_pair;
ElementPtr mutable_cfg;
SrvConfigPtr srv_cfg;
try {
SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg();
// Get the staging configuration
srv_cfg = CfgMgr::instance().getStagingCfg();
// Preserve all scalar global parameters
srv_cfg->extractConfiguredGlobals(config_set);
......@@ -328,7 +332,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
// This is a way to convert ConstElementPtr to ElementPtr.
// We need a config that can be edited, because we will insert
// default values and will insert derived values as well.
ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(config_set);
mutable_cfg = boost::const_pointer_cast<Element>(config_set);
// Set all default values if not specified by the user.
SimpleParser4::setAllDefaults(mutable_cfg);
......@@ -464,7 +468,6 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
}
if (config_pair.first == "subnet4") {
SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg();
Subnets4ListConfigParser subnets_parser;
// parse() returns number of subnets parsed. We may log it one day.
subnets_parser.parse(srv_cfg, config_pair.second);
......@@ -500,8 +503,8 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
}
if (config_pair.first == "config-control") {
process::ConfigControlParser parser;
process::ConfigControlInfoPtr config_ctl_info = parser.parse(config_pair.second);
ConfigControlParser parser;
ConfigControlInfoPtr config_ctl_info = parser.parse(config_pair.second);
CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
continue;
}
......@@ -574,9 +577,6 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
// This operation should be exception safe but let's make sure.
if (!rollback) {
try {
// if we have config-control DBs attempt to create them here,
// if that fails, rollback?
// Setup the command channel.
configureCommandChannel();
......@@ -595,8 +595,8 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
CfgMgr::instance().getStagingCfg()->getHooksConfig();
libraries.loadLibraries();
// now that we have config-db and hooks, merge in config from DB
// databaseConfigFetch(srv_config, mutable_cfg);
// If there are config backends, fetch and merge into staging config
databaseConfigFetch(srv_cfg, mutable_cfg);
}
catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
......@@ -629,5 +629,47 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
return (answer);
}
bool databaseConfigConnect(const SrvConfigPtr& srv_cfg) {
// We need to get rid of any existing backends. These would be any
// opened by previous configuration cycle.
ConfigBackendDHCPv4Mgr& mgr = ConfigBackendDHCPv4Mgr::instance();
mgr.delAllBackends();
// SrvConfigPtr staging_cfg = CfgMgr::instance().getStagingCfg();
ConstConfigControlInfoPtr config_ctl = srv_cfg->getConfigControlInfo();
if (!config_ctl || config_ctl->getConfigDatabases().empty()) {
// No config dbs, nothing to do.
return (false);
}
// First step is to create all of the backends.
for (auto db : config_ctl->getConfigDatabases()) {
// Good place for a log message?
mgr.addBackend(db.getAccessString());
}
return (true);
}
void databaseConfigFetch(const SrvConfigPtr& srv_cfg, ElementPtr /*global_scope*/) {
// Close any existing CB databasess, then open all in srv_cfg (if any)
if (!databaseConfigConnect(srv_cfg)) {
// There are no CB databases so we're done
return;
}
// @todo Fetching and merging the configuration falls under #99
// ConfigBackendDHCPv4Mgr& mgr = ConfigBackendDHCPv4Mgr::instance();
// Next we have to fetch the pieces we care about it and merge them
// probably in this order?
// globals
// shared networks
// subnets
// option defs
// options
}
}; // end of isc::dhcp namespace
}; // end of isc namespace
......@@ -59,6 +59,34 @@ configureDhcp4Server(Dhcpv4Srv&,
isc::data::ConstElementPtr config_set,
bool check_only = false);
/// @brief Attempts to the configured CB databases (if any)
///
/// This function will close all the existing CB backends. It
/// then attempt to connect to all of the CB databases in the
/// given SrvConfig (if any).
///
/// It will return true if there are configured CB databases
/// false, otherwise. Any errors encountered along the way
/// should generate throws.
///
/// @param srv_cfg Server configuration from which to get
/// the config-control information to use.
///
/// @return True if are configured CB databases, false if not.
bool
databaseConfigConnect(const SrvConfigPtr& srv_cfg);
/// @brief Fetch configuration from CB databases and merge it into the given configuration
///
/// It will call @c databaseConfigConnect passing in the given server configuration. If
/// results in open CB databases it will proceed to fetch configuration components from
/// those databases and merge them into the given server configuration.
///
/// @param srv_config Server configuration to merge into
/// @param global_scope global configuration as elements
void
databaseConfigFetch(const SrvConfigPtr& srv_cfg, isc::data::ElementPtr /*global_scope*/);
}; // end of isc::dhcp namespace
}; // end of isc namespace
......
......@@ -25,6 +25,7 @@
#include <dhcpsrv/cfg_hosts.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/testutils/config_result_check.h>
#include <dhcpsrv/testutils/test_config_backend_dhcp4.h>
#include <process/config_ctl_info.h>
#include <hooks/hooks_manager.h>
......@@ -6258,10 +6259,27 @@ TEST_F(Dhcp4ParserTest, globalReservations) {
EXPECT_FALSE(hosts_cfg->get4(542, Host::IDENT_DUID, &duid[0], duid.size()));
}
// This test verifies that configuration control with unsupported type fails
TEST_F(Dhcp4ParserTest, configControlInfoNoFactory) {
string config = PARSER_CONFIGS[6];
extractConfig(config);
// Should fail because "type=mysql" has no factories.
configure(config, CONTROL_RESULT_COMMAND_UNSUPPORTED,
"The type of the configuration backend: 'mysql' is not supported");
}
// This test verifies that configuration control info gets populated.
TEST_F(Dhcp4ParserTest, configControlInfo) {
string config = PARSER_CONFIGS[6];
extractConfig(config);
// Should be able to register a backend factory for "mysql".
ASSERT_TRUE(TestConfigBackendDHCPv4Impl::
registerBackendType(ConfigBackendDHCPv4Mgr::instance(),
"mysql"));
// Should parse ok, now that the factory has been registered.
configure(config, CONTROL_RESULT_SUCCESS, "");
// Make sure the config control info is there.
......@@ -6273,12 +6291,12 @@ TEST_F(Dhcp4ParserTest, configControlInfo) {
const process::ConfigDbInfoList& dblist = info->getConfigDatabases();
ASSERT_EQ(2, dblist.size());
// Make sure the entries are what we expect and in the right order.
// (DbAccessParser creates access strings with the keywords in
// Make sure the entries are what we expect and in the right order.
// (DbAccessParser creates access strings with the keywords in
// alphabetical order).
EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest",
EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest",
dblist.front().getAccessString());
EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest",
EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest",
dblist.back().getAccessString());
}
......
......@@ -19,6 +19,7 @@ libdhcpsrvtest_la_SOURCES += memory_host_data_source.cc memory_host_data_source.
libdhcpsrvtest_la_SOURCES += generic_backend_unittest.cc generic_backend_unittest.h
libdhcpsrvtest_la_SOURCES += generic_host_data_source_unittest.cc generic_host_data_source_unittest.h
libdhcpsrvtest_la_SOURCES += lease_file_io.cc lease_file_io.h
libdhcpsrvtest_la_SOURCES += test_config_backend_dhcp4.cc test_config_backend_dhcp4.h
libdhcpsrvtest_la_CXXFLAGS = $(AM_CXXFLAGS)
libdhcpsrvtest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
......
// 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 <database/database_connection.h>
#include <test_config_backend_dhcp4.h>
namespace isc {
namespace dhcp {
namespace test {
bool
TestConfigBackendDHCPv4Impl::registerBackendType(ConfigBackendDHCPv4Mgr& mgr,
const std::string& db_type) {
return(mgr.registerBackendFactory(db_type,
[](const db::DatabaseConnection::ParameterMap& params)
-> dhcp::ConfigBackendDHCPv4Ptr {
return (TestConfigBackendDHCPv4ImplPtr(new TestConfigBackendDHCPv4Impl(params)));
})
);
}
void
TestConfigBackendDHCPv4Impl::unregisterBackendType(ConfigBackendDHCPv4Mgr& mgr,
const std::string& db_type) {
mgr.unregisterBackendFactory(db_type);
}
Subnet4Ptr
TestConfigBackendDHCPv4Impl::getSubnet4(const db::ServerSelector& /* server_selector */,
const std::string& /* subnet_prefix */) const{
return (Subnet4Ptr());
}
Subnet4Ptr
TestConfigBackendDHCPv4Impl::getSubnet4(const db::ServerSelector& /* server_selector */,
const SubnetID& /* subnet_id */) const {
return (Subnet4Ptr());
}
Subnet4Collection
TestConfigBackendDHCPv4Impl::getAllSubnets4(const db::ServerSelector& /* server_selector */) const {
return(subnets_);
}
Subnet4Collection
TestConfigBackendDHCPv4Impl::getModifiedSubnets4(const db::ServerSelector& /* server_selector */,
const boost::posix_time::ptime& /* modification_time */) const {
return(subnets_);
}
SharedNetwork4Ptr
TestConfigBackendDHCPv4Impl::getSharedNetwork4(const db::ServerSelector& /* server_selector */,
const std::string& /* name */) const {
return(SharedNetwork4Ptr());
}
SharedNetwork4Collection
TestConfigBackendDHCPv4Impl::getAllSharedNetworks4(const db::ServerSelector& /* server_selector */) const{
return(shared_networks_);
}
SharedNetwork4Collection
TestConfigBackendDHCPv4Impl::getModifiedSharedNetworks4(const db::ServerSelector& /* server_selector */,
const boost::posix_time::ptime& /* modification_time */) const {
return(shared_networks_);
}
OptionDefinitionPtr
TestConfigBackendDHCPv4Impl::getOptionDef4(const db::ServerSelector& /* server_selector */,
const uint16_t /* code */,
const std::string& /* space */) const {
return (OptionDefinitionPtr());
}
OptionDefContainer
TestConfigBackendDHCPv4Impl::getAllOptionDefs4(const db::ServerSelector& /* server_selector */) const {
return (option_defs_);
}
OptionDefContainer
TestConfigBackendDHCPv4Impl::getModifiedOptionDefs4(const db::ServerSelector& /* server_selector */,
const boost::posix_time::ptime& /* modification_time */) const {
return (option_defs_);
}
OptionDescriptorPtr
TestConfigBackendDHCPv4Impl::getOption4(const db::ServerSelector& /* server_selector */,
const uint16_t /* code */,
const std::string& /* space */) const {
return (OptionDescriptorPtr());
}
OptionContainer
TestConfigBackendDHCPv4Impl::getAllOptions4(const db::ServerSelector& /* server_selector */) const {
return (options_);
}
OptionContainer
TestConfigBackendDHCPv4Impl::getModifiedOptions4(const db::ServerSelector& /* selector */,
const boost::posix_time::ptime& /* modification_time */) const {
return (options_);
}
data::StampedValuePtr
TestConfigBackendDHCPv4Impl::getGlobalParameter4(const db::ServerSelector& /* selector */,
const std::string& /* name */) const {
return(data::StampedValuePtr());
}
data::StampedValueCollection
TestConfigBackendDHCPv4Impl::getAllGlobalParameters4(const db::ServerSelector& /* selector */) const {
return (globals_);
}
data::StampedValueCollection
TestConfigBackendDHCPv4Impl::getModifiedGlobalParameters4(const db::ServerSelector& /* selector */,
const boost::posix_time::ptime& /* modification_time */) const {
return (globals_);
}
void
TestConfigBackendDHCPv4Impl::createUpdateSubnet4(const db::ServerSelector& /* server_selector */,
const Subnet4Ptr& /* subnet */) {
}
void
TestConfigBackendDHCPv4Impl::createUpdateSharedNetwork4(const db::ServerSelector& /* server_selector */,
const SharedNetwork4Ptr& /* shared_network */) {
}
void
TestConfigBackendDHCPv4Impl::createUpdateOptionDef4(const db::ServerSelector& /* server_selector */,
const OptionDefinitionPtr& /* option_def */) {
}
void
TestConfigBackendDHCPv4Impl::createUpdateOption4(const db::ServerSelector& /* server_selector */,
const OptionDescriptorPtr& /* option */) {
}
void
TestConfigBackendDHCPv4Impl::createUpdateOption4(const db::ServerSelector& /* selector */,
const std::string& /* shared_network_name */,
const OptionDescriptorPtr& /* option */) {
}
void
TestConfigBackendDHCPv4Impl::createUpdateOption4(const db::ServerSelector& /* server_selector */,
const SubnetID& /* subnet_id */,
const OptionDescriptorPtr& /* option */) {
}
void
TestConfigBackendDHCPv4Impl::createUpdateOption4(const db::ServerSelector& /* server_selector */,
const asiolink::IOAddress& /* pool_start_address */,
const asiolink::IOAddress& /* pool_end_address */,
const OptionDescriptorPtr& /* option */) {
}
void
TestConfigBackendDHCPv4Impl::createUpdateGlobalParameter4(const db::ServerSelector& /* server_selector */,
const data::StampedValuePtr& /* value */) {
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteSubnet4(const db::ServerSelector& /* server_selector */,
const std::string& /* subnet_prefix */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteSubnet4(const db::ServerSelector& /* server_selector */,
const SubnetID& /* subnet_id */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteAllSubnets4(const db::ServerSelector& /* server_selector */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteSharedNetwork4(const db::ServerSelector& /* server_selector */,
const std::string& /* name */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteAllSharedNetworks4(const db::ServerSelector& /* server_selector */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteOptionDef4(const db::ServerSelector& /* server_selector */,
const uint16_t /* code */,
const std::string& /* space */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteAllOptionDefs4(const db::ServerSelector& /* server_selector */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteOption4(const db::ServerSelector& /* server_selector */,
const uint16_t /* code */,
const std::string& /* space */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteOption4(const db::ServerSelector& /* server_selector */,
const std::string& /* shared_network_name */,
const uint16_t /* code */,
const std::string& /* space */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteOption4(const db::ServerSelector& /* server_selector */,
const SubnetID& /* subnet_id */,
const uint16_t /* code */,
const std::string& /* space */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteOption4(const db::ServerSelector& /* server_selector */,
const asiolink::IOAddress& /* pool_start_address */,
const asiolink::IOAddress& /* pool_end_address */,
const uint16_t /* code */,
const std::string& /* space */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteGlobalParameter4(const db::ServerSelector& /* server_selector */,
const std::string& /* name */) {
return (0);
}
uint64_t
TestConfigBackendDHCPv4Impl::deleteAllGlobalParameters4(const db::ServerSelector& /* server_selector */) {
return (0);
}
} // namespace test
} // namespace dhcp
} // namespace isc
// 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 <database/database_connection.h>
#include <dhcpsrv/config_backend_dhcp4_mgr.h>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
namespace isc {
namespace dhcp {
namespace test {
/// @brief Base class for implementing fake backends
class TestConfigBackendDHCPv4 : public ConfigBackendDHCPv4 {
public:
/// @brief Constructor
///
/// @param params database connection parameters
/// @throw BadValue if parameters do not include "type"
TestConfigBackendDHCPv4(const db::DatabaseConnection::ParameterMap& params)
: connection_(params) {
try {
db_type_ = connection_.getParameter("type");
} catch (...) {
isc_throw(BadValue, "Backend parameters must include \"type\"");
}
try {
db_type_ = connection_.getParameter("host");
} catch (...) {
host_ = "default_host";
}
try {
port_ = boost::lexical_cast<uint16_t>(connection_.getParameter("host"));
} catch (...) {
port_ = 0;
}
}
/// @brief virtual Destructor.
virtual ~TestConfigBackendDHCPv4(){};
/// @brief Returns backend type.
///
/// @return string db_type name
virtual std::string getType() const {
return (db_type_);
}
/// @brief Returns backend host.
///
/// @return string host
virtual std::string getHost() const {
return (host_);
}
/// @brief Returns backend port.
///
/// @return uint16_t port
virtual uint16_t getPort() const {
return (port_);
}
/// @brief Fake database connection
db::DatabaseConnection connection_;
/// @brief Back end type
std::string db_type_;
/// @brief Back end host
std::string host_;
/// @brief Back end port
uint16_t port_;
};
/// @brief Test backend for that implements all of the DHCPv4 API calls
///
/// Currently all API get calls which return a single entry, will return an
/// empty pointer of appropriate type. API calls which return a collection of
/// entires will return an empty collection of the appropriate type.
///
/// In addition provides static register and unregister methods so it may be
/// registered with a configuration backend manager.
class TestConfigBackendDHCPv4Impl : public TestConfigBackendDHCPv4 {
public:
/// @brief Constructor
///
///
TestConfigBackendDHCPv4Impl(const db::DatabaseConnection::ParameterMap& params)
: TestConfigBackendDHCPv4(params) {
}
/// @brief virtual Destructor.
virtual ~TestConfigBackendDHCPv4Impl(){};
/// @brief Registers the backend type with the given backend manager
///
/// @param mgr configuration manager to register with
/// @brief db_type back end type - Note you will need to
/// use the same value here as you do when creating backend instances.
static bool registerBackendType(ConfigBackendDHCPv4Mgr& mgr,
const std::string& db_type);
/// @brief Unregisters the backend from the given backend manager
///
/// @param mgr configuration manager to unregister from
/// @brief db_type back end type - Note you will need to
/// use the same value here as you do when registering the backend type
static void unregisterBackendType(ConfigBackendDHCPv4Mgr& mgr,
const std::string& db_type);
/// @brief Retrieves a single subnet by subnet_prefix.
///
/// @param server_selector Server selector.
/// @param subnet_prefix Prefix of the subnet to be retrieved.
/// @return Pointer to the retrieved subnet or NULL if not found.
virtual Subnet4Ptr
getSubnet4(const db::ServerSelector& server_selector,
const std::string& subnet_prefix) const;
/// @brief Retrieves a single subnet by subnet identifier.
///
/// @param server_selector Server selector.
/// @param subnet_id Identifier of a subnet to be retrieved.
/// @return Pointer to the retrieved subnet or NULL if not found.
virtual Subnet4Ptr
getSubnet4(const db::ServerSelector& server_selector, const SubnetID& subnet_id) const;
/// @brief Retrieves all subnets.
///
/// @param server_selector Server selector.
/// @return Collection of subnets or empty collection if no subnet found.
virtual Subnet4Collection
getAllSubnets4(const db::ServerSelector& server_selector) const;
/// @brief Retrieves subnets modified after specified time.
///
/// @param server_selector Server selector.
/// @param modification_time Lower bound subnet modification time.
/// @return Collection of subnets or empty collection if no subnet found.
virtual Subnet4Collection
getModifiedSubnets4(const db::ServerSelector& server_selector,