/// the insertion of a trailing null regardless of whether the data returned
/// contains a trailing null (the documentation is not clear on this point).
-const size_t ADDRESS6_TEXT_MAX_LEN = 42; // Max size of a IPv6 text buffer
-const size_t DUID_MAX_LEN = 128; // Max size of a DUID
+const size_t ADDRESS6_TEXT_MAX_LEN = 42; ///< Max size of a IPv6 text buffer
+const size_t DUID_MAX_LEN = 128; ///< Max size of a DUID
+const size_t HWADDR_MAX_LEN = 128; ///< Max size of a hardware address
+const size_t CLIENT_ID_MAX_LEN = 128; ///< Max size of a client ID
///@}
/// @brief MySQL Selection Statements
};
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 "
+ "FROM lease4 "
+ "WHERE address = ?"},
{MySqlLeaseMgr::GET_LEASE6_ADDR,
"SELECT address, duid, valid_lifetime, "
"expire, subnet_id, pref_lifetime, "
"WHERE duid = ? AND iaid = ? AND subnet_id = ?"},
{MySqlLeaseMgr::GET_VERSION,
"SELECT version, minor FROM schema_version"},
+ {MySqlLeaseMgr::INSERT_LEASE4,
+ "INSERT INTO lease4(address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id) "
+ "VALUES (?, ?, ?, ?, ?, ?)"},
{MySqlLeaseMgr::INSERT_LEASE6,
"INSERT INTO lease6(address, duid, valid_lifetime, "
"expire, subnet_id, pref_lifetime, "
+/// @brief Exchange MySQL and Lease4 Data
+///
+/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
+/// describe the parameters in the prepared statements. Where information is
+/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
+/// structure is identical - it defines data values in the Lease4 structure.
+/// This class handles the creation of that array.
+///
+/// Owing to the MySQL API, the process requires some intermediate variables
+/// to hold things like length etc. This object holds the intermediate
+/// variables as well.
+///
+/// @note There are no unit tests for this class. It is tested indirectly
+/// in all MySqlLeaseMgr::xxx4() calls where it is used.
+
+class MySqlLease4Exchange {
+public:
+ /// @brief Constructor
+ ///
+ /// Apart from the initialization of false_ and true_, the initialization
+ /// of addr4_, hwaddr_length_, hwaddr_buffer_, client_id_length_ and
+ /// client_id_buffer_ are to satisfy cppcheck: none are really needed, as
+ /// all variables are initialized/set in the methods.
+ MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0),
+ false_(0), true_(1) {
+ memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
+ memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
+ }
+
+ /// @brief Create MYSQL_BIND objects for Lease4 Pointer
+ ///
+ /// Fills in the MYSQL_BIND objects for the Lease4 passed to it.
+ ///
+ /// @param lease Lease object to be added to the database.
+ ///
+ /// @return Vector of MySQL BIND objects representing the data to be added.
+ std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
+ // Store lease object to ensure it remains valid.
+ lease_ = lease;
+
+ // Ensure bind_ array clear for constructing the MYSQL_BIND structures
+ // for this lease.
+ memset(bind_, 0, sizeof(bind_));
+
+ // Address: uint32_t
+ // Address in the Lease structre is an IOAddress object. Convert to
+ // an integer for storage.
+ addr4_ = static_cast<uint32_t>(lease_->addr_);
+ bind_[0].buffer_type = MYSQL_TYPE_LONG;
+ bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
+ bind_[0].is_unsigned = true_;
+
+ // hwaddr: varbinary
+ // For speed, we avoid copying the data into temporary storage and
+ // instead extract it from the lease structure directly.
+ 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_;
+
+ // client_id: varbinary
+ client_id_ = lease_->client_id_->getClientId();
+ client_id_length_ = client_id_.size();
+ bind_[2].buffer_type = MYSQL_TYPE_BLOB;
+ bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
+ bind_[2].buffer_length = client_id_length_;
+ bind_[2].length = &client_id_length_;
+
+ // valid lifetime: unsigned int
+ bind_[3].buffer_type = MYSQL_TYPE_LONG;
+ bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
+ bind_[3].is_unsigned = true_;
+
+ // expire: timestamp
+ // The lease structure holds the client last transmission time (cltt_)
+ // For convenience for external tools, this is converted to lease
+ /// expiry time (expire). The relationship is given by:
+ //
+ // expire = cltt_ + valid_lft_
+ //
+ // @TODO Handle overflows
+ MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+ expire_);
+ bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
+ bind_[4].buffer = reinterpret_cast<char*>(&expire_);
+ bind_[4].buffer_length = sizeof(expire_);
+
+ // subnet_id: unsigned int
+ // Can use lease_->subnet_id_ directly as it is of type uint32_t.
+ bind_[5].buffer_type = MYSQL_TYPE_LONG;
+ bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
+ bind_[5].is_unsigned = true_;
+
+ // Add the data to the vector. Note the end element is one after the
+ // end of the array.
+ return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[6]));
+ }
+
+ /// @brief Create BIND array to receive data
+ ///
+ /// Creates a MYSQL_BIND array to receive Lease4 data from the database.
+ /// After data is successfully received, getLeaseData() is used to copy
+ /// it to a Lease6 object.
+ ///
+ /// @return Vector of MySQL BIND objects.
+ std::vector<MYSQL_BIND> createBindForReceive() {
+
+ // Ensure both the array of MYSQL_BIND structures and the error array
+ // are clear.
+ memset(bind_, 0, sizeof(bind_));
+ memset(error_, 0, sizeof(error_));
+
+ // address: uint32_t
+ bind_[0].buffer_type = MYSQL_TYPE_LONG;
+ bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
+ bind_[0].is_unsigned = true_;
+ bind_[0].error = &error_[0];
+
+ // hwaddr: varbinary(20)
+ hwaddr_length_ = sizeof(hwaddr_buffer_);
+ bind_[1].buffer_type = MYSQL_TYPE_BLOB;
+ bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
+ bind_[1].buffer_length = hwaddr_length_;
+ bind_[1].length = &hwaddr_length_;
+ bind_[1].error = &error_[1];
+
+ // client_id: varbinary(128)
+ client_id_length_ = sizeof(client_id_buffer_);
+ bind_[2].buffer_type = MYSQL_TYPE_BLOB;
+ bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
+ bind_[2].buffer_length = client_id_length_;
+ bind_[2].length = &client_id_length_;
+ bind_[2].error = &error_[2];
+
+ // lease_time: unsigned int
+ bind_[3].buffer_type = MYSQL_TYPE_LONG;
+ bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
+ bind_[3].is_unsigned = true_;
+ bind_[3].error = &error_[3];
+
+ // expire: timestamp
+ bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
+ bind_[4].buffer = reinterpret_cast<char*>(&expire_);
+ bind_[4].buffer_length = sizeof(expire_);
+ bind_[4].error = &error_[4];
+
+ // subnet_id: unsigned int
+ bind_[5].buffer_type = MYSQL_TYPE_LONG;
+ bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
+ bind_[5].is_unsigned = true_;
+ bind_[5].error = &error_[5];
+
+ // Add the data to the vector. Note the end element is one after the
+ // end of the array.
+ return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[6]));
+ }
+
+ /// @brief Copy Received Data into Lease6 Object
+ ///
+ /// Called after the MYSQL_BIND array created by createBindForReceive()
+ /// has been used, this copies data from the internal member vairables
+ /// into a Lease4 object.
+ ///
+ /// @return Lease6Ptr Pointer to a Lease6 object holding the relevant
+ /// data.
+ ///
+ /// @throw isc::BadValue Unable to convert Lease Type value in database
+ Lease4Ptr getLeaseData() {
+ // Convert times to units for the lease structure
+ time_t cltt = 0;
+ MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+
+ return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
+ client_id_buffer_, client_id_length_,
+ valid_lifetime_, cltt, subnet_id_)));
+ }
+
+private:
+ // Note: All array lengths are equal to the corresponding variable in the
+ // schema.
+ // Note: arrays are declared fixed length for speed of creation
+ uint32_t addr4_; ///< IPv4 address
+ MYSQL_BIND bind_[9]; ///< Bind array
+ std::vector<uint8_t> hwaddr_; ///< Hardware address
+ uint8_t hwaddr_buffer_[HWADDR_MAX_LEN]; ///< Hardware address buffer
+ unsigned long hwaddr_length_; ///< Hardware address length
+ std::vector<uint8_t> client_id_; ///< Client identification
+ uint8_t client_id_buffer_[CLIENT_ID_MAX_LEN]; ///< Client ID buffer
+ unsigned long client_id_length_; ///< Client ID address length
+ my_bool error_[6]; ///< For error reporting
+ MYSQL_TIME expire_; ///< Lease expiry time
+ const my_bool false_; ///< "false" for MySql
+ Lease4Ptr lease_; ///< Pointer to lease object
+ uint32_t valid_lifetime_; ///< Lease time
+ uint32_t subnet_id_; ///< Subnet identification
+ const my_bool true_; ///< "true_" for MySql
+};
+
+
+
/// @brief Exchange MySQL and Lease6 Data
///
/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
// valid lifetime: unsigned int
bind_[2].buffer_type = MYSQL_TYPE_LONG;
- bind_[2].buffer = reinterpret_cast<char*>(&lease->valid_lft_);
+ bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
bind_[2].is_unsigned = true_;
// expire: timestamp
private:
// Note: All array lengths are equal to the corresponding variable in the
// schema.
+ // Note: arrays are declared fixed length for speed of creation
std::string addr6_; ///< String form of address
char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN]; ///< Character
///< array form of V6 address
unsigned long addr6_length_; ///< Length of the address
- MYSQL_BIND bind_[9]; ///< Static array for speed of access
+ MYSQL_BIND bind_[9]; ///< Bind array
std::vector<uint8_t> duid_; ///< Client identification
uint8_t duid_buffer_[DUID_MAX_LEN]; ///< Buffer form of DUID
unsigned long duid_length_; ///< Length of the DUID
// Prepare all statements likely to be used.
prepareStatements();
- // Create the exchange object for use in exchanging data between the
+ // Create the exchange objects for use in exchanging data between the
// program and the database.
+ exchange4_.reset(new MySqlLease4Exchange());
exchange6_.reset(new MySqlLease6Exchange());
}
}
-bool
-MySqlLeaseMgr::addLease(const Lease4Ptr& /* lease */) {
- isc_throw(NotImplemented, "MySqlLeaseMgr::addLease(const Lease4Ptr&) "
- "not implemented yet");
- return (false);
-}
-
+// Common add lease code
bool
-MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
- const StatementIndex stindex = INSERT_LEASE6;
-
- // Create the MYSQL_BIND array for the lease
- std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
+MySqlLeaseMgr::addLease(StatementIndex stindex, std::vector<MYSQL_BIND>& bind) {
// Bind the parameters to the statement
int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]);
return (true);
}
+bool
+MySqlLeaseMgr::addLease(const Lease4Ptr& lease) {
+ // Create the MYSQL_BIND array for the lease
+ std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
+
+ // ... and drop to common code.
+ return (addLease(INSERT_LEASE4, bind));
+}
+
+bool
+MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
+ // Create the MYSQL_BIND array for the lease
+ std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
+
+ // ... and drop to common code.
+ return (addLease(INSERT_LEASE6, bind));
+}
+
+
+template <typename Exchange, typename LeasePtr>
+void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* inbind,
+ Exchange& exchange, LeasePtr& result) const {
+
+ // Bind the input parameters to the statement and bind the output
+ // to fields in the exchange object, then execute the prepared statement.
+ bindAndExecute(stindex, exchange, inbind);
+
+ // Fetch the data and set up the "release" object to release associated
+ // resources when this method exits.
+ MySqlFreeResult fetch_release(statements_[stindex]);
+ int status = mysql_stmt_fetch(statements_[stindex]);
+
+ if (status == 0) {
+ try {
+ result = exchange->getLeaseData();
+ } catch (const isc::BadValue& ex) {
+ // Lease type is returned, to rethrow the exception with a bit
+ // more data.
+ isc_throw(BadValue, ex.what() << ". Statement is <" <<
+ text_statements_[stindex] << ">");
+ }
+
+ // As the address is the primary key in the table, we can't return
+ // two rows, so we don't bother checking whether multiple rows have
+ // been returned.
+
+ } else if (status == 1) {
+ checkError(status, stindex, "unable to fetch results");
+
+ } else {
+ // @TODO Handle truncation
+ // We are ignoring truncation for now, so the only other result is
+ // no data was found. In that case, we return a null Lease6 structure.
+ // This has already been set, so no action is needed.
+ }
+}
Lease4Ptr
-MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */,
- SubnetID /* subnet_id */) const {
- isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const IOAddress&, SubnetID) "
- "not implemented yet");
- return (Lease4Ptr());
+MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[1];
+ memset(inbind, 0, sizeof(inbind));
+
+ uint32_t addr4 = static_cast<uint32_t>(addr);
+ inbind[0].buffer_type = MYSQL_TYPE_LONG;
+ inbind[0].buffer = reinterpret_cast<char*>(&addr4);
+ inbind[0].is_unsigned = static_cast<my_bool>(1);
+
+ Lease4Ptr result;
+ getLease(GET_LEASE4_ADDR, inbind, exchange4_, result);
+
+ return (result);
}
Lease4Ptr
-MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */) const {
- isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const IOAddress&) "
+MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */,
+ SubnetID /* subnet_id */) const {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const IOAddress&, SubnetID) "
"not implemented yet");
return (Lease4Ptr());
}
}
-// A convenience function used in the various getLease6() methods. It binds
+// A convenience function used in the various getLease() methods. It binds
// the selection parameters to the prepared statement, and binds the variables
// that will receive the data. These are stored in the MySqlLease6Exchange
// object associated with the lease manager and converted to a Lease6 object
// when retrieved.
+template <typename Exchange>
void
-MySqlLeaseMgr::bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const {
+MySqlLeaseMgr::bindAndExecute(StatementIndex stindex, Exchange& exchange,
+ MYSQL_BIND* inbind) const {
// Bind the input parameters to the statement
int status = mysql_stmt_bind_param(statements_[stindex], inbind);
checkError(status, stindex, "unable to bind WHERE clause parameter");
// Set up the SELECT clause
- std::vector<MYSQL_BIND> outbind = exchange6_->createBindForReceive();
+ std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
// Bind the output parameters to the statement
status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]);
Lease6Ptr
MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
- const StatementIndex stindex = GET_LEASE6_ADDR;
-
// Set up the WHERE clause value
MYSQL_BIND inbind[1];
memset(inbind, 0, sizeof(inbind));
inbind[0].buffer_length = addr6_length;
inbind[0].length = &addr6_length;
- // Bind the input parameters to the statement and bind the output
- // to fields in the exchange object, then execute the prepared statement.
- bind6AndExecute(stindex, inbind);
-
- // Fetch the data and set up the "release" object to release associated
- // resources when this method exits.
- MySqlFreeResult fetch_release(statements_[stindex]);
- int status = mysql_stmt_fetch(statements_[stindex]);
-
Lease6Ptr result;
- if (status == 0) {
- try {
- result = exchange6_->getLeaseData();
- } catch (const isc::BadValue& ex) {
- // Lease type is returned, to rethrow the exception with a bit
- // more data.
- isc_throw(BadValue, ex.what() << ". Statement is <" <<
- text_statements_[stindex] << ">");
- }
-
- // As the address is the primary key in the table, we can't return
- // two rows, so we don't bother checking whether multiple rows have
- // been returned.
-
- } else if (status == 1) {
- checkError(status, stindex, "unable to fetch results");
-
- } else {
- // @TODO Handle truncation
- // We are ignoring truncation for now, so the only other result is
- // no data was found. In that case, we return a null Lease6 structure.
- // This has already been set, so no action is needed.
- }
+ getLease(GET_LEASE6_ADDR, inbind, exchange6_, result);
return (result);
}
// Bind the input parameters to the statement and bind the output
// to fields in the exchange object, then execute the prepared statement.
- bind6AndExecute(stindex, inbind);
+ bindAndExecute(stindex, exchange6_, inbind);
// Ensure that all the lease information is retrieved in one go to avoid
// overhead of going back and forth between client and server.
// Bind the input parameters to the statement and bind the output
// to fields in the exchange object, then execute the prepared statement.
- bind6AndExecute(stindex, inbind);
+ bindAndExecute(stindex, exchange6_, inbind);
// Fetch the data and set up the "release" object to release associated
// resources when this method exits then retrieve the data.
}
}
-
+// TODO: Replace by deleteLease
bool
-MySqlLeaseMgr::deleteLease4(const isc::asiolink::IOAddress& /* addr */) {
+MySqlLeaseMgr::deleteLease4(const isc::asiolink::IOAddress& addr) {
+ const StatementIndex stindex = DELETE_LEASE4;
+
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[1];
+ memset(inbind, 0, sizeof(inbind));
+
+ uint32_t addr4 = static_cast<uint32_t>(addr);
+
+ // See the earlier description of the use of "const_cast" when accessing
+ // the address for an explanation of the reason.
+ inbind[0].buffer_type = MYSQL_TYPE_LONG;
+ inbind[0].buffer = reinterpret_cast<char*>(&addr4);
+ inbind[0].is_unsigned = my_bool(1);
+
+ // Bind the input parameters to the statement
+ int status = mysql_stmt_bind_param(statements_[stindex], inbind);
+ checkError(status, stindex, "unable to bind WHERE clause parameter");
+
+ // Execute
+ status = mysql_stmt_execute(statements_[stindex]);
+ checkError(status, stindex, "unable to execute");
+
+ // See how many rows were affected. Note that the statement may delete
+ // multiple rows.
+ return (mysql_stmt_affected_rows(statements_[stindex]) > 0);
isc_throw(NotImplemented, "MySqlLeaseMgr::deleteLease4(const IOAddress&) "
"not implemented yet");
return (false);
const uint32_t CURRENT_VERSION_MINOR = 1;
-// Forward declaration of the Lease6 exchange object. This class is defined
+// Forward declaration of the Lease exchange objects. This class is defined
// in the .cc file.
+class MySqlLease4Exchange;
class MySqlLease6Exchange;
/// @brief Deletes an IPv4 lease.
///
+ /// @todo Merge with deleteLease6: it is possible to determine whether
+ /// an address is V4 or V6 from the IOAddress argument, so there
+ /// is no need for separate V4 or V6 methods.
+ ///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
/// @brief Deletes an IPv6 lease.
///
+ /// @todo Merge with deleteLease4: it is possible to determine whether
+ /// an address is V4 or V6 from the IOAddress argument, so there
+ /// is no need for separate V4 or V6 methods.
+ ///
/// @param addr IPv6 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
///
/// 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_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_LEASE6, // Update a Lease6 entry
NUM_STATEMENTS // Number of statements
/// @throw DbOpenError Error opening the database
void openDatabase();
+ /// @brief Add Lease Common Code
+ ///
+ /// This method performs the common actions for both flavours of the
+ /// addLease method.
+ ///
+ /// @param stindex Index of statemnent being executed
+ /// @param bind MYSQL_BIND array that has been created for the type
+ /// of lease in question.
+ ///
+ /// @return true if the lease was added, false if it was not added because
+ /// a lease with that address already exists in the database.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ bool addLease(StatementIndex stindex, std::vector<MYSQL_BIND>& bind);
+
+ /// @brief Get Lease Common Code
+ ///
+ /// This method performs the common actions for the getLease methods.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param inbind MYSQL_BIND array for input parameters
+ /// @param exchange Exchange object to use
+ /// @param lease Lease object returned
+ template <typename Exchange, typename LeasePtr>
+ void getLease(StatementIndex stindex, MYSQL_BIND* inbind,
+ Exchange& exchange, LeasePtr& result) const;
+
/// @brief Binds Parameters and Executes
///
/// This method abstracts a lot of common processing from the getXxxx()
/// statement.
///
/// The data can be retrieved using mysql_stmt_fetch and the getLeaseData()
- /// method on the exchange6 object.
+ /// method on the appropriate exchange object.
///
/// @param stindex Index of prepared statement to be executed
+ /// @param exchange Exchange object to use
/// @param inbind Array of MYSQL_BIND objects representing the parameters.
/// (Note that the number is determined by the number of parameters
/// in the statement.)
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- void bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const;
+ template <typename Exchange>
+ void bindAndExecute(StatementIndex stindex, Exchange& exchange,
+ MYSQL_BIND* inbind) const;
/// @brief Check Error and Throw Exception
///
/// object as its contents may change in "const" calls, while the rest
/// of this object does not. (At alternative would be to declare it as
/// "mutable".)
- boost::scoped_ptr<MySqlLease6Exchange> exchange6_;
MYSQL* mysql_; ///< MySQL context object
std::vector<std::string> text_statements_; ///< Raw text of statements
std::vector<MYSQL_STMT*> statements_; ///< Prepared statements
+ boost::scoped_ptr<MySqlLease4Exchange> exchange4_; ///< Exchange object
+ boost::scoped_ptr<MySqlLease6Exchange> exchange6_; ///< Exchange object
};
}; // end of isc::dhcp namespace
// Creation of the schema
#include "schema_copy.h"
-// IPv6 addresseses
+// IPv4 and IPv6 addresseses
+const char* ADDRESS4[] = {
+ "192.0.2.0", "192.0.2.1", "192.0.2.2", "192.0.2.3",
+ "192.0.2.4", "192.0.2.5", "192.0.2.6", "192.0.2.7",
+ NULL
+};
const char* ADDRESS6[] = {
"2001:db8::0", "2001:db8::1", "2001:db8::2", "2001:db8::3",
"2001:db8::4", "2001:db8::5", "2001:db8::6", "2001:db8::7",
// Deletes everything from the database and opens it.
MySqlLeaseMgrTest() {
// Initialize address strings and IOAddresses
+ for (int i = 0; ADDRESS4[i] != NULL; ++i) {
+ string addr(ADDRESS4[i]);
+ straddress4_.push_back(addr);
+ IOAddress ioaddr(addr);
+ ioaddress4_.push_back(ioaddr);
+ }
+
for (int i = 0; ADDRESS6[i] != NULL; ++i) {
string addr(ADDRESS6[i]);
straddress6_.push_back(addr);
destroySchema();
createSchema();
+
try {
LeaseMgrFactory::create(validConnectionString());
} catch (...) {
lmptr_ = &(LeaseMgrFactory::instance());
}
+ // @brief Initialize Lease4 Fields
+ //
+ // Returns a pointer to a Lease4 structure. Different values are put
+ // in the lease according to the address passed.
+ //
+ // This is just a convenience function for the test methods.
+ //
+ // @param address Address to use for the initialization
+ //
+ // @return Lease4Ptr. This will not point to anything if the initialization
+ // failed (e.g. unknown address).
+ Lease4Ptr initializeLease4(std::string address) {
+ Lease4Ptr lease(new Lease4());
+
+ // Set the address of the lease
+ lease->addr_ = IOAddress(address);
+
+ // Initialize unused fields.
+ lease->ext_ = 0; // Not saved
+ 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
+
+ // Set other parameters. For historical reasons, address 0 is not used.
+ if (address == straddress4_[0]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x08);
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x77)));
+ lease->valid_lft_ = 8677; // Actual lifetime
+ lease->cltt_ = 168256; // Current time of day
+ lease->subnet_id_ = 23; // Arbitrary number
+
+ } else if (address == straddress4_[1]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x19);
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x42)));
+ lease->valid_lft_ = 3677; // Actual lifetime
+ lease->cltt_ = 123456; // Current time of day
+ lease->subnet_id_ = 73; // Arbitrary number
+
+ } else if (address == straddress4_[2]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x2a);
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x3a)));
+ lease->valid_lft_ = 5412; // Actual lifetime
+ lease->cltt_ = 234567; // Current time of day
+ lease->subnet_id_ = 73; // Same as for straddress4_1
+
+ } else if (address == straddress4_[3]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x3b);
+ vector<uint8_t> clientid;
+ for (uint8_t i = 31; i < 126; ++i) {
+ clientid.push_back(i);
+ }
+ lease->client_id_ = boost::shared_ptr<ClientId>
+ (new ClientId(clientid));
+
+ // The times used in the next tests are deliberately restricted - we
+ // should be able to cope with valid lifetimes up to 0xffffffff.
+ // However, this will lead to overflows.
+ // @TODO: test overflow conditions when code has been fixed
+ lease->valid_lft_ = 7000; // Actual lifetime
+ lease->cltt_ = 234567; // Current time of day
+ lease->subnet_id_ = 37; // Different from L1 and L2
+
+ } else if (address == straddress4_[4]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x4c);
+ // Same ClientId as straddr4_[1]
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x42)));
+ lease->valid_lft_ = 7736; // Actual lifetime
+ lease->cltt_ = 222456; // Current time of day
+ lease->subnet_id_ = 75; // Arbitrary number
+
+ } else if (address == straddress4_[5]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x5d);
+ // Same ClientId and IAID as straddress4_1
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x42)));
+ lease->valid_lft_ = 7832; // Actual lifetime
+ lease->cltt_ = 227476; // Current time of day
+ lease->subnet_id_ = 175; // Arbitrary number
+
+ } else if (address == straddress4_[6]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x6e);
+ // Same ClientId as straddress4_1
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x42)));
+ lease->valid_lft_ = 1832; // Actual lifetime
+ lease->cltt_ = 627476; // Current time of day
+ lease->subnet_id_ = 112; // Arbitrary number
+
+ } else if (address == straddress4_[7]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x7f);
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0xe5)));
+ lease->valid_lft_ = 7975; // Actual lifetime
+ lease->cltt_ = 213876; // Current time of day
+ lease->subnet_id_ = 19; // Arbitrary number
+
+ } else {
+ // Unknown address, return an empty pointer.
+ lease.reset();
+
+ }
+
+ return (lease);
+ }
+
// @brief Initialize Lease6 Fields
//
// Returns a pointer to a Lease6 structure. Different values are put
return (lease);
}
- // @brief Creates Leases for the test
+ // @brief Check Leases Present and Different
//
- // Creates all leases for the test and checks that they are different.
+ // Checks a vector of lease pointers and ensures that all the leases
+ // they point to are present and different. If not, a GTest assertion
+ // will fail.
//
- // @return vector<Lease6Ptr> Vector of pointers to leases
- vector<Lease6Ptr> createLeases6() {
-
- // Create leases for each address
- vector<Lease6Ptr> leases;
- for (int i = 0; i < straddress6_.size(); ++i) {
- leases.push_back(initializeLease6(straddress6_[i]));
- }
- EXPECT_EQ(8, leases.size());
+ // @param leases Vector of pointers to leases
+ template <typename T>
+ void checkLeasesDifferent(const std::vector<T> leases) const {
// Check they were created
for (int i = 0; i < leases.size(); ++i) {
EXPECT_TRUE(leases[i] != leases[j]);
}
}
+ }
+
+ // @brief Creates Leases for the test
+ //
+ // Creates all leases for the test and checks that they are different.
+ //
+ // @return vector<Lease4Ptr> Vector of pointers to leases
+ vector<Lease4Ptr> createLeases4() {
+
+ // Create leases for each address
+ vector<Lease4Ptr> leases;
+ for (int i = 0; i < straddress4_.size(); ++i) {
+ leases.push_back(initializeLease4(straddress4_[i]));
+ }
+ EXPECT_EQ(8, leases.size());
+
+ // Check all were created and that they are different.
+ checkLeasesDifferent(leases);
+
+ return (leases);
+ }
+
+ // @brief Creates Leases for the test
+ //
+ // Creates all leases for the test and checks that they are different.
+ //
+ // @return vector<Lease6Ptr> Vector of pointers to leases
+ vector<Lease6Ptr> createLeases6() {
+
+ // Create leases for each address
+ vector<Lease6Ptr> leases;
+ for (int i = 0; i < straddress6_.size(); ++i) {
+ leases.push_back(initializeLease6(straddress6_[i]));
+ }
+ EXPECT_EQ(8, leases.size());
+
+ // Check all were created and that they are different.
+ checkLeasesDifferent(leases);
return (leases);
}
LeaseMgr* lmptr_; // Pointer to the lease manager
+ vector<string> straddress4_; // String forms of IPv4 addresses
+ vector<IOAddress> ioaddress4_; // IOAddress forms of IPv4 addresses
vector<string> straddress6_; // String forms of IPv6 addresses
vector<IOAddress> ioaddress6_; // IOAddress forms of IPv6 addresses
};
}
+// @brief Basic Lease Checks
+//
+// Checks that the add/get/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
+// Lease4Collection.
+//
+// @param leases Vector of leases used in the tests
+// @param ioaddress Vector of IOAddresses used in the tests
+
+TEST_F(MySqlLeaseMgrTest, basicLease4) {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Start the tests. Add three leases to the database, read them back and
+ // check they are what we think they are.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ EXPECT_TRUE(lmptr_->addLease(leases[2]));
+ EXPECT_TRUE(lmptr_->addLease(leases[3]));
+ lmptr_->commit();
+
+ // Reopen the database to ensure that they actually got stored.
+ reopen();
+
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[3]);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease(leases[3], l_returned);
+
+ // Check that we can't add a second lease with the same address
+ EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+ // Delete a lease, check that it's gone, and that we can't delete it
+ // a second time.
+ EXPECT_TRUE(lmptr_->deleteLease4(ioaddress4_[1]));
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ EXPECT_FALSE(l_returned);
+ EXPECT_FALSE(lmptr_->deleteLease4(ioaddress4_[1]));
+
+ // Check that the second address is still there.
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+}
+
+
// @brief Check individual Lease6 methods
//
// Checks that the add/get/delete works. All are done within one