src/lib/config/tests/testdata/Makefile
src/lib/cryptolink/Makefile
src/lib/cryptolink/tests/Makefile
+ src/lib/database/Makefile
+ src/lib/database/tests/Makefile
src/lib/dhcp/Makefile
src/lib/dhcp/tests/Makefile
src/lib/dhcp_ddns/Makefile
# The following build order must be maintained.
-SUBDIRS = exceptions util log cryptolink dns asiolink cc testutils hooks dhcp \
- config stats asiodns dhcp_ddns eval dhcpsrv cfgrpt process http
+SUBDIRS = exceptions util log cryptolink dns asiolink cc database testutils \
+ hooks dhcp config stats asiodns dhcp_ddns eval dhcpsrv cfgrpt \
+ process http
--- /dev/null
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+lib_LTLIBRARIES = libkea-database.la
+libkea_database_la_SOURCES = database_connection.cc database_connection.h
+libkea_database_la_SOURCES += db_exceptions.h
+libkea_database_la_SOURCES += db_log.cc db_log.h
+
+libkea_database_la_LIBADD = $(top_builddir)/src/lib/log/libkea-log.la
+libkea_database_la_LIBADD += $(top_builddir)/src/lib/log/interprocess/libkea-log_interprocess.la
+libkea_database_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
+libkea_database_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libkea_database_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libkea_database_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libkea_database_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS)
+
+libkea_database_la_LDFLAGS = -no-undefined -version-info 0:0:0
+
+# The message file should be in the distribution.
+#EXTRA_DIST = config_backend.dox
+
+CLEANFILES = *.gcno *.gcda
+
+# Specify the headers for copying into the installation directory tree.
+#libkea_cb_includedir = $(pkgincludedir)/config
+#libkea_cb_include_HEADERS =
--- /dev/null
+// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <database/database_connection.h>
+#include <database/db_exceptions.h>
+#include <database/db_log.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <vector>
+
+using namespace std;
+
+namespace isc {
+namespace db {
+
+const time_t DatabaseConnection::MAX_DB_TIME = 2147483647;
+
+std::string
+DatabaseConnection::getParameter(const std::string& name) const {
+ ParameterMap::const_iterator param = parameters_.find(name);
+ if (param == parameters_.end()) {
+ isc_throw(BadValue, "Parameter " << name << " not found");
+ }
+ return (param->second);
+}
+
+DatabaseConnection::ParameterMap
+DatabaseConnection::parse(const std::string& dbaccess) {
+ DatabaseConnection::ParameterMap mapped_tokens;
+
+ if (!dbaccess.empty()) {
+ vector<string> tokens;
+
+ // We need to pass a string to is_any_of, not just char*. Otherwise
+ // there are cryptic warnings on Debian6 running g++ 4.4 in
+ // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above
+ // array bounds"
+ boost::split(tokens, dbaccess, boost::is_any_of(string("\t ")));
+ BOOST_FOREACH(std::string token, tokens) {
+ size_t pos = token.find("=");
+ if (pos != string::npos) {
+ string name = token.substr(0, pos);
+ string value = token.substr(pos + 1);
+ mapped_tokens.insert(make_pair(name, value));
+ } else {
+ DB_LOG_ERROR(DB_INVALID_ACCESS).arg(dbaccess);
+ isc_throw(InvalidParameter, "Cannot parse " << token
+ << ", expected format is name=value");
+ }
+ }
+ }
+
+ return (mapped_tokens);
+}
+
+std::string
+DatabaseConnection::redactedAccessString(const ParameterMap& parameters) {
+ // Reconstruct the access string: start of with an empty string, then
+ // work through all the parameters in the original string and add them.
+ std::string access;
+ for (DatabaseConnection::ParameterMap::const_iterator i = parameters.begin();
+ i != parameters.end(); ++i) {
+
+ // Separate second and subsequent tokens are preceded by a space.
+ if (!access.empty()) {
+ access += " ";
+ }
+
+ // Append name of parameter...
+ access += i->first;
+ access += "=";
+
+ // ... and the value, except in the case of the password, where a
+ // redacted value is appended.
+ if (i->first == std::string("password")) {
+ access += "*****";
+ } else {
+ access += i->second;
+ }
+ }
+
+ return (access);
+}
+
+bool
+DatabaseConnection::configuredReadOnly() const {
+ std::string readonly_value = "false";
+ try {
+ readonly_value = getParameter("readonly");
+ boost::algorithm::to_lower(readonly_value);
+ } catch (...) {
+ // Parameter "readonly" hasn't been specified so we simply use
+ // the default value of "false".
+ }
+
+ if ((readonly_value != "false") && (readonly_value != "true")) {
+ isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value
+ << "' specified for boolean parameter 'readonly'");
+ }
+
+ return (readonly_value == "true");
+}
+
+ReconnectCtlPtr
+DatabaseConnection::makeReconnectCtl() const {
+ ReconnectCtlPtr retry;
+ string type = "unknown";
+ unsigned int retries = 0;
+ unsigned int interval = 0;
+
+ // Assumes that parsing ensurse only valid values are present
+ try {
+ type = getParameter("type");
+ } catch (...) {
+ // Wasn't specified so we'll use default of "unknown".
+ }
+
+ std::string parm_str;
+ try {
+ parm_str = getParameter("max-reconnect-tries");
+ retries = boost::lexical_cast<unsigned int>(parm_str);
+ } catch (...) {
+ // Wasn't specified so we'll use default of 0;
+ }
+
+ try {
+ parm_str = getParameter("reconnect-wait-time");
+ interval = boost::lexical_cast<unsigned int>(parm_str);
+ } catch (...) {
+ // Wasn't specified so we'll use default of 0;
+ }
+
+ retry.reset(new ReconnectCtl(type, retries, interval));
+ return (retry);
+}
+
+bool
+DatabaseConnection::invokeDbLostCallback() const {
+ if (DatabaseConnection::db_lost_callback) {
+ // Invoke the callback, passing in a new instance of ReconnectCtl
+ return (DatabaseConnection::db_lost_callback)(makeReconnectCtl());
+ }
+
+ return (false);
+}
+
+
+DatabaseConnection::DbLostCallback
+DatabaseConnection::db_lost_callback = 0;
+
+};
+};
--- /dev/null
+// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef DATABASE_CONNECTION_H
+#define DATABASE_CONNECTION_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <exceptions/exceptions.h>
+#include <map>
+#include <string>
+
+namespace isc {
+namespace db {
+
+/// @brief Exception thrown if name of database is not specified
+class NoDatabaseName : public Exception {
+public:
+ NoDatabaseName(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception thrown on failure to open database
+class DbOpenError : public Exception {
+public:
+ DbOpenError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception thrown on failure to execute a database function
+class DbOperationError : public Exception {
+public:
+ DbOperationError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Invalid type exception
+///
+/// Thrown when the factory doesn't recognize the type of the backend.
+class InvalidType : public Exception {
+public:
+ InvalidType(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Invalid Timeout
+///
+/// Thrown when the timeout specified for the database connection is invalid.
+class DbInvalidTimeout : public Exception {
+public:
+ DbInvalidTimeout(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Invalid 'readonly' value specification.
+///
+/// Thrown when the value of the 'readonly' boolean parameter is invalid.
+class DbInvalidReadOnly : public Exception {
+public:
+ DbInvalidReadOnly(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Warehouses DB reconnect control values
+///
+/// When a DatabaseConnection loses connectivity to its backend, it
+/// creates an instance of this class based on its configuration parameters and
+/// passes the instance into connection's DB lost callback. This allows
+/// the layer(s) above the connection to know how to proceed.
+///
+class ReconnectCtl {
+public:
+ /// @brief Constructor
+ /// @param backend_type type of the caller backend.
+ /// @param max_retries maximum number of reconnect attempts to make
+ /// @param retry_interval amount of time to between reconnect attempts
+ ReconnectCtl(const std::string& backend_type, unsigned int max_retries,
+ unsigned int retry_interval)
+ : backend_type_(backend_type), max_retries_(max_retries),
+ retries_left_(max_retries), retry_interval_(retry_interval) {}
+
+ /// @brief Returns the type of the caller backend.
+ std::string backendType() const {
+ return (backend_type_);
+ }
+
+ /// @brief Decrements the number of retries remaining
+ ///
+ /// Each call decrements the number of retries by one until zero is reached.
+ /// @return true the number of retries remaining is greater than zero.
+ bool checkRetries() {
+ return (retries_left_ ? --retries_left_ : false);
+ }
+
+ /// @brief Returns the maximum number for retries allowed
+ unsigned int maxRetries() {
+ return (max_retries_);
+ }
+
+ /// @brief Returns the number for retries remaining
+ unsigned int retriesLeft() {
+ return (retries_left_);
+ }
+
+ /// @brief Returns the amount of time to wait between reconnect attempts
+ unsigned int retryInterval() {
+ return (retry_interval_);
+ }
+
+private:
+ /// @brief Caller backend type.
+ const std::string backend_type_;
+
+ /// @brief Maximum number of retry attempts to make
+ unsigned int max_retries_;
+
+ /// @brief Number of attempts remaining
+ unsigned int retries_left_;
+
+ /// @brief The amount of time to wait between reconnect attempts
+ unsigned int retry_interval_;
+};
+
+/// @brief Pointer to an instance of ReconnectCtl
+typedef boost::shared_ptr<ReconnectCtl> ReconnectCtlPtr;
+
+/// @brief Common database connection class.
+///
+/// This class provides functions that are common for establishing
+/// connection with different types of databases; enables operations
+/// on access parameters strings. In particular, it provides a way
+/// to parse parameters in key=value format. This class is expected
+/// to be a base class for all @ref LeaseMgr and possibly
+/// @ref BaseHostDataSource derived classes.
+class DatabaseConnection : public boost::noncopyable {
+public:
+
+ /// @brief Defines maximum value for time that can be reliably stored.
+ ///
+ /// @todo: Is this common for MySQL and Postgres? Maybe we should have
+ /// specific values for each backend?
+ ///
+ /// If I'm still alive I'll be too old to care. You fix it.
+ static const time_t MAX_DB_TIME;
+
+ /// @brief Database configuration parameter map
+ typedef std::map<std::string, std::string> ParameterMap;
+
+ /// @brief Constructor
+ ///
+ /// @param parameters A data structure relating keywords and values
+ /// concerned with the database.
+ DatabaseConnection(const ParameterMap& parameters)
+ :parameters_(parameters) {
+ }
+
+ /// @brief Destructor
+ virtual ~DatabaseConnection(){};
+
+ /// @brief Instantiates a ReconnectCtl based on the connection's
+ /// reconnect parameters
+ /// @return pointer to the new ReconnectCtl object
+ virtual ReconnectCtlPtr makeReconnectCtl() const;
+
+ /// @brief Returns value of a connection parameter.
+ ///
+ /// @param name Name of the parameter which value should be returned.
+ /// @return Value of one of the connection parameters.
+ /// @throw BadValue if parameter is not found
+ std::string getParameter(const std::string& name) const;
+
+ /// @brief Parse database access string
+ ///
+ /// Parses the string of "keyword=value" pairs and separates them
+ /// out into the map.
+ ///
+ /// @param dbaccess Database access string.
+ ///
+ /// @return @ref ParameterMap of keyword/value pairs.
+ static ParameterMap parse(const std::string& dbaccess);
+
+ /// @brief Redact database access string
+ ///
+ /// Takes the database parameters and returns a database access string
+ /// passwords replaced by asterisks. This string is used in log messages.
+ ///
+ /// @param parameters Database access parameters (output of "parse").
+ ///
+ /// @return Redacted database access string.
+ static std::string redactedAccessString(const ParameterMap& parameters);
+
+ /// @brief Convenience method checking if database should be opened with
+ /// read only access.
+ ///
+ /// @return true if "readonly" parameter is specified and set to true;
+ /// false if "readonly" parameter is not specified or it is specified
+ /// and set to false.
+ bool configuredReadOnly() const;
+
+ /// @brief Defines a callback prototype for propogating events upward
+ typedef boost::function<bool (ReconnectCtlPtr db_retry)> DbLostCallback;
+
+ /// @brief Invokes the connection's lost connectivity callback
+ ///
+ /// This function may be called by derivations when the connectivity
+ /// to their data server is lost. If connectivity callback was specified,
+ /// this function will instantiate a ReconnectCtl and pass it to the
+ /// callback.
+ ///
+ /// @return Returns the result of the callback or false if there is no
+ /// callback.
+ bool invokeDbLostCallback() const;
+
+ /// @brief Optional call back function to invoke if a successfully
+ /// open connection subsequently fails
+ static DbLostCallback db_lost_callback;
+
+private:
+
+ /// @brief List of parameters passed in dbconfig
+ ///
+ /// That will be mostly used for storing database name, username,
+ /// password and other parameters required for DB access. It is not
+ /// intended to keep any DHCP-related parameters.
+ ParameterMap parameters_;
+
+};
+
+}; // end of isc::db namespace
+}; // end of isc namespace
+
+#endif // DATABASE_CONNECTION_H
#include <exceptions/exceptions.h>
namespace isc {
-namespace dhcp {
+namespace db {
/// @brief Database statement not applied
///
};
} // namespace isc
-} // namespace dhcp
+} // namespace db
#endif
#include <config.h>
#include <exceptions/exceptions.h>
-#include <dhcpsrv/db_log.h>
-#include <dhcpsrv/dhcpsrv_db_log.h>
+#include <database/db_log.h>
using namespace isc::log;
namespace isc {
-namespace dhcp {
+namespace db {
+
+DbLoggerStack db_logger_stack;
const MessageID&
DbLogger::translateMessage(const DbMessageID& id) const {
}
}
-} // namespace dhcp
+} // namespace db
} // namespace isc
/// logger with mapped messages.
namespace isc {
-namespace dhcp {
+namespace db {
///@{
/// @brief Database logging levels
///@}
-} // namespace dhcp
+} // namespace db
} // namespace isc
#endif // DB_LOG_H
--- /dev/null
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\"
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += libdatabase_unittests
+
+libdatabase_unittests_SOURCES = database_connection_unittest.cc
+libdatabase_unittests_SOURCES += run_unittests.cc
+
+libdatabase_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+libdatabase_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+libdatabase_unittests_LDADD = $(top_builddir)/src/lib/database/libkea-database.la
+libdatabase_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+libdatabase_unittests_LDADD += $(top_builddir)/src/lib/log/interprocess/libkea-log_interprocess.la
+libdatabase_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
+libdatabase_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+libdatabase_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libdatabase_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libdatabase_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD)
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
--- /dev/null
+// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <exceptions/exceptions.h>
+#include <database/database_connection.h>
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+
+using namespace isc::db;
+
+/// @brief Test fixture for exercising DbLostCallback invocation
+class DatabaseConnectionCallbackTest : public ::testing::Test {
+public:
+ /// Constructor
+ DatabaseConnectionCallbackTest()
+ : db_reconnect_ctl_(0) {
+ }
+
+ /// @brief Callback to register with a DatabaseConnection
+ ///
+ /// @param db_reconnect_ctl ReconnectCtl containing reconnect
+ /// parameters
+ bool dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ isc_throw(isc::BadValue, "db_reconnect_ctl should not be null");
+ }
+
+ db_reconnect_ctl_ = db_reconnect_ctl;
+ return (true);
+ }
+
+ /// @brief Retainer for the control passed into the callback
+ ReconnectCtlPtr db_reconnect_ctl_;
+};
+
+/// @brief getParameter test
+///
+/// This test checks if the LeaseMgr can be instantiated and that it
+/// parses parameters string properly.
+TEST(DatabaseConnectionTest, getParameter) {
+
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("param1")] = std::string("value1");
+ pmap[std::string("param2")] = std::string("value2");
+ DatabaseConnection datasrc(pmap);
+
+ EXPECT_EQ("value1", datasrc.getParameter("param1"));
+ EXPECT_EQ("value2", datasrc.getParameter("param2"));
+ EXPECT_THROW(datasrc.getParameter("param3"), isc::BadValue);
+}
+
+/// @brief NoDbLostCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbLostCallback
+/// returns a false if there is connection has no registered
+/// DbLostCallback.
+TEST_F(DatabaseConnectionCallbackTest, NoDbLostCallback) {
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60");
+ DatabaseConnection datasrc(pmap);
+
+ bool ret = false;
+ ASSERT_NO_THROW(ret = datasrc.invokeDbLostCallback());
+ EXPECT_FALSE(ret);
+ EXPECT_FALSE(db_reconnect_ctl_);
+}
+
+/// @brief dbLostCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbLostCallback
+/// safely invokes the registered DbLostCallback. It also tests
+/// operation of DbReconnectCtl retry accounting methods.
+TEST_F(DatabaseConnectionCallbackTest, dbLostCallback) {
+ /// Create a Database configuration that includes the reconnect
+ /// control parameters.
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60");
+
+ /// Install the callback.
+ DatabaseConnection::db_lost_callback =
+ boost::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, _1);
+ /// Create the connection..
+ DatabaseConnection datasrc(pmap);
+
+ /// We should be able to invoke the callback and glean
+ /// the correct reconnect contorl parameters from it.
+ bool ret = false;
+ ASSERT_NO_THROW(ret = datasrc.invokeDbLostCallback());
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ(3, db_reconnect_ctl_->maxRetries());
+ ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(60, db_reconnect_ctl_->retryInterval());
+
+ /// Verify that checkRetries() correctly decrements
+ /// down to zero, and that retriesLeft() returns
+ /// the correct value.
+ for (int i = 3; i > 1 ; --i) {
+ ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
+ }
+
+ /// Retries are exhausted, verify that's reflected.
+ EXPECT_FALSE(db_reconnect_ctl_->checkRetries());
+ EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+}
+
+// This test checks that a database access string can be parsed correctly.
+TEST(DatabaseConnectionTest, parse) {
+
+ DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(
+ "user=me password=forbidden name=kea somethingelse= type=mysql");
+
+ EXPECT_EQ(5, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("forbidden", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+ EXPECT_EQ("", parameters["somethingelse"]);
+}
+
+// This test checks that an invalid database access string behaves as expected.
+TEST(DatabaseConnectionTest, parseInvalid) {
+
+ // No tokens in the string, so we expect no parameters
+ std::string invalid = "";
+ DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(invalid);
+ EXPECT_EQ(0, parameters.size());
+
+ // With spaces, there are some tokens so we expect invalid parameter
+ // as there are no equals signs.
+ invalid = " \t ";
+ EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter);
+
+ invalid = " noequalshere ";
+ EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter);
+
+ // A single "=" is valid string, but is placed here as the result is
+ // expected to be nothing.
+ invalid = "=";
+ parameters = DatabaseConnection::parse(invalid);
+ EXPECT_EQ(1, parameters.size());
+ EXPECT_EQ("", parameters[""]);
+}
+
+/// @brief redactConfigString test
+///
+/// Checks that the redacted configuration string includes the password only
+/// as a set of asterisks.
+TEST(DatabaseConnectionTest, redactAccessString) {
+
+ DatabaseConnection::ParameterMap parameters =
+ DatabaseConnection::parse("user=me password=forbidden name=kea type=mysql");
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("forbidden", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Redact the result. To check, break the redacted string down into its
+ // components.
+ std::string redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("*****", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+}
+
+/// @brief redactConfigString test - empty password
+///
+/// Checks that the redacted configuration string includes the password only
+/// as a set of asterisks, even if the password is null.
+TEST(DatabaseConnectionTest, redactAccessStringEmptyPassword) {
+
+ DatabaseConnection::ParameterMap parameters =
+ DatabaseConnection::parse("user=me name=kea type=mysql password=");
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Redact the result. To check, break the redacted string down into its
+ // components.
+ std::string redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("*****", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // ... and again to check that the position of the empty password in the
+ // string does not matter.
+ parameters = DatabaseConnection::parse("user=me password= name=kea type=mysql");
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(4, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("*****", parameters["password"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+}
+
+/// @brief redactConfigString test - no password
+///
+/// Checks that the redacted configuration string excludes the password if there
+/// was no password to begin with.
+TEST(DatabaseConnectionTest, redactAccessStringNoPassword) {
+
+ DatabaseConnection::ParameterMap parameters =
+ DatabaseConnection::parse("user=me name=kea type=mysql");
+ EXPECT_EQ(3, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+
+ // Redact the result. To check, break the redacted string down into its
+ // components.
+ std::string redacted = DatabaseConnection::redactedAccessString(parameters);
+ parameters = DatabaseConnection::parse(redacted);
+
+ EXPECT_EQ(3, parameters.size());
+ EXPECT_EQ("me", parameters["user"]);
+ EXPECT_EQ("kea", parameters["name"]);
+ EXPECT_EQ("mysql", parameters["type"]);
+}
--- /dev/null
+#! /bin/sh
+
+# libdatabase_unittests - temporary wrapper script for .libs/libdatabase_unittests
+# Generated by libtool (GNU libtool) 2.4.6
+#
+# The libdatabase_unittests program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='2.4.6'
+ notinst_deplibs=' ../../../../src/lib/database/libkea-database.la /Users/marcin/devel/kea/src/lib/log/libkea-log.la /Users/marcin/devel/kea/src/lib/asiolink/libkea-asiolink.la ../../../../src/lib/log/libkea-log.la /Users/marcin/devel/kea/src/lib/util/threads/libkea-threads.la /Users/marcin/devel/kea/src/lib/util/libkea-util.la ../../../../src/lib/util/threads/libkea-threads.la ../../../../src/lib/util/libkea-util.la ../../../../src/lib/asiolink/libkea-asiolink.la /Users/marcin/devel/kea/src/lib/exceptions/libkea-exceptions.la ../../../../src/lib/exceptions/libkea-exceptions.la'
+else
+ # When we are sourced in execute mode, $file and $ECHO are already set.
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+ ECHO="printf %s\\n"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../../../../libtool value, followed by no.
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=$0
+ shift
+ for lt_opt
+ do
+ case "$lt_opt" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
+ test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
+ lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
+ cat "$lt_dump_D/$lt_dump_F"
+ exit 0
+ ;;
+ --lt-*)
+ $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n "$lt_option_debug"; then
+ echo "libdatabase_unittests:libdatabase_unittests:$LINENO: libtool wrapper (GNU libtool) 2.4.6" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ $ECHO "libdatabase_unittests:libdatabase_unittests:$LINENO: newargv[$lt_dump_args_N]: $lt_arg"
+ lt_dump_args_N=`expr $lt_dump_args_N + 1`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+ if test -n "$lt_option_debug"; then
+ $ECHO "libdatabase_unittests:libdatabase_unittests:$LINENO: newargv[0]: $progdir/$program" 1>&2
+ func_lt_dump_args ${1+"$@"} 1>&2
+ fi
+ exec "$progdir/$program" ${1+"$@"}
+
+ $ECHO "$0: cannot exec $program $*" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case " $* " in
+ *\ --lt-*)
+ for lt_wr_arg
+ do
+ case $lt_wr_arg in
+ --lt-*) ;;
+ *) set x "$@" "$lt_wr_arg"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core ${1+"$@"}
+}
+
+ # Parse options
+ func_parse_lt_options "$0" ${1+"$@"}
+
+ # Find the directory that this script lives in.
+ thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+ test "x$thisdir" = "x$file" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+ while test -n "$file"; do
+ destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+ # If there was a directory component, then change thisdir.
+ if test "x$destdir" != "x$file"; then
+ case "$destdir" in
+ [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+ *) thisdir="$thisdir/$destdir" ;;
+ esac
+ fi
+
+ file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+ file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+ if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+ # special case for '.'
+ if test "$thisdir" = "."; then
+ thisdir=`pwd`
+ fi
+ # remove .libs from thisdir
+ case "$thisdir" in
+ *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
+ .libs ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=`cd "$thisdir" && pwd`
+ test -n "$absdir" && thisdir="$absdir"
+
+ program='libdatabase_unittests'
+ progdir="$thisdir/.libs"
+
+
+ if test -f "$progdir/$program"; then
+ # Add our own library path to DYLD_LIBRARY_PATH
+ DYLD_LIBRARY_PATH="/Users/marcin/devel/kea/src/lib/database/.libs:/Users/marcin/devel/kea/src/lib/log/.libs:/Users/marcin/devel/kea/src/lib/asiolink/.libs:/Users/marcin/devel/kea/src/lib/util/threads/.libs:/Users/marcin/devel/kea/src/lib/util/.libs:/Users/marcin/devel/kea/src/lib/exceptions/.libs:$DYLD_LIBRARY_PATH"
+
+ # Some systems cannot cope with colon-terminated DYLD_LIBRARY_PATH
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ DYLD_LIBRARY_PATH=`$ECHO "$DYLD_LIBRARY_PATH" | /usr/bin/sed 's/::*$//'`
+
+ export DYLD_LIBRARY_PATH
+
+ if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
+ # Run the actual program with our arguments.
+ func_exec_program ${1+"$@"}
+ fi
+ else
+ # The program doesn't exist.
+ $ECHO "$0: error: '$progdir/$program' does not exist" 1>&2
+ $ECHO "This script is just a wrapper for $program." 1>&2
+ $ECHO "See the libtool documentation for more information." 1>&2
+ exit 1
+ fi
+fi
--- /dev/null
+// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <log/logger_support.h>
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+
+ int result = RUN_ALL_TESTS();
+
+ return (result);
+}
libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
libkea_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h
-libkea_dhcpsrv_la_SOURCES += database_connection.cc database_connection.h
-libkea_dhcpsrv_la_SOURCES += db_exceptions.h
-libkea_dhcpsrv_la_SOURCES += db_log.cc db_log.h
libkea_dhcpsrv_la_SOURCES += db_type.h
libkea_dhcpsrv_la_SOURCES += dhcp4o6_ipc.cc dhcp4o6_ipc.h
libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
libkea_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
#include <dhcpsrv/dhcpsrv_db_log.h>
#include <dhcpsrv/dhcpsrv_log.h>
+using namespace isc::db;
+
namespace isc {
namespace dhcp {
DbLogger dhcpsrv_db_logger(dhcpsrv_logger, dhcpsrv_db_message_map);
// Do this initialization here!
-DbLoggerStack db_logger_stack = { dhcpsrv_db_logger };
+//DbLoggerStack db_logger_stack = { dhcpsrv_db_logger };
} // namespace dhcp
#ifndef DHCPSRV_DB_LOG_H
#define DHCPSRV_DB_LOG_H
-#include <dhcpsrv/db_log.h>
+#include <database/db_log.h>
namespace isc {
namespace dhcp {
/// @brief DHCP server database message map
-extern const DbLogger::MessageMap dhcpsrv_db_message_map;
+extern const db::DbLogger::MessageMap dhcpsrv_db_message_map;
/// @brief DHCP server database Logger
///
/// It is the default database logger.
-extern DbLogger dhcpsrv_db_logger;
+extern db::DbLogger dhcpsrv_db_logger;
} // namespace dhcp
} // namespace isc
#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
+#include <database/db_exceptions.h>
#include <dhcp/duid.h>
#include <dhcp/option.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/subnet.h>
-#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/sql_common.h>
#include <boost/noncopyable.hpp>
#include <config.h>
-#include <dhcpsrv/db_log.h>
+#include <database/db_log.h>
#include <dhcpsrv/mysql_connection.h>
#include <exceptions/exceptions.h>
#ifndef MYSQL_CONNECTION_H
#define MYSQL_CONNECTION_H
-#include <dhcpsrv/database_connection.h>
-#include <dhcpsrv/db_log.h>
+#include <database/database_connection.h>
+#include <database/db_log.h>
#include <exceptions/exceptions.h>
#include <boost/scoped_ptr.hpp>
#include <mysql.h>
/// @throw DbOpenError Unable to initialize MySql handle.
MySqlHolder() : mysql_(mysql_init(NULL)) {
if (mysql_ == NULL) {
- isc_throw(DbOpenError, "unable to initialize MySQL");
+ isc_throw(db::DbOpenError, "unable to initialize MySQL");
}
}
/// to the database and preparing compiled statements. Its fields are
/// public, because they are used (both set and retrieved) in classes
/// that use instances of MySqlConnection.
-class MySqlConnection : public DatabaseConnection {
+class MySqlConnection : public db::DatabaseConnection {
public:
/// @brief Constructor
#ifndef MYSQL_HOST_DATA_SOURCE_H
#define MYSQL_HOST_DATA_SOURCE_H
+#include <database/db_exceptions.h>
#include <dhcpsrv/base_host_data_source.h>
-#include <dhcpsrv/db_exceptions.h>
#include <dhcpsrv/mysql_connection.h>
#include <stdint.h>
#ifndef PGSQL_CONNECTION_H
#define PGSQL_CONNECTION_H
-#include <dhcpsrv/database_connection.h>
+#include <database/database_connection.h>
#include <libpq-fe.h>
#include <boost/scoped_ptr.hpp>