BIND 10 master, updated. 2cbabde53f226cb4fa4883b9578268719f28732b [master] ChangeLog for trac2559

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Jan 21 22:19:01 UTC 2013


The branch, master has been updated
       via  2cbabde53f226cb4fa4883b9578268719f28732b (commit)
       via  6c6f405188cc02d2358e114c33daff58edabd52a (commit)
       via  5237d9415f6fb14c932695d59d1d2721b185bfe3 (commit)
       via  d117d8720025b9e57fd1cd2eae7792c0700cdfff (commit)
       via  aad83b6edde86e70a460cdf887b985841c161016 (commit)
       via  88d25efcbc3e7b6a8fe7dea5c78e40115be1b1f4 (commit)
       via  9f03e9dd3f34b0a502b0d69ec7f08b582011dcf6 (commit)
       via  79f0d62ef03b3774a0b0c6237eff78af821bdbc4 (commit)
       via  fdbba41a48cffe7efc185b8eb59fd3471aeb78d5 (commit)
       via  2b81c6b347116512da7c653b0ca542a04b82015a (commit)
       via  c61ae0fbbba155063ada04a08ec44f3dc27ab6bc (commit)
       via  808decc6c1639304e030a84d2bb2a04bd1b27db8 (commit)
       via  f4e272c729e2a77f720b06a22819013f6695f06b (commit)
       via  ff5861d1c7d7a689d775b251086d12032fcabd54 (commit)
       via  aae75525fe351f4df368402e7e05aa0e52b234b1 (commit)
       via  5267d959d14763af2a7c938a136267655722cd77 (commit)
       via  6a47ad417ae15c4cc9bad53347ae06088a642ec3 (commit)
       via  c74f64796e9901c453ab7d4e509d736d037884fd (commit)
       via  90f9161b787dfe68158c12bd07d2e362f2db8cc6 (commit)
       via  99d1141934d6bb027bfd402110b5706aa114adaf (commit)
       via  6f2e770cfbec4ffed1f314bed4e1b8b480c32c16 (commit)
      from  7e31b4ded3983b1271fec3799798b52c105b140e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 2cbabde53f226cb4fa4883b9578268719f28732b
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Jan 21 22:16:54 2013 +0000

    [master] ChangeLog for trac2559

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                         |    5 +
 src/bin/dhcp4/config_parser.cc                    |   35 +-
 src/bin/dhcp4/dhcp4.spec                          |   44 ++-
 src/bin/dhcp4/dhcp4_messages.mes                  |   32 ++
 src/bin/dhcp6/config_parser.cc                    |   35 +-
 src/bin/dhcp6/ctrl_dhcp6_srv.cc                   |    6 +-
 src/bin/dhcp6/ctrl_dhcp6_srv.h                    |    6 +-
 src/bin/dhcp6/dhcp6.spec                          |   44 ++-
 src/bin/dhcp6/dhcp6_messages.mes                  |   37 ++
 src/bin/dhcp6/dhcp6_srv.cc                        |    8 +-
 src/bin/dhcp6/dhcp6_srv.h                         |    7 +-
 src/bin/dhcp6/main.cc                             |   15 +-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc         |   13 +-
 src/lib/dhcpsrv/Makefile.am                       |    1 +
 src/lib/dhcpsrv/dbaccess_parser.cc                |  116 ++++++
 src/lib/dhcpsrv/dbaccess_parser.h                 |  133 +++++++
 src/lib/dhcpsrv/dhcp_config_parser.h              |    3 +
 src/lib/dhcpsrv/dhcpsrv_messages.mes              |   32 +-
 src/lib/dhcpsrv/lease_mgr_factory.cc              |   14 +-
 src/lib/dhcpsrv/memfile_lease_mgr.cc              |    5 +-
 src/lib/dhcpsrv/tests/Makefile.am                 |    3 +
 src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc |  434 +++++++++++++++++++++
 22 files changed, 949 insertions(+), 79 deletions(-)
 create mode 100644 src/lib/dhcpsrv/dbaccess_parser.cc
 create mode 100644 src/lib/dhcpsrv/dbaccess_parser.h
 create mode 100644 src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 6c64e34..2297f84 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+553.    [func]      stephen
+	Values of the parameters to access the DHCP server lease database
+	can now be set through the BIND 10 configuration mechanism.
+	(Trac #2559, git 6c6f405188cc02d2358e114c33daff58edabd52a)
+
 552.    [bug]       shane
     Build on Raspberry PI.
 	The main issue was use of char for reading from input streams,
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index fdb4240..67a7ec6 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -18,6 +18,7 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option_definition.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dbaccess_parser.h>
 #include <dhcpsrv/dhcp_config_parser.h>
 #include <dhcpsrv/option_space_container.h>
 #include <util/encode/hex.h>
@@ -49,9 +50,6 @@ typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
 typedef boost::shared_ptr<StringParser> StringParserPtr;
 typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
 
-/// @brief auxiliary type used for storing element name and its parser
-typedef pair<string, ConstElementPtr> ConfigPair;
-
 /// @brief a factory method that will create a parser for a given element name
 typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
 
@@ -1607,6 +1605,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
     factories["option-data"] = OptionDataListParser::factory;
     factories["option-def"] = OptionDefListParser::factory;
     factories["version"] = StringParser::factory;
+    factories["lease-database"] = DbAccessParser::factory;
 
     FactoryMap::iterator f = factories.find(config_id);
     if (f == factories.end()) {
@@ -1661,13 +1660,18 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
     // rollback informs whether error occured and original data
     // have to be restored to global storages.
     bool rollback = false;
-
+    // config_pair holds the details of the current parser when iterating over
+    // the parsers.  It is declared outside the loops so in case of an error,
+    // the name of the failing parser can be retrieved in the "catch" clause.
+    ConfigPair config_pair;
     try {
         // Make parsers grouping.
         const std::map<std::string, ConstElementPtr>& values_map =
             config_set->mapValue();
-        BOOST_FOREACH(ConfigPair config_pair, values_map) {
+        BOOST_FOREACH(config_pair, values_map) {
             ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first));
+            LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
+                      .arg(config_pair.first);
             if (config_pair.first == "subnet4") {
                 subnet_parser = parser;
 
@@ -1702,16 +1706,19 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
         }
 
     } catch (const isc::Exception& ex) {
-        answer =
-            isc::config::createAnswer(1, string("Configuration parsing failed: ") + ex.what());
+        LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
+                  .arg(config_pair.first).arg(ex.what());
+        answer = isc::config::createAnswer(1,
+                     string("Configuration parsing failed: ") + ex.what());
 
         // An error occured, so make sure that we restore original data.
         rollback = true;
 
     } catch (...) {
         // for things like bad_cast in boost::lexical_cast
-        answer =
-            isc::config::createAnswer(1, string("Configuration parsing failed"));
+        LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(config_pair.first);
+        answer = isc::config::createAnswer(1,
+                     string("Configuration parsing failed"));
 
         // An error occured, so make sure that we restore original data.
         rollback = true;
@@ -1728,14 +1735,16 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
             }
         }
         catch (const isc::Exception& ex) {
-            answer =
-                isc::config::createAnswer(2, string("Configuration commit failed: ") + ex.what());
+            LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
+            answer = isc::config::createAnswer(2,
+                         string("Configuration commit failed: ") + ex.what());
             rollback = true;
 
         } catch (...) {
             // for things like bad_cast in boost::lexical_cast
-            answer =
-                isc::config::createAnswer(2, string("Configuration commit failed"));
+            LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
+            answer = isc::config::createAnswer(2,
+                         string("Configuration commit failed"));
             rollback = true;
 
         }
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index c2b755c..b139d82 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -55,13 +55,13 @@
           { "item_name": "code",
             "item_type": "integer",
             "item_optional": false,
-            "item_default": 0,
+            "item_default": 0
           },
 
           { "item_name": "type",
             "item_type": "string",
             "item_optional": false,
-            "item_default": "",
+            "item_default": ""
           },
 
           { "item_name": "array",
@@ -73,7 +73,7 @@
           { "item_name": "record_types",
             "item_type": "string",
             "item_optional": false,
-            "item_default": "",
+            "item_default": ""
           },
 
           { "item_name": "space",
@@ -125,6 +125,44 @@
         }
       },
 
+      { "item_name": "lease-database",
+        "item_type": "map",
+        "item_optional": false,
+        "item_default": {"type": "memfile"},
+        "map_item_spec": [
+            {
+                "item_name": "type",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            },
+            {
+                "item_name": "name",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            },
+            {
+                "item_name": "user",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            },
+            {
+                "item_name": "host",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            },
+            {
+                "item_name": "password",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            }
+        ]
+      },
+
       { "item_name": "subnet4",
         "item_type": "list",
         "item_optional": false,
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 02ad0a0..17f9967 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -115,6 +115,38 @@ This error is output if the server failed to assemble the data to be
 returned to the client into a valid packet.  The cause is most likely
 to be a programming error: please raise a bug report.
 
+% DHCP4_PARSER_COMMIT_EXCEPTION parser failed to commit changes
+On receipt of message containing details to a change of the IPv4 DHCP
+server configuration, a set of parsers were successfully created, but one
+of them failed to commit its changes due to a low-level system exception
+being raised.  Additional messages may be output indicating the reason.
+
+% DHCP4_PARSER_COMMIT_FAIL parser failed to commit changes: %1
+On receipt of message containing details to a change of the IPv4 DHCP
+server configuration, a set of parsers were successfully created, but
+one of them failed to commit its changes.  The reason for the failure
+is given in the message.
+
+% DHCP4_PARSER_CREATED created parser for configuration element %1
+A debug message output during a configuration update of the IPv4 DHCP
+server, notifying that the parser for the specified configuration element
+has been successfully created.
+
+% DHCP4_PARSER_FAIL failed to create or run parser for configuration element %1: %2
+On receipt of message containing details to a change of its configuration,
+the IPv4 DHCP server failed to create a parser to decode the contents
+of the named configuration element, or the creation succeeded but the
+parsing actions and committal of changes failed.  The reason for the
+failure is given in the message.
+
+% DHCP4_PARSER_EXCEPTION failed to create or run parser for configuration element %1
+On receipt of message containing details to a change of its configuration,
+the IPv4 DHCP server failed to create a parser to decode the contents of
+the named configuration element, or the creation succeeded but the parsing
+actions and committal of changes failed.  The message has been output in
+response to a non-BIND 10 exception being raised.  Additional messages
+may give further information.
+
 % DHCP4_QUERY_DATA received packet type %1, data is <%2>
 A debug message listing the data received from the client.
 
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index f2eb34c..be67f69 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -20,6 +20,7 @@
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dbaccess_parser.h>
 #include <dhcpsrv/dhcp_config_parser.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/subnet.h>
@@ -59,9 +60,6 @@ typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
 typedef boost::shared_ptr<StringParser> StringParserPtr;
 typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
 
-/// @brief Auxiliary type used for storing an element name and its parser.
-typedef pair<string, ConstElementPtr> ConfigPair;
-
 /// @brief Factory method that will create a parser for a given element name
 typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
 
@@ -1663,6 +1661,7 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
     factories["option-data"] = OptionDataListParser::factory;
     factories["option-def"] = OptionDefListParser::factory;
     factories["version"] = StringParser::factory;
+    factories["lease-database"] = DbAccessParser::factory;
 
     FactoryMap::iterator f = factories.find(config_id);
     if (f == factories.end()) {
@@ -1717,13 +1716,19 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
     // rollback informs whether error occured and original data
     // have to be restored to global storages.
     bool rollback = false;
+    // config_pair holds ther details of the current parser when iterating over
+    // the parsers.  It is declared outside the loop so in case of error, the
+    // name of the failing parser can be retrieved within the "catch" clause.
+    ConfigPair config_pair;
     try {
 
         // Make parsers grouping.
         const std::map<std::string, ConstElementPtr>& values_map =
             config_set->mapValue();
-        BOOST_FOREACH(ConfigPair config_pair, values_map) {
+        BOOST_FOREACH(config_pair, values_map) {
             ParserPtr parser(createGlobalDhcpConfigParser(config_pair.first));
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
+                      .arg(config_pair.first);
             if (config_pair.first == "subnet6") {
                 subnet_parser = parser;
 
@@ -1758,15 +1763,18 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
         }
 
     } catch (const isc::Exception& ex) {
-        answer =
-            isc::config::createAnswer(1, string("Configuration parsing failed: ") + ex.what());
+        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
+                  .arg(config_pair.first).arg(ex.what());
+        answer = isc::config::createAnswer(1,
+                     string("Configuration parsing failed: ") + ex.what());
         // An error occured, so make sure that we restore original data.
         rollback = true;
 
     } catch (...) {
         // for things like bad_cast in boost::lexical_cast
-        answer =
-            isc::config::createAnswer(1, string("Configuration parsing failed"));
+        LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(config_pair.first);
+        answer = isc::config::createAnswer(1,
+                     string("Configuration parsing failed"));
         // An error occured, so make sure that we restore original data.
         rollback = true;
     }
@@ -1782,15 +1790,16 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
             }
         }
         catch (const isc::Exception& ex) {
-            answer =
-                isc::config::createAnswer(2, string("Configuration commit failed:") 
-                                          + ex.what());
+            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
+            answer = isc::config::createAnswer(2,
+                         string("Configuration commit failed:") + ex.what());
             // An error occured, so make sure to restore the original data.
             rollback = true;
         } catch (...) {
             // for things like bad_cast in boost::lexical_cast
-            answer =
-                isc::config::createAnswer(2, string("Configuration commit failed"));
+            LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION);
+            answer = isc::config::createAnswer(2,
+                         string("Configuration commit failed"));
             // An error occured, so make sure to restore the original data.
             rollback = true;
         }
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index 5505a36..a1fe4e5 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013  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
@@ -149,8 +149,8 @@ void ControlledDhcpv6Srv::disconnectSession() {
     IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
 }
 
-ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port, const char* dbconfig)
-    : Dhcpv6Srv(port, dbconfig), cc_session_(NULL), config_session_(NULL) {
+ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)
+    : Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) {
     server_ = this; // remember this instance for use in callback
 }
 
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h
index ef1acab..ef8f085 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.h
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013  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
@@ -41,9 +41,7 @@ public:
     /// @brief Constructor
     ///
     /// @param port UDP port to be opened for DHCP traffic
-    /// @param dbconfig Lease manager database configuration string
-    ControlledDhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT,
-                        const char* dbconfig = "type=memfile");
+    ControlledDhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT);
 
     /// @brief Destructor.
     ~ControlledDhcpv6Srv();
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 7f80457..2799f06 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -61,13 +61,13 @@
           { "item_name": "code",
             "item_type": "integer",
             "item_optional": false,
-            "item_default": 0,
+            "item_default": 0
           },
 
           { "item_name": "type",
             "item_type": "string",
             "item_optional": false,
-            "item_default": "",
+            "item_default": ""
           },
 
           { "item_name": "array",
@@ -79,7 +79,7 @@
           { "item_name": "record_types",
             "item_type": "string",
             "item_optional": false,
-            "item_default": "",
+            "item_default": ""
           },
 
           { "item_name": "space",
@@ -131,6 +131,44 @@
         }
       },
 
+      { "item_name": "lease-database",
+        "item_type": "map",
+        "item_optional": false,
+        "item_default": {"type": "memfile"},
+        "map_item_spec": [
+            {
+                "item_name": "type",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            },
+            {
+                "item_name": "name",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            },
+            {
+                "item_name": "user",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            },
+            {
+                "item_name": "host",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            },
+            {
+                "item_name": "password",
+                "item_type": "string",
+                "item_optional": true,
+                "item_default": ""
+            }
+        ]
+      },
+
       { "item_name": "subnet6",
         "item_type": "list",
         "item_optional": false,
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index c38776c..65ad74b 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -172,6 +172,43 @@ This error is output if the server failed to assemble the data to be
 returned to the client into a valid packet.  The reason is most likely
 to be to a programming error: please raise a bug report.
 
+% DHCP6_PARSER_COMMIT_EXCEPTION parser failed to commit changes
+On receipt of message containing details to a change of the IPv6 DHCP
+server configuration, a set of parsers were successfully created, but one
+of them failed to commit its changes due to a low-level system exception
+being raised.  Additional messages may be output indicating the reason.
+
+% DHCP6_PARSER_COMMIT_FAIL parser failed to commit changes: %1
+On receipt of message containing details to a change of the IPv6 DHCP
+server configuration, a set of parsers were successfully created, but
+one of them failed to commit its changes.  The reason for the failure
+is given in the message.
+
+% DHCP6_PARSER_CREATED created parser for configuration element %1
+A debug message output during a configuration update of the IPv6 DHCP
+server, notifying that the parser for the specified configuration element
+has been successfully created.
+
+% DHCP6_PARSER_FAIL failed to create or run parser for configuration element %1: %2
+On receipt of message containing details to a change of its configuration,
+the IPv6 DHCP server failed to create a parser to decode the contents
+of the named configuration element, or the creation succeeded but the
+parsing actions and committal of changes failed.  The reason for the
+failure is given in the message.
+
+% DHCP6_PARSER_EXCEPTION failed to create or run parser for configuration element %1
+On receipt of message containing details to a change of its configuration,
+the IPv6 DHCP server failed to create a parser to decode the contents of
+the named configuration element, or the creation succeeded but the parsing
+actions and committal of changes failed.  The message has been output in
+response to a non-BIND 10 exception being raised.  Additional messages
+may give further information.
+
+The most likely cause of this is that the specification file for the server
+(which details the allowable contents of the configuration) is not correct for
+this version of BIND 10.  This former may be the result of an interrupted
+installation of an update to BIND 10.
+
 % DHCP6_PROCESS_IA_NA_REQUEST server is processing IA_NA option (duid=%1, iaid=%2, hint=%3)
 This is a debug message that indicates a processing of received IA_NA
 option. It may optionally contain an address that may be used by the server
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index e50950f..1e0b4da 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -56,7 +56,7 @@ using namespace std;
 namespace isc {
 namespace dhcp {
 
-Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
+Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
     : alloc_engine_(), serverid_(), shutdown_(true) {
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
@@ -92,12 +92,6 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
 
         }
 
-        // Instantiate LeaseMgr
-        LeaseMgrFactory::create(dbconfig);
-        LOG_INFO(dhcp6_logger, DHCP6_DB_BACKEND_STARTED)
-            .arg(LeaseMgrFactory::instance().getType())
-            .arg(LeaseMgrFactory::instance().getName());
-
         // Instantiate allocation engine
         alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
 
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 6515543..7c6f77b 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013 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
@@ -74,10 +74,7 @@ public:
     /// old or create new DUID.
     ///
     /// @param port port on will all sockets will listen
-    /// @param dbconfig Lease manager configuration string.  The default
-    ///        of the "memfile" manager is used for testing.
-    Dhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT,
-              const char* dbconfig = "type=memfile");
+    Dhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT);
 
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     virtual ~Dhcpv6Srv();
diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc
index ced7422..269eae4 100644
--- a/src/bin/dhcp6/main.cc
+++ b/src/bin/dhcp6/main.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  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
@@ -36,17 +36,6 @@ using namespace std;
 /// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
 
 namespace {
-// @todo: Replace the next line by extraction from configuration parameters
-// This is the "dbconfig" string for the MySQL database.  It is likely
-// that a long-term solution will be to create the instance of the lease manager
-// somewhere other than the Dhcpv6Srv constructor, to give time to extract
-// the connection string from the configuration database.
-#ifdef HAVE_MYSQL
-const char* DBCONFIG = "type=mysql name=kea user=kea password=kea host=localhost";
-#else
-const char* DBCONFIG = "type=memfile";
-#endif
-
 const char* const DHCP6_NAME = "b10-dhcp6";
 
 void
@@ -115,7 +104,7 @@ main(int argc, char* argv[]) {
 
     int ret = EXIT_SUCCESS;
     try {
-        ControlledDhcpv6Srv server(port_number, DBCONFIG);
+        ControlledDhcpv6Srv server(port_number);
         if (!stand_alone) {
             try {
                 server.establishSession();
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 1df143f..ef443e7 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  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
@@ -55,7 +55,16 @@ namespace {
 class NakedDhcpv6Srv: public Dhcpv6Srv {
     // "naked" Interface Manager, exposes internal members
 public:
-    NakedDhcpv6Srv(uint16_t port):Dhcpv6Srv(port) { }
+    NakedDhcpv6Srv(uint16_t port) : Dhcpv6Srv(port) {
+        // Open the "memfile" database for leases
+        std::string memfile = "type=memfile";
+        LeaseMgrFactory::create(memfile);
+    }
+
+    virtual ~NakedDhcpv6Srv() {
+        // Close the lease database
+        LeaseMgrFactory::destroy();
+    }
 
     using Dhcpv6Srv::processSolicit;
     using Dhcpv6Srv::processRequest;
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index ce066e7..0b02ef8 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -33,6 +33,7 @@ lib_LTLIBRARIES = libb10-dhcpsrv.la
 libb10_dhcpsrv_la_SOURCES  =
 libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
 libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
+libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
 libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
 libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
 libb10_dhcpsrv_la_SOURCES += dhcp_config_parser.h
diff --git a/src/lib/dhcpsrv/dbaccess_parser.cc b/src/lib/dhcpsrv/dbaccess_parser.cc
new file mode 100644
index 0000000..c32284e
--- /dev/null
+++ b/src/lib/dhcpsrv/dbaccess_parser.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2012-2013  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 <dhcpsrv/dbaccess_parser.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+
+#include <boost/foreach.hpp>
+
+#include <map>
+#include <string>
+#include <utility>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+
+// Factory function to build the parser
+DbAccessParser::DbAccessParser(const std::string& param_name) : values_()
+{
+    if (param_name != "lease-database") {
+        LOG_WARN(dhcpsrv_logger, DHCPSRV_UNEXPECTED_NAME).arg(param_name);
+    }
+}
+
+// Parse the configuration and check that the various keywords are consistent.
+void
+DbAccessParser::build(isc::data::ConstElementPtr config_value) {
+
+    // To cope with incremental updates, the strategy is:
+    // 1. Take a copy of the stored keyword/value pairs.
+    // 2. Update the copy with the passed keywords.
+    // 3. Perform validation checks on the updated keyword/value pairs.
+    // 4. If all is OK, update the stored keyword/value pairs.
+
+    // 1. Take a copy of the stored keyword/value pairs.
+    map<string, string> values_copy = values_;
+
+    // 2. Update the copy with the passed keywords.
+    BOOST_FOREACH(ConfigPair param, config_value->mapValue()) {
+        values_copy[param.first] = param.second->stringValue();
+    }
+
+    // 3. Perform validation checks on the updated set of keyword/values.
+    //
+    // a. Check if the "type" keyword exists and thrown an exception if not.
+    StringPairMap::const_iterator type_ptr = values_copy.find("type");
+    if (type_ptr == values_copy.end()) {
+        isc_throw(TypeKeywordMissing, "lease database access parameters must "
+                  "include the keyword 'type' to determine type of database "
+                  "to be accessed");
+    }
+
+    // b. Check if the 'type; keyword known and throw an exception if not.
+    string dbtype = type_ptr->second;
+    if ((dbtype != "memfile") && (dbtype != "mysql")) {
+        isc_throw(BadValue, "unknown backend database type: " << dbtype);
+    }
+
+    // 4. If all is OK, update the stored keyword/value pairs.  We do this by
+    // swapping contents - values_copy is destroyed immediately after the
+    // operation (when the method exits), so we are not interested in its new
+    // value.
+    values_.swap(values_copy);
+}
+
+// Create the database access string
+std::string
+DbAccessParser::getDbAccessString() const {
+
+    // Construct the database access string from all keywords and values in the
+    // parameter map where the value is not null.
+    string dbaccess;
+    BOOST_FOREACH(StringPair keyval, values_) {
+        if (!keyval.second.empty()) {
+
+            // Separate keyword/value pair from predecessor (if there is one).
+            if (!dbaccess.empty()) {
+                dbaccess += std::string(" ");
+            }
+
+            // Add the keyword/value pair to the access string.
+            dbaccess += (keyval.first + std::string("=") + keyval.second);
+        }
+    }
+
+    return (dbaccess);
+}
+
+// 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());
+}
+
+};  // namespace dhcp
+};  // namespace isc
+
diff --git a/src/lib/dhcpsrv/dbaccess_parser.h b/src/lib/dhcpsrv/dbaccess_parser.h
new file mode 100644
index 0000000..140f11d
--- /dev/null
+++ b/src/lib/dhcpsrv/dbaccess_parser.h
@@ -0,0 +1,133 @@
+// Copyright (C) 2012-2013  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 DBACCESS_PARSER_H
+#define DBACCESS_PARSER_H
+
+#include <cc/data.h>
+#include <dhcpsrv/dhcp_config_parser.h>
+#include <exceptions/exceptions.h>
+
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Exception thrown when 'type' keyword is missing from string
+///
+/// This condition is checked, but should never occur because 'type' is marked
+/// as mandatory in the .spec file for the server.
+class TypeKeywordMissing : public isc::Exception {
+public:
+    TypeKeywordMissing(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Parse Lease Database Parameters
+///
+/// This class is the parser for the lease database configuration.  This is a
+/// map under the top-level "lease-database" element, and comprises a map of
+/// strings.
+///
+/// Only the "type" sub-element is mandatory: the remaining sub-elements 
+/// depend on the datbase chosen.
+class DbAccessParser: public DhcpConfigParser {
+public:
+    /// @brief Keyword and associated value
+    typedef std::pair<std::string, std::string> StringPair;
+
+    /// @brief Keyword/value collection of database access parameters
+    typedef std::map<std::string, std::string> StringPairMap;
+
+    /// @brief Constructor
+    ///
+    /// @param param_name Name of the parameter under which the database
+    ///        access details are held.
+    DbAccessParser(const std::string& param_name);
+
+    /// The destructor.
+    virtual ~DbAccessParser()
+    {}
+
+    /// @brief Prepare configuration value.
+    ///
+    /// Parses the set of strings forming the database access specification and
+    /// checks that all are OK.  In particular it checks:
+    ///
+    /// - "type" is "memfile" or "mysql"
+    /// - If "type" is "memfile", checks that no other values are present: if
+    ///   they are, logs a warning that they will be ignored.
+    ///
+    /// Once all has been validated, constructs the database access string
+    /// expected by the lease manager.
+    ///
+    /// @param config_value The configuration value for the "lease-database"
+    ///        identifier.
+    ///
+    /// @throw isc::BadValue The 'type' keyword contains an unknown database
+    ///        type.
+    /// @throw isc::dhcp::MissingTypeKeyword The 'type' keyword is missing from
+    ///        the list of database access keywords.
+    virtual void build(isc::data::ConstElementPtr config_value);
+
+    /// @brief Apply the prepared configuration value to the server.
+    ///
+    /// With the string validated, this closes the currently open database (if
+    /// any), then opens a database corresponding to the stored string.
+    ///
+    /// This method is expected to be called after \c build(), and only once.
+    /// The result is undefined otherwise.
+    virtual void commit();
+
+    /// @brief Factory method to create parser
+    ///
+    /// Creates an instance of this parser.
+    ///
+    /// @param name Name of the parameter used to access the configuration.
+    ///
+    /// @return Pointer to a DbAccessParser.  The caller is responsible for
+    ///         destroying the parser after use.
+    static DhcpConfigParser* factory(const std::string& param_name) {
+        return (new DbAccessParser(param_name));
+    }
+
+protected:
+    /// @brief Get database access parameters
+    ///
+    /// Used in testing to check that the configuration information has been
+    /// parsed correctly.
+    ///
+    /// @return Reference to the internal map of keyword/value pairs
+    ///         representing database access information.  This is valid only
+    ///         for so long as the the parser remains in existence.
+    const StringPairMap& getDbAccessParameters() const {
+        return (values_);
+    }
+
+    /// @brief Construct dbtabase access string
+    ///
+    /// Constructs the database access string from the stored parameters.
+    ///
+    /// @return Database access string
+    std::string getDbAccessString() const;
+
+private:
+    std::map<std::string, std::string> values_; ///< Stored parameter values
+};
+
+};  // namespace dhcp
+};  // namespace isc
+
+
+#endif // DBACCESS_PARSER_H
diff --git a/src/lib/dhcpsrv/dhcp_config_parser.h b/src/lib/dhcpsrv/dhcp_config_parser.h
index d3d05f6..44cf519 100644
--- a/src/lib/dhcpsrv/dhcp_config_parser.h
+++ b/src/lib/dhcpsrv/dhcp_config_parser.h
@@ -46,6 +46,9 @@ typedef boost::shared_ptr<DhcpConfigParser> ParserPtr;
 /// This container is used to store pointer to parsers for a given scope.
 typedef std::vector<ParserPtr> ParserCollection;
 
+/// @brief Combination of parameter name and configuration contents
+typedef std::pair<std::string, isc::data::ConstElementPtr> ConfigPair;
+
 /// @brief Base abstract class for all DHCP parsers
 ///
 /// Each instance of a class derived from this class parses one specific config
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index 8a7610b..bf4e1e1 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -1,4 +1,4 @@
-# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2012-2013  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
@@ -61,10 +61,18 @@ returned the specified IPv6 subnet when given the address hint specified
 as the address is within the subnet.
 
 % DHCPSRV_CFGMGR_SUBNET6_IFACE selected subnet %1 for packet received over interface %2
-This is a debug message reporting that the DHCP configuration manager has
-returned the specified IPv6 subnet for a packet received over given interface.
-This particular subnet was selected, because it was specified as being directly
-reachable over given interface. (see 'interface' parameter in subnet6 definition).
+This is a debug message reporting that the DHCP configuration manager
+has returned the specified IPv6 subnet for a packet received over
+given interface.  This particular subnet was selected, because it
+was specified as being directly reachable over given interface. (see
+'interface' parameter in subnet6 definition).
+
+% DHCPSRV_CLOSE_DB closing currently open %1 database
+This is a debug message, issued when the DHCP server closes the currently
+open lease database.  It is issued at program shutdown and whenever
+the database access parameters are changed: in the latter case, the
+server closes the currently open database, and opens a database using
+the new parameters.
 
 % DHCPSRV_INVALID_ACCESS invalid database access string: %1
 This is logged when an attempt has been made to parse a database access string
@@ -149,6 +157,13 @@ lease from the memory file database for the specified address.
 A debug message issued when the server is attempting to update IPv6
 lease from the memory file database for the specified address.
 
+% DHCPSRV_MEMFILE_WARNING using early version of memfile lease database - leases will be lost after a restart
+This warning message is issued when the 'memfile' lease database is
+opened.  The current version of memfile does not store anything
+to disk, so lease information will be lost in the event of a restart.
+Using this version of memfile in a production environment is NOT 
+recommended.
+
 % DHCPSRV_MYSQL_ADD_ADDR4 adding IPv4 lease with address %1
 A debug message issued when the server is about to add an IPv4 lease
 with the specified address to the MySQL backend database.
@@ -232,6 +247,13 @@ a database backend, but where no 'type' keyword has been included in
 the access string.  The access string (less any passwords) is included
 in the message.
 
+% DHCPSRV_UNEXPECTED_NAME database access parameters passed through '%1', expected 'lease-database'
+The parameters for access the lease database were passed to the server through
+the named configuration parameter, but the code was expecting them to be
+passed via the parameter named "lease-database".  If the database opens
+successfully, there is no impact on server operation.  However, as this does
+indicate an error in the source code, please submit a bug report.
+
 % DHCPSRV_UNKNOWN_DB unknown database type: %1
 The database access string specified a database type (given in the
 message) that is unknown to the software.  This is a configuration error.
diff --git a/src/lib/dhcpsrv/lease_mgr_factory.cc b/src/lib/dhcpsrv/lease_mgr_factory.cc
index 9fd276d..ede0dba 100644
--- a/src/lib/dhcpsrv/lease_mgr_factory.cc
+++ b/src/lib/dhcpsrv/lease_mgr_factory.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 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
@@ -120,15 +120,13 @@ LeaseMgrFactory::create(const std::string& dbaccess) {
     // Yes, check what it is.
 #ifdef HAVE_MYSQL
     if (parameters[type] == string("mysql")) {
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_MYSQL_DB)
-            .arg(redacted);
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_MYSQL_DB).arg(redacted);
         getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters));
         return;
     }
 #endif
     if (parameters[type] == string("memfile")) {
-        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_MEMFILE_DB)
-            .arg(redacted);
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB).arg(redacted);
         getLeaseMgrPtr().reset(new Memfile_LeaseMgr(parameters));
         return;
     }
@@ -141,6 +139,12 @@ LeaseMgrFactory::create(const std::string& dbaccess) {
 
 void
 LeaseMgrFactory::destroy() {
+    // Destroy current lease manager.  This is a no-op if no lease manager
+    // is available.
+    if (getLeaseMgrPtr()) {
+        LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CLOSE_DB)
+            .arg(getLeaseMgrPtr()->getType());
+    }
     getLeaseMgrPtr().reset();
 }
 
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc
index 34f21e9..e5846eb 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.cc
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013 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
@@ -22,8 +22,7 @@ using namespace isc::dhcp;
 
 Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
     : LeaseMgr(parameters) {
-    std::cout << "Warning: Using memfile database backend. It is usable for limited"
-              << " testing only. Leases will be lost after restart." << std::endl;
+    LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_WARNING);
 }
 
 Memfile_LeaseMgr::~Memfile_LeaseMgr() {
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index dfe3e78..00476c0 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -30,6 +30,7 @@ libdhcpsrv_unittests_SOURCES  = run_unittests.cc
 libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
 libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
@@ -63,6 +64,8 @@ endif
 
 libdhcpsrv_unittests_LDADD  = $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc
new file mode 100644
index 0000000..05c0743
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc
@@ -0,0 +1,434 @@
+// Copyright (C) 2012-2013 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 <dhcpsrv/dbaccess_parser.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <config/ccsession.h>
+#include <gtest/gtest.h>
+
+#include <map>
+#include <string>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace {
+
+/// @brief Database Access Parser test fixture class
+class DbAccessParserTest : public ::testing::Test {
+public:
+    /// @brief Constructor
+    ///
+    /// Just make sure that the lease database is closed before every test
+    /// (the first in particular).
+    DbAccessParserTest() {
+        LeaseMgrFactory::destroy();
+    }
+    /// @brief Destructor
+    ///
+    /// Just make sure that the lease database is closed after every test
+    /// (the last in particular).
+    ~DbAccessParserTest() {
+        LeaseMgrFactory::destroy();
+    }
+
+    /// @brief Build JSON String
+    ///
+    /// Given a array of "const char*" strings representing in order, keyword,
+    /// value, keyword, value, ... and terminated by a NULL, return a string
+    /// that represents the JSON map for the keywords and values.
+    ///
+    /// E.g. given the array of strings: alpha, one, beta, two, NULL, it would
+    /// return the string '{ "alpha": "one", "beta": "two" }'
+    ///
+    /// @param keyval Array of "const char*" strings in the order keyword,
+    ///        value, keyword, value ...  A NULL entry terminates the list.
+    ///
+    /// @return JSON map for the keyword value array.
+    std::string toJson(const char* keyval[]) {
+        const std::string quote = "\"";
+        const std::string colon = ":";
+        const std::string space = " ";
+
+        string result = "{ ";
+
+        for (size_t i = 0; keyval[i] != NULL; i+= 2) {
+            // Get the value.  This should not be NULL.  As ASSERT_NE will
+            // cause a return - which gives compilation problems as a return
+            // statement is expected to return a string - use EXPECT_NE and
+            // explicitly return if the expected array is incorrect.
+            EXPECT_NE(static_cast<const char*>(NULL), keyval[i + 1]) <<
+                "Supplied reference keyword/value list does not contain values "
+                "for all keywords";
+            if (keyval[i + 1] == NULL) {
+                return (std::string(""));
+            }
+
+            // Add the separating comma if not the first.
+            if (i != 0) {
+                result += ", ";
+            }
+
+            // Add the keyword and value - make sure that they are quoted.
+            result += quote + keyval[i] + quote + colon + space +
+                      quote + keyval[i + 1] + quote;
+        }
+
+        // Add the terminating brace
+        result += " }";
+
+        return (result);
+    }
+
+    /// @brief Check for Keywords
+    ///
+    /// Takes a database access string and checks it against a list of keywords
+    /// and values.  It checks that:
+    ///
+    /// a. Every keyword in the string appears once and only once in the
+    ///    list.
+    /// b. Every keyword in the list appears in the string.
+    /// c. Every keyword's value is the same as that in the string.
+    ///
+    /// To parse the access string, we use the parsing function in the
+    /// DHCP lease manager.
+    ///
+    /// @param trace_string String that will be used to set the value of a
+    ///        SCOPED_TRACE for this call.
+    /// @param dbaccess set of database access parameters to check
+    /// @param keyval Array of "const char*" strings in the order keyword,
+    ///        value, keyword, value ...  A NULL entry terminates the list.
+    void checkAccessString(const char* trace_string,
+                           const DbAccessParser::StringPairMap& parameters,
+                           const char* keyval[]) {
+        SCOPED_TRACE(trace_string);
+
+        // Construct a map of keyword value pairs.
+        map<string, string> expected;
+        size_t expected_count = 0;
+        for (size_t i = 0; keyval[i] != NULL; i += 2) {
+            // Get the value.  This should not be NULL
+            ASSERT_NE(static_cast<const char*>(NULL), keyval[i + 1]) <<
+                "Supplied reference keyword/value list does not contain values "
+                "for all keywords";
+            expected[keyval[i]] = keyval[i + 1];
+
+            // One more keyword processed
+            ++expected_count;
+        }
+
+        // Check no duplicates in the test set of reference keywords.
+        ASSERT_EQ(expected_count, expected.size()) << 
+            "Supplied reference keyword/value list contains duplicate keywords";
+
+        // The passed parameter map should have the same number of entries as
+        // the reference set of keywords.
+        EXPECT_EQ(expected_count, parameters.size());
+
+        // Check that the keywords and keyword values are the same: loop
+        // through the keywords in the database access string.
+        for (LeaseMgr::ParameterMap::const_iterator actual = parameters.begin();
+             actual != parameters.end(); ++actual) {
+
+            // Does the keyword exist in the set of expected keywords?
+            map<string, string>::iterator corresponding =
+                expected.find(actual->first);
+            ASSERT_TRUE(corresponding != expected.end());
+
+            // Keyword exists, is the value the same?
+            EXPECT_EQ(corresponding->second, actual->second);
+        }
+    }
+};
+
+
+/// @brief Version of parser with protected methods public
+///
+/// Some of the methods in DbAccessParser are not required to be public in
+/// BIND 10.  Instead of being declared "private", they are declared "protected"
+/// so that they can be accessed through a derived class in the unit tests.
+class TestDbAccessParser : public DbAccessParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @brief Keyword/value collection of ddatabase access parameters
+    TestDbAccessParser(const std::string& param_name)
+        : DbAccessParser(param_name)
+    {}
+
+    /// @brief Destructor
+    virtual ~TestDbAccessParser()
+    {}
+
+    /// Allow use of superclass's protected functions.
+    using DbAccessParser::getDbAccessParameters;
+    using DbAccessParser::getDbAccessString;
+
+    /// @brief Get database access parameters
+    ///
+    /// Used in testing to check that the configuration information has been
+    /// parsed corrected.
+    ///
+    /// @return Map of keyword/value pairs representing database access
+    ///         information.
+    const StringPairMap& getDbAccessParameters() const {
+        return (DbAccessParser::getDbAccessParameters());
+    }
+
+    /// @brief Construct database access string
+    ///
+    /// Constructs the database access string from the stored parameters.
+    ///
+    /// @return Database access string
+    std::string getDbAccessString() const {
+        return (DbAccessParser::getDbAccessString());
+    }
+};
+
+// Check that the parser works with a simple configuration.
+TEST_F(DbAccessParserTest, validTypeMemfile) {
+    const char* config[] = {"type", "memfile",
+                            NULL};
+
+    string json_config = toJson(config);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    TestDbAccessParser parser("lease-database");
+    EXPECT_NO_THROW(parser.build(json_elements));
+    checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
+}
+
+// Check that the parser works with a simple configuration that
+// includes empty elements.
+TEST_F(DbAccessParserTest, emptyKeyword) {
+    const char* config[] = {"type", "memfile",
+                            "name", "",
+                            NULL};
+
+    string json_config = toJson(config);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    TestDbAccessParser parser("lease-database");
+    EXPECT_NO_THROW(parser.build(json_elements));
+    checkAccessString("Valid memfile", parser.getDbAccessParameters(), config);
+}
+
+// Check that the parser works with a valid MySQL configuration
+TEST_F(DbAccessParserTest, validTypeMysql) {
+    const char* config[] = {"type",     "mysql",
+                            "host",     "erewhon",
+                            "user",     "kea",
+                            "password", "keapassword",
+                            "name",     "keatest",
+                            NULL};
+
+    string json_config = toJson(config);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    TestDbAccessParser parser("lease-database");
+    EXPECT_NO_THROW(parser.build(json_elements));
+    checkAccessString("Valid mysql", parser.getDbAccessParameters(), config);
+}
+
+// A missing 'type' keyword should cause an exception to be thrown.
+TEST_F(DbAccessParserTest, missingTypeKeyword) {
+    const char* config[] = {"host",     "erewhon",
+                            "user",     "kea",
+                            "password", "keapassword",
+                            "name",     "keatest",
+                            NULL};
+
+    string json_config = toJson(config);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    TestDbAccessParser parser("lease-database");
+    EXPECT_THROW(parser.build(json_elements), TypeKeywordMissing);
+}
+
+// Check that the factory function works.
+TEST_F(DbAccessParserTest, factory) {
+
+    // Check that the parser is built through the factory.
+    boost::scoped_ptr<DhcpConfigParser> parser(
+        DbAccessParser::factory("lease-database"));
+    EXPECT_TRUE(parser);
+    DbAccessParser* dbap = dynamic_cast<DbAccessParser*>(parser.get());
+    EXPECT_NE(static_cast<DbAccessParser*>(NULL), dbap);
+}
+
+// Check reconfiguration.  Checks that incremental changes applied to the
+// database configuration are incremental.
+TEST_F(DbAccessParserTest, incrementalChanges) {
+    const char* config1[] = {"type", "memfile",
+                             NULL};
+
+    // Applying config2 will cause a wholesale change.
+    const char* config2[] = {"type",     "mysql",
+                             "host",     "erewhon",
+                             "user",     "kea",
+                             "password", "keapassword",
+                             "name",     "keatest",
+                             NULL};
+
+    // Applying incremental2 should cause a change to config3.
+    const char* incremental2[] = {"user",     "me",
+                                  "password", "meagain",
+                                  NULL};
+    const char* config3[] = {"type",     "mysql",
+                             "host",     "erewhon",
+                             "user",     "me",
+                             "password", "meagain",
+                             "name",     "keatest",
+                             NULL};
+
+    // incremental3 will cause an exception.  There should be no change
+    // to the returned value.
+    const char* incremental3[] = {"type",     "invalid",
+                                  "user",     "you",
+                                  "password", "youagain",
+                                  NULL};
+
+    // incremental4 is a compatible change and should cause a transition
+    // to config4.
+    const char* incremental4[] = {"user",     "them",
+                                  "password", "",
+                                  NULL};
+    const char* config4[] = {"type",     "mysql",
+                             "host",     "erewhon",
+                             "user",     "them",
+                             "password", "",
+                             "name",     "keatest",
+                             NULL};
+
+    TestDbAccessParser parser("lease-database");
+
+    // First configuration string should cause a representation of that string
+    // to be held.
+    string json_config = toJson(config1);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    EXPECT_NO_THROW(parser.build(json_elements));
+    checkAccessString("Initial configuration", parser.getDbAccessParameters(),
+                      config1);
+
+    // Applying a wholesale change will cause the access string to change
+    // to a representation of the new configuration.
+    json_config = toJson(config2);
+    json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    EXPECT_NO_THROW(parser.build(json_elements));
+    checkAccessString("Subsequent configuration", parser.getDbAccessParameters(),
+                      config2);
+
+    // Applying an incremental change will cause the representation to change
+    // incrementally.
+    json_config = toJson(incremental2);
+    json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    EXPECT_NO_THROW(parser.build(json_elements));
+    checkAccessString("Incremental configuration", parser.getDbAccessParameters(),
+                      config3);
+
+    // Applying the next incremental change should cause an exception to be
+    // thrown and there be no change to the access string.
+    json_config = toJson(incremental3);
+    json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    EXPECT_THROW(parser.build(json_elements), BadValue);
+    checkAccessString("Incompatible incremental change", parser.getDbAccessParameters(),
+                      config3);
+
+    // Applying an incremental change will cause the representation to change
+    // incrementally.
+    json_config = toJson(incremental4);
+    json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    EXPECT_NO_THROW(parser.build(json_elements));
+    checkAccessString("Compatible incremental change", parser.getDbAccessParameters(),
+                      config4);
+}
+
+// Check that the database access string is constructed correctly.
+TEST_F(DbAccessParserTest, getDbAccessString) {
+    const char* config[] = {"type",     "mysql",
+                            "host",     "" ,
+                            "name",     "keatest",
+                            NULL};
+
+    string json_config = toJson(config);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    TestDbAccessParser parser("lease-database");
+    EXPECT_NO_THROW(parser.build(json_elements));
+
+    // Get the database access string
+    std::string dbaccess = parser.getDbAccessString();
+
+    // String should be either "type=mysql name=keatest" or
+    // "name=keatest type=mysql". The "host" entry is null, so should not be
+    // output.
+    EXPECT_TRUE((dbaccess == "type=mysql name=keatest") ||
+                (dbaccess == "name=keatest type=mysql"));
+}
+
+// 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) {
+
+    // Verify that no lease database is open
+    EXPECT_THROW({
+            LeaseMgr& manager = LeaseMgrFactory::instance();
+            manager.getType();  // Never executed but satisfies compiler
+            }, isc::dhcp::NoLeaseManager);
+
+    // Set up the parser to open the memfile database.
+    const char* config[] = {"type", "memfile",
+                            NULL};
+    string json_config = toJson(config);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    TestDbAccessParser parser("lease-database");
+    EXPECT_NO_THROW(parser.build(json_elements));
+
+    // Ensure that the access string is as expected.
+    EXPECT_EQ(std::string("type=memfile"), parser.getDbAccessString());
+
+    // Committal of the parser changes should open the database.
+    EXPECT_NO_THROW(parser.commit());
+
+    // Verify by checking the type of database open.
+    std::string dbtype;
+    EXPECT_NO_THROW(dbtype = LeaseMgrFactory::instance().getType());
+    EXPECT_EQ(std::string("memfile"), dbtype);
+}
+
+};  // Anonymous namespace



More information about the bind10-changes mailing list