]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5531] Done trac5531p_base trac5532_base
authorFrancis Dupont <fdupont@isc.org>
Mon, 12 Feb 2018 05:09:55 +0000 (06:09 +0100)
committerFrancis Dupont <fdupont@isc.org>
Mon, 12 Feb 2018 05:09:55 +0000 (06:09 +0100)
20 files changed:
doc/examples/kea4/pgsql-reservations.json
doc/examples/kea6/pgsql-reservations.json
doc/guide/dhcp4-srv.xml
doc/guide/dhcp6-srv.xml
doc/guide/hooks.xml
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp4/tests/config_parser_unittest.cc
src/bin/dhcp4/tests/get_config_unittest.cc
src/bin/dhcp4/tests/get_config_unittest.cc.skel
src/bin/dhcp6/json_config_parser.cc
src/bin/dhcp6/tests/config_parser_unittest.cc
src/bin/dhcp6/tests/get_config_unittest.cc
src/bin/dhcp6/tests/get_config_unittest.cc.skel
src/lib/dhcpsrv/cfg_db_access.cc
src/lib/dhcpsrv/cfg_db_access.h
src/lib/dhcpsrv/parsers/dbaccess_parser.cc
src/lib/dhcpsrv/parsers/dbaccess_parser.h
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc
src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc

index d01b966a30a687ae73b28d022d922d2fb659bffb..a7c3edb4a3d78aebb009e69c9aabfb2279517801 100644 (file)
 // reservations list, within the subnet (configuration file). If there are
 // no reservations there, the server will try to retrieve reservations
 // from this database.
-  "hosts-database": {
-    "type": "postgresql",
-    "name": "kea",
-    "user": "kea",
-    "password": "kea",
-    "host": "localhost"
-  },
+// The database specification can go into one hosts-database entry for
+// backward compatibility or be listed in hosts-databases list.
+  "hosts-databases": [
+    {
+       "type": "postgresql",
+       "name": "kea",
+       "user": "kea",
+       "password": "kea",
+       "host": "localhost"
+    }
+  ],
 
 // Define a subnet with a single pool of dynamic addresses. Addresses from
 // this pool will be assigned to clients which don't have reservations in the
index 7f7f70369c1a3be15c61a0afc3882aa910ec3ba3..6f930753df425c823e7dcea35bb77e59500df464 100644 (file)
 // reservations list, within the subnet (configuration file). If there are
 // no reservations there, the server will try to retrieve reservations
 // from this database.
-  "hosts-database": {
-    "type": "postgresql",
-    "name": "kea",
-    "user": "kea",
-    "password": "kea",
-    "host": "localhost"
-  },
+// The database specification can go into one hosts-database entry for
+// backward compatibility or be listed in hosts-databases list.
+  "hosts-databases": [
+    {
+       "type": "postgresql",
+       "name": "kea",
+       "user": "kea",
+       "password": "kea",
+       "host": "localhost"
+    }
+  ],
 
 // Define a subnet with a pool of dynamic addresses and a pool of dynamic
 // prefixes. Addresses and prefixes from those pools will be assigned to
index 83de4dadb9edb33f9ed011dfdb1d3169c55cb251..dc0743542b0b7f2063036327ac4caa6f96bd5f91 100644 (file)
@@ -529,7 +529,13 @@ If a timeout is given though, it should be an integer greater than zero.
     from the configuration file are checked first and external storage is checked
     later, if necessary.</para>
 
-<section xml:id="hosts-database-configuration4">
+    <para>Version 1.4 extends the host storage to multiple storages. Operations
+    are performed on host storages in the configuration order with a special
+    case for addition: read-only storages must be configured after a
+    required read-write storage, or host reservation addition will
+    always fail.</para>
+
+<section xml:id="hosts-databases-configuration4">
   <title>DHCPv4 Hosts Database Configuration</title>
 
   <para>Hosts database configuration is controlled through the Dhcp4/hosts-database
@@ -572,6 +578,16 @@ If a timeout is given though, it should be an integer greater than zero.
 </screen>
   If there is no password to the account, set the password to the empty string
   "". (This is also the default.)</para>
+
+  <para>The multiple storage extension uses a similar syntax: a configuration
+  is placed into a "hosts-databases" list instead of into a "hosts-database"
+  entry as in:
+<screen>
+"Dhcp4": { "hosts-databases": [ { <userinput>"type": "mysql"</userinput>, ... }, ... ], ... }
+</screen>
+
+   </para>
+
 </section>
 
 <section xml:id="read-only-database-configuration4">
index f5e1ae340eaf3aab3d7b513da3510ab09fa8b4ee..8a7df130df9ef475c5f427ed77244d3c053c84e9 100644 (file)
@@ -525,7 +525,13 @@ If a timeout is given though, it should be an integer greater than zero.
     from the configuration file are checked first and external storage is checked
     later, if necessary.</para>
 
-<section xml:id="hosts-database-configuration6">
+    <para>Version 1.4 extends the host storage to multiple storages. Operations
+    are performed on host storages in the configuration order with a special
+    case for addition: read-only storages must be configured after a
+    required read-write storage, or host reservation addition will
+    always fail.</para>
+
+<section xml:id="hosts-databases-configuration6">
   <title>DHCPv6 Hosts Database Configuration</title>
 
   <para>Hosts database configuration is controlled through the Dhcp6/hosts-database
@@ -565,6 +571,16 @@ If a timeout is given though, it should be an integer greater than zero.
 </screen>
   If there is no password to the account, set the password to the empty string
   "". (This is also the default.)</para>
+
+  <para>The multiple storage extension uses a similar syntax: a configuration
+  is placed into a "hosts-databases" list instead of into a "hosts-database"
+  entry as in:
+<screen>
+"Dhcp6": { "hosts-databases": [ { <userinput>"type": "mysql"</userinput>, ... }, ... ], ... }
+</screen>
+
+  </para>
+
 </section>
 
 <section xml:id="read-only-database-configuration6">
index 50115e310616c766b672b9ce5631651ed1057682..0115a00e243462d2063d62cea9d1154511ee2381 100644 (file)
@@ -1025,9 +1025,9 @@ Administrator deleted a lease for a device identified by: duid of 1a:1b:1c:1d:1e
           criteria). To use commands that change the reservation information
           (currently these are reservation-add and reservation-del, but this
           rule applies to other commands that may be implemented in the future),
-          hosts database must be specified (see hosts-database description in
-          <xref linkend="hosts-database-configuration4"/> and <xref linkend="hosts-database-configuration6"/>) and it must not operate in
-          read-only mode. If the hosts-database is not specified or is running
+          hosts database must be specified (see hosts-databases description in
+          <xref linkend="hosts-databases-configuration4"/> and <xref linkend="hosts-databases-configuration6"/>) and it must not operate in
+          read-only mode. If the hosts-databases are not specified or are running
           in read-only mode, the host_cmds library will load, but any attempts
           to use reservation-add or reservation-del will fail.
         </para>
@@ -1146,8 +1146,8 @@ Here is an example of complex IPv6 reservation:
 
         <para>
           As <command>reservation-add</command> is expected to store the host,
-          hosts-database parameter must be specified in your configuration and
-          the database must not run in read-only mode. In the future versions
+          hosts-databases parameter must be specified in your configuration and
+          databases must not run in read-only mode. In the future versions
           it will be possible to modify the reservations read from a
           configuration file. Please contact ISC if you are interested in this
           functionality.
index ea009f5a58100bad4770573fa8b8c306eb52606b..5d39d1cb2c0f0fffb74680deb5a44924830dbc1b 100644 (file)
@@ -412,27 +412,25 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set,
 
             // Please move at the end when migration will be finished.
             if (config_pair.first == "lease-database") {
-                DbAccessParser parser(DbAccessParser::LEASE_DB);
+                DbAccessParser parser(CfgDbAccess::LEASE_DB);
                 CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess();
                 parser.parse(cfg_db_access, config_pair.second);
                 continue;
             }
 
             if (config_pair.first == "hosts-database") {
-                DbAccessParser parser(DbAccessParser::HOSTS_DB);
+                DbAccessParser parser(CfgDbAccess::HOSTS_DB);
                 CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess();
                 parser.parse(cfg_db_access, config_pair.second);
                 continue;
             }
 
-            // For now only support empty or singleton, ignoring extra entries.
             if (config_pair.first == "hosts-databases") {
-                if (config_pair.second->size() == 0) {
-                    continue;
-                }
-                DbAccessParser parser(DbAccessParser::HOSTS_DB);
                 CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess();
-                parser.parse(cfg_db_access, config_pair.second->get(0));
+                for (size_t i = 0; i < config_pair.second->size(); ++i) {
+                    DbAccessParser parser(CfgDbAccess::HOSTS_DB + i);
+                    parser.parse(cfg_db_access, config_pair.second->get(i));
+                }
                 continue;
             }
 
index da4b250be76cf2159af0ce0c6ab28f55bf53cc30..ae70f42cac5cfdca786f54a45d5772cb6a6caff9 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -131,6 +131,28 @@ const char* PARSER_CONFIGS[] = {
     "     } ]"
     "}",
 
+    // Configuration 4: two host databases
+    "{"
+    "    \"interfaces-config\": {"
+    "        \"interfaces\": [\"*\" ]"
+    "    },"
+    "    \"valid-lifetime\": 4000,"
+    "    \"rebind-timer\": 2000,"
+    "    \"renew-timer\": 1000,"
+    "    \"hosts-databases\": [ {"
+    "        \"type\": \"mysql\","
+    "        \"name\": \"keatest1\","
+    "        \"user\": \"keatest\","
+    "        \"password\": \"keatest\""
+    "      },{"
+    "        \"type\": \"mysql\","
+    "        \"name\": \"keatest2\","
+    "        \"user\": \"keatest\","
+    "        \"password\": \"keatest\""
+    "      }"
+    "    ]"
+    "}",
+
     // Last Configuration for comments
     "{"
     "    \"comment\": \"A DHCPv4 server\","
@@ -5757,10 +5779,28 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDeriveClientClass) {
     EXPECT_TRUE(classes.empty());
 }
 
+// This test checks multiple host data sources.
+TEST_F(Dhcp4ParserTest, hostsDatabases) {
+
+    string config = PARSER_CONFIGS[4];
+    extractConfig(config);
+    configure(config, CONTROL_RESULT_SUCCESS, "");
+
+    // Check database config
+    ConstCfgDbAccessPtr cfgdb =
+        CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
+    ASSERT_TRUE(cfgdb);
+    const std::vector<std::string>& hal = cfgdb->getHostDbAccessStringList();
+    ASSERT_EQ(2, hal.size());
+    // Keywords are in alphabetical order
+    EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest", hal[0]);
+    EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest", hal[1]);
+}
+
 // This test checks comments. Please keep it last.
 TEST_F(Dhcp4ParserTest, comments) {
 
-    string config = PARSER_CONFIGS[4];
+    string config = PARSER_CONFIGS[5];
     extractConfig(config);
     configure(config, CONTROL_RESULT_SUCCESS, "");
 
index be47e2599d2353d681d46f5517b0bb5bbbb7193a..f82269eb804c09b8425b5b3e6fff82400bf1d764 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -1719,6 +1719,30 @@ const char* EXTRACTED_CONFIGS[] = {
 "    }\n",
     // CONFIGURATION 60
 "{\n"
+"        \"hosts-databases\": [\n"
+"            {\n"
+"                \"name\": \"keatest1\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            },\n"
+"            {\n"
+"                \"name\": \"keatest2\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            }\n"
+"        ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ],\n"
+"            \"re-detect\": false\n"
+"        },\n"
+"        \"rebind-timer\": 2000,\n"
+"        \"renew-timer\": 1000,\n"
+"        \"valid-lifetime\": 4000\n"
+"    }\n",
+    // CONFIGURATION 61
+"{\n"
 "        \"comment\": \"A DHCPv4 server\",\n"
 "        \"client-classes\": [\n"
 "            {\n"
@@ -6683,6 +6707,63 @@ const char* UNPARSED_CONFIGS[] = {
 "    }\n",
     // CONFIGURATION 60
 "{\n"
+"        \"decline-probation-period\": 86400,\n"
+"        \"dhcp-ddns\": {\n"
+"            \"always-include-fqdn\": false,\n"
+"            \"enable-updates\": false,\n"
+"            \"generated-prefix\": \"myhost\",\n"
+"            \"max-queue-size\": 1024,\n"
+"            \"ncr-format\": \"JSON\",\n"
+"            \"ncr-protocol\": \"UDP\",\n"
+"            \"override-client-update\": false,\n"
+"            \"override-no-update\": false,\n"
+"            \"qualifying-suffix\": \"\",\n"
+"            \"replace-client-name\": \"never\",\n"
+"            \"sender-ip\": \"0.0.0.0\",\n"
+"            \"sender-port\": 0,\n"
+"            \"server-ip\": \"127.0.0.1\",\n"
+"            \"server-port\": 53001\n"
+"        },\n"
+"        \"dhcp4o6-port\": 0,\n"
+"        \"echo-client-id\": true,\n"
+"        \"expired-leases-processing\": {\n"
+"            \"flush-reclaimed-timer-wait-time\": 25,\n"
+"            \"hold-reclaimed-time\": 3600,\n"
+"            \"max-reclaim-leases\": 100,\n"
+"            \"max-reclaim-time\": 250,\n"
+"            \"reclaim-timer-wait-time\": 10,\n"
+"            \"unwarned-reclaim-cycles\": 5\n"
+"        },\n"
+"        \"hooks-libraries\": [ ],\n"
+"        \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\", \"client-id\" ],\n"
+"        \"hosts-databases\": [\n"
+"            {\n"
+"                \"name\": \"keatest1\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            },\n"
+"            {\n"
+"                \"name\": \"keatest2\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            }\n"
+"        ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ],\n"
+"            \"re-detect\": false\n"
+"        },\n"
+"        \"lease-database\": {\n"
+"            \"type\": \"memfile\"\n"
+"        },\n"
+"        \"option-data\": [ ],\n"
+"        \"option-def\": [ ],\n"
+"        \"shared-networks\": [ ],\n"
+"        \"subnet4\": [ ]\n"
+"    }\n",
+    // CONFIGURATION 61
+"{\n"
 "        \"comment\": \"A DHCPv4 server\",\n"
 "        \"client-classes\": [\n"
 "            {\n"
index b2959745576daf5abf13a3ad390023e98ac0e1e9..0f764b31fcfdb8a2e30a88adc05091b5330d7833 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -124,7 +124,7 @@ outputFormatted(const std::string& config) {
     }
 }
 
-};
+}  // namespace
 
 namespace isc {
 namespace dhcp {
@@ -155,9 +155,9 @@ extractConfig(const std::string& config) {
     ++extract_count;
 }
 
-};
-};
-};
+}  // namespace test
+}  // namespace dhcp
+}  // namespace isc
 
 namespace {
 
index a999ebe407789931d11b8f431bb3bacf65c1798c..f7d31980c2502f13991e736418f63ba58a3a3dec 100644 (file)
@@ -525,14 +525,14 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set,
 
             // Please move at the end when migration will be finished.
             if (config_pair.first == "lease-database") {
-                DbAccessParser parser(DbAccessParser::LEASE_DB);
+                DbAccessParser parser(CfgDbAccess::LEASE_DB);
                 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
                 parser.parse(cfg_db_access, config_pair.second);
                 continue;
             }
 
             if (config_pair.first == "hosts-database") {
-                DbAccessParser parser(DbAccessParser::HOSTS_DB);
+                DbAccessParser parser(CfgDbAccess::HOSTS_DB);
                 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
                 parser.parse(cfg_db_access, config_pair.second);
                 continue;
@@ -540,12 +540,11 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set,
 
             // For now only support empty or singleton, ignoring extra entries.
             if (config_pair.first == "hosts-databases") {
-                if (config_pair.second->size() == 0) {
-                    continue;
-                }
-                DbAccessParser parser(DbAccessParser::HOSTS_DB);
                 CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
-                parser.parse(cfg_db_access, config_pair.second->get(0));
+                for (size_t i = 0; i < config_pair.second->size(); ++i) {
+                    DbAccessParser parser(CfgDbAccess::HOSTS_DB + i);
+                    parser.parse(cfg_db_access, config_pair.second->get(i));
+                }
                 continue;
             }
 
index ead3a0d9585b170c279d1a554024a1c234b5c9dd..8910f69a7b6660f889bb9e66fd72dcf032f5f8dc 100644 (file)
@@ -208,6 +208,29 @@ const char* PARSER_CONFIGS[] = {
     "     } ]"
     "}",
 
+    // Configuration 7: two host databases
+    "{"
+    "    \"interfaces-config\": {"
+    "        \"interfaces\": [\"*\" ]"
+    "    },"
+    "    \"valid-lifetime\": 4000,"
+    "    \"preferred-lifetime\": 3000,"
+    "    \"rebind-timer\": 2000,"
+    "    \"renew-timer\": 1000,"
+    "    \"hosts-databases\": [ {"
+    "        \"type\": \"mysql\","
+    "        \"name\": \"keatest1\","
+    "        \"user\": \"keatest\","
+    "        \"password\": \"keatest\""
+    "      },{"
+    "        \"type\": \"mysql\","
+    "        \"name\": \"keatest2\","
+    "        \"user\": \"keatest\","
+    "        \"password\": \"keatest\""
+    "      }"
+    "    ]"
+    "}",
+
     // Last configuration for comments
     "{"
     "    \"comment\": \"A DHCPv6 server\","
@@ -6314,10 +6337,28 @@ TEST_F(Dhcp6ParserTest, sharedNetworksRapidCommitMix) {
               "shared-network or the shared-network itself used rapid-commit true");
 }
 
+// This test checks multiple host data sources.
+TEST_F(Dhcp6ParserTest, hostsDatabases) {
+
+    string config = PARSER_CONFIGS[7];
+    extractConfig(config);
+    configure(config, CONTROL_RESULT_SUCCESS, "");
+
+    // Check database config
+    ConstCfgDbAccessPtr cfgdb =
+        CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
+    ASSERT_TRUE(cfgdb);
+    const std::vector<std::string>& hal = cfgdb->getHostDbAccessStringList();
+    ASSERT_EQ(2, hal.size());
+    // Keywords are in alphabetical order
+    EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest", hal[0]);
+    EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest", hal[1]);
+}
+
 // This test checks comments. Please keep it last.
 TEST_F(Dhcp6ParserTest, comments) {
 
-    string config = PARSER_CONFIGS[7];
+    string config = PARSER_CONFIGS[8];
     extractConfig(config);
     configure(config, CONTROL_RESULT_SUCCESS, "");
 
index 36a54a330b129cf5a4796c6eb76fa91edf8674a8..77e5ad4604ee49d2ee654b1a825570f98d08c598 100644 (file)
@@ -1584,6 +1584,31 @@ const char* EXTRACTED_CONFIGS[] = {
 "    }\n",
     // CONFIGURATION 53
 "{\n"
+"        \"hosts-databases\": [\n"
+"            {\n"
+"                \"name\": \"keatest1\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            },\n"
+"            {\n"
+"                \"name\": \"keatest2\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            }\n"
+"        ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ],\n"
+"            \"re-detect\": false\n"
+"        },\n"
+"        \"preferred-lifetime\": 3000,\n"
+"        \"rebind-timer\": 2000,\n"
+"        \"renew-timer\": 1000,\n"
+"        \"valid-lifetime\": 4000\n"
+"    }\n",
+    // CONFIGURATION 54
+"{\n"
 "        \"comment\": \"A DHCPv6 server\",\n"
 "        \"client-classes\": [\n"
 "            {\n"
@@ -6240,6 +6265,72 @@ const char* UNPARSED_CONFIGS[] = {
 "    }\n",
     // CONFIGURATION 53
 "{\n"
+"        \"decline-probation-period\": 86400,\n"
+"        \"dhcp-ddns\": {\n"
+"            \"always-include-fqdn\": false,\n"
+"            \"enable-updates\": false,\n"
+"            \"generated-prefix\": \"myhost\",\n"
+"            \"max-queue-size\": 1024,\n"
+"            \"ncr-format\": \"JSON\",\n"
+"            \"ncr-protocol\": \"UDP\",\n"
+"            \"override-client-update\": false,\n"
+"            \"override-no-update\": false,\n"
+"            \"qualifying-suffix\": \"\",\n"
+"            \"replace-client-name\": \"never\",\n"
+"            \"sender-ip\": \"0.0.0.0\",\n"
+"            \"sender-port\": 0,\n"
+"            \"server-ip\": \"127.0.0.1\",\n"
+"            \"server-port\": 53001\n"
+"        },\n"
+"        \"dhcp4o6-port\": 0,\n"
+"        \"expired-leases-processing\": {\n"
+"            \"flush-reclaimed-timer-wait-time\": 25,\n"
+"            \"hold-reclaimed-time\": 3600,\n"
+"            \"max-reclaim-leases\": 100,\n"
+"            \"max-reclaim-time\": 250,\n"
+"            \"reclaim-timer-wait-time\": 10,\n"
+"            \"unwarned-reclaim-cycles\": 5\n"
+"        },\n"
+"        \"hooks-libraries\": [ ],\n"
+"        \"host-reservation-identifiers\": [ \"hw-address\", \"duid\" ],\n"
+"        \"hosts-databases\": [\n"
+"            {\n"
+"                \"name\": \"keatest1\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            },\n"
+"            {\n"
+"                \"name\": \"keatest2\",\n"
+"                \"password\": \"keatest\",\n"
+"                \"type\": \"mysql\",\n"
+"                \"user\": \"keatest\"\n"
+"            }\n"
+"        ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ],\n"
+"            \"re-detect\": false\n"
+"        },\n"
+"        \"lease-database\": {\n"
+"            \"type\": \"memfile\"\n"
+"        },\n"
+"        \"mac-sources\": [ \"any\" ],\n"
+"        \"option-data\": [ ],\n"
+"        \"option-def\": [ ],\n"
+"        \"relay-supplied-options\": [ \"65\" ],\n"
+"        \"server-id\": {\n"
+"            \"enterprise-id\": 0,\n"
+"            \"htype\": 0,\n"
+"            \"identifier\": \"\",\n"
+"            \"persist\": true,\n"
+"            \"time\": 0,\n"
+"            \"type\": \"LLT\"\n"
+"        },\n"
+"        \"shared-networks\": [ ],\n"
+"        \"subnet6\": [ ]\n"
+"    }\n",
+    // CONFIGURATION 54
+"{\n"
 "        \"comment\": \"A DHCPv6 server\",\n"
 "        \"client-classes\": [\n"
 "            {\n"
@@ -6459,7 +6550,7 @@ outputFormatted(const std::string& config) {
     }
 }
 
-}
+} // namespace
 
 namespace isc {
 namespace dhcp {
index bd08eae7ce09517ca9852e4bb3dea62c3a68ce32..3ac9a3216a3701957608b39e96fe70758fb0717a 100644 (file)
@@ -125,7 +125,7 @@ outputFormatted(const std::string& config) {
     }
 }
 
-};
+} // namespace
 
 namespace isc {
 namespace dhcp {
@@ -156,9 +156,9 @@ extractConfig(const std::string& config) {
     ++extract_count;
 }
 
-};
-};
-};
+}  // namespace test
+}  // namespace dhcp
+}  // namespace isc
 
 namespace {
 
index 44c4b670400e8bbf4f443a78d793c1d09d61cd9c..ad6c3965875a96039a505275ea086ee5af782e17 100644 (file)
@@ -21,21 +21,31 @@ namespace isc {
 namespace dhcp {
 
 CfgDbAccess::CfgDbAccess()
-    : appended_parameters_(), lease_db_access_("type=memfile"),
-      host_db_access_() {
+    : appended_parameters_(), db_access_(2) {
+    db_access_[LEASE_DB] = "type=memfile";
 }
 
 std::string
 CfgDbAccess::getLeaseDbAccessString() const {
-    return (getAccessString(lease_db_access_));
+    return (getAccessString(db_access_[LEASE_DB]));
 }
 
 
 std::string
 CfgDbAccess::getHostDbAccessString() const {
-    return (getAccessString(host_db_access_));
+    return (getAccessString(db_access_[HOSTS_DB]));
 }
 
+std::vector<std::string>
+CfgDbAccess::getHostDbAccessStringList() const {
+    std::vector<std::string> ret;
+    for (size_t idx = HOSTS_DB; idx < db_access_.size(); ++idx) {
+        if (!db_access_[idx].empty()) {
+            ret.push_back(getAccessString(db_access_[idx]));
+        }
+    }
+    return (ret);
+}
 
 void
 CfgDbAccess::createManagers() const {
@@ -45,8 +55,10 @@ CfgDbAccess::createManagers() const {
 
     // Recreate host data source.
     HostMgr::create();
-    if (!host_db_access_.empty()) {
-        HostMgr::addSource(getHostDbAccessString());
+    auto host_db_access_list = getHostDbAccessStringList();
+    for (auto it = host_db_access_list.begin();
+         it != host_db_access_list.end(); ++it) {
+        HostMgr::addSource(*it);
     }
 }
 
index 2c87e4dafce22a384f14b4a553988360ee1a5b83..2f8f95ea461f700558e8107b43efbefe20e00f22 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -10,6 +10,7 @@
 #include <cc/cfg_to_element.h>
 #include <boost/shared_ptr.hpp>
 #include <string>
+#include <vector>
 
 namespace isc {
 namespace dhcp {
@@ -21,6 +22,9 @@ namespace dhcp {
 /// passed to the @ref isc::dhcp::LeaseMgrFactory::create function.
 class CfgDbAccess {
 public:
+    /// @brief Specifies the database types
+    static const size_t LEASE_DB = 0;
+    static const size_t HOSTS_DB = 1;
 
     /// @brief Constructor.
     CfgDbAccess();
@@ -44,7 +48,7 @@ public:
     ///
     /// @param lease_db_access New lease database access string.
     void setLeaseDbAccessString(const std::string& lease_db_access) {
-        lease_db_access_ = lease_db_access;
+        db_access_[LEASE_DB] = lease_db_access;
     }
 
     /// @brief Retrieves host database access string.
@@ -57,10 +61,23 @@ public:
     ///
     /// @param host_db_access New host database access string.
     void setHostDbAccessString(const std::string& host_db_access) {
-        host_db_access_ = host_db_access;
+        db_access_[HOSTS_DB] = host_db_access;
     }
 
-    /// @brief Creates instance of lease manager and host data source
+    /// @brief Retrieves host database access string.
+    ///
+    /// @return Database access strings with additional parameters
+    /// specified with @ref CfgDbAccess::setAppendedParameters
+    std::vector<std::string> getHostDbAccessStringList() const;
+
+    /// @brief Pushes host database access string.
+    ///
+    /// @param db_access New host database access string.
+    void pushHostDbAccessString(const std::string& db_access) {
+        db_access_.push_back(db_access);
+    }
+
+    /// @brief Creates instance of lease manager and host data sources
     /// according to the configuration specified.
     void createManagers() const;
 
@@ -82,11 +99,8 @@ protected:
     /// 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 Holds database access strings.
+    std::vector<std::string> db_access_;
 
 };
 
@@ -107,7 +121,7 @@ struct CfgLeaseDbAccess : public CfgDbAccess, public isc::data::CfgToElement {
     ///
     /// @result a pointer to a configuration
     virtual isc::data::ElementPtr toElement() const {
-        return (CfgDbAccess::toElementDbAccessString(lease_db_access_));
+        return (CfgDbAccess::toElementDbAccessString(db_access_[LEASE_DB]));
     }
 };
 
@@ -121,7 +135,15 @@ struct CfgHostDbAccess : public CfgDbAccess, public isc::data::CfgToElement {
     ///
     /// @result a pointer to a configuration
     virtual isc::data::ElementPtr toElement() const {
-        return (CfgDbAccess::toElementDbAccessString(host_db_access_));
+        isc::data::ElementPtr result = isc::data::Element::createList();
+        for (size_t idx = HOSTS_DB; idx < db_access_.size(); ++idx) {
+            isc::data::ElementPtr entry =
+                CfgDbAccess::toElementDbAccessString(db_access_[idx]);
+            if (entry->size() > 0) {
+                result->add(entry);
+            }
+        }
+        return (result);
     }
 };
 
index 5fb6434c0e8c3c6a570fc703789030881493ff3b..cd7b4e417972d051d71a7fe2d71d8f69492611da 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -30,7 +30,7 @@ namespace dhcp {
 
 
 // Factory function to build the parser
-DbAccessParser::DbAccessParser(DBType db_type)
+DbAccessParser::DbAccessParser(size_t db_type)
     : values_(), type_(db_type) {
 }
 
@@ -110,7 +110,8 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db,
     // a. Check if the "type" keyword exists and thrown an exception if not.
     StringPairMap::const_iterator type_ptr = values_copy.find("type");
     if (type_ptr == values_copy.end()) {
-        isc_throw(DhcpConfigError, (type_ == LEASE_DB ? "lease" : "host")
+        isc_throw(DhcpConfigError,
+                  (type_ == CfgDbAccess::LEASE_DB ? "lease" : "host")
                   << " database access parameters must "
                   "include the keyword 'type' to determine type of database "
                   "to be accessed (" << database_config->getPosition() << ")");
@@ -167,10 +168,12 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db,
     values_.swap(values_copy);
 
     // 5. Save the database access string in the Configuration Manager.
-    if (type_ == LEASE_DB) {
+    if (type_ == CfgDbAccess::LEASE_DB) {
         cfg_db->setLeaseDbAccessString(getDbAccessString());
-    } else if (type_ == HOSTS_DB) {
+    } else if (type_ == CfgDbAccess::HOSTS_DB) {
         cfg_db->setHostDbAccessString(getDbAccessString());
+    } else if (type_ > CfgDbAccess::HOSTS_DB) {
+        cfg_db->pushHostDbAccessString(getDbAccessString());
     }
 }
 
index 4e662d43f11007eed7e597a533d4edaa33be656d..94943ce154fff7308324cf848020b64288ec663c 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -28,12 +28,6 @@ namespace dhcp {
 class DbAccessParser: public isc::data::SimpleParser {
 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;
 
@@ -43,7 +37,7 @@ public:
     /// @brief Constructor
     ///
     /// @param db_type Specifies database type (lease or hosts)
-    explicit DbAccessParser(DBType db_type);
+    explicit DbAccessParser(size_t db_type);
 
     /// The destructor.
     virtual ~DbAccessParser()
@@ -98,7 +92,7 @@ private:
 
     std::map<std::string, std::string> values_; ///< Stored parameter values
 
-    DBType type_; ///< Database type (leases or hosts)
+    size_t type_; ///< Database type (leases or hosts)
 };
 
 };  // namespace dhcp
index fef466d19f10b215038168592036440c05370e12..473310c53fca28c8cad37513fa7ebabf1589c1d1 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -342,12 +342,11 @@ SrvConfig::toElement() const {
     // Set lease-database
     CfgLeaseDbAccess lease_db(*cfg_db_access_);
     dhcp->set("lease-database", lease_db.toElement());
-    // Set hosts-database
+    // Set hosts-databases
     CfgHostDbAccess host_db(*cfg_db_access_);
-    // @todo accept empty map
-    ConstElementPtr hosts_database = host_db.toElement();
-    if (hosts_database->size() > 0) {
-        dhcp->set("hosts-database", hosts_database);
+    ConstElementPtr hosts_databases = host_db.toElement();
+    if (hosts_databases->size() > 0) {
+        dhcp->set("hosts-databases", hosts_databases);
     }
     // Set host-reservation-identifiers
     ConstElementPtr host_ids;
index cc8361ae500635d0e63ac7d0c4999714e0c4d993..1e931ea0732eab71514e0c01390c9e672555700c 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -30,7 +30,7 @@ TEST(CfgDbAccessTest, defaults) {
     runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg));
 
     EXPECT_TRUE(cfg.getHostDbAccessString().empty());
-    runToElementTest<CfgHostDbAccess>("{ }", CfgHostDbAccess(cfg));
+    runToElementTest<CfgHostDbAccess>("[ ]", CfgHostDbAccess(cfg));
 }
 
 // This test verifies that it is possible to set the lease database
@@ -65,7 +65,7 @@ TEST(CfgDbAccessTest, setHostDbAccessString) {
     EXPECT_EQ("type=mysql", cfg.getHostDbAccessString());
 
     // Check unparse
-    std::string expected = "{ \"type\": \"mysql\" }";
+    std::string expected = "[ { \"type\": \"mysql\" } ]";
     runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg));
 
     // Append additional parameter.
@@ -80,6 +80,39 @@ TEST(CfgDbAccessTest, setHostDbAccessString) {
     EXPECT_TRUE(cfg.getHostDbAccessString().empty());
 }
 
+// This test verifies that it is possible to set multiple host
+// database string.
+TEST(CfgDbAccessTest, pushHostDbAccessString) {
+    CfgDbAccess cfg;
+    ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql"));
+    EXPECT_EQ("type=mysql", cfg.getHostDbAccessString());
+
+    // Push two other strings
+    ASSERT_NO_THROW(cfg.pushHostDbAccessString("type=foo"));
+    ASSERT_NO_THROW(cfg.pushHostDbAccessString("type=bar"));
+
+    // Check unparse
+    std::string expected = "[ { \"type\": \"mysql\" }, ";
+    expected += "{ \"type\": \"foo\" }, { \"type\": \"bar\" } ]";
+    runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg));
+
+    // Check access strings
+    std::vector<std::string> hal = cfg.getHostDbAccessStringList();
+    ASSERT_EQ(3, hal.size());
+    EXPECT_EQ("type=mysql", hal[0]);
+    EXPECT_EQ("type=foo", hal[1]);
+    EXPECT_EQ("type=bar", hal[2]);
+
+    // Reset the first string so it will be ignored.
+    ASSERT_NO_THROW(cfg.setHostDbAccessString(""));
+    expected = "[ { \"type\": \"foo\" }, { \"type\": \"bar\" } ]";
+    runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg));
+    hal = cfg.getHostDbAccessStringList();
+    ASSERT_EQ(2, hal.size());
+    EXPECT_EQ("type=foo", hal[0]);
+    EXPECT_EQ("type=bar", hal[1]);
+}
+
 // Tests that lease manager can be created from a specified configuration.
 TEST(CfgDbAccessTest, createLeaseMgr) {
     CfgDbAccess cfg;
index 0fe421c90245b5746168203f05f631d20170b3a8..052b88110160a9e8c2c81eb854593a7dca5b5847 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -195,7 +195,7 @@ public:
     /// @brief Constructor
     ///
     /// @brief Keyword/value collection of database access parameters
-    TestDbAccessParser(DbAccessParser::DBType type) 
+    TestDbAccessParser(size_t type) 
         : DbAccessParser(type)
     {}
 
@@ -243,7 +243,7 @@ TEST_F(DbAccessParserTest, validTypeMemfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
     checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
 }
@@ -257,7 +257,7 @@ TEST_F(DbAccessParserTest, hosts) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::HOSTS_DB);
+    TestDbAccessParser parser(CfgDbAccess::HOSTS_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
     checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
 }
@@ -273,7 +273,7 @@ TEST_F(DbAccessParserTest, emptyKeyword) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
     checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
 }
@@ -290,7 +290,7 @@ TEST_F(DbAccessParserTest, persistV4Memfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
 
     checkAccessString("Valid memfile", parser.getDbAccessParameters(),
@@ -309,7 +309,7 @@ TEST_F(DbAccessParserTest, persistV6Memfile) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
 
     checkAccessString("Valid memfile", parser.getDbAccessParameters(),
@@ -328,7 +328,7 @@ TEST_F(DbAccessParserTest, validLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
     checkAccessString("Valid LFC Interval", parser.getDbAccessParameters(),
                       config);
@@ -346,7 +346,7 @@ TEST_F(DbAccessParserTest, negativeLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
@@ -362,7 +362,7 @@ TEST_F(DbAccessParserTest, largeLFCInterval) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
@@ -378,7 +378,7 @@ TEST_F(DbAccessParserTest, validTimeout) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
     checkAccessString("Valid timeout", parser.getDbAccessParameters(),
                       config);
@@ -396,7 +396,7 @@ TEST_F(DbAccessParserTest, negativeTimeout) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
@@ -412,7 +412,7 @@ TEST_F(DbAccessParserTest, largeTimeout) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
@@ -428,7 +428,7 @@ TEST_F(DbAccessParserTest, validPort) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
     checkAccessString("Valid port", parser.getDbAccessParameters(),
                       config);
@@ -446,7 +446,7 @@ TEST_F(DbAccessParserTest, negativePort) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
@@ -462,7 +462,7 @@ TEST_F(DbAccessParserTest, largePort) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
@@ -480,7 +480,7 @@ TEST_F(DbAccessParserTest, validTypeMysql) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
     checkAccessString("Valid mysql", parser.getDbAccessParameters(), config);
 }
@@ -498,7 +498,7 @@ TEST_F(DbAccessParserTest, missingTypeKeyword) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
@@ -549,7 +549,7 @@ TEST_F(DbAccessParserTest, incrementalChanges) {
                              "name",     "keatest",
                              NULL};
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
 
     // First configuration string should cause a representation of that string
     // to be held.
@@ -613,7 +613,7 @@ TEST_F(DbAccessParserTest, getDbAccessString) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
 
     // Get the database access string
@@ -639,7 +639,7 @@ TEST_F(DbAccessParserTest, validReadOnly) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_NO_THROW(parser.parse(json_elements));
 
     checkAccessString("Valid readonly parameter",
@@ -661,9 +661,35 @@ TEST_F(DbAccessParserTest, invalidReadOnly) {
     ConstElementPtr json_elements = Element::fromJSON(json_config);
     EXPECT_TRUE(json_elements);
 
-    TestDbAccessParser parser(DbAccessParser::LEASE_DB);
+    TestDbAccessParser parser(CfgDbAccess::LEASE_DB);
     EXPECT_THROW(parser.parse(json_elements), DhcpConfigError);
 }
 
+// Check that multiple host storages are correctly parsed.
+TEST_F(DbAccessParserTest, multipleHost) {
+    const char* config1[] = {"type", "mysql",
+                             "name", "keatest1",
+                             NULL};
+    const char* config2[] = {"type", "mysql",
+                             "name", "keatest2",
+                             NULL};
+
+    string json_config1 = toJson(config1);
+    string json_config2 = toJson(config2);
+    ConstElementPtr json_elements1 = Element::fromJSON(json_config1);
+    ConstElementPtr json_elements2 = Element::fromJSON(json_config2);
+    
+    TestDbAccessParser parser1(2);
+    TestDbAccessParser parser2(3);
+    EXPECT_NO_THROW(parser1.parse(json_elements1));
+    EXPECT_NO_THROW(parser2.parse(json_elements2));
+
+    checkAccessString("First config",
+                      parser1.getDbAccessParameters(),
+                      config1);
+    checkAccessString("Second config",
+                      parser2.getDbAccessParameters(),
+                      config2);
+}
 
 };  // Anonymous namespace