From: Stephen Morris Date: Wed, 21 Nov 2012 12:44:18 +0000 (+0000) Subject: [2404] Basic Lease4 functionality working X-Git-Tag: bind10-1.0.0-beta-release~39^2~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=61071513480693ec4d32a3aa73cd60bf63bbb808;p=thirdparty%2Fkea.git [2404] Basic Lease4 functionality working --- diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index a3387acaa4..aa057c15dd 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -41,8 +41,10 @@ namespace { /// 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 @@ -55,8 +57,15 @@ struct TaggedStatement { }; 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, " @@ -77,6 +86,10 @@ TaggedStatement tagged_statements[] = { "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, " @@ -99,6 +112,207 @@ namespace dhcp { +/// @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 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(lease_->addr_); + bind_[0].buffer_type = MYSQL_TYPE_LONG; + bind_[0].buffer = reinterpret_cast(&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(&(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(&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(&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(&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(&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(&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 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(&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(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(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(&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(&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(&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(&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 hwaddr_; ///< Hardware address + uint8_t hwaddr_buffer_[HWADDR_MAX_LEN]; ///< Hardware address buffer + unsigned long hwaddr_length_; ///< Hardware address length + std::vector 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 @@ -177,7 +391,7 @@ public: // valid lifetime: unsigned int bind_[2].buffer_type = MYSQL_TYPE_LONG; - bind_[2].buffer = reinterpret_cast(&lease->valid_lft_); + bind_[2].buffer = reinterpret_cast(&lease_->valid_lft_); bind_[2].is_unsigned = true_; // expire: timestamp @@ -370,11 +584,12 @@ public: 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 duid_; ///< Client identification uint8_t duid_buffer_[DUID_MAX_LEN]; ///< Buffer form of DUID unsigned long duid_length_; ///< Length of the DUID @@ -457,8 +672,9 @@ MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters) // 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()); } @@ -649,20 +865,10 @@ MySqlLeaseMgr::prepareStatements() { } -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 bind = exchange6_->createBindForSend(lease); +MySqlLeaseMgr::addLease(StatementIndex stindex, std::vector& bind) { // Bind the parameters to the statement int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]); @@ -685,19 +891,85 @@ MySqlLeaseMgr::addLease(const Lease6Ptr& lease) { return (true); } +bool +MySqlLeaseMgr::addLease(const Lease4Ptr& lease) { + // Create the MYSQL_BIND array for the lease + std::vector 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 bind = exchange6_->createBindForSend(lease); + + // ... and drop to common code. + return (addLease(INSERT_LEASE6, bind)); +} + + +template +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(addr); + inbind[0].buffer_type = MYSQL_TYPE_LONG; + inbind[0].buffer = reinterpret_cast(&addr4); + inbind[0].is_unsigned = static_cast(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()); } @@ -737,20 +1009,22 @@ MySqlLeaseMgr::getLease4(const ClientId& /* clientid */, } -// 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 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 outbind = exchange6_->createBindForReceive(); + std::vector outbind = exchange->createBindForReceive(); // Bind the output parameters to the statement status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]); @@ -764,8 +1038,6 @@ MySqlLeaseMgr::bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const 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)); @@ -780,39 +1052,8 @@ MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const { 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); } @@ -853,7 +1094,7 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const { // 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. @@ -924,7 +1165,7 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid, // 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. @@ -1014,9 +1255,34 @@ MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) { } } - +// 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(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(&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); diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h index 1884da69c6..cd6f320436 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.h +++ b/src/lib/dhcpsrv/mysql_lease_mgr.h @@ -31,8 +31,9 @@ const uint32_t CURRENT_VERSION_VERSION = 0; 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; @@ -226,6 +227,10 @@ public: /// @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 @@ -233,6 +238,10 @@ public: /// @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 @@ -343,11 +352,14 @@ public: /// /// 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 @@ -389,6 +401,34 @@ private: /// @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& 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 + 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() @@ -398,16 +438,19 @@ private: /// 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 + void bindAndExecute(StatementIndex stindex, Exchange& exchange, + MYSQL_BIND* inbind) const; /// @brief Check Error and Throw Exception /// @@ -437,10 +480,11 @@ private: /// 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 exchange6_; MYSQL* mysql_; ///< MySQL context object std::vector text_statements_; ///< Raw text of statements std::vector statements_; ///< Prepared statements + boost::scoped_ptr exchange4_; ///< Exchange object + boost::scoped_ptr exchange6_; ///< Exchange object }; }; // end of isc::dhcp namespace diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index 32355fcde5..7db8d06d82 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -35,7 +35,12 @@ 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", @@ -167,6 +172,13 @@ public: // 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); @@ -176,6 +188,7 @@ public: destroySchema(); createSchema(); + try { LeaseMgrFactory::create(validConnectionString()); } catch (...) { @@ -210,6 +223,119 @@ public: 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(6, 0x08); + lease->client_id_ = boost::shared_ptr( + new ClientId(vector(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(6, 0x19); + lease->client_id_ = boost::shared_ptr( + new ClientId(vector(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(6, 0x2a); + lease->client_id_ = boost::shared_ptr( + new ClientId(vector(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(6, 0x3b); + vector clientid; + for (uint8_t i = 31; i < 126; ++i) { + clientid.push_back(i); + } + lease->client_id_ = boost::shared_ptr + (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(6, 0x4c); + // Same ClientId as straddr4_[1] + lease->client_id_ = boost::shared_ptr( + new ClientId(vector(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(6, 0x5d); + // Same ClientId and IAID as straddress4_1 + lease->client_id_ = boost::shared_ptr( + new ClientId(vector(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(6, 0x6e); + // Same ClientId as straddress4_1 + lease->client_id_ = boost::shared_ptr( + new ClientId(vector(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(6, 0x7f); + lease->client_id_ = boost::shared_ptr( + new ClientId(vector(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 @@ -339,19 +465,15 @@ public: 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 Vector of pointers to leases - vector createLeases6() { - - // Create leases for each address - vector 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 + void checkLeasesDifferent(const std::vector leases) const { // Check they were created for (int i = 0; i < leases.size(); ++i) { @@ -364,6 +486,44 @@ public: 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 Vector of pointers to leases + vector createLeases4() { + + // Create leases for each address + vector 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 Vector of pointers to leases + vector createLeases6() { + + // Create leases for each address + vector 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); } @@ -373,6 +533,8 @@ public: LeaseMgr* lmptr_; // Pointer to the lease manager + vector straddress4_; // String forms of IPv4 addresses + vector ioaddress4_; // IOAddress forms of IPv4 addresses vector straddress6_; // String forms of IPv6 addresses vector ioaddress6_; // IOAddress forms of IPv6 addresses }; @@ -542,6 +704,61 @@ detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) { } +// @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 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