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 += data_source.cc data_source.h
libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libkea_dhcpsrv_la_SOURCES += host.cc host.h
libkea_dhcpsrv_la_SOURCES += host_container.h
if HAVE_MYSQL
libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
+libkea_dhcpsrv_la_SOURCES += mysql_connection.cc mysql_connection.h
endif
if HAVE_PGSQL
libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
--- /dev/null
+// Copyright (C) 2015 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/data_source.h>
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace dhcp {
+
+std::string DataSource::getParameter(const std::string& name) const {
+ ParameterMap::const_iterator param = parameters_.find(name);
+ if (param == parameters_.end()) {
+ isc_throw(BadValue, "Parameter not found");
+ }
+ return (param->second);
+}
+
+};
+};
--- /dev/null
+// Copyright (C) 2015 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 DATA_SOURCE_H
+#define DATA_SOURCE_H
+
+#include <boost/noncopyable.hpp>
+#include <exceptions/exceptions.h>
+#include <map>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @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) {}
+};
+
+class DataSource : public boost::noncopyable {
+
+public:
+ /// Database configuration parameter map
+ typedef std::map<std::string, std::string> ParameterMap;
+
+ DataSource(const ParameterMap& parameters)
+ :parameters_(parameters) {
+ }
+
+ /// @brief returns value of the parameter
+ /// @throw BadValue if parameter is not found
+ /// @return parameter
+ std::string getParameter(const std::string& name) const;
+
+protected:
+
+ /// @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::dhcp namespace
+}; // end of isc namespace
+
+#endif // DATA_SOURCE_H
namespace isc {
namespace dhcp {
+const time_t LeaseMgr::MAX_DB_TIME = 2147483647;
+
Lease6Ptr
LeaseMgr::getLease6(Lease::Type type, const DUID& duid,
uint32_t iaid, SubnetID subnet_id) const {
/// of those classes for details.
class LeaseMgr {
public:
+ /// @brief Defines maximum value for time that can be reliably stored.
+ // If I'm still alive I'll be too old to care. You fix it.
+ static const time_t MAX_DB_TIME;
+
/// @brief Constructor
///
/// @param parameters A data structure relating keywords and values
return (leaseMgrPtr);
}
-LeaseMgr::ParameterMap
+DataSource::ParameterMap
LeaseMgrFactory::parse(const std::string& dbaccess) {
- LeaseMgr::ParameterMap mapped_tokens;
+ DataSource::ParameterMap mapped_tokens;
if (!dbaccess.empty()) {
vector<string> tokens;
}
std::string
-LeaseMgrFactory::redactedAccessString(const LeaseMgr::ParameterMap& parameters) {
+LeaseMgrFactory::redactedAccessString(const DataSource::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 (LeaseMgr::ParameterMap::const_iterator i = parameters.begin();
+ for (DataSource::ParameterMap::const_iterator i = parameters.begin();
i != parameters.end(); ++i) {
// Separate second and subsequent tokens are preceded by a space.
const std::string type = "type";
// Parse the access string and create a redacted string for logging.
- LeaseMgr::ParameterMap parameters = parse(dbaccess);
+ DataSource::ParameterMap parameters = parse(dbaccess);
std::string redacted = redactedAccessString(parameters);
// Is "type" present?
#define LEASE_MGR_FACTORY_H
#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/data_source.h>
#include <exceptions/exceptions.h>
#include <string>
/// @param dbaccess Database access string.
///
/// @return std::map<std::string, std::string> Map of keyword/value pairs.
- static LeaseMgr::ParameterMap parse(const std::string& dbaccess);
+ static DataSource::ParameterMap parse(const std::string& dbaccess);
/// @brief Redact database access string
///
///
/// @return Redacted database access string.
static std::string redactedAccessString(
- const LeaseMgr::ParameterMap& parameters);
+ const DataSource::ParameterMap& parameters);
private:
/// @brief Hold pointer to lease manager
using namespace isc::dhcp;
using namespace std;
-
-namespace {
-
-const time_t MySqlConnection::MAX_DB_TIME = 2147483647;
-
-/// @brief MySQL Selection Statements
-///
-/// Each statement is associated with an index, which is used to reference the
-/// associated prepared statement.
-
-struct TaggedStatement {
- MySqlConnection::StatementIndex index;
- const char* text;
-};
-
-TaggedStatement tagged_statements[] = {
- {MySqlLeaseMgr::DELETE_LEASE4,
- "DELETE FROM lease4 WHERE address = ?"},
- {MySqlLeaseMgr::DELETE_LEASE6,
- "DELETE FROM lease6 WHERE address = ?"},
- {MySqlLeaseMgr::GET_LEASE4_ADDR,
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, "
- "fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE address = ?"},
- {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, "
- "fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE client_id = ?"},
- {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, "
- "fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE client_id = ? AND subnet_id = ?"},
- {MySqlLeaseMgr::GET_LEASE4_HWADDR,
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, "
- "fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE hwaddr = ?"},
- {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, "
- "fqdn_fwd, fqdn_rev, hostname "
- "FROM lease4 "
- "WHERE hwaddr = ? AND subnet_id = ?"},
- {MySqlLeaseMgr::GET_LEASE6_ADDR,
- "SELECT address, duid, valid_lifetime, "
- "expire, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, "
- "fqdn_fwd, fqdn_rev, hostname, "
- "hwaddr, hwtype, hwaddr_source "
- "FROM lease6 "
- "WHERE address = ? AND lease_type = ?"},
- {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
- "SELECT address, duid, valid_lifetime, "
- "expire, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, "
- "fqdn_fwd, fqdn_rev, hostname, "
- "hwaddr, hwtype, hwaddr_source "
- "FROM lease6 "
- "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
- {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
- "SELECT address, duid, valid_lifetime, "
- "expire, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, "
- "fqdn_fwd, fqdn_rev, hostname, "
- "hwaddr, hwtype, hwaddr_source "
- "FROM lease6 "
- "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
- "AND lease_type = ?"},
- {MySqlLeaseMgr::GET_VERSION,
- "SELECT version, minor FROM schema_version"},
- {MySqlLeaseMgr::INSERT_LEASE4,
- "INSERT INTO lease4(address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, "
- "fqdn_fwd, fqdn_rev, hostname) "
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
- {MySqlLeaseMgr::INSERT_LEASE6,
- "INSERT INTO lease6(address, duid, valid_lifetime, "
- "expire, subnet_id, pref_lifetime, "
- "lease_type, iaid, prefix_len, "
- "fqdn_fwd, fqdn_rev, hostname, "
- "hwaddr, hwtype, hwaddr_source) "
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
- {MySqlLeaseMgr::UPDATE_LEASE4,
- "UPDATE lease4 SET address = ?, hwaddr = ?, "
- "client_id = ?, valid_lifetime = ?, expire = ?, "
- "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
- "hostname = ? "
- "WHERE address = ?"},
- {MySqlLeaseMgr::UPDATE_LEASE6,
- "UPDATE lease6 SET address = ?, duid = ?, "
- "valid_lifetime = ?, expire = ?, subnet_id = ?, "
- "pref_lifetime = ?, lease_type = ?, iaid = ?, "
- "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
- "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ? "
- "WHERE address = ?"},
- // End of list sentinel
- {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
-};
-
-
-}; // Anonymous namespace
-
namespace isc {
namespace dhcp {
/// be set greater than or equal to the length of the field plus 1: this allows
/// for the insertion of a trailing null whatever data is returned.
-/// @brief Maximum size of an IPv6 address represented as a text string.
-///
-/// This is 32 hexadecimal characters written in 8 groups of four, plus seven
-/// colon separators.
-const size_t ADDRESS6_TEXT_MAX_LEN = 39;
-
-/// @brief MySQL True/False constants
-///
-/// Declare typed values so as to avoid problems of data conversion. These
-/// are local to the file but are given the prefix MLM (MySql Lease Manager) to
-/// avoid any likely conflicts with variables in header files named TRUE or
-/// FALSE.
-
const my_bool MLM_FALSE = 0; ///< False value
const my_bool MLM_TRUE = 1; ///< True value
-/// @brief Maximum length of the hostname stored in DNS.
-///
-/// This length is restricted by the length of the domain-name carried
-/// in the Client FQDN %Option (see RFC4702 and RFC4704).
-const size_t HOSTNAME_MAX_LEN = 255;
-
///@}
-std::string MySqlConnection::getParameter(const std::string& name) const {
- ParameterMap::const_iterator param = parameters_.find(name);
- if (param == parameters_.end()) {
- isc_throw(BadValue, "Parameter not found");
- }
- return (param->second);
-}
-
// Open the database using the parameters passed to the constructor.
void
// class destructor explicitly destroys them.
void
-MySqlConnection::prepareStatement(StatementIndex index, const char* text) {
+MySqlConnection::prepareStatement(uint32_t index, const char* text) {
// Validate that there is space for the statement in the statements array
// and that nothing has been placed there before.
if ((index >= this->statements_.size()) || (this->statements_[index] != NULL)) {
}
}
-
void
-MySqlConnection::prepareStatements() {
+MySqlConnection::prepareStatements(const TaggedStatement tagged_statements[],
+ size_t num_statements) {
// Allocate space for all statements
statements_.clear();
- statements_.resize(NUM_STATEMENTS, NULL);
+ statements_.resize(num_statements, NULL);
text_statements_.clear();
- text_statements_.resize(NUM_STATEMENTS, std::string(""));
-
+ text_statements_.resize(num_statements, std::string(""));
// Created the MySQL prepared statements for each DML statement.
for (int i = 0; tagged_statements[i].text != NULL; ++i) {
}
}
-
-
-// Miscellaneous database methods.
-
-std::string
-MySqlConnection::getName() const {
- std::string name = "";
- try {
- name = getParameter("name");
- } catch (...) {
- // Return an empty name
- }
- return (name);
-}
-
-
-std::string
-MySqlConnection::getDescription() const {
- return (std::string("MySQL Database"));
-}
-
-
-std::pair<uint32_t, uint32_t>
-MySqlConnection::getVersion() const {
- const StatementIndex stindex = GET_VERSION;
-
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_MYSQL_GET_VERSION);
-
- uint32_t major; // Major version number
- uint32_t minor; // Minor version number
-
- // Execute the prepared statement
- int status = mysql_stmt_execute(statements_[stindex]);
- if (status != 0) {
- isc_throw(DbOperationError, "unable to execute <"
- << text_statements_[stindex] << "> - reason: " <<
- mysql_error(mysql_));
- }
-
- // Bind the output of the statement to the appropriate variables.
- MYSQL_BIND bind[2];
- memset(bind, 0, sizeof(bind));
-
- bind[0].buffer_type = MYSQL_TYPE_LONG;
- bind[0].is_unsigned = 1;
- bind[0].buffer = &major;
- bind[0].buffer_length = sizeof(major);
-
- bind[1].buffer_type = MYSQL_TYPE_LONG;
- bind[1].is_unsigned = 1;
- bind[1].buffer = &minor;
- bind[1].buffer_length = sizeof(minor);
-
- status = mysql_stmt_bind_result(statements_[stindex], bind);
- if (status != 0) {
- isc_throw(DbOperationError, "unable to bind result set: " <<
- mysql_error(mysql_));
- }
-
- // Fetch the data and set up the "release" object to release associated
- // resources when this method exits then retrieve the data.
- MySqlFreeResult fetch_release(statements_[stindex]);
- status = mysql_stmt_fetch(statements_[stindex]);
- if (status != 0) {
- isc_throw(DbOperationError, "unable to obtain result set: " <<
- mysql_error(mysql_));
- }
-
- return (std::make_pair(major, minor));
-}
-
-
-void
-MySqlConnection::commit() {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
- if (mysql_commit(mysql_) != 0) {
- isc_throw(DbOperationError, "commit failed: " << mysql_error(mysql_));
- }
-}
-
-
-void
-MySqlConnection::rollback() {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
- if (mysql_rollback(mysql_) != 0) {
- isc_throw(DbOperationError, "rollback failed: " << mysql_error(mysql_));
- }
-}
-
} // namespace isc::dhcp
} // namespace isc
#include <dhcp/hwaddr.h>
#include <dhcpsrv/lease_mgr.h>
-
+#include <dhcpsrv/data_source.h>
#include <boost/scoped_ptr.hpp>
-#include <boost/utility.hpp>
#include <mysql/mysql.h> // TODO poprawić przed oddaniem
#include <time.h>
namespace isc {
namespace dhcp {
-/// @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 MySQL True/False constants
+///
+/// Declare typed values so as to avoid problems of data conversion. These
+/// are local to the file but are given the prefix MLM (MySql Lease Manager) to
+/// avoid any likely conflicts with variables in header files named TRUE or
+/// FALSE.
+extern const my_bool MLM_FALSE;
+extern const my_bool MLM_TRUE;
/// @brief Fetch and Release MySQL Results
///
MYSQL_STMT* statement_; ///< Statement for which results are freed
};
+/// @brief MySQL Selection Statements
+///
+/// Each statement is associated with an index, which is used to reference the
+/// associated prepared statement.
+
+struct TaggedStatement {
+ uint32_t index;
+ const char* text;
+};
+
/// @brief MySQL Handle Holder
///
/// Small RAII object for safer initialization, will close the database
// Define the current database schema values
-const uint32_t CURRENT_VERSION_VERSION = 3; // version 3: adding host managment features
-const uint32_t CURRENT_VERSION_MINOR = 0;
-
-
-
-class MySqlConnection {
+class MySqlConnection : public DataSource {
public:
- /// @brief Defines maximum value for time that can be reliably stored.
- // If I'm still alive I'll be too old to care. You fix it.
- static const time_t MAX_DB_TIME;
-
- /// Database configuration parameter map
- typedef std::map<std::string, std::string> ParameterMap;
-
- MySqlConnection(const ParameterMap& parameters)
- : parameters_(parameters)
- {}
-
- virtual ~MySqlConnection()
- {}
-
- /// @brief Statement Tags
- ///
- /// The contents of the enum are indexes into the list of SQL statements
- enum StatementIndex {
- DELETE_LEASE4, // Delete from lease4 by address
- DELETE_LEASE6, // Delete from lease6 by address
- GET_LEASE4_ADDR, // Get lease4 by address
- GET_LEASE4_CLIENTID, // Get lease4 by client ID
- GET_LEASE4_CLIENTID_SUBID, // Get lease4 by client ID & subnet ID
- GET_LEASE4_HWADDR, // Get lease4 by HW address
- GET_LEASE4_HWADDR_SUBID, // Get lease4 by HW address & subnet ID
- GET_LEASE6_ADDR, // Get lease6 by address
- GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID
- GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
- GET_VERSION, // Obtain version number
- INSERT_LEASE4, // Add entry to lease4 table
- INSERT_LEASE6, // Add entry to lease6 table
- UPDATE_LEASE4, // Update a Lease4 entry
- UPDATE_LEASE6, // Update a Lease6 entry
- NUM_STATEMENTS // Number of statements
- };
-
- /// @brief returns value of the parameter
- virtual std::string getParameter(const std::string& name) const;
-
- /// @brief Return backend type
- ///
- /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
- ///
- /// @return Type of the backend.
- virtual std::string getType() const {
- return (std::string("mysql"));
+ MySqlConnection(const ParameterMap& parameters)
+ : DataSource(parameters) {
}
- /// @brief Returns backend name.
- ///
- /// Each backend have specific name, e.g. "mysql" or "sqlite".
- ///
- /// @return Name of the backend.
- virtual std::string getName() const;
-
- /// @brief Returns description of the backend.
- ///
- /// This description may be multiline text that describes the backend.
- ///
- /// @return Description of the backend.
- virtual std::string getDescription() const;
-
- /// @brief Returns backend version.
- ///
- /// @return Version number as a pair of unsigned integers. "first" is the
- /// major version number, "second" the minor number.
- ///
- /// @throw isc::dhcp::DbOperationError An operation on the open database has
- /// failed.
- virtual std::pair<uint32_t, uint32_t> getVersion() const;
-
- /// @brief Commit Transactions
- ///
- /// Commits all pending database operations. On databases that don't
- /// support transactions, this is a no-op.
- ///
- /// @throw DbOperationError Iif the commit failed.
- virtual void commit();
-
- /// @brief Rollback Transactions
- ///
- /// Rolls back all pending database operations. On databases that don't
- /// support transactions, this is a no-op.
- ///
- /// @throw DbOperationError If the rollback failed.
- virtual void rollback();
+ virtual ~MySqlConnection() {
+ }
/// @brief Prepare Single Statement
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
/// @throw isc::InvalidParameter 'index' is not valid for the vector.
- void prepareStatement(StatementIndex index, const char* text);
+ void prepareStatement(uint32_t index, const char* text);
/// @brief Prepare statements
///
/// failed.
/// @throw isc::InvalidParameter 'index' is not valid for the vector. This
/// represents an internal error within the code.
- void prepareStatements();
+ void prepareStatements(const TaggedStatement tagged_statements[],
+ size_t num_statements);
/// @brief Open Database
///
std::vector<MYSQL_STMT*> statements_; ///< Prepared statements
std::vector<std::string> text_statements_; ///< Raw text of statements
-
-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_;
-
+protected:
MySqlHolder mysql_;
/// - If there is output, copy the data from the bound variables to the output
/// lease object.
+namespace {
+/// @brief Maximum length of the hostname stored in DNS.
+///
+/// This length is restricted by the length of the domain-name carried
+/// in the Client FQDN %Option (see RFC4702 and RFC4704).
+const size_t HOSTNAME_MAX_LEN = 255;
+
+/// @brief Maximum size of an IPv6 address represented as a text string.
+///
+/// This is 32 hexadecimal characters written in 8 groups of four, plus seven
+/// colon separators.
+const size_t ADDRESS6_TEXT_MAX_LEN = 39;
+
+TaggedStatement tagged_statements[] = {
+ {MySqlLeaseMgr::DELETE_LEASE4,
+ "DELETE FROM lease4 WHERE address = ?"},
+ {MySqlLeaseMgr::DELETE_LEASE6,
+ "DELETE FROM lease6 WHERE address = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_ADDR,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE address = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE client_id = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE client_id = ? AND subnet_id = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_HWADDR,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE hwaddr = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname "
+ "FROM lease4 "
+ "WHERE hwaddr = ? AND subnet_id = ?"},
+ {MySqlLeaseMgr::GET_LEASE6_ADDR,
+ "SELECT address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, "
+ "fqdn_fwd, fqdn_rev, hostname, "
+ "hwaddr, hwtype, hwaddr_source "
+ "FROM lease6 "
+ "WHERE address = ? AND lease_type = ?"},
+ {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
+ "SELECT address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, "
+ "fqdn_fwd, fqdn_rev, hostname, "
+ "hwaddr, hwtype, hwaddr_source "
+ "FROM lease6 "
+ "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
+ {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
+ "SELECT address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, "
+ "fqdn_fwd, fqdn_rev, hostname, "
+ "hwaddr, hwtype, hwaddr_source "
+ "FROM lease6 "
+ "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
+ "AND lease_type = ?"},
+ {MySqlLeaseMgr::GET_VERSION,
+ "SELECT version, minor FROM schema_version"},
+ {MySqlLeaseMgr::INSERT_LEASE4,
+ "INSERT INTO lease4(address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+ {MySqlLeaseMgr::INSERT_LEASE6,
+ "INSERT INTO lease6(address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len, "
+ "fqdn_fwd, fqdn_rev, hostname, "
+ "hwaddr, hwtype, hwaddr_source) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+ {MySqlLeaseMgr::UPDATE_LEASE4,
+ "UPDATE lease4 SET address = ?, hwaddr = ?, "
+ "client_id = ?, valid_lifetime = ?, expire = ?, "
+ "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
+ "hostname = ? "
+ "WHERE address = ?"},
+ {MySqlLeaseMgr::UPDATE_LEASE6,
+ "UPDATE lease6 SET address = ?, duid = ?, "
+ "valid_lifetime = ?, expire = ?, subnet_id = ?, "
+ "pref_lifetime = ?, lease_type = ?, iaid = ?, "
+ "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
+ "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ? "
+ "WHERE address = ?"},
+ // End of list sentinel
+ {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
+};
+
+};
namespace isc {
namespace dhcp {
}
// Prepare all statements likely to be used.
- prepareStatements();
+ prepareStatements(tagged_statements, MySqlLeaseMgr::NUM_STATEMENTS);
// Create the exchange objects for use in exchanging data between the
// program and the database.
}
}
+// Miscellaneous database methods.
+
+std::string
+MySqlLeaseMgr::getName() const {
+ std::string name = "";
+ try {
+ name = getParameter("name");
+ } catch (...) {
+ // Return an empty name
+ }
+ return (name);
+}
+
+
+std::string
+MySqlLeaseMgr::getDescription() const {
+ return (std::string("MySQL Database"));
+}
+
+
+std::pair<uint32_t, uint32_t>
+MySqlLeaseMgr::getVersion() const {
+ const StatementIndex stindex = GET_VERSION;
+
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_MYSQL_GET_VERSION);
+
+ uint32_t major; // Major version number
+ uint32_t minor; // Minor version number
+
+ // Execute the prepared statement
+ int status = mysql_stmt_execute(statements_[stindex]);
+ if (status != 0) {
+ isc_throw(DbOperationError, "unable to execute <"
+ << text_statements_[stindex] << "> - reason: " <<
+ mysql_error(mysql_));
+ }
+
+ // Bind the output of the statement to the appropriate variables.
+ MYSQL_BIND bind[2];
+ memset(bind, 0, sizeof(bind));
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].is_unsigned = 1;
+ bind[0].buffer = &major;
+ bind[0].buffer_length = sizeof(major);
+
+ bind[1].buffer_type = MYSQL_TYPE_LONG;
+ bind[1].is_unsigned = 1;
+ bind[1].buffer = &minor;
+ bind[1].buffer_length = sizeof(minor);
+
+ status = mysql_stmt_bind_result(statements_[stindex], bind);
+ if (status != 0) {
+ isc_throw(DbOperationError, "unable to bind result set: " <<
+ mysql_error(mysql_));
+ }
+
+ // Fetch the data and set up the "release" object to release associated
+ // resources when this method exits then retrieve the data.
+ MySqlFreeResult fetch_release(statements_[stindex]);
+ status = mysql_stmt_fetch(statements_[stindex]);
+ if (status != 0) {
+ isc_throw(DbOperationError, "unable to obtain result set: " <<
+ mysql_error(mysql_));
+ }
+
+ return (std::make_pair(major, minor));
+}
+
+
+void
+MySqlLeaseMgr::commit() {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
+ if (mysql_commit(mysql_) != 0) {
+ isc_throw(DbOperationError, "commit failed: " << mysql_error(mysql_));
+ }
+}
+
+
+void
+MySqlLeaseMgr::rollback() {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
+ if (mysql_rollback(mysql_) != 0) {
+ isc_throw(DbOperationError, "rollback failed: " << mysql_error(mysql_));
+ }
+}
}; // end of isc::dhcp namespace
}; // end of isc namespace
namespace isc {
namespace dhcp {
-/// @brief MySQL Handle Holder
-///
-/// Small RAII object for safer initialization, will close the database
-/// connection upon destruction. This means that if an exception is thrown
-/// during database initialization, resources allocated to the database are
-/// guaranteed to be freed.
-///
-/// It makes no sense to copy an object of this class. After the copy, both
-/// objects would contain pointers to the same MySql context object. The
-/// destruction of one would invalidate the context in the remaining object.
-/// For this reason, the class is declared noncopyable.
-class MySqlHolder : public boost::noncopyable {
-public:
-
- /// @brief Constructor
- ///
- /// Initialize MySql and store the associated context object.
- ///
- /// @throw DbOpenError Unable to initialize MySql handle.
- MySqlHolder() : mysql_(mysql_init(NULL)) {
- if (mysql_ == NULL) {
- isc_throw(DbOpenError, "unable to initialize MySQL");
- }
- }
-
- /// @brief Destructor
- ///
- /// Frees up resources allocated by the initialization of MySql.
- ~MySqlHolder() {
- if (mysql_ != NULL) {
- mysql_close(mysql_);
- }
- // The library itself shouldn't be needed anymore
- mysql_library_end();
- }
-
- /// @brief Conversion Operator
- ///
- /// Allows the MySqlHolder object to be passed as the context argument to
- /// mysql_xxx functions.
- operator MYSQL*() const {
- return (mysql_);
- }
-
-private:
- MYSQL* mysql_; ///< Initialization context
-};
-
// Define the current database schema values
-const uint32_t CURRENT_VERSION_VERSION = 2;
+const uint32_t CURRENT_VERSION_VERSION = 3;
const uint32_t CURRENT_VERSION_MINOR = 0;
/// failed.
virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
+ /// @brief Return backend type
+ ///
+ /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+ ///
+ /// @return Type of the backend.
+ virtual std::string getType() const {
+ return (std::string("mysql"));
+ }
+
+ /// @brief Returns backend name.
+ ///
+ /// Each backend have specific name, e.g. "mysql" or "sqlite".
+ ///
+ /// @return Name of the backend.
+ virtual std::string getName() const;
+
+ /// @brief Returns description of the backend.
+ ///
+ /// This description may be multiline text that describes the backend.
+ ///
+ /// @return Description of the backend.
+ virtual std::string getDescription() const;
+
+ /// @brief Returns backend version.
+ ///
+ /// @return Version number as a pair of unsigned integers. "first" is the
+ /// major version number, "second" the minor number.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+ /// @brief Commit Transactions
+ ///
+ /// Commits all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ ///
+ /// @throw DbOperationError Iif the commit failed.
+ virtual void commit();
+
+ /// @brief Rollback Transactions
+ ///
+ /// Rolls back all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ ///
+ /// @throw DbOperationError If the rollback failed.
+ virtual void rollback();
+
///@{
/// The following methods are used to convert between times and time
/// intervals stored in the Lease object, and the times stored in the
uint32_t valid_lifetime, time_t& cltt);
///@}
-
+ /// @brief Statement Tags
+ ///
+ /// The contents of the enum are indexes into the list of SQL statements
+ enum StatementIndex {
+ DELETE_LEASE4, // Delete from lease4 by address
+ DELETE_LEASE6, // Delete from lease6 by address
+ GET_LEASE4_ADDR, // Get lease4 by address
+ GET_LEASE4_CLIENTID, // Get lease4 by client ID
+ GET_LEASE4_CLIENTID_SUBID, // Get lease4 by client ID & subnet ID
+ GET_LEASE4_HWADDR, // Get lease4 by HW address
+ GET_LEASE4_HWADDR_SUBID, // Get lease4 by HW address & subnet ID
+ GET_LEASE6_ADDR, // Get lease6 by address
+ GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID
+ GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
+ GET_VERSION, // Obtain version number
+ INSERT_LEASE4, // Add entry to lease4 table
+ INSERT_LEASE6, // Add entry to lease6 table
+ UPDATE_LEASE4, // Update a Lease4 entry
+ UPDATE_LEASE6, // Update a Lease6 entry
+ NUM_STATEMENTS // Number of statements
+ };
private:
//@}
};
-PgSqlLeaseMgr::PgSqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
- : LeaseMgr(parameters), exchange4_(new PgSqlLease4Exchange()),
+PgSqlLeaseMgr::PgSqlLeaseMgr(const DataSource::ParameterMap& parameters)
+ : LeaseMgr(), DataSource(parameters), exchange4_(new PgSqlLease4Exchange()),
exchange6_(new PgSqlLease6Exchange()), conn_(NULL) {
openDatabase();
prepareStatements();
#include <dhcp/hwaddr.h>
#include <dhcpsrv/lease_mgr.h>
-
+#include <dhcpsrv/data_source.h>
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
#include <libpq-fe.h>
/// This class provides the \ref isc::dhcp::LeaseMgr interface to the PostgreSQL
/// database. Use of this backend presupposes that a PostgreSQL database is
/// available and that the Kea schema has been created within it.
-class PgSqlLeaseMgr : public LeaseMgr {
+class PgSqlLeaseMgr : public LeaseMgr, DataSource {
public:
/// @brief Constructor
/// @throw isc::dhcp::DbOpenError Error opening the database
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- PgSqlLeaseMgr(const ParameterMap& parameters);
+ PgSqlLeaseMgr(const DataSource::ParameterMap& parameters);
/// @brief Destructor (closes database)
virtual ~PgSqlLeaseMgr();
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
libdhcpsrv_unittests_SOURCES += daemon_unittest.cc
+libdhcpsrv_unittests_SOURCES += data_source_unittest.cc
libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += host_mgr_unittest.cc
libdhcpsrv_unittests_SOURCES += host_unittest.cc
// 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();
+ for (DataSource::ParameterMap::const_iterator actual = parameters.begin();
actual != parameters.end(); ++actual) {
// Does the keyword exist in the set of expected keywords?
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
#include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/data_source.h>
#include <asiolink/io_address.h>
#include <gtest/gtest.h>
#include <sstream>
// This test checks that a database access string can be parsed correctly.
TEST_F(LeaseMgrFactoryTest, parse) {
- LeaseMgr::ParameterMap parameters = LeaseMgrFactory::parse(
+ DataSource::ParameterMap parameters = LeaseMgrFactory::parse(
"user=me password=forbidden name=kea somethingelse= type=mysql");
EXPECT_EQ(5, parameters.size());
// No tokens in the string, so we expect no parameters
std::string invalid = "";
- LeaseMgr::ParameterMap parameters = LeaseMgrFactory::parse(invalid);
+ DataSource::ParameterMap parameters = LeaseMgrFactory::parse(invalid);
EXPECT_EQ(0, parameters.size());
// With spaces, there are some tokens so we expect invalid parameter
/// as a set of asterisks.
TEST_F(LeaseMgrFactoryTest, redactAccessString) {
- LeaseMgr::ParameterMap parameters =
+ DataSource::ParameterMap parameters =
LeaseMgrFactory::parse("user=me password=forbidden name=kea type=mysql");
EXPECT_EQ(4, parameters.size());
EXPECT_EQ("me", parameters["user"]);
/// as a set of asterisks, even if the password is null.
TEST_F(LeaseMgrFactoryTest, redactAccessStringEmptyPassword) {
- LeaseMgr::ParameterMap parameters =
+ DataSource::ParameterMap parameters =
LeaseMgrFactory::parse("user=me name=kea type=mysql password=");
EXPECT_EQ(4, parameters.size());
EXPECT_EQ("me", parameters["user"]);
/// was no password to begin with.
TEST_F(LeaseMgrFactoryTest, redactAccessStringNoPassword) {
- LeaseMgr::ParameterMap parameters =
+ DataSource::ParameterMap parameters =
LeaseMgrFactory::parse("user=me name=kea type=mysql");
EXPECT_EQ(3, parameters.size());
EXPECT_EQ("me", parameters["user"]);
/// dbconfig is a generic way of passing parameters. Parameters
/// are passed in the "name=value" format, separated by spaces.
/// Values may be enclosed in double quotes, if needed.
- ///
- /// @param parameters A data structure relating keywords and values
- /// concerned with the database.
- ConcreteLeaseMgr(const LeaseMgr::ParameterMap& parameters)
- : LeaseMgr(parameters)
+ ConcreteLeaseMgr(const DataSource::ParameterMap&)
+ : LeaseMgr()
{}
/// @brief Destructor
namespace {
-/// @brief getParameter test
-///
-/// This test checks if the LeaseMgr can be instantiated and that it
-/// parses parameters string properly.
-TEST_F(LeaseMgrTest, getParameter) {
-
- LeaseMgr::ParameterMap pmap;
- pmap[std::string("param1")] = std::string("value1");
- pmap[std::string("param2")] = std::string("value2");
- ConcreteLeaseMgr leasemgr(pmap);
-
- EXPECT_EQ("value1", leasemgr.getParameter("param1"));
- EXPECT_EQ("value2", leasemgr.getParameter("param2"));
- EXPECT_THROW(leasemgr.getParameter("param3"), BadValue);
-}
-
// This test checks if getLease6() method is working properly for 0 (NULL),
// 1 (return the lease) and more than 1 leases (throw).
TEST_F(LeaseMgrTest, getLease6) {
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
boost::scoped_ptr<ConcreteLeaseMgr> mgr(new ConcreteLeaseMgr(pmap));
vector<Lease6Ptr> leases = createLeases6();
// This test checks if the LeaseMgr can be instantiated and that it
// parses parameters string properly.
TEST_F(MemfileLeaseMgrTest, constructor) {
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["universe"] = "4";
pmap["persist"] = "false";
boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr;
LeaseFileIO io4(getLeaseFilePath("leasefile4_1.csv"));
LeaseFileIO io6(getLeaseFilePath("leasefile6_1.csv"));
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["universe"] = "4";
pmap["name"] = getLeaseFilePath("leasefile4_1.csv");
boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
LeaseFileIO io4(getLeaseFilePath("leasefile4_1.csv"));
LeaseFileIO io6(getLeaseFilePath("leasefile6_1.csv"));
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["universe"] = "4";
// Specify the names of the lease files. Leases will be written.
pmap["name"] = getLeaseFilePath("leasefile4_1.csv");
// Check if it is possible to schedule the timer to perform the Lease
// File Cleanup periodically.
TEST_F(MemfileLeaseMgrTest, lfcTimer) {
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "4";
// Specify the names of the lease files. Leases will be written.
// This test checks if the LFC timer is disabled (doesn't trigger)
// cleanups when the lfc-interval is set to 0.
TEST_F(MemfileLeaseMgrTest, lfcTimerDisabled) {
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "4";
pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
previous_file.writeFile(previous_file_contents);
// Create the backend.
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "4";
pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
previous_file.writeFile(previous_file_contents);
// Create the backend.
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "6";
pmap["name"] = getLeaseFilePath("leasefile6_0.csv");
setenv("KEA_LFC_EXECUTABLE", "foobar", 1);
// Create the backend.
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "4";
pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
finish_file.writeFile(finish_file_contents);
// Create the backend.
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "6";
pmap["name"] = getLeaseFilePath("leasefile6_0.csv");
input_file.writeFile(input_file_contents);
// Create the backend.
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "6";
pmap["name"] = getLeaseFilePath("leasefile6_0.csv");
// at which the IOService must be executed to run the handlers
// for the installed timers.
TEST_F(MemfileLeaseMgrTest, getIOServiceExecInterval) {
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "4";
pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
// lease files if the LFC is in progress.
TEST_F(MemfileLeaseMgrTest, load4LFCInProgress) {
// Create the backend configuration.
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "4";
pmap["name"] = getLeaseFilePath("leasefile4_0.csv");
// lease files if the LFC is in progress.
TEST_F(MemfileLeaseMgrTest, load6LFCInProgress) {
// Create the backend configuration.
- LeaseMgr::ParameterMap pmap;
+ DataSource::ParameterMap pmap;
pmap["type"] = "memfile";
pmap["universe"] = "6";
pmap["name"] = getLeaseFilePath("leasefile6_0.csv");