Browse Source

[master] Merge branch 'trac3696'

Marcin Siodelski 9 years ago
parent
commit
0be5e6eb32

+ 13 - 1
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 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
@@ -11,6 +11,7 @@
 #include <hooks/hooks_manager.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_db_access.h>
 #include <config/command_mgr.h>
 #include <stats/stats_mgr.h>
 
@@ -168,6 +169,17 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         return (isc::config::createAnswer(1, err.str()));
     }
 
+    // Re-open lease and host database with new parameters.
+    try {
+        CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
+        cfg_db->setAppendedParameters("universe=4");
+        cfg_db->createManagers();
+
+    } catch (const std::exception& ex) {
+        err << "Unable to open database: " << ex.what();
+        return (isc::config::createAnswer(1, err.str()));
+    }
+
     // Server will start DDNS communications if its enabled.
     try {
         srv->startD2();

+ 3 - 5
src/bin/dhcp4/json_config_parser.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -431,11 +431,9 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
         parser  = new StringParser(config_id,
                                     globalContext()->string_values_);
     } else if (config_id.compare("lease-database") == 0) {
-        parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB,
-                                    *globalContext());
+        parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB);
     } else if (config_id.compare("hosts-database") == 0) {
-        parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB,
-                                    *globalContext());
+        parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB);
     } else if (config_id.compare("hooks-libraries") == 0) {
         parser = new HooksLibrariesParser(config_id);
     } else if (config_id.compare("echo-client-id") == 0) {

+ 16 - 1
src/bin/dhcp4/tests/dhcp4_test_utils.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 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
@@ -32,6 +32,21 @@ namespace isc {
 namespace dhcp {
 namespace test {
 
+BaseServerTest::BaseServerTest()
+    : original_datadir_(CfgMgr::instance().getDataDir()) {
+    CfgMgr::instance().setDataDir(TEST_DATA_BUILDDIR);
+}
+
+BaseServerTest::~BaseServerTest() {
+    // Remove default lease file.
+    std::ostringstream s2;
+    s2 << CfgMgr::instance().getDataDir() << "/" << "kea-leases4.csv";
+    static_cast<void>(::remove(s2.str().c_str()));
+
+    // Revert to original data directory.
+    CfgMgr::instance().setDataDir(original_datadir_);
+}
+
 Dhcpv4SrvTest::Dhcpv4SrvTest()
 :rcode_(-1), srv_(0) {
 

+ 23 - 2
src/bin/dhcp4/tests/dhcp4_test_utils.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 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
@@ -209,7 +209,28 @@ public:
 // dependencies, we use forward declaration here.
 class Dhcp4Client;
 
-class Dhcpv4SrvTest : public ::testing::Test {
+/// @brief Base class for DHCPv4 server testing.
+///
+/// Currently it configures the test data path directory in
+/// the @c CfgMgr. When the object is destroyed, the original
+/// path is reverted.
+class BaseServerTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor.
+    BaseServerTest();
+
+    /// @brief Destructor.
+    virtual ~BaseServerTest();
+
+private:
+
+    /// @brief Holds the original data directory.
+    std::string original_datadir_;
+
+};
+
+class Dhcpv4SrvTest : public BaseServerTest {
 public:
 
     enum ExpectedResult {

+ 174 - 2
src/bin/dhcp4/tests/kea_controller_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 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
@@ -12,9 +12,11 @@
 #include <dhcp/hwaddr.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/testutils/mysql_schema.h>
 #include <log/logger_support.h>
 #include <util/stopwatch.h>
 
@@ -23,6 +25,7 @@
 
 #include <fstream>
 #include <iostream>
+#include <signal.h>
 #include <sstream>
 
 #include <arpa/inet.h>
@@ -34,6 +37,7 @@ using namespace isc::asiolink;
 using namespace isc::config;
 using namespace isc::data;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace isc::hooks;
 
 namespace {
@@ -42,6 +46,7 @@ class NakedControlledDhcpv4Srv: public ControlledDhcpv4Srv {
     // "Naked" DHCPv4 server, exposes internal fields
 public:
     NakedControlledDhcpv4Srv():ControlledDhcpv4Srv(0) { }
+    using ControlledDhcpv4Srv::signal_handler_;
 };
 
 /// @brief test class for Kea configuration backend
@@ -50,7 +55,7 @@ public:
 /// It is very simple and currently focuses on reading
 /// config file from disk. It is expected to be expanded in the
 /// near future.
-class JSONFileBackendTest : public ::testing::Test {
+class JSONFileBackendTest : public isc::dhcp::test::BaseServerTest {
 public:
     JSONFileBackendTest() {
     }
@@ -395,4 +400,171 @@ TEST_F(JSONFileBackendTest, timers) {
     EXPECT_FALSE(lease_reclaimed);
 }
 
+// This test verifies that the server uses default (Memfile) lease database
+// backend when no backend is explicitly specified in the configuration.
+TEST_F(JSONFileBackendTest, defaultLeaseDbBackend) {
+    // This is basic server configuration which excludes lease database
+    // backend specification. The default Memfile backend should be
+    // initialized in this case.
+    string config =
+        "{ \"Dhcp4\": {"
+        "\"interfaces-config\": {"
+        "    \"interfaces\": [ ]"
+        "},"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, \n"
+        "\"subnet4\": [ ],"
+        "\"valid-lifetime\": 4000 }"
+        "}";
+    writeFile(config);
+
+    // Create an instance of the server and intialize it.
+    boost::scoped_ptr<ControlledDhcpv4Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new ControlledDhcpv4Srv(0)));
+    ASSERT_NO_THROW(srv->init(TEST_FILE));
+
+    // The backend should have been created.
+    EXPECT_NO_THROW(static_cast<void>(LeaseMgrFactory::instance()));
+}
+
+// Starting tests which require MySQL backend availability. Those tests
+// will not be executed if Kea has been compiled without the
+// --with-dhcp-mysql.
+#ifdef HAVE_MYSQL
+
+/// @brief Test fixture class for the tests utilizing MySQL database
+/// backend.
+class JSONFileBackendMySQLTest : public JSONFileBackendTest {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Recreates MySQL schema for a test.
+    JSONFileBackendMySQLTest() : JSONFileBackendTest() {
+        destroyMySQLSchema();
+        createMySQLSchema();
+    }
+
+    /// @brief Destructor.
+    ///
+    /// Destroys MySQL schema.
+    virtual ~JSONFileBackendMySQLTest() {
+        destroyMySQLSchema();
+    }
+
+    /// @brief Creates server configuration with specified backend type.
+    ///
+    /// @param backend Backend type or empty string to indicate that the
+    /// backend configuration should not be placed in the resulting
+    /// JSON configuration.
+    ///
+    /// @return Server configuration.
+    std::string createConfiguration(const std::string& backend) const;
+
+    /// @brief Test reconfiguration with a backend change.
+    ///
+    /// If any of the parameters is an empty string it indicates that the
+    /// created configuration should exclude backend configuration.
+    ///
+    /// @param backend_first Type of a backend to be used initially.
+    /// @param backend_second Type of a backend to be used after
+    /// reconfiguration.
+    void testBackendReconfiguration(const std::string& backend_first,
+                                    const std::string& backend_second);
+};
+
+std::string
+JSONFileBackendMySQLTest::createConfiguration(const std::string& backend) const {
+    // This is basic server configuration which excludes lease database
+    // backend specification. The default Memfile backend should be
+    // initialized in this case.
+    std::ostringstream config;
+    config <<
+        "{ \"Dhcp4\": {"
+        "\"interfaces-config\": {"
+        "    \"interfaces\": [ ]"
+        "},";
+
+    // For non-empty lease backend type we have to add a backend configuration
+    // section.
+    if (!backend.empty()) {
+        config <<
+        "\"lease-database\": {"
+        "     \"type\": \"" << backend << "\"";
+
+        // SQL backends require database credentials.
+        if (backend != "memfile") {
+            config <<
+                ","
+                "     \"name\": \"keatest\","
+                "     \"user\": \"keatest\","
+                "     \"password\": \"keatest\"";
+        }
+        config << "},";
+    }
+
+    // Append the rest of the configuration.
+    config <<
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, \n"
+        "\"subnet4\": [ ],"
+        "\"valid-lifetime\": 4000 }"
+        "}";
+
+    return (config.str());
+}
+
+void
+JSONFileBackendMySQLTest::
+testBackendReconfiguration(const std::string& backend_first,
+                           const std::string& backend_second) {
+    writeFile(createConfiguration(backend_first));
+
+    // Create an instance of the server and intialize it.
+    boost::scoped_ptr<NakedControlledDhcpv4Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new NakedControlledDhcpv4Srv()));
+    srv->setConfigFile(TEST_FILE);
+    ASSERT_NO_THROW(srv->init(TEST_FILE));
+
+    // The backend should have been created and its type should be
+    // correct.
+    ASSERT_NO_THROW(static_cast<void>(LeaseMgrFactory::instance()));
+    EXPECT_EQ(backend_first.empty() ? "memfile" : backend_first,
+              LeaseMgrFactory::instance().getType());
+
+    // New configuration modifies the lease database backend type.
+    writeFile(createConfiguration(backend_second));
+
+    // Explicitly calling signal handler for SIGHUP to trigger server
+    // reconfiguration.
+    srv->signal_handler_(SIGHUP);
+
+    // The backend should have been created and its type should be
+    // correct.
+    ASSERT_NO_THROW(static_cast<void>(LeaseMgrFactory::instance()));
+    EXPECT_EQ(backend_second.empty() ? "memfile" : backend_second,
+              LeaseMgrFactory::instance().getType());
+}
+
+
+// This test verifies that backend specification can be added on
+// server reconfiguration.
+TEST_F(JSONFileBackendMySQLTest, reconfigureBackendUndefinedToMySQL) {
+    testBackendReconfiguration("", "mysql");
+}
+
+// This test verifies that when backend specification is removed the
+// default backend is used.
+TEST_F(JSONFileBackendMySQLTest, reconfigureBackendMySQLToUndefined) {
+    testBackendReconfiguration("mysql", "");
+}
+
+// This test verifies that backend type can be changed from Memfile
+// to MySQL.
+TEST_F(JSONFileBackendMySQLTest, reconfigureBackendMemfileToMySQL) {
+    testBackendReconfiguration("memfile", "mysql");
+}
+
+#endif
+
 } // End of anonymous namespace

+ 13 - 1
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 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
@@ -9,6 +9,7 @@
 #include <config/command_mgr.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_db_access.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/json_config_parser.h>
@@ -173,6 +174,17 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
                                           + string(ex.what())));
     }
 
+    // Re-open lease and host database with new parameters.
+    try {
+        CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
+        cfg_db->setAppendedParameters("universe=6");
+        cfg_db->createManagers();
+
+    } catch (const std::exception& ex) {
+        return (isc::config::createAnswer(1, "Unable to open database: "
+                                          + std::string(ex.what())));
+    }
+
     // Regenerate server identifier if needed.
     try {
         const std::string duid_file = CfgMgr::instance().getDataDir() + "/" +

+ 3 - 5
src/bin/dhcp6/json_config_parser.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -682,11 +682,9 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
         parser  = new StringParser(config_id,
                                    globalContext()->string_values_);
     } else if (config_id.compare("lease-database") == 0) {
-        parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB,
-                                    *globalContext());
+        parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB);
     } else if (config_id.compare("hosts-database") == 0) {
-        parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB,
-                                    *globalContext());
+        parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB);
     } else if (config_id.compare("hooks-libraries") == 0) {
         parser = new HooksLibrariesParser(config_id);
     } else if (config_id.compare("dhcp-ddns") == 0) {

+ 7 - 1
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 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
@@ -38,6 +38,12 @@ BaseServerTest::~BaseServerTest() {
     std::ostringstream s;
     s << CfgMgr::instance().getDataDir() << "/" << DUID_FILE;
     static_cast<void>(::remove(s.str().c_str()));
+
+    // Remove default lease file.
+    std::ostringstream s2;
+    s2 << CfgMgr::instance().getDataDir() << "/" << "kea-leases6.csv";
+    static_cast<void>(::remove(s2.str().c_str()));
+
     // Revert to original data directory.
     CfgMgr::instance().setDataDir(original_datadir_);
 }

+ 173 - 1
src/bin/dhcp6/tests/kea_controller_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -16,6 +16,7 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/testutils/mysql_schema.h>
 #include <log/logger_support.h>
 #include <util/stopwatch.h>
 
@@ -35,6 +36,7 @@ using namespace isc::asiolink;
 using namespace isc::config;
 using namespace isc::data;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace isc::hooks;
 
 namespace {
@@ -43,6 +45,7 @@ class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
     // "Naked" DHCPv6 server, exposes internal fields
 public:
     NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(0) { }
+    using ControlledDhcpv6Srv::signal_handler_;
 };
 
 
@@ -378,4 +381,173 @@ TEST_F(JSONFileBackendTest, serverId) {
     EXPECT_EQ(1234, duid_cfg->getEnterpriseId());
 }
 
+// This test verifies that the server uses default (Memfile) lease database
+// backend when no backend is explicitly specified in the configuration.
+TEST_F(JSONFileBackendTest, defaultLeaseDbBackend) {
+    // This is basic server configuration which excludes lease database
+    // backend specification. The default Memfile backend should be
+    // initialized in this case.
+    string config =
+        "{ \"Dhcp6\": {"
+        "\"interfaces-config\": {"
+        "    \"interfaces\": [ ]"
+        "},"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, \n"
+        "\"subnet6\": [ ],"
+        "\"preferred-lifetime\": 3000, "
+        "\"valid-lifetime\": 4000 }"
+        "}";
+    writeFile(TEST_FILE, config);
+
+    // Create an instance of the server and intialize it.
+    boost::scoped_ptr<ControlledDhcpv6Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new ControlledDhcpv6Srv(0)));
+    ASSERT_NO_THROW(srv->init(TEST_FILE));
+
+    // The backend should have been created.
+    EXPECT_NO_THROW(static_cast<void>(LeaseMgrFactory::instance()));
+}
+
+// Starting tests which require MySQL backend availability. Those tests
+// will not be executed if Kea has been compiled without the
+// --with-dhcp-mysql.
+#ifdef HAVE_MYSQL
+
+/// @brief Test fixture class for the tests utilizing MySQL database
+/// backend.
+class JSONFileBackendMySQLTest : public JSONFileBackendTest {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Recreates MySQL schema for a test.
+    JSONFileBackendMySQLTest() : JSONFileBackendTest() {
+        destroyMySQLSchema();
+        createMySQLSchema();
+    }
+
+    /// @brief Destructor.
+    ///
+    /// Destroys MySQL schema.
+    virtual ~JSONFileBackendMySQLTest() {
+        destroyMySQLSchema();
+    }
+
+    /// @brief Creates server configuration with specified backend type.
+    ///
+    /// @param backend Backend type or empty string to indicate that the
+    /// backend configuration should not be placed in the resulting
+    /// JSON configuration.
+    ///
+    /// @return Server configuration.
+    std::string createConfiguration(const std::string& backend) const;
+
+    /// @brief Test reconfiguration with a backend change.
+    ///
+    /// If any of the parameters is an empty string it indicates that the
+    /// created configuration should exclude backend configuration.
+    ///
+    /// @param backend_first Type of a backend to be used initially.
+    /// @param backend_second Type of a backend to be used after
+    /// reconfiguration.
+    void testBackendReconfiguration(const std::string& backend_first,
+                                    const std::string& backend_second);
+};
+
+std::string
+JSONFileBackendMySQLTest::createConfiguration(const std::string& backend) const {
+    // This is basic server configuration which excludes lease database
+    // backend specification. The default Memfile backend should be
+    // initialized in this case.
+    std::ostringstream config;
+    config <<
+        "{ \"Dhcp6\": {"
+        "\"interfaces-config\": {"
+        "    \"interfaces\": [ ]"
+        "},";
+
+    // For non-empty lease backend type we have to add a backend configuration
+    // section.
+    if (!backend.empty()) {
+        config <<
+        "\"lease-database\": {"
+        "     \"type\": \"" << backend << "\"";
+
+        // SQL backends require database credentials.
+        if (backend != "memfile") {
+            config <<
+                ","
+                "     \"name\": \"keatest\","
+                "     \"user\": \"keatest\","
+                "     \"password\": \"keatest\"";
+        }
+        config << "},";
+    }
+
+    // Append the rest of the configuration.
+    config <<
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, \n"
+        "\"subnet6\": [ ],"
+        "\"preferred-lifetime\": 3000, "
+        "\"valid-lifetime\": 4000 }"
+        "}";
+
+    return (config.str());
+}
+
+void
+JSONFileBackendMySQLTest::
+testBackendReconfiguration(const std::string& backend_first,
+                           const std::string& backend_second) {
+    writeFile(TEST_FILE, createConfiguration(backend_first));
+
+    // Create an instance of the server and intialize it.
+    boost::scoped_ptr<NakedControlledDhcpv6Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new NakedControlledDhcpv6Srv()));
+    srv->setConfigFile(TEST_FILE);
+    ASSERT_NO_THROW(srv->init(TEST_FILE));
+
+    // The backend should have been created and its type should be
+    // correct.
+    ASSERT_NO_THROW(static_cast<void>(LeaseMgrFactory::instance()));
+    EXPECT_EQ(backend_first.empty() ? "memfile" : backend_first,
+              LeaseMgrFactory::instance().getType());
+
+    // New configuration modifies the lease database backend type.
+    writeFile(TEST_FILE, createConfiguration(backend_second));
+
+    // Explicitly calling signal handler for SIGHUP to trigger server
+    // reconfiguration.
+    srv->signal_handler_(SIGHUP);
+
+    // The backend should have been created and its type should be
+    // correct.
+    ASSERT_NO_THROW(static_cast<void>(LeaseMgrFactory::instance()));
+    EXPECT_EQ(backend_second.empty() ? "memfile" : backend_second,
+              LeaseMgrFactory::instance().getType());
+}
+
+
+// This test verifies that backend specification can be added on
+// server reconfiguration.
+TEST_F(JSONFileBackendMySQLTest, reconfigureBackendUndefinedToMySQL) {
+    testBackendReconfiguration("", "mysql");
+}
+
+// This test verifies that when backend specification is removed the
+// default backend is used.
+TEST_F(JSONFileBackendMySQLTest, reconfigureBackendMySQLToUndefined) {
+    testBackendReconfiguration("mysql", "");
+}
+
+// This test verifies that backend type can be changed from Memfile
+// to MySQL.
+TEST_F(JSONFileBackendMySQLTest, reconfigureBackendMemfileToMySQL) {
+    testBackendReconfiguration("memfile", "mysql");
+}
+
+#endif
+
 } // End of anonymous namespace

+ 1 - 0
src/lib/dhcpsrv/Makefile.am

@@ -85,6 +85,7 @@ libkea_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
 libkea_dhcpsrv_la_SOURCES += alloc_engine_log.cc alloc_engine_log.h
 libkea_dhcpsrv_la_SOURCES += base_host_data_source.h
 libkea_dhcpsrv_la_SOURCES += callout_handle_store.h
+libkea_dhcpsrv_la_SOURCES += cfg_db_access.cc cfg_db_access.h
 libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h
 libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h
 libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h

+ 64 - 0
src/lib/dhcpsrv/cfg_db_access.cc

@@ -0,0 +1,64 @@
+// Copyright (C) 2016 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 <dhcpsrv/cfg_db_access.h>
+#include <dhcpsrv/host_data_source_factory.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <sstream>
+
+namespace isc {
+namespace dhcp {
+
+CfgDbAccess::CfgDbAccess()
+    : appended_parameters_(), lease_db_access_("type=memfile"),
+      host_db_access_() {
+}
+
+std::string
+CfgDbAccess::getLeaseDbAccessString() const {
+    return (getAccessString(lease_db_access_));
+}
+
+
+std::string
+CfgDbAccess::getHostDbAccessString() const {
+    return (getAccessString(host_db_access_));
+}
+
+
+void
+CfgDbAccess::createManagers() const {
+    // Recreate lease manager.
+    LeaseMgrFactory::destroy();
+    LeaseMgrFactory::create(getLeaseDbAccessString());
+
+    // Recreate host data source.
+    HostDataSourceFactory::destroy();
+    if (!host_db_access_.empty()) {
+        HostDataSourceFactory::create(getHostDbAccessString());
+    }
+}
+
+std::string 
+CfgDbAccess::getAccessString(const std::string& access_string) const {
+    std::ostringstream s;
+    s << access_string;
+    // Only append additional parameters if any parameters are specified
+    // in a configuration. For host database, no parameters mean that
+    // database access is disabled and thus we don't want to append any
+    // parameters.
+    if ((s.tellp() != std::streampos(0)) && (!appended_parameters_.empty())) {
+        s << " " << appended_parameters_;
+    }
+
+    return (s.str());
+}
+
+
+
+} // end of isc::dhcp namespace
+} // end of isc namespace

+ 94 - 0
src/lib/dhcpsrv/cfg_db_access.h

@@ -0,0 +1,94 @@
+// Copyright (C) 2016 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 CFG_DBACCESS_H
+#define CFG_DBACCESS_H
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Holds access parameters and the configuration of the
+/// lease and hosts database connection.
+///
+/// The database access strings use the same format as the strings
+/// passed to the @ref isc::dhcp::LeaseMgrFactory::create function.
+class CfgDbAccess {
+public:
+
+    /// @brief Constructor.
+    CfgDbAccess();
+
+    /// @brief Sets parameters which will be appended to the database
+    /// access strings.
+    ///
+    /// @param appended_parameters String holding collection of parameters
+    /// in the following format: "parameter0=value0 parameter1=value1 ...".
+    void setAppendedParameters(const std::string& appended_parameters) {
+        appended_parameters_ = appended_parameters;
+    }
+
+    /// @brief Retrieves lease database access string.
+    ///
+    /// @return Lease database access string with additional parameters
+    /// specified with @ref CfgDbAccess::setAppendedParameters.
+    std::string getLeaseDbAccessString() const;
+
+    /// @brief Sets lease database access string.
+    ///
+    /// @param lease_db_access New lease database access string.
+    void setLeaseDbAccessString(const std::string& lease_db_access) {
+        lease_db_access_ = lease_db_access;
+    }
+
+    /// @brief Retrieves host database access string.
+    ///
+    /// @return Host database access string with additional parameters
+    /// specified with @ref CfgDbAccess::setAppendedParameters.
+    std::string getHostDbAccessString() const;
+
+    /// @brief Sets host database access string.
+    ///
+    /// @param host_db_access New host database access string.
+    void setHostDbAccessString(const std::string& host_db_access) {
+        host_db_access_ = host_db_access;
+    }
+
+    /// @brief Creates instance of @ref LeaseMgr @ref HostDataSource
+    /// according to the configuration specified.
+    void createManagers() const;
+
+private:
+
+    /// @brief Returns lease or host database access string.
+    ///
+    /// @param Access string without additional (appended) parameters.
+    std::string getAccessString(const std::string& access_string) const;
+
+    /// @brief Parameters to be appended to the database access
+    /// strings.
+    std::string appended_parameters_;
+
+    /// @brief Holds lease database access string.
+    std::string lease_db_access_;
+
+    /// @brief Holds host database access string.
+    std::string host_db_access_;
+
+};
+
+/// @brief A pointer to the @c CfgDbAccess.
+typedef boost::shared_ptr<CfgDbAccess> CfgDbAccessPtr;
+
+/// @brief A pointer to the const @c CfgDbAccess.
+typedef boost::shared_ptr<const CfgDbAccess> ConstCfgDbAccessPtr;
+
+}
+}
+
+#endif // CFG_DBACCESS_H

+ 9 - 1
src/lib/dhcpsrv/database_connection.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -36,6 +36,14 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Invalid type exception
+///
+/// Thrown when the factory doesn't recognize the type of the backend.
+class InvalidType : public Exception {
+public:
+    InvalidType(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
 
 /// @brief Common database connection class.
 ///

+ 1 - 10
src/lib/dhcpsrv/host_data_source_factory.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -17,15 +17,6 @@
 namespace isc {
 namespace dhcp {
 
-/// @brief Invalid type exception
-///
-/// Thrown when the factory doesn't recognise the type of the backend.
-class InvalidType : public Exception {
-public:
-    InvalidType(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
 /// @brief No host data source instance exception
 ///
 /// Thrown if an attempt is made to get a reference to the current

+ 1 - 9
src/lib/dhcpsrv/lease_mgr_factory.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -18,14 +18,6 @@
 namespace isc {
 namespace dhcp {
 
-/// @brief Invalid type exception
-///
-/// Thrown when the factory doesn't recognize the type of the backend.
-class InvalidType : public Exception {
-public:
-    InvalidType(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
 
 /// @brief No lease manager exception
 ///

+ 2 - 4
src/lib/dhcpsrv/memfile_lease_mgr.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -777,9 +777,7 @@ Memfile_LeaseMgr::deleteExpiredReclaimedLeases(const uint32_t secs,
 
 std::string
 Memfile_LeaseMgr::getDescription() const {
-    return (std::string("This is a dummy memfile backend implementation.\n"
-                        "It does not offer any useful lease management and its only\n"
-                        "purpose is to test abstract lease manager API."));
+    return (std::string("In memory database with leases stored in a CSV file."));
 }
 
 void

+ 23 - 41
src/lib/dhcpsrv/parsers/dbaccess_parser.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -7,6 +7,8 @@
 #include <config.h>
 
 #include <dhcp/option.h>
+#include <dhcpsrv/cfg_db_access.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/host_mgr.h>
@@ -27,10 +29,8 @@ namespace dhcp {
 
 
 // Factory function to build the parser
-DbAccessParser::DbAccessParser(const std::string&, DBType db_type,
-                               const ParserContext& ctx)
-    : values_(), type_(db_type), ctx_(ctx)
-{
+DbAccessParser::DbAccessParser(const std::string&, DBType db_type)
+    : values_(), type_(db_type) {
 }
 
 // Parse the configuration and check that the various keywords are consistent.
@@ -39,21 +39,17 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
 
     // To cope with incremental updates, the strategy is:
     // 1. Take a copy of the stored keyword/value pairs.
-    // 2. Inject the universe parameter.
-    // 3. Update the copy with the passed keywords.
-    // 4. Perform validation checks on the updated keyword/value pairs.
-    // 5. If all is OK, update the stored keyword/value pairs.
+    // 2. Update the copy with the passed keywords.
+    // 3. Perform validation checks on the updated keyword/value pairs.
+    // 4. If all is OK, update the stored keyword/value pairs.
+    // 5. Save resulting database access string in the Configuration
+    // Manager.
 
     // 1. Take a copy of the stored keyword/value pairs.
     std::map<string, string> values_copy = values_;
 
-    // 2. Inject the parameter which defines whether we are configuring
-    // DHCPv4 or DHCPv6. Some database backends (e.g. Memfile make
-    // use of it).
-    values_copy["universe"] = ctx_.universe_ == Option::V4 ? "4" : "6";
-
     int64_t lfc_interval = 0;
-    // 3. Update the copy with the passed keywords.
+    // 2. Update the copy with the passed keywords.
     BOOST_FOREACH(ConfigPair param, config_value->mapValue()) {
         try {
             if (param.first == "persist") {
@@ -75,7 +71,7 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
         }
     }
 
-    // 4. Perform validation checks on the updated set of keyword/values.
+    // 3. Perform validation checks on the updated set of keyword/values.
     //
     // a. Check if the "type" keyword exists and thrown an exception if not.
     StringPairMap::const_iterator type_ptr = values_copy.find("type");
@@ -100,11 +96,21 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
                   << std::numeric_limits<uint32_t>::max());
     }
 
-    // 5. If all is OK, update the stored keyword/value pairs.  We do this by
+    // 4. If all is OK, update the stored keyword/value pairs.  We do this by
     // swapping contents - values_copy is destroyed immediately after the
     // operation (when the method exits), so we are not interested in its new
     // value.
     values_.swap(values_copy);
+
+    // 5. Save the database access string in the Configuration Manager.
+    CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
+    if (type_ == LEASE_DB) {
+        cfg_db->setLeaseDbAccessString(getDbAccessString());
+
+    } else {
+        cfg_db->setHostDbAccessString(getDbAccessString());
+    }
+
 }
 
 // Create the database access string
@@ -133,30 +139,6 @@ DbAccessParser::getDbAccessString() const {
 // Commit the changes - reopen the database with the new parameters
 void
 DbAccessParser::commit() {
-
-    switch (type_) {
-    case LEASE_DB:
-    {
-        // Close current lease manager database.
-        LeaseMgrFactory::destroy();
-
-        // ... and open the new database using the access string.
-        LeaseMgrFactory::create(getDbAccessString());
-        break;
-    }
-    case HOSTS_DB:
-    {
-        // Let's instantiate HostMgr with new parameters. Note that HostMgr's
-        // create method will call HostDataSourceFactory::create() with
-        // appropriate parameters. It will also destroy a pre-existing
-        // instance, if it existed.
-        HostMgr::create(getDbAccessString());
-        break;
-    }
-    default:
-        isc_throw(BadValue, "Incorrect type specified in DbAccessParser: "
-                  << type_);
-    };
 }
 
 };  // namespace dhcp

+ 6 - 18
src/lib/dhcpsrv/parsers/dbaccess_parser.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -55,9 +55,7 @@ public:
     /// @param param_name Name of the parameter under which the database
     ///        access details are held.
     /// @param db_type Specifies database type (lease or hosts)
-    /// @param ctx Parser context.
-    DbAccessParser(const std::string& param_name, DBType db_type,
-                   const ParserContext& ctx);
+    DbAccessParser(const std::string& param_name, DBType db_type);
 
     /// The destructor.
     virtual ~DbAccessParser()
@@ -83,13 +81,7 @@ public:
     ///        the list of database access keywords.
     virtual void build(isc::data::ConstElementPtr config_value);
 
-    /// @brief Apply the prepared configuration value to the server.
-    ///
-    /// With the string validated, this closes the currently open database (if
-    /// any), then opens a database corresponding to the stored string.
-    ///
-    /// This method is expected to be called after \c build(), and only once.
-    /// The result is undefined otherwise.
+    /// @brief This method is no-op.
     virtual void commit();
 
     /// @brief Factory method to create parser
@@ -98,16 +90,14 @@ public:
     ///
     /// @param param_name Name of the parameter used to access the
     ///         configuration.
-    /// @param ctx Parser context.
     ///
     /// @return Pointer to a DbAccessParser.  The caller is responsible for
     ///         destroying the parser after use.
-    static DhcpConfigParser* factory(const std::string& param_name,
-                                     const ParserContext& ctx) {
+    static DhcpConfigParser* factory(const std::string& param_name) {
         if (param_name == "lease-database") {
-            return (new DbAccessParser(param_name, DbAccessParser::LEASE_DB, ctx));
+            return (new DbAccessParser(param_name, DbAccessParser::LEASE_DB));
         } else if (param_name == "hosts-database") {
-            return (new DbAccessParser(param_name, DbAccessParser::HOSTS_DB, ctx));
+            return (new DbAccessParser(param_name, DbAccessParser::HOSTS_DB));
         } else {
             isc_throw(BadValue, "Unexpected parameter name (" << param_name
                       << ") passed to DbAccessParser::factory");
@@ -139,8 +129,6 @@ private:
     std::map<std::string, std::string> values_; ///< Stored parameter values
 
     DBType type_; ///< Database type (leases or hosts)
-
-    ParserContext ctx_; ///< Parser context
 };
 
 };  // namespace dhcp

+ 3 - 1
src/lib/dhcpsrv/srv_config.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 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
@@ -24,6 +24,7 @@ SrvConfig::SrvConfig()
       cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
       cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
       cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
+      cfg_db_access_(new CfgDbAccess()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0) {
 }
@@ -34,6 +35,7 @@ SrvConfig::SrvConfig(const uint32_t sequence)
       cfg_subnets4_(new CfgSubnets4()), cfg_subnets6_(new CfgSubnets6()),
       cfg_hosts_(new CfgHosts()), cfg_rsoo_(new CfgRSOO()),
       cfg_expiration_(new CfgExpiration()), cfg_duid_(new CfgDUID()),
+      cfg_db_access_(new CfgDbAccess()),
       class_dictionary_(new ClientClassDictionary()),
       decline_timer_(0) {
 }

+ 18 - 1
src/lib/dhcpsrv/srv_config.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 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
@@ -7,6 +7,7 @@
 #ifndef DHCPSRV_CONFIG_H
 #define DHCPSRV_CONFIG_H
 
+#include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_duid.h>
 #include <dhcpsrv/cfg_expiration.h>
 #include <dhcpsrv/cfg_hosts.h>
@@ -278,6 +279,18 @@ public:
         return (cfg_duid_);
     }
 
+    /// @brief Returns pointer to the object holding configuration of the
+    /// lease and host database connection parameters.
+    CfgDbAccessPtr getCfgDbAccess() {
+        return (cfg_db_access_);
+    }
+
+    /// @brief Returns const pointer to the object holding configuration of
+    /// the lease and host database connection parameters.
+    ConstCfgDbAccessPtr getCfgDbAccess() const {
+        return (cfg_db_access_);
+    }
+
     //@}
 
     /// @brief Returns non-const reference to an array that stores
@@ -485,6 +498,10 @@ private:
     /// @brief Pointer to the configuration of the server identifier.
     CfgDUIDPtr cfg_duid_;
 
+    /// @brief Pointer to the configuration of the lease and host database
+    /// connection parameters.
+    CfgDbAccessPtr cfg_db_access_;
+
     /// @brief Pointer to the control-socket information
     isc::data::ConstElementPtr control_socket_;
 

+ 1 - 2
src/lib/dhcpsrv/tests/Makefile.am

@@ -65,6 +65,7 @@ libdhcpsrv_unittests_SOURCES += alloc_engine_hooks_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine4_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine6_unittest.cc
 libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
+libdhcpsrv_unittests_SOURCES += cfg_db_access_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_duid_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_expiration_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_hosts_unittest.cc
@@ -104,7 +105,6 @@ libdhcpsrv_unittests_SOURCES += generic_host_data_source_unittest.cc generic_hos
 libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc
 if HAVE_MYSQL
-libdhcpsrv_unittests_SOURCES += mysql_schema.cc mysql_schema.h
 libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += mysql_host_data_source_unittest.cc
 endif
@@ -115,7 +115,6 @@ if HAVE_PGSQL
 libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc
 endif
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
-libdhcpsrv_unittests_SOURCES += schema_mysql_copy.h
 libdhcpsrv_unittests_SOURCES += schema_pgsql_copy.h
 libdhcpsrv_unittests_SOURCES += srv_config_unittest.cc
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc

+ 117 - 0
src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc

@@ -0,0 +1,117 @@
+// Copyright (C) 2016 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 <dhcpsrv/cfg_db_access.h>
+#include <dhcpsrv/host_data_source_factory.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/testutils/mysql_schema.h>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+// Test default values of the lease and host database access strings.
+TEST(CfgDbAccessTest, defaults) {
+    CfgDbAccess cfg;
+    EXPECT_EQ("type=memfile", cfg.getLeaseDbAccessString());
+    EXPECT_TRUE(cfg.getHostDbAccessString().empty());
+}
+
+// This test verifies that it is possible to set the lease database
+// string.
+TEST(CfgDbAccessTest, setLeaseDbAccessString) {
+    CfgDbAccess cfg;
+    ASSERT_NO_THROW(cfg.setLeaseDbAccessString("type=mysql"));
+    EXPECT_EQ("type=mysql", cfg.getLeaseDbAccessString());
+
+    // Append additional parameter.
+    ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4"));
+    EXPECT_EQ("type=mysql universe=4", cfg.getLeaseDbAccessString());
+
+    // If access string is empty, no parameters will be appended.
+    ASSERT_NO_THROW(cfg.setLeaseDbAccessString(""));
+    EXPECT_TRUE(cfg.getLeaseDbAccessString().empty());
+}
+
+
+// This test verifies that it is possible to set the host database
+// string.
+TEST(CfgDbAccessTest, setHostDbAccessString) {
+    CfgDbAccess cfg;
+    ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql"));
+    EXPECT_EQ("type=mysql", cfg.getHostDbAccessString());
+
+    // Append additional parameter.
+    ASSERT_NO_THROW(cfg.setAppendedParameters("universe=4"));
+    EXPECT_EQ("type=mysql universe=4", cfg.getHostDbAccessString());
+
+    // If access string is empty, no parameters will be appended.
+    ASSERT_NO_THROW(cfg.setHostDbAccessString(""));
+    EXPECT_TRUE(cfg.getHostDbAccessString().empty());
+}
+
+// Tests that lease manager can be created from a specified configuration.
+TEST(CfgDbAccessTest, createLeaseMgr) {
+    CfgDbAccess cfg;
+    ASSERT_NO_THROW(cfg.setLeaseDbAccessString("type=memfile persist=false universe=4"));
+    ASSERT_NO_THROW(cfg.createManagers());
+
+    ASSERT_NO_THROW({
+        LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+        EXPECT_EQ("memfile",lease_mgr.getType());
+    });
+}
+
+// The following tests require MySQL enabled.
+#if defined HAVE_MYSQL
+
+/// @brief Test fixture class for testing @ref CfgDbAccessTest using MySQL
+/// backend.
+class CfgMySQLDbAccessTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor.
+    CfgMySQLDbAccessTest() {
+        // Ensure schema is the correct one.
+        destroyMySQLSchema();
+        createMySQLSchema();
+    }
+
+    /// @brief Destructor.
+    virtual ~CfgMySQLDbAccessTest() {
+        destroyMySQLSchema();
+    }
+};
+
+
+// Tests that MySQL lease manager and host data source can be created from a
+// specified configuration.
+TEST_F(CfgMySQLDbAccessTest, createManagers) {
+    CfgDbAccess cfg;
+    ASSERT_NO_THROW(cfg.setLeaseDbAccessString(validMySQLConnectionString()));
+    ASSERT_NO_THROW(cfg.setHostDbAccessString(validMySQLConnectionString()));
+    ASSERT_NO_THROW(cfg.createManagers());
+
+    ASSERT_NO_THROW({
+        LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+        EXPECT_EQ("mysql", lease_mgr.getType());
+    });
+
+    ASSERT_NO_THROW({
+        HostDataSourcePtr& host_data_source =
+            HostDataSourceFactory::getHostDataSourcePtr();
+        EXPECT_EQ("mysql", host_data_source->getType());
+    });
+}
+
+#endif
+
+} // end of anonymous namespace

+ 23 - 128
src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -9,7 +9,7 @@
 #include <cc/command_interpreter.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/parsers/dbaccess_parser.h>
-#include <dhcpsrv/tests/mysql_schema.h>
+#include <dhcpsrv/testutils/mysql_schema.h>
 #include <dhcpsrv/host_mgr.h>
 #include <log/logger_support.h>
 
@@ -123,18 +123,14 @@ public:
     /// @param dbaccess set of database access parameters to check
     /// @param keyval Array of "const char*" strings in the order keyword,
     ///        value, keyword, value ...  A NULL entry terminates the list.
-    /// @param u Universe (V4 or V6).
     void checkAccessString(const char* trace_string,
                            const DbAccessParser::StringPairMap& parameters,
-                           const char* keyval[],
-                           Option::Universe u = Option::V4) {
+                           const char* keyval[]) {
         SCOPED_TRACE(trace_string);
 
         // Construct a map of keyword value pairs.
         std::map<string, string> expected;
-        expected["universe"] = (u == Option::V4 ? "4" : "6");
-        // The universe is always injected by the parser itself.
-        size_t expected_count = 1;
+        size_t expected_count = 0;
         for (size_t i = 0; keyval[i] != NULL; i += 2) {
             // Get the value.  This should not be NULL
             ASSERT_NE(static_cast<const char*>(NULL), keyval[i + 1]) <<
@@ -195,10 +191,9 @@ public:
 
     /// @brief Constructor
     ///
-    /// @brief Keyword/value collection of ddatabase access parameters
-    TestDbAccessParser(const std::string& param_name, DbAccessParser::DBType type,
-                       const ParserContext& ctx)
-        : DbAccessParser(param_name, type, ctx)
+    /// @brief Keyword/value collection of database access parameters
+    TestDbAccessParser(const std::string& param_name, DbAccessParser::DBType type) 
+        : DbAccessParser(param_name, type)
     {}
 
     /// @brief Destructor
@@ -239,8 +234,7 @@ TEST_F(DbAccessParserTest, validTypeMemfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
 }
@@ -256,8 +250,7 @@ TEST_F(DbAccessParserTest, emptyKeyword) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
 }
@@ -274,8 +267,7 @@ TEST_F(DbAccessParserTest, persistV4Memfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_NO_THROW(parser.build(json_elements));
 
     checkAccessString("Valid memfile", parser.getDbAccessParameters(),
@@ -294,12 +286,11 @@ TEST_F(DbAccessParserTest, persistV6Memfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V6));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_NO_THROW(parser.build(json_elements));
 
     checkAccessString("Valid memfile", parser.getDbAccessParameters(),
-                      config, Option::V6);
+                      config);
 }
 
 // This test checks that the parser accepts the valid value of the
@@ -314,11 +305,10 @@ TEST_F(DbAccessParserTest, validLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V6));
-    ASSERT_NO_THROW(parser.build(json_elements));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
+    EXPECT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid LFC Interval", parser.getDbAccessParameters(),
-                      config, Option::V6);
+                      config);
 }
 
 // This test checks that the parser rejects the negative value of the
@@ -333,8 +323,7 @@ TEST_F(DbAccessParserTest, negativeLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V6));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_THROW(parser.build(json_elements), BadValue);
 }
 
@@ -350,8 +339,7 @@ TEST_F(DbAccessParserTest, largeLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V6));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_THROW(parser.build(json_elements), BadValue);
 }
 
@@ -368,8 +356,7 @@ TEST_F(DbAccessParserTest, validTypeMysql) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid mysql", parser.getDbAccessParameters(), config);
 }
@@ -386,8 +373,7 @@ TEST_F(DbAccessParserTest, missingTypeKeyword) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_THROW(parser.build(json_elements), TypeKeywordMissing);
 }
 
@@ -396,7 +382,7 @@ TEST_F(DbAccessParserTest, factory) {
 
     // Check that the parser is built through the factory.
     boost::scoped_ptr<DhcpConfigParser> parser(
-        DbAccessParser::factory("lease-database", ParserContext(Option::V4))
+        DbAccessParser::factory("lease-database")
     );
     EXPECT_TRUE(parser);
     DbAccessParser* dbap = dynamic_cast<DbAccessParser*>(parser.get());
@@ -447,8 +433,7 @@ TEST_F(DbAccessParserTest, incrementalChanges) {
                              "name",     "keatest",
                              NULL};
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
 
     // First configuration string should cause a representation of that string
     // to be held.
@@ -512,8 +497,7 @@ TEST_F(DbAccessParserTest, getDbAccessString) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB);
     EXPECT_NO_THROW(parser.build(json_elements));
 
     // Get the database access string
@@ -522,96 +506,7 @@ TEST_F(DbAccessParserTest, getDbAccessString) {
     // String should be either "type=mysql name=keatest" or
     // "name=keatest type=mysql". The "host" entry is null, so should not be
     // output.
-    EXPECT_EQ(dbaccess, "name=keatest type=mysql universe=4");
-}
-
-// Check that the "commit" function actually opens the database, when type
-// is set to LEASE_DB.  We will only do this for the "memfile" database, as
-// that does not assume that the test has been built with MySQL support.
-TEST_F(DbAccessParserTest, commitLeaseDb) {
-
-    // Verify that no lease database is open
-    EXPECT_THROW({
-            LeaseMgr& manager = LeaseMgrFactory::instance();
-            manager.getType();  // Never executed but satisfies compiler
-            }, isc::dhcp::NoLeaseManager);
-
-    // Set up the parser to open the memfile database.
-    const char* config[] = {"type", "memfile", "persist", "false", NULL};
-    string json_config = toJson(config);
-
-    ConstElementPtr json_elements = Element::fromJSON(json_config);
-    EXPECT_TRUE(json_elements);
-
-    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
-                              ParserContext(Option::V4));
-    EXPECT_NO_THROW(parser.build(json_elements));
-
-    // Ensure that the access string is as expected.
-    EXPECT_EQ("persist=false type=memfile universe=4",
-              parser.getDbAccessString());
-
-    // Committal of the parser changes should open the database.
-    EXPECT_NO_THROW(parser.commit());
-
-    // Verify by checking the type of database open.
-    std::string dbtype;
-    EXPECT_NO_THROW(dbtype = LeaseMgrFactory::instance().getType());
-    EXPECT_EQ(std::string("memfile"), dbtype);
-}
-
-#ifdef HAVE_MYSQL
-// Check that the "commit" function actually opens the database, when type
-// is set to HOSTS_DB. We're using MySQL here. Since the only currently supported
-// host data source is the one that uses MySQL, we have no other choice, but to
-// depend this test on MYSQL availability.
-TEST_F(DbAccessParserTest, commitHostsDb) {
-
-    // Remove schema (if there are any leftovers from previous tests).
-    destroyMySQLSchema();
-
-    // Verify that no lease database is open
-    EXPECT_THROW({
-            LeaseMgr& manager = LeaseMgrFactory::instance();
-            manager.getType();  // Never executed but satisfies compiler
-            }, isc::dhcp::NoLeaseManager);
-
-    // Set up the parser to open the memfile database.
-    const char* config[] = {"type", "mysql", "user", "keatest",
-                            "password", "keatest", "name", "keatest", NULL};
-    string json_config = toJson(config);
-
-    ConstElementPtr json_elements = Element::fromJSON(json_config);
-    EXPECT_TRUE(json_elements);
-
-    TestDbAccessParser parser("hosts-database", DbAccessParser::HOSTS_DB,
-                              ParserContext(Option::V4));
-    EXPECT_NO_THROW(parser.build(json_elements));
-
-    // Ensure that the access string is as expected.
-    EXPECT_EQ("name=keatest password=keatest type=mysql universe=4 user=keatest",
-              parser.getDbAccessString());
-
-    // Destroy lease mgr (if there's any)
-    LeaseMgrFactory::destroy();
-
-    EXPECT_NO_THROW(createMySQLSchema());
-
-    // Committal of the parser changes should not create LeaseMgr.
-    // It should create HostDataSource instead.
-    EXPECT_NO_THROW(parser.commit());
-
-    // Check that LeaseMgr was NOT created (it shouldn't, this is for HOSTS_DB).
-    EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
-
-    // Verify that HostDataSource has been created.
-    HostDataSourcePtr hds = HostMgr::instance().getHostDataSource();
-    ASSERT_TRUE(hds);
-
-    EXPECT_EQ("mysql", hds->getType());
-
-    EXPECT_NO_THROW(destroyMySQLSchema());
+    EXPECT_EQ(dbaccess, "name=keatest type=mysql");
 }
-#endif
 
 };  // Anonymous namespace

+ 2 - 2
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -12,7 +12,7 @@
 #include <dhcpsrv/mysql_connection.h>
 #include <dhcpsrv/mysql_host_data_source.h>
 #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
-#include <dhcpsrv/tests/mysql_schema.h>
+#include <dhcpsrv/testutils/mysql_schema.h>
 #include <dhcpsrv/host_data_source_factory.h>
 
 #include <gtest/gtest.h>

+ 2 - 2
src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 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
@@ -12,7 +12,7 @@
 #include <dhcpsrv/mysql_lease_mgr.h>
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
-#include <dhcpsrv/tests/mysql_schema.h>
+#include <dhcpsrv/testutils/mysql_schema.h>
 #include <exceptions/exceptions.h>
 
 #include <gtest/gtest.h>

+ 13 - 2
src/lib/dhcpsrv/testutils/Makefile.am

@@ -16,9 +16,20 @@ if HAVE_GTEST
 noinst_LTLIBRARIES = libdhcpsrvtest.la
 
 libdhcpsrvtest_la_SOURCES  = config_result_check.cc config_result_check.h
+if HAVE_MYSQL
+libdhcpsrvtest_la_SOURCES += mysql_schema.cc mysql_schema.h
+libdhcpsrvtest_la_SOURCES += schema_mysql_copy.h
+endif
+
 libdhcpsrvtest_la_CXXFLAGS = $(AM_CXXFLAGS)
-libdhcpsrvtest_la_CPPFLAGS = $(AM_CPPFLAGS)
-libdhcpsrvtest_la_LDFLAGS  = $(AM_LDFLAGS)
+libdhcpsrvtest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+if HAVE_MYSQL
+libdhcpsrvtest_la_CPPFLAGS += $(MYSQL_CPPFLAGS)
+endif
+libdhcpsrvtest_la_LDFLAGS  = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+if HAVE_MYSQL
+libdhcpsrvtest_la_LDFLAGS  += $(MYSQL_LIBS)
+endif
 libdhcpsrvtest_la_LIBADD   = $(top_builddir)/src/lib/cc/libkea-cc.la
 libdhcpsrvtest_la_LIBADD  += $(top_builddir)/src/lib/log/libkea-log.la
 

src/lib/dhcpsrv/tests/mysql_schema.cc → src/lib/dhcpsrv/testutils/mysql_schema.cc


src/lib/dhcpsrv/tests/mysql_schema.h → src/lib/dhcpsrv/testutils/mysql_schema.h


src/lib/dhcpsrv/tests/schema_mysql_copy.h → src/lib/dhcpsrv/testutils/schema_mysql_copy.h