BIND 10 master, updated. ce7db48d3ff5d5aad12b1da5e67ae60073cb2607 [2404] Merge branch 'master' into trac2404
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Dec 7 13:39:42 UTC 2012
The branch, master has been updated
via ce7db48d3ff5d5aad12b1da5e67ae60073cb2607 (commit)
via 1c59115d79bc9a80821a72cfab2c1bb6a80137f2 (commit)
via fca31329405278e34049e2e19982a82bc6fdf4f2 (commit)
via 8f6903dc068a22c2966e9c28c90c14b6d83543cc (commit)
via b6c480a59485c8a53d9beea2dba85a667aef7556 (commit)
via 495d4e2f6970ef05f5f764f1fcd6661b5403cefb (commit)
via ef364ccb84c6b8eea4952c0a59c6bb560ff43ca4 (commit)
via 46cfa0779257292e55c048a3854f0df4fe1cbd08 (commit)
via 5b1850bbc20932642495b8ae8e5f14ae5cd9c1dc (commit)
via 3b2c2c14fb9f99e1f189c70441d23063c7a26b6e (commit)
via a08894f45157cd19450927d2e8b80ce49a86acaf (commit)
via 7e6602a873c952f8c01e141b4a40f4f2639758cd (commit)
via 3eab2458f1e6b122161361a50bff80e4f5d24825 (commit)
via 59c65b476bbc17417361b99234a7001a894b71e0 (commit)
via af71689ee49cd8668dd830e7a90f06c9b7fc7de7 (commit)
via 61071513480693ec4d32a3aa73cd60bf63bbb808 (commit)
via f15be5f7bf0fa3e5381afe4bb820a06af434a5af (commit)
via 01ee67bc74edb15536ae92b558eddd496da13fce (commit)
via d135a0e1f84ed2700d283ce20c7d96e5be614619 (commit)
via b7bce1d66baed1e01a951a6b13151783b2d74444 (commit)
from 126aeb9d11c7f1d670a30bb2a5a3cad31cbd9c0d (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit ce7db48d3ff5d5aad12b1da5e67ae60073cb2607
Merge: 1c59115 126aeb9
Author: Stephen Morris <stephen at isc.org>
Date: Fri Dec 7 12:28:21 2012 +0000
[2404] Merge branch 'master' into trac2404
commit 1c59115d79bc9a80821a72cfab2c1bb6a80137f2
Author: Stephen Morris <stephen at isc.org>
Date: Fri Dec 7 12:25:40 2012 +0000
[2404] Alter memfile "getName()" to return "memory"
This reflects the fact that at the moment, the data is stored in
memory and not in a backing file.
commit fca31329405278e34049e2e19982a82bc6fdf4f2
Author: Stephen Morris <stephen at isc.org>
Date: Fri Dec 7 11:18:47 2012 +0000
[2404] Minor changes as a result of review
commit 8f6903dc068a22c2966e9c28c90c14b6d83543cc
Author: Stephen Morris <stephen at isc.org>
Date: Tue Dec 4 11:32:08 2012 +0000
[2404] Updated comments etc.
commit b6c480a59485c8a53d9beea2dba85a667aef7556
Author: Stephen Morris <stephen at isc.org>
Date: Mon Dec 3 21:23:48 2012 +0000
[2404] Extending test cases as the result of review
commit 495d4e2f6970ef05f5f764f1fcd6661b5403cefb
Author: Stephen Morris <stephen at isc.org>
Date: Mon Dec 3 15:35:36 2012 +0000
[2404] More changes as a result of review
Including:
* Updates to comments etc.
* Addition of Lease4::operator==() (and associated unit tests)
commit ef364ccb84c6b8eea4952c0a59c6bb560ff43ca4
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 30 15:49:42 2012 +0000
[2404] First set of changes as a result of review
* Corrections of miscellaneous typos in comments
* Update database version to 1.0
* Handling of truncation while fetching data from the database
commit 46cfa0779257292e55c048a3854f0df4fe1cbd08
Author: Stephen Morris <stephen at isc.org>
Date: Mon Nov 26 19:17:27 2012 +0000
[2404] Further refactoring and a bit of simplification.
Also, included dhcpsrv in the list of directories searched by Doxygen.
commit 5b1850bbc20932642495b8ae8e5f14ae5cd9c1dc
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 23 18:23:21 2012 +0000
[2404] Tidying up and refactoring the code
commit 3b2c2c14fb9f99e1f189c70441d23063c7a26b6e
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 23 14:08:00 2012 +0000
[2404] Add ability to update an IPv4 lease
commit a08894f45157cd19450927d2e8b80ce49a86acaf
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 23 13:48:42 2012 +0000
[2404] Add method to get lease4 by client ID and subnet ID
commit 7e6602a873c952f8c01e141b4a40f4f2639758cd
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 23 12:47:40 2012 +0000
[2404] Add get lease4 by client ID
commit 3eab2458f1e6b122161361a50bff80e4f5d24825
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 23 12:23:17 2012 +0000
[2404] Add method to get lease4 by hardware address & subnet ID
commit 59c65b476bbc17417361b99234a7001a894b71e0
Author: Stephen Morris <stephen at isc.org>
Date: Fri Nov 23 12:05:55 2012 +0000
[2404] Now able to get IPv4 leases by hardware address
commit af71689ee49cd8668dd830e7a90f06c9b7fc7de7
Author: Stephen Morris <stephen at isc.org>
Date: Wed Nov 21 19:38:26 2012 +0000
[2404] Add getLease4(const IOAddress&, SubnetId)
Add ability to select IPv4 leases by address and Subnet ID.
commit 61071513480693ec4d32a3aa73cd60bf63bbb808
Author: Stephen Morris <stephen at isc.org>
Date: Wed Nov 21 12:44:18 2012 +0000
[2404] Basic Lease4 functionality working
commit f15be5f7bf0fa3e5381afe4bb820a06af434a5af
Author: Stephen Morris <stephen at isc.org>
Date: Tue Nov 20 12:49:38 2012 +0000
[2404] lease_time renamed valid_lifetime in lease4 table
Make the names of the columns consistent between the lease4 and
lease6 tables.
commit 01ee67bc74edb15536ae92b558eddd496da13fce
Author: Stephen Morris <stephen at isc.org>
Date: Tue Nov 20 12:47:22 2012 +0000
[2404] Add another Lease4 constructor
This constructore is for use by the MySQL code. By passing buffer
addresses and lengths, the need for the creation of intermediate
std::vectors is avoided.
commit d135a0e1f84ed2700d283ce20c7d96e5be614619
Author: Stephen Morris <stephen at isc.org>
Date: Tue Nov 20 12:45:01 2012 +0000
[2404] Minor changes to make files conform to programming style
commit b7bce1d66baed1e01a951a6b13151783b2d74444
Author: Stephen Morris <stephen at isc.org>
Date: Mon Nov 19 19:37:07 2012 +0000
[2404] Change multiple discrete variables to arrays
... in preparation for adding IPv4 tests
-----------------------------------------------------------------------
Summary of changes:
doc/Doxyfile | 4 +-
src/lib/dhcp/duid.cc | 24 +-
src/lib/dhcp/duid.h | 49 +-
src/lib/dhcpsrv/database_backends.dox | 13 +
src/lib/dhcpsrv/dhcpdb_create.mysql | 11 +-
src/lib/dhcpsrv/lease_mgr.cc | 53 +-
src/lib/dhcpsrv/lease_mgr.h | 227 ++--
src/lib/dhcpsrv/memfile_lease_mgr.h | 5 +-
src/lib/dhcpsrv/mysql_lease_mgr.cc | 1218 +++++++++++++++-----
src/lib/dhcpsrv/mysql_lease_mgr.h | 257 ++++-
.../dhcpsrv/tests/lease_mgr_factory_unittest.cc | 2 +-
src/lib/dhcpsrv/tests/lease_mgr_unittest.cc | 382 +++++-
.../dhcpsrv/tests/memfile_lease_mgr_unittest.cc | 2 +-
src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc | 1104 ++++++++++++++----
src/lib/dhcpsrv/tests/schema_copy.h | 10 +-
15 files changed, 2609 insertions(+), 752 deletions(-)
-----------------------------------------------------------------------
diff --git a/doc/Doxyfile b/doc/Doxyfile
index cc3b595..fbd082f 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -580,8 +580,8 @@ INPUT = ../src/lib/exceptions ../src/lib/cc \
../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
../src/lib/util/threads/ ../src/lib/resolve ../src/lib/acl \
- ../src/lib/statistics ../src/bin/dhcp6 ../src/lib/dhcp ../src/bin/dhcp4 \
- ../tests/tools/perfdhcp devel
+ ../src/lib/statistics ../src/bin/dhcp6 ../src/lib/dhcp ../src/lib/dhcpsrv \
+ ../src/bin/dhcp4 ../tests/tools/perfdhcp devel
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc
index c3f46da..91efe94 100644
--- a/src/lib/dhcp/duid.cc
+++ b/src/lib/dhcp/duid.cc
@@ -33,7 +33,7 @@ DUID::DUID(const std::vector<uint8_t>& duid) {
}
}
-DUID::DUID(const uint8_t * data, size_t len) {
+DUID::DUID(const uint8_t* data, size_t len) {
if (len > MAX_DUID_LEN) {
isc_throw(OutOfRange, "DUID too large");
}
@@ -72,36 +72,36 @@ std::string DUID::toText() const {
return (tmp.str());
}
-bool DUID::operator == (const DUID& other) const {
+bool DUID::operator==(const DUID& other) const {
return (this->duid_ == other.duid_);
}
-bool DUID::operator != (const DUID& other) const {
+bool DUID::operator!=(const DUID& other) const {
return (this->duid_ != other.duid_);
}
-/// constructor based on vector<uint8_t>
+// Constructor based on vector<uint8_t>
ClientId::ClientId(const std::vector<uint8_t>& clientid)
- :DUID(clientid) {
+ : DUID(clientid) {
}
-/// constructor based on C-style data
+// Constructor based on C-style data
ClientId::ClientId(const uint8_t *clientid, size_t len)
- :DUID(clientid, len) {
+ : DUID(clientid, len) {
}
-/// @brief returns a copy of client-id data
+// Returns a copy of client-id data
const std::vector<uint8_t> ClientId::getClientId() const {
return (duid_);
}
-// compares two client-ids
-bool ClientId::operator == (const ClientId& other) const {
+// Compares two client-ids
+bool ClientId::operator==(const ClientId& other) const {
return (this->duid_ == other.duid_);
}
-// compares two client-ids
-bool ClientId::operator != (const ClientId& other) const {
+// Compares two client-ids
+bool ClientId::operator!=(const ClientId& other) const {
return (this->duid_ != other.duid_);
}
diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h
index 001e362..1b75f73 100644
--- a/src/lib/dhcp/duid.h
+++ b/src/lib/dhcp/duid.h
@@ -45,13 +45,13 @@ class DUID {
DUID_MAX ///< not a real type, just maximum defined value + 1
} DUIDType;
- /// @brief creates a DUID
+ /// @brief Constructor from vector
DUID(const std::vector<uint8_t>& duid);
- /// @brief creates a DUID
- DUID(const uint8_t *duid, size_t len);
+ /// @brief Constructor from array and array size
+ DUID(const uint8_t* duid, size_t len);
- /// @brief returns a const reference to the actual DUID value
+ /// @brief Returns a const reference to the actual DUID value
///
/// Note: For safety reasons, this method returns a copy of data as
/// otherwise the reference would be only valid as long as the object that
@@ -60,47 +60,58 @@ class DUID {
/// (e.g. storeSelf()) that will avoid data copying.
const std::vector<uint8_t> getDuid() const;
- /// @brief returns DUID type
+ /// @brief Returns the DUID type
DUIDType getType() const;
- /// returns textual prepresentation (e.g. 00:01:02:03:ff)
+ /// @brief Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
std::string toText() const;
- /// compares two DUIDs
+ /// @brief Compares two DUIDs for equality
bool operator==(const DUID& other) const;
- /// compares two DUIDs
+ /// @brief Compares two DUIDs for inequality
bool operator!=(const DUID& other) const;
protected:
- /// the actual content of the DUID
+ /// The actual content of the DUID
std::vector<uint8_t> duid_;
};
+/// @brief Shared pointer to a DUID
typedef boost::shared_ptr<DUID> DuidPtr;
+
+
/// @brief Holds Client identifier or client IPv4 address
///
/// This class is intended to be a generic IPv4 client identifier. It can hold
/// a client-id
class ClientId : DUID {
- public:
+public:
+ /// @brief Maximum size of a client ID
+ ///
+ /// This is the same as the maximum size of the underlying DUID.
+ ///
+ /// @note RFC 2131 does not specify an upper length of a client ID, the
+ /// value chosen here just being that of the underlying DUID. For
+ /// some backend database, there may be a possible (minor)
+ /// performance enhancement if this were smaller.
+ static const size_t MAX_CLIENT_ID_LEN = DUID::MAX_DUID_LEN;
- /// constructor based on vector<uint8_t>
+ /// @brief Constructor based on vector<uint8_t>
ClientId(const std::vector<uint8_t>& clientid);
- /// constructor based on C-style data
- ClientId(const uint8_t *clientid, size_t len);
+ /// @brief Constructor based on array and array size
+ ClientId(const uint8_t* clientid, size_t len);
- /// @brief returns reference to the client-id data
- ///
+ /// @brief Returns reference to the client-id data
const std::vector<uint8_t> getClientId() const;
- // compares two client-ids
- bool operator == (const ClientId& other) const;
+ /// @brief Compares two client-ids for equality
+ bool operator==(const ClientId& other) const;
- // compares two client-ids
- bool operator != (const ClientId& other) const;
+ /// @brief Compares two client-ids for inequality
+ bool operator!=(const ClientId& other) const;
};
}; // end of isc::dhcp namespace
diff --git a/src/lib/dhcpsrv/database_backends.dox b/src/lib/dhcpsrv/database_backends.dox
index 8eeb5c5..f954bed 100644
--- a/src/lib/dhcpsrv/database_backends.dox
+++ b/src/lib/dhcpsrv/database_backends.dox
@@ -8,6 +8,17 @@
the abstract isc::dhcp::LeaseMgr class. This provides methods to
create, retrieve, modify and delete leases in the database.
+ There are currently two available Lease Managers, MySQL and Memfile:
+
+ - The MySQL lease manager uses the freely available MySQL as its backend
+ database. This is not included in BIND 10 DHCP by default:
+ the --with-dhcp-mysql switch must be supplied to "configure" for support
+ to be compiled into the software.
+ - Memfile is an in-memory lease database, with (currently) nothing being
+ written to persistent storage. The long-term plans for the backend do
+ include the ability to store this on disk, but it is currently a
+ low-priority item.
+
@section dhcpdb-instantiation Instantiation of Lease Managers
A lease manager is instantiated through the LeaseMgrFactory class. This
@@ -32,6 +43,8 @@
- <b>type</b> - specifies the type of database backend. The following values
for the type keyword are supported:
+ - <B>memfile</b> - In-memory database. Nothing is written to any
+ external storage, so this should not be used in production.
- <b>mysql</b> - Use MySQL as the database
The following sections list the database-specific keywords:
diff --git a/src/lib/dhcpsrv/dhcpdb_create.mysql b/src/lib/dhcpsrv/dhcpdb_create.mysql
index 7a292ec..695091d 100644
--- a/src/lib/dhcpsrv/dhcpdb_create.mysql
+++ b/src/lib/dhcpsrv/dhcpdb_create.mysql
@@ -34,7 +34,7 @@ CREATE TABLE lease4 (
address INT UNSIGNED PRIMARY KEY NOT NULL, # IPv4 address
hwaddr VARBINARY(20), # Hardware address
client_id VARBINARY(128), # Client ID
- lease_time INT UNSIGNED, # Length of the lease (seconds)
+ valid_lifetime INT UNSIGNED, # Length of the lease (seconds)
expire TIMESTAMP, # Expiration time of the lease
subnet_id INT UNSIGNED # Subnet identification
) ENGINE = INNODB;
@@ -43,7 +43,7 @@ CREATE TABLE lease4 (
# N.B. The use of a VARCHAR for the address is temporary for development:
# it will eventually be replaced by BINARY(16).
CREATE TABLE lease6 (
- address VARCHAR(40) PRIMARY KEY NOT NULL, # IPv6 address
+ address VARCHAR(39) PRIMARY KEY NOT NULL, # IPv6 address
duid VARBINARY(128), # DUID
valid_lifetime INT UNSIGNED, # Length of the lease (seconds)
expire TIMESTAMP, # Expiration time of the lease
@@ -72,12 +72,17 @@ COMMIT;
# This table is only modified during schema upgrades. For historical reasons
# (related to the names of the columns in the BIND 10 DNS database file), the
# first column is called "version" and not "major".
+#
+# NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h,
+# which defines the schema for the unit tests. If you are updating
+# the version number, the schema has changed: please ensure that
+# schema_copy.h has been updated as well.
CREATE TABLE schema_version (
version INT PRIMARY KEY NOT NULL, # Major version number
minor INT # Minor version number
);
START TRANSACTION;
-INSERT INTO schema_version VALUES (0, 1);
+INSERT INTO schema_version VALUES (1, 0);
COMMIT;
# Notes:
diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc
index 5f4dc2f..f7b6373 100644
--- a/src/lib/dhcpsrv/lease_mgr.cc
+++ b/src/lib/dhcpsrv/lease_mgr.cc
@@ -29,15 +29,16 @@
using namespace std;
-using namespace isc::dhcp;
+namespace isc {
+namespace dhcp {
-Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
- uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
- uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
- :type_(type), addr_(addr), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
- preferred_lft_(preferred), valid_lft_(valid), t1_(t1), t2_(t2),
- subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false),
- fqdn_rev_(false) {
+Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr,
+ DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
+ uint32_t t1, uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
+ : addr_(addr), type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
+ preferred_lft_(preferred), valid_lft_(valid), t1_(t1), t2_(t2),
+ subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false),
+ fqdn_rev_(false) {
if (!duid) {
isc_throw(InvalidOperation, "DUID must be specified for a lease");
}
@@ -83,16 +84,46 @@ Lease6::toText() {
}
bool
+Lease4::operator==(const Lease4& other) const {
+ return (
+ addr_ == other.addr_ &&
+ ext_ == other.ext_ &&
+ hwaddr_ == other.hwaddr_ &&
+ *client_id_ == *other.client_id_ &&
+ t1_ == other.t1_ &&
+ t2_ == other.t2_ &&
+ valid_lft_ == other.valid_lft_ &&
+ cltt_ == other.cltt_ &&
+ subnet_id_ == other.subnet_id_ &&
+ fixed_ == other.fixed_ &&
+ hostname_ == other.hostname_ &&
+ fqdn_fwd_ == other.fqdn_fwd_ &&
+ fqdn_rev_ == other.fqdn_rev_ &&
+ comments_ == other.comments_
+ );
+}
+
+bool
Lease6::operator==(const Lease6& other) const {
return (
- type_ == other.type_ &&
addr_ == other.addr_ &&
+ type_ == other.type_ &&
prefixlen_ == other.prefixlen_ &&
iaid_ == other.iaid_ &&
*duid_ == *other.duid_ &&
preferred_lft_ == other.preferred_lft_ &&
valid_lft_ == other.valid_lft_ &&
+ t1_ == other.t1_ &&
+ t2_ == other.t2_ &&
cltt_ == other.cltt_ &&
- subnet_id_ == other.subnet_id_
- );
+ subnet_id_ == other.subnet_id_ &&
+ fixed_ == other.fixed_ &&
+ hostname_ == other.hostname_ &&
+ fqdn_fwd_ == other.fqdn_fwd_ &&
+ fqdn_rev_ == other.fqdn_rev_ &&
+ comments_ == other.comments_
+ );
}
+
+} // namespace isc::dhcp
+} // namespace isc
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index 0e1f8d8..a264743 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -87,6 +87,13 @@ public:
isc::Exception(file, line, what) {}
};
+/// @brief Multiple lease records found where one expected
+class MultipleRecords : public Exception {
+public:
+ MultipleRecords(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
/// @brief Attempt to update lease that was not there
class NoSuchLease : public Exception {
public:
@@ -94,6 +101,13 @@ public:
isc::Exception(file, line, what) {}
};
+/// @brief Data is truncated
+class DataTruncated : public Exception {
+public:
+ DataTruncated(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
/// @brief Structure that holds a lease for IPv4 address
///
/// For performance reasons it is a simple structure, not a class. If we chose
@@ -101,191 +115,253 @@ public:
/// would be required. As this is a critical part of the code that will be used
/// extensively, direct access is warranted.
struct Lease4 {
+ /// @brief Maximum size of a hardware address
+ static const size_t HWADDR_MAX = 20;
+
+ /// @brief Constructor
+ ///
+ /// @param addr IPv4 address as unsigned 32-bit integer in network byte
+ /// order.
+ /// @param hwaddr Hardware address buffer
+ /// @param hwaddr_len Length of hardware address buffer
+ /// @param clientid Client identification buffer
+ /// @param clientid_len Length of client identification buffer
+ /// @param valid_lft Lifetime of the lease
+ /// @param cltt Client last transmission time
+ /// @param subnet_id Subnet identification
+ Lease4(uint32_t addr, const uint8_t* hwaddr, size_t hwaddr_len,
+ const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
+ time_t cltt, uint32_t subnet_id)
+ : addr_(addr), ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len),
+ client_id_(new ClientId(clientid, clientid_len)), t1_(0), t2_(0),
+ valid_lft_(valid_lft), cltt_(cltt), subnet_id_(subnet_id),
+ fixed_(false), hostname_(), fqdn_fwd_(false), fqdn_rev_(false),
+ comments_()
+ {}
+
+ /// @brief Default Constructor
+ ///
+ /// Initialize fields that don't have a default constructor.
+ Lease4() : addr_(0) {}
+
+
/// IPv4 address
isc::asiolink::IOAddress addr_;
/// @brief Address extension
///
- /// It is envisaged that in some cases IPv4 address will be accompanied with some
- /// additional data. One example of such use are Address + Port solutions (or
- /// Port-restricted Addresses), where several clients may get the same address, but
- /// different port ranges. This feature is not expected to be widely used.
- /// Under normal circumstances, the value should be 0.
+ /// It is envisaged that in some cases IPv4 address will be accompanied
+ /// with some additional data. One example of such use are Address + Port
+ /// solutions (or Port-restricted Addresses), where several clients may get
+ /// the same address, but different port ranges. This feature is not
+ /// expected to be widely used. Under normal circumstances, the value
+ /// should be 0.
uint32_t ext_;
- /// @brief hardware address
+ /// @brief Hardware address
std::vector<uint8_t> hwaddr_;
- /// @brief client identifier
+ /// @brief Client identifier
+ ///
+ /// @todo Should this be a pointer to a client ID or the ID itself?
+ /// Compare with the DUID in the Lease6 structure.
boost::shared_ptr<ClientId> client_id_;
- /// @brief renewal timer
+ /// @brief Renewal timer
///
- /// Specifies renewal time. Although technically it is a property of IA container,
- /// not the address itself, since our data model does not define separate IA
- /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
- /// for the same IA, each must have consistent T1 and T2 values. Specified in
- /// seconds since cltt.
+ /// Specifies renewal time. Although technically it is a property of the
+ /// IA container and not the address itself, since our data model does not
+ /// define a separate IA entity, we are keeping it in the lease. In the
+ /// case of multiple addresses/prefixes for the same IA, each must have
+ /// consistent T1 and T2 values. This is specified in seconds since cltt.
uint32_t t1_;
- /// @brief rebinding timer
+ /// @brief Rebinding timer
///
- /// Specifies rebinding time. Although technically it is a property of IA container,
- /// not the address itself, since our data model does not define separate IA
- /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
- /// for the same IA, each must have consistent T1 and T2 values. Specified in
- /// seconds since cltt.
+ /// Specifies rebinding time. Although technically it is a property of the
+ /// IA container and not the address itself, since our data model does not
+ /// define a separate IA entity, we are keeping it in the lease. In the
+ /// case of multiple addresses/prefixes for the same IA, each must have
+ /// consistent T1 and T2 values. This is specified in seconds since cltt.
uint32_t t2_;
- /// @brief valid lifetime
+ /// @brief Valid lifetime
///
- /// Expressed as number of seconds since cltt
+ /// Expressed as number of seconds since cltt.
uint32_t valid_lft_;
- /// @brief client last transmission time
+ /// @brief Client last transmission time
///
- /// Specifies a timestamp, when last transmission from a client was received.
+ /// Specifies a timestamp giving the time when the last transmission from a
+ /// client was received.
time_t cltt_;
/// @brief Subnet identifier
///
- /// Specifies subnet-id of the subnet that the lease belongs to
+ /// Specifies the identification of the subnet to which the lease belongs.
SubnetID subnet_id_;
- /// @brief Is this a fixed lease?
+ /// @brief Fixed lease?
///
/// Fixed leases are kept after they are released/expired.
bool fixed_;
- /// @brief client hostname
+ /// @brief Client hostname
///
/// This field may be empty
std::string hostname_;
- /// @brief did we update AAAA record for this lease?
+ /// @brief Forward zone updated?
+ ///
+ /// Set true if the DNS AAAA record for this lease has been updated.
bool fqdn_fwd_;
- /// @brief did we update PTR record for this lease?
+ /// @brief Reverse zone updated?
+ ///
+ /// Set true if the DNS PTR record for this lease has been updated.
bool fqdn_rev_;
- /// @brief Lease comments.
+ /// @brief Lease comments
///
/// Currently not used. It may be used for keeping comments made by the
/// system administrator.
std::string comments_;
- /// @todo: Add DHCPv4 failover related fields here
+ /// @brief Compare two leases for equality
+ ///
+ /// @param other lease6 object with which to compare
+ bool operator==(const Lease4& other) const;
- /// @brief Constructor
+ /// @brief Compare two leases for inequality
///
- /// Initialize fields that don't have a default constructor.
- /// @todo Remove this
- Lease4() : addr_(0) {}
+ /// @param other lease6 object with which to compare
+ bool operator!=(const Lease4& other) const {
+ return (!operator==(other));
+ }
+
+ /// @todo: Add DHCPv4 failover related fields here
};
/// @brief Pointer to a Lease4 structure.
typedef boost::shared_ptr<Lease4> Lease4Ptr;
/// @brief A collection of IPv4 leases.
-typedef std::vector< boost::shared_ptr<Lease4Ptr> > Lease4Collection;
+typedef std::vector<Lease4Ptr> Lease4Collection;
+
+
/// @brief Structure that holds a lease for IPv6 address and/or prefix
///
-/// For performance reasons it is a simple structure, not a class. Had we chose to
-/// make it a class, all fields would have to be made private and getters/setters
+/// For performance reasons it is a simple structure, not a class. If we chose
+/// make it a class, all fields would have to made private and getters/setters
/// would be required. As this is a critical part of the code that will be used
-/// extensively, direct access rather than through getters/setters is warranted.
+/// extensively, direct access is warranted.
struct Lease6 {
+
+ /// @brief Type of lease contents
typedef enum {
LEASE_IA_NA, /// the lease contains non-temporary IPv6 address
LEASE_IA_TA, /// the lease contains temporary IPv6 address
LEASE_IA_PD /// the lease contains IPv6 prefix (for prefix delegation)
} LeaseType;
+ /// @brief Constructor
Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
uint32_t t2, SubnetID subnet_id, uint8_t prefixlen_ = 0);
- /// @brief specifies lease type (normal addr, temporary addr, prefix)
- LeaseType type_;
-
- /// IPv6 address
+ /// @brief IPv6 address
+ ///
+ /// IPv6 address or, in the case of a prefix delegation, the prefix.
isc::asiolink::IOAddress addr_;
- /// IPv6 prefix length (used only for PD)
+ /// @brief Lease type
+ ///
+ /// One of normal address, temporary address, or prefix.
+ LeaseType type_;
+
+ /// @brief IPv6 prefix length
+ ///
+ /// This is used only for prefix delegations and is ignored otherwise.
uint8_t prefixlen_;
- /// @brief IAID
+ /// @brief Identity Association Identifier (IAID)
///
- /// Identity Association IDentifier. DHCPv6 stores all addresses and prefixes
- /// in IA containers (IA_NA, IA_TA, IA_PD). Most containers may appear more
- /// than once in a message. To differentiate between them, IAID field is present
+ /// DHCPv6 stores all addresses and prefixes in IA containers (IA_NA,
+ /// IA_TA, IA_PD). All containers may appear more than once in a message.
+ /// To differentiate between them, the IAID field is present
uint32_t iaid_;
- /// @brief client identifier
- boost::shared_ptr<DUID> duid_;
+ /// @brief Client identifier
+ DuidPtr duid_;
/// @brief preferred lifetime
///
- /// This parameter specifies preferred lifetime since the lease was assigned/renewed
- /// (cltt), expressed in seconds.
+ /// This parameter specifies the preferred lifetime since the lease was
+ /// assigned or renewed (cltt), expressed in seconds.
uint32_t preferred_lft_;
/// @brief valid lifetime
///
- /// This parameter specified valid lifetime since the lease was assigned/renewed
- /// (cltt), expressed in seconds.
+ /// This parameter specifies the valid lifetime since the lease waa
+ /// assigned/renewed (cltt), expressed in seconds.
uint32_t valid_lft_;
/// @brief T1 timer
///
- /// Specifies renewal time. Although technically it is a property of IA container,
- /// not the address itself, since our data model does not define separate IA
- /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
- /// for the same IA, each must have consistent T1 and T2 values. Specified in
- /// seconds since cltt.
- /// This value will also be useful for failover to calculate the next expected
- /// client transmission time.
+ /// Specifies renewal time. Although technically it is a property of the
+ /// IA container and not the address itself, since our data model does not
+ /// define a separate IA entity, we are keeping it in the lease. In the
+ /// case of multiple addresses/prefixes for the same IA, each must have
+ /// consistent T1 and T2 values. This is specified in seconds since cltt.
+ /// The value will also be useful for failover to calculate the next
+ /// expected client transmission time.
uint32_t t1_;
/// @brief T2 timer
///
- /// Specifies rebinding time. Although technically it is a property of IA container,
- /// not the address itself, since our data model does not define separate IA
- /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
- /// for the same IA, each must have consistent T1 and T2 values. Specified in
- /// seconds since cltt.
+ /// Specifies rebinding time. Although technically it is a property of the
+ /// IA container and not the address itself, since our data model does not
+ /// define a separate IA entity, we are keeping it in the lease. In the
+ /// case of multiple addresses/prefixes for the same IA, each must have
+ /// consistent T1 and T2 values. This is specified in seconds since cltt.
uint32_t t2_;
- /// @brief client last transmission time
+ /// @brief Client last transmission time
///
- /// Specifies a timestamp, when last transmission from a client was received.
+ /// Specifies a timestamp giving the time when the last transmission from a
+ /// client was received.
time_t cltt_;
/// @brief Subnet identifier
///
- /// Specifies subnet-id of the subnet that the lease belongs to
+ /// Specifies the identification of the subnet to which the lease belongs.
SubnetID subnet_id_;
- /// @brief Is this a fixed lease?
+ /// @brief Fixed lease?
///
/// Fixed leases are kept after they are released/expired.
bool fixed_;
- /// @brief client hostname
+ /// @brief Client hostname
///
/// This field may be empty
std::string hostname_;
- /// @brief did we update AAAA record for this lease?
+ /// @brief Forward zone updated?
+ ///
+ /// Set true if the DNS AAAA record for this lease has been updated.
bool fqdn_fwd_;
- /// @brief did we update PTR record for this lease?
+ /// @brief Reverse zone updated?
+ ///
+ /// Set true if the DNS PTR record for this lease has been updated.
bool fqdn_rev_;
/// @brief Lease comments
///
- /// This field is currently not used.
+ /// Currently not used. It may be used for keeping comments made by the
+ /// system administrator.
std::string comments_;
/// @todo: Add DHCPv6 failover related fields here
@@ -311,13 +387,12 @@ struct Lease6 {
bool operator!=(const Lease6& other) const {
return (!operator==(other));
}
-
};
/// @brief Pointer to a Lease6 structure.
typedef boost::shared_ptr<Lease6> Lease6Ptr;
-/// @brief Const pointer to a Lease6 structure.
+/// @brief Pointer to a const Lease6 structure.
typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
/// @brief A collection of IPv6 leases.
@@ -335,7 +410,7 @@ typedef std::vector<Lease6Ptr> Lease6Collection;
/// see the documentation of those classes for details.
class LeaseMgr {
public:
- /// Client Hardware address
+ /// Client hardware address
typedef std::vector<uint8_t> HWAddr;
/// Database configuration parameter map
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.h b/src/lib/dhcpsrv/memfile_lease_mgr.h
index d9b40e5..3f54edc 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.h
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.h
@@ -199,12 +199,11 @@ public:
/// @brief Returns backend name.
///
- /// As there is no variation, in this case we return the type of the
- /// backend.
+ /// For now, memfile can only store data in memory.
///
/// @return Name of the backend.
virtual std::string getName() const {
- return ("memfile");
+ return ("memory");
}
/// @brief Returns description of the backend.
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc
index a3387ac..428a137 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.cc
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc
@@ -15,10 +15,13 @@
#include <config.h>
#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
#include <dhcpsrv/mysql_lease_mgr.h>
+#include <boost/static_assert.hpp>
#include <mysql/mysqld_error.h>
+#include <algorithm>
#include <iostream>
#include <iomanip>
#include <string>
@@ -28,35 +31,128 @@ using namespace isc;
using namespace isc::dhcp;
using namespace std;
+/// @file
+///
+/// This file holds the implementation of the Lease Manager using MySQL. The
+/// implementation uses MySQL's C API, as it comes as standard with the MySQL
+/// client libraries.
+///
+/// In general, each of the database access methods corresponds to one SQL
+/// statement. To avoid the overhead of parsing a statement every time it is
+/// used, when the database is opened "prepared statements" are created -
+/// essentially doing the SQL parsing up front. Every time a method is used
+/// to access data, the corresponding prepared statement is referenced. Each
+/// prepared statement contains a set of placeholders for data, each
+/// placeholder being for:
+///
+/// - data being added to the database (as in adding or updating a lease)
+/// - data being retrieved from the database (as in getting lease information)
+/// - selection criteria used to determine which records to update/retrieve.
+///
+/// All such data is associated with the prepared statment using an array of
+/// MYSQL_BIND structures. Each element in the array corresponds to one
+/// parameter in the prepared statement - the first element in the array is
+/// associated with the first parameter, the second element with the second
+/// parameter etc.
+///
+/// Within this file, the setting up of the MYSQL_BIND arrays for data being
+/// passed to and retrieved from the database is handled in the
+/// isc::dhcp::MySqlLease4Exchange and isc::dhcp::MySqlLease6Exchange classes.
+/// The classes also hold intermediate variables required for exchanging some
+/// of the data.
+///
+/// With these exchange objects in place, many of the methods follow similar
+/// logic:
+/// - Set up the MYSQL_BIND array for data being transferred to/from the
+/// database. For data being transferred to the database, some of the
+/// data is extracted from the lease to intermediate variables, whilst
+/// in other cases the MYSQL_BIND arrays point to the data in the lease.
+/// - Set up the MYSQL_BIND array for the data selection parameters.
+/// - Bind these arrays to the prepared statement.
+/// - Execute the statement.
+/// - If there is output, copy the data from the bound variables to the output
+/// lease object.
+
namespace {
///@{
-/// @brief Maximum Size of Database Fields
+
+/// @brief Maximum size of database fields
///
/// The following constants define buffer sizes for variable length database
/// fields. The values should be greater than or equal to the length set in
/// the schema definition.
///
-/// The exception is the length of any VARCHAR fields: these should be set
-/// greater than or equal to the length of the field plus 2: this allows for
-/// the insertion of a trailing null regardless of whether the data returned
-/// contains a trailing null (the documentation is not clear on this point).
+/// The exception is the length of any VARCHAR fields: buffers for these should
+/// be set greater than or equal to the length of the field plus 1: this allows
+/// for the insertion of a trailing null whatever data is returned.
+
+/// @brief Maximum size of an IPv6 address represented as a text string.
+///
+/// This is 32 hexadecimal characters written in 8 groups of four, plus seven
+/// colon separators.
+const size_t ADDRESS6_TEXT_MAX_LEN = 39;
+
+/// @brief Maximum size of a hardware address.
+const size_t HWADDR_MAX_LEN = 20;
+
+/// @brief Number of columns in Lease4 table
+const size_t LEASE4_COLUMNS = 6;
+
+/// @brief Number of columns in Lease6 table
+const size_t LEASE6_COLUMNS = 9;
+
+/// @brief MySQL True/False constants
+///
+/// Declare typed values so as to avoid problems of data conversion. These
+/// are local to the file but are given the prefix MLM (MySql Lease Manager) to
+/// avoid any likely conflicts with variables in header files named TRUE or
+/// FALSE.
+
+const my_bool MLM_FALSE = 0; ///< False value
+const my_bool MLM_TRUE = 1; ///< True value
-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
///@}
/// @brief MySQL Selection Statements
///
-/// Each statement is associated with the index, used by the various methods
-/// to access the prepared statement.
+/// Each statement is associated with an index, which is used to reference the
+/// associated prepared statement.
+
struct TaggedStatement {
MySqlLeaseMgr::StatementIndex index;
const char* text;
};
TaggedStatement tagged_statements[] = {
+ {MySqlLeaseMgr::DELETE_LEASE4,
+ "DELETE FROM lease4 WHERE address = ?"},
{MySqlLeaseMgr::DELETE_LEASE6,
"DELETE FROM lease6 WHERE address = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_ADDR,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id "
+ "FROM lease4 "
+ "WHERE address = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id "
+ "FROM lease4 "
+ "WHERE client_id = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id "
+ "FROM lease4 "
+ "WHERE client_id = ? AND subnet_id = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_HWADDR,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id "
+ "FROM lease4 "
+ "WHERE hwaddr = ?"},
+ {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
+ "SELECT address, hwaddr, client_id, "
+ "valid_lifetime, expire, subnet_id "
+ "FROM lease4 "
+ "WHERE hwaddr = ? AND subnet_id = ?"},
{MySqlLeaseMgr::GET_LEASE6_ADDR,
"SELECT address, duid, valid_lifetime, "
"expire, subnet_id, pref_lifetime, "
@@ -77,11 +173,20 @@ 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, "
"lease_type, iaid, prefix_len) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+ {MySqlLeaseMgr::UPDATE_LEASE4,
+ "UPDATE lease4 SET address = ?, hwaddr = ?, "
+ "client_id = ?, valid_lifetime = ?, expire = ?, "
+ "subnet_id = ? "
+ "WHERE address = ?"},
{MySqlLeaseMgr::UPDATE_LEASE6,
"UPDATE lease6 SET address = ?, duid = ?, "
"valid_lifetime = ?, expire = ?, subnet_id = ?, "
@@ -97,6 +202,300 @@ TaggedStatement tagged_statements[] = {
namespace isc {
namespace dhcp {
+/// @brief Common MySQL and Lease Data Methods
+///
+/// The MySqlLease4Exchange and MySqlLease6Exchange classes provide the
+/// functionaility to set up binding information between variables in the
+/// program and data extracted from the database. This class is the common
+/// base to both of them, containing some common methods.
+
+class MySqlLeaseExchange {
+public:
+ /// @brief Set error indicators
+ ///
+ /// Sets the error indicator for each of the MYSQL_BIND elements. It points
+ /// the "error" field within an element of the input array to the
+ /// corresponding element of the passed error array.
+ ///
+ /// @param bind Array of BIND elements
+ /// @param error Array of error elements. If there is an error in getting
+ /// data associated with one of the "bind" elements, the
+ /// corresponding element in the error array is set to MLM_TRUE.
+ /// @param count Size of each of the arrays.
+ void setErrorIndicators(MYSQL_BIND* bind, my_bool* error, size_t count) {
+ for (size_t i = 0; i < count; ++i) {
+ error[i] = MLM_FALSE;
+ bind[i].error = reinterpret_cast<char*>(&error[i]);
+ }
+ }
+
+ /// @brief Return columns in error
+ ///
+ /// If an error is returned from a fetch (in particular, a truncated
+ /// status), this method can be called to get the names of the fields in
+ /// error. It returns a string comprising the names of the fields
+ /// separated by commas. In the case of there being no error indicators
+ /// set, it returns the string "(None)".
+ ///
+ /// @param error Array of error elements. An element is set to MLM_TRUE
+ /// if the corresponding column in the database is the source of
+ /// the error.
+ /// @param names Array of column names, the same size as the error array.
+ /// @param count Size of each of the arrays.
+ std::string getColumnsInError(my_bool* error, std::string* names,
+ size_t count) {
+ std::string result = "";
+
+ // Accumulate list of column names
+ for (size_t i = 0; i < count; ++i) {
+ if (error[i] == MLM_TRUE) {
+ if (!result.empty()) {
+ result += ", ";
+ }
+ result += names[i];
+ }
+ }
+
+ if (result.empty()) {
+ result = "(None)";
+ }
+
+ return (result);
+ }
+};
+
+
+/// @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. This class handles the creation of that array.
+///
+/// Owing to the MySQL API, the process requires some intermediate variables
+/// to hold things like data length etc. This object holds those variables.
+///
+/// @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 MySqlLeaseExchange {
+public:
+ /// @brief Set number of columns in this lease structure
+ static const size_t LEASE_COLUMNS = LEASE4_COLUMNS;
+
+ /// @brief Constructor
+ ///
+ /// The initialization of the variables here is only to satisfy cppcheck -
+ /// all variables are initialized/set in the methods before they are used.
+ MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0) {
+ memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
+ memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
+ std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
+
+ // Set the column names (for error messages)
+ columns_[0] = "address";
+ columns_[1] = "hwaddr";
+ columns_[2] = "client_id";
+ columns_[3] = "valid_lifetime";
+ columns_[4] = "expire";
+ columns_[5] = "subnet_id";
+ BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+ }
+
+ /// @brief Create MYSQL_BIND objects for Lease4 Pointer
+ ///
+ /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
+ /// the database.
+ ///
+ /// @param lease Lease object to be added to the database. None of the
+ /// fields in the lease are modified - the lease data is only read.
+ ///
+ /// @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;
+
+ // Initialize prior to constructing the array of MYSQL_BIND structures.
+ memset(bind_, 0, sizeof(bind_));
+
+ // Set up the structures for the various components of the lease4
+ // structure.
+
+ // Address: uint32_t
+ // The address in the Lease structure is an IOAddress object. Convert
+ // this 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 = MLM_TRUE;
+
+ // hwaddr: varbinary(128)
+ // 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(128)
+ 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 = MLM_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 - a large enough valid_lft_ could cause
+ // an overflow on a 32-bit system.
+ 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 = MLM_TRUE;
+
+ // Add the error flags
+ setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+ // .. and check that we have the numbers correct at compile time.
+ BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+
+ // 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_[LEASE_COLUMNS]));
+ }
+
+ /// @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() can be used to copy
+ /// it to a Lease6 object.
+ ///
+ std::vector<MYSQL_BIND> createBindForReceive() {
+
+ // Initialize MYSQL_BIND array.
+ memset(bind_, 0, sizeof(bind_));
+
+ // address: uint32_t
+ bind_[0].buffer_type = MYSQL_TYPE_LONG;
+ bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
+ bind_[0].is_unsigned = MLM_TRUE;
+
+ // 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_;
+
+ // 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_;
+
+ // lease_time: unsigned int
+ bind_[3].buffer_type = MYSQL_TYPE_LONG;
+ bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
+ bind_[3].is_unsigned = MLM_TRUE;
+
+ // expire: timestamp
+ bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
+ bind_[4].buffer = reinterpret_cast<char*>(&expire_);
+ bind_[4].buffer_length = sizeof(expire_);
+
+ // subnet_id: unsigned int
+ bind_[5].buffer_type = MYSQL_TYPE_LONG;
+ bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
+ bind_[5].is_unsigned = MLM_TRUE;
+
+ // Add the error flags
+ setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+ // .. and check that we have the numbers correct at compile time.
+ BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
+
+ // 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_[LEASE_COLUMNS]));
+ }
+
+ /// @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 variables
+ /// into a Lease4 objec.
+ ///
+ /// @return Lease4Ptr Pointer to a Lease6 object holding the relevant
+ /// data.
+ Lease4Ptr getLeaseData() {
+ // Convert times received from the database to times 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_)));
+ }
+
+ /// @brief Return columns in error
+ ///
+ /// If an error is returned from a fetch (in particular, a truncated
+ /// status), this method can be called to get the names of the fields in
+ /// error. It returns a string comprising the names of the fields
+ /// separated by commas. In the case of there being no error indicators
+ /// set, it returns the string "(None)".
+ ///
+ /// @return Comma-separated list of columns in error, or the string
+ /// "(None)".
+ std::string getErrorColumns() {
+ return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
+ }
+
+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_[LEASE_COLUMNS]; ///< Bind array
+ std::string columns_[LEASE_COLUMNS];///< Column names
+ my_bool error_[LEASE_COLUMNS]; ///< Error 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_[ClientId::MAX_CLIENT_ID_LEN];
+ ///< Client ID buffer
+ unsigned long client_id_length_; ///< Client ID address length
+ MYSQL_TIME expire_; ///< Lease expiry time
+ Lease4Ptr lease_; ///< Pointer to lease object
+ uint32_t subnet_id_; ///< Subnet identification
+ uint32_t valid_lifetime_; ///< Lease time
+};
+
/// @brief Exchange MySQL and Lease6 Data
@@ -104,33 +503,45 @@ namespace dhcp {
/// 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 Lease6 structure.
-/// This class handles the creation of that array.
+/// structure is identical. 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.
+/// to hold things like data length etc. This object holds those variables.
///
/// @note There are no unit tests for this class. It is tested indirectly
/// in all MySqlLeaseMgr::xxx6() calls where it is used.
-class MySqlLease6Exchange {
+class MySqlLease6Exchange : public MySqlLeaseExchange {
+ /// @brief Set number of columns in this lease structure
+ static const size_t LEASE_COLUMNS = LEASE6_COLUMNS;
+
public:
/// @brief Constructor
///
- /// Apart from the initialization of false_ and true_, the initialization
- /// of addr6_length_, duid_length_, addr6_buffer_ and duid_buffer_ are
- /// to satisfy cppcheck: none are really needed, as all variables are
- /// initialized/set in the methods.
- MySqlLease6Exchange() : addr6_length_(0), duid_length_(0),
- false_(0), true_(1) {
+ /// The initialization of the variables here is nonly to satisfy cppcheck -
+ /// all variables are initialized/set in the methods before they are used.
+ MySqlLease6Exchange() : addr6_length_(0), duid_length_(0) {
memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
memset(duid_buffer_, 0, sizeof(duid_buffer_));
+ std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
+
+ // Set the column names (for error messages)
+ columns_[0] = "address";
+ columns_[1] = "duid";
+ columns_[2] = "valid_lifetime";
+ columns_[3] = "expire";
+ columns_[4] = "subnet_id";
+ columns_[5] = "pref_lifetime";
+ columns_[6] = "lease_type";
+ columns_[7] = "iaid";
+ columns_[8] = "prefix_len";
+ BOOST_STATIC_ASSERT(5 < LEASE_COLUMNS);
}
/// @brief Create MYSQL_BIND objects for Lease6 Pointer
///
- /// Fills in the MYSQL_BIND objects for the Lease6 passed to it.
+ /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
+ /// the database.
///
/// @param lease Lease object to be added to the database.
///
@@ -143,7 +554,7 @@ public:
// for this lease.
memset(bind_, 0, sizeof(bind_));
- // address: varchar(40)
+ // address: varchar(39)
addr6_ = lease_->addr_.toText();
addr6_length_ = addr6_.size();
@@ -154,13 +565,13 @@ public:
// is guaranteed to be valid until the next non-const operation on
// addr6_.)
//
- // Note that the const_cast could be avoided by copying the string to
- // a writeable buffer and storing the address of that in the "buffer"
- // element. However, this introduces a copy operation (with additional
- // overhead) purely to get round the strictures introduced by design of
- // the MySQL interface (which uses the area pointed to by "buffer" as
- // input when specifying query parameters and as output when retrieving
- // data). For that reason, "const_cast" has been used.
+ // The const_cast could be avoided by copying the string to a writeable
+ // buffer and storing the address of that in the "buffer" element.
+ // However, this introduces a copy operation (with additional overhead)
+ // purely to get round the structures introduced by design of the
+ // MySQL interface (which uses the area pointed to by "buffer" as input
+ // when specifying query parameters and as output when retrieving data).
+ // For that reason, "const_cast" has been used.
bind_[0].buffer_type = MYSQL_TYPE_STRING;
bind_[0].buffer = const_cast<char*>(addr6_.c_str());
bind_[0].buffer_length = addr6_length_;
@@ -177,8 +588,8 @@ public:
// valid lifetime: unsigned int
bind_[2].buffer_type = MYSQL_TYPE_LONG;
- bind_[2].buffer = reinterpret_cast<char*>(&lease->valid_lft_);
- bind_[2].is_unsigned = true_;
+ bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
+ bind_[2].is_unsigned = MLM_TRUE;
// expire: timestamp
// The lease structure holds the client last transmission time (cltt_)
@@ -187,7 +598,7 @@ public:
//
// expire = cltt_ + valid_lft_
//
- // @TODO Handle overflows
+ // @todo Handle overflows
MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
expire_);
bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
@@ -198,36 +609,42 @@ public:
// Can use lease_->subnet_id_ directly as it is of type uint32_t.
bind_[4].buffer_type = MYSQL_TYPE_LONG;
bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
- bind_[4].is_unsigned = true_;
+ bind_[4].is_unsigned = MLM_TRUE;
// pref_lifetime: unsigned int
// Can use lease_->preferred_lft_ directly as it is of type uint32_t.
bind_[5].buffer_type = MYSQL_TYPE_LONG;
bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
- bind_[5].is_unsigned = true_;
+ bind_[5].is_unsigned = MLM_TRUE;
// lease_type: tinyint
- // Must convert to uint8_t as lease_->type_ is a LeaseType variable
+ // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
lease_type_ = lease_->type_;
bind_[6].buffer_type = MYSQL_TYPE_TINY;
bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
- bind_[6].is_unsigned = true_;
+ bind_[6].is_unsigned = MLM_TRUE;
// iaid: unsigned int
// Can use lease_->iaid_ directly as it is of type uint32_t.
bind_[7].buffer_type = MYSQL_TYPE_LONG;
bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
- bind_[7].is_unsigned = true_;
+ bind_[7].is_unsigned = MLM_TRUE;
// prefix_len: unsigned tinyint
// Can use lease_->prefixlen_ directly as it is uint32_t.
bind_[8].buffer_type = MYSQL_TYPE_TINY;
bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
- bind_[8].is_unsigned = true_;
+ bind_[8].is_unsigned = MLM_TRUE;
+
+ // Add the error flags
+ setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+ // .. and check that we have the numbers correct at compile time.
+ BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
// 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_[9]));
+ return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
}
/// @brief Create BIND array to receive data
@@ -236,24 +653,22 @@ public:
/// After data is successfully received, getLeaseData() is used to copy
/// it to a Lease6 object.
///
- /// @return Vector of MySQL BIND objects.
+ /// @return Vector of MySQL BIND objects passed to the MySQL data retrieval
+ /// functions.
std::vector<MYSQL_BIND> createBindForReceive() {
- // Ensure both the array of MYSQL_BIND structures and the error array
- // are clear.
+ // Initialize MYSQL_BIND array.
memset(bind_, 0, sizeof(bind_));
- memset(error_, 0, sizeof(error_));
- // address: varchar
+ // address: varchar(39)
// A Lease6_ address has a maximum of 39 characters. The array is
- // a few bites longer than this to guarantee that we can always null
- // terminate it.
+ // one byte longer than this to guarantee that we can always null
+ // terminate it whatever is returned.
addr6_length_ = sizeof(addr6_buffer_) - 1;
bind_[0].buffer_type = MYSQL_TYPE_STRING;
bind_[0].buffer = addr6_buffer_;
bind_[0].buffer_length = addr6_length_;
bind_[0].length = &addr6_length_;
- bind_[0].error = &error_[0];
// client_id: varbinary(128)
duid_length_ = sizeof(duid_buffer_);
@@ -261,59 +676,57 @@ public:
bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
bind_[1].buffer_length = duid_length_;
bind_[1].length = &duid_length_;
- bind_[1].error = &error_[1];
// lease_time: unsigned int
bind_[2].buffer_type = MYSQL_TYPE_LONG;
bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
- bind_[2].is_unsigned = true_;
- bind_[2].error = &error_[2];
+ bind_[2].is_unsigned = MLM_TRUE;
// expire: timestamp
bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
bind_[3].buffer = reinterpret_cast<char*>(&expire_);
bind_[3].buffer_length = sizeof(expire_);
- bind_[3].error = &error_[3];
// subnet_id: unsigned int
bind_[4].buffer_type = MYSQL_TYPE_LONG;
bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
- bind_[4].is_unsigned = true_;
- bind_[4].error = &error_[4];
+ bind_[4].is_unsigned = MLM_TRUE;
// pref_lifetime: unsigned int
bind_[5].buffer_type = MYSQL_TYPE_LONG;
bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
- bind_[5].is_unsigned = true_;
- bind_[5].error = &error_[5];
+ bind_[5].is_unsigned = MLM_TRUE;
// lease_type: tinyint
bind_[6].buffer_type = MYSQL_TYPE_TINY;
bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
- bind_[6].is_unsigned = true_;
- bind_[6].error = &error_[6];
+ bind_[6].is_unsigned = MLM_TRUE;
// iaid: unsigned int
bind_[7].buffer_type = MYSQL_TYPE_LONG;
bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
- bind_[7].is_unsigned = true_;
- bind_[7].error = &error_[7];
+ bind_[7].is_unsigned = MLM_TRUE;
// prefix_len: unsigned tinyint
bind_[8].buffer_type = MYSQL_TYPE_TINY;
bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
- bind_[8].is_unsigned = true_;
- bind_[8].error = &error_[8];
+ bind_[8].is_unsigned = MLM_TRUE;
+
+ // Add the error flags
+ setErrorIndicators(bind_, error_, LEASE_COLUMNS);
+
+ // .. and check that we have the numbers correct at compile time.
+ BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
// 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_[9]));
+ return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
}
/// @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
+ /// has been used, this copies data from the internal member variables
/// into a Lease6 object.
///
/// @return Lease6Ptr Pointer to a Lease6 object holding the relevant
@@ -321,74 +734,86 @@ public:
///
/// @throw isc::BadValue Unable to convert Lease Type value in database
Lease6Ptr getLeaseData() {
-
- // Create the object to be returned.
- Lease6Ptr result(new Lease6());
-
- // Put the data in the lease object
-
// The address buffer is declared larger than the buffer size passed
// to the access function so that we can always append a null byte.
+ // Create the IOAddress object corresponding to the received data.
addr6_buffer_[addr6_length_] = '\0';
std::string address = addr6_buffer_;
+ isc::asiolink::IOAddress addr(address);
- // Set the other data, converting time as needed.
- result->addr_ = isc::asiolink::IOAddress(address);
- result->duid_.reset(new DUID(duid_buffer_, duid_length_));
- MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_,
- result->cltt_);
- result->valid_lft_ = valid_lifetime_;
- result->subnet_id_ = subnet_id_;
- result->preferred_lft_ = pref_lifetime_;
-
- // We can't convert from a numeric value to an enum, hence:
+ // Set the lease type in a variable of the appropriate data type, which
+ // has been initialized with an arbitrary (but valid) value.
+ Lease6::LeaseType type = Lease6::LEASE_IA_NA;
switch (lease_type_) {
case Lease6::LEASE_IA_NA:
- result->type_ = Lease6::LEASE_IA_NA;
+ type = Lease6::LEASE_IA_NA;
break;
case Lease6::LEASE_IA_TA:
- result->type_ = Lease6::LEASE_IA_TA;
+ type = Lease6::LEASE_IA_TA;
break;
case Lease6::LEASE_IA_PD:
- result->type_ = Lease6::LEASE_IA_PD;
+ type = Lease6::LEASE_IA_PD;
break;
default:
isc_throw(BadValue, "invalid lease type returned (" <<
lease_type_ << ") for lease with address " <<
- result->addr_.toText() << ". Only 0, 1, or 2 "
- "are allowed.");
+ address << ". Only 0, 1, or 2 are allowed.");
}
- result->iaid_ = iaid_;
- result->prefixlen_ = prefixlen_;
+
+ // Set up DUID,
+ DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
+
+ // Create the lease and set the cltt (after converting from the
+ // expire time retrieved from the database).
+ Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
+ pref_lifetime_, valid_lifetime_, 0, 0,
+ subnet_id_, prefixlen_));
+ time_t cltt = 0;
+ MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+ result->cltt_ = cltt;
return (result);
}
+ /// @brief Return columns in error
+ ///
+ /// If an error is returned from a fetch (in particular, a truncated
+ /// status), this method can be called to get the names of the fields in
+ /// error. It returns a string comprising the names of the fields
+ /// separated by commas. In the case of there being no error indicators
+ /// set, it returns the string "(None)".
+ ///
+ /// @return Comma-separated list of columns in error, or the string
+ /// "(None)".
+ std::string getErrorColumns() {
+ return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
+ }
+
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
+ char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1]; ///< 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_[LEASE_COLUMNS]; ///< Bind array
+ std::string columns_[LEASE_COLUMNS];///< Column names
std::vector<uint8_t> duid_; ///< Client identification
- uint8_t duid_buffer_[DUID_MAX_LEN]; ///< Buffer form of DUID
+ uint8_t duid_buffer_[DUID::MAX_DUID_LEN]; ///< Buffer form of DUID
unsigned long duid_length_; ///< Length of the DUID
- my_bool error_[9]; ///< For error reporting
+ my_bool error_[LEASE_COLUMNS]; ///< Error indicators
MYSQL_TIME expire_; ///< Lease expiry time
- const my_bool false_; ///< "false" for MySql
uint32_t iaid_; ///< Identity association ID
Lease6Ptr lease_; ///< Pointer to lease object
- uint32_t valid_lifetime_; ///< Lease time
uint8_t lease_type_; ///< Lease type
uint8_t prefixlen_; ///< Prefix length
uint32_t pref_lifetime_; ///< Preferred lifetime
uint32_t subnet_id_; ///< Subnet identification
- const my_bool true_; ///< "true_" for MySql
+ uint32_t valid_lifetime_; ///< Lease time
};
@@ -396,15 +821,16 @@ private:
///
/// When a MySQL statement is exected, to fetch the results the function
/// mysql_stmt_fetch() must be called. As well as getting data, this
-/// allocated internal state. Subsequent calls to mysql_stmt_fetch
-/// can be made, but when all the data is retrieved, mysql_stmt_free_result
-/// must be called to free up the resources allocated.
+/// allocates internal state. Subsequent calls to mysql_stmt_fetch can be
+/// made, but when all the data is retrieved, mysql_stmt_free_result must be
+/// called to free up the resources allocated.
///
/// Created prior to the first fetch, this class's destructor calls
/// mysql_stmt_free_result, so eliminating the need for an explicit release
-/// in the method using mysql_stmt_free_result. In this way, it guarantees
+/// in the method calling mysql_stmt_free_result. In this way, it guarantees
/// that the resources are released even if the MySqlLeaseMgr method concerned
/// exits via an exception.
+
class MySqlFreeResult {
public:
@@ -433,7 +859,7 @@ private:
};
-// MySqlLeaseMgr Methods
+// MySqlLeaseMgr Constructor and Destructor
MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
: LeaseMgr(parameters), mysql_(NULL) {
@@ -444,11 +870,14 @@ MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
isc_throw(DbOpenError, "unable to initialize MySQL");
}
- // Open the database
+ // Open the database.
openDatabase();
- // Enable autocommit. For maximum speed, the global parameter
- // innodb_flush_log_at_trx_commit should be set to 2.
+ // Enable autocommit. To avoid a flush to disk on every commit, the global
+ // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
+ // cause the changes to be written to the log, but flushed to disk in the
+ // background every second. Setting the parameter to that value will speed
+ // up the system, but at the risk of losing data if the system crashes.
my_bool result = mysql_autocommit(mysql_, 1);
if (result != 0) {
isc_throw(DbOperationError, mysql_error(mysql_));
@@ -457,16 +886,17 @@ 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());
}
MySqlLeaseMgr::~MySqlLeaseMgr() {
// Free up the prepared statements, ignoring errors. (What would we do
- // about them - we're destroying this object and are not really concerned
- // with errors on a database connection that it about to go away.)
+ // about them? We're destroying this object and are not really concerned
+ // with errors on a database connection that is about to go away.)
for (int i = 0; i < statements_.size(); ++i) {
if (statements_[i] != NULL) {
(void) mysql_stmt_close(statements_[i]);
@@ -495,7 +925,7 @@ void
MySqlLeaseMgr::convertToDatabaseTime(time_t cltt, uint32_t valid_lifetime,
MYSQL_TIME& expire) {
- // Calculate expiry time and convert to various date/time fields.
+ // Calculate expiry time.
// @TODO: handle overflows
time_t expire_time = cltt + valid_lifetime;
@@ -510,8 +940,8 @@ MySqlLeaseMgr::convertToDatabaseTime(time_t cltt, uint32_t valid_lifetime,
expire.hour = expire_tm.tm_hour;
expire.minute = expire_tm.tm_min;
expire.second = expire_tm.tm_sec;
- expire.second_part = 0; // No fractional seconds
- expire.neg = static_cast<my_bool>(0); // Not negative
+ expire.second_part = 0; // No fractional seconds
+ expire.neg = my_bool(0); // Not negative
}
void
@@ -535,6 +965,9 @@ MySqlLeaseMgr::convertFromDatabaseTime(const MYSQL_TIME& expire,
}
+
+// Open the database using the parameters passed to the constructor.
+
void
MySqlLeaseMgr::openDatabase() {
@@ -555,7 +988,6 @@ MySqlLeaseMgr::openDatabase() {
user = suser.c_str();
} catch (...) {
// No user. Fine, we'll use NULL
- ;
}
const char* password = NULL;
@@ -565,7 +997,6 @@ MySqlLeaseMgr::openDatabase() {
password = spassword.c_str();
} catch (...) {
// No password. Fine, we'll use NULL
- ;
}
const char* name = NULL;
@@ -579,8 +1010,11 @@ MySqlLeaseMgr::openDatabase() {
}
// Set options for the connection:
- // - automatic reconnection
- my_bool auto_reconnect = 1;
+ //
+ // Automatic reconnection: after a period of inactivity, the client will
+ // disconnect from the database. This option causes it to automatically
+ // reconnect when another operation is about to be done.
+ my_bool auto_reconnect = MLM_TRUE;
int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
if (result != 0) {
isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
@@ -596,7 +1030,7 @@ MySqlLeaseMgr::openDatabase() {
// changed and so the "affected rows" (retrievable from MySQL) is zero.
// This makes it hard to distinguish whether the UPDATE changed no rows
// because no row matching the WHERE clause was found, or because a
- // row was found by no data was altered.
+ // row was found but no data was altered.
MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
0, NULL, CLIENT_FOUND_ROWS);
if (status != mysql_) {
@@ -604,6 +1038,12 @@ MySqlLeaseMgr::openDatabase() {
}
}
+// Prepared statement setup. The textual form of an SQL statement is stored
+// in a vector of strings (text_statements_) and is used in the output of
+// error messages. The SQL statement is also compiled into a "prepared
+// statement" (stored in statements_), which avoids the overhead of compilation
+// during use. As prepared statements have resources allocated to them, the
+// class destructor explicitly destroys them.
void
MySqlLeaseMgr::prepareStatement(StatementIndex index, const char* text) {
@@ -617,11 +1057,10 @@ MySqlLeaseMgr::prepareStatement(StatementIndex index, const char* text) {
// All OK, so prepare the statement
text_statements_[index] = std::string(text);
-
statements_[index] = mysql_stmt_init(mysql_);
if (statements_[index] == NULL) {
isc_throw(DbOperationError, "unable to allocate MySQL prepared "
- "statement structure" << mysql_error(mysql_));
+ "statement structure, reason: " << mysql_error(mysql_));
}
int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
@@ -648,21 +1087,13 @@ MySqlLeaseMgr::prepareStatements() {
}
}
+// Add leases to the database. The two public methods accept a lease object
+// (either V4 of V6), bind the contents to the appropriate prepared
+// statement, then call common code to execute the statement.
bool
-MySqlLeaseMgr::addLease(const Lease4Ptr& /* lease */) {
- isc_throw(NotImplemented, "MySqlLeaseMgr::addLease(const Lease4Ptr&) "
- "not implemented yet");
- return (false);
-}
-
-
-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::addLeaseCommon(StatementIndex stindex,
+ std::vector<MYSQL_BIND>& bind) {
// Bind the parameters to the statement
int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]);
@@ -685,87 +1116,294 @@ MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
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 (addLeaseCommon(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 (addLeaseCommon(INSERT_LEASE6, bind));
+}
+
+// Extraction of leases from the database.
+//
+// All getLease() methods ultimately call getLeaseCollection(). This
+// binds the input parameters passed to it with the appropriate prepared
+// statement and executes the statement. It then gets the results from the
+// database. getlease() methods that expect a single result back call it
+// with the "single" parameter set true: this causes an exception to be
+// generated if multiple records can be retrieved from the result set. (Such
+// an occurrence either indicates corruption in the database, or that an
+// assumption that a query can only return a single record is incorrect.)
+// Methods that require a collection of records have "single" set to the
+// default value of false. The logic is the same for both Lease4 and Lease6
+// objects, so the code is templated.
+//
+// Methods that require a collection of objects access this method through
+// two interface methods (also called getLeaseCollection()). These are
+// short enough as to be defined in the header file: all they do is to supply
+// the appropriate MySqlLeaseXExchange object depending on the type of the
+// LeaseCollection objects passed to them.
+//
+// Methods that require a single object to be returned access the method
+// through two interface methods (called getLease()). As well as supplying
+// the appropriate exchange object, they convert between lease collection
+// holding zero or one leases into an appropriate Lease object.
+
+template <typename Exchange, typename LeaseCollection>
+void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
+ MYSQL_BIND* bind,
+ Exchange& exchange,
+ LeaseCollection& result,
+ bool single) const {
+
+ // Bind the selection parameters to the statement
+ int status = mysql_stmt_bind_param(statements_[stindex], bind);
+ checkError(status, stindex, "unable to bind WHERE clause parameter");
+
+ // Set up the MYSQL_BIND array for the data being returned and bind it to
+ // the statement.
+ std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
+ status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]);
+ checkError(status, stindex, "unable to bind SELECT clause parameters");
+
+ // Execute the statement
+ status = mysql_stmt_execute(statements_[stindex]);
+ checkError(status, stindex, "unable to execute");
+
+ // Ensure that all the lease information is retrieved in one go to avoid
+ // overhead of going back and forth between client and server.
+ status = mysql_stmt_store_result(statements_[stindex]);
+ checkError(status, stindex, "unable to set up for storing all results");
+
+ // Set up the fetch "release" object to release resources associated
+ // with the call to mysql_stmt_fetch when this method exits, then
+ // retrieve the data.
+ MySqlFreeResult fetch_release(statements_[stindex]);
+ int count = 0;
+ while ((status = mysql_stmt_fetch(statements_[stindex])) == 0) {
+ try {
+ result.push_back(exchange->getLeaseData());
+
+ } catch (const isc::BadValue& ex) {
+ // Rethrow the exception with a bit more data.
+ isc_throw(BadValue, ex.what() << ". Statement is <" <<
+ text_statements_[stindex] << ">");
+ }
+
+ if (single && (++count > 1)) {
+ isc_throw(MultipleRecords, "multiple records were found in the "
+ "database where only one was expected for query "
+ << text_statements_[stindex]);
+ }
+ }
+
+ // How did the fetch end?
+ if (status == 1) {
+ // Error - unable to fetch results
+ checkError(status, stindex, "unable to fetch results");
+ } else if (status == MYSQL_DATA_TRUNCATED) {
+ // Data truncated - throw an exception indicating what was at fault
+ isc_throw(DataTruncated, text_statements_[stindex]
+ << " returned truncated data: columns affected are "
+ << exchange->getErrorColumns());
+ }
+}
+
+
+void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
+ Lease4Ptr& result) const {
+ // Create appropriate collection object and get all leases matching
+ // the selection criteria. The "single" paraeter is true to indicate
+ // that the called method should throw an exception if multiple
+ // matching records are found: this particular method is called when only
+ // one or zero matches is expected.
+ Lease4Collection collection;
+ getLeaseCollection(stindex, bind, exchange4_, collection, true);
+
+ // Return single record if present, else clear the lease.
+ if (collection.empty()) {
+ result.reset();
+ } else {
+ result = *collection.begin();
+ }
+}
+
+
+void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
+ Lease6Ptr& result) const {
+ // Create appropriate collection object and get all leases matching
+ // the selection criteria. The "single" paraeter is true to indicate
+ // that the called method should throw an exception if multiple
+ // matching records are found: this particular method is called when only
+ // one or zero matches is expected.
+ Lease6Collection collection;
+ getLeaseCollection(stindex, bind, exchange6_, collection, true);
+
+ // Return single record if present, else clear the lease.
+ if (collection.empty()) {
+ result.reset();
+ } else {
+ result = *collection.begin();
+ }
+}
+
+
+// Basic lease access methods. Obtain leases from the database using various
+// criteria.
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 = MLM_TRUE;
+
+ // Get the data
+ Lease4Ptr result;
+ getLease(GET_LEASE4_ADDR, inbind, result);
+
+ return (result);
}
Lease4Ptr
-MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */) const {
- isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const IOAddress&) "
- "not implemented yet");
- return (Lease4Ptr());
+MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr,
+ SubnetID subnet_id) const {
+
+ // As the address is the unique primary key of the lease4 table, there can
+ // only be one lease with a given address. Therefore we will get that
+ // lease and do the filtering on subnet ID here.
+ Lease4Ptr result = getLease4(addr);
+ if (result && (result->subnet_id_ != subnet_id)) {
+
+ // Lease found but IDs do not match. Return null pointer
+ result.reset();
+ }
+
+ return (result);
}
Lease4Collection
-MySqlLeaseMgr::getLease4(const HWAddr& /* hwaddr */) const {
- isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const HWAddr&) "
- "not implemented yet");
- return (Lease4Collection());
+MySqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[1];
+ memset(inbind, 0, sizeof(inbind));
+
+ // As "buffer" is "char*" - even though the data is being read - we need
+ // to cast away the "const"ness as well as reinterpreting the data as
+ // a "char*". (We could avoid the "const_cast" by copying the data to a
+ // local variable, but as the data is only being read, this introduces
+ // an unnecessary copy).
+ unsigned long hwaddr_length = hwaddr.size();
+ uint8_t* data = const_cast<uint8_t*>(&hwaddr[0]);
+
+ inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+ inbind[0].buffer = reinterpret_cast<char*>(data);
+ inbind[0].buffer_length = hwaddr_length;
+ inbind[0].length = &hwaddr_length;
+
+ // Get the data
+ Lease4Collection result;
+ getLeaseCollection(GET_LEASE4_HWADDR, inbind, result);
+
+ return (result);
}
Lease4Ptr
-MySqlLeaseMgr::getLease4(const HWAddr& /* hwaddr */,
- SubnetID /* subnet_id */) const {
- isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const HWAddr&, SubnetID) "
- "not implemented yet");
- return (Lease4Ptr());
+MySqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[2];
+ memset(inbind, 0, sizeof(inbind));
+
+ // As "buffer" is "char*" - even though the data is being read - we need
+ // to cast away the "const"ness as well as reinterpreting the data as
+ // a "char*". (We could avoid the "const_cast" by copying the data to a
+ // local variable, but as the data is only being read, this introduces
+ // an unnecessary copy).
+ unsigned long hwaddr_length = hwaddr.size();
+ uint8_t* data = const_cast<uint8_t*>(&hwaddr[0]);
+
+ inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+ inbind[0].buffer = reinterpret_cast<char*>(data);
+ inbind[0].buffer_length = hwaddr_length;
+ inbind[0].length = &hwaddr_length;
+
+ inbind[1].buffer_type = MYSQL_TYPE_LONG;
+ inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
+ inbind[1].is_unsigned = MLM_TRUE;
+
+ // Get the data
+ Lease4Ptr result;
+ getLease(GET_LEASE4_HWADDR_SUBID, inbind, result);
+
+ return (result);
}
Lease4Collection
-MySqlLeaseMgr::getLease4(const ClientId& /* clientid */) const {
- isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const ClientID&) "
- "not implemented yet");
- return (Lease4Collection());
-}
+MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[1];
+ memset(inbind, 0, sizeof(inbind));
+ std::vector<uint8_t> client_data = clientid.getClientId();
+ unsigned long client_data_length = client_data.size();
+ inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+ inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
+ inbind[0].buffer_length = client_data_length;
+ inbind[0].length = &client_data_length;
-Lease4Ptr
-MySqlLeaseMgr::getLease4(const ClientId& /* clientid */,
- SubnetID /* subnet_id */) const {
- isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const ClientID&, SubnetID) "
- "not implemented yet");
- return (Lease4Ptr());
+ // Get the data
+ Lease4Collection result;
+ getLeaseCollection(GET_LEASE4_CLIENTID, inbind, result);
+
+ return (result);
}
-// A convenience function used in the various getLease6() 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.
-void
-MySqlLeaseMgr::bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const {
+Lease4Ptr
+MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[2];
+ memset(inbind, 0, sizeof(inbind));
- // 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");
+ std::vector<uint8_t> client_data = clientid.getClientId();
+ unsigned long client_data_length = client_data.size();
+ inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+ inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
+ inbind[0].buffer_length = client_data_length;
+ inbind[0].length = &client_data_length;
- // Set up the SELECT clause
- std::vector<MYSQL_BIND> outbind = exchange6_->createBindForReceive();
+ inbind[1].buffer_type = MYSQL_TYPE_LONG;
+ inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
+ inbind[1].is_unsigned = MLM_TRUE;
- // Bind the output parameters to the statement
- status = mysql_stmt_bind_result(statements_[stindex], &outbind[0]);
- checkError(status, stindex, "unable to bind SELECT caluse parameters");
+ // Get the data
+ Lease4Ptr result;
+ getLease(GET_LEASE4_CLIENTID_SUBID, inbind, result);
- // Execute the statement
- status = mysql_stmt_execute(statements_[stindex]);
- checkError(status, stindex, "unable to execute");
+ return (result);
}
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 +1418,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, result);
return (result);
}
@@ -820,7 +1427,6 @@ MySqlLeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
Lease6Collection
MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
- const StatementIndex stindex = GET_LEASE6_DUID_IAID;
// Set up the WHERE clause value
MYSQL_BIND inbind[2];
@@ -834,7 +1440,7 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
// Note that the const_cast could be avoided by copying the DUID to
// a writeable buffer and storing the address of that in the "buffer"
// element. However, this introduces a copy operation (with additional
- // overhead) purely to get round the strictures introduced by design of
+ // overhead) purely to get round the structures introduced by design of
// the MySQL interface (which uses the area pointed to by "buffer" as
// input when specifying query parameters and as output when retrieving
// data). For that reason, "const_cast" has been used.
@@ -849,45 +1455,11 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
// IAID
inbind[1].buffer_type = MYSQL_TYPE_LONG;
inbind[1].buffer = reinterpret_cast<char*>(&iaid);
- inbind[1].is_unsigned = static_cast<my_bool>(1);
-
- // 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);
-
- // Ensure that all the lease information is retrieved in one go to avoid
- // overhead of going back and forth between client and server.
- int status = mysql_stmt_store_result(statements_[stindex]);
- checkError(status, stindex, "unable to set up for storing all results");
+ inbind[1].is_unsigned = MLM_TRUE;
- // Fetch the data. There could be multiple rows, so we need to iterate
- // until all data has been retrieved.
+ // ... and get the data
Lease6Collection result;
-
- // Set up the fetch "release" object to release resources associated
- // with the call to mysql_stmt_fetch when this method exits, then
- // retrieve the data.
- MySqlFreeResult fetch_release(statements_[stindex]);
- while ((status = mysql_stmt_fetch(statements_[stindex])) == 0) {
- try {
- Lease6Ptr lease = exchange6_->getLeaseData();
- result.push_back(lease);
-
- } catch (const isc::BadValue& ex) {
- // Rethrow the exception with a bit more data.
- isc_throw(BadValue, ex.what() << ". Statement is <" <<
- text_statements_[stindex] << ">");
- }
- }
-
- // How did the fetch end?
- if (status == 1) {
- // Error - unable to fecth results
- checkError(status, stindex, "unable to fetch results");
- } else if (status == MYSQL_DATA_TRUNCATED) {
- // @TODO Handle truncation
- ;
- }
+ getLeaseCollection(GET_LEASE6_DUID_IAID, inbind, result);
return (result);
}
@@ -896,7 +1468,6 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid) const {
Lease6Ptr
MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
SubnetID subnet_id) const {
- const StatementIndex stindex = GET_LEASE6_DUID_IAID_SUBID;
// Set up the WHERE clause value
MYSQL_BIND inbind[3];
@@ -915,58 +1486,70 @@ MySqlLeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
// IAID
inbind[1].buffer_type = MYSQL_TYPE_LONG;
inbind[1].buffer = reinterpret_cast<char*>(&iaid);
- inbind[1].is_unsigned = static_cast<my_bool>(1);
+ inbind[1].is_unsigned = MLM_TRUE;
// Subnet ID
inbind[2].buffer_type = MYSQL_TYPE_LONG;
inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
- inbind[2].is_unsigned = static_cast<my_bool>(1);
-
- // 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);
+ inbind[2].is_unsigned = MLM_TRUE;
- // Fetch the data and set up the "release" object to release associated
- // resources when this method exits then retrieve the data.
Lease6Ptr result;
- MySqlFreeResult fetch_release(statements_[stindex]);
- int status = mysql_stmt_fetch(statements_[stindex]);
- if (status == 0) {
- try {
- result = exchange6_->getLeaseData();
+ getLease(GET_LEASE6_DUID_IAID_SUBID, inbind, result);
- // TODO: check for more than one row returned. At present, just
- // ignore the excess and take the first.
+ return (result);
+}
- } 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] << ">");
- }
+// Update lease methods. These comprise common code that handles the actual
+// update, and type-specific methods that set up the parameters for the prepared
+// statement depending on the type of lease.
- // 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.
+template <typename LeasePtr>
+void
+MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
+ const LeasePtr& lease) {
- } else if (status == 1) {
- checkError(status, stindex, "unable to fetch results");
+ // Bind the parameters to the statement
+ int status = mysql_stmt_bind_param(statements_[stindex], bind);
+ checkError(status, stindex, "unable to bind parameters");
- } 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 the action is a no-op.
- }
+ // Execute
+ status = mysql_stmt_execute(statements_[stindex]);
+ checkError(status, stindex, "unable to execute");
- return (result);
+ // See how many rows were affected. The statement should only update a
+ // single row.
+ int affected_rows = mysql_stmt_affected_rows(statements_[stindex]);
+ if (affected_rows == 0) {
+ isc_throw(NoSuchLease, "unable to update lease for address " <<
+ lease->addr_.toText() << " as it does not exist");
+ } else if (affected_rows > 1) {
+ // Should not happen - primary key constraint should only have selected
+ // one row.
+ isc_throw(DbOperationError, "apparently updated more than one lease "
+ "that had the address " << lease->addr_.toText());
+ }
}
void
-MySqlLeaseMgr::updateLease4(const Lease4Ptr& /* lease4 */) {
- isc_throw(NotImplemented, "MySqlLeaseMgr::updateLease4(const Lease4Ptr&) "
- "not implemented yet");
+MySqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
+ const StatementIndex stindex = UPDATE_LEASE4;
+
+ // Create the MYSQL_BIND array for the data being updated
+ std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
+
+ // Set up the WHERE clause and append it to the MYSQL_BIND array
+ MYSQL_BIND where;
+ memset(&where, 0, sizeof(where));
+
+ uint32_t addr4 = static_cast<uint32_t>(lease->addr_);
+ where.buffer_type = MYSQL_TYPE_LONG;
+ where.buffer = reinterpret_cast<char*>(&addr4);
+ where.is_unsigned = MLM_TRUE;
+ bind.push_back(where);
+
+ // Drop to common update code
+ updateLeaseCommon(stindex, &bind[0], lease);
}
@@ -992,40 +1575,52 @@ MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
where.length = &addr6_length;
bind.push_back(where);
- // Bind the parameters to the statement
- int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]);
- checkError(status, stindex, "unable to bind parameters");
+ // Drop to common update code
+ updateLeaseCommon(stindex, &bind[0], lease);
+}
+
+// Delete lease methods. As with other groups of methods, these comprise
+// a per-type method that sets up the relevant MYSQL_BIND array and a
+// common method than handles the common processing.
+
+bool
+MySqlLeaseMgr::deleteLease(StatementIndex stindex, MYSQL_BIND* bind) {
+
+ // Bind the input parameters to the statement
+ int status = mysql_stmt_bind_param(statements_[stindex], bind);
+ 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. The statement should only delete a
- // single row.
- int affected_rows = mysql_stmt_affected_rows(statements_[stindex]);
- if (affected_rows == 0) {
- isc_throw(NoSuchLease, "unable to update lease for address " <<
- addr6 << " as it does not exist");
- } else if (affected_rows > 1) {
- // Should not happen - primary key constraint should only have selected
- // one row.
- isc_throw(DbOperationError, "apparently updated more than one lease "
- "that had the address " << addr6);
- }
+ // See how many rows were affected. Note that the statement may delete
+ // multiple rows.
+ return (mysql_stmt_affected_rows(statements_[stindex]) > 0);
}
bool
-MySqlLeaseMgr::deleteLease4(const isc::asiolink::IOAddress& /* addr */) {
- isc_throw(NotImplemented, "MySqlLeaseMgr::deleteLease4(const IOAddress&) "
- "not implemented yet");
- return (false);
+MySqlLeaseMgr::deleteLease4(const isc::asiolink::IOAddress& addr) {
+
+ // 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 = MLM_TRUE;
+
+ return (deleteLease(DELETE_LEASE4, inbind));
}
bool
MySqlLeaseMgr::deleteLease6(const isc::asiolink::IOAddress& addr) {
- const StatementIndex stindex = DELETE_LEASE6;
// Set up the WHERE clause value
MYSQL_BIND inbind[1];
@@ -1041,19 +1636,10 @@ MySqlLeaseMgr::deleteLease6(const isc::asiolink::IOAddress& addr) {
inbind[0].buffer_length = addr6_length;
inbind[0].length = &addr6_length;
- // 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);
+ return (deleteLease(DELETE_LEASE6, inbind));
}
+// Miscellaneous database methods.
std::string
MySqlLeaseMgr::getName() const {
@@ -1061,7 +1647,7 @@ MySqlLeaseMgr::getName() const {
try {
name = getParameter("name");
} catch (...) {
- ;
+ // Return an empty name
}
return (name);
}
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.h b/src/lib/dhcpsrv/mysql_lease_mgr.h
index 1884da6..8a97fc0 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.h
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.h
@@ -27,18 +27,22 @@ namespace dhcp {
// Define the current database schema values
-const uint32_t CURRENT_VERSION_VERSION = 0;
-const uint32_t CURRENT_VERSION_MINOR = 1;
+const uint32_t CURRENT_VERSION_VERSION = 1;
+const uint32_t CURRENT_VERSION_MINOR = 0;
-// Forward declaration of the Lease6 exchange object. This class is defined
+// Forward declaration of the Lease exchange objects. These classes are defined
// in the .cc file.
+class MySqlLease4Exchange;
class MySqlLease6Exchange;
/// @brief MySQL Lease Manager
///
-/// This is a concrete API for the backend for the MySQL database.
+/// This class provides the \ref isc::dhcp::LeaseMgr interface to the MySQL
+/// database. Use of this backend presupposes that a MySQL database is
+/// available and that the Kea schema has been created within it.
+
class MySqlLeaseMgr : public LeaseMgr {
public:
/// @brief Constructor
@@ -68,7 +72,7 @@ public:
/// @brief Destructor (closes database)
virtual ~MySqlLeaseMgr();
- /// @brief Adds an IPv4 lease.
+ /// @brief Adds an IPv4 lease
///
/// @param lease lease to be added
///
@@ -79,7 +83,7 @@ public:
/// failed.
virtual bool addLease(const Lease4Ptr& lease);
- /// @brief Adds an IPv6 lease.
+ /// @brief Adds an IPv6 lease
///
/// @param lease lease to be added
///
@@ -106,7 +110,7 @@ public:
/// @brief Returns an IPv4 lease for specified IPv4 address
///
/// This method return a lease that is associated with a given address.
- /// For other query types (by hardware addr, by DUID) there can be
+ /// For other query types (by hardware addr, by Client ID) there can be
/// several leases in different subnets (e.g. for mobile clients that
/// got address in different subnets). However, for a single address
/// there can be only one lease, so this method returns a pointer to
@@ -115,6 +119,12 @@ public:
/// @param addr address of the searched lease
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
@@ -128,6 +138,12 @@ public:
/// @param hwaddr hardware address of the client
///
/// @return lease collection
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
/// @brief Returns existing IPv4 leases for specified hardware address
@@ -140,6 +156,12 @@ public:
/// @param subnet_id identifier of the subnet that lease must belong to
///
/// @return a pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
SubnetID subnet_id) const;
@@ -153,6 +175,12 @@ public:
/// @param clientid client identifier
///
/// @return lease collection
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Collection getLease4(const ClientId& clientid) const;
/// @brief Returns existing IPv4 lease for specified client-id
@@ -164,6 +192,12 @@ public:
/// @param subnet_id identifier of the subnet that lease must belong to
///
/// @return a pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease4Ptr getLease4(const ClientId& clientid,
SubnetID subnet_id) const;
@@ -179,6 +213,9 @@ public:
///
/// @throw isc::BadValue record retrieved from database had an invalid
/// lease type field.
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
@@ -194,6 +231,14 @@ public:
/// @param iaid IA identifier
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::BadValue record retrieved from database had an invalid
+ /// lease type field.
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease6Collection getLease6(const DUID& duid,
uint32_t iaid) const;
@@ -204,18 +249,35 @@ public:
/// @param subnet_id subnet id of the subnet the lease belongs to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
+ ///
+ /// @throw isc::BadValue record retrieved from database had an invalid
+ /// lease type field.
+ /// @throw isc::dhcp::DataTruncation Data was truncated on retrieval to
+ /// fit into the space allocated for the result. This indicates a
+ /// programming error.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
SubnetID subnet_id) const;
/// @brief Updates IPv4 lease.
///
+ /// Updates the record of the lease in the database (as identified by the
+ /// address) with the data in the passed lease object.
+ ///
/// @param lease4 The lease to be updated.
///
- /// If no such lease is present, an exception will be thrown.
+ /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+ /// exist.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual void updateLease4(const Lease4Ptr& lease4);
/// @brief Updates IPv6 lease.
///
+ /// Updates the record of the lease in the database (as identified by the
+ /// address) with the data in the passed lease object.
+ ///
/// @param lease6 The lease to be updated.
///
/// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
@@ -226,13 +288,24 @@ 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
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
/// @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,12 +416,20 @@ 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_LEASE4_CLIENTID, // Get lease4 by client ID
+ GET_LEASE4_CLIENTID_SUBID, // Get lease4 by client ID & subnet ID
+ GET_LEASE4_HWADDR, // Get lease4 by HW address
+ GET_LEASE4_HWADDR_SUBID, // Get lease4 by HW address & subnet ID
GET_LEASE6_ADDR, // Get lease6 by address
GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID
- GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and Subnet ID
+ GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
GET_VERSION, // Obtain version number
+ INSERT_LEASE4, // Add entry to lease4 table
INSERT_LEASE6, // Add entry to lease6 table
+ UPDATE_LEASE4, // Update a Lease4 entry
UPDATE_LEASE6, // Update a Lease6 entry
NUM_STATEMENTS // Number of statements
};
@@ -389,25 +470,150 @@ private:
/// @throw DbOpenError Error opening the database
void openDatabase();
- /// @brief Binds Parameters and Executes
+ /// @brief Add Lease Common Code
+ ///
+ /// This method performs the common actions for both flavours (V4 and V6)
+ /// of the addLease method. It binds the contents of the lease object to
+ /// the prepared statement and adds it to the database.
///
- /// This method abstracts a lot of common processing from the getXxxx()
- /// methods. It binds the parameters passed to it to the appropriate
- /// prepared statement, and binds the variables in the exchange6 object to
- /// the output parameters of the statement. It then executes the prepared
- /// statement.
+ /// @param stindex Index of statemnent being executed
+ /// @param bind MYSQL_BIND array that has been created for the type
+ /// of lease in question.
///
- /// The data can be retrieved using mysql_stmt_fetch and the getLeaseData()
- /// method on the exchange6 object.
+ /// @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 addLeaseCommon(StatementIndex stindex, std::vector<MYSQL_BIND>& bind);
+
+ /// @brief Get Lease Collection Common Code
+ ///
+ /// This method performs the common actions for obtaining multiple leases
+ /// from the database.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param bind MYSQL_BIND array for input parameters
+ /// @param exchange Exchange object to use
+ /// @param lease LeaseCollection object returned. Note that any leases in
+ /// the collection when this method is called are not erased: the
+ /// new data is appended to the end.
+ /// @param single If true, only a single data item is to be retrieved.
+ /// If more than one is present, a MultipleRecords exception will
+ /// be thrown.
+ ///
+ /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+ /// from the database where only one was expected.
+ template <typename Exchange, typename LeaseCollection>
+ void getLeaseCollection(StatementIndex stindex, MYSQL_BIND* bind,
+ Exchange& exchange, LeaseCollection& result,
+ bool single = false) const;
+
+ /// @brief Get Lease Collection
+ ///
+ /// Gets a collection of Lease4 objects. This is just an interface to
+ /// the get lease collection common code.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param bind MYSQL_BIND array for input parameters
+ /// @param lease LeaseCollection object returned. Note that any leases in
+ /// the collection when this method is called are not erased: the
+ /// new data is appended to the end.
+ ///
+ /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+ /// from the database where only one was expected.
+ void getLeaseCollection(StatementIndex stindex, MYSQL_BIND* bind,
+ Lease4Collection& result) const {
+ getLeaseCollection(stindex, bind, exchange4_, result);
+ }
+
+ /// @brief Get Lease Collection
+ ///
+ /// Gets a collection of Lease6 objects. This is just an interface to
+ /// the get lease collection common code.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param bind MYSQL_BIND array for input parameters
+ /// @param lease LeaseCollection object returned. Note that any existing
+ /// data in the collection is erased first.
+ ///
+ /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+ /// from the database where only one was expected.
+ void getLeaseCollection(StatementIndex stindex, MYSQL_BIND* bind,
+ Lease6Collection& result) const {
+ getLeaseCollection(stindex, bind, exchange6_, result);
+ }
+
+ /// @brief Get Lease4 Common Code
+ ///
+ /// This method performs the common actions for the various getLease4()
+ /// methods. It acts as an interface to the getLeaseCollection() method,
+ /// but retrieveing only a single lease.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param bind MYSQL_BIND array for input parameters
+ /// @param lease Lease4 object returned
+ void getLease(StatementIndex stindex, MYSQL_BIND* bind,
+ Lease4Ptr& result) const;
+
+ /// @brief Get Lease6 Common Code
+ ///
+ /// This method performs the common actions for the various getLease46)
+ /// methods. It acts as an interface to the getLeaseCollection() method,
+ /// but retrieveing only a single lease.
+ ///
+ /// @param stindex Index of statement being executed
+ /// @param bind MYSQL_BIND array for input parameters
+ /// @param lease Lease6 object returned
+ void getLease(StatementIndex stindex, MYSQL_BIND* bind,
+ Lease6Ptr& result) const;
+
+ /// @brief Update lease common code
+ ///
+ /// Holds the common code for updating a lease. It binds the parameters
+ /// to the prepared statement, executes it, then checks how many rows
+ /// were affected.
+ ///
+ /// @param stindex Index of prepared statement to be executed
+ /// @param bind Array of MYSQL_BIND objects representing the parameters.
+ /// (Note that the number is determined by the number of parameters
+ /// in the statement.)
+ /// @param lease Pointer to the lease object whose record is being updated.
+ ///
+ /// @throw NoSuchLease Could not update a lease because no lease matches
+ /// the address given.
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ template <typename LeasePtr>
+ void updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
+ const LeasePtr& lease);
+
+ /// @brief Delete lease common code
+ ///
+ /// Holds the common code for deleting a lease. It binds the parameters
+ /// to the prepared statement, executes the statement and checks to
+ /// see how many rows were deleted.
///
/// @param stindex Index of prepared statement to be executed
- /// @param inbind Array of MYSQL_BIND objects representing the parameters.
+ /// @param bind Array of MYSQL_BIND objects representing the parameters.
/// (Note that the number is determined by the number of parameters
/// in the statement.)
///
+ /// @return true if one or more rows were deleted, false if none were
+ /// deleted.
+ ///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- void bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const;
+ bool deleteLease(StatementIndex stindex, MYSQL_BIND* bind);
/// @brief Check Error and Throw Exception
///
@@ -433,14 +639,15 @@ private:
// Members
- /// Used for transfer of data to/from the database. This is a pointed-to
- /// 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_;
+ /// The exchange objects are used for transfer of data to/from the database.
+ /// They are pointed-to objects as the contents may change in "const" calls,
+ /// while the rest of this object does not. (At alternative would be to
+ /// declare them as "mutable".)
+ boost::scoped_ptr<MySqlLease4Exchange> exchange4_; ///< Exchange object
+ boost::scoped_ptr<MySqlLease6Exchange> exchange6_; ///< Exchange object
MYSQL* mysql_; ///< MySQL context object
- std::vector<std::string> text_statements_; ///< Raw text of statements
std::vector<MYSQL_STMT*> statements_; ///< Prepared statements
+ std::vector<std::string> text_statements_; ///< Raw text of statements
};
}; // end of isc::dhcp namespace
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc
index 40ecba3..e343c44 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_factory_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
index 74a0fb4..6384c1c 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -22,6 +22,8 @@
#include <iostream>
#include <sstream>
+#include <time.h>
+
using namespace std;
using namespace isc;
using namespace isc::asiolink;
@@ -236,16 +238,12 @@ public:
};
namespace {
-// empty class for now, but may be extended once Addr6 becomes bigger
-class LeaseMgrTest : public ::testing::Test {
-public:
- LeaseMgrTest() {
- }
-};
-// This test checks if the LeaseMgr can be instantiated and that it
-// parses parameters string properly.
-TEST_F(LeaseMgrTest, getParameter) {
+/// @brief getParameter test
+///
+/// This test checks if the LeaseMgr can be instantiated and that it
+/// parses parameters string properly.
+TEST(LeaseMgr, getParameter) {
LeaseMgr::ParameterMap pmap;
pmap[std::string("param1")] = std::string("value1");
@@ -261,36 +259,368 @@ TEST_F(LeaseMgrTest, getParameter) {
// are purely virtual, so we would only call ConcreteLeaseMgr methods.
// Those methods are just stubs that do not return anything.
+/// @brief Lease4 Constructor Test
+///
+/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
+// This test checks if the Lease4 structure can be instantiated correctly
+TEST(Lease4, Lease4Constructor) {
+
+ // Random values for the tests
+ const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+ std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+ const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+ std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+ ClientId clientid(clientid_vec);
+
+ // ...and a time
+ const time_t current_time = time(NULL);
+
+ // Other random constants.
+ const uint32_t SUBNET_ID = 42;
+ const uint32_t VALID_LIFETIME = 500;
+
+ // We want to check that various addresses work, so let's iterate over
+ // these.
+ const uint32_t ADDRESS[] = {
+ 0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff
+ };
+
+ for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+
+ // Create the lease
+ Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
+ SUBNET_ID);
+
+ EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
+ EXPECT_EQ(0, lease.ext_);
+ EXPECT_TRUE(hwaddr == lease.hwaddr_);
+ EXPECT_TRUE(clientid == *lease.client_id_);
+ EXPECT_EQ(0, lease.t1_);
+ EXPECT_EQ(0, lease.t2_);
+ EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
+ EXPECT_EQ(current_time, lease.cltt_);
+ EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
+ EXPECT_FALSE(lease.fixed_);
+ EXPECT_TRUE(lease.hostname_.empty());
+ EXPECT_FALSE(lease.fqdn_fwd_);
+ EXPECT_FALSE(lease.fqdn_rev_);
+ EXPECT_TRUE(lease.comments_.empty());
+ }
+}
+
+/// @brief Lease4 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease4, OperatorEquals) {
+
+ // Random values for the tests
+ const uint32_t ADDRESS = 0x01020304;
+ const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+ std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+ const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+ std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+ ClientId clientid(clientid_vec);
+ const time_t current_time = time(NULL);
+ const uint32_t SUBNET_ID = 42;
+ const uint32_t VALID_LIFETIME = 500;
+
+ // Check when the leases are equal.
+ Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
+ SUBNET_ID);
+ Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
+ SUBNET_ID);
+ EXPECT_TRUE(lease1 == lease2);
+ EXPECT_FALSE(lease1 != lease2);
+
+ // Now vary individual fields in a lease and check that the leases compare
+ // not equal in every case.
+ lease1.addr_ = IOAddress(ADDRESS + 1);
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.addr_ = lease2.addr_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.ext_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.ext_ = lease2.ext_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.hwaddr_[0];
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.hwaddr_ = lease2.hwaddr_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++clientid_vec[0];
+ lease1.client_id_.reset(new ClientId(clientid_vec));
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ --clientid_vec[0];
+ lease1.client_id_.reset(new ClientId(clientid_vec));
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t1_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t1_ = lease2.t1_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t2_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t2_ = lease2.t2_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.valid_lft_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.valid_lft_ = lease2.valid_lft_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.cltt_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.cltt_ = lease2.cltt_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.subnet_id_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.subnet_id_ = lease2.subnet_id_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fixed_ = !lease1.fixed_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fixed_ = lease2.fixed_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.hostname_ += string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.hostname_ = lease2.hostname_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_rev_ = lease2.fqdn_rev_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.comments_ += string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.comments_ = lease2.comments_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
+
+
+
// Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
// This test checks if the Lease6 structure can be instantiated correctly
TEST(Lease6, Lease6Constructor) {
- IOAddress addr("2001:db8:1::456");
+ // check a variety of addresses with different bits set.
+ const char* ADDRESS[] = {
+ "::", "::1", "2001:db8:1::456",
+ "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ "8000::", "8000::1",
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+ };
+ // Other values
uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
DuidPtr duid(new DUID(llt, sizeof(llt)));
-
uint32_t iaid = 7; // just a number
-
SubnetID subnet_id = 8; // just another number
- Lease6Ptr x(new Lease6(Lease6::LEASE_IA_NA, addr,
- duid, iaid, 100, 200, 50, 80,
- subnet_id));
-
- EXPECT_TRUE(x->addr_ == addr);
- EXPECT_TRUE(*x->duid_ == *duid);
- EXPECT_TRUE(x->iaid_ == iaid);
- EXPECT_TRUE(x->subnet_id_ == subnet_id);
- EXPECT_TRUE(x->type_ == Lease6::LEASE_IA_NA);
- EXPECT_TRUE(x->preferred_lft_ == 100);
- EXPECT_TRUE(x->valid_lft_ == 200);
- EXPECT_TRUE(x->t1_ == 50);
- EXPECT_TRUE(x->t2_ == 80);
+ for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+ IOAddress addr(ADDRESS[i]);
+ Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr,
+ duid, iaid, 100, 200, 50, 80,
+ subnet_id));
+
+ EXPECT_TRUE(lease->addr_ == addr);
+ EXPECT_TRUE(*lease->duid_ == *duid);
+ EXPECT_TRUE(lease->iaid_ == iaid);
+ EXPECT_TRUE(lease->subnet_id_ == subnet_id);
+ EXPECT_TRUE(lease->type_ == Lease6::LEASE_IA_NA);
+ EXPECT_TRUE(lease->preferred_lft_ == 100);
+ EXPECT_TRUE(lease->valid_lft_ == 200);
+ EXPECT_TRUE(lease->t1_ == 50);
+ EXPECT_TRUE(lease->t2_ == 80);
+ }
// Lease6 must be instantiated with a DUID, not with NULL pointer
+ IOAddress addr(ADDRESS[0]);
EXPECT_THROW(new Lease6(Lease6::LEASE_IA_NA, addr,
DuidPtr(), iaid, 100, 200, 50, 80,
subnet_id), InvalidOperation);
}
+
+/// @brief Lease6 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease6, OperatorEquals) {
+
+ // check a variety of addressemas with different bits set.
+ const IOAddress addr("2001:db8:1::456");
+ uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
+ uint32_t iaid = 7; // just a number
+ SubnetID subnet_id = 8; // just another number
+
+ // Check for equality.
+ Lease6 lease1(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+ subnet_id);
+ Lease6 lease2(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+ subnet_id);
+ EXPECT_TRUE(lease1 == lease2);
+ EXPECT_FALSE(lease1 != lease2);
+
+ // Go through and alter all the fields one by one
+
+ lease1.addr_ = IOAddress("::1");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.addr_ = lease2.addr_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.type_ = Lease6::LEASE_IA_PD;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.type_ = lease2.type_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.prefixlen_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.prefixlen_ = lease2.prefixlen_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.iaid_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.iaid_ = lease2.iaid_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++duid_array[0];
+ lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ --duid_array[0];
+ lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.preferred_lft_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.preferred_lft_ = lease2.preferred_lft_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.valid_lft_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.valid_lft_ = lease2.valid_lft_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t1_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t1_ = lease2.t1_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t2_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t2_ = lease2.t2_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.cltt_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.cltt_ = lease2.cltt_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.subnet_id_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.subnet_id_ = lease2.subnet_id_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fixed_ = !lease1.fixed_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fixed_ = lease2.fixed_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.hostname_ += string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.hostname_ = lease2.hostname_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_rev_ = lease2.fqdn_rev_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.comments_ += string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.comments_ = lease2.comments_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
}; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
index 0f026f2..b3f504f 100644
--- a/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
@@ -53,7 +53,7 @@ TEST_F(MemfileLeaseMgrTest, getTypeAndName) {
boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
EXPECT_EQ(std::string("memfile"), lease_mgr->getType());
- EXPECT_EQ(std::string("memfile"), lease_mgr->getName());
+ EXPECT_EQ(std::string("memory"), lease_mgr->getName());
}
// Checks that adding/getting/deleting a Lease6 object works.
diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
index 930bc06..7f6bcc9 100644
--- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -20,6 +20,7 @@
#include <gtest/gtest.h>
+#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
@@ -32,21 +33,24 @@ using namespace std;
namespace {
-// Creation of the schema
+// This holds statements to create and destroy the schema.
#include "schema_copy.h"
-// IPv6 addresseses
-const char* ADDRESS_0 = "2001:db8::0";
-const char* ADDRESS_1 = "2001:db8::1";
-const char* ADDRESS_2 = "2001:db8::2";
-const char* ADDRESS_3 = "2001:db8::3";
-const char* ADDRESS_4 = "2001:db8::4";
-const char* ADDRESS_5 = "2001:db8::5";
-const char* ADDRESS_6 = "2001:db8::6";
-const char* ADDRESS_7 = "2001:db8::7";
-
-// Connection strings. Assume:
+// IPv4 and IPv6 addresses used in the tests
+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",
+ NULL
+};
+
+// Connection strings.
// Database: keatest
+// Host: localhost
// Username: keatest
// Password: keatest
const char* VALID_TYPE = "type=mysql";
@@ -69,7 +73,6 @@ string connectionString(const char* type, const char* name, const char* host,
if (type != NULL) {
result += string(type);
}
-
if (name != NULL) {
if (! result.empty()) {
result += space;
@@ -111,7 +114,7 @@ validConnectionString() {
// @brief Clear everything from the database
//
// There is no error checking in this code: if something fails, one of the
-// tests will fall over.
+// tests will (should) fall over.
void destroySchema() {
// Initialise
MYSQL handle;
@@ -145,7 +148,7 @@ void createSchema() {
(void) mysql_real_connect(&handle, "localhost", "keatest",
"keatest", "keatest", 0, NULL, 0);
- // Get rid of everything in it.
+ // Execute creation statements.
for (int i = 0; create_statement[i] != NULL; ++i) {
(void) mysql_query(&handle, create_statement[i]);
}
@@ -154,32 +157,37 @@ void createSchema() {
(void) mysql_close(&handle);
}
-// Note: Doxygen "///" not used - even though Doxygen is used to
-// document class and methods - to avoid the comments appearing
-// in the programming manual.
-
-// @brief Test Fixture Class
-//
-// Opens the database prior to each test and closes it afterwards.
-// All pending transactions are deleted prior to closure.
+/// @brief Test fixture class for testing MySQL Lease Manager
+///
+/// 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
- //
- // Deletes everything from the database and opens it.
- MySqlLeaseMgrTest() :
- L0_ADDRESS(ADDRESS_0), L0_IOADDRESS(L0_ADDRESS),
- L1_ADDRESS(ADDRESS_1), L1_IOADDRESS(L1_ADDRESS),
- L2_ADDRESS(ADDRESS_2), L2_IOADDRESS(L2_ADDRESS),
- L3_ADDRESS(ADDRESS_3), L3_IOADDRESS(L3_ADDRESS),
- L4_ADDRESS(ADDRESS_4), L4_IOADDRESS(L4_ADDRESS),
- L5_ADDRESS(ADDRESS_5), L5_IOADDRESS(L5_ADDRESS),
- L6_ADDRESS(ADDRESS_6), L6_IOADDRESS(L6_ADDRESS),
- L7_ADDRESS(ADDRESS_7), L7_IOADDRESS(L7_ADDRESS) {
+ /// @brief Constructor
+ ///
+ /// 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);
+ IOAddress ioaddr(addr);
+ ioaddress6_.push_back(ioaddr);
+ }
+
+ // Ensure schema is the correct one.
destroySchema();
createSchema();
+
+ // Connect to the database
try {
LeaseMgrFactory::create(validConnectionString());
} catch (...) {
@@ -193,38 +201,146 @@ public:
lmptr_ = &(LeaseMgrFactory::instance());
}
- // @brief Destructor
- //
- // Rolls back all pending transactions. The deletion of the
- // lmptr_ member variable will close the database. Then
- // reopen it and delete everything created by the test.
+ /// @brief Destructor
+ ///
+ /// Rolls back all pending transactions. The deletion of lmptr_ will close
+ /// the database. Then reopen it and delete everything created by the test.
virtual ~MySqlLeaseMgrTest() {
lmptr_->rollback();
LeaseMgrFactory::destroy();
destroySchema();
}
- // @brief Reopen the database
- //
- // Closes the database and re-open it. Anything committed should be
- // visible.
+ /// @brief Reopen the database
+ ///
+ /// Closes the database and re-open it. Anything committed should be
+ /// visible.
void reopen() {
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(validConnectionString());
lmptr_ = &(LeaseMgrFactory::instance());
}
- // @brief Initialize Lease6 Fields
- //
- // Returns a pointer to a Lease6 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 Lease6Ptr. This will not point to anything if the initialization
- // failed (e.g. unknown address).
+ /// @brief Initialize Lease4 Fields
+ ///
+ /// Returns a pointer to a Lease4 structure. Different values are put into
+ /// 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, 0x42)));
+ lease->valid_lft_ = 8677;
+ lease->cltt_ = 168256;
+ lease->subnet_id_ = 23;
+
+ } 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, 0x53)));
+ lease->valid_lft_ = 3677;
+ lease->cltt_ = 123456;
+ lease->subnet_id_ = 73;
+
+ } 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, 0x64)));
+ lease->valid_lft_ = 5412;
+ lease->cltt_ = 234567;
+ lease->subnet_id_ = 73; // Same as lease 1
+
+ } else if (address == straddress4_[3]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x19); // Same as lease 1
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x75)));
+
+ // 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;
+ lease->cltt_ = 234567;
+ lease->subnet_id_ = 37;
+
+ } 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, 0x53))); // Same as lease 1
+ lease->valid_lft_ = 7736;
+ lease->cltt_ = 222456;
+ lease->subnet_id_ = 85;
+
+ } else if (address == straddress4_[5]) {
+ lease->hwaddr_ = vector<uint8_t>(6, 0x19); // Same as lease 1
+ // Same ClientId and IAID as straddress4_1
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>(8, 0x53))); // Same as lease 1
+ lease->valid_lft_ = 7832;
+ lease->cltt_ = 227476;
+ lease->subnet_id_ = 175;
+
+ } 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, 0x53))); // Same as lease 1
+ lease->valid_lft_ = 1832;
+ lease->cltt_ = 627476;
+ lease->subnet_id_ = 112;
+
+ } else if (address == straddress4_[7]) {
+ lease->hwaddr_ = vector<uint8_t>(); // Empty
+ lease->client_id_ = boost::shared_ptr<ClientId>(
+ new ClientId(vector<uint8_t>())); // Empty
+ lease->valid_lft_ = 7975;
+ lease->cltt_ = 213876;
+ lease->subnet_id_ = 19;
+
+ } 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 into
+ /// 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 Lease6Ptr. This will not point to anything if the initialization
+ /// failed (e.g. unknown address).
Lease6Ptr initializeLease6(std::string address) {
Lease6Ptr lease(new Lease6());
@@ -240,38 +356,38 @@ public:
lease->fqdn_rev_ = false; // Unused
lease->comments_ = std::string(""); // Unused
- // Set the other parameters. For historical reasons, L0_ADDRESS is not used.
- if (address == L0_ADDRESS) {
+ // Set other parameters. For historical reasons, address 0 is not used.
+ if (address == straddress6_[0]) {
lease->type_ = Lease6::LEASE_IA_TA;
lease->prefixlen_ = 4;
lease->iaid_ = 142;
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x77)));
- lease->preferred_lft_ = 900; // Preferred lifetime
- lease->valid_lft_ = 8677; // Actual lifetime
- lease->cltt_ = 168256; // Current time of day
- lease->subnet_id_ = 23; // Arbitrary number
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x77)));
+ lease->preferred_lft_ = 900;
+ lease->valid_lft_ = 8677;
+ lease->cltt_ = 168256;
+ lease->subnet_id_ = 23;
- } else if (address == L1_ADDRESS) {
+ } else if (address == straddress6_[1]) {
lease->type_ = Lease6::LEASE_IA_TA;
lease->prefixlen_ = 0;
lease->iaid_ = 42;
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
- lease->preferred_lft_ = 3600; // Preferred lifetime
- lease->valid_lft_ = 3677; // Actual lifetime
- lease->cltt_ = 123456; // Current time of day
- lease->subnet_id_ = 73; // Arbitrary number
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ lease->preferred_lft_ = 3600;
+ lease->valid_lft_ = 3677;
+ lease->cltt_ = 123456;
+ lease->subnet_id_ = 73;
- } else if (address == L2_ADDRESS) {
+ } else if (address == straddress6_[2]) {
lease->type_ = Lease6::LEASE_IA_PD;
lease->prefixlen_ = 7;
lease->iaid_ = 89;
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x3a)));
- lease->preferred_lft_ = 1800; // Preferred lifetime
- lease->valid_lft_ = 5412; // Actual lifetime
- lease->cltt_ = 234567; // Current time of day
- lease->subnet_id_ = 73; // Same as for L1_ADDRESS
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x3a)));
+ lease->preferred_lft_ = 1800;
+ lease->valid_lft_ = 5412;
+ lease->cltt_ = 234567;
+ lease->subnet_id_ = 73; // Same as lease 1
- } else if (address == L3_ADDRESS) {
+ } else if (address == straddress6_[3]) {
lease->type_ = Lease6::LEASE_IA_NA;
lease->prefixlen_ = 28;
lease->iaid_ = 0xfffffffe;
@@ -279,60 +395,62 @@ public:
for (uint8_t i = 31; i < 126; ++i) {
duid.push_back(i);
}
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(duid));
+ lease->duid_ = DuidPtr(new DUID(duid));
// 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->preferred_lft_ = 7200; // Preferred lifetime
- lease->valid_lft_ = 7000; // Actual lifetime
- lease->cltt_ = 234567; // Current time of day
- lease->subnet_id_ = 37; // Different from L1 and L2
+ lease->preferred_lft_ = 7200;
+ lease->valid_lft_ = 7000;
+ lease->cltt_ = 234567;
+ lease->subnet_id_ = 37;
- } else if (address == L4_ADDRESS) {
- // Same DUID and IAID as L1_ADDRESS
+ } else if (address == straddress6_[4]) {
+ // Same DUID and IAID as straddress6_1
lease->type_ = Lease6::LEASE_IA_PD;
lease->prefixlen_ = 15;
lease->iaid_ = 42;
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
- lease->preferred_lft_ = 4800; // Preferred lifetime
- lease->valid_lft_ = 7736; // Actual lifetime
- lease->cltt_ = 222456; // Current time of day
- lease->subnet_id_ = 75; // Arbitrary number
-
- } else if (address == L5_ADDRESS) {
- // Same DUID and IAID as L1_ADDRESS
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ lease->preferred_lft_ = 4800;
+ lease->valid_lft_ = 7736;
+ lease->cltt_ = 222456;
+ lease->subnet_id_ = 671;
+
+ } else if (address == straddress6_[5]) {
+ // Same DUID and IAID as straddress6_1
lease->type_ = Lease6::LEASE_IA_PD;
lease->prefixlen_ = 24;
- lease->iaid_ = 42;
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
- lease->preferred_lft_ = 5400; // Preferred lifetime
- lease->valid_lft_ = 7832; // Actual lifetime
- lease->cltt_ = 227476; // Current time of day
- lease->subnet_id_ = 175; // Arbitrary number
-
- } else if (address == L6_ADDRESS) {
- // Same DUID as L1_ADDRESS
+ lease->iaid_ = 42; // Same as lease 4
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ // Same as lease 4
+ lease->preferred_lft_ = 5400;
+ lease->valid_lft_ = 7832;
+ lease->cltt_ = 227476;
+ lease->subnet_id_ = 175;
+
+ } else if (address == straddress6_[6]) {
+ // Same DUID as straddress6_1
lease->type_ = Lease6::LEASE_IA_PD;
lease->prefixlen_ = 24;
lease->iaid_ = 93;
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
- lease->preferred_lft_ = 5400; // Preferred lifetime
- lease->valid_lft_ = 1832; // Actual lifetime
- lease->cltt_ = 627476; // Current time of day
- lease->subnet_id_ = 112; // Arbitrary number
-
- } else if (address == L7_ADDRESS) {
- // Same IAID as L1_ADDRESS
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
+ // Same as lease 4
+ lease->preferred_lft_ = 5400;
+ lease->valid_lft_ = 1832;
+ lease->cltt_ = 627476;
+ lease->subnet_id_ = 112;
+
+ } else if (address == straddress6_[7]) {
+ // Same IAID as straddress6_1
lease->type_ = Lease6::LEASE_IA_PD;
lease->prefixlen_ = 24;
lease->iaid_ = 42;
- lease->duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xe5)));
- lease->preferred_lft_ = 5600; // Preferred lifetime
- lease->valid_lft_ = 7975; // Actual lifetime
- lease->cltt_ = 213876; // Current time of day
- lease->subnet_id_ = 19; // Arbitrary number
+ lease->duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0xe5)));
+ lease->preferred_lft_ = 5600;
+ lease->valid_lft_ = 7975;
+ lease->cltt_ = 213876;
+ lease->subnet_id_ = 19;
} else {
// Unknown address, return an empty pointer.
@@ -343,78 +461,130 @@ public:
return (lease);
}
- // @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
- vector<Lease6Ptr> leases;
- leases.push_back(initializeLease6(L0_ADDRESS));
- leases.push_back(initializeLease6(L1_ADDRESS));
- leases.push_back(initializeLease6(L2_ADDRESS));
- leases.push_back(initializeLease6(L3_ADDRESS));
- leases.push_back(initializeLease6(L4_ADDRESS));
- leases.push_back(initializeLease6(L5_ADDRESS));
- leases.push_back(initializeLease6(L6_ADDRESS));
- leases.push_back(initializeLease6(L7_ADDRESS));
-
- EXPECT_EQ(8, leases.size());
+ /// @brief Check Leases present and 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.
+ ///
+ /// @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]);
+ ASSERT_TRUE(leases[i]);
}
// Check they are different
for (int i = 0; i < (leases.size() - 1); ++i) {
for (int j = (i + 1); j < leases.size(); ++j) {
- EXPECT_TRUE(leases[i] != leases[j]);
+ stringstream s;
+ s << "Comparing leases " << i << " & " << j << " for equality";
+ SCOPED_TRACE(s.str());
+ 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());
- // Member variables
+ // Check all were created and that they are different.
+ checkLeasesDifferent(leases);
- LeaseMgr* lmptr_; // Pointer to the lease manager
+ return (leases);
+ }
- string L0_ADDRESS; // String form of address 1
- IOAddress L0_IOADDRESS; // IOAddress form of L1_ADDRESS
- string L1_ADDRESS; // String form of address 1
- IOAddress L1_IOADDRESS; // IOAddress form of L1_ADDRESS
+ // Member variables
- string L2_ADDRESS; // String form of address 2
- IOAddress L2_IOADDRESS; // IOAddress form of L2_ADDRESS
+ 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
+};
- string L3_ADDRESS; // String form of address 3
- IOAddress L3_IOADDRESS; // IOAddress form of L3_ADDRESS
+///@{
+/// @brief Test Utilities
+///
+/// The follow are a set of functions used during the tests.
- string L4_ADDRESS; // String form of address 4
- IOAddress L4_IOADDRESS; // IOAddress form of L4_ADDRESS
+/// @brief Compare two Lease4 structures for equality
+void
+detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second) {
+ // Compare address strings. Comparison of address objects is not used, as
+ // odd things happen when they are different: the EXPECT_EQ macro appears to
+ // call the operator uint32_t() function, which causes an exception to be
+ // thrown for IPv6 addresses.
+ EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
+ EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
+ EXPECT_TRUE(*first->client_id_ == *second->client_id_);
+ EXPECT_EQ(first->valid_lft_, second->valid_lft_);
+ EXPECT_EQ(first->cltt_, second->cltt_);
+ EXPECT_EQ(first->subnet_id_, second->subnet_id_);
+}
- string L5_ADDRESS; // String form of address 5
- IOAddress L5_IOADDRESS; // IOAddress form of L5_ADDRESS
+/// @brief Compare two Lease6 structures for equality
+void
+detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) {
+ EXPECT_EQ(first->type_, second->type_);
- string L6_ADDRESS; // String form of address 6
- IOAddress L6_IOADDRESS; // IOAddress form of L6_ADDRESS
+ // Compare address strings. Comparison of address objects is not used, as
+ // odd things happen when they are different: the EXPECT_EQ macro appears to
+ // call the operator uint32_t() function, which causes an exception to be
+ // thrown for IPv6 addresses.
+ EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
+ EXPECT_EQ(first->prefixlen_, second->prefixlen_);
+ EXPECT_EQ(first->iaid_, second->iaid_);
+ EXPECT_TRUE(*first->duid_ == *second->duid_);
+ EXPECT_EQ(first->preferred_lft_, second->preferred_lft_);
+ EXPECT_EQ(first->valid_lft_, second->valid_lft_);
+ EXPECT_EQ(first->cltt_, second->cltt_);
+ EXPECT_EQ(first->subnet_id_, second->subnet_id_);
+}
- string L7_ADDRESS; // String form of address 7
- IOAddress L7_IOADDRESS; // IOAddress form of L7_ADDRESS
-};
+///@}
-// @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.
+/// @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) {
@@ -472,25 +642,25 @@ TEST(MySqlOpenTest, OpenDatabase) {
destroySchema();
}
-// @brief Check the getType() method
-//
-// getType() returns a string giving the type of the backend, which should
-// always be "mysql".
+/// @brief Check the getType() method
+///
+/// getType() returns a string giving the type of the backend, which should
+/// always be "mysql".
TEST_F(MySqlLeaseMgrTest, getType) {
EXPECT_EQ(std::string("mysql"), lmptr_->getType());
}
-// @brief Check conversion functions
-//
-// The server works using cltt and valid_filetime. In the database, the
-// information is stored as expire_time and valid-lifetime, which are
-// related by
-//
-// expire_time = cltt + valid_lifetime
-//
-// This test checks that the conversion is correct. It does not check that the
-// data is entered into the database correctly, only that the MYSQL_TIME
-// structure used for the entry is correctly set up.
+/// @brief Check conversion functions
+///
+/// The server works using cltt and valid_filetime. In the database, the
+/// information is stored as expire_time and valid-lifetime, which are
+/// related by
+///
+/// expire_time = cltt + valid_lifetime
+///
+/// This test checks that the conversion is correct. It does not check that the
+/// data is entered into the database correctly, only that the MYSQL_TIME
+/// structure used for the entry is correctly set up.
TEST_F(MySqlLeaseMgrTest, checkTimeConversion) {
const time_t cltt = time(NULL);
const uint32_t valid_lft = 86400; // 1 day
@@ -522,14 +692,12 @@ TEST_F(MySqlLeaseMgrTest, checkTimeConversion) {
}
-// @brief Check getName() returns correct database name
+/// @brief Check getName() returns correct database name
TEST_F(MySqlLeaseMgrTest, getName) {
EXPECT_EQ(std::string("keatest"), lmptr_->getName());
-
- // @TODO: check for the negative
}
-// @brief Check that getVersion() returns the expected version
+/// @brief Check that getVersion() returns the expected version
TEST_F(MySqlLeaseMgrTest, checkVersion) {
// Check version
pair<uint32_t, uint32_t> version;
@@ -538,34 +706,54 @@ TEST_F(MySqlLeaseMgrTest, checkVersion) {
EXPECT_EQ(CURRENT_VERSION_MINOR, version.second);
}
-// @brief Compare two Lease6 structures for equality
-void
-detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
- EXPECT_EQ(first->type_, second->type_);
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4 (by address) and deleteLease4 works.
+TEST_F(MySqlLeaseMgrTest, basicLease4) {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
- // Compare address strings. Comparison of address objects is not used, as
- // odd things happen when they are different: the EXPECT_EQ macro appears to
- // call the operator uint32_t() function, which causes an exception to be
- // thrown for IPv6 addresses.
- EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
- EXPECT_EQ(first->prefixlen_, second->prefixlen_);
- EXPECT_EQ(first->iaid_, second->iaid_);
- EXPECT_TRUE(*first->duid_ == *second->duid_);
- EXPECT_EQ(first->preferred_lft_, second->preferred_lft_);
- EXPECT_EQ(first->valid_lft_, second->valid_lft_);
- EXPECT_EQ(first->cltt_, second->cltt_);
- EXPECT_EQ(first->subnet_id_, second->subnet_id_);
-}
+ // 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]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
-// @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.
+ l_returned = lmptr_->getLease4(ioaddress4_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease4(ioaddress4_[3]);
+ ASSERT_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]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
+}
+
+/// @brief Basic Lease6 Checks
+///
+/// Checks that the addLease, getLease6 (by address) and deleteLease6 works.
TEST_F(MySqlLeaseMgrTest, basicLease6) {
// Get the leases to be used for the test.
vector<Lease6Ptr> leases = createLeases6();
@@ -580,42 +768,360 @@ TEST_F(MySqlLeaseMgrTest, basicLease6) {
// Reopen the database to ensure that they actually got stored.
reopen();
- Lease6Ptr l_returned = lmptr_->getLease6(L1_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[1], l_returned);
+ Lease6Ptr l_returned = lmptr_->getLease6(ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
- l_returned = lmptr_->getLease6(L2_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[2], l_returned);
+ l_returned = lmptr_->getLease6(ioaddress6_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
- l_returned = lmptr_->getLease6(L3_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[3], l_returned);
+ l_returned = lmptr_->getLease6(ioaddress6_[3]);
+ ASSERT_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_->deleteLease6(L1_IOADDRESS));
- l_returned = lmptr_->getLease6(L1_IOADDRESS);
+ EXPECT_TRUE(lmptr_->deleteLease6(ioaddress6_[1]));
+ l_returned = lmptr_->getLease6(ioaddress6_[1]);
EXPECT_FALSE(l_returned);
- EXPECT_FALSE(lmptr_->deleteLease6(L1_IOADDRESS));
+ EXPECT_FALSE(lmptr_->deleteLease6(ioaddress6_[1]));
// Check that the second address is still there.
- l_returned = lmptr_->getLease6(L2_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[2], l_returned);
+ l_returned = lmptr_->getLease6(ioaddress6_[2]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[2], l_returned);
}
-// @brief Check GetLease6 methods - Access by DUID/IAID
+/// @brief Check GetLease4 methods - access by Address and SubnetID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a the hardware address
+TEST_F(MySqlLeaseMgrTest, getLease4AddressSubnetId) {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ const SubnetID lease1_subnetid = leases[1]->subnet_id_;
+
+ // Generate a Subnet ID known to be invalid - one more than the maximum
+ // Subnet ID in all the leases.
+ SubnetID invalid_subnetid = 0;
+ for (int i = 0; i < leases.size(); ++i) {
+ invalid_subnetid = max(invalid_subnetid, leases[i]->subnet_id_);
+ }
+ ++invalid_subnetid;
+
+
+ // Add just one to the database.
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+
+ // Look for a known lease with a valid Subnet ID
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1], lease1_subnetid);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Look for a lease known to be in the database with an invalid Subnet ID
+ l_returned = lmptr_->getLease4(ioaddress4_[1], invalid_subnetid);
+ EXPECT_FALSE(l_returned);
+
+ // Look for a lease known not to be in the database with a valid Subnet ID
+ l_returned = lmptr_->getLease4(ioaddress4_[2], lease1_subnetid);
+ EXPECT_FALSE(l_returned);
+
+ // Look for a lease known not to be in the database with and invalid
+ l_returned = lmptr_->getLease4(ioaddress4_[2], invalid_subnetid);
+ EXPECT_FALSE(l_returned);
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(MySqlLeaseMgrTest, getLease4Hwaddr) {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the hardware address of lease 1
+ Lease4Collection returned = lmptr_->getLease4(leases[1]->hwaddr_);
+
+ // Should be three leases, matching leases[1], [3] and [5].
+ ASSERT_EQ(3, returned.size());
+
+ // Easiest way to check is to look at the addresses.
+ vector<string> addresses;
+ for (Lease4Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ addresses.push_back((*i)->addr_.toText());
+ }
+ sort(addresses.begin(), addresses.end());
+ EXPECT_EQ(straddress4_[1], addresses[0]);
+ EXPECT_EQ(straddress4_[3], addresses[1]);
+ EXPECT_EQ(straddress4_[5], addresses[2]);
+
+ // Repeat test with just one expected match
+ returned = lmptr_->getLease4(leases[2]->hwaddr_);
+ EXPECT_EQ(1, returned.size());
+ detailCompareLease(leases[2], *returned.begin());
+
+ // Check that an empty vector is valid
+ EXPECT_TRUE(leases[7]->hwaddr_.empty());
+ returned = lmptr_->getLease4(leases[7]->hwaddr_);
+ EXPECT_EQ(1, returned.size());
+ detailCompareLease(leases[7], *returned.begin());
+
+ // Try to get something with invalid hardware address
+ vector<uint8_t> invalid(6, 0);
+ returned = lmptr_->getLease4(invalid);
+ EXPECT_EQ(0, returned.size());
+}
+
+// @brief Get lease4 by hardware address (2)
+//
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSize) {
+
+ // Create leases, although we need only one.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Now add leases with increasing hardware address size.
+ for (uint8_t i = 0; i <= Lease4::HWADDR_MAX; ++i) {
+ leases[1]->hwaddr_.resize(i, i);
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Collection returned = lmptr_->getLease4(leases[1]->hwaddr_);
+ ASSERT_EQ(1, returned.size());
+ detailCompareLease(leases[1], *returned.begin());
+ (void) lmptr_->deleteLease4(leases[1]->addr_);
+ }
+
+ // Expect some problem when accessing a lease that had too long a hardware
+ // address. (The 42 is a random value put in each byte of the address.)
+ // In fact the address is stored in a truncated form, so we won't find it
+ // when we look.
+ // @todo Check if there is some way of detecting that data added
+ // to the database is truncated. There does not appear to
+ // be any indication in the C API.
+ leases[1]->hwaddr_.resize(Lease4::HWADDR_MAX + 100, 42);
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Collection returned = lmptr_->getLease4(leases[1]->hwaddr_);
+ EXPECT_EQ(0, returned.size());
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of hardware address and subnet ID
+TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetId) {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the hardware address of lease 1 and
+ // subnet ID of lease 1. Result should be a single lease - lease 1.
+ Lease4Ptr returned = lmptr_->getLease4(leases[1]->hwaddr_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+
+ // Try for a match to the hardware address of lease 1 and the wrong
+ // subnet ID.
+ returned = lmptr_->getLease4(leases[1]->hwaddr_,
+ leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to the subnet ID of lease 1 (and lease 4) but
+ // the wrong hardware address.
+ vector<uint8_t> invalid_hwaddr(15, 0x77);
+ returned = lmptr_->getLease4(invalid_hwaddr,
+ leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to an unknown hardware address and an unknown
+ // subnet ID.
+ returned = lmptr_->getLease4(invalid_hwaddr,
+ leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Add a second lease with the same values as the first and check that
+ // an attempt to access the database by these parameters throws a
+ // "multiple records" exception. (We expect there to be only one record
+ // with that combination, so getting them via getLeaseX() (as opposed
+ // to getLeaseXCollection() should throw an exception.)
+ EXPECT_TRUE(lmptr_->deleteLease4(leases[2]->addr_));
+ leases[1]->addr_ = leases[2]->addr_;
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ EXPECT_THROW(returned = lmptr_->getLease4(leases[1]->hwaddr_,
+ leases[1]->subnet_id_),
+ isc::dhcp::MultipleRecords);
+
+ // Delete all leases in the database
+ for (int i = 0; ADDRESS4[i] != NULL; ++i) {
+ IOAddress addr(ADDRESS4[i]);
+ (void) lmptr_->deleteLease4(addr);
+ }
+}
+
+// @brief Get lease4 by hardware address and subnet ID (2)
//
-// Adds leases to the database and checks that they can be accessed via
-// a combination of DIUID and IAID.
-TEST_F(MySqlLeaseMgrTest, getLease6Extended1) {
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(MySqlLeaseMgrTest, getLease4HwaddrSubnetIdSize) {
+
+ // Create leases, although we need only one.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Now add leases with increasing hardware address size and check
+ // that they can be retrieved.
+ for (uint8_t i = 0; i <= Lease4::HWADDR_MAX; ++i) {
+ leases[1]->hwaddr_.resize(i, i);
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Ptr returned = lmptr_->getLease4(leases[1]->hwaddr_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+ (void) lmptr_->deleteLease4(leases[1]->addr_);
+ }
+
+ // Expect some error when getting a lease with too long a hardware
+ // address. Set the contents of each byte to 42, a random value.
+ // @todo Check if there is some way of detecting that data added
+ // to the database is truncated. There does not appear to
+ // be any indication in the C API.
+ leases[1]->hwaddr_.resize(Lease4::HWADDR_MAX + 100, 42);
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Ptr returned = lmptr_->getLease4(leases[1]->hwaddr_,
+ leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+}
+
+/// @brief Check GetLease4 methods - access by Client ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// the Client ID.
+TEST_F(MySqlLeaseMgrTest, getLease4ClientId) {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the Client ID address of lease 1
+ Lease4Collection returned = lmptr_->getLease4(*leases[1]->client_id_);
+
+ // Should be four leases, matching leases[1], [4], [5] and [6].
+ ASSERT_EQ(4, returned.size());
+
+ // Easiest way to check is to look at the addresses.
+ vector<string> addresses;
+ for (Lease4Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ addresses.push_back((*i)->addr_.toText());
+ }
+ sort(addresses.begin(), addresses.end());
+ EXPECT_EQ(straddress4_[1], addresses[0]);
+ EXPECT_EQ(straddress4_[4], addresses[1]);
+ EXPECT_EQ(straddress4_[5], addresses[2]);
+ EXPECT_EQ(straddress4_[6], addresses[3]);
+
+ // Repeat test with just one expected match
+ returned = lmptr_->getLease4(*leases[3]->client_id_);
+ EXPECT_EQ(1, returned.size());
+ detailCompareLease(leases[3], *returned.begin());
+
+ // Check that an empty vector is valid
+ EXPECT_TRUE(leases[7]->client_id_->getClientId().empty());
+ returned = lmptr_->getLease4(leases[7]->hwaddr_);
+ EXPECT_EQ(1, returned.size());
+ detailCompareLease(leases[7], *returned.begin());
+
+ // Try to get something with invalid client ID
+ const uint8_t invalid_data[] = {0, 0, 0};
+ ClientId invalid(invalid_data, sizeof(invalid_data));
+ returned = lmptr_->getLease4(invalid);
+ EXPECT_EQ(0, returned.size());
+}
+
+// @brief Get Lease4 by client ID (2)
+//
+// Check that the system can cope with a client ID of any size.
+TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSize) {
+
+ // Create leases, although we need only one.
+ vector<Lease4Ptr> leases = createLeases4();
+
+ // Now add leases with increasing Client ID size can be retrieved.
+ // For speed, go from 0 to 128 is steps of 16.
+ // Intermediate client_id_max is to overcome problem if
+ // ClientId::MAX_CLIENT_ID_LEN is used in an EXPECT_EQ.
+ int client_id_max = ClientId::MAX_CLIENT_ID_LEN;
+ EXPECT_EQ(128, client_id_max);
+ for (uint8_t i = 0; i <= client_id_max; i += 16) {
+ vector<uint8_t> clientid_vec(i, i);
+ leases[1]->client_id_.reset(new ClientId(clientid_vec));
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease4Collection returned = lmptr_->getLease4(*leases[1]->client_id_);
+ ASSERT_TRUE(returned.size() == 1);
+ detailCompareLease(leases[1], *returned.begin());
+ (void) lmptr_->deleteLease4(leases[1]->addr_);
+ }
+
+ // Don't bother to check client IDs longer than the maximum -
+ // these cannot be constructed, and that limitation is tested
+ // in the DUID/Client ID unit tests.
+}
+
+/// @brief Check GetLease4 methods - access by Client ID & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of client and subnet IDs.
+TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSubnetId) {
+ // Get the leases to be used for the test and add to the database
+ vector<Lease4Ptr> leases = createLeases4();
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the client ID of lease 1 and
+ // subnet ID of lease 1. Result should be a single lease - lease 1.
+ Lease4Ptr returned = lmptr_->getLease4(*leases[1]->client_id_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+
+ // Try for a match to the client ID of lease 1 and the wrong
+ // subnet ID.
+ returned = lmptr_->getLease4(*leases[1]->client_id_,
+ leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to the subnet ID of lease 1 (and lease 4) but
+ // the wrong client ID
+ const uint8_t invalid_data[] = {0, 0, 0};
+ ClientId invalid(invalid_data, sizeof(invalid_data));
+ returned = lmptr_->getLease4(invalid, leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+
+ // Try for a match to an unknown hardware address and an unknown
+ // subnet ID.
+ returned = lmptr_->getLease4(invalid, leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaid) {
// Get the leases to be used for the test.
vector<Lease6Ptr> leases = createLeases6();
- EXPECT_LE(6, leases.size()); // Expect to access leases 0 through 5
+ ASSERT_LE(6, leases.size()); // Expect to access leases 0 through 5
// Add them to the database
for (int i = 0; i < leases.size(); ++i) {
@@ -636,9 +1142,9 @@ TEST_F(MySqlLeaseMgrTest, getLease6Extended1) {
addresses.push_back((*i)->addr_.toText());
}
sort(addresses.begin(), addresses.end());
- EXPECT_EQ(L1_ADDRESS, addresses[0]);
- EXPECT_EQ(L4_ADDRESS, addresses[1]);
- EXPECT_EQ(L5_ADDRESS, addresses[2]);
+ EXPECT_EQ(straddress6_[1], addresses[0]);
+ EXPECT_EQ(straddress6_[4], addresses[1]);
+ EXPECT_EQ(straddress6_[5], addresses[2]);
// Check that nothing is returned when either the IAID or DUID match
// nothing.
@@ -653,18 +1159,41 @@ TEST_F(MySqlLeaseMgrTest, getLease6Extended1) {
EXPECT_EQ(0, returned.size());
}
-
-
-// @brief Check GetLease6 methods - Access by DUID/IAID/SubnetID
+// @brief Get Lease4 by DUID and IAID (2)
//
-// Adds leases to the database and checks that they can be accessed via
-// a combination of DIUID and IAID.
-TEST_F(MySqlLeaseMgrTest, getLease6Extended2) {
- // Get the leases to be used for the test.
+// Check that the system can cope with a DUID of any size.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSize) {
+
+ // Create leases, although we need only one.
vector<Lease6Ptr> leases = createLeases6();
- EXPECT_LE(6, leases.size()); // Expect to access leases 0 through 5
- // Add them to the database
+ // Now add leases with increasing DUID size can be retrieved.
+ // For speed, go from 0 to 128 is steps of 16.
+ int duid_max = DUID::MAX_DUID_LEN;
+ EXPECT_EQ(128, duid_max);
+ for (uint8_t i = 0; i <= duid_max; i += 16) {
+ vector<uint8_t> duid_vec(i, i);
+ leases[1]->duid_.reset(new DUID(duid_vec));
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease6Collection returned = lmptr_->getLease6(*leases[1]->duid_,
+ leases[1]->iaid_);
+ EXPECT_EQ(1, returned.size());
+ detailCompareLease(leases[1], *returned.begin());
+ (void) lmptr_->deleteLease6(leases[1]->addr_);
+ }
+
+ // Don't bother to check DUIDs longer than the maximum - these cannot be
+ // constructed, and that limitation is tested in the DUID/Client ID unit
+ // tests.
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID/SubnetID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetId) {
+ // Get the leases to be used for the test and add them to the database.
+ vector<Lease6Ptr> leases = createLeases6();
for (int i = 0; i < leases.size(); ++i) {
EXPECT_TRUE(lmptr_->addLease(leases[i]));
}
@@ -695,24 +1224,94 @@ TEST_F(MySqlLeaseMgrTest, getLease6Extended2) {
EXPECT_FALSE(returned);
}
+// @brief Get Lease4 by DUID, IAID & subnet ID (2)
+//
+// Check that the system can cope with a DUID of any size.
+TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
+ // Create leases, although we need only one.
+ vector<Lease6Ptr> leases = createLeases6();
-// @brief Lease6 Update Tests
-//
-// Checks that we are able to update a lease in the database.
+ // Now add leases with increasing DUID size can be retrieved.
+ // For speed, go from 0 to 128 is steps of 16.
+ int duid_max = DUID::MAX_DUID_LEN;
+ EXPECT_EQ(128, duid_max);
+ for (uint8_t i = 0; i <= duid_max; i += 16) {
+ vector<uint8_t> duid_vec(i, i);
+ leases[1]->duid_.reset(new DUID(duid_vec));
+ EXPECT_TRUE(lmptr_->addLease(leases[1]));
+ Lease6Ptr returned = lmptr_->getLease6(*leases[1]->duid_,
+ leases[1]->iaid_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ detailCompareLease(leases[1], returned);
+ (void) lmptr_->deleteLease6(leases[1]->addr_);
+ }
+
+ // Don't bother to check DUIDs longer than the maximum - these cannot be
+ // constructed, and that limitation is tested in the DUID/Client ID unit
+ // tests.
+}
+
+/// @brief Lease4 update tests
+///
+/// Checks that we are able to update a lease in the database.
+TEST_F(MySqlLeaseMgrTest, updateLease4) {
+ // Get the leases to be used for the test and add them to the database.
+ vector<Lease4Ptr> leases = createLeases4();
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Modify some fields in lease 1 (not the address) and update it.
+ ++leases[1]->subnet_id_;
+ leases[1]->valid_lft_ *= 2;
+ lmptr_->updateLease4(leases[1]);
+
+ // ... and check what is returned is what is expected.
+ Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Alter the lease again and check.
+ ++leases[1]->subnet_id_;
+ leases[1]->cltt_ += 6;
+ lmptr_->updateLease4(leases[1]);
+
+ // Explicitly clear the returned pointer before getting new data to ensure
+ // that the new data is returned.
+ l_returned.reset();
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Check we can do an update without changing data.
+ lmptr_->updateLease4(leases[1]);
+ l_returned.reset();
+ l_returned = lmptr_->getLease4(ioaddress4_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
+
+ // Try updating a lease not in the database.
+ lmptr_->deleteLease4(ioaddress4_[2]);
+ EXPECT_THROW(lmptr_->updateLease4(leases[2]), isc::dhcp::NoSuchLease);
+}
+
+/// @brief Lease6 update tests
+///
+/// Checks that we are able to update a lease in the database.
TEST_F(MySqlLeaseMgrTest, updateLease6) {
// Get the leases to be used for the test.
vector<Lease6Ptr> leases = createLeases6();
- EXPECT_LE(3, leases.size()); // Expect to access leases 0 through 5
+ ASSERT_LE(3, leases.size()); // Expect to access leases 0 through 2
// Add a lease to the database and check that the lease is there.
EXPECT_TRUE(lmptr_->addLease(leases[1]));
lmptr_->commit();
- reopen();
- Lease6Ptr l_returned = lmptr_->getLease6(L1_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[1], l_returned);
+ Lease6Ptr l_returned = lmptr_->getLease6(ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
// Modify some fields in lease 1 (not the address) and update it.
++leases[1]->iaid_;
@@ -720,13 +1319,12 @@ TEST_F(MySqlLeaseMgrTest, updateLease6) {
leases[1]->valid_lft_ *= 2;
lmptr_->updateLease6(leases[1]);
lmptr_->commit();
- reopen();
// ... and check what is returned is what is expected.
l_returned.reset();
- l_returned = lmptr_->getLease6(L1_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[1], l_returned);
+ l_returned = lmptr_->getLease6(ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
// Alter the lease again and check.
++leases[1]->iaid_;
@@ -736,16 +1334,16 @@ TEST_F(MySqlLeaseMgrTest, updateLease6) {
lmptr_->updateLease6(leases[1]);
l_returned.reset();
- l_returned = lmptr_->getLease6(L1_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[1], l_returned);
+ l_returned = lmptr_->getLease6(ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
// Check we can do an update without changing data.
lmptr_->updateLease6(leases[1]);
l_returned.reset();
- l_returned = lmptr_->getLease6(L1_IOADDRESS);
- EXPECT_TRUE(l_returned);
- detailCompareLease6(leases[1], l_returned);
+ l_returned = lmptr_->getLease6(ioaddress6_[1]);
+ ASSERT_TRUE(l_returned);
+ detailCompareLease(leases[1], l_returned);
// Try updating a lease not in the database.
EXPECT_THROW(lmptr_->updateLease6(leases[2]), isc::dhcp::NoSuchLease);
diff --git a/src/lib/dhcpsrv/tests/schema_copy.h b/src/lib/dhcpsrv/tests/schema_copy.h
index 1dd796d..48a11ca 100644
--- a/src/lib/dhcpsrv/tests/schema_copy.h
+++ b/src/lib/dhcpsrv/tests/schema_copy.h
@@ -25,7 +25,9 @@ namespace {
// by semicolons, and the strings must end with a comma. The final line
// statement must be NULL (not in quotes)
-// THIS MUST BE KEPT UP TO DATE AND UPDATED IF THE SCHEMA CHANGES
+// NOTE: This file mirrors the schema in src/lib/dhcpsrv/dhcpdb_create.mysql.
+// If this file is altered, please ensure that any change is compatible
+// with the schema in dhcpdb_create.mysql.
// Deletion of existing tables.
@@ -44,13 +46,13 @@ const char* create_statement[] = {
"address INT UNSIGNED PRIMARY KEY NOT NULL,"
"hwaddr VARBINARY(20),"
"client_id VARBINARY(128),"
- "lease_time INT UNSIGNED,"
+ "valid_lifetime INT UNSIGNED,"
"expire TIMESTAMP,"
"subnet_id INT UNSIGNED"
") ENGINE = INNODB",
"CREATE TABLE lease6 ("
- "address VARCHAR(40) PRIMARY KEY NOT NULL,"
+ "address VARCHAR(39) PRIMARY KEY NOT NULL,"
"duid VARBINARY(128),"
"valid_lifetime INT UNSIGNED,"
"expire TIMESTAMP,"
@@ -75,7 +77,7 @@ const char* create_statement[] = {
"minor INT"
")",
- "INSERT INTO schema_version VALUES (0, 1)",
+ "INSERT INTO schema_version VALUES (1, 0)",
NULL
};
More information about the bind10-changes
mailing list