From 0b223d0364bf762ba4ecd7b48fe7a985b3309d77 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Mon, 12 Feb 2018 06:09:55 +0100 Subject: [PATCH] [5531] Done --- doc/examples/kea4/pgsql-reservations.json | 18 ++-- doc/examples/kea6/pgsql-reservations.json | 18 ++-- doc/guide/dhcp4-srv.xml | 18 +++- doc/guide/dhcp6-srv.xml | 18 +++- doc/guide/hooks.xml | 10 +- src/bin/dhcp4/json_config_parser.cc | 14 ++- src/bin/dhcp4/tests/config_parser_unittest.cc | 44 ++++++++- src/bin/dhcp4/tests/get_config_unittest.cc | 83 ++++++++++++++++- .../dhcp4/tests/get_config_unittest.cc.skel | 10 +- src/bin/dhcp6/json_config_parser.cc | 13 ++- src/bin/dhcp6/tests/config_parser_unittest.cc | 43 ++++++++- src/bin/dhcp6/tests/get_config_unittest.cc | 93 ++++++++++++++++++- .../dhcp6/tests/get_config_unittest.cc.skel | 8 +- src/lib/dhcpsrv/cfg_db_access.cc | 24 +++-- src/lib/dhcpsrv/cfg_db_access.h | 44 ++++++--- src/lib/dhcpsrv/parsers/dbaccess_parser.cc | 13 ++- src/lib/dhcpsrv/parsers/dbaccess_parser.h | 12 +-- src/lib/dhcpsrv/srv_config.cc | 11 +-- .../dhcpsrv/tests/cfg_db_access_unittest.cc | 39 +++++++- .../dhcpsrv/tests/dbaccess_parser_unittest.cc | 70 +++++++++----- 20 files changed, 491 insertions(+), 112 deletions(-) diff --git a/doc/examples/kea4/pgsql-reservations.json b/doc/examples/kea4/pgsql-reservations.json index d01b966a30..a7c3edb4a3 100644 --- a/doc/examples/kea4/pgsql-reservations.json +++ b/doc/examples/kea4/pgsql-reservations.json @@ -54,13 +54,17 @@ // 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 diff --git a/doc/examples/kea6/pgsql-reservations.json b/doc/examples/kea6/pgsql-reservations.json index 7f7f70369c..6f930753df 100644 --- a/doc/examples/kea6/pgsql-reservations.json +++ b/doc/examples/kea6/pgsql-reservations.json @@ -41,13 +41,17 @@ // 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 diff --git a/doc/guide/dhcp4-srv.xml b/doc/guide/dhcp4-srv.xml index 83de4dadb9..dc0743542b 100644 --- a/doc/guide/dhcp4-srv.xml +++ b/doc/guide/dhcp4-srv.xml @@ -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. -
+ 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. + +
DHCPv4 Hosts Database Configuration 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. If there is no password to the account, set the password to the empty string "". (This is also the default.) + + 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: + +"Dhcp4": { "hosts-databases": [ { "type": "mysql", ... }, ... ], ... } + + + +
diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml index f5e1ae340e..8a7df130df 100644 --- a/doc/guide/dhcp6-srv.xml +++ b/doc/guide/dhcp6-srv.xml @@ -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. -
+ 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. + +
DHCPv6 Hosts Database Configuration 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. If there is no password to the account, set the password to the empty string "". (This is also the default.) + + 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: + +"Dhcp6": { "hosts-databases": [ { "type": "mysql", ... }, ... ], ... } + + + +
diff --git a/doc/guide/hooks.xml b/doc/guide/hooks.xml index 50115e3106..0115a00e24 100644 --- a/doc/guide/hooks.xml +++ b/doc/guide/hooks.xml @@ -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 - and ) 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 + and ) 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. @@ -1146,8 +1146,8 @@ Here is an example of complex IPv6 reservation: As reservation-add 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. diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index ea009f5a58..5d39d1cb2c 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -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; } diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index da4b250be7..ae70f42cac 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -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& 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, ""); diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc index be47e2599d..f82269eb80 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -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" diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc.skel b/src/bin/dhcp4/tests/get_config_unittest.cc.skel index b295974557..0f764b31fc 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc.skel +++ b/src/bin/dhcp4/tests/get_config_unittest.cc.skel @@ -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 { diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index a999ebe407..f7d31980c2 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -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; } diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index ead3a0d958..8910f69a7b 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -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& 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, ""); diff --git a/src/bin/dhcp6/tests/get_config_unittest.cc b/src/bin/dhcp6/tests/get_config_unittest.cc index 36a54a330b..77e5ad4604 100644 --- a/src/bin/dhcp6/tests/get_config_unittest.cc +++ b/src/bin/dhcp6/tests/get_config_unittest.cc @@ -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 { diff --git a/src/bin/dhcp6/tests/get_config_unittest.cc.skel b/src/bin/dhcp6/tests/get_config_unittest.cc.skel index bd08eae7ce..3ac9a3216a 100644 --- a/src/bin/dhcp6/tests/get_config_unittest.cc.skel +++ b/src/bin/dhcp6/tests/get_config_unittest.cc.skel @@ -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 { diff --git a/src/lib/dhcpsrv/cfg_db_access.cc b/src/lib/dhcpsrv/cfg_db_access.cc index 44c4b67040..ad6c396587 100644 --- a/src/lib/dhcpsrv/cfg_db_access.cc +++ b/src/lib/dhcpsrv/cfg_db_access.cc @@ -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 +CfgDbAccess::getHostDbAccessStringList() const { + std::vector 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); } } diff --git a/src/lib/dhcpsrv/cfg_db_access.h b/src/lib/dhcpsrv/cfg_db_access.h index 2c87e4dafc..2f8f95ea46 100644 --- a/src/lib/dhcpsrv/cfg_db_access.h +++ b/src/lib/dhcpsrv/cfg_db_access.h @@ -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 #include #include +#include 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 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 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); } }; diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc index 5fb6434c0e..cd7b4e4179 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc @@ -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()); } } diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.h b/src/lib/dhcpsrv/parsers/dbaccess_parser.h index 4e662d43f1..94943ce154 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.h +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.h @@ -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 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 values_; ///< Stored parameter values - DBType type_; ///< Database type (leases or hosts) + size_t type_; ///< Database type (leases or hosts) }; }; // namespace dhcp diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index fef466d19f..473310c53f 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -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; diff --git a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc index cc8361ae50..1e931ea073 100644 --- a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc @@ -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(expected, CfgLeaseDbAccess(cfg)); EXPECT_TRUE(cfg.getHostDbAccessString().empty()); - runToElementTest("{ }", CfgHostDbAccess(cfg)); + runToElementTest("[ ]", 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(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(expected, CfgHostDbAccess(cfg)); + + // Check access strings + std::vector 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(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; diff --git a/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc index 0fe421c902..052b881101 100644 --- a/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc @@ -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 -- 2.47.3