BIND 10 master, updated. fa362e401a2f5830a3a7bd130dda2d8f0fae3972 [master] ChangeLog for trac2342
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Nov 12 19:18:13 UTC 2012
The branch, master has been updated
via fa362e401a2f5830a3a7bd130dda2d8f0fae3972 (commit)
via c7defffb89bd0f3fdd7ad2437c78950bcb86ad37 (commit)
via 5520594354d6170126103c973b77dc017b4b0285 (commit)
via 4e7a8f7575704052e70432f1e2508d310e8f6bb0 (commit)
via fb185ed8c6c0b67fe710225c87b7e106cc405885 (commit)
via bdb8039cc69c3d7bdc844126aab12ce28b20a135 (commit)
via 1cee186c837d355ef28b00773bbb145e75444b85 (commit)
via b0ad16d99eb0bc2148976a31c4dbf7ff0ed5516a (commit)
via e68fed5260c3e7f14b596ff8ea897257c9a634d0 (commit)
via 9fa65c7819dc3b166da2e639214f3905106c3a6b (commit)
via 3edde39acb177b96c1aaa74f42562c4b7ab0488f (commit)
via d141c47a59c217c07e377a988ed92fa1cb9a3314 (commit)
via 3dc78f0e8380b2d5e21c8e07f392a8dde4c3d456 (commit)
via 2d399aa7d71950c6424ed6deddb6c74f5e102525 (commit)
via 1ed8ce0cc009b89158583d462e96b42e160dbe3e (commit)
via eb211d7cfc40962bd64193b3a91ecd2560edf3f3 (commit)
via 0a14e05f428a3c6494a6f92f032ca9ecbb401919 (commit)
via 434286943084a3b35f13811179f72ea72d675fb4 (commit)
via a0f0141e8e52b1ebc6f0bfa17b13d6b98da3e72c (commit)
via 89c8076272fb81a9e49b3bdbcd704103707db83f (commit)
via 45feb03e15fa555b846d0b8dfa4f8c88a1d50d64 (commit)
via df141d203ebfefd11828290020db8f9177ef9908 (commit)
via 08daa03beac5d16305734fc434edcc28962205e9 (commit)
via 39c82ec533e3642015ce742a132b31350e91c928 (commit)
via 3e52b2fec4e3fed3898964ad0ae10d899b31c8bb (commit)
via 942be75acdf13e402a5dc5582a16d782ac23d4eb (commit)
via b0ed08d21421ca321502cda1e9811a4bfe560275 (commit)
via 15bc004f2076bd04df69df0efa59728b795ee7c5 (commit)
via 47be873b787d2a1a355b04cb6e4ec5dce905f180 (commit)
via 1d70d3df4d008f881ab31dfed3e8805da63a3c65 (commit)
via fdbdac116c417a4e3236669554da431c66cbea09 (commit)
via 599bfb2239028e494c88143cc9c0a708f8b7f24b (commit)
via a64f318cb0ea2ea0ef73145071fb04b85f5853ec (commit)
via 93ffcbb3ecdb4f882bb833addb68908446f7ae77 (commit)
via c7cb0c33363a126c12a6df098295b06b8921e7f6 (commit)
via 68953b80a3f7155b10728dabc583f9e5bbe080ed (commit)
via 6b45e5c026a844d688367308bd0b4e70b568b2e5 (commit)
via cc65a33e2a84e88a4c1703d360d18187b09a8850 (commit)
from a78bb9934e09d0d98a670f3b61a8c66446806ecc (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 fa362e401a2f5830a3a7bd130dda2d8f0fae3972
Author: Stephen Morris <stephen at isc.org>
Date: Mon Nov 12 19:17:51 2012 +0000
[master] ChangeLog for trac2342
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 9 +
configure.ac | 66 ++
doc/devel/mainpage.dox | 1 +
src/bin/dhcp6/dhcp6_srv.cc | 35 +-
src/bin/dhcp6/dhcp6_srv.h | 5 +-
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 5 +-
src/lib/dhcp/Makefile.am | 34 +-
src/lib/dhcp/addr_utilities.h | 5 +
src/lib/dhcp/alloc_engine.cc | 11 +-
src/lib/dhcp/cfgmgr.h | 2 +-
src/lib/dhcp/database_backends.dox | 64 ++
src/lib/dhcp/dhcp6.h | 2 +-
src/lib/dhcp/dhcpdb_create.mysql | 115 ++
src/lib/dhcp/iface_mgr.h | 2 +-
src/lib/dhcp/iface_mgr_linux.cc | 2 +-
src/lib/dhcp/lease_mgr.cc | 109 +-
src/lib/dhcp/lease_mgr.h | 185 ++--
src/lib/dhcp/lease_mgr_factory.cc | 117 ++
src/lib/dhcp/lease_mgr_factory.h | 123 +++
src/lib/dhcp/libdhcp++.h | 10 +-
src/lib/dhcp/memfile_lease_mgr.cc | 22 +-
src/lib/dhcp/memfile_lease_mgr.h | 39 +-
src/lib/dhcp/mysql_lease_mgr.cc | 1139 ++++++++++++++++++++
src/lib/dhcp/mysql_lease_mgr.h | 444 ++++++++
src/lib/dhcp/option.h | 18 +-
src/lib/dhcp/tests/Makefile.am | 39 +-
src/lib/dhcp/tests/alloc_engine_unittest.cc | 38 +-
src/lib/dhcp/tests/lease_mgr_factory_unittest.cc | 50 +
src/lib/dhcp/tests/lease_mgr_unittest.cc | 309 ++++--
...r_unittest.cc => memfile_lease_mgr_unittest.cc} | 105 +-
src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc | 732 +++++++++++++
src/lib/dhcp/tests/schema_copy.h | 85 ++
32 files changed, 3496 insertions(+), 426 deletions(-)
create mode 100644 src/lib/dhcp/database_backends.dox
create mode 100644 src/lib/dhcp/dhcpdb_create.mysql
create mode 100644 src/lib/dhcp/lease_mgr_factory.cc
create mode 100644 src/lib/dhcp/lease_mgr_factory.h
create mode 100644 src/lib/dhcp/mysql_lease_mgr.cc
create mode 100644 src/lib/dhcp/mysql_lease_mgr.h
create mode 100644 src/lib/dhcp/tests/lease_mgr_factory_unittest.cc
copy src/lib/dhcp/tests/{lease_mgr_unittest.cc => memfile_lease_mgr_unittest.cc} (51%)
create mode 100644 src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc
create mode 100644 src/lib/dhcp/tests/schema_copy.h
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 99b8e45..0bd6020 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+503. [func] Stephen
+ Add initial version of a MySQL backend for the DHCP code. This
+ implements the basic IPv6 lease access functions - add lease, delete
+ lease and update lease. The backend is enabled by specifying
+ --with-dhcp-mysql on the "configure" command line: without this
+ switch, the MySQL code is not compiled, so leaving BIND 10 able to
+ be built on systems without MySQL installed.
+ (Trac #2342, git c7defffb89bd0f3fdd7ad2437c78950bcb86ad37)
+
502. [func] vorner
TTLs can be specified with units as well as number of seconds now.
This allows specifications like "1D3H".
diff --git a/configure.ac b/configure.ac
index 754d667..f432499 100644
--- a/configure.ac
+++ b/configure.ac
@@ -733,6 +733,60 @@ AC_LINK_IFELSE(
CPPFLAGS=$CPPFLAGS_SAVED
LIBS=$LIBS_SAVED
+# Check for MySql. The path to the mysql_config program is given with
+# the --with-mysql-config (default to /usr/bin/mysql-config). By default,
+# the software is not built with MySQL support enabled.
+mysql_config="no"
+AC_ARG_WITH([dhcp-mysql],
+ AC_HELP_STRING([--with-dhcp-mysql=PATH],
+ [path to the MySQL 'mysql_config' script (MySQL is used for the DHCP database)]),
+ [mysql_config="$withval"])
+
+if test "${mysql_config}" = "yes" ; then
+ MYSQL_CONFIG="/usr/bin/mysql_config"
+elif test "${mysql_config}" != "no" ; then
+ MYSQL_CONFIG="${withval}"
+fi
+
+if test "$MYSQL_CONFIG" != "" ; then
+ if test -d "$MYSQL_CONFIG" -o ! -x "$MYSQL_CONFIG" ; then
+ AC_MSG_ERROR([--with-dhcp-mysql should point to a mysql_config program])
+ fi
+
+ MYSQL_CPPFLAGS=`$MYSQL_CONFIG --cflags`
+ MYSQL_LIBS=`$MYSQL_CONFIG --libs`
+
+ AC_SUBST(MYSQL_CPPFLAGS)
+ AC_SUBST(MYSQL_LIBS)
+
+ # Check that a simple program using MySQL functions can compile and link.
+ CPPFLAGS_SAVED="$CPPFLAGS"
+ LIBS_SAVED="$LIBS"
+
+ CPPFLAGS="$MYSQL_CPPFLAGS $CPPFLAGS"
+ LIBS="$MYSQL_LIBS $LIBS"
+
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([#include <mysql/mysql.h>],
+ [MYSQL mysql_handle;
+ (void) mysql_init(&mysql_handle);
+ ])],
+ [AC_MSG_RESULT([checking for MySQL headers and library... yes])],
+ [AC_MSG_RESULT([checking for MySQL headers and library... no])
+ AC_MSG_ERROR([Needs MySQL library])]
+ )
+
+ CPPFLAGS=$CPPFLAGS_SAVED
+ LIBS=$LIBS_SAVED
+
+ # Note that MYSQL is present in the config.h file
+ AC_DEFINE([HAVE_MYSQL], [1], [MySQL is present])
+fi
+
+# ... and at the shell level, so Makefile.am can take action depending on this.
+AM_CONDITIONAL(HAVE_MYSQL, test "$MYSQL_CONFIG" != "")
+
+
# Check for log4cplus
log4cplus_path="yes"
AC_ARG_WITH([log4cplus],
@@ -1421,6 +1475,18 @@ dnl includes too
${LOG4CPLUS_LIBS}
SQLite: $SQLITE_CFLAGS
$SQLITE_LIBS
+END
+
+# Avoid confusion on DNS/DHCP and only mention MySQL if it
+# were specified on the command line.
+if test "$MYSQL_CPPFLAGS" != "" ; then
+cat >> config.report << END
+ MySQL: $MYSQL_CPPFLAGS
+ $MYSQL_LIBS
+END
+fi
+
+cat >> config.report << END
Features:
$enable_features
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index e1761ec..db42c14 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -33,6 +33,7 @@
* - @subpage leasemgr
* - @subpage cfgmgr
* - @subpage allocengine
+ * - @subpage dhcp-database-backends
* - @subpage perfdhcpInternals
*
* @section misc Miscellaneous topics
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 7b1f721..1c76b75 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <config.h>
+
#include <stdlib.h>
#include <time.h>
@@ -33,13 +35,10 @@
#include <util/range_utilities.h>
#include <dhcp/duid.h>
#include <dhcp/lease_mgr.h>
+#include <dhcp/lease_mgr_factory.h>
#include <dhcp/cfgmgr.h>
#include <dhcp/option6_iaaddr.h>
-// @todo: Replace this with MySQL_LeaseMgr (or a LeaseMgr factory)
-// once it is merged
-#include <dhcp/memfile_lease_mgr.h>
-
#include <boost/foreach.hpp>
using namespace isc;
@@ -51,8 +50,8 @@ using namespace std;
namespace isc {
namespace dhcp {
-Dhcpv6Srv::Dhcpv6Srv(uint16_t port) : alloc_engine_(), serverid_(),
- shutdown_(false) {
+Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
+ : alloc_engine_(), serverid_(), shutdown_(true) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
@@ -71,7 +70,6 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) : alloc_engine_(), serverid_(),
if (port > 0) {
if (IfaceMgr::instance().countIfaces() == 0) {
LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
- shutdown_ = true;
return;
}
IfaceMgr::instance().openSockets6(port);
@@ -79,28 +77,27 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) : alloc_engine_(), serverid_(),
setServerID();
+ // Instantiate LeaseMgr
+ LeaseMgrFactory::create(dbconfig);
+ LOG_INFO(dhcp6_logger, DHCP6_DB_BACKEND_STARTED)
+ .arg(LeaseMgrFactory::instance().getName());
+
+ // Instantiate allocation engine
+ alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+
} catch (const std::exception &e) {
LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
- shutdown_ = true;
return;
}
- // Instantiate LeaseMgr
- // @todo: Replace this with MySQL_LeaseMgr (or a LeaseMgr factory)
- // once it is merged
- new isc::dhcp::test::Memfile_LeaseMgr("");
-
- LOG_INFO(dhcp6_logger, DHCP6_DB_BACKEND_STARTED)
- .arg(LeaseMgr::instance().getName());
-
- // Instantiate allocation engine
- alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+ // All done, so can proceed
+ shutdown_ = false;
}
Dhcpv6Srv::~Dhcpv6Srv() {
IfaceMgr::instance().closeSockets();
- LeaseMgr::destroy_instance();
+ LeaseMgrFactory::destroy();
}
void Dhcpv6Srv::shutdown() {
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 6574226..f4f0f0c 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -57,7 +57,10 @@ public:
/// old or create new DUID.
///
/// @param port port on will all sockets will listen
- Dhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT);
+ /// @param dbconfig Lease manager configuration string. The default
+ /// of the "memfile" manager is used for testing.
+ Dhcpv6Srv(uint16_t port = DHCP6_SERVER_PORT,
+ const char* dbconfig = "type=memfile");
/// @brief Destructor. Used during DHCPv6 service shutdown.
virtual ~Dhcpv6Srv();
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index f1cf7de..afb05d1 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -27,6 +27,7 @@
#include <dhcp/dhcp6.h>
#include <dhcp/duid.h>
#include <dhcp/lease_mgr.h>
+#include <dhcp/lease_mgr_factory.h>
#include <dhcp/option.h>
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_ia.h>
@@ -166,7 +167,7 @@ public:
boost::shared_ptr<Option6IAAddr> addr) {
boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);
- Lease6Ptr lease = LeaseMgr::instance().getLease6(addr->getAddress());
+ Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(addr->getAddress());
if (!lease) {
cout << "Lease for " << addr->getAddress().toText()
<< " not found in the database backend.";
@@ -673,7 +674,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
// check that the lease is really in the database
Lease6Ptr l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
EXPECT_TRUE(l);
- LeaseMgr::instance().deleteLease6(addr->getAddress());
+ LeaseMgrFactory::instance().deleteLease6(addr->getAddress());
}
// This test checks that the server is offering different addresses to different
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 0d54a07..017a13e 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -2,6 +2,9 @@ SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
+if HAVE_MYSQL
+AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
+endif
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -17,8 +20,8 @@ lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la
libb10_dhcp___la_SOURCES =
libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
-libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
+libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
libb10_dhcp___la_SOURCES += iface_mgr_sun.cc
libb10_dhcp___la_SOURCES += option.cc option.h
libb10_dhcp___la_SOURCES += option_data_types.h
@@ -32,7 +35,6 @@ libb10_dhcp___la_SOURCES += option6_int_array.h
libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
-libb10_dhcp___la_SOURCES += duid.cc duid.h
libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
@@ -40,26 +42,38 @@ libb10_dhcp___la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libb10_dhcp___la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
libb10_dhcp___la_LDFLAGS = -no-undefined -version-info 2:0:0
-libb10_dhcpsrv_la_SOURCES = cfgmgr.cc cfgmgr.h
+libb10_dhcpsrv_la_SOURCES =
+libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
+libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
+libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
+libb10_dhcpsrv_la_SOURCES += duid.cc duid.h
+libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
+libb10_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
+libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
+if HAVE_MYSQL
+libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
+endif
libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
libb10_dhcpsrv_la_SOURCES += triplet.h
-libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
-libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
-libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
-libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
-libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
libb10_dhcpsrv_la_LDFLAGS = -no-undefined -version-info 2:0:0
+if HAVE_MYSQL
+libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
+endif
-EXTRA_DIST = README
+EXTRA_DIST = README database_backends.dox
if USE_CLANGPP
# Disable unused parameter warning caused by some of the
# Boost headers when compiling with clang.
libb10_dhcp___la_CXXFLAGS += -Wno-unused-parameter
endif
+
+# Distribute MySQL schema creation script
+EXTRA_DIST += dhcpdb_create.mysql
+dist_pkgdata_DATA = dhcpdb_create.mysql
diff --git a/src/lib/dhcp/addr_utilities.h b/src/lib/dhcp/addr_utilities.h
index 15532d0..a1d856c 100644
--- a/src/lib/dhcp/addr_utilities.h
+++ b/src/lib/dhcp/addr_utilities.h
@@ -12,6 +12,9 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#ifndef ADDR_UTILITIES_H
+#define ADDR_UTILITIES_H
+
#include <asiolink/io_address.h>
namespace isc {
@@ -51,3 +54,5 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix
};
};
+
+#endif // ADDR_UTILITIES_H
diff --git a/src/lib/dhcp/alloc_engine.cc b/src/lib/dhcp/alloc_engine.cc
index 650fd5f..53ef7f3 100644
--- a/src/lib/dhcp/alloc_engine.cc
+++ b/src/lib/dhcp/alloc_engine.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <alloc_engine.h>
+#include <lease_mgr_factory.h>
#include <string.h>
#include <cstring>
@@ -172,7 +173,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
}
// check if there's existing lease for that subnet/duid/iaid combination.
- Lease6Ptr existing = LeaseMgr::instance().getLease6(*duid, iaid, subnet->getID());
+ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID());
if (existing) {
// we have a lease already. This is a returning client, probably after
// his reboot.
@@ -181,7 +182,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
// check if the hint is in pool and is available
if (subnet->inPool(hint)) {
- existing = LeaseMgr::instance().getLease6(hint);
+ existing = LeaseMgrFactory::instance().getLease6(hint);
if (!existing) {
/// @todo: check if the hint is reserved once we have host support
/// implemented
@@ -205,7 +206,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
/// @todo: check if the address is reserved once we have host support
/// implemented
- Lease6Ptr existing = LeaseMgr::instance().getLease6(candidate);
+ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(candidate);
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
if (!existing) {
@@ -241,7 +242,7 @@ Lease6Ptr AllocEngine::createLease(const Subnet6Ptr& subnet,
if (!fake_allocation) {
// That is a real (REQUEST) allocation
- bool status = LeaseMgr::instance().addLease(lease);
+ bool status = LeaseMgrFactory::instance().addLease(lease);
if (status) {
@@ -258,7 +259,7 @@ Lease6Ptr AllocEngine::createLease(const Subnet6Ptr& subnet,
// It is for advertise only. We should not insert the lease into LeaseMgr,
// but rather check that we could have inserted it.
- Lease6Ptr existing = LeaseMgr::instance().getLease6(addr);
+ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(addr);
if (!existing) {
return (lease);
} else {
diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h
index 2911d05..ea8125f 100644
--- a/src/lib/dhcp/cfgmgr.h
+++ b/src/lib/dhcp/cfgmgr.h
@@ -162,4 +162,4 @@ protected:
} // namespace isc::dhcp
} // namespace isc
-#endif
+#endif // CFGMGR_H
diff --git a/src/lib/dhcp/database_backends.dox b/src/lib/dhcp/database_backends.dox
new file mode 100644
index 0000000..8eeb5c5
--- /dev/null
+++ b/src/lib/dhcp/database_backends.dox
@@ -0,0 +1,64 @@
+/**
+ @page dhcp-database-backends DHCP Database Back-Ends
+
+ All DHCP lease data is stored in some form of database, the interface
+ to this being through the Lease Manager.
+
+ All backend classes such as isc::dhcp::MySqlLeaseMgr are derived from
+ the abstract isc::dhcp::LeaseMgr class. This provides methods to
+ create, retrieve, modify and delete leases in the database.
+
+ @section dhcpdb-instantiation Instantiation of Lease Managers
+
+ A lease manager is instantiated through the LeaseMgrFactory class. This
+ has three methods:
+
+ - isc::dhcp::LeaseMgrFactory::create - Creates a singleton Lease
+ Manager of the appropriate type.
+ - isc::dhcp::LeaseMgrFactory::instance - Returns a reference to the
+ the instance of the Lease Manager.
+ - isc::dhcp::LeaseMgrFactory::destroy - Destroys the singleton lease manager.
+
+ The selection of the Lease Manager (and thus the backend database) is
+ controlled by the connection string passed to
+ isc::dhcp::LeaseMgrFactory::create. This is a set of "keyword=value" pairs
+ (no embedded spaces), each pair separated by a space from the others, e.g.
+
+ \code
+ type=mysql user=keatest password=keatest name=keatest host=localhost
+ \endcode
+
+ The following keywords are used for all backends:
+
+ - <b>type</b> - specifies the type of database backend. The following values
+ for the type keyword are supported:
+ - <b>mysql</b> - Use MySQL as the database
+
+ The following sections list the database-specific keywords:
+
+ @subsection dhcpdb-keywords-mysql MySQL connection string keywords
+
+ - <b>host</b> - host on which the selected database is running. If not
+ supplied, "localhost" is assumed.
+ - <b>name</b> - name of the MySQL database to access. There is no default -
+ this must always be supplied.
+ - <b>password</b> - password for the selected user ID (see below). If not
+ specified, no password is used.
+ - <b>user</b> - database user ID under which the database is accessed. If not
+ specified, no user ID is used - the database is assumed to be open.
+
+
+ @section dhcp-backend-unittest Running Unit Tests
+
+ With the use of databases requiring separate authorisation, there are
+ certain database-specific pre-requisites for successfully running the unit
+ tests. These are listed in the following sections.
+
+ @subsection dhcp-mysql-unittest MySQL
+
+ A database called <i>keatest</i> needs to be set up using the MySQL
+ <b>CREATE DATABASE</b> command. A database user, also called <i>keatest</i>
+ (with a password <i>keatest</i>) must be given full privileges in that
+ database. The unit tests create the schema in the database before each test
+ and delete it afterwards.
+ */
diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h
index 22e7c8d..116019e 100644
--- a/src/lib/dhcp/dhcp6.h
+++ b/src/lib/dhcp/dhcp6.h
@@ -191,4 +191,4 @@ extern const int dhcpv6_type_name_max;
#define IRT_DEFAULT 86400
#define IRT_MINIMUM 600
-#endif
+#endif /* DHCP6_H */
diff --git a/src/lib/dhcp/dhcpdb_create.mysql b/src/lib/dhcp/dhcpdb_create.mysql
new file mode 100644
index 0000000..7a292ec
--- /dev/null
+++ b/src/lib/dhcp/dhcpdb_create.mysql
@@ -0,0 +1,115 @@
+# Copyright (C) 2012 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This is the BIND 10 DHCP schema specification for MySQL.
+#
+# The schema is reasonably portable (with the exception of the engine
+# specification, which is MySQL-specific). Minor changes might be needed for
+# other databases.
+
+# To create the schema, either type the command:
+#
+# mysql -u <user> -p <password> <database> < dhcpdb_create.mysql
+#
+# ... at the command prompt, or log in to the MySQL database and at the "mysql>"
+# prompt, issue the command:
+#
+# source dhcpdb_create.mysql
+
+
+# Holds the IPv4 leases.
+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)
+ expire TIMESTAMP, # Expiration time of the lease
+ subnet_id INT UNSIGNED # Subnet identification
+ ) ENGINE = INNODB;
+
+# Holds the IPv6 leases.
+# 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
+ duid VARBINARY(128), # DUID
+ valid_lifetime INT UNSIGNED, # Length of the lease (seconds)
+ expire TIMESTAMP, # Expiration time of the lease
+ subnet_id INT UNSIGNED, # Subnet identification
+ pref_lifetime INT UNSIGNED, # Preferred lifetime
+ lease_type TINYINT, # Lease type (see lease6_types
+ # table for possible values)
+ iaid INT UNSIGNED, # See Section 10 of RFC 3315
+ prefix_len TINYINT UNSIGNED # For IA_PD only
+ ) ENGINE = INNODB;
+
+# ... and a definition of lease6 types. This table is a convenience for
+# users of the database - if they want to view the lease table and use the
+# type names, they can join this table with the lease6 table
+CREATE TABLE lease6_types (
+ lease_type TINYINT PRIMARY KEY NOT NULL, # Lease type code.
+ name VARCHAR(5) # Name of the lease type
+ );
+START TRANSACTION;
+INSERT INTO lease6_types VALUES (0, "IA_NA"); # Non-temporary v6 addresses
+INSERT INTO lease6_types VALUES (1, "IA_TA"); # Temporary v6 addresses
+INSERT INTO lease6_types VALUES (2, "IA_PD"); # Prefix delegations
+COMMIT;
+
+# Finally, the version of the schema. We start at 0.1 during development.
+# 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".
+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);
+COMMIT;
+
+# Notes:
+#
+# Indexes
+# =======
+# It is likely that additional indexes will be needed. However, the
+# increase in lookup performance from these will come at the expense
+# of a decrease in performance during insert operations due to the need
+# to update the indexes. For this reason, the need for additional indexes
+# will be determined by experiment during performance tests.
+#
+# The most likely additional indexes will cover the following columns:
+#
+# expire
+# To speed up the deletion of expired leases from the database.
+#
+# hwaddr and client_id
+# For lease stability: if a client requests a new lease, try to find an
+# existing or recently expired lease for it so that it can keep using the
+# same IP address.
+#
+# Field Sizes
+# ===========
+# If any of the VARxxx field sizes are altered, the lengths in the MySQL
+# backend source file (mysql_lease_mgr.cc) must be correspondingly changed.
+#
+# Portability
+# ===========
+# The "ENGINE = INNODB" on some tables is not portablea to another database
+# and will need to be removed.
+#
+# Some columns contain binary data so are stored as VARBINARY instead of
+# VARCHAR. This may be non-portable between databases: in this case, the
+# definition should be changed to VARCHAR.
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index a7b9a78..1b15595 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -681,4 +681,4 @@ private:
}; // namespace isc::dhcp
}; // namespace isc
-#endif
+#endif // IFACE_MGR_H
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index 189fe90..d7ebe1a 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -125,7 +125,7 @@ const static size_t RCVBUF_SIZE = 32768;
/// @brief Opens netlink socket and initializes handle structure.
///
-/// @exception Unexpected Thrown if socket configuration fails.
+/// @throw isc::Unexpected Thrown if socket configuration fails.
void Netlink::rtnl_open_socket() {
fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc
index 59582fb..c7dba6c 100644
--- a/src/lib/dhcp/lease_mgr.cc
+++ b/src/lib/dhcp/lease_mgr.cc
@@ -12,26 +12,25 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include "lease_mgr.h"
+#include <dhcp/lease_mgr.h>
#include <exceptions/exceptions.h>
+
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
-#include <sstream>
+
+#include <algorithm>
#include <iostream>
+#include <iterator>
#include <map>
-#include <iostream>
-#include <string>
#include <sstream>
-#include <algorithm>
-#include <iterator>
+#include <string>
+
#include <time.h>
using namespace std;
using namespace isc::dhcp;
-LeaseMgr* LeaseMgr::instance_ = NULL;
-
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)
@@ -46,62 +45,54 @@ Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr dui
cltt_ = time(NULL);
}
-LeaseMgr& LeaseMgr::instance() {
- if (!instance_) {
- isc_throw(InvalidOperation, "LeaseManager not instantiated yet");
- }
- return (*instance_);
-}
-
-void LeaseMgr::destroy_instance() {
- if (!instance_) {
- isc_throw(InvalidOperation, "LeaseManager not instantiated yet");
- }
- delete instance_;
- instance_ = NULL;
-}
-
-LeaseMgr::LeaseMgr(const std::string& dbconfig) {
- if (instance_) {
- isc_throw(InvalidOperation, "LeaseManager already instantiated");
- }
-
- // remember the pointer to the singleton instance
- instance_ = this;
-
- if (dbconfig.length() == 0) {
- return;
- }
-
- vector<string> tokens;
-
- // we need to pass a string to is_any_of, not just char *. Otherwise there
- // are cryptic warnings on Debian6 running g++ 4.4 in /usr/include/c++/4.4
- // /bits/stl_algo.h:2178 "array subscript is above array bounds"
- boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") ));
- BOOST_FOREACH(std::string token, tokens) {
- size_t pos = token.find("=");
- if (pos != string::npos) {
- string name = token.substr(0, pos);
- string value = token.substr(pos + 1);
- parameters_.insert(pair<string,string>(name, value));
- } else {
- isc_throw(InvalidParameter, "Cannot parse " << token
- << ", expected format is name=value");
- }
-
- }
-}
-
std::string LeaseMgr::getParameter(const std::string& name) const {
- std::map<std::string, std::string>::const_iterator param
- = parameters_.find(name);
+ ParameterMap::const_iterator param = parameters_.find(name);
if (param == parameters_.end()) {
isc_throw(BadValue, "Parameter not found");
}
return (param->second);
}
-LeaseMgr::~LeaseMgr() {
- instance_ = NULL;
+std::string
+Lease6::toText() {
+ ostringstream stream;
+
+ stream << "Type: " << static_cast<int>(type_) << " (";
+ switch (type_) {
+ case Lease6::LEASE_IA_NA:
+ stream << "IA_NA)\n";
+ break;
+ case Lease6::LEASE_IA_TA:
+ stream << "IA_TA)\n";
+ break;
+ case Lease6::LEASE_IA_PD:
+ stream << "IA_PD)\n";
+ break;
+ default:
+ stream << "unknown)\n";
+ }
+ stream << "Address: " << addr_.toText() << "\n"
+ << "Prefix length: " << static_cast<int>(prefixlen_) << "\n"
+ << "IAID: " << iaid_ << "\n"
+ << "Pref life: " << preferred_lft_ << "\n"
+ << "Valid life: " << valid_lft_ << "\n"
+ << "Cltt: " << cltt_ << "\n"
+ << "Subnet ID: " << subnet_id_ << "\n";
+
+ return (stream.str());
+}
+
+bool
+Lease6::operator==(const Lease6& other) const {
+ return (
+ type_ == other.type_ &&
+ addr_ == other.addr_ &&
+ prefixlen_ == other.prefixlen_ &&
+ iaid_ == other.iaid_ &&
+ *duid_ == *other.duid_ &&
+ preferred_lft_ == other.preferred_lft_ &&
+ valid_lft_ == other.valid_lft_ &&
+ cltt_ == other.cltt_ &&
+ subnet_id_ == other.subnet_id_
+ );
}
diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h
index e859dea..40c20cd 100644
--- a/src/lib/dhcp/lease_mgr.h
+++ b/src/lib/dhcp/lease_mgr.h
@@ -15,16 +15,20 @@
#ifndef LEASE_MGR_H
#define LEASE_MGR_H
-#include <string>
-#include <fstream>
-#include <vector>
-#include <map>
#include <asiolink/io_address.h>
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-#include <dhcp/option.h>
#include <dhcp/duid.h>
+#include <dhcp/option.h>
#include <dhcp/subnet.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <fstream>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
/// @file dhcp/lease_mgr.h
/// @brief An abstract API for lease database
@@ -57,9 +61,39 @@
/// Nevertheless, we hope to have failover protocol eventually implemented in
/// the Kea.
+#include <iostream>
+
namespace isc {
namespace dhcp {
+/// @brief Exception thrown if name of database is not specified
+class NoDatabaseName : public Exception {
+public:
+ NoDatabaseName(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception thrown on failure to open database
+class DbOpenError : public Exception {
+public:
+ DbOpenError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception thrown on failure to execute a database function
+class DbOperationError : public Exception {
+public:
+ DbOperationError(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:
+ NoSuchLease(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
@@ -141,6 +175,12 @@ struct Lease4 {
std::string comments_;
/// @todo: Add DHCPv4 failover related fields here
+
+ /// @brief Constructor
+ ///
+ /// Initialize fields that don't have a default constructor.
+ /// @todo Remove this
+ Lease4() : addr_(0) {}
};
/// @brief Pointer to a Lease4 structure.
@@ -182,15 +222,6 @@ struct Lease6 {
/// than once in a message. To differentiate between them, IAID field is present
uint32_t iaid_;
- /// @brief hardware address
- ///
- /// This field is not really used and is optional at best. The concept of identifying
- /// clients by their hardware address was replaced in DHCPv6 by DUID concept. Each
- /// client has its own unique DUID (DHCP Unique IDentifier). Furthermore, client's
- /// HW address is not always available, because client may be behind a relay (relay
- /// stores only link-local address).
- std::vector<uint8_t> hwaddr_;
-
/// @brief client identifier
boost::shared_ptr<DUID> duid_;
@@ -258,6 +289,29 @@ struct Lease6 {
std::string comments_;
/// @todo: Add DHCPv6 failover related fields here
+
+ /// @brief Constructor
+ ///
+ /// Initialize fields that don't have a default constructor.
+ Lease6() : addr_("::") {}
+
+ /// @brief Convert Lease6 to Printable Form
+ ///
+ /// @return String form of the lease
+ std::string toText();
+
+ /// @brief Compare two leases for equality
+ ///
+ /// @param other lease6 object with which to compare
+ bool operator==(const Lease6& other) const;
+
+ /// @brief Compare two leases for inequality
+ ///
+ /// @param other lease6 object with which to compare
+ bool operator!=(const Lease6& other) const {
+ return (!operator==(other));
+ }
+
};
/// @brief Pointer to a Lease6 structure.
@@ -267,7 +321,7 @@ typedef boost::shared_ptr<Lease6> Lease6Ptr;
typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
/// @brief A collection of IPv6 leases.
-typedef std::vector< boost::shared_ptr<Lease6Ptr> > Lease6Collection;
+typedef std::vector<Lease6Ptr> Lease6Collection;
/// @brief Abstract Lease Manager
///
@@ -276,43 +330,45 @@ typedef std::vector< boost::shared_ptr<Lease6Ptr> > Lease6Collection;
/// be used directly, but rather specialized derived class should be used
/// instead.
///
-/// This class is a meta-singleton. At any given time, there is only one
-/// instance of any classes derived from that class. That is achieved with
-/// defining only a single protected constructor, so every derived class has
-/// to use it. Furthermore, this sole constructor registers the first instance
-/// (and throws InvalidOperation if there is an attempt to create a second one).
-class LeaseMgr : public boost::noncopyable {
+/// As all methods are virtual, this class throws no exceptions. However,
+/// methods in concrete implementations of this class may throw exceptions:
+/// see the documentation of those classes for details.
+class LeaseMgr {
public:
/// Client Hardware address
typedef std::vector<uint8_t> HWAddr;
- /// @brief returns a single instance of LeaseMgr
- ///
- /// LeaseMgr is a singleton and this method is the only way of
- /// accessing it. LeaseMgr must be created first. See
- /// isc::dhcp::LeaseMgrFactory class (work of ticket #2342.
- /// Otherwise instance() will throw InvalidOperation exception.
- /// @throw InvalidOperation if LeaseMgr not instantiated
- static LeaseMgr& instance();
+ /// Database configuration parameter map
+ typedef std::map<std::string, std::string> ParameterMap;
- /// @brief destroys the only instance of LeaseMgr
+ /// @brief Constructor
///
- /// This method is used mostly in tests, where LeaseMgr is destroyed
- /// at the end of each test, just to be created at the beginning of
- /// the next one.
- static void destroy_instance();
+ /// @param parameters A data structure relating keywords and values
+ /// concerned with the database.
+ LeaseMgr(const ParameterMap& parameters) : parameters_(parameters)
+ {}
+
+ /// @brief Destructor
+ virtual ~LeaseMgr()
+ {}
/// @brief Adds an IPv4 lease.
///
/// @param lease lease to be added
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
virtual bool addLease(const Lease4Ptr& lease) = 0;
/// @brief Adds an IPv6 lease.
///
/// @param lease lease to be added
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
virtual bool addLease(const Lease6Ptr& lease) = 0;
- /// @brief Returns existing IPv4 lease for specified IPv4 address and subnet_id
+ /// @brief Returns IPv4 lease for specified IPv4 address and subnet_id
///
/// This method is used to get a lease for specific subnet_id. There can be
/// at most one lease for any given subnet, so this method returns a single
@@ -322,7 +378,7 @@ public:
/// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
SubnetID subnet_id) const = 0;
/// @brief Returns an IPv4 lease for specified IPv4 address
@@ -335,10 +391,9 @@ public:
/// a single lease, not a container of leases.
///
/// @param addr address of the searched lease
- /// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const = 0;
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const = 0;
/// @brief Returns existing IPv4 leases for specified hardware address.
///
@@ -431,11 +486,9 @@ public:
/// If no such lease is present, an exception will be thrown.
virtual void updateLease4(const Lease4Ptr& lease4) = 0;
- /// @brief Updates IPv4 lease.
+ /// @brief Updates IPv6 lease.
///
- /// @param lease4 The lease to be updated.
- ///
- /// If no such lease is present, an exception will be thrown.
+ /// @param lease6 The lease to be updated.
virtual void updateLease6(const Lease6Ptr& lease6) = 0;
/// @brief Deletes a lease.
@@ -443,11 +496,11 @@ public:
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- virtual bool deleteLease4(uint32_t addr) = 0;
+ virtual bool deleteLease4(const isc::asiolink::IOAddress& addr) = 0;
/// @brief Deletes a lease.
///
- /// @param addr IPv4 address of the lease to be deleted.
+ /// @param addr IPv6 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
virtual bool deleteLease6(const isc::asiolink::IOAddress& addr) = 0;
@@ -464,6 +517,9 @@ public:
/// @brief Returns backend version.
///
+ /// @return Version number as a pair of unsigned integers. "first" is the
+ /// major version number, "second" the minor number.
+ ///
/// @todo: We will need to implement 3 version functions eventually:
/// A. abstract API version
/// B. backend version
@@ -473,44 +529,37 @@ public:
/// B>=A and B=C (it is ok to have newer backend, as it should be backward
/// compatible)
/// Also if B>C, some database upgrade procedure may be triggered
- virtual std::string getVersion() const = 0;
-
- /// @todo: Add host management here
- /// As host reservation is outside of scope for 2012, support for hosts
- /// is currently postponed.
+ virtual std::pair<uint32_t, uint32_t> getVersion() const = 0;
-protected:
- /// @brief The sole lease manager constructor
+ /// @brief Commit Transactions
///
- /// dbconfig is a generic way of passing parameters. Parameters are passed
- /// in the "name=value" format, separated by spaces. Values may be enclosed
- /// in double quotes, if needed. This ctor guarantees that there will be
- /// only one instance of any derived classes. If there is a second instance
- /// being created with the first one still around, it will throw
- /// InvalidOperation.
+ /// Commits all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ virtual void commit() = 0;
+
+ /// @brief Rollback Transactions
///
- /// @param dbconfig database configuration
- /// @throw InvalidOperation when trying to create second LeaseMgr
- LeaseMgr(const std::string& dbconfig);
+ /// Rolls back all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ virtual void rollback() = 0;
- /// @brief Destructor
- virtual ~LeaseMgr();
+ /// @todo: Add host management here
+ /// As host reservation is outside of scope for 2012, support for hosts
+ /// is currently postponed.
/// @brief returns value of the parameter
std::string getParameter(const std::string& name) const;
+private:
/// @brief list of parameters passed in dbconfig
///
/// That will be mostly used for storing database name, username,
/// password and other parameters required for DB access. It is not
/// intended to keep any DHCP-related parameters.
- std::map<std::string, std::string> parameters_;
-
- static LeaseMgr* instance_;
+ ParameterMap parameters_;
};
}; // end of isc::dhcp namespace
-
}; // end of isc namespace
#endif // LEASE_MGR_H
diff --git a/src/lib/dhcp/lease_mgr_factory.cc b/src/lib/dhcp/lease_mgr_factory.cc
new file mode 100644
index 0000000..7e75633
--- /dev/null
+++ b/src/lib/dhcp/lease_mgr_factory.cc
@@ -0,0 +1,117 @@
+// 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "config.h"
+
+#include <dhcp/lease_mgr_factory.h>
+#include <dhcp/memfile_lease_mgr.h>
+#ifdef HAVE_MYSQL
+#include <dhcp/mysql_lease_mgr.h>
+#endif
+
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <map>
+#include <sstream>
+#include <utility>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+boost::scoped_ptr<LeaseMgr>&
+LeaseMgrFactory::getLeaseMgrPtr() {
+ static boost::scoped_ptr<LeaseMgr> leaseMgrPtr;
+ return (leaseMgrPtr);
+}
+
+LeaseMgr::ParameterMap
+LeaseMgrFactory::parse(const std::string& dbconfig) {
+ LeaseMgr::ParameterMap mapped_tokens;
+
+ if (!dbconfig.empty()) {
+ vector<string> tokens;
+
+ // We need to pass a string to is_any_of, not just char*. Otherwise
+ // there are cryptic warnings on Debian6 running g++ 4.4 in
+ // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above
+ // array bounds"
+ boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") ));
+ BOOST_FOREACH(std::string token, tokens) {
+ size_t pos = token.find("=");
+ if (pos != string::npos) {
+ string name = token.substr(0, pos);
+ string value = token.substr(pos + 1);
+ mapped_tokens.insert(make_pair(name, value));
+ } else {
+ isc_throw(InvalidParameter, "Cannot parse " << token
+ << ", expected format is name=value");
+ }
+ }
+ }
+
+ return (mapped_tokens);
+}
+
+void
+LeaseMgrFactory::create(const std::string& dbconfig) {
+ const std::string type = "type";
+
+ // Is "type" present?
+ LeaseMgr::ParameterMap parameters = parse(dbconfig);
+ if (parameters.find(type) == parameters.end()) {
+ isc_throw(InvalidParameter, "Database configuration parameters do not "
+ "contain the 'type' keyword");
+ }
+
+ // Yes, check what it is.
+#ifdef HAVE_MYSQL
+ if (parameters[type] == string("mysql")) {
+ getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters));
+ return;
+ }
+#endif
+ if (parameters[type] == string("memfile")) {
+ getLeaseMgrPtr().reset(new Memfile_LeaseMgr(parameters));
+ return;
+ }
+
+ // Get here on no match
+ isc_throw(InvalidType, "Database configuration parameter 'type' does "
+ "not specify a supported database backend");
+}
+
+void
+LeaseMgrFactory::destroy() {
+ getLeaseMgrPtr().reset();
+}
+
+LeaseMgr&
+LeaseMgrFactory::instance() {
+ LeaseMgr* lmptr = getLeaseMgrPtr().get();
+ if (lmptr == NULL) {
+ isc_throw(NoLeaseManager, "no current lease manager is available");
+ }
+ return (*lmptr);
+}
+
+
+}; // namespace dhcp
+}; // namespace isc
diff --git a/src/lib/dhcp/lease_mgr_factory.h b/src/lib/dhcp/lease_mgr_factory.h
new file mode 100644
index 0000000..c691e12
--- /dev/null
+++ b/src/lib/dhcp/lease_mgr_factory.h
@@ -0,0 +1,123 @@
+// 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LEASE_MGR_FACTORY_H
+#define LEASE_MGR_FACTORY_H
+
+#include <string>
+#include <dhcp/lease_mgr.h>
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Invalid type exception
+///
+/// Thrown when the factory doesn't recognise the type of the backend.
+class InvalidType : public Exception {
+public:
+ InvalidType(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief No lease manager exception
+///
+/// Thrown if an attempt is made to get a reference to the current lease
+/// manager and none is currently available.
+class NoLeaseManager : public Exception {
+public:
+ NoLeaseManager(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Lease Manager Factory
+///
+/// This class comprises nothing but static methods used to create a lease
+/// manager. It analyzes the database information passed to the creation
+/// function and instantiates an appropriate lease manager based on the type
+/// requested.
+///
+/// Strictly speaking these functions could be stand-alone functions. However,
+/// it is convenient to encapsulate them in a class for naming purposes.
+///
+/// @todo: Will need to develop some form of registration mechanism for
+/// user-supplied backends (so that there is no need to modify the code).
+class LeaseMgrFactory {
+public:
+ /// @brief Create an instance of a lease manager.
+ ///
+ /// Each database backend has its own lease manager type. This static
+ /// method sets the "current" lease manager to be a manager of the
+ /// appropriate type. The actual lease manager is returned by the
+ /// "instance" method.
+ ///
+ /// @note When called, the current lease manager is <b>always</b> destroyed
+ /// and a new one created - even if the parameters are the same.
+ ///
+ /// dbconfig is a generic way of passing parameters. Parameters are passed
+ /// in the "name=value" format, separated by spaces. The data MUST include
+ /// a keyword/value pair of the form "type=dbtype" giving the database
+ /// type, e.q. "mysql" or "sqlite3".
+ ///
+ /// @param dbconfig Database configuration parameters. These are in
+ /// the form of "keyword=value" pairs, separated by spaces. These
+ /// are back-end specific, although must include the "type" keyword
+ /// which gives the backend in use.
+ ///
+ /// @throw isc::InvalidParameter dbconfig string does not contain the "type"
+ /// keyword.
+ /// @throw isc::dhcp::InvalidType The "type" keyword in dbconfig does not
+ /// identify a supported backend.
+ static void create(const std::string& dbconfig);
+
+ /// @brief Destroy lease manager
+ ///
+ /// Destroys the current lease manager object. This should have the effect
+ /// of closing the database connection. The method is a no-op if no
+ /// lease manager is available.
+ static void destroy();
+
+ /// @brief Return Current Lease Manager
+ ///
+ /// Returns an instance of the "current" lease manager. An exception
+ /// will be thrown if none is available.
+ ///
+ /// @throw isc::dhcp::NoLeaseManager No lease manager is available: use
+ /// create() to create one before calling this method.
+ static LeaseMgr& instance();
+
+ /// @brief Parse Database Parameters
+ ///
+ /// Parses the string of "keyword=value" pairs and separates them
+ /// out into the map.
+ ///
+ /// @param dbconfig Database configuration string
+ ///
+ /// @return std::map<std::string, std::string> Map of keyword/value pairs.
+ static LeaseMgr::ParameterMap parse(const std::string& dbconfig);
+
+private:
+ /// @brief Hold pointer to lease manager
+ ///
+ /// Holds a pointer to the singleton lease manager. The singleton
+ /// is encapsulated in this method to avoid a "static initialization
+ /// fiasco" if defined in an external static variable.
+ static boost::scoped_ptr<LeaseMgr>& getLeaseMgrPtr();
+
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // LEASE_MGR_FACTORY_H
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index c057eea..b848ad3 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -45,9 +45,11 @@ public:
/// @param u universe of the option (V4 or V6)
/// @param type option-type
/// @param buf option-buffer
- /// @throw isc::InvalidOperation if there is no factory function
- /// registered for specified option type.
+ ///
/// @return instance of option.
+ ///
+ /// @throw isc::InvalidOperation if there is no factory function registered
+ /// for the specified option type.
static isc::dhcp::OptionPtr optionFactory(isc::dhcp::Option::Universe u,
uint16_t type,
const OptionBuffer& buf);
@@ -100,8 +102,8 @@ public:
/// Registers factory method that produces options of specific option types.
///
- /// @exception BadValue if provided type is already registered, has too large
- /// value or invalid universe is specified
+ /// @throw isc::BadValue if provided the type is already registered, has
+ /// too large a value or an invalid universe is specified.
///
/// @param u universe of the option (V4 or V6)
/// @param type option-type
diff --git a/src/lib/dhcp/memfile_lease_mgr.cc b/src/lib/dhcp/memfile_lease_mgr.cc
index 63023c4..1424b39 100644
--- a/src/lib/dhcp/memfile_lease_mgr.cc
+++ b/src/lib/dhcp/memfile_lease_mgr.cc
@@ -13,13 +13,13 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <iostream>
-#include "memfile_lease_mgr.h"
+
+#include <dhcp/memfile_lease_mgr.h>
using namespace isc::dhcp;
-using namespace isc::dhcp::test;
-Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig)
- : LeaseMgr(dbconfig) {
+Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
+ : LeaseMgr(parameters) {
std::cout << "Warning: Using memfile database backend. It is usable for" << std::endl;
std::cout << "Warning: limited testing only. File support not implemented yet." << std::endl;
std::cout << "Warning: Leases will be lost after restart." << std::endl;
@@ -41,7 +41,7 @@ bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
return (true);
}
-Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
+Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const {
return (Lease4Ptr());
}
@@ -49,7 +49,7 @@ Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
return (Lease4Collection());
}
-Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress ,
+Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&,
SubnetID) const {
return (Lease4Ptr());
}
@@ -102,7 +102,7 @@ void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& ) {
}
-bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
+bool Memfile_LeaseMgr::deleteLease4(const isc::asiolink::IOAddress&) {
return (false);
}
@@ -122,3 +122,11 @@ std::string Memfile_LeaseMgr::getDescription() const {
"It does not offer any useful lease management and its only\n"
"purpose is to test abstract lease manager API."));
}
+
+void
+Memfile_LeaseMgr::commit() {
+}
+
+void
+Memfile_LeaseMgr::rollback() {
+}
diff --git a/src/lib/dhcp/memfile_lease_mgr.h b/src/lib/dhcp/memfile_lease_mgr.h
index c5a41e6..cb02360 100644
--- a/src/lib/dhcp/memfile_lease_mgr.h
+++ b/src/lib/dhcp/memfile_lease_mgr.h
@@ -15,15 +15,15 @@
#ifndef MEMFILE_LEASE_MGR_H
#define MEMFILE_LEASE_MGR_H
-#include <dhcp/lease_mgr.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
+#include <dhcp/lease_mgr.h>
+
namespace isc {
namespace dhcp {
-namespace test {
// This is a concrete implementation of a Lease database.
//
@@ -42,8 +42,9 @@ public:
/// are passed in the "name=value" format, separated by spaces.
/// Values may be enclosed in double quotes, if needed.
///
- /// @param dbconfig database configuration
- Memfile_LeaseMgr(const std::string& dbconfig);
+ /// @param parameters A data structure relating keywords and values
+ /// concerned with the database.
+ Memfile_LeaseMgr(const ParameterMap& parameters);
/// @brief Destructor (closes file)
virtual ~Memfile_LeaseMgr();
@@ -65,7 +66,7 @@ public:
/// @param addr address of the searched lease
///
/// @return a collection of leases
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
/// @brief Returns existing IPv4 lease for specific address and subnet
///
@@ -74,7 +75,7 @@ public:
/// @param subnet_id ID of the subnet the lease must belong to
///
/// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
SubnetID subnet_id) const;
/// @brief Returns existing IPv4 leases for specified hardware address.
@@ -168,19 +169,17 @@ public:
///
/// @todo Not implemented yet
///
- /// @param lease4 The lease to be updated.
+ /// @param lease6 The lease to be updated.
///
/// If no such lease is present, an exception will be thrown.
void updateLease6(const Lease6Ptr& lease6);
/// @brief Deletes a lease.
///
- /// @todo Not implemented yet
- ///
/// @param addr IPv4 address of the lease to be deleted.
///
/// @return true if deletion was successful, false if no such lease exists
- bool deleteLease4(uint32_t addr);
+ virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
/// @brief Deletes a lease.
///
@@ -200,7 +199,21 @@ public:
std::string getDescription() const;
/// @brief Returns backend version.
- std::string getVersion() const { return ("test-version"); }
+ virtual std::pair<uint32_t, uint32_t> getVersion() const {
+ return (std::make_pair(1, 0));
+ }
+
+ /// @brief Commit Transactions
+ ///
+ /// Commits all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ virtual void commit();
+
+ /// @brief Rollback Transactions
+ ///
+ /// Rolls back all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ virtual void rollback();
using LeaseMgr::getParameter;
@@ -221,8 +234,8 @@ protected:
Lease6Storage storage6_;
};
-}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
-#endif // MEMFILE_LEASE_MGR_H
+#endif // MEMFILE_LEASE_MGR
+
diff --git a/src/lib/dhcp/mysql_lease_mgr.cc b/src/lib/dhcp/mysql_lease_mgr.cc
new file mode 100644
index 0000000..f030cd3
--- /dev/null
+++ b/src/lib/dhcp/mysql_lease_mgr.cc
@@ -0,0 +1,1139 @@
+// 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/mysql_lease_mgr.h>
+#include <asiolink/io_address.h>
+
+#include <mysql/mysqld_error.h>
+
+#include <iostream>
+#include <iomanip>
+#include <string>
+#include <time.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace std;
+
+namespace {
+///@{
+/// @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).
+
+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.
+struct TaggedStatement {
+ MySqlLeaseMgr::StatementIndex index;
+ const char* text;
+};
+
+TaggedStatement tagged_statements[] = {
+ {MySqlLeaseMgr::DELETE_LEASE6,
+ "DELETE FROM lease6 WHERE address = ?"},
+ {MySqlLeaseMgr::GET_LEASE6_ADDR,
+ "SELECT address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len "
+ "FROM lease6 "
+ "WHERE address = ?"},
+ {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
+ "SELECT address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len "
+ "FROM lease6 "
+ "WHERE duid = ? AND iaid = ?"},
+ {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
+ "SELECT address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len "
+ "FROM lease6 "
+ "WHERE duid = ? AND iaid = ? AND subnet_id = ?"},
+ {MySqlLeaseMgr::GET_VERSION,
+ "SELECT version, minor FROM schema_version"},
+ {MySqlLeaseMgr::INSERT_LEASE6,
+ "INSERT INTO lease6(address, duid, valid_lifetime, "
+ "expire, subnet_id, pref_lifetime, "
+ "lease_type, iaid, prefix_len) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
+ {MySqlLeaseMgr::UPDATE_LEASE6,
+ "UPDATE lease6 SET address = ?, duid = ?, "
+ "valid_lifetime = ?, expire = ?, subnet_id = ?, "
+ "pref_lifetime = ?, lease_type = ?, iaid = ?, "
+ "prefix_len = ? "
+ "WHERE address = ?"},
+ // End of list sentinel
+ {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
+};
+
+}; // Anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+
+
+/// @brief Exchange MySQL and Lease6 Data
+///
+/// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
+/// describe the parameters in the prepared statements. Where information is
+/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
+/// structure is identical - it defines data values in the Lease6 structure.
+/// This class handles the creation of that array.
+///
+/// Owing to the MySQL API, the process requires some intermediate variables
+/// to hold things like length etc. This object holds the intermediate
+/// variables as well.
+///
+/// @note There are no unit tests for this class. It is tested indirectly
+/// in all MySqlLeaseMgr::xxx6() calls where it is used.
+
+class MySqlLease6Exchange {
+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) {
+ memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
+ memset(duid_buffer_, 0, sizeof(duid_buffer_));
+ }
+
+ /// @brief Create MYSQL_BIND objects for Lease6 Pointer
+ ///
+ /// Fills in the MYSQL_BIND objects for the Lease6 passed to it.
+ ///
+ /// @param lease Lease object to be added to the database.
+ ///
+ /// @return Vector of MySQL BIND objects representing the data to be added.
+ std::vector<MYSQL_BIND> createBindForSend(const Lease6Ptr& lease) {
+ // Store lease object to ensure it remains valid.
+ lease_ = lease;
+
+ // Ensure bind_ array clear for constructing the MYSQL_BIND structures
+ // for this lease.
+ memset(bind_, 0, sizeof(bind_));
+
+ // address: varchar(40)
+ addr6_ = lease_->addr_.toText();
+ addr6_length_ = addr6_.size();
+
+ // In the following statement, the string is being read. However, the
+ // MySQL C interface does not use "const", so the "buffer" element
+ // is declared as "char*" instead of "const char*". To resolve this,
+ // the "const" is discarded. (Note that the address of addr6_.c_str()
+ // 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.
+ bind_[0].buffer_type = MYSQL_TYPE_STRING;
+ bind_[0].buffer = const_cast<char*>(addr6_.c_str());
+ bind_[0].buffer_length = addr6_length_;
+ bind_[0].length = &addr6_length_;
+
+ // duid: varchar(128)
+ duid_ = lease_->duid_->getDuid();
+ duid_length_ = duid_.size();
+
+ bind_[1].buffer_type = MYSQL_TYPE_BLOB;
+ bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
+ bind_[1].buffer_length = duid_length_;
+ bind_[1].length = &duid_length_;
+
+ // 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_;
+
+ // expire: timestamp
+ // The lease structure holds the client last transmission time (cltt_)
+ // For convenience for external tools, this is converted to lease
+ /// expiry time (expire). The relationship is given by:
+ //
+ // expire = cltt_ + valid_lft_
+ //
+ // @TODO Handle overflows
+ MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+ expire_);
+ bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
+ bind_[3].buffer = reinterpret_cast<char*>(&expire_);
+ bind_[3].buffer_length = sizeof(expire_);
+
+ // subnet_id: unsigned int
+ // 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_;
+
+ // 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_;
+
+ // lease_type: tinyint
+ // 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_;
+
+ // 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_;
+
+ // 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_;
+
+ // 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]));
+ }
+
+ /// @brief Create BIND array to receive data
+ ///
+ /// Creates a MYSQL_BIND array to receive Lease6 data from the database.
+ /// After data is successfully received, getLeaseData() is used to copy
+ /// it to a Lease6 object.
+ ///
+ /// @return Vector of MySQL BIND objects.
+ std::vector<MYSQL_BIND> createBindForReceive() {
+
+ // Ensure both the array of MYSQL_BIND structures and the error array
+ // are clear.
+ memset(bind_, 0, sizeof(bind_));
+ memset(error_, 0, sizeof(error_));
+
+ // address: varchar
+ // 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.
+ 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_);
+ bind_[1].buffer_type = MYSQL_TYPE_BLOB;
+ 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];
+
+ // 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];
+
+ // 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];
+
+ // 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];
+
+ // 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];
+
+ // 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];
+
+ // 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]));
+ }
+
+ /// @brief Copy Received Data into Lease6 Object
+ ///
+ /// Called after the MYSQL_BIND array created by createBindForReceive()
+ /// has been used, this copies data from the internal member vairables
+ /// into a Lease6 object.
+ ///
+ /// @return Lease6Ptr Pointer to a Lease6 object holding the relevant
+ /// data.
+ ///
+ /// @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.
+ addr6_buffer_[addr6_length_] = '\0';
+ std::string address = addr6_buffer_;
+
+ // 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:
+ switch (lease_type_) {
+ case Lease6::LEASE_IA_NA:
+ result->type_ = Lease6::LEASE_IA_NA;
+ break;
+
+ case Lease6::LEASE_IA_TA:
+ result->type_ = Lease6::LEASE_IA_TA;
+ break;
+
+ case Lease6::LEASE_IA_PD:
+ result->type_ = Lease6::LEASE_IA_PD;
+ break;
+
+ default:
+ isc_throw(BadValue, "invalid lease type returned (" <<
+ lease_type_ << ") for lease with address " <<
+ result->addr_.toText() << ". Only 0, 1, or 2 "
+ "are allowed.");
+ }
+ result->iaid_ = iaid_;
+ result->prefixlen_ = prefixlen_;
+
+ return (result);
+ }
+
+private:
+ // Note: All array lengths are equal to the corresponding variable in the
+ // schema.
+ std::string addr6_; ///< String form of address
+ char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN]; ///< Character
+ ///< array form of V6 address
+ unsigned long addr6_length_; ///< Length of the address
+ MYSQL_BIND bind_[9]; ///< Static array for speed of access
+ std::vector<uint8_t> duid_; ///< Client identification
+ uint8_t duid_buffer_[DUID_MAX_LEN]; ///< Buffer form of DUID
+ unsigned long duid_length_; ///< Length of the DUID
+ my_bool error_[9]; ///< For error reporting
+ 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
+};
+
+
+/// @brief Fetch and Release MySQL Results
+///
+/// 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.
+///
+/// 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
+/// that the resources are released even if the MySqlLeaseMgr method concerned
+/// exits via an exception.
+class MySqlFreeResult {
+public:
+
+ /// @brief Constructor
+ ///
+ /// Store the pointer to the statement for which data is being fetched.
+ ///
+ /// Note that according to the MySQL documentation, mysql_stmt_free_result
+ /// only releases resources if a cursor has been allocated for the
+ /// statement. This implies that it is a no-op if none have been. Either
+ /// way, any error from mysql_stmt_free_result is ignored. (Generating
+ /// an exception is not much help, as it will only confuse things if the
+ /// method calling mysql_stmt_fetch is exiting via an exception.)
+ MySqlFreeResult(MYSQL_STMT* statement) : statement_(statement)
+ {}
+
+ /// @brief Destructor
+ ///
+ /// Frees up fetch context if a fetch has been successfully executed.
+ ~MySqlFreeResult() {
+ (void) mysql_stmt_free_result(statement_);
+ }
+
+private:
+ MYSQL_STMT* statement_; ///< Statement for which results are freed
+};
+
+
+// MySqlLeaseMgr Methods
+
+MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
+ : LeaseMgr(parameters), mysql_(NULL) {
+
+ // Allocate context for MySQL - it is destroyed in the destructor.
+ mysql_ = mysql_init(NULL);
+ if (mysql_ == NULL) {
+ isc_throw(DbOpenError, "unable to initialize MySQL");
+ }
+
+ // Open the database
+ openDatabase();
+
+ // Disable autocommit
+ my_bool result = mysql_autocommit(mysql_, 0);
+ if (result != 0) {
+ isc_throw(DbOperationError, mysql_error(mysql_));
+ }
+
+ // Prepare all statements likely to be used.
+ prepareStatements();
+
+ // Create the exchange object for use in exchanging data between the
+ // program and the database.
+ 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.)
+ for (int i = 0; i < statements_.size(); ++i) {
+ if (statements_[i] != NULL) {
+ (void) mysql_stmt_close(statements_[i]);
+ statements_[i] = NULL;
+ }
+ }
+
+ // Close the database
+ mysql_close(mysql_);
+ mysql_ = NULL;
+}
+
+
+// Time conversion methods.
+//
+// Note that the MySQL TIMESTAMP data type (used for "expire") converts data
+// from the current timezone to UTC for storage, and from UTC to the current
+// timezone for retrieval.
+//
+// This causes no problems providing that:
+// a) cltt is given in local time
+// b) We let the system take care of timezone conversion when converting
+// from a time read from the database into a local time.
+
+void
+MySqlLeaseMgr::convertToDatabaseTime(time_t cltt, uint32_t valid_lifetime,
+ MYSQL_TIME& expire) {
+
+ // Calculate expiry time and convert to various date/time fields.
+ // @TODO: handle overflows
+ time_t expire_time = cltt + valid_lifetime;
+
+ // Convert to broken-out time
+ struct tm expire_tm;
+ (void) localtime_r(&expire_time, &expire_tm);
+
+ // Place in output expire structure.
+ expire.year = expire_tm.tm_year + 1900;
+ expire.month = expire_tm.tm_mon + 1; // Note different base
+ expire.day = expire_tm.tm_mday;
+ expire.hour = expire_tm.tm_hour;
+ expire.minute = expire_tm.tm_min;
+ expire.second = expire_tm.tm_sec;
+ expire.second_part = 0; // No fractional seconds
+ expire.neg = static_cast<my_bool>(0); // Not negative
+}
+
+void
+MySqlLeaseMgr::convertFromDatabaseTime(const MYSQL_TIME& expire,
+ uint32_t valid_lifetime, time_t& cltt) {
+
+ // Copy across fields from MYSQL_TIME structure.
+ struct tm expire_tm;
+ memset(&expire_tm, 0, sizeof(expire_tm));
+
+ expire_tm.tm_year = expire.year - 1900;
+ expire_tm.tm_mon = expire.month - 1;
+ expire_tm.tm_mday = expire.day;
+ expire_tm.tm_hour = expire.hour;
+ expire_tm.tm_min = expire.minute;
+ expire_tm.tm_sec = expire.second;
+ expire_tm.tm_isdst = -1; // Let the system work out about DST
+
+ // Convert to local time
+ cltt = mktime(&expire_tm) - valid_lifetime;
+}
+
+
+void
+MySqlLeaseMgr::openDatabase() {
+
+ // Set up the values of the parameters
+ const char* host = "localhost";
+ string shost;
+ try {
+ shost = getParameter("host");
+ host = shost.c_str();
+ } catch (...) {
+ // No host. Fine, we'll use "localhost"
+ }
+
+ const char* user = NULL;
+ string suser;
+ try {
+ suser = getParameter("user");
+ user = suser.c_str();
+ } catch (...) {
+ // No user. Fine, we'll use NULL
+ ;
+ }
+
+ const char* password = NULL;
+ string spassword;
+ try {
+ spassword = getParameter("password");
+ password = spassword.c_str();
+ } catch (...) {
+ // No password. Fine, we'll use NULL
+ ;
+ }
+
+ const char* name = NULL;
+ string sname;
+ try {
+ sname = getParameter("name");
+ name = sname.c_str();
+ } catch (...) {
+ // No database name. Throw a "NoName" exception
+ isc_throw(NoDatabaseName, "must specified a name for the database");
+ }
+
+ // Set options for the connection:
+ // - automatic reconnection
+ my_bool auto_reconnect = 1;
+ int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
+ if (result != 0) {
+ isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
+ mysql_error(mysql_));
+ }
+
+ // Open the database.
+ //
+ // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE,
+ // the affected rows are the number of rows found that match the
+ // WHERE clause of the SQL statement, not the rows changed. The reason
+ // here is that MySQL apparently does not update a row if data has not
+ // 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.
+ MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
+ 0, NULL, CLIENT_FOUND_ROWS);
+ if (status != mysql_) {
+ isc_throw(DbOpenError, mysql_error(mysql_));
+ }
+}
+
+
+void
+MySqlLeaseMgr::prepareStatement(StatementIndex index, const char* text) {
+ // Validate that there is space for the statement in the statements array
+ // and that nothing has been placed there before.
+ if ((index >= statements_.size()) || (statements_[index] != NULL)) {
+ isc_throw(InvalidParameter, "invalid prepared statement index (" <<
+ static_cast<int>(index) << ") or indexed prepared " <<
+ "statement is not null");
+ }
+
+ // 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_));
+ }
+
+ int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
+ if (status != 0) {
+ isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
+ text << ">, reason: " << mysql_error(mysql_));
+ }
+}
+
+
+void
+MySqlLeaseMgr::prepareStatements() {
+ // Allocate space for all statements
+ statements_.clear();
+ statements_.resize(NUM_STATEMENTS, NULL);
+
+ text_statements_.clear();
+ text_statements_.resize(NUM_STATEMENTS, std::string(""));
+
+ // Created the MySQL prepared statements for each DML statement.
+ for (int i = 0; tagged_statements[i].text != NULL; ++i) {
+ prepareStatement(tagged_statements[i].index,
+ tagged_statements[i].text);
+ }
+}
+
+
+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);
+
+ // Bind the parameters to the statement
+ int status = mysql_stmt_bind_param(statements_[stindex], &bind[0]);
+ checkError(status, stindex, "unable to bind parameters");
+
+ // Execute the statement
+ status = mysql_stmt_execute(statements_[stindex]);
+ if (status != 0) {
+
+ // Failure: check for the special case of duplicate entry. If this is
+ // the case, we return false to indicate that the row was not added.
+ // Otherwise we throw an exception.
+ if (mysql_errno(mysql_) == ER_DUP_ENTRY) {
+ return (false);
+ }
+ checkError(status, stindex, "unable to execute");
+ }
+
+ // Insert succeeded
+ return (true);
+}
+
+
+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());
+}
+
+
+Lease4Ptr
+MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& /* addr */) const {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const IOAddress&) "
+ "not implemented yet");
+ return (Lease4Ptr());
+}
+
+
+Lease4Collection
+MySqlLeaseMgr::getLease4(const HWAddr& /* hwaddr */) const {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const HWAddr&) "
+ "not implemented yet");
+ return (Lease4Collection());
+}
+
+
+Lease4Ptr
+MySqlLeaseMgr::getLease4(const HWAddr& /* hwaddr */,
+ SubnetID /* subnet_id */) const {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const HWAddr&, SubnetID) "
+ "not implemented yet");
+ return (Lease4Ptr());
+}
+
+
+Lease4Collection
+MySqlLeaseMgr::getLease4(const ClientId& /* clientid */) const {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const ClientID&) "
+ "not implemented yet");
+ return (Lease4Collection());
+}
+
+
+Lease4Ptr
+MySqlLeaseMgr::getLease4(const ClientId& /* clientid */,
+ SubnetID /* subnet_id */) const {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::getLease4(const ClientID&, SubnetID) "
+ "not implemented yet");
+ return (Lease4Ptr());
+}
+
+
+// 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 {
+
+ // Bind the input parameters to the statement
+ int status = mysql_stmt_bind_param(statements_[stindex], inbind);
+ checkError(status, stindex, "unable to bind WHERE clause parameter");
+
+ // Set up the SELECT clause
+ std::vector<MYSQL_BIND> outbind = exchange6_->createBindForReceive();
+
+ // 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");
+
+ // Execute the statement
+ status = mysql_stmt_execute(statements_[stindex]);
+ checkError(status, stindex, "unable to execute");
+}
+
+
+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));
+
+ std::string addr6 = addr.toText();
+ unsigned long addr6_length = addr6.size();
+
+ // 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_STRING;
+ inbind[0].buffer = const_cast<char*>(addr6.c_str());
+ 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.
+ }
+
+ return (result);
+}
+
+
+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];
+ memset(inbind, 0, sizeof(inbind));
+
+ // In the following statement, the DUID is being read. However, the
+ // MySQL C interface does not use "const", so the "buffer" element
+ // is declared as "char*" instead of "const char*". To resolve this,
+ // the "const" is discarded before the uint8_t* is cast to char*.
+ //
+ // 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
+ // 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.
+ const vector<uint8_t>& duid_vector = duid.getDuid();
+ unsigned long duid_length = duid_vector.size();
+ inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+ inbind[0].buffer = reinterpret_cast<char*>(
+ const_cast<uint8_t*>(&duid_vector[0]));
+ inbind[0].buffer_length = duid_length;
+ inbind[0].length = &duid_length;
+
+ // 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");
+
+ // Fetch the data. There could be multiple rows, so we need to iterate
+ // until all data has been retrieved.
+ 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
+ ;
+ }
+
+ return (result);
+}
+
+
+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];
+ memset(inbind, 0, sizeof(inbind));
+
+ // See the earlier description of the use of "const_cast" when accessing
+ // the DUID for an explanation of the reason.
+ const vector<uint8_t>& duid_vector = duid.getDuid();
+ unsigned long duid_length = duid_vector.size();
+ inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+ inbind[0].buffer = reinterpret_cast<char*>(
+ const_cast<uint8_t*>(&duid_vector[0]));
+ inbind[0].buffer_length = duid_length;
+ inbind[0].length = &duid_length;
+
+ // IAID
+ inbind[1].buffer_type = MYSQL_TYPE_LONG;
+ inbind[1].buffer = reinterpret_cast<char*>(&iaid);
+ inbind[1].is_unsigned = static_cast<my_bool>(1);
+
+ // 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);
+
+ // 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();
+
+ // TODO: check for more than one row returned. At present, just
+ // ignore the excess and take the first.
+
+ } 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 the action is a no-op.
+ }
+
+ return (result);
+}
+
+
+void
+MySqlLeaseMgr::updateLease4(const Lease4Ptr& /* lease4 */) {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::updateLease4(const Lease4Ptr&) "
+ "not implemented yet");
+}
+
+
+void
+MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
+ const StatementIndex stindex = UPDATE_LEASE6;
+
+ // Create the MYSQL_BIND array for the data being updated
+ std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
+
+ // Set up the WHERE clause value
+ MYSQL_BIND where;
+ memset(&where, 0, sizeof(where));
+
+ std::string addr6 = lease->addr_.toText();
+ unsigned long addr6_length = addr6.size();
+
+ // See the earlier description of the use of "const_cast" when accessing
+ // the address for an explanation of the reason.
+ where.buffer_type = MYSQL_TYPE_STRING;
+ where.buffer = const_cast<char*>(addr6.c_str());
+ where.buffer_length = addr6_length;
+ 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");
+
+ // 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);
+ }
+}
+
+
+bool
+MySqlLeaseMgr::deleteLease4(const isc::asiolink::IOAddress& /* addr */) {
+ isc_throw(NotImplemented, "MySqlLeaseMgr::deleteLease4(const IOAddress&) "
+ "not implemented yet");
+ return (false);
+}
+
+
+bool
+MySqlLeaseMgr::deleteLease6(const isc::asiolink::IOAddress& addr) {
+ const StatementIndex stindex = DELETE_LEASE6;
+
+ // Set up the WHERE clause value
+ MYSQL_BIND inbind[1];
+ memset(inbind, 0, sizeof(inbind));
+
+ std::string addr6 = addr.toText();
+ unsigned long addr6_length = addr6.size();
+
+ // 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_STRING;
+ inbind[0].buffer = const_cast<char*>(addr6.c_str());
+ 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);
+}
+
+
+std::string
+MySqlLeaseMgr::getName() const {
+ std::string name = "";
+ try {
+ name = getParameter("name");
+ } catch (...) {
+ ;
+ }
+ return (name);
+}
+
+
+std::string
+MySqlLeaseMgr::getDescription() const {
+ return (std::string("MySQL Database"));
+}
+
+
+std::pair<uint32_t, uint32_t>
+MySqlLeaseMgr::getVersion() const {
+ const StatementIndex stindex = GET_VERSION;
+
+ uint32_t major; // Major version number
+ uint32_t minor; // Minor version number
+
+ // Execute the prepared statement
+ int status = mysql_stmt_execute(statements_[stindex]);
+ if (status != 0) {
+ isc_throw(DbOperationError, "unable to execute <"
+ << text_statements_[stindex] << "> - reason: " <<
+ mysql_error(mysql_));
+ }
+
+ // Bind the output of the statement to the appropriate variables.
+ MYSQL_BIND bind[2];
+ memset(bind, 0, sizeof(bind));
+
+ bind[0].buffer_type = MYSQL_TYPE_LONG;
+ bind[0].is_unsigned = 1;
+ bind[0].buffer = &major;
+ bind[0].buffer_length = sizeof(major);
+
+ bind[1].buffer_type = MYSQL_TYPE_LONG;
+ bind[1].is_unsigned = 1;
+ bind[1].buffer = &minor;
+ bind[1].buffer_length = sizeof(minor);
+
+ status = mysql_stmt_bind_result(statements_[stindex], bind);
+ if (status != 0) {
+ isc_throw(DbOperationError, "unable to bind result set: " <<
+ mysql_error(mysql_));
+ }
+
+ // Fetch the data and set up the "release" object to release associated
+ // resources when this method exits then retrieve the data.
+ MySqlFreeResult fetch_release(statements_[stindex]);
+ status = mysql_stmt_fetch(statements_[stindex]);
+ if (status != 0) {
+ isc_throw(DbOperationError, "unable to obtain result set: " <<
+ mysql_error(mysql_));
+ }
+
+ return (std::make_pair(major, minor));
+}
+
+
+void
+MySqlLeaseMgr::commit() {
+ if (mysql_commit(mysql_) != 0) {
+ isc_throw(DbOperationError, "commit failed: " << mysql_error(mysql_));
+ }
+}
+
+
+void
+MySqlLeaseMgr::rollback() {
+ if (mysql_rollback(mysql_) != 0) {
+ isc_throw(DbOperationError, "rollback failed: " << mysql_error(mysql_));
+ }
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcp/mysql_lease_mgr.h b/src/lib/dhcp/mysql_lease_mgr.h
new file mode 100644
index 0000000..8712c33
--- /dev/null
+++ b/src/lib/dhcp/mysql_lease_mgr.h
@@ -0,0 +1,444 @@
+// 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef MYSQL_LEASE_MGR_H
+#define MYSQL_LEASE_MGR_H
+
+#include <time.h>
+#include <boost/scoped_ptr.hpp>
+#include <mysql.h>
+#include <dhcp/lease_mgr.h>
+
+namespace isc {
+namespace dhcp {
+
+// Define the current database schema values
+
+const uint32_t CURRENT_VERSION_VERSION = 0;
+const uint32_t CURRENT_VERSION_MINOR = 1;
+
+
+// Forward declaration of the Lease6 exchange object. This class is defined
+// in the .cc file.
+class MySqlLease6Exchange;
+
+
+/// @brief MySQL Lease Manager
+///
+/// This is a concrete API for the backend for the MySQL database.
+class MySqlLeaseMgr : public LeaseMgr {
+public:
+ /// @brief Constructor
+ ///
+ /// Uses the following keywords in the parameters passed to it to
+ /// connect to the database:
+ /// - name - Name of the database to which to connect (mandatory)
+ /// - host - Host to which to connect (optional, defaults to "localhost")
+ /// - user - Username under which to connect (optional)
+ /// - password - Password for "user" on the database (optional)
+ ///
+ /// If the database is successfully opened, the version number in the
+ /// schema_version table will be checked against hard-coded value in
+ /// the implementation file.
+ ///
+ /// Finally, all the SQL commands are pre-compiled.
+ ///
+ /// @param parameters A data structure relating keywords and values
+ /// concerned with the database.
+ ///
+ /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
+ /// @throw isc::dhcp::DbOpenError Error opening the database
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ MySqlLeaseMgr(const ParameterMap& parameters);
+
+ /// @brief Destructor (closes database)
+ virtual ~MySqlLeaseMgr();
+
+ /// @brief Adds an IPv4 lease.
+ ///
+ /// @param lease lease to be added
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ virtual bool addLease(const Lease4Ptr& lease);
+
+ /// @brief Adds an IPv6 lease.
+ ///
+ /// @param lease lease to be added
+ ///
+ /// @result true if the lease was added, false if not (because a lease
+ /// with the same address was already there).
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ virtual bool addLease(const Lease6Ptr& lease);
+
+ /// @brief Return IPv4 lease for specified IPv4 address and subnet_id
+ ///
+ /// This method is used to get a lease for specific subnet_id. There can be
+ /// at most one lease for any given subnet, so this method returns a single
+ /// pointer.
+ ///
+ /// @param addr address of the sought lease
+ /// @param subnet_id ID of the subnet the lease must belong to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr,
+ SubnetID subnet_id) const;
+
+ /// @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
+ /// 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
+ /// a single lease, not a container of leases.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+
+
+ /// @brief Returns existing IPv4 leases for specified hardware address.
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param hwaddr hardware address of the client
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address
+ /// and a subnet
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param hwaddr hardware address of the client
+ /// @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)
+ virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
+ SubnetID subnet_id) const;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param clientid client identifier
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const ClientId& clientid) const;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param clientid client identifier
+ /// @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)
+ virtual Lease4Ptr getLease4(const ClientId& clientid,
+ SubnetID subnet_id) const;
+
+ /// @brief Returns existing IPv6 lease for a given IPv6 address.
+ ///
+ /// For a given address, we assume that there will be only one lease.
+ /// The assumtion here is that there will not be site or link-local
+ /// addresses used, so there is no way of having address duplication.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @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::DbOperationError An operation on the open database has
+ /// failed.
+ virtual Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
+
+ /// @brief Returns existing IPv6 leases for a given DUID+IA combination
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease6Collection getLease6(const DUID& duid,
+ uint32_t iaid) const;
+
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ /// @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)
+ virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
+ SubnetID subnet_id) const;
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ virtual void updateLease4(const Lease4Ptr& lease4);
+
+ /// @brief Updates IPv6 lease.
+ ///
+ /// @param lease6 The lease to be updated.
+ ///
+ /// @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 updateLease6(const Lease6Ptr& lease6);
+
+ /// @brief Deletes an IPv4 lease.
+ ///
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ virtual bool deleteLease4(const isc::asiolink::IOAddress& addr);
+
+ /// @brief Deletes an IPv6 lease.
+ ///
+ /// @param addr IPv6 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 deleteLease6(const isc::asiolink::IOAddress& addr);
+
+ /// @brief Returns backend name.
+ ///
+ /// Each backend have specific name, e.g. "mysql" or "sqlite".
+ virtual std::string getName() const;
+
+ /// @brief Returns description of the backend.
+ ///
+ /// This description may be multiline text that describes the backend.
+ virtual std::string getDescription() const;
+
+ /// @brief Returns backend version.
+ ///
+ /// @return Version number as a pair of unsigned integers. "first" is the
+ /// major version number, "second" the minor number.
+ ///
+ /// @todo: We will need to implement 3 version functions eventually:
+ /// A. abstract API version
+ /// B. backend version
+ /// C. database version (stored in the database scheme)
+ ///
+ /// and then check that:
+ /// B>=A and B=C (it is ok to have newer backend, as it should be backward
+ /// compatible)
+ /// Also if B>C, some database upgrade procedure may be triggered
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+ /// @brief Commit Transactions
+ ///
+ /// Commits all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ ///
+ /// @throw DbOperationError Iif the commit failed.
+ virtual void commit();
+
+ /// @brief Rollback Transactions
+ ///
+ /// Rolls back all pending database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ ///
+ /// @throw DbOperationError If the rollback failed.
+ virtual void rollback();
+
+ ///@{
+ /// The following methods are used to convert between times and time
+ /// intervals stored in the Lease object, and the times stored in the
+ /// database. The reason for the difference is because in the DHCP server,
+ /// the cltt (Client Time Since Last Transmission) is the natural data; in
+ /// the lease file - which may be read by the user - it is the expiry time
+ /// of the lease.
+
+ /// @brief Convert Lease Time to Database Times
+ ///
+ /// Within the DHCP servers, times are stored as client last transmit time
+ /// and valid lifetime. In the database, the information is stored as
+ /// valid lifetime and "expire" (time of expiry of the lease). They are
+ /// related by the equation:
+ ///
+ /// - expire = client last transmit time + valid lifetime
+ ///
+ /// This method converts from the times in the lease object into times
+ /// able to be added to the database.
+ ///
+ /// @param cltt Client last transmit time
+ /// @param valid_lifetime Valid lifetime
+ /// @param expire Reference to MYSQL_TIME object where the expiry time of
+ /// the lease will be put.
+ static
+ void convertToDatabaseTime(time_t cltt, uint32_t valid_lifetime,
+ MYSQL_TIME& expire);
+
+ /// @brief Convert Database Time to Lease Times
+ ///
+ /// Within the database, time is stored as "expire" (time of expiry of the
+ /// lease) and valid lifetime. In the DHCP server, the information is
+ /// stored client last transmit time and valid lifetime. These are related
+ /// by the equation:
+ ///
+ /// - client last transmit time = expire - valid_lifetime
+ ///
+ /// This method converts from the times in the database into times
+ /// able to be inserted into the lease object.
+ ///
+ /// @param expire Reference to MYSQL_TIME object from where the expiry
+ /// time of the lease is taken.
+ /// @param valid_lifetime lifetime of the lease.
+ /// @param cltt Reference to location where client last transmit time
+ /// is put.
+ static
+ void convertFromDatabaseTime(const MYSQL_TIME& expire,
+ uint32_t valid_lifetime, time_t& cltt);
+ ///@}
+
+ /// @brief Statement Tags
+ ///
+ /// The contents of the enum are indexes into the list of SQL statements
+ enum StatementIndex {
+ DELETE_LEASE6, // Delete from lease6 by address
+ GET_LEASE6_ADDR, // Get lease6 by address
+ GET_LEASE6_DUID_IAID, // Get lease6 by DUID and IAID
+ GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and Subnet ID
+ GET_VERSION, // Obtain version number
+ INSERT_LEASE6, // Add entry to lease6 table
+ UPDATE_LEASE6, // Update a Lease6 entry
+ NUM_STATEMENTS // Number of statements
+ };
+
+private:
+ /// @brief Prepare Single Statement
+ ///
+ /// Creates a prepared statement from the text given and adds it to the
+ /// statements_ vector at the given index.
+ ///
+ /// @param index Index into the statements_ vector into which the text
+ /// should be placed. The vector must be big enough for the index
+ /// to be valid, else an exception will be thrown.
+ /// @param text Text of the SQL statement to be prepared.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::InvalidParameter 'index' is not valid for the vector.
+ void prepareStatement(StatementIndex index, const char* text);
+
+ /// @brief Prepare statements
+ ///
+ /// Creates the prepared statements for all of the SQL statements used
+ /// by the MySQL backend.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ /// @throw isc::InvalidParameter 'index' is not valid for the vector. This
+ /// represents an internal error within the code.
+ void prepareStatements();
+
+ /// @brief Open Database
+ ///
+ /// Opens the database using the information supplied in the parameters
+ /// passed to the constructor.
+ ///
+ /// @throw NoDatabaseName Mandatory database name not given
+ /// @throw DbOpenError Error opening the database
+ void openDatabase();
+
+ /// @brief Binds Parameters and Executes
+ ///
+ /// 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.
+ ///
+ /// The data can be retrieved using mysql_stmt_fetch and the getLeaseData()
+ /// method on the exchange6 object.
+ ///
+ /// @param stindex Index of prepared statement to be executed
+ /// @param inbind Array of MYSQL_BIND objects representing the parameters.
+ /// (Note that the number is determined by the number of parameters
+ /// in the statement.)
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ void bind6AndExecute(StatementIndex stindex, MYSQL_BIND* inbind) const;
+
+ /// @brief Check Error and Throw Exception
+ ///
+ /// Virtually all MySQL functions return a status which, if non-zero,
+ /// indicates an error. This inline function conceals a lot of error
+ /// checking/exception-throwing code.
+ ///
+ /// @param status Status code: non-zero implies an error
+ /// @param index Index of statement that caused the error
+ /// @param what High-level description of the error
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ inline void checkError(int status, StatementIndex index,
+ const char* what) const {
+ if (status != 0) {
+ isc_throw(DbOperationError, what << " for <" <<
+ text_statements_[index] << ">, reason: " <<
+ mysql_error(mysql_) << " (error code " <<
+ mysql_errno(mysql_) << ")");
+ }
+ }
+
+ // 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_;
+ MYSQL* mysql_; ///< MySQL context object
+ std::vector<std::string> text_statements_; ///< Raw text of statements
+ std::vector<MYSQL_STMT*> statements_; ///< Prepared statements
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // MYSQL_LEASE_MGR_H
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index 080a869..4617c26 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.h
@@ -78,9 +78,11 @@ public:
/// @param u universe of the option (V4 or V6)
/// @param type option-type
/// @param buf option-buffer
- /// @throw isc::InvalidOperation if there is no factory function
- /// registered for specified option type.
+ ///
/// @return instance of option.
+ ///
+ /// @throw isc::InvalidOperation if there is no factory function
+ /// registered for specified option type.
static OptionPtr factory(Option::Universe u,
uint16_t type,
const OptionBuffer& buf);
@@ -95,9 +97,11 @@ public:
///
/// @param u universe of the option (V4 or V6)
/// @param type option-type
- /// @throw isc::InvalidOperation if there is no factory function
- /// registered for specified option type.
+ ///
/// @return instance of option.
+ ///
+ /// @throw isc::InvalidOperation if there is no factory function
+ /// registered for specified option type.
static OptionPtr factory(Option::Universe u, uint16_t type) {
return factory(u, type, OptionBuffer());
}
@@ -240,21 +244,21 @@ public:
/// @brief Returns content of first byte.
///
- /// @exception OutOfRange Thrown if the option has a length of 0.
+ /// @throw isc::OutOfRange Thrown if the option has a length of 0.
///
/// @return value of the first byte
uint8_t getUint8();
/// @brief Returns content of first word.
///
- /// @exception OutOfRange Thrown if the option has a length less than 2.
+ /// @throw isc::OutOfRange Thrown if the option has a length less than 2.
///
/// @return uint16_t value stored on first two bytes
uint16_t getUint16();
/// @brief Returns content of first double word.
///
- /// @exception OutOfRange Thrown if the option has a length less than 4.
+ /// @throw isc::OutOfRange Thrown if the option has a length less than 4.
///
/// @return uint32_t value stored on first four bytes
uint32_t getUint32();
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 57055a7..bf5e2e7 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -4,6 +4,9 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/dhcp/tests\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+# Temp
+AM_CPPFLAGS += -I/usr/include/mysql -DBIG_JOINS=1 -fno-strict-aliasing
+AM_CPPFLAGS += -DUNIV_LINUX
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -26,30 +29,38 @@ TESTS =
if HAVE_GTEST
TESTS += libdhcp++_unittests libdhcpsrv_unittests
libdhcp___unittests_SOURCES = run_unittests.cc
-libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
-libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
-libdhcp___unittests_SOURCES += option6_ia_unittest.cc
-libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
-libdhcp___unittests_SOURCES += option6_int_unittest.cc
+libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
+libdhcp___unittests_SOURCES += option6_ia_unittest.cc
+libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
libdhcp___unittests_SOURCES += option6_int_array_unittest.cc
-libdhcp___unittests_SOURCES += option_unittest.cc
+libdhcp___unittests_SOURCES += option6_int_unittest.cc
libdhcp___unittests_SOURCES += option_definition_unittest.cc
-libdhcp___unittests_SOURCES += pkt6_unittest.cc
+libdhcp___unittests_SOURCES += option_unittest.cc
libdhcp___unittests_SOURCES += pkt4_unittest.cc
-libdhcp___unittests_SOURCES += duid_unittest.cc
+libdhcp___unittests_SOURCES += pkt6_unittest.cc
+libdhcp___unittests_SOURCES += schema_copy.h
libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
libdhcpsrv_unittests_SOURCES = run_unittests.cc
-libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc triplet_unittest.cc
-libdhcpsrv_unittests_SOURCES += pool_unittest.cc subnet_unittest.cc
libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
-libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
+libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += duid_unittest.cc
+libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
+libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
+if HAVE_MYSQL
+libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
+endif
+libdhcpsrv_unittests_SOURCES += pool_unittest.cc
+libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
+libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
libdhcpsrv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
@@ -57,9 +68,13 @@ libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
libdhcpsrv_unittests_LDADD = $(GTEST_LDADD)
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+if HAVE_MYSQL
+libdhcpsrv_unittests_CPPFLAGS += $(MYSQL_CPPFLAGS)
+libdhcpsrv_unittests_LDFLAGS += $(MYSQL_LIBS)
+endif
if USE_CLANGPP
diff --git a/src/lib/dhcp/tests/alloc_engine_unittest.cc b/src/lib/dhcp/tests/alloc_engine_unittest.cc
index 91e08d8..456ad53 100644
--- a/src/lib/dhcp/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcp/tests/alloc_engine_unittest.cc
@@ -15,6 +15,7 @@
#include <config.h>
#include <asiolink/io_address.h>
#include <dhcp/lease_mgr.h>
+#include <dhcp/lease_mgr_factory.h>
#include <dhcp/duid.h>
#include <dhcp/alloc_engine.h>
#include <dhcp/cfgmgr.h>
@@ -30,7 +31,6 @@ using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
-using namespace isc::dhcp::test; // Memfile_LeaseMgr
namespace {
@@ -59,7 +59,7 @@ public:
subnet_->addPool6(pool_);
cfg_mgr.addSubnet6(subnet_);
- leasemgr_ = new Memfile_LeaseMgr("");
+ factory_.create("type=memfile");
}
void checkLease6(const Lease6Ptr& lease) {
@@ -82,29 +82,26 @@ public:
}
~AllocEngineTest() {
- LeaseMgr::instance().destroy_instance();
- leasemgr_ = NULL;
+ factory_.destroy();
}
DuidPtr duid_;
uint32_t iaid_;
Subnet6Ptr subnet_;
Pool6Ptr pool_;
- LeaseMgr* leasemgr_;
+ LeaseMgrFactory factory_;
};
// This test checks if the Allocation Engine can be instantiated and that it
// parses parameters string properly.
TEST_F(AllocEngineTest, constructor) {
- AllocEngine* x = NULL;
+ boost::scoped_ptr<AllocEngine> x;
// Hashed and random allocators are not supported yet
- ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_HASHED, 5), NotImplemented);
- ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_RANDOM, 5), NotImplemented);
+ ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_HASHED, 5)), NotImplemented);
+ ASSERT_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_RANDOM, 5)), NotImplemented);
- ASSERT_NO_THROW(x = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
-
- delete x;
+ ASSERT_NO_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
}
/// @todo: This method is taken from mysql_lease_mgr_utilities.cc from ticket
@@ -119,7 +116,6 @@ detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
EXPECT_EQ(first->prefixlen_, second->prefixlen_);
EXPECT_EQ(first->iaid_, second->iaid_);
- EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
EXPECT_TRUE(*first->duid_ == *second->duid_);
EXPECT_EQ(first->preferred_lft_, second->preferred_lft_);
EXPECT_EQ(first->valid_lft_, second->valid_lft_);
@@ -144,7 +140,7 @@ TEST_F(AllocEngineTest, simpleAlloc) {
checkLease6(lease);
// Check that the lease is indeed in LeaseMgr
- Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
@@ -167,7 +163,7 @@ TEST_F(AllocEngineTest, fakeAlloc) {
checkLease6(lease);
// Check that the lease is NOT in LeaseMgr
- Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
ASSERT_FALSE(from_mgr);
}
@@ -192,7 +188,7 @@ TEST_F(AllocEngineTest, allocWithValidHint) {
checkLease6(lease);
// Check that the lease is indeed in LeaseMgr
- Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
@@ -210,7 +206,7 @@ TEST_F(AllocEngineTest, allocWithUsedHint) {
DuidPtr duid2 = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
Lease6Ptr used(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1f"),
duid2, 1, 2, 3, 4, 5, subnet_->getID()));
- ASSERT_TRUE(LeaseMgr::instance().addLease(used));
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
// another client comes in and request an address that is in pool, but
// unfortunately it is used already. The same address must not be allocated
@@ -231,7 +227,7 @@ TEST_F(AllocEngineTest, allocWithUsedHint) {
checkLease6(lease);
// Check that the lease is indeed in LeaseMgr
- Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
@@ -261,7 +257,7 @@ TEST_F(AllocEngineTest, allocBogusHint) {
checkLease6(lease);
// Check that the lease is indeed in LeaseMgr
- Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
@@ -271,15 +267,13 @@ TEST_F(AllocEngineTest, allocBogusHint) {
// This test verifies that the allocator picks addresses that belong to the
// pool
TEST_F(AllocEngineTest, IterativeAllocator) {
- NakedAllocEngine::Allocator* alloc = new NakedAllocEngine::IterativeAllocator();
+ boost::scoped_ptr<NakedAllocEngine::Allocator>
+ alloc(new NakedAllocEngine::IterativeAllocator());
for (int i = 0; i < 1000; ++i) {
IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::"));
-
EXPECT_TRUE(subnet_->inPool(candidate));
}
-
- delete alloc;
}
diff --git a/src/lib/dhcp/tests/lease_mgr_factory_unittest.cc b/src/lib/dhcp/tests/lease_mgr_factory_unittest.cc
new file mode 100644
index 0000000..37063b2
--- /dev/null
+++ b/src/lib/dhcp/tests/lease_mgr_factory_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2011-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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/lease_mgr_factory.h>
+
+using namespace std;
+using namespace isc::dhcp;
+
+// This set of tests only check the parsing functions of LeaseMgrFactory.
+// Tests of the LeaseMgr create/instance/destroy are implicitly carried out
+// in the tests for the different concrete lease managers (e.g. MySqlLeaseMgr).
+
+namespace {
+// empty class for now, but may be extended once Addr6 becomes bigger
+class LeaseMgrFactoryTest : public ::testing::Test {
+public:
+ LeaseMgrFactoryTest() {
+ }
+};
+
+// This test checks if the LeaseMgr can be instantiated and that it
+// parses parameters string properly.
+TEST_F(LeaseMgrFactoryTest, parse) {
+
+ std::map<std::string, std::string> parameters = LeaseMgrFactory::parse(
+ "param1=value1 param2=value2 param3=value3");
+
+ EXPECT_EQ("value1", parameters["param1"]);
+ EXPECT_EQ("value2", parameters["param2"]);
+ EXPECT_TRUE(parameters.find("type") == parameters.end());
+}
+
+}; // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc
index 66521ba..64ce9b1 100644
--- a/src/lib/dhcp/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-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
@@ -16,148 +16,238 @@
#include <iostream>
#include <sstream>
#include <gtest/gtest.h>
+
#include <asiolink/io_address.h>
#include <dhcp/lease_mgr.h>
-#include <dhcp/duid.h>
-#include <dhcp/memfile_lease_mgr.h>
using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
-using namespace isc::dhcp::test; // Memfile_LeaseMgr
-namespace {
-// empty class for now, but may be extended once Addr6 becomes bigger
-class LeaseMgrTest : public ::testing::Test {
+// This is a concrete implementation of a Lease database. It does not do
+// anything useful and is used for abstract LeaseMgr class testing.
+class ConcreteLeaseMgr : public LeaseMgr {
public:
- LeaseMgrTest() {
- }
-};
-// This test checks if the LeaseMgr can be instantiated and that it
-// parses parameters string properly.
-TEST_F(LeaseMgrTest, constructor) {
-
- // should not throw any exceptions here
- Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr("");
- delete leaseMgr;
-
- leaseMgr = new Memfile_LeaseMgr("param1=value1 param2=value2");
-
- EXPECT_EQ("value1", leaseMgr->getParameter("param1"));
- EXPECT_EQ("value2", leaseMgr->getParameter("param2"));
- EXPECT_THROW(leaseMgr->getParameter("param3"), BadValue);
+ /// @brief The sole lease manager constructor
+ ///
+ /// dbconfig is a generic way of passing parameters. Parameters
+ /// are passed in the "name=value" format, separated by spaces.
+ /// Values may be enclosed in double quotes, if needed.
+ ///
+ /// @param parameters A data structure relating keywords and values
+ /// concerned with the database.
+ ConcreteLeaseMgr(const LeaseMgr::ParameterMap& parameters)
+ : LeaseMgr(parameters)
+ {}
+
+ /// @brief Destructor
+ virtual ~ConcreteLeaseMgr()
+ {}
+
+ /// @brief Adds an IPv4 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(const Lease4Ptr&) {
+ return (false);
+ }
- delete leaseMgr;
-}
+ /// @brief Adds an IPv6 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(const Lease6Ptr&) {
+ return (false);
+ }
-// There's no point in calling any other methods in LeaseMgr, as they
-// are purely virtual, so we would only call Memfile_LeaseMgr methods.
-// Those methods are just stubs that does not return anything.
-// It seems likely that we will need to extend the memfile code for
-// allocation engine tests, so we may implement tests that call
-// Memfile_LeaseMgr methods then.
+ /// @brief Returns existing IPv4 lease for specified IPv4 address.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&) const {
+ return (Lease4Ptr());
+ }
-TEST_F(LeaseMgrTest, addGetDelete) {
- Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr("");
+ /// @brief Returns existing IPv4 lease for specific address and subnet
+ /// @param addr address of the searched lease
+ /// @param subnet_id ID of the subnet the lease must belong to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&,
+ SubnetID) const {
+ return (Lease4Ptr());
+ }
- IOAddress addr("2001:db8:1::456");
+ /// @brief Returns existing IPv4 leases for specified hardware address.
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param hwaddr hardware address of the client
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const HWAddr&) const {
+ return (Lease4Collection());
+ }
- uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
- DuidPtr duid(new DUID(llt, sizeof(llt)));
+ /// @brief Returns existing IPv4 leases for specified hardware address
+ /// and a subnet
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param hwaddr hardware address of the client
+ /// @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)
+ virtual Lease4Ptr getLease4(const HWAddr&, SubnetID) const {
+ return (Lease4Ptr());
+ }
- uint32_t iaid = 7; // just a number
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// @param clientid client identifier
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const ClientId&) const {
+ return (Lease4Collection());
+ }
- SubnetID subnet_id = 8; // just another number
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param clientid client identifier
+ /// @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)
+ virtual Lease4Ptr getLease4(const ClientId&, SubnetID) const {
+ return (Lease4Ptr());
+ }
- Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr,
- duid, iaid, 100, 200, 50, 80,
- subnet_id));
+ /// @brief Returns existing IPv6 lease for a given IPv6 address.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ Lease6Ptr getLease6(const isc::asiolink::IOAddress&) const {
+ return (Lease6Ptr());
+ }
- EXPECT_TRUE(leaseMgr->addLease(lease));
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ ///
+ /// @return collection of IPv6 leases
+ Lease6Collection getLease6(const DUID&, uint32_t) const {
+ return (Lease6Collection());
+ }
- // should not be allowed to add a second lease with the same address
- EXPECT_FALSE(leaseMgr->addLease(lease));
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ /// @param subnet_id identifier of the subnet the lease must belong to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ Lease6Ptr getLease6(const DUID&, uint32_t, SubnetID) const {
+ return (Lease6Ptr());
+ }
- Lease6Ptr x = leaseMgr->getLease6(IOAddress("2001:db8:1::234"));
- EXPECT_EQ(Lease6Ptr(), x);
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ void updateLease4(const Lease4Ptr&) {}
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ void updateLease6(const Lease6Ptr&) {}
+
+ /// @brief Deletes a lease.
+ ///
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ bool deleteLease4(const isc::asiolink::IOAddress&) {
+ return (false);
+ }
- x = leaseMgr->getLease6(IOAddress("2001:db8:1::456"));
- ASSERT_TRUE(x);
+ /// @brief Deletes a lease.
+ ///
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ bool deleteLease6(const isc::asiolink::IOAddress&) {
+ return (false);
+ }
- EXPECT_EQ(x->addr_.toText(), addr.toText());
- EXPECT_TRUE(*x->duid_ == *duid);
- EXPECT_EQ(x->iaid_, iaid);
- EXPECT_EQ(x->subnet_id_, subnet_id);
-
- // These are not important from lease management perspective, but
- // let's check them anyway.
- EXPECT_EQ(x->type_, Lease6::LEASE_IA_NA);
- EXPECT_EQ(x->preferred_lft_, 100);
- EXPECT_EQ(x->valid_lft_, 200);
- EXPECT_EQ(x->t1_, 50);
- EXPECT_EQ(x->t2_, 80);
-
- // Test getLease6(duid, iaid, subnet_id) - positive case
- Lease6Ptr y = leaseMgr->getLease6(*duid, iaid, subnet_id);
- ASSERT_TRUE(y);
- EXPECT_TRUE(*y->duid_ == *duid);
- EXPECT_EQ(y->iaid_, iaid);
- EXPECT_EQ(y->addr_.toText(), addr.toText());
-
- // Test getLease6(duid, iaid, subnet_id) - wrong iaid
- uint32_t invalid_iaid = 9; // no such iaid
- y = leaseMgr->getLease6(*duid, invalid_iaid, subnet_id);
- EXPECT_FALSE(y);
-
- uint32_t invalid_subnet_id = 999;
- y = leaseMgr->getLease6(*duid, iaid, invalid_subnet_id);
- EXPECT_FALSE(y);
-
- // truncated duid
- DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1));
- y = leaseMgr->getLease6(*invalid_duid, iaid, subnet_id);
- EXPECT_FALSE(y);
-
- // should return false - there's no such address
- EXPECT_FALSE(leaseMgr->deleteLease6(IOAddress("2001:db8:1::789")));
-
- // this one should succeed
- EXPECT_TRUE(leaseMgr->deleteLease6(IOAddress("2001:db8:1::456")));
-
- // after the lease is deleted, it should really be gone
- x = leaseMgr->getLease6(IOAddress("2001:db8:1::456"));
- EXPECT_EQ(Lease6Ptr(), x);
-
- delete leaseMgr;
-}
+ /// @brief Returns backend name.
+ ///
+ /// Each backend have specific name, e.g. "mysql" or "sqlite".
+ std::string getName() const {
+ return (std::string("concrete"));
+ }
-// This test checks there that leaseMgr is really a singleton and that
-// no more than one can be created.
-TEST_F(LeaseMgrTest, singleton) {
- Memfile_LeaseMgr* leaseMgr1 = NULL;
- Memfile_LeaseMgr* leaseMgr2 = NULL;
+ /// @brief Returns description of the backend.
+ ///
+ /// This description may be multiline text that describes the backend.
+ std::string getDescription() const {
+ return (std::string("This is a dummy concrete backend implementation."));
+ }
- EXPECT_THROW(LeaseMgr::instance(), InvalidOperation);
+ /// @brief Returns backend version.
+ std::pair<uint32_t, uint32_t> getVersion() const {
+ return (make_pair(uint32_t(0), uint32_t(0)));
+ }
- EXPECT_NO_THROW( leaseMgr1 = new Memfile_LeaseMgr("") );
+ /// @brief Commit transactions
+ void commit() {
+ }
- EXPECT_NO_THROW(LeaseMgr::instance());
+ /// @brief Rollback transactions
+ void rollback() {
+ }
+};
- // There can be only one instance of any LeaseMgr derived
- // objects instantiated at any time.
- ASSERT_THROW(leaseMgr2 = new Memfile_LeaseMgr(""), InvalidOperation);
+namespace {
+// empty class for now, but may be extended once Addr6 becomes bigger
+class LeaseMgrTest : public ::testing::Test {
+public:
+ LeaseMgrTest() {
+ }
+};
- delete leaseMgr1;
+// This test checks if the LeaseMgr can be instantiated and that it
+// parses parameters string properly.
+TEST_F(LeaseMgrTest, getParameter) {
- ASSERT_NO_THROW(leaseMgr2 = new Memfile_LeaseMgr("") );
+ LeaseMgr::ParameterMap pmap;
+ pmap[std::string("param1")] = std::string("value1");
+ pmap[std::string("param2")] = std::string("value2");
+ ConcreteLeaseMgr leasemgr(pmap);
- delete leaseMgr2;
+ EXPECT_EQ("value1", leasemgr.getParameter("param1"));
+ EXPECT_EQ("value2", leasemgr.getParameter("param2"));
+ EXPECT_THROW(leasemgr.getParameter("param3"), BadValue);
}
+// There's no point in calling any other methods in LeaseMgr, as they
+// are purely virtual, so we would only call ConcreteLeaseMgr methods.
+// Those methods are just stubs that do not return anything.
+
+// 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, ctor) {
+TEST(Lease6, Lease6Constructor) {
IOAddress addr("2001:db8:1::456");
@@ -187,5 +277,4 @@ TEST(Lease6, ctor) {
DuidPtr(), iaid, 100, 200, 50, 80,
subnet_id), InvalidOperation);
}
-
}; // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/memfile_lease_mgr_unittest.cc b/src/lib/dhcp/tests/memfile_lease_mgr_unittest.cc
new file mode 100644
index 0000000..c625a16
--- /dev/null
+++ b/src/lib/dhcp/tests/memfile_lease_mgr_unittest.cc
@@ -0,0 +1,130 @@
+// 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <gtest/gtest.h>
+#include <asiolink/io_address.h>
+
+#include <dhcp/lease_mgr.h>
+#include <dhcp/duid.h>
+#include <dhcp/memfile_lease_mgr.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+// empty class for now, but may be extended once Addr6 becomes bigger
+class MemfileLeaseMgrTest : public ::testing::Test {
+public:
+ MemfileLeaseMgrTest() {
+ }
+};
+
+// This test checks if the LeaseMgr can be instantiated and that it
+// parses parameters string properly.
+TEST_F(MemfileLeaseMgrTest, constructor) {
+
+ const LeaseMgr::ParameterMap pmap; // Empty parameter map
+ boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr;
+
+ ASSERT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
+}
+
+// There's no point in calling any other methods in LeaseMgr, as they
+// are purely virtual, so we would only call Memfile_LeaseMgr methods.
+// Those methods are just stubs that does not return anything.
+// It seems likely that we will need to extend the memfile code for
+// allocation engine tests, so we may implement tests that call
+// Memfile_LeaseMgr methods then.
+
+TEST_F(MemfileLeaseMgrTest, addGetDelete) {
+ const LeaseMgr::ParameterMap pmap; // Empty parameter map
+ boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr(new Memfile_LeaseMgr(pmap));
+
+ IOAddress addr("2001:db8:1::456");
+
+ 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 lease(new Lease6(Lease6::LEASE_IA_NA, addr,
+ duid, iaid, 100, 200, 50, 80,
+ subnet_id));
+
+ EXPECT_TRUE(lease_mgr->addLease(lease));
+
+ // should not be allowed to add a second lease with the same address
+ EXPECT_FALSE(lease_mgr->addLease(lease));
+
+ Lease6Ptr x = lease_mgr->getLease6(IOAddress("2001:db8:1::234"));
+ EXPECT_EQ(Lease6Ptr(), x);
+
+ x = lease_mgr->getLease6(IOAddress("2001:db8:1::456"));
+ ASSERT_TRUE(x);
+
+ EXPECT_EQ(x->addr_.toText(), addr.toText());
+ EXPECT_TRUE(*x->duid_ == *duid);
+ EXPECT_EQ(x->iaid_, iaid);
+ EXPECT_EQ(x->subnet_id_, subnet_id);
+
+ // These are not important from lease management perspective, but
+ // let's check them anyway.
+ EXPECT_EQ(x->type_, Lease6::LEASE_IA_NA);
+ EXPECT_EQ(x->preferred_lft_, 100);
+ EXPECT_EQ(x->valid_lft_, 200);
+ EXPECT_EQ(x->t1_, 50);
+ EXPECT_EQ(x->t2_, 80);
+
+ // Test getLease6(duid, iaid, subnet_id) - positive case
+ Lease6Ptr y = lease_mgr->getLease6(*duid, iaid, subnet_id);
+ ASSERT_TRUE(y);
+ EXPECT_TRUE(*y->duid_ == *duid);
+ EXPECT_EQ(y->iaid_, iaid);
+ EXPECT_EQ(y->addr_.toText(), addr.toText());
+
+ // Test getLease6(duid, iaid, subnet_id) - wrong iaid
+ uint32_t invalid_iaid = 9; // no such iaid
+ y = lease_mgr->getLease6(*duid, invalid_iaid, subnet_id);
+ EXPECT_FALSE(y);
+
+ uint32_t invalid_subnet_id = 999;
+ y = lease_mgr->getLease6(*duid, iaid, invalid_subnet_id);
+ EXPECT_FALSE(y);
+
+ // truncated duid
+ DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1));
+ y = lease_mgr->getLease6(*invalid_duid, iaid, subnet_id);
+ EXPECT_FALSE(y);
+
+ // should return false - there's no such address
+ EXPECT_FALSE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::789")));
+
+ // this one should succeed
+ EXPECT_TRUE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::456")));
+
+ // after the lease is deleted, it should really be gone
+ x = lease_mgr->getLease6(IOAddress("2001:db8:1::456"));
+ EXPECT_EQ(Lease6Ptr(), x);
+}
+
+// TODO: Write more memfile tests
+
+}; // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc
new file mode 100644
index 0000000..a9b09ff
--- /dev/null
+++ b/src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc
@@ -0,0 +1,732 @@
+// Copyright (C) 2011-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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <utility>
+#include <string>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/lease_mgr_factory.h>
+#include <dhcp/mysql_lease_mgr.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace std;
+
+namespace {
+
+// Creation of 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:
+// Database: keatest
+// Username: keatest
+// Password: keatest
+const char* VALID_TYPE = "type=mysql";
+const char* INVALID_TYPE = "type=unknown";
+const char* VALID_NAME = "name=keatest";
+const char* INVALID_NAME = "name=invalidname";
+const char* VALID_HOST = "host=localhost";
+const char* INVALID_HOST = "host=invalidhost";
+const char* VALID_USER = "user=keatest";
+const char* INVALID_USER = "user=invaliduser";
+const char* VALID_PASSWORD = "password=keatest";
+const char* INVALID_PASSWORD = "password=invalid";
+
+// Given a combination of strings above, produce a connection string.
+string connectionString(const char* type, const char* name, const char* host,
+ const char* user, const char* password) {
+ const string space = " ";
+ string result = "";
+
+ if (type != NULL) {
+ result += string(type);
+ }
+
+ if (name != NULL) {
+ if (! result.empty()) {
+ result += space;
+ }
+ result += string(name);
+ }
+
+ if (host != NULL) {
+ if (! result.empty()) {
+ result += space;
+ }
+ result += string(host);
+ }
+
+ if (user != NULL) {
+ if (! result.empty()) {
+ result += space;
+ }
+ result += string(user);
+ }
+
+ if (password != NULL) {
+ if (! result.empty()) {
+ result += space;
+ }
+ result += string(password);
+ }
+
+ return (result);
+}
+
+// Return valid connection string
+string
+validConnectionString() {
+ return (connectionString(VALID_TYPE, VALID_NAME, VALID_HOST,
+ VALID_USER, VALID_PASSWORD));
+}
+
+// @brief Clear everything from the database
+//
+// There is no error checking in this code: if something fails, one of the
+// tests will fall over.
+void destroySchema() {
+ // Initialise
+ MYSQL handle;
+ (void) mysql_init(&handle);
+
+ // Open database
+ (void) mysql_real_connect(&handle, "localhost", "keatest",
+ "keatest", "keatest", 0, NULL, 0);
+
+ // Get rid of everything in it.
+ for (int i = 0; destroy_statement[i] != NULL; ++i) {
+ (void) mysql_query(&handle, destroy_statement[i]);
+ }
+
+ // ... and close
+ (void) mysql_close(&handle);
+}
+
+// @brief Create the Schema
+//
+// Creates all the tables in what is assumed to be an empty database.
+//
+// There is no error checking in this code: if it fails, one of the tests
+// will fall over.
+void createSchema() {
+ // Initialise
+ MYSQL handle;
+ (void) mysql_init(&handle);
+
+ // Open database
+ (void) mysql_real_connect(&handle, "localhost", "keatest",
+ "keatest", "keatest", 0, NULL, 0);
+
+ // Get rid of everything in it.
+ for (int i = 0; create_statement[i] != NULL; ++i) {
+ (void) mysql_query(&handle, create_statement[i]);
+ }
+
+ // ... and close
+ (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.
+
+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) {
+
+ destroySchema();
+ createSchema();
+ try {
+ LeaseMgrFactory::create(validConnectionString());
+ } catch (...) {
+ std::cerr << "*** ERROR: unable to open database. The test\n"
+ "*** environment is broken and must be fixed before\n"
+ "*** the MySQL tests will run correctly.\n"
+ "*** The reason for the problem is described in the\n"
+ "*** accompanying exception output.\n";
+ throw;
+ }
+ 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.
+ virtual ~MySqlLeaseMgrTest() {
+ lmptr_->rollback();
+ LeaseMgrFactory::destroy();
+ destroySchema();
+ }
+
+ // @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).
+ Lease6Ptr initializeLease6(std::string address) {
+ Lease6Ptr lease(new Lease6());
+
+ // Set the address of the lease
+ lease->addr_ = IOAddress(address);
+
+ // Initialize unused fields.
+ 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 the other parameters. For historical reasons, L0_ADDRESS is not used.
+ if (address == L0_ADDRESS) {
+ 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
+
+ } else if (address == L1_ADDRESS) {
+ 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
+
+ } else if (address == L2_ADDRESS) {
+ 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
+
+ } else if (address == L3_ADDRESS) {
+ lease->type_ = Lease6::LEASE_IA_NA;
+ lease->prefixlen_ = 28;
+ lease->iaid_ = 0xfffffffe;
+ vector<uint8_t> duid;
+ for (uint8_t i = 31; i < 126; ++i) {
+ duid.push_back(i);
+ }
+ lease->duid_ = boost::shared_ptr<DUID>(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
+
+ } else if (address == L4_ADDRESS) {
+ // Same DUID and IAID as L1_ADDRESS
+ 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->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->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->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
+
+ } else {
+ // Unknown address, return an empty pointer.
+ lease.reset();
+
+ }
+
+ 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());
+
+ // Check they were created
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_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]);
+ }
+ }
+
+ return (leases);
+ }
+
+
+ // Member variables
+
+ LeaseMgr* lmptr_; // Pointer to the lease manager
+
+ 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
+
+ string L2_ADDRESS; // String form of address 2
+ IOAddress L2_IOADDRESS; // IOAddress form of L2_ADDRESS
+
+ string L3_ADDRESS; // String form of address 3
+ IOAddress L3_IOADDRESS; // IOAddress form of L3_ADDRESS
+
+ string L4_ADDRESS; // String form of address 4
+ IOAddress L4_IOADDRESS; // IOAddress form of L4_ADDRESS
+
+ string L5_ADDRESS; // String form of address 5
+ IOAddress L5_IOADDRESS; // IOAddress form of L5_ADDRESS
+
+ string L6_ADDRESS; // String form of address 6
+ IOAddress L6_IOADDRESS; // IOAddress form of L6_ADDRESS
+
+ 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.
+
+TEST(MySqlOpenTest, OpenDatabase) {
+
+ // Schema needs to be created for the test to work.
+ destroySchema();
+ createSchema();
+
+ // Check that lease manager open the database opens correctly and tidy up.
+ // If it fails, print the error message.
+ try {
+ LeaseMgrFactory::create(validConnectionString());
+ EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
+ LeaseMgrFactory::destroy();
+ } catch (const isc::Exception& ex) {
+ FAIL() << "*** ERROR: unable to open database, reason:\n"
+ << " " << ex.what() << "\n"
+ << "*** The test environment is broken and must be fixed\n"
+ << "*** before the MySQL tests will run correctly.\n";
+ }
+
+ // Check that attempting to get an instance of the lease manager when
+ // none is set throws an exception.
+ EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
+
+ // Check that wrong specification of backend throws an exception.
+ // (This is really a check on LeaseMgrFactory, but is convenient to
+ // perform here.)
+ EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+ NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+ InvalidParameter);
+ EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+ INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
+ InvalidType);
+
+ // Check that invalid login data causes an exception.
+ EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+ VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
+ DbOpenError);
+ EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+ VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
+ DbOpenError);
+ EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+ VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+ DbOpenError);
+ EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+ VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
+ DbOpenError);
+
+ // Check for missing parameters
+ EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+ VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+ NoDatabaseName);
+
+ // Tidy up after the test
+ destroySchema();
+}
+
+// @brief Check conversion functions
+TEST_F(MySqlLeaseMgrTest, CheckTimeConversion) {
+ const time_t cltt = time(NULL);
+ const uint32_t valid_lft = 86400; // 1 day
+ struct tm tm_expire;
+ MYSQL_TIME mysql_expire;
+
+ // Work out what the broken-down time will be for one day
+ // after the current time.
+ time_t expire_time = cltt + valid_lft;
+ (void) localtime_r(&expire_time, &tm_expire);
+
+ // Convert to the database time
+ MySqlLeaseMgr::convertToDatabaseTime(cltt, valid_lft, mysql_expire);
+
+ // Are the times the same?
+ EXPECT_EQ(tm_expire.tm_year + 1900, mysql_expire.year);
+ EXPECT_EQ(tm_expire.tm_mon + 1, mysql_expire.month);
+ EXPECT_EQ(tm_expire.tm_mday, mysql_expire.day);
+ EXPECT_EQ(tm_expire.tm_hour, mysql_expire.hour);
+ EXPECT_EQ(tm_expire.tm_min, mysql_expire.minute);
+ EXPECT_EQ(tm_expire.tm_sec, mysql_expire.second);
+ EXPECT_EQ(0, mysql_expire.second_part);
+ EXPECT_EQ(0, mysql_expire.neg);
+
+ // Convert back
+ time_t converted_cltt = 0;
+ MySqlLeaseMgr::convertFromDatabaseTime(mysql_expire, valid_lft, converted_cltt);
+ EXPECT_EQ(cltt, converted_cltt);
+}
+
+
+// @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() works
+TEST_F(MySqlLeaseMgrTest, CheckVersion) {
+ // Check version
+ pair<uint32_t, uint32_t> version;
+ ASSERT_NO_THROW(version = lmptr_->getVersion());
+ EXPECT_EQ(CURRENT_VERSION_VERSION, version.first);
+ EXPECT_EQ(CURRENT_VERSION_MINOR, version.second);
+}
+
+void
+detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
+ EXPECT_EQ(first->type_, second->type_);
+
+ // Compare address strings - odd things happen when they are different
+ // as the EXPECT_EQ 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_);
+}
+
+
+// @brief Check individual Lease6 methods
+//
+// Checks that the add/update/delete works. All are done within one
+// test so that "rollback" can be used to remove trace of the tests
+// from the database.
+//
+// Tests where a collection of leases can be returned are in the test
+// Lease6Collection.
+TEST_F(MySqlLeaseMgrTest, BasicLease6) {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+
+ // 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();
+
+ Lease6Ptr l_returned = lmptr_->getLease6(L1_IOADDRESS);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease6(leases[1], l_returned);
+
+ l_returned = lmptr_->getLease6(L2_IOADDRESS);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease6(leases[2], l_returned);
+
+ l_returned = lmptr_->getLease6(L3_IOADDRESS);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease6(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_FALSE(l_returned);
+ EXPECT_FALSE(lmptr_->deleteLease6(L1_IOADDRESS));
+
+ // Check that the second address is still there.
+ l_returned = lmptr_->getLease6(L2_IOADDRESS);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease6(leases[2], l_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, GetLease6Extended1) {
+ // 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
+
+ // Add them to the database
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the DUID and IAID of lease[1].
+ Lease6Collection returned = lmptr_->getLease6(*leases[1]->duid_,
+ leases[1]->iaid_);
+
+ // Should be three leases, matching leases[1], [4] and [5].
+ ASSERT_EQ(3, returned.size());
+
+ // Easiest way to check is to look at the addresses.
+ vector<string> addresses;
+ for (Lease6Collection::const_iterator i = returned.begin();
+ i != returned.end(); ++i) {
+ 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]);
+
+ // Check that nothing is returned when either the IAID or DUID match
+ // nothing.
+ returned = lmptr_->getLease6(*leases[1]->duid_, leases[1]->iaid_ + 1);
+ EXPECT_EQ(0, returned.size());
+
+ // Alter the leases[1] DUID to match nothing in the database.
+ vector<uint8_t> duid_vector = leases[1]->duid_->getDuid();
+ ++duid_vector[0];
+ DUID new_duid(duid_vector);
+ returned = lmptr_->getLease6(new_duid, leases[1]->iaid_);
+ EXPECT_EQ(0, returned.size());
+}
+
+
+
+// @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, GetLease6Extended2) {
+ // 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
+
+ // Add them to the database
+ for (int i = 0; i < leases.size(); ++i) {
+ EXPECT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Get the leases matching the DUID and IAID of lease[1].
+ Lease6Ptr returned = lmptr_->getLease6(*leases[1]->duid_,
+ leases[1]->iaid_,
+ leases[1]->subnet_id_);
+ ASSERT_TRUE(returned);
+ EXPECT_TRUE(*returned == *leases[1]);
+
+ // Modify each of the three parameters (DUID, IAID, Subnet ID) and
+ // check that nothing is returned.
+ returned = lmptr_->getLease6(*leases[1]->duid_, leases[1]->iaid_ + 1,
+ leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+
+ returned = lmptr_->getLease6(*leases[1]->duid_, leases[1]->iaid_,
+ leases[1]->subnet_id_ + 1);
+ EXPECT_FALSE(returned);
+
+ // Alter the leases[1] DUID to match nothing in the database.
+ vector<uint8_t> duid_vector = leases[1]->duid_->getDuid();
+ ++duid_vector[0];
+ DUID new_duid(duid_vector);
+ returned = lmptr_->getLease6(new_duid, leases[1]->iaid_,
+ leases[1]->subnet_id_);
+ EXPECT_FALSE(returned);
+}
+
+
+
+// @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
+
+ // 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);
+
+ // Modify some fields in lease 1 (not the address) and update it.
+ ++leases[1]->iaid_;
+ leases[1]->type_ = Lease6::LEASE_IA_PD;
+ 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);
+
+ // Alter the lease again and check.
+ ++leases[1]->iaid_;
+ leases[1]->type_ = Lease6::LEASE_IA_TA;
+ leases[1]->cltt_ += 6;
+ leases[1]->prefixlen_ = 93;
+ lmptr_->updateLease6(leases[1]);
+
+ l_returned.reset();
+ l_returned = lmptr_->getLease6(L1_IOADDRESS);
+ EXPECT_TRUE(l_returned);
+ detailCompareLease6(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);
+
+ // Try updating a lease not in the database.
+ EXPECT_THROW(lmptr_->updateLease6(leases[2]), isc::dhcp::NoSuchLease);
+}
+
+}; // Of anonymous namespace
diff --git a/src/lib/dhcp/tests/schema_copy.h b/src/lib/dhcp/tests/schema_copy.h
new file mode 100644
index 0000000..1dd796d
--- /dev/null
+++ b/src/lib/dhcp/tests/schema_copy.h
@@ -0,0 +1,85 @@
+// 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef SCHEMA_COPY_H
+#define SCHEMA_COPY_H
+
+namespace {
+
+// What follows is a set of statements that creates a copy of the schema
+// in the test database. It is used by the MySQL unit test prior to each
+// test.
+//
+// Each SQL statement is a single string. The statements are not terminated
+// 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
+
+// Deletion of existing tables.
+
+const char* destroy_statement[] = {
+ "DROP TABLE lease4",
+ "DROP TABLE lease6",
+ "DROP TABLE lease6_types",
+ "DROP TABLE schema_version",
+ NULL
+};
+
+// Creation of the new tables.
+
+const char* create_statement[] = {
+ "CREATE TABLE lease4 ("
+ "address INT UNSIGNED PRIMARY KEY NOT NULL,"
+ "hwaddr VARBINARY(20),"
+ "client_id VARBINARY(128),"
+ "lease_time INT UNSIGNED,"
+ "expire TIMESTAMP,"
+ "subnet_id INT UNSIGNED"
+ ") ENGINE = INNODB",
+
+ "CREATE TABLE lease6 ("
+ "address VARCHAR(40) PRIMARY KEY NOT NULL,"
+ "duid VARBINARY(128),"
+ "valid_lifetime INT UNSIGNED,"
+ "expire TIMESTAMP,"
+ "subnet_id INT UNSIGNED,"
+ "pref_lifetime INT UNSIGNED,"
+ "lease_type TINYINT,"
+ "iaid INT UNSIGNED,"
+ "prefix_len TINYINT UNSIGNED"
+ ") ENGINE = INNODB",
+
+ "CREATE TABLE lease6_types ("
+ "lease_type TINYINT PRIMARY KEY NOT NULL,"
+ "name VARCHAR(5)"
+ ")",
+
+ "INSERT INTO lease6_types VALUES (0, \"IA_NA\")",
+ "INSERT INTO lease6_types VALUES (1, \"IA_TA\")",
+ "INSERT INTO lease6_types VALUES (2, \"IA_PD\")",
+
+ "CREATE TABLE schema_version ("
+ "version INT PRIMARY KEY NOT NULL,"
+ "minor INT"
+ ")",
+
+ "INSERT INTO schema_version VALUES (0, 1)",
+
+ NULL
+};
+
+}; // Anonymous namespace
+
+#endif // SCHEMA_COPY_H
More information about the bind10-changes
mailing list