From: Stephen Morris Date: Sat, 20 Oct 2012 21:52:25 +0000 (+0100) Subject: [2342] Now have addLease and getLease6 methods apparently working X-Git-Tag: trac2487_base~1^2~61 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=93ffcbb3ecdb4f882bb833addb68908446f7ae77;p=thirdparty%2Fkea.git [2342] Now have addLease and getLease6 methods apparently working --- diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h index c6287342e5..73155516c2 100644 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@ -160,6 +160,11 @@ struct Lease4 { 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. @@ -271,6 +276,11 @@ struct Lease6 { 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. @@ -309,12 +319,22 @@ public: /// @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 /// @@ -326,7 +346,7 @@ public: /// @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 @@ -342,7 +362,7 @@ public: /// @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. /// @@ -402,7 +422,7 @@ public: /// @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 /// @@ -433,28 +453,28 @@ public: /// @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. /// @@ -482,10 +502,27 @@ public: /// Also if B>C, some database upgrade procedure may be triggered virtual std::pair 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; diff --git a/src/lib/dhcp/mysql_lease_mgr.cc b/src/lib/dhcp/mysql_lease_mgr.cc index a98f1fde4c..31fcfa8b08 100644 --- a/src/lib/dhcp/mysql_lease_mgr.cc +++ b/src/lib/dhcp/mysql_lease_mgr.cc @@ -16,14 +16,64 @@ #include #include #include - +#include #include +#include 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(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() { @@ -111,8 +161,18 @@ MySqlLeaseMgr::prepareStatements() { 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); } @@ -152,23 +212,122 @@ MySqlLeaseMgr::~MySqlLeaseMgr() { } 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(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(&(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 clientid = lease->duid_->getDuid(); + unsigned long clientid_length = clientid.size(); + + bind[2].buffer_type = MYSQL_TYPE_BLOB; + bind[2].buffer = reinterpret_cast(&(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(&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(&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(&(lease->subnet_id_)); + bind[5].is_unsigned = static_cast(1); + bind[5].is_null = &MLM_FALSE; + + // pref_lifetime: unsigned int + bind[6].buffer_type = MYSQL_TYPE_LONG; + bind[6].buffer = reinterpret_cast(&(lease->preferred_lft_)); + bind[6].is_unsigned = static_cast(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(&lease_type); + bind[7].is_unsigned = static_cast(1); + bind[7].is_null = &MLM_FALSE; + + // iaid: unsigned int + bind[8].buffer_type = MYSQL_TYPE_LONG; + bind[8].buffer = reinterpret_cast(&(lease->iaid_)); + bind[8].is_unsigned = static_cast(1); + bind[8].is_null = &MLM_FALSE; + + // prefix_len: unsigned tinyint + bind[9].buffer_type = MYSQL_TYPE_TINY; + bind[9].buffer = reinterpret_cast(&(lease->prefixlen_)); + bind[9].is_unsigned = static_cast(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()); } @@ -195,8 +354,147 @@ MySqlLeaseMgr::getLease4(const ClientId& /* clientid */, } 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(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(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(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(&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(&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(&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(&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(&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(&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(&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(&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 @@ -211,20 +509,20 @@ MySqlLeaseMgr::getLease6(const DUID& /* duid */, uint32_t /* iaid */, } 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); } @@ -244,10 +542,10 @@ MySqlLeaseMgr::getVersion() const { 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_)); } @@ -265,14 +563,14 @@ MySqlLeaseMgr::getVersion() const { 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_)); @@ -281,5 +579,19 @@ MySqlLeaseMgr::getVersion() const { 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 diff --git a/src/lib/dhcp/mysql_lease_mgr.h b/src/lib/dhcp/mysql_lease_mgr.h index 1f5b71599c..1633d625b9 100644 --- a/src/lib/dhcp/mysql_lease_mgr.h +++ b/src/lib/dhcp/mysql_lease_mgr.h @@ -15,6 +15,7 @@ #ifndef __MYSQL_LEASE_MGR_H #define __MYSQL_LEASE_MGR_H +#include #include #include @@ -51,12 +52,22 @@ public: /// @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 /// @@ -68,7 +79,7 @@ public: /// @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 @@ -84,7 +95,7 @@ public: /// @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. /// @@ -144,7 +155,7 @@ public: /// @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 /// @@ -175,28 +186,28 @@ public: /// @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. /// @@ -224,13 +235,89 @@ public: /// Also if B>C, some database upgrade procedure may be triggered virtual std::pair 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 @@ -262,6 +349,26 @@ private: /// @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 raw_statements_; ///< Raw text of statements diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc index a817d4db24..897301a7fa 100644 --- a/src/lib/dhcp/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc @@ -49,26 +49,26 @@ public: /// @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. @@ -118,7 +118,7 @@ public: /// @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 /// @@ -142,28 +142,28 @@ public: /// @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. /// @@ -180,6 +180,14 @@ public: return (make_pair(uint32_t(0), uint32_t(0))); } + /// @brief Commit transactions + void commit() { + } + + /// @brief Rollback transactions + void rollback() { + } + using LeaseMgr::getParameter; protected: @@ -194,15 +202,15 @@ Memfile_LeaseMgr::Memfile_LeaseMgr(const LeaseMgr::ParameterMap& parameters) Memfile_LeaseMgr::~Memfile_LeaseMgr() { } -bool Memfile_LeaseMgr::addLease(boost::shared_ptr) { +bool Memfile_LeaseMgr::addLease(const boost::shared_ptr&) { return (false); } -bool Memfile_LeaseMgr::addLease(boost::shared_ptr) { +bool Memfile_LeaseMgr::addLease(const boost::shared_ptr&) { return (false); } -Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const { +Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const { return (Lease4Ptr()); } @@ -210,7 +218,7 @@ Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const { return (Lease4Collection()); } -Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress , +Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress & , SubnetID) const { return (Lease4Ptr()); } @@ -230,7 +238,7 @@ Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const { return (Lease4Collection()); } -Lease6Ptr Memfile_LeaseMgr::getLease6(isc::asiolink::IOAddress) const { +Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress&) const { return (Lease6Ptr()); } @@ -243,18 +251,18 @@ Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID&, uint32_t, 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); } diff --git a/src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc index b219677f43..a4626d8278 100644 --- a/src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -28,12 +29,6 @@ using namespace isc::dhcp; 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"; @@ -61,13 +56,42 @@ string validConnectionString() { 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. @@ -92,22 +116,156 @@ TEST_F(MySqlLeaseMgrTest, OpenDatabase) { 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 version = lmptr->getVersion(); + pair 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(6, 0x42); // Six hex 42's + l1->duid_ = boost::shared_ptr(new DUID(vector(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(6, 0xf43); // Six hex 42's + l2->duid_ = boost::shared_ptr(new DUID(vector(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