std::string comments_;
/// @todo: Add DHCPv4 failover related fields here
+
+ /// @brief Constructor
+ ///
+ /// Initialize fields that don't have a default constructor.
+ Lease4() : addr_(0) {}
};
/// @brief Pointer to a Lease4 structure.
std::string comments_;
/// @todo: Add DHCPv6 failover related fields here
+
+ /// @brief Constructor
+ ///
+ /// Initialize fields that don't have a default constructor.
+ Lease6() : addr_(0) {}
};
/// @brief Pointer to a Lease6 structure.
/// @brief Adds an IPv4 lease.
///
/// @param lease lease to be added
- virtual bool addLease(Lease4Ptr lease) = 0;
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @exception DbOperationError Database function failed
+ virtual bool addLease(const Lease4Ptr& lease) = 0;
/// @brief Adds an IPv6 lease.
///
/// @param lease lease to be added
- virtual bool addLease(Lease6Ptr lease) = 0;
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @exception DbOperationError Database function failed
+ virtual bool addLease(const Lease6Ptr& lease) = 0;
/// @brief Returns existing IPv4 lease for specified IPv4 address and subnet_id
///
/// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
SubnetID subnet_id) const = 0;
/// @brief Returns an IPv4 lease for specified IPv4 address
/// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const = 0;
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const = 0;
/// @brief Returns existing IPv4 leases for specified hardware address.
///
/// @param addr address of the searched lease
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const = 0;
+ virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const = 0;
/// @brief Returns existing IPv6 leases for a given DUID+IA combination
///
/// @param lease4 The lease to be updated.
///
/// If no such lease is present, an exception will be thrown.
- virtual void updateLease4(Lease4Ptr lease4) = 0;
+ virtual void updateLease4(const Lease4Ptr& lease4) = 0;
/// @brief Updates IPv4 lease.
///
/// @param lease4 The lease to be updated.
///
/// If no such lease is present, an exception will be thrown.
- virtual void updateLease6(Lease6Ptr lease6) = 0;
+ virtual void updateLease6(const Lease6Ptr& lease6) = 0;
/// @brief Deletes a lease.
///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- virtual bool deleteLease4(uint32_t addr) = 0;
+ virtual bool deleteLease4(const isc::asiolink::IOAddress& addr) = 0;
/// @brief Deletes a lease.
///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- virtual bool deleteLease6(isc::asiolink::IOAddress addr) = 0;
+ virtual bool deleteLease6(const isc::asiolink::IOAddress& addr) = 0;
/// @brief Returns backend name.
///
/// Also if B>C, some database upgrade procedure may be triggered
virtual std::pair<uint32_t, uint32_t> getVersion() const = 0;
+ /// @brief Commit Transactions
+ ///
+ /// Commits all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ ///
+ /// @exception DbOperationError if the commit failed.
+ virtual void commit() = 0;
+
+ /// @brief Rollback Transactions
+ ///
+ /// Rolls back all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ ///
+ /// @exception DbOperationError if the rollback failed.
+ virtual void rollback() = 0;
+
/// @todo: Add host management here
/// As host reservation is outside of scope for 2012, support for hosts
/// is currently postponed.
+
protected:
/// @brief returns value of the parameter
std::string getParameter(const std::string& name) const;
#include <iomanip>
#include <string>
#include <config.h>
-
+#include <time.h>
#include <dhcp/mysql_lease_mgr.h>
+#include <asiolink/io_address.h>
using namespace std;
namespace isc {
namespace dhcp {
+// Time conversion methods.
+//
+// Note that the MySQL TIMESTAMP data type (used for "expire") converts data
+// from the current timezone to UTC for storage, and from UTC to the current
+// timezone for retrieval. This means that the external interface - cltt -
+// must be local time.
+
+void
+MySqlLeaseMgr::convertFromLeaseTime(time_t cltt, uint32_t valid_lft,
+ MYSQL_TIME& expire, uint32_t& lease_time) {
+
+ // Calculate expiry time and convert to various date/time fields.
+ time_t expire_time = cltt + valid_lft;
+ struct tm expire_tm;
+ (void) localtime_r(&expire_time, &expire_tm);
+
+ // Place in output expire structure.
+ expire.year = expire_tm.tm_year + 1900;
+ expire.month = expire_tm.tm_mon + 1; // Note different base
+ expire.day = expire_tm.tm_mday;
+ expire.hour = expire_tm.tm_hour;
+ expire.minute = expire_tm.tm_min;
+ expire.second = expire_tm.tm_sec;
+ expire.second_part = 0; // No fractional seconds
+ expire.neg = static_cast<my_bool>(0); // Not negative
+
+ // Set the lease time.
+ lease_time = valid_lft;
+}
+
+void
+MySqlLeaseMgr::convertToLeaseTime(const MYSQL_TIME& expire, uint32_t lease_time,
+ time_t& cltt, uint32_t& valid_lft) {
+ valid_lft = lease_time;
+
+ // Copy across fields from MYSQL_TIME structure.
+ struct tm expire_tm;
+
+ expire_tm.tm_year = expire.year - 1900;
+ expire_tm.tm_mon = expire.month - 1;
+ expire_tm.tm_mday = expire.day;
+ expire_tm.tm_hour = expire.hour;
+ expire_tm.tm_min = expire.minute;
+ expire_tm.tm_sec = expire.second;
+
+ // Convert to local time
+ cltt = mktime(&expire_tm) - valid_lft;
+}
+
void
MySqlLeaseMgr::openDatabase() {
raw_statements_.resize(NUM_STATEMENTS, std::string(""));
// Now allocate the statements
- prepareStatement(SELECT_VERSION,
+ prepareStatement(GET_LEASE6,
+ "SELECT hwaddr, client_id, "
+ "lease_time, expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len "
+ "FROM lease6 WHERE address = ?");
+ prepareStatement(GET_VERSION,
"SELECT version, minor FROM schema_version");
+ prepareStatement(INSERT_LEASE6,
+ "INSERT INTO lease6(address, hwaddr, client_id, "
+ "lease_time, expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
}
}
bool
-MySqlLeaseMgr::addLease(Lease4Ptr /* lease */) {
+MySqlLeaseMgr::addLease(const Lease4Ptr& /* lease */) {
+
return (false);
}
bool
-MySqlLeaseMgr::addLease(Lease6Ptr /* lease */) {
- return (false);
+MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
+ my_bool MLM_FALSE = 0;
+ MYSQL_BIND bind[10];
+ memset(bind, 0, sizeof(bind));
+
+ // address: varchar(40)
+ std::string addr6 = lease->addr_.toText();
+ unsigned long addr6_length = addr6.size();
+
+ bind[0].buffer_type = MYSQL_TYPE_STRING;
+ bind[0].buffer = const_cast<char*>(addr6.c_str());
+ bind[0].buffer_length = addr6_length;
+ bind[0].length = &addr6_length;
+ bind[0].is_null = &MLM_FALSE;
+
+ // hwaddr: binary(20)
+ unsigned long hwaddr_length = lease->hwaddr_.size();
+
+ bind[1].buffer_type = MYSQL_TYPE_BLOB;
+
+ bind[1].buffer = reinterpret_cast<char*>(&(lease->hwaddr_[0]));
+ bind[1].buffer_length = hwaddr_length;
+ bind[1].length = &hwaddr_length;
+ bind[1].is_null = &MLM_FALSE;
+
+ // client_id: varchar(128)
+ vector<uint8_t> clientid = lease->duid_->getDuid();
+ unsigned long clientid_length = clientid.size();
+
+ bind[2].buffer_type = MYSQL_TYPE_BLOB;
+ bind[2].buffer = reinterpret_cast<char*>(&(clientid[0]));
+ bind[2].buffer_length = clientid_length;
+ bind[2].length = &clientid_length;
+ bind[2].is_null = &MLM_FALSE;
+
+ // The lease structure holds the client last transmission time (cltt_)
+ // and the valid lifetime (valid_lft_). For convenience, the data stored
+ // in the database is expiry time (expire) and lease time (lease+time).
+ // The relationship is given by:
+ //
+ // lease_time - valid_lft_
+ // expire = cltt_ + valid_lft_
+ MYSQL_TIME mysql_expire;
+ uint32_t lease_time;
+ convertFromLeaseTime(lease->cltt_, lease->valid_lft_,
+ mysql_expire, lease_time);
+
+ // lease_time: unsigned int
+ bind[3].buffer_type = MYSQL_TYPE_LONG;
+ bind[3].buffer = reinterpret_cast<char*>(&lease_time);
+ bind[3].is_unsigned = 1;
+ bind[3].is_null = &MLM_FALSE;
+
+ // expire: timestamp
+ bind[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
+ bind[4].buffer = reinterpret_cast<char*>(&mysql_expire);
+ bind[4].buffer_length = sizeof(mysql_expire);
+ bind[4].is_null = &MLM_FALSE;
+
+ // subnet_id: unsigned int
+ bind[5].buffer_type = MYSQL_TYPE_LONG;
+ bind[5].buffer = reinterpret_cast<char*>(&(lease->subnet_id_));
+ bind[5].is_unsigned = static_cast<my_bool>(1);
+ bind[5].is_null = &MLM_FALSE;
+
+ // pref_lifetime: unsigned int
+ bind[6].buffer_type = MYSQL_TYPE_LONG;
+ bind[6].buffer = reinterpret_cast<char*>(&(lease->preferred_lft_));
+ bind[6].is_unsigned = static_cast<my_bool>(1);
+ bind[6].is_null = &MLM_FALSE;
+
+ // lease_type: tinyint
+ uint8_t lease_type = lease->type_; // Needed for int -> uint8_t conversion
+ bind[7].buffer_type = MYSQL_TYPE_TINY;
+ bind[7].buffer = reinterpret_cast<char*>(&lease_type);
+ bind[7].is_unsigned = static_cast<my_bool>(1);
+ bind[7].is_null = &MLM_FALSE;
+
+ // iaid: unsigned int
+ bind[8].buffer_type = MYSQL_TYPE_LONG;
+ bind[8].buffer = reinterpret_cast<char*>(&(lease->iaid_));
+ bind[8].is_unsigned = static_cast<my_bool>(1);
+ bind[8].is_null = &MLM_FALSE;
+
+ // prefix_len: unsigned tinyint
+ bind[9].buffer_type = MYSQL_TYPE_TINY;
+ bind[9].buffer = reinterpret_cast<char*>(&(lease->prefixlen_));
+ bind[9].is_unsigned = static_cast<my_bool>(1);
+ bind[9].is_null = &MLM_FALSE;
+
+ // Bind the parameters to the statement
+ my_bool status = mysql_stmt_bind_param(statements_[INSERT_LEASE6], bind);
+ checkError(status, INSERT_LEASE6, "unable to bind parameters");
+
+ // Execute the statement
+ status = mysql_stmt_execute(statements_[INSERT_LEASE6]);
+ checkError(status, INSERT_LEASE6, "unable to execute");
+
+ // ... and find out whether a row as inserted.
+ return (mysql_stmt_affected_rows(statements_[INSERT_LEASE6]) == 1);
}
Lease4Ptr
-MySqlLeaseMgr::getLease4(isc::asiolink::IOAddress /* addr */,
+MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */,
SubnetID /* subnet_id */) const {
return (Lease4Ptr());
}
Lease4Ptr
-MySqlLeaseMgr::getLease4(isc::asiolink::IOAddress /* addr */) const {
+MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */) const {
return (Lease4Ptr());
}
}
Lease6Ptr
-MySqlLeaseMgr::getLease6(isc::asiolink::IOAddress /* addr */) const {
- return (Lease6Ptr());
+MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
+ my_bool MLM_FALSE = 0; // MySqlLeaseMgr false
+ my_bool MLM_TRUE = 1; // MySqlLeaseMgr true
+
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[1];
+ memset(inbind, 0, sizeof(inbind));
+
+ std::string addr6 = addr.toText();
+ unsigned long addr6_length = addr6.size();
+
+ inbind[0].buffer_type = MYSQL_TYPE_STRING;
+ inbind[0].buffer = const_cast<char*>(addr6.c_str());
+ inbind[0].buffer_length = addr6_length;
+ inbind[0].length = &addr6_length;
+ inbind[0].is_null = &MLM_FALSE;
+
+ // Bind the parameters to the statement
+ my_bool status = mysql_stmt_bind_param(statements_[GET_LEASE6], inbind);
+ checkError(status, GET_LEASE6, "unable to bind WHERE clause parameter");
+
+ // Output values
+ MYSQL_BIND outbind[9];
+ memset(outbind, 0, sizeof(outbind));
+
+ // address: Not obtained - because of the WHERE clause, it will always be
+ // the same as the input parameter.
+
+ // hwaddr: varbinary(20)
+ uint8_t hwaddr[20];
+ unsigned long hwaddr_length;
+
+ outbind[0].buffer_type = MYSQL_TYPE_BLOB;
+ outbind[0].buffer = reinterpret_cast<char*>(hwaddr);
+ outbind[0].buffer_length = sizeof(hwaddr);
+ outbind[0].length = &hwaddr_length;
+
+ // client_id: varbinary(128)
+ uint8_t clientid[128];
+ unsigned long clientid_length;
+
+ outbind[1].buffer_type = MYSQL_TYPE_BLOB;
+ outbind[1].buffer = reinterpret_cast<char*>(clientid);
+ outbind[1].buffer_length = sizeof(clientid);
+ outbind[1].length = &clientid_length;
+
+ // lease_time: unsigned int
+ unsigned lease_time;
+ outbind[2].buffer_type = MYSQL_TYPE_LONG;
+ outbind[2].buffer = reinterpret_cast<char*>(&lease_time);
+ outbind[2].is_unsigned = MLM_TRUE;
+
+ // expire: timestamp
+ MYSQL_TIME mysql_expire;
+ outbind[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
+ outbind[3].buffer = reinterpret_cast<char*>(&mysql_expire);
+ outbind[3].buffer_length = sizeof(mysql_expire);
+
+ // subnet_id: unsigned int
+ unsigned subnet_id;
+ outbind[4].buffer_type = MYSQL_TYPE_LONG;
+ outbind[4].buffer = reinterpret_cast<char*>(&subnet_id);
+ outbind[4].is_unsigned = MLM_TRUE;
+
+ // pref_lifetime: unsigned int
+ unsigned pref_lifetime;
+ outbind[5].buffer_type = MYSQL_TYPE_LONG;
+ outbind[5].buffer = reinterpret_cast<char*>(&pref_lifetime);
+ outbind[5].is_unsigned = MLM_TRUE;
+
+ // lease_type: tinyint
+ uint8_t lease_type;
+ outbind[6].buffer_type = MYSQL_TYPE_TINY;
+ outbind[6].buffer = reinterpret_cast<char*>(&lease_type);
+ outbind[6].is_unsigned = MLM_TRUE;
+
+ // iaid: unsigned int
+ unsigned iaid;
+ outbind[7].buffer_type = MYSQL_TYPE_LONG;
+ outbind[7].buffer = reinterpret_cast<char*>(&iaid);
+ outbind[7].is_unsigned = MLM_TRUE;
+
+ // prefix_len: unsigned tinyint
+ uint8_t prefixlen;
+ outbind[8].buffer_type = MYSQL_TYPE_TINY;
+ outbind[8].buffer = reinterpret_cast<char*>(&prefixlen);
+ outbind[8].is_unsigned = MLM_TRUE;
+
+ // Bind the parameters to the statement
+ status = mysql_stmt_bind_result(statements_[GET_LEASE6], outbind);
+ checkError(status, GET_LEASE6, "unable to bind SELECT caluse parameters");
+
+ // Execute the statement
+ status = mysql_stmt_execute(statements_[GET_LEASE6]);
+ checkError(status, GET_LEASE6, "unable to execute");
+
+ // Fetch the data.
+ Lease6Ptr result(new Lease6());
+ status = mysql_stmt_fetch(statements_[GET_LEASE6]);
+ if (status == 0) {
+ // Success - put the data in the lease object
+ result->addr_ = addr;
+ result->hwaddr_ = vector<uint8_t>(&hwaddr[0], &hwaddr[hwaddr_length]);
+ result->duid_.reset(new DUID(clientid, clientid_length));
+ convertToLeaseTime(mysql_expire, lease_time,
+ result->cltt_, result->valid_lft_);
+ result->subnet_id_ = subnet_id;
+ result->preferred_lft_ = pref_lifetime;
+ switch (lease_type) {
+ case Lease6::LEASE_IA_NA:
+ result->type_ = Lease6::LEASE_IA_NA;
+ break;
+
+ case Lease6::LEASE_IA_TA:
+ result->type_ = Lease6::LEASE_IA_TA;
+ break;
+
+ case Lease6::LEASE_IA_PD:
+ result->type_ = Lease6::LEASE_IA_PD;
+ break;
+
+ default:
+ isc_throw(BadValue, "invalid lease type returned for <" <<
+ raw_statements_[GET_LEASE6] << ">");
+ }
+ result->iaid_ = iaid;
+ result->prefixlen_ = prefixlen;
+
+ // As the address is the primary key in the table, we can't return
+ // two rows, so we don't bother checking.
+
+ } else if (status == 1) {
+ checkError(status, GET_LEASE6, "unable to fetch results");
+
+ } else {
+ // We are ignoring truncation for now, so the only other result is
+ // no data was found. In that case, we returrn a null Lease6 structure.
+ // This has already been set, so ther action is a no-op.
+ }
+
+ return (result);
}
Lease6Collection
}
void
-MySqlLeaseMgr::updateLease4(Lease4Ptr /* lease4 */) {
+MySqlLeaseMgr::updateLease4(const Lease4Ptr& /* lease4 */) {
}
void
-MySqlLeaseMgr::updateLease6(Lease6Ptr /* lease6 */) {
+MySqlLeaseMgr::updateLease6(const Lease6Ptr& /* lease6 */) {
}
bool
-MySqlLeaseMgr::deleteLease4(uint32_t /* addr */) {
+MySqlLeaseMgr::deleteLease4(const isc::asiolink::IOAddress& /* addr */) {
return (false);
}
bool
-MySqlLeaseMgr::deleteLease6(isc::asiolink::IOAddress /* addr */) {
+MySqlLeaseMgr::deleteLease6(const isc::asiolink::IOAddress& /* addr */) {
return (false);
}
uint32_t minor; // Minor version number
// Execute the prepared statement
- int status = mysql_stmt_execute(statements_[SELECT_VERSION]);
+ int status = mysql_stmt_execute(statements_[GET_VERSION]);
if (status != 0) {
isc_throw(DbOperationError, "unable to execute <"
- << raw_statements_[SELECT_VERSION] << "> - reason: " <<
+ << raw_statements_[GET_VERSION] << "> - reason: " <<
mysql_error(mysql_));
}
bind[1].buffer = &minor;
bind[1].buffer_length = sizeof(minor);
- status = mysql_stmt_bind_result(statements_[SELECT_VERSION], bind);
+ status = mysql_stmt_bind_result(statements_[GET_VERSION], bind);
if (status != 0) {
isc_throw(DbOperationError, "unable to bind result set: " <<
mysql_error(mysql_));
}
// Get the result
- status = mysql_stmt_fetch(statements_[SELECT_VERSION]);
+ status = mysql_stmt_fetch(statements_[GET_VERSION]);
if (status != 0) {
isc_throw(DbOperationError, "unable to obtain result set: " <<
mysql_error(mysql_));
return (std::make_pair(major, minor));
}
+void
+MySqlLeaseMgr::commit() {
+ if (mysql_commit(mysql_) != 0) {
+ isc_throw(DbOperationError, "commit failed: " << mysql_error(mysql_));
+ }
+}
+
+void
+MySqlLeaseMgr::rollback() {
+ if (mysql_rollback(mysql_) != 0) {
+ isc_throw(DbOperationError, "rollback failed: " << mysql_error(mysql_));
+ }
+}
+
}; // end of isc::dhcp namespace
}; // end of isc namespace
#ifndef __MYSQL_LEASE_MGR_H
#define __MYSQL_LEASE_MGR_H
+#include <time.h>
#include <mysql.h>
#include <dhcp/lease_mgr.h>
/// @brief Adds an IPv4 lease.
///
/// @param lease lease to be added
- virtual bool addLease(Lease4Ptr lease);
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @exception DbOperationError Database function failed
+ virtual bool addLease(const Lease4Ptr& lease);
/// @brief Adds an IPv6 lease.
///
/// @param lease lease to be added
- virtual bool addLease(Lease6Ptr lease);
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @exception DbOperationError Database function failed
+ virtual bool addLease(const Lease6Ptr& lease);
/// @brief Returns existing IPv4 lease for specified IPv4 address and subnet_id
///
/// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
SubnetID subnet_id) const;
/// @brief Returns an IPv4 lease for specified IPv4 address
/// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
/// @brief Returns existing IPv4 leases for specified hardware address.
///
/// @param addr address of the searched lease
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const;
+ virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
/// @brief Returns existing IPv6 leases for a given DUID+IA combination
///
/// @param lease4 The lease to be updated.
///
/// If no such lease is present, an exception will be thrown.
- virtual void updateLease4(Lease4Ptr lease4);
+ virtual void updateLease4(const Lease4Ptr& lease4);
/// @brief Updates IPv4 lease.
///
/// @param lease4 The lease to be updated.
///
/// If no such lease is present, an exception will be thrown.
- virtual void updateLease6(Lease6Ptr lease6);
+ virtual void updateLease6(const Lease6Ptr& lease6);
/// @brief Deletes a lease.
///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- virtual bool deleteLease4(uint32_t addr);
+ virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
/// @brief Deletes a lease.
///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- virtual bool deleteLease6(isc::asiolink::IOAddress addr);
+ virtual bool deleteLease6(const isc::asiolink::IOAddress& addr);
/// @brief Returns backend name.
///
/// Also if B>C, some database upgrade procedure may be triggered
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.
+ ///
+ /// @exception DbOperationError if 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.
+ ///
+ /// @exception DbOperationError if the rollback failed.
+ virtual void rollback();
+
+ ///@{
+ /// The following methods are used to convert between times and time
+ /// intervals stored in the server in the Lease object, and the times
+ /// stored in the database. The reason for the difference is because
+ /// in the DHCP server, the cltt (Client Time Since Last Transmission)
+ /// is the natural data: in the lease file - which may be read by the
+ /// user - it is the expiry time of the lease.
+
+ /// @brief Convert Lease Time to Database Times
+ ///
+ /// Within the DHCP servers, times are stored as cltt (client last transmit
+ /// time) and valid_lft (valid lifetime). In the database, the information
+ /// is stored as lease_time (lease time) and expire (time of expiry of the
+ /// lease). They are related by the equations:
+ ///
+ /// lease_time = valid_lft
+ /// expire = cltt + valid_lft
+ ///
+ /// This method converts from the times in the lease object into times
+ /// able to be added to the database.
+ ///
+ /// @param cltt Client last transmit time
+ /// @param valid_lft Valid lifetime
+ /// @param expire Reference to MYSQL_TIME object where the expiry time of
+ /// the lease will be put.
+ /// @param lease_time Reference to the time_t object where the lease time
+ /// will be put.
+ static
+ void convertFromLeaseTime(time_t cltt, uint32_t valid_lft,
+ MYSQL_TIME& expire, uint32_t& lease_time);
+
+ /// @brief Convert Database Time to Lease Times
+ ///
+ /// Within the database, time is stored as lease_time (lease time) and
+ /// expire (time of expiry of the lease). In the DHCP server, the
+ /// information is stored as cltt (client last transmit time) and
+ /// valid_lft (valid lifetime). These arr related by the equations:
+ ///
+ /// valid_lft = lease_time
+ /// cltt = expire - lease_time
+ ///
+ /// This method converts from the times in the database into times
+ /// able to be inserted into the lease object.
+ ///
+ /// @param expire Reference to MYSQL_TIME object from where the expiry
+ /// time of the lease is taken.
+ /// @param lease_time lifetime of the lease.
+ /// @param cltt Reference to location where client last transmit time
+ /// is put.
+ /// @param valid_lft Reference to location where valid lifetime is put.
+ static
+ void convertToLeaseTime(const MYSQL_TIME& expire, uint32_t lease_time,
+ time_t& cltt, uint32_t& valid_lft);
+
+ ///@}
+
+
private:
/// @brief Enum of Statements
///
/// This is provided to set indexes into a list of prepared statements.
enum StatementIndex {
- SELECT_VERSION, // Obtain version number
- NUM_STATEMENTS // Number of statements
+ GET_LEASE6,
+ GET_VERSION, // Obtain version number
+ INSERT_LEASE6, // Add entry to lease6 table
+ NUM_STATEMENTS // Number of statements
};
/// @brief Prepare Single Statement
/// @exception DbOpenError Error opening the database
void openDatabase();
+ /// @brief Check Error and Throw Exception
+ ///
+ /// Virtually all MySQL functions return a status which, if non-zero,
+ /// indicates an error. This inline function conceals a lot of error
+ /// checking/exception-throwing code.
+ ///
+ /// @param status Status code: non-zero implies an error
+ /// @param index Index of statement that caused the error
+ /// @param what High-level description of the error
+ ///
+ /// @exception DbOperationError Error doing a database operation
+ inline void checkError(my_bool status, StatementIndex index,
+ const char* what) const {
+ if (status != 0) {
+ isc_throw(DbOperationError, what << " for <" <<
+ raw_statements_[index] << ">, reason: " <<
+ mysql_error(mysql_));
+ }
+ }
+
// Members
MYSQL* mysql_; ///< MySQL context object
std::vector<std::string> raw_statements_; ///< Raw text of statements
/// @brief Adds an IPv4 lease.
///
/// @param lease lease to be added
- virtual bool addLease(Lease4Ptr lease);
+ virtual bool addLease(const Lease4Ptr& lease);
/// @brief Adds an IPv6 lease.
///
/// @param lease lease to be added
- virtual bool addLease(Lease6Ptr lease);
+ virtual bool addLease(const Lease6Ptr& lease);
/// @brief Returns existing IPv4 lease for specified IPv4 address.
///
/// @param addr address of the searched lease
///
/// @return a collection of leases
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
/// @brief Returns existing IPv4 lease for specific address and subnet
/// @param addr address of the searched lease
/// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
SubnetID subnet_id) const;
/// @brief Returns existing IPv4 leases for specified hardware address.
/// @param addr address of the searched lease
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const;
+ Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
/// @brief Returns existing IPv6 lease for a given DUID+IA combination
///
/// @param lease4 The lease to be updated.
///
/// If no such lease is present, an exception will be thrown.
- void updateLease4(Lease4Ptr lease4);
+ void updateLease4(const Lease4Ptr& lease4);
/// @brief Updates IPv4 lease.
///
/// @param lease4 The lease to be updated.
///
/// If no such lease is present, an exception will be thrown.
- void updateLease6(Lease6Ptr lease6);
+ void updateLease6(const Lease6Ptr& lease6);
/// @brief Deletes a lease.
///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- bool deleteLease4(uint32_t addr);
+ bool deleteLease4(const isc::asiolink::IOAddress& addr);
/// @brief Deletes a lease.
///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- bool deleteLease6(isc::asiolink::IOAddress addr);
+ bool deleteLease6(const isc::asiolink::IOAddress& addr);
/// @brief Returns backend name.
///
return (make_pair(uint32_t(0), uint32_t(0)));
}
+ /// @brief Commit transactions
+ void commit() {
+ }
+
+ /// @brief Rollback transactions
+ void rollback() {
+ }
+
using LeaseMgr::getParameter;
protected:
Memfile_LeaseMgr::~Memfile_LeaseMgr() {
}
-bool Memfile_LeaseMgr::addLease(boost::shared_ptr<isc::dhcp::Lease4>) {
+bool Memfile_LeaseMgr::addLease(const boost::shared_ptr<isc::dhcp::Lease4>&) {
return (false);
}
-bool Memfile_LeaseMgr::addLease(boost::shared_ptr<isc::dhcp::Lease6>) {
+bool Memfile_LeaseMgr::addLease(const boost::shared_ptr<isc::dhcp::Lease6>&) {
return (false);
}
-Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
+Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const {
return (Lease4Ptr());
}
return (Lease4Collection());
}
-Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress ,
+Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress & ,
SubnetID) const {
return (Lease4Ptr());
}
return (Lease4Collection());
}
-Lease6Ptr Memfile_LeaseMgr::getLease6(isc::asiolink::IOAddress) const {
+Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress&) const {
return (Lease6Ptr());
}
return (Lease6Ptr());
}
-void Memfile_LeaseMgr::updateLease4(Lease4Ptr ) {
+void Memfile_LeaseMgr::updateLease4(const Lease4Ptr&) {
}
-void Memfile_LeaseMgr::updateLease6(Lease6Ptr ) {
+void Memfile_LeaseMgr::updateLease6(const Lease6Ptr&) {
}
-bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
+bool Memfile_LeaseMgr::deleteLease4(const isc::asiolink::IOAddress&) {
return (false);
}
-bool Memfile_LeaseMgr::deleteLease6(isc::asiolink::IOAddress ) {
+bool Memfile_LeaseMgr::deleteLease6(const isc::asiolink::IOAddress&) {
return (false);
}
#include <iostream>
#include <sstream>
#include <utility>
+#include <string>
#include <gtest/gtest.h>
#include <asiolink/io_address.h>
using namespace std;
namespace {
-// empty class for now, but may be extended later.
-class MySqlLeaseMgrTest : public ::testing::Test {
-public:
- MySqlLeaseMgrTest() {
- }
-};
// Connection strings
const char* VALID_TYPE = "type=mysql";
VALID_USER, VALID_PASSWORD));
}
+/// @brief Test Fixture Class
+///
+/// Opens the database prior to each test and closes it afterwards.
+/// All pending transactions are deleted prior to closure.
+
+class MySqlLeaseMgrTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ ///
+ /// Open the database.
+
+ MySqlLeaseMgrTest() {
+ lmptr_ = LeaseMgrFactory::create(validConnectionString());
+ }
+
+ /// @brief Destructor
+ ///
+ /// Rolls back all pending transactions. The deletion of the
+ /// lmptr_ member variable will close the database.
+
+ virtual ~MySqlLeaseMgrTest() {
+ //lmptr_->rollback();
+ }
+
+ LeaseMgrPtr lmptr_; // Pointer to the lease manager
+};
+
-// This test checks if the MySqlLeaseMgr can be instantiated. This happens
-// only if:
-// a) The database can be opened
-// b) It finds the schema_version table
-// c) The schema version matches that in the Lease Manager code
-TEST_F(MySqlLeaseMgrTest, OpenDatabase) {
+/// @brief Check that Database Can Be Opened
+///
+/// This test checks if the MySqlLeaseMgr can be instantiated. This happens
+/// only if the database can be opened. Note that this is not part of the
+/// MySqlLeaseMgr test fixure set. This test checks that the database can be
+/// opened: the fixtures assume that and check basic operations.
+
+TEST(MySqlOpenTest, OpenDatabase) {
LeaseMgrPtr lmptr;
// Check that wrong specification of backend throws an exception.
DbOpenError);
// Check that database opens correctly and that version is as expected
- ASSERT_NO_THROW(lmptr = LeaseMgrFactory::create(connectionString(
- VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)));
+ ASSERT_NO_THROW(lmptr = LeaseMgrFactory::create(validConnectionString()));
ASSERT_TRUE(lmptr);
}
-TEST_F(MySqlLeaseMgrTest, CheckVersion) {
- // Open database
- LeaseMgrPtr lmptr;
- ASSERT_NO_THROW(lmptr = LeaseMgrFactory::create(connectionString(
- VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)));
- ASSERT_TRUE(lmptr);
+/// @brief Check conversion functions
+TEST_F(MySqlLeaseMgrTest, CheckTimeConversion) {
+ const time_t cltt = time(NULL);
+ const uint32_t valid_lft = 86400; // 1 day
+ MYSQL_TIME expire;
+ uint32_t lease_time;
+
+ MySqlLeaseMgr::convertFromLeaseTime(cltt, valid_lft, expire, lease_time);
+ EXPECT_EQ(valid_lft, lease_time);
+ EXPECT_LE(2012, expire.year); // Code was written in 2012
+ EXPECT_EQ(0, expire.second_part);
+ EXPECT_EQ(0, expire.neg);
+ // Convert back
+ time_t converted_cltt = 0;
+ uint32_t converted_valid_lft = 0;
+ MySqlLeaseMgr::convertToLeaseTime(expire, lease_time, converted_cltt,
+ converted_valid_lft);
+ EXPECT_EQ(cltt, converted_cltt);
+ EXPECT_EQ(valid_lft, converted_valid_lft);
+}
+
+/// @brief Check that getVersion() works
+TEST_F(MySqlLeaseMgrTest, CheckVersion) {
// Check version
- pair<uint32_t, uint32_t> version = lmptr->getVersion();
+ pair<uint32_t, uint32_t> version;
+ ASSERT_NO_THROW(version = lmptr_->getVersion());
EXPECT_EQ(0, version.first);
EXPECT_EQ(1, version.second);
}
+
+/// @brief Compare Lease4 Structure
+bool
+compareLease6(const Lease6Ptr& l1, const Lease6Ptr& l2) {
+ return (
+ l1->type_ == l2->type_ &&
+ l1->addr_ == l2->addr_ &&
+ l1->prefixlen_ == l2->prefixlen_ &&
+ l1->iaid_ == l2->iaid_ &&
+ l1->hwaddr_ == l2->hwaddr_ &&
+ *l1->duid_ == *l2->duid_ &&
+ l1->preferred_lft_ == l2->preferred_lft_ &&
+ l1->valid_lft_ == l2->valid_lft_ &&
+ l1->cltt_ == l2->cltt_ &&
+ l1->subnet_id_ == l2->subnet_id_
+ );
+}
+
+void
+detailCompareLease6(const Lease6Ptr& l1, const Lease6Ptr& l2) {
+ EXPECT_EQ(l1->type_, l2->type_);
+ EXPECT_EQ(l1->addr_, l2->addr_);
+ EXPECT_EQ(l1->prefixlen_, l2->prefixlen_);
+ EXPECT_EQ(l1->iaid_, l2->iaid_);
+ EXPECT_TRUE(l1->hwaddr_ == l2->hwaddr_);
+ EXPECT_TRUE(*l1->duid_ == *l2->duid_);
+ EXPECT_EQ(l1->preferred_lft_, l2->preferred_lft_);
+ EXPECT_EQ(l1->valid_lft_, l2->valid_lft_);
+ EXPECT_EQ(l1->cltt_, l2->cltt_);
+ EXPECT_EQ(l1->subnet_id_, l2->subnet_id_);
+}
+
+/// @brief Initialize Lease
+///
+/// Initializes the unused fields in a lease to known values for
+/// testing purposes.
+void initializeUnusedLease6(Lease6Ptr& lease) {
+ lease->t1_ = 0; // Not saved
+ lease->t2_ = 0; // Not saved
+ lease->fixed_ = false; // Unused
+ lease->hostname_ = std::string(""); // Unused
+ lease->fqdn_fwd_ = false; // Unused
+ lease->fqdn_rev_ = false; // Unused
+ lease->comments_ = std::string(""); // Unused
+}
+
+/// @brief Check individual Lease6 methods
+///
+/// Checks that the add/update/delete works. All are done within one
+/// test so that "rollback" can be used to remove trace of the tests
+/// from the database.
+///
+/// Tests where a collection of leases can be returned are in the test
+/// Lease6Collection.
+TEST_F(MySqlLeaseMgrTest, BasicLease6) {
+
+ // Define the leases being used for testing.
+ const IOAddress L1_ADDRESS(std::string("2001:db8::1"));
+ Lease6Ptr l1(new Lease6());
+ initializeUnusedLease6(l1);
+
+ l1->type_ = Lease6::LEASE_IA_TA;
+ l1->addr_ = L1_ADDRESS;
+ l1->prefixlen_ = 0;
+ l1->iaid_ = 42;
+ l1->hwaddr_ = std::vector<uint8_t>(6, 0x42); // Six hex 42's
+ l1->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
+ l1->preferred_lft_ = 3600; // Preferred lifetime
+ l1->valid_lft_ = 3600; // Actual lifetime
+ l1->cltt_ = 123456; // Current time of day
+ l1->subnet_id_ = 73; // Arbitrary number
+
+ const IOAddress L2_ADDRESS(std::string("2001:db8::2"));
+ Lease6Ptr l2(new Lease6());
+ initializeUnusedLease6(l2);
+
+ l2->type_ = Lease6::LEASE_IA_TA;
+ l2->addr_ = L1_ADDRESS;
+ l2->prefixlen_ = 0;
+ l2->iaid_ = 89;
+ l2->hwaddr_ = std::vector<uint8_t>(6, 0xf43); // Six hex 42's
+ l2->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x3a)));
+ l2->preferred_lft_ = 1800; // Preferred lifetime
+ l2->valid_lft_ = 5400; // Actual lifetime
+ l2->cltt_ = 234567; // Current time of day
+ l2->subnet_id_ = l1->subnet_id_; // Same as l1
+
+ // Sanity check that the leases are different
+ ASSERT_FALSE(compareLease6(l1, l2));
+
+ // Start the tests. Add the first lease to the database. Then read it
+ // back to see whether it is what we think it is.
+ Lease6Ptr l_returned;
+
+ ASSERT_TRUE(lmptr_->addLease(l1));
+ l_returned = lmptr_->getLease6(L1_ADDRESS);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease6(l1, l_returned);
+/*
+ // Delete the lease and check that it has been deleted.
+ EXPECT_TRUE(lmptr_->deleteLease6(L1_ADDRESS));
+ l_returned = lmptr_->getLease6(L1_ADDRESS);
+ EXPECT_FALSE(l_returned);
+
+ // Add the address again and check that we can't add it a second time
+ ASSERT_TRUE(lmptr_->addLease(l1));
+ ASSERT_FALSE(lmptr_->addLease(l1));
+
+ // Add the second lease
+ ASSERT_TRUE(lmptr_->addLease(l2));
+
+ // Finally, delete the lease and check we can't delete it again.
+ EXPECT_TRUE(lmptr_->deleteLease6(L1_ADDRESS));
+ EXPECT_FALSE(lmptr_->deleteLease6(L1_ADDRESS));
+ */
+}
+
}; // end of anonymous namespace