Commit 373aa0a4 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[3681] DataSource class added.

 - code refactoring after last commit, clean-up, compilation fixes
 - Makefile updates
 - Added new unit-tests for DataSource (mostly moved from LeaseMgr
   unit-tests)
parent 2a3ac3ef
......@@ -92,6 +92,7 @@ libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
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
......@@ -110,6 +111,7 @@ libkea_dhcpsrv_la_SOURCES += memfile_lease_storage.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
......
// 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);
}
};
};
// 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
......@@ -36,6 +36,8 @@ using namespace std;
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 {
......
......@@ -112,6 +112,10 @@ public:
/// 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
......
......@@ -46,9 +46,9 @@ LeaseMgrFactory::getLeaseMgrPtr() {
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;
......@@ -76,11 +76,11 @@ LeaseMgrFactory::parse(const std::string& dbaccess) {
}
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.
......@@ -109,7 +109,7 @@ LeaseMgrFactory::create(const std::string& dbaccess) {
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?
......
......@@ -16,6 +16,7 @@
#define LEASE_MGR_FACTORY_H
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/data_source.h>
#include <exceptions/exceptions.h>
#include <string>
......@@ -106,7 +107,7 @@ public:
/// @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
///
......@@ -117,7 +118,7 @@ public:
///
/// @return Redacted database access string.
static std::string redactedAccessString(
const LeaseMgr::ParameterMap& parameters);
const DataSource::ParameterMap& parameters);
private:
/// @brief Hold pointer to lease manager
......
......@@ -33,115 +33,6 @@ using namespace isc;
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 {
......@@ -155,39 +46,12 @@ 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
......@@ -280,7 +144,7 @@ MySqlConnection::openDatabase() {
// 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)) {
......@@ -304,16 +168,15 @@ MySqlConnection::prepareStatement(StatementIndex index, const char* text) {
}
}
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) {
......@@ -322,95 +185,5 @@ MySqlConnection::prepareStatements() {
}
}
// 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
......@@ -17,9 +17,8 @@
#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>
......@@ -27,26 +26,14 @@
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
///
......@@ -89,6 +76,16 @@ private:
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
......@@ -139,100 +136,15 @@ private:
// 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.