Browse Source

[master] Merge branch 'trac3569_rebase' (host data source configuration)

Tomek Mrugalski 9 years ago
parent
commit
8896c9c3f7

+ 74 - 5
doc/guide/dhcp4-srv.xml

@@ -425,17 +425,17 @@ be followed by a comma and another object definition.</para>
 </section>
 
 <section id="database-configuration4">
-  <title>Database Configuration</title>
+  <title>Lease Database Configuration</title>
 
   <note>
-    <para>Database access information must be configured for the DHCPv4 server,
-    even if it has already been configured for the DHCPv6 server.  The servers
+    <para>Lease database access information must be configured for the DHCPv4 server,
+    even if it has already been configured for the DHCPv6 server. The servers
     store their information independently, so each server can use a separate
     database or both servers can use the same database.</para>
   </note>
 
-  <para>Database configuration is controlled through the Dhcp4/lease-database
-  parameters. The type of the database must be set to "mysql" or "postgresql",
+  <para>Lease database configuration is controlled through the Dhcp4/lease-database
+  parameters. The type of the database must be set to "memfile", "mysql" or "postgresql",
   e.g.
 <screen>
 "Dhcp4": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
@@ -471,6 +471,75 @@ be followed by a comma and another object definition.</para>
 </section>
 </section>
 
+<section>
+  <title>Hosts Storage</title>
+
+    <note>
+      <para>This feature did not undergo the regular system level
+      testing conducted by ISC. As such, please treat it as
+      experimental.</para>
+    </note>
+
+    <para>Kea is also able to store information about host reservations in the
+    database. Hosts database configuration uses the same syntax as lease
+    database. In fact, Kea server opens independent connections for each
+    purpose, be it lease or hosts information. This gives the solution most
+    flexibility. Kea can be used to keep leases and host reservations
+    separately, but can also point to the same database. Currently the only
+    supported hosts database type is MySQL.</para>
+
+    <para>Please note that usage of hosts storage is optional. User can define
+    all host reservations in the configuration file. That is the recommended way
+    if the number of reservations is small. However, with the number of
+    reservations growing it's more convenient to use host storage. Please note
+    that both storages (configuration file and MySQL) can be used together. If
+    hosts are defined in both places, the definitions from configuration file
+    are checked first and external storage is checked later, if
+    necessary.</para>
+
+    <para>All hosts leases issued by the server are stored in the hosts
+    database. Currently there is only one available backend: MySQL. Other host
+    backends will become available in future Kea versions.</para>
+
+<section id="hosts-database-configuration4">
+  <title>IPv4 Hosts Database Configuration</title>
+
+  <para>Hosts database configuration is controlled through the Dhcp4/hosts-database
+  parameters. If enabled, the type of the database must be set to "mysql". Other
+  hosts backends may be added in later Kea versions.
+<screen>
+"Dhcp4": { "hosts-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
+</screen>
+  Next, the name of the database to hold the leases must be set: this is the
+  name used when the lease database was created (see <xref linkend="mysql-database-create"/>).
+<screen>
+"Dhcp4": { "hosts-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
+</screen>
+  If the database is located on a different system to the DHCPv4 server, the
+  database host name must also be specified (although it should be noted that this
+  configuration may have a severe impact on server performance):
+<screen>
+"Dhcp4": { "hosts-database": { <userinput>"host": <replaceable>remote-host-name</replaceable></userinput>, ... }, ... }
+</screen>
+  The usual state of affairs will be to have the database on the same machine as
+  the DHCPv4 server.  In this case, set the value to the empty string:
+<screen>
+"Dhcp4": { "hosts-database": { <userinput>"host" : ""</userinput>, ... }, ... }
+</screen>
+  </para>
+  <para>Finally, the credentials of the account under which the server will
+  access the database should be set:
+<screen>
+"Dhcp4": { "hosts-database": { <userinput>"user": "<replaceable>user-name</replaceable>"</userinput>,
+                               <userinput>"password": "<replaceable>password</replaceable>"</userinput>,
+                              ... },
+           ... }
+</screen>
+  If there is no password to the account, set the password to the empty string
+  "". (This is also the default.)</para>
+</section>
+</section>
+
 <section id="dhcp4-interface-configuration">
   <title>Interface configuration</title>
   <para>The DHCPv4 server has to be configured to listen on specific network

+ 82 - 5
doc/guide/dhcp6-srv.xml

@@ -424,18 +424,18 @@ be followed by a comma and another object definition.</para>
 </section>
 
 <section id="database-configuration6">
-  <title>Database Configuration</title>
+  <title>Lease Database Configuration</title>
 
   <note>
-    <para>Database access information must be configured for the DHCPv6 server,
+    <para>Lease database access information must be configured for the DHCPv6 server,
     even if it has already been configured for the DHCPv4 server.  The servers
     store their information independently, so each server can use a separate
     database or both servers can use the same database.</para>
   </note>
 
-  <para>Database configuration is controlled through the Dhcp6/lease-database
-  parameters. The type of the database must be set to "mysql" or "postgresql",
-  e.g.
+  <para>Lease database configuration is controlled through the
+  Dhcp6/lease-database parameters. The type of the database must be set to
+  "memfile", "mysql" or "postgresql", e.g.
 <screen>
 "Dhcp6": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
 </screen>
@@ -470,6 +470,83 @@ be followed by a comma and another object definition.</para>
 </section>
 </section>
 
+<!-- Uncomment this once 4212 is merged. While the host storage for
+     MySQL is there, it is able to store hosts, but not IPv6
+     reservations. So technically we have host reservations for v6 in
+     MySQL, but in practice it's kinda useless right now as it can't
+     store addresses or prefixes. Let's keep it commented out until
+     we implement that functionality
+
+<section>
+  <title>Hosts Storage</title>
+
+    <note>
+      <para>This feature did not undergo the regular system level
+      testing conducted by ISC. As such, please treat it as
+      experimental.</para>
+    </note>
+
+    <para>Kea is also able to store information about host reservations in the
+    database. Hosts database configuration uses the same syntax as lease
+    database. In fact, Kea server opens independent connections for each
+    purpose, be it lease or hosts information. This gives the solution most
+    flexibility. Kea can be used to keep leases and host reservations
+    separately, but can also point to the same database. Currently the only
+    supported hosts database type is MySQL.</para>
+
+    <para>Please note that usage of hosts storage is optional. User can define
+    all host reservations in the configuration file. That is the recommended way
+    if the number of reservations is small. However, with the number of
+    reservations growing it's more convenient to use host storage. Please note
+    that both storages (configuration file and MySQL) can be used together. If
+    hosts are defined in both places, the definitions from configuration file
+    are checked first and external storage is checked later, if
+    necessary.</para>
+
+    <para>All hosts leases issued by the server are stored in the hosts
+    database. Currently there is only one available backend: MySQL. Other host
+    backends will become available in future Kea versions.</para>
+
+<section id="hosts-database-configuration6">
+  <title>IPv6 Hosts Database Configuration</title>
+
+  <para>Hosts database configuration is controlled through the Dhcp6/hosts-database
+  parameters. If enabled, the type of the database must be set to "mysql". Other
+  hosts backends may be added in later Kea versions.
+<screen>
+"Dhcp6": { "hosts-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
+</screen>
+  Next, the name of the database to hold the leases must be set: this is the
+  name used when the lease database was created (see <xref linkend="mysql-database-create"/>).
+<screen>
+"Dhcp6": { "hosts-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
+</screen>
+  If the database is located on a different system to the DHCPv6 server, the
+  database host name must also be specified (although it should be noted that this
+  configuration may have a severe impact on server performance):
+<screen>
+"Dhcp6": { "hosts-database": { <userinput>"host": <replaceable>remote-host-name</replaceable></userinput>, ... }, ... }
+</screen>
+  The usual state of affairs will be to have the database on the same machine as
+  the DHCPv6 server.  In this case, set the value to the empty string:
+<screen>
+"Dhcp6": { "hosts-database": { <userinput>"host" : ""</userinput>, ... }, ... }
+</screen>
+  </para>
+  <para>Finally, the credentials of the account under which the server will
+  access the database should be set:
+<screen>
+"Dhcp6": { "hosts-database": { <userinput>"user": "<replaceable>user-name</replaceable>"</userinput>,
+                               <userinput>"password": "<replaceable>password</replaceable>"</userinput>,
+                              ... },
+           ... }
+</screen>
+  If there is no password to the account, set the password to the empty string
+  "". (This is also the default.)</para>
+</section>
+</section>
+-->
+
 <section id="dhcp6-interface-selection">
   <title>Interface selection</title>
   <para>The DHCPv6 server has to be configured to listen on specific network

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

@@ -438,7 +438,11 @@ 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, *globalContext());
+        parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB,
+                                    *globalContext());
+    } else if (config_id.compare("hosts-database") == 0) {
+        parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB,
+                                    *globalContext());
     } else if (config_id.compare("hooks-libraries") == 0) {
         parser = new HooksLibrariesParser(config_id);
     } else if (config_id.compare("echo-client-id") == 0) {

+ 29 - 1
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -39,6 +39,7 @@
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/utils.h>
+#include <dhcpsrv/host_mgr.h>
 #include <gtest/gtest.h>
 #include <stats/stats_mgr.h>
 #include <boost/scoped_ptr.hpp>
@@ -74,7 +75,31 @@ const char* CONFIGS[] = {
         "    \"valid-lifetime\": 4000,"
         "    \"interface\": \"eth0\" "
         " } ],"
-        "\"valid-lifetime\": 4000 }"
+    "\"valid-lifetime\": 4000 }",
+
+    // Configuration 1:
+    // - 1 subnet: 192.0.2.0/24
+    // - MySQL Host Data Source configured
+    "{ \"interfaces-config\": {"
+        "    \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"hosts-database\": {"
+        "    \"type\": \"mysql\","
+        "    \"name\": \"keatest\","
+        "    \"user\": \"keatest\","
+        "    \"password\": \"keatest\""
+        "},"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"rebind-timer\": 2000, "
+        "    \"renew-timer\": 1000, "
+        "    \"valid-lifetime\": 4000,"
+        "    \"interface\": \"eth0\" "
+        " } ],"
+    "\"valid-lifetime\": 4000 }"
 };
 
 // This test verifies that the destination address of the response
@@ -2634,4 +2659,7 @@ TEST_F(Dhcpv4SrvTest, statisticsUnknownRcvd) {
     EXPECT_EQ(1, drop_stat->getInteger().first);
 }
 
+/// @todo: Implement proper tests for MySQL lease/host database,
+///        see ticket #4214.
+
 }; // end of anonymous namespace

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

@@ -689,7 +689,11 @@ 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, *globalContext());
+        parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB,
+                                    *globalContext());
+    } else if (config_id.compare("hosts-database") == 0) {
+        parser = new DbAccessParser(config_id, DbAccessParser::HOSTS_DB,
+                                    *globalContext());
     } else if (config_id.compare("hooks-libraries") == 0) {
         parser = new HooksLibrariesParser(config_id);
     } else if (config_id.compare("dhcp-ddns") == 0) {

+ 51 - 23
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -34,6 +34,7 @@
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/utils.h>
 #include <util/buffer.h>
 #include <util/range_utilities.h>
@@ -61,6 +62,53 @@ using namespace std;
 
 namespace {
 
+const char* CONFIGS[] = {
+    // Configuration 0:
+    // - used in advertiseOptions
+    "{ \"interfaces-config\": {"
+    "  \"interfaces\": [ \"*\" ]"
+    "},"
+    "\"preferred-lifetime\": 3000,"
+    "\"rebind-timer\": 2000, "
+    "\"renew-timer\": 1000, "
+    "\"subnet6\": [ { "
+    "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+    "    \"subnet\": \"2001:db8:1::/48\", "
+    "    \"interface\": \"eth0\", "
+    "    \"option-data\": [ {"
+    "          \"name\": \"dns-servers\","
+    "          \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\""
+    "        },"
+    "        {"
+    "          \"name\": \"subscriber-id\","
+    "          \"data\": \"1234\","
+    "          \"csv-format\": False"
+    "        } ]"
+    " } ],"
+    "\"valid-lifetime\": 4000 }",
+
+    // Configuration 1:
+    // - a single subnet
+    // - MySQL host data source
+    "{ \"interfaces-config\": {"
+    "  \"interfaces\": [ \"*\" ]"
+    "},"
+    "\"hosts-database\": {"
+    "    \"type\": \"mysql\","
+    "    \"name\": \"keatest\","
+    "    \"user\": \"keatest\","
+    "    \"password\": \"keatest\""
+    "},"
+    "\"preferred-lifetime\": 3000,"
+    "\"rebind-timer\": 2000, "
+    "\"renew-timer\": 1000, "
+    "\"subnet6\": [ { "
+    "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+    "    \"subnet\": \"2001:db8:1::/48\" "
+    " } ],"
+    "\"valid-lifetime\": 4000 }"
+};
+
 // This test verifies that incoming SOLICIT can be handled properly when
 // there are no subnets configured.
 //
@@ -288,28 +336,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
     IfaceMgrTestConfig test_config(true);
 
     ConstElementPtr x;
-    string config = "{ \"interfaces-config\": {"
-        "  \"interfaces\": [ \"*\" ]"
-        "},"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
-        "\"subnet6\": [ { "
-        "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
-        "    \"subnet\": \"2001:db8:1::/48\", "
-        "    \"interface\": \"eth0\", "
-        "    \"option-data\": [ {"
-        "          \"name\": \"dns-servers\","
-        "          \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\""
-        "        },"
-        "        {"
-        "          \"name\": \"subscriber-id\","
-        "          \"data\": \"1234\","
-        "          \"csv-format\": False"
-        "        } ]"
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
-    ASSERT_NO_THROW(configure(config));
+    ASSERT_NO_THROW(configure(CONFIGS[0]));
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
@@ -2707,9 +2734,10 @@ TEST_F(Dhcpv6SrvTest, receiveParseFailedStat) {
     EXPECT_EQ(1, recv_drop->getInteger().first);
 }
 
-
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// to call processX() methods.
 
+/// @todo: Implement proper tests for MySQL lease/host database,
+///        see ticket #4214.
 
 }   // end of anonymous namespace

+ 4 - 0
src/lib/dhcpsrv/base_host_data_source.h

@@ -20,6 +20,7 @@
 #include <dhcp/hwaddr.h>
 #include <dhcpsrv/host.h>
 #include <exceptions/exceptions.h>
+#include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace dhcp {
@@ -214,6 +215,9 @@ public:
     virtual void rollback() {};
 };
 
+/// @brief HostDataSource pointer
+typedef boost::shared_ptr<BaseHostDataSource> HostDataSourcePtr;
+
 }
 }
 

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

@@ -39,9 +39,9 @@ namespace isc {
 namespace dhcp {
 
 
-boost::scoped_ptr<BaseHostDataSource>&
+HostDataSourcePtr&
 HostDataSourceFactory::getHostDataSourcePtr() {
-    static boost::scoped_ptr<BaseHostDataSource> hostDataSourcePtr;
+    static HostDataSourcePtr hostDataSourcePtr;
     return (hostDataSourcePtr);
 }
 
@@ -99,6 +99,7 @@ HostDataSourceFactory::destroy() {
     getHostDataSourcePtr().reset();
 }
 
+#if 0
 BaseHostDataSource&
 HostDataSourceFactory::instance() {
     BaseHostDataSource* hdsptr = getHostDataSourcePtr().get();
@@ -108,6 +109,7 @@ HostDataSourceFactory::instance() {
     }
     return (*hdsptr);
 }
+#endif
 
 }; // namespace dhcp
 }; // namespace isc

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

@@ -88,23 +88,12 @@ public:
     /// host data source is available.
     static void destroy();
 
-    /// @brief Return current host data source
-    ///
-    /// @returns An instance of the "current" host data source.  An exception
-    /// will be thrown if none is available.
-    ///
-    /// @throw NoHostDataSourceManager No host data source is available: use
-    ///        create() to create one before calling this method.
-    static BaseHostDataSource& instance();
-
-private:
     /// @brief Hold pointer to host data source instance
     ///
     /// Holds a pointer to the singleton host data source.  The singleton
     /// is encapsulated in this method to avoid a "static initialization
     /// fiasco" if defined in an external static variable.
-    static boost::scoped_ptr<BaseHostDataSource>& getHostDataSourcePtr();
-
+    static HostDataSourcePtr& getHostDataSourcePtr();
 };
 
 

+ 26 - 19
src/lib/dhcpsrv/host_mgr.cc

@@ -38,8 +38,6 @@ namespace dhcp {
 
 using namespace isc::asiolink;
 
-boost::shared_ptr<BaseHostDataSource> HostMgr::alternate_source;
-
 boost::scoped_ptr<HostMgr>&
 HostMgr::getHostMgrPtr() {
     static boost::scoped_ptr<HostMgr> host_mgr_ptr;
@@ -51,11 +49,20 @@ HostMgr::create(const std::string& access) {
     getHostMgrPtr().reset(new HostMgr());
 
     if (!access.empty()) {
+        // If the user specified parameters, let's pass them to the create
+        // method. It will destroy any prior instances and will create
+        // the new one.
         HostDataSourceFactory::create(access);
-
-        /// @todo Initialize alternate_source here.
-        //alternate_source = HostDataSourceFactory::getHostDataSourcePtr();
+    } else {
+        // Ok, no parameters were specified. We should destroy the existing
+        // insteance.
+        HostDataSourceFactory::destroy();
     }
+
+    // Now store the host data source pointer. It may be NULL. That's ok as
+    // NULL value indicates that there's no host data source configured.
+    getHostMgrPtr()->alternate_source_ =
+        HostDataSourceFactory::getHostDataSourcePtr();
 }
 
 HostMgr&
@@ -70,8 +77,8 @@ HostMgr::instance() {
 ConstHostCollection
 HostMgr::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
     ConstHostCollection hosts = getCfgHosts()->getAll(hwaddr, duid);
-    if (alternate_source) {
-        ConstHostCollection hosts_plus = alternate_source->getAll(hwaddr, duid);
+    if (alternate_source_) {
+        ConstHostCollection hosts_plus = alternate_source_->getAll(hwaddr, duid);
         hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
     }
     return (hosts);
@@ -80,8 +87,8 @@ HostMgr::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
 ConstHostCollection
 HostMgr::getAll4(const IOAddress& address) const {
     ConstHostCollection hosts = getCfgHosts()->getAll4(address);
-    if (alternate_source) {
-        ConstHostCollection hosts_plus = alternate_source->getAll4(address);
+    if (alternate_source_) {
+        ConstHostCollection hosts_plus = alternate_source_->getAll4(address);
         hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
     }
     return (hosts);
@@ -91,13 +98,13 @@ ConstHostPtr
 HostMgr::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
               const DuidPtr& duid) const {
     ConstHostPtr host = getCfgHosts()->get4(subnet_id, hwaddr, duid);
-    if (!host && alternate_source) {
+    if (!host && alternate_source_) {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
                   HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_HWADDR_DUID)
             .arg(subnet_id)
             .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
             .arg(duid ? duid->toText() : "(duid)");
-        host = alternate_source->get4(subnet_id, hwaddr, duid);
+        host = alternate_source_->get4(subnet_id, hwaddr, duid);
     }
     return (host);
 }
@@ -106,12 +113,12 @@ ConstHostPtr
 HostMgr::get4(const SubnetID& subnet_id,
               const asiolink::IOAddress& address) const {
     ConstHostPtr host = getCfgHosts()->get4(subnet_id, address);
-    if (!host && alternate_source) {
+    if (!host && alternate_source_) {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
                   HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_ADDRESS4)
             .arg(subnet_id)
             .arg(address.toText());
-        host = alternate_source->get4(subnet_id, address);
+        host = alternate_source_->get4(subnet_id, address);
     }
     return (host);
 }
@@ -121,13 +128,13 @@ ConstHostPtr
 HostMgr::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) const {
     ConstHostPtr host = getCfgHosts()->get6(subnet_id, duid, hwaddr);
-    if (!host && alternate_source) {
+    if (!host && alternate_source_) {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
                   HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_DUID_HWADDR)
             .arg(subnet_id)
             .arg(duid ? duid->toText() : "(duid)")
             .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)");
-        host = alternate_source->get6(subnet_id, duid, hwaddr);
+        host = alternate_source_->get6(subnet_id, duid, hwaddr);
     }
     return (host);
 }
@@ -135,12 +142,12 @@ HostMgr::get6(const SubnetID& subnet_id, const DuidPtr& duid,
 ConstHostPtr
 HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
     ConstHostPtr host = getCfgHosts()->get6(prefix, prefix_len);
-    if (!host && alternate_source) {
+    if (!host && alternate_source_) {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
                   HOSTS_MGR_ALTERNATE_GET6_PREFIX)
             .arg(prefix.toText())
             .arg(static_cast<int>(prefix_len));
-        host = alternate_source->get6(prefix, prefix_len);
+        host = alternate_source_->get6(prefix, prefix_len);
     }
     return (host);
 }
@@ -149,12 +156,12 @@ ConstHostPtr
 HostMgr::get6(const SubnetID& subnet_id,
               const asiolink::IOAddress& addr) const {
     ConstHostPtr host = getCfgHosts()->get6(subnet_id, addr);
-    if (!host && alternate_source) {
+    if (!host && alternate_source_) {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
                   HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_ADDRESS6)
             .arg(subnet_id)
             .arg(addr.toText());
-        host = alternate_source->get6(subnet_id, addr);
+        host = alternate_source_->get6(subnet_id, addr);
     }
     return (host);
 }

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

@@ -207,6 +207,14 @@ public:
         return (std::string("host_mgr"));
     }
 
+    /// @brief Returns pointer to the host data source
+    ///
+    /// May return NULL
+    /// @return pointer to the host data source (or NULL)
+    HostDataSourcePtr getHostDataSource() const {
+        return (alternate_source_);
+    }
+
 private:
 
     /// @brief Private default constructor.
@@ -215,7 +223,7 @@ private:
     /// @brief Pointer to an alternate host data source.
     ///
     /// If this pointer is NULL, the source is not in use.
-    static boost::shared_ptr<BaseHostDataSource> alternate_source;
+    HostDataSourcePtr alternate_source_;
 
     /// @brief Returns a pointer to the currently used instance of the
     /// @c HostMgr.

+ 1 - 0
src/lib/dhcpsrv/mysql_connection.h

@@ -19,6 +19,7 @@
 #include <boost/scoped_ptr.hpp>
 #include <mysql.h>
 #include <vector>
+#include <stdint.h>
 
 namespace isc {
 namespace dhcp {

+ 27 - 6
src/lib/dhcpsrv/parsers/dbaccess_parser.cc

@@ -17,6 +17,7 @@
 #include <dhcp/option.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/parsers/dbaccess_parser.h>
 
 #include <boost/foreach.hpp>
@@ -34,8 +35,9 @@ namespace dhcp {
 
 
 // Factory function to build the parser
-DbAccessParser::DbAccessParser(const std::string&, const ParserContext& ctx)
-    : values_(), ctx_(ctx)
+DbAccessParser::DbAccessParser(const std::string&, DBType db_type,
+                               const ParserContext& ctx)
+    : values_(), type_(db_type), ctx_(ctx)
 {
 }
 
@@ -139,11 +141,30 @@ DbAccessParser::getDbAccessString() const {
 // Commit the changes - reopen the database with the new parameters
 void
 DbAccessParser::commit() {
-    // Close current lease manager database.
-    LeaseMgrFactory::destroy();
 
-    // ... and open the new database using the access string.
-    LeaseMgrFactory::create(getDbAccessString());
+    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

+ 20 - 2
src/lib/dhcpsrv/parsers/dbaccess_parser.h

@@ -45,6 +45,13 @@ public:
 /// depend on the database chosen.
 class DbAccessParser: public DhcpConfigParser {
 public:
+
+    /// @brief Specifies the database type
+    typedef enum {
+        LEASE_DB = 1,
+        HOSTS_DB = 2
+    } DBType;
+
     /// @brief Keyword and associated value
     typedef std::pair<std::string, std::string> StringPair;
 
@@ -55,8 +62,10 @@ 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, const ParserContext& ctx);
+    DbAccessParser(const std::string& param_name, DBType db_type,
+                   const ParserContext& ctx);
 
     /// The destructor.
     virtual ~DbAccessParser()
@@ -103,7 +112,14 @@ public:
     ///         destroying the parser after use.
     static DhcpConfigParser* factory(const std::string& param_name,
                                      const ParserContext& ctx) {
-        return (new DbAccessParser(param_name, ctx));
+        if (param_name == "lease-database") {
+            return (new DbAccessParser(param_name, DbAccessParser::LEASE_DB, ctx));
+        } else if (param_name == "hosts-database") {
+            return (new DbAccessParser(param_name, DbAccessParser::HOSTS_DB, ctx));
+        } else {
+            isc_throw(BadValue, "Unexpected parameter name (" << param_name
+                      << ") passed to DbAccessParser::factory");
+        }
     }
 
 protected:
@@ -130,6 +146,8 @@ private:
 
     std::map<std::string, std::string> values_; ///< Stored parameter values
 
+    DBType type_; ///< Database type (leases or hosts)
+
     ParserContext ctx_; ///< Parser context
 };
 

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

@@ -104,6 +104,7 @@ 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

+ 88 - 18
src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc

@@ -17,6 +17,8 @@
 #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/host_mgr.h>
 #include <log/logger_support.h>
 
 #include <gtest/gtest.h>
@@ -27,6 +29,7 @@
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace isc::data;
 using namespace isc::config;
 
@@ -201,8 +204,9 @@ public:
     /// @brief Constructor
     ///
     /// @brief Keyword/value collection of ddatabase access parameters
-    TestDbAccessParser(const std::string& param_name, const ParserContext& ctx)
-        : DbAccessParser(param_name, ctx)
+    TestDbAccessParser(const std::string& param_name, DbAccessParser::DBType type,
+                       const ParserContext& ctx)
+        : DbAccessParser(param_name, type, ctx)
     {}
 
     /// @brief Destructor
@@ -243,7 +247,8 @@ TEST_F(DbAccessParserTest, validTypeMemfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V4));
     EXPECT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
 }
@@ -259,7 +264,8 @@ TEST_F(DbAccessParserTest, emptyKeyword) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V4));
     EXPECT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
 }
@@ -276,7 +282,8 @@ TEST_F(DbAccessParserTest, persistV4Memfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V4));
     EXPECT_NO_THROW(parser.build(json_elements));
 
     checkAccessString("Valid memfile", parser.getDbAccessParameters(),
@@ -295,7 +302,8 @@ TEST_F(DbAccessParserTest, persistV6Memfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V6));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V6));
     EXPECT_NO_THROW(parser.build(json_elements));
 
     checkAccessString("Valid memfile", parser.getDbAccessParameters(),
@@ -314,7 +322,8 @@ TEST_F(DbAccessParserTest, validLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V6));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V6));
     ASSERT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid LFC Interval", parser.getDbAccessParameters(),
                       config, Option::V6);
@@ -332,7 +341,8 @@ TEST_F(DbAccessParserTest, negativeLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V6));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V6));
     EXPECT_THROW(parser.build(json_elements), BadValue);
 }
 
@@ -348,7 +358,8 @@ TEST_F(DbAccessParserTest, largeLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V6));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V6));
     EXPECT_THROW(parser.build(json_elements), BadValue);
 }
 
@@ -365,7 +376,8 @@ TEST_F(DbAccessParserTest, validTypeMysql) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V4));
     EXPECT_NO_THROW(parser.build(json_elements));
     checkAccessString("Valid mysql", parser.getDbAccessParameters(), config);
 }
@@ -382,7 +394,8 @@ TEST_F(DbAccessParserTest, missingTypeKeyword) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V4));
     EXPECT_THROW(parser.build(json_elements), TypeKeywordMissing);
 }
 
@@ -442,7 +455,8 @@ TEST_F(DbAccessParserTest, incrementalChanges) {
                              "name",     "keatest",
                              NULL};
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V4));
 
     // First configuration string should cause a representation of that string
     // to be held.
@@ -506,7 +520,8 @@ TEST_F(DbAccessParserTest, getDbAccessString) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    TestDbAccessParser parser("lease-database", DbAccessParser::LEASE_DB,
+                              ParserContext(Option::V4));
     EXPECT_NO_THROW(parser.build(json_elements));
 
     // Get the database access string
@@ -518,10 +533,10 @@ TEST_F(DbAccessParserTest, getDbAccessString) {
     EXPECT_EQ(dbaccess, "name=keatest type=mysql universe=4");
 }
 
-// Check that the "commit" function actually opens the database.  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, commit) {
+// 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({
@@ -536,7 +551,8 @@ TEST_F(DbAccessParserTest, commit) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser("lease-database", ParserContext(Option::V4));
+    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.
@@ -552,4 +568,58 @@ TEST_F(DbAccessParserTest, commit) {
     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());
+}
+#endif
+
 };  // Anonymous namespace

+ 1 - 1
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -27,7 +27,7 @@ namespace dhcp {
 namespace test {
 
 GenericHostDataSourceTest::GenericHostDataSourceTest()
-    :hdsptr_(NULL) {
+    :hdsptr_() {
 
 }
 

+ 1 - 1
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -125,7 +125,7 @@ public:
                               const ClientClasses& classes2);
 
     /// @brief Pointer to the host data source
-    BaseHostDataSource* hdsptr_;
+    HostDataSourcePtr hdsptr_;
 
     /// @brief Test that checks that simple host with IPv4 reservation
     ///        can be inserted and later retrieved.

+ 14 - 118
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -20,6 +20,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/host_data_source_factory.h>
 
 #include <gtest/gtest.h>
@@ -38,111 +39,6 @@ using namespace std;
 
 namespace {
 
-// This holds statements to create and destroy the schema.
-#include "schema_mysql_copy.h"
-
-// Connection strings.
-// Database: keatest
-// Host: localhost
-// Username: keatest
-// Password: keatest
-const char* VALID_TYPE = "type=mysql";
-const char* INVALID_TYPE = "type=unknown";
-const char* VALID_NAME = "name=keatest";
-const char* INVALID_NAME = "name=invalidname";
-const char* VALID_HOST = "host=localhost";
-const char* INVALID_HOST = "host=invalidhost";
-const char* VALID_USER = "user=keatest";
-const char* INVALID_USER = "user=invaliduser";
-const char* VALID_PASSWORD = "password=keatest";
-const char* INVALID_PASSWORD = "password=invalid";
-
-// Given a combination of strings above, produce a connection string.
-string connectionString(const char* type, const char* name, const char* host,
-                        const char* user, const char* password) {
-    const string space = " ";
-    string result = "";
-
-    if (type != NULL) {
-        result += string(type);
-    }
-    if (name != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(name);
-    }
-
-    if (host != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(host);
-    }
-
-    if (user != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(user);
-    }
-
-    if (password != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(password);
-    }
-
-    return (result);
-}
-
-// Return valid connection string
-string
-validConnectionString() {
-    return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
-                             VALID_USER, VALID_PASSWORD));
-}
-
-// @brief Clear everything from the database
-//
-// There is no error checking in this code: if something fails, one of the
-// tests will (should) fall over.
-void destroySchema() {
-    MySqlHolder mysql;
-
-    // Open database
-    (void) mysql_real_connect(mysql, "localhost", "keatest",
-                              "keatest", "keatest", 0, NULL, 0);
-
-    // Get rid of everything in it.
-    for (int i = 0; destroy_statement[i] != NULL; ++i) {
-        (void) mysql_query(mysql, destroy_statement[i]);
-    }
-}
-
-// @brief Create the Schema
-//
-// Creates all the tables in what is assumed to be an empty database.
-//
-// There is no error checking in this code: if it fails, one of the tests
-// will fall over.
-void createSchema() {
-    MySqlHolder mysql;
-
-    // Open database
-    (void) mysql_real_connect(mysql, "localhost", "keatest",
-                              "keatest", "keatest", 0, NULL, 0);
-
-    // Execute creation statements.
-    for (int i = 0; create_statement[i] != NULL; ++i) {
-        ASSERT_EQ(0, mysql_query(mysql, create_statement[i]))
-            << "Failed on statement " << i << ": " << create_statement[i];
-    }
-}
-
-
-
 class MySqlHostDataSourceTest : public GenericHostDataSourceTest {
 public:
     /// @brief Constructor
@@ -151,12 +47,12 @@ public:
     MySqlHostDataSourceTest() {
 
         // Ensure schema is the correct one.
-        destroySchema();
-        createSchema();
+        destroyMySQLSchema();
+        createMySQLSchema();
 
         // Connect to the database
         try {
-            HostDataSourceFactory::create(validConnectionString());
+            HostDataSourceFactory::create(validMySQLConnectionString());
         } catch (...) {
             std::cerr << "*** ERROR: unable to open database. The test\n"
                          "*** environment is broken and must be fixed before\n"
@@ -166,7 +62,7 @@ public:
             throw;
         }
 
-        hdsptr_ = &(HostDataSourceFactory::instance());
+        hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
     }
 
     /// @brief Destructor
@@ -176,7 +72,7 @@ public:
     virtual ~MySqlHostDataSourceTest() {
         hdsptr_->rollback();
         HostDataSourceFactory::destroy();
-        destroySchema();
+        destroyMySQLSchema();
     }
 
     /// @brief Reopen the database
@@ -188,8 +84,8 @@ public:
     /// the same database.
     void reopen(Universe) {
         HostDataSourceFactory::destroy();
-        HostDataSourceFactory::create(validConnectionString());
-        hdsptr_ = &(HostDataSourceFactory::instance());
+        HostDataSourceFactory::create(validMySQLConnectionString());
+        hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
     }
 
 };
@@ -204,14 +100,14 @@ public:
 TEST(MySqlHostDataSource, OpenDatabase) {
 
     // Schema needs to be created for the test to work.
-    destroySchema();
-    createSchema();
+    destroyMySQLSchema();
+    createMySQLSchema();
 
     // Check that lease manager open the database opens correctly and tidy up.
     //  If it fails, print the error message.
     try {
-        HostDataSourceFactory::create(validConnectionString());
-        EXPECT_NO_THROW((void) HostDataSourceFactory::instance());
+        HostDataSourceFactory::create(validMySQLConnectionString());
+        EXPECT_NO_THROW((void) HostDataSourceFactory::getHostDataSourcePtr());
         HostDataSourceFactory::destroy();
     } catch (const isc::Exception& ex) {
         FAIL() << "*** ERROR: unable to open database, reason:\n"
@@ -222,7 +118,7 @@ TEST(MySqlHostDataSource, OpenDatabase) {
 
     // Check that attempting to get an instance of the lease manager when
     // none is set throws an exception.
-    EXPECT_THROW(HostDataSourceFactory::instance(), NoHostDataSourceManager);
+    EXPECT_FALSE(HostDataSourceFactory::getHostDataSourcePtr());
 
     // Check that wrong specification of backend throws an exception.
     // (This is really a check on LeaseMgrFactory, but is convenient to
@@ -254,7 +150,7 @@ TEST(MySqlHostDataSource, OpenDatabase) {
         NoDatabaseName);
 
     // Tidy up after the test
-    destroySchema();
+    destroyMySQLSchema();
 }
 
 /// @brief Check conversion functions

+ 10 - 111
src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc

@@ -20,6 +20,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 <exceptions/exceptions.h>
 
 #include <gtest/gtest.h>
@@ -38,108 +39,6 @@ using namespace std;
 
 namespace {
 
-// This holds statements to create and destroy the schema.
-#include "schema_mysql_copy.h"
-
-// Connection strings.
-// Database: keatest
-// Host: localhost
-// Username: keatest
-// Password: keatest
-const char* VALID_TYPE = "type=mysql";
-const char* INVALID_TYPE = "type=unknown";
-const char* VALID_NAME = "name=keatest";
-const char* INVALID_NAME = "name=invalidname";
-const char* VALID_HOST = "host=localhost";
-const char* INVALID_HOST = "host=invalidhost";
-const char* VALID_USER = "user=keatest";
-const char* INVALID_USER = "user=invaliduser";
-const char* VALID_PASSWORD = "password=keatest";
-const char* INVALID_PASSWORD = "password=invalid";
-
-// Given a combination of strings above, produce a connection string.
-string connectionString(const char* type, const char* name, const char* host,
-                        const char* user, const char* password) {
-    const string space = " ";
-    string result = "";
-
-    if (type != NULL) {
-        result += string(type);
-    }
-    if (name != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(name);
-    }
-
-    if (host != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(host);
-    }
-
-    if (user != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(user);
-    }
-
-    if (password != NULL) {
-        if (! result.empty()) {
-            result += space;
-        }
-        result += string(password);
-    }
-
-    return (result);
-}
-
-// Return valid connection string
-string
-validConnectionString() {
-    return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
-                             VALID_USER, VALID_PASSWORD));
-}
-
-// @brief Clear everything from the database
-//
-// There is no error checking in this code: if something fails, one of the
-// tests will (should) fall over.
-void destroySchema() {
-    MySqlHolder mysql;
-
-    // Open database
-    (void) mysql_real_connect(mysql, "localhost", "keatest",
-                              "keatest", "keatest", 0, NULL, 0);
-
-    // Get rid of everything in it.
-    for (int i = 0; destroy_statement[i] != NULL; ++i) {
-        (void) mysql_query(mysql, destroy_statement[i]);
-    }
-}
-
-// @brief Create the Schema
-//
-// Creates all the tables in what is assumed to be an empty database.
-//
-// There is no error checking in this code: if it fails, one of the tests
-// will fall over.
-void createSchema() {
-    MySqlHolder mysql;
-
-    // Open database
-    (void) mysql_real_connect(mysql, "localhost", "keatest",
-                              "keatest", "keatest", 0, NULL, 0);
-
-    // Execute creation statements.
-    for (int i = 0; create_statement[i] != NULL; ++i) {
-        ASSERT_EQ(0, mysql_query(mysql, create_statement[i]))
-            << "Failed on statement " << i << ": " << create_statement[i];
-    }
-}
 
 /// @brief Test fixture class for testing MySQL Lease Manager
 ///
@@ -154,12 +53,12 @@ public:
     MySqlLeaseMgrTest() {
 
         // Ensure schema is the correct one.
-        destroySchema();
-        createSchema();
+        destroyMySQLSchema();
+        createMySQLSchema();
 
         // Connect to the database
         try {
-            LeaseMgrFactory::create(validConnectionString());
+            LeaseMgrFactory::create(validMySQLConnectionString());
         } catch (...) {
             std::cerr << "*** ERROR: unable to open database. The test\n"
                          "*** environment is broken and must be fixed before\n"
@@ -178,7 +77,7 @@ public:
     virtual ~MySqlLeaseMgrTest() {
         lmptr_->rollback();
         LeaseMgrFactory::destroy();
-        destroySchema();
+        destroyMySQLSchema();
     }
 
     /// @brief Reopen the database
@@ -190,7 +89,7 @@ public:
     /// the same database.
     void reopen(Universe) {
         LeaseMgrFactory::destroy();
-        LeaseMgrFactory::create(validConnectionString());
+        LeaseMgrFactory::create(validMySQLConnectionString());
         lmptr_ = &(LeaseMgrFactory::instance());
     }
 
@@ -206,13 +105,13 @@ public:
 TEST(MySqlOpenTest, OpenDatabase) {
 
     // Schema needs to be created for the test to work.
-    destroySchema();
-    createSchema();
+    destroyMySQLSchema();
+    createMySQLSchema();
 
     // Check that lease manager open the database opens correctly and tidy up.
     //  If it fails, print the error message.
     try {
-        LeaseMgrFactory::create(validConnectionString());
+        LeaseMgrFactory::create(validMySQLConnectionString());
         EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
         LeaseMgrFactory::destroy();
     } catch (const isc::Exception& ex) {
@@ -256,7 +155,7 @@ TEST(MySqlOpenTest, OpenDatabase) {
         NoDatabaseName);
 
     // Tidy up after the test
-    destroySchema();
+    destroyMySQLSchema();
 }
 
 /// @brief Check the getType() method

+ 131 - 0
src/lib/dhcpsrv/tests/mysql_schema.cc

@@ -0,0 +1,131 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <string>
+#include <mysql.h>
+#include <dhcpsrv/mysql_connection.h>
+#include <gtest/gtest.h>
+
+// This holds statements to create and destroy the schema.
+#include "schema_mysql_copy.h"
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+// Connection strings.
+// Database: keatest
+// Host: localhost
+// Username: keatest
+// Password: keatest
+const char* VALID_TYPE = "type=mysql";
+const char* INVALID_TYPE = "type=unknown";
+const char* VALID_NAME = "name=keatest";
+const char* INVALID_NAME = "name=invalidname";
+const char* VALID_HOST = "host=localhost";
+const char* INVALID_HOST = "host=invalidhost";
+const char* VALID_USER = "user=keatest";
+const char* INVALID_USER = "user=invaliduser";
+const char* VALID_PASSWORD = "password=keatest";
+const char* INVALID_PASSWORD = "password=invalid";
+
+string connectionString(const char* type, const char* name, const char* host,
+                        const char* user, const char* password) {
+    const string space = " ";
+    string result = "";
+
+    if (type != NULL) {
+        result += string(type);
+    }
+    if (name != NULL) {
+        if (! result.empty()) {
+            result += space;
+        }
+        result += string(name);
+    }
+
+    if (host != NULL) {
+        if (! result.empty()) {
+            result += space;
+        }
+        result += string(host);
+    }
+
+    if (user != NULL) {
+        if (! result.empty()) {
+            result += space;
+        }
+        result += string(user);
+    }
+
+    if (password != NULL) {
+        if (! result.empty()) {
+            result += space;
+        }
+        result += string(password);
+    }
+
+    return (result);
+}
+
+// Return valid connection string
+string
+validMySQLConnectionString() {
+    return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
+                             VALID_USER, VALID_PASSWORD));
+}
+
+// @brief Clear everything from the database
+//
+// There is no error checking in this code: if something fails, one of the
+// tests will (should) fall over.
+void destroyMySQLSchema() {
+    MySqlHolder mysql;
+
+    // Open database
+    (void) mysql_real_connect(mysql, "localhost", "keatest",
+                              "keatest", "keatest", 0, NULL, 0);
+
+    // Get rid of everything in it.
+    for (int i = 0; destroy_statement[i] != NULL; ++i) {
+        (void) mysql_query(mysql, destroy_statement[i]);
+    }
+}
+
+// @brief Create the Schema
+//
+// Creates all the tables in what is assumed to be an empty database.
+//
+// There is no error checking in this code: if it fails, one of the tests
+// will fall over.
+void createMySQLSchema() {
+    MySqlHolder mysql;
+
+    // Open database
+    (void) mysql_real_connect(mysql, "localhost", "keatest",
+                              "keatest", "keatest", 0, NULL, 0);
+
+    // Execute creation statements.
+    for (int i = 0; create_statement[i] != NULL; ++i) {
+        ASSERT_EQ(0, mysql_query(mysql, create_statement[i]))
+            << "Failed on statement " << i << ": " << create_statement[i];
+    }
+}
+
+};
+};
+};

+ 69 - 0
src/lib/dhcpsrv/tests/mysql_schema.h

@@ -0,0 +1,69 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef TEST_MYSQL_SCHEMA_H
+#define TEST_MYSQL_SCHEMA_H
+
+#include <config.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+extern const char* VALID_TYPE;
+extern const char* INVALID_TYPE;
+extern const char* VALID_NAME;
+extern const char* INVALID_NAME;
+extern const char* VALID_HOST;
+extern const char* INVALID_HOST;
+extern const char* VALID_USER;
+extern const char* INVALID_USER;
+extern const char* VALID_PASSWORD;
+extern const char* INVALID_PASSWORD;
+
+/// @brief Create the Schema
+///
+/// Creates all the tables in what is assumed to be an empty database.
+///
+/// There is no error checking in this code: if it fails, one of the tests
+/// will fall over.
+void createMySQLSchema();
+
+/// @brief Clear everything from the database
+///
+/// There is no error checking in this code: if something fails, one of the
+/// tests will (should) fall over.
+void destroyMySQLSchema();
+
+/// Return valid connection string
+///
+/// @return valid MySQL connection string.
+std::string validMySQLConnectionString();
+
+/// @brief Given a combination of strings above, produce a connection string.
+///
+/// @param type type of the database
+/// @param name name of the database to connect to
+/// @param host hostname
+/// @param user username used to authendicate during connection attempt
+/// @param password password used to authendicate during connection attempt
+/// @return string containing all specified parameters
+std::string connectionString(const char* type, const char* name, const char* host,
+                             const char* user, const char* password);
+};
+};
+};
+
+#endif