BIND 10 trac2559, updated. c74f64796e9901c453ab7d4e509d736d037884fd [2559] Added parser to list of DHCP6 parsers

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Dec 21 18:21:38 UTC 2012


The branch, trac2559 has been updated
       via  c74f64796e9901c453ab7d4e509d736d037884fd (commit)
       via  90f9161b787dfe68158c12bd07d2e362f2db8cc6 (commit)
       via  99d1141934d6bb027bfd402110b5706aa114adaf (commit)
       via  6f2e770cfbec4ffed1f314bed4e1b8b480c32c16 (commit)
      from  8d8605b657d5e554f0c496143fe1a3b3cb8d3ea8 (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 c74f64796e9901c453ab7d4e509d736d037884fd
Author: Stephen Morris <stephen at isc.org>
Date:   Fri Dec 21 18:21:00 2012 +0000

    [2559] Added parser to list of DHCP6 parsers

commit 90f9161b787dfe68158c12bd07d2e362f2db8cc6
Author: Stephen Morris <stephen at isc.org>
Date:   Fri Dec 21 11:48:42 2012 +0000

    [2559] Add factory function for DbAccessParser
    
    Also add ability to handle incremental changes.

commit 99d1141934d6bb027bfd402110b5706aa114adaf
Author: Stephen Morris <stephen at isc.org>
Date:   Fri Dec 21 09:55:32 2012 +0000

    [2559] Add semantic checks on the lease-database/type keyword value

commit 6f2e770cfbec4ffed1f314bed4e1b8b480c32c16
Author: Stephen Morris <stephen at isc.org>
Date:   Thu Dec 20 21:01:00 2012 +0000

    [2559] Basic parser and associated tests added
    
    Able to construct a database access string from elements in the
    configuration database.  No semantic checks have been done yet.

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

Summary of changes:
 src/bin/dhcp6/Makefile.am                       |    1 +
 src/bin/dhcp6/config_parser.cc                  |    3 +
 src/bin/dhcp6/dbaccess_parser.cc                |   94 +++++++
 src/bin/dhcp6/dbaccess_parser.h                 |  115 ++++++++
 src/bin/dhcp6/dhcp6.spec                        |   38 +++
 src/bin/dhcp6/tests/Makefile.am                 |    2 +
 src/bin/dhcp6/tests/dbaccess_parser_unittest.cc |  338 +++++++++++++++++++++++
 7 files changed, 591 insertions(+)
 create mode 100644 src/bin/dhcp6/dbaccess_parser.cc
 create mode 100644 src/bin/dhcp6/dbaccess_parser.h
 create mode 100644 src/bin/dhcp6/tests/dbaccess_parser_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 1d9766f..40a7eef 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -46,6 +46,7 @@ pkglibexec_PROGRAMS = b10-dhcp6
 
 b10_dhcp6_SOURCES  = main.cc
 b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
+b10_dhcp6_SOURCES += dbaccess_parser.cc dbaccess_parser.h
 b10_dhcp6_SOURCES += config_parser.cc config_parser.h
 b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
 b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 4c6fab1..da78852 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -17,6 +17,7 @@
 #include <config/ccsession.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp6/config_parser.h>
+#include <dhcp6/dbaccess_parser.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/pool.h>
@@ -1185,6 +1186,8 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
 
     factories.insert(pair<string, ParserFactory*>(
                          "version", StringParser::Factory));
+    factories.insert(pair<string, ParserFactory*>(
+                         "lease-database", DbAccessParser::factory));
 
     FactoryMap::iterator f = factories.find(config_id);
     if (f == factories.end()) {
diff --git a/src/bin/dhcp6/dbaccess_parser.cc b/src/bin/dhcp6/dbaccess_parser.cc
new file mode 100644
index 0000000..9743380
--- /dev/null
+++ b/src/bin/dhcp6/dbaccess_parser.cc
@@ -0,0 +1,94 @@
+// Copyright (C) 2012  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 <dhcp6/dbaccess_parser.h>
+
+#include <boost/foreach.hpp>
+
+#include <map>
+#include <string>
+#include <utility>
+
+using namespace std;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+typedef map<string, ConstElementPtr> ConfigPairMap;
+typedef pair<string, ConstElementPtr> ConfigPair;
+typedef map<string, string> StringPairMap;
+typedef pair<string, string> StringPair;
+
+// Parse the configuration and check that the various keywords are consistent.
+void
+DbAccessParser::build(isc::data::ConstElementPtr config_value) {
+    const ConfigPairMap& config_map = config_value->mapValue();
+
+    // 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.
+    // 5. Construct the updated database access string.
+
+    // 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);
+    }
+
+    /// @todo Log a warning if the type is memfile and there are other keywords.
+    ///       This will be done when the module is moved to libdhcpsrv
+
+    // 4. If all is OK, update the stored keyword/value pairs.
+    values_ = values_copy;
+
+    // 5. Construct the updated database access string.
+    dbaccess_ = "";
+    BOOST_FOREACH(StringPair keyval, values_) {
+        if (! dbaccess_.empty()) {
+            dbaccess_ += std::string(" ");
+        }
+        dbaccess_ += (keyval.first + std::string("=") + keyval.second);
+    }
+}
+
+// Commit the changes - reopen the database with the new parameters
+void
+DbAccessParser::commit() {
+    std::cout << "DB_ACCESS_PARSER_COMMIT: " << dbaccess_ << "\n";
+}
+
+};  // namespace dhcp
+};  // namespace isc
+
diff --git a/src/bin/dhcp6/dbaccess_parser.h b/src/bin/dhcp6/dbaccess_parser.h
new file mode 100644
index 0000000..fe44ad6
--- /dev/null
+++ b/src/bin/dhcp6/dbaccess_parser.h
@@ -0,0 +1,115 @@
+// Copyright (C) 2012  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 <dhcp6/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 Default constructor
+    ///
+    /// @param param_name Name of the configuration parameter being parsed.
+    DbAccessParser(const std::string& param_name)
+        : dbaccess_(), param_name_(param_name), values_()
+    {}
+
+    /// 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.
+    static DhcpConfigParser* factory(const std::string& param_name) {
+        return (new DbAccessParser(param_name));
+    }
+
+    /// @brief Get database access string
+    ///
+    /// Used in testing to check that the configuration information has been
+    /// parsed corrected.
+    std::string getDbAccessString() const {
+        return (dbaccess_);
+    }
+
+private:
+    std::string     dbaccess_;      ///< Database access string
+    std::string     param_name_;    ///< Parameter name
+    std::map<std::string, std::string> values_;
+                                    ///< Stored parameter values
+};
+
+};  // namespace dhcp
+};  // namespace isc
+
+
+#endif // DBACCESS_PARSER_H
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index c5e9565..3cd4525 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -71,6 +71,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/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index b01b877..d84cf57 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -47,8 +47,10 @@ dhcp6_unittests_SOURCES  = dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
+dhcp6_unittests_SOURCES += dbaccess_parser_unittest.cc
 dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
+dhcp6_unittests_SOURCES += ../dbaccess_parser.h ../dbaccess_parser.cc
 dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
 nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
diff --git a/src/bin/dhcp6/tests/dbaccess_parser_unittest.cc b/src/bin/dhcp6/tests/dbaccess_parser_unittest.cc
new file mode 100644
index 0000000..291b8d4
--- /dev/null
+++ b/src/bin/dhcp6/tests/dbaccess_parser_unittest.cc
@@ -0,0 +1,338 @@
+// Copyright (C) 2012 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 <dhcp6/dbaccess_parser.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <config/ccsession.h>
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <arpa/inet.h>
+
+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:
+    /// @ 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 Database access string 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, std::string& dbaccess,
+                           const char* keyval[]) {
+        SCOPED_TRACE(trace_string);
+
+        // Construct a map of keyword value pairs.  Check that no keyword
+        // is repeated.
+        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 supplied keywords
+        ASSERT_EQ(expected_count, expected.size()) << 
+            "Supplied reference keyword/value list contains duplicate keywords";
+
+        // Split the database access string.
+        const LeaseMgr::ParameterMap dbamap = LeaseMgrFactory::parse(dbaccess);
+
+        // It should have the same number keyword value pairs as the
+        EXPECT_EQ(expected_count, dbamap.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 = dbamap.begin();
+             actual != dbamap.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);
+        }
+    }
+};
+
+// 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);
+
+    DbAccessParser parser("lease-database");
+    EXPECT_NO_THROW(parser.build(json_elements));
+    string dbaccess = parser.getDbAccessString();
+    checkAccessString("Valid memfile", dbaccess, 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);
+
+    DbAccessParser parser("lease-database");
+    EXPECT_NO_THROW(parser.build(json_elements));
+    string dbaccess = parser.getDbAccessString();
+    checkAccessString("Valid mysql", dbaccess, 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);
+
+    DbAccessParser parser("lease-database");
+    EXPECT_THROW(parser.build(json_elements), TypeKeywordMissing);
+}
+
+// If the value of the "type" keyword is unknown, a BadValue exception should
+// be thrown.
+TEST_F(DbAccessParserTest, badTypeKeyword) {
+    const char* config[] = {"type",     "invalid",
+                            "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);
+
+    DbAccessParser parser("lease-database");
+    EXPECT_THROW(parser.build(json_elements), BadValue);
+}
+
+// Check that the factory function works.
+TEST_F(DbAccessParserTest, factory) {
+    const char* config[] = {"type", "memfile",
+                            NULL};
+
+    string json_config = toJson(config);
+    ConstElementPtr json_elements = Element::fromJSON(json_config);
+    EXPECT_TRUE(json_elements);
+
+    // Check that the parser is built through the factory.
+    boost::scoped_ptr<DhcpConfigParser> parser(
+        DbAccessParser::factory("lease-database"));
+    EXPECT_NO_THROW(parser->build(json_elements));
+
+    // Access the "raw" parser.
+    DbAccessParser* dbap = dynamic_cast<DbAccessParser*>(parser.get());
+    EXPECT_NE(static_cast<DbAccessParser*>(NULL), dbap);
+    string dbaccess = dbap->getDbAccessString();
+    checkAccessString("Valid mysql", dbaccess, config);
+}
+
+// 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", "themagain",
+                                  NULL};
+    const char* config4[] = {"type",     "mysql",
+                             "host",     "erewhon",
+                             "user",     "them",
+                             "password", "themagain",
+                             "name",     "keatest",
+                             NULL};
+
+    DbAccessParser 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));
+    string dbaccess = parser.getDbAccessString();
+    checkAccessString("Initial configuration", dbaccess, 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));
+    dbaccess = parser.getDbAccessString();
+    checkAccessString("Subsequent configuration", dbaccess, 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));
+    dbaccess = parser.getDbAccessString();
+    checkAccessString("Incremental configuration", dbaccess, 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);
+    dbaccess = parser.getDbAccessString();
+    checkAccessString("Incompatible incremental change", dbaccess, 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));
+    dbaccess = parser.getDbAccessString();
+    checkAccessString("Compatible incremental change", dbaccess, config4);
+}
+
+};  // Anonymous namespace



More information about the bind10-changes mailing list