Parcourir la source

[3080] Initial PostgreSQL patch

 - patch contributed by David Carlier (thanks!)
 - updated to current master
 - fixed compliation issues
 - written developer's guide (pgsql setup steps)
 - many smaller changes
 - still WIP
Tomek Mrugalski il y a 11 ans
Parent
commit
a3d6102cfa

+ 72 - 0
configure.ac

@@ -969,6 +969,58 @@ AC_CHECK_HEADER(sys/filio.h)
 # ... and at the shell level, so Makefile.am can take action depending on this.
 # ... and at the shell level, so Makefile.am can take action depending on this.
 AM_CONDITIONAL(HAVE_MYSQL, test "$MYSQL_CONFIG" != "")
 AM_CONDITIONAL(HAVE_MYSQL, test "$MYSQL_CONFIG" != "")
 
 
+pg_config="no"
+AC_ARG_WITH([dhcp-pgsql],
+  AC_HELP_STRING([--with-dhcp-pgsql=PATH],
+    [path to the PostgreSQL 'pg_config' script]),
+    [pg_config="$withval"])
+
+if test "${pg_config}" = "yes" ; then
+    PG_CONFIG="/usr/bin/pg_config"
+elif test "${pg_config}" != "no" ; then
+    PG_CONFIG="${withval}"
+fi
+
+if test "$PG_CONFIG" != "" ; then
+    if test -d "$PG_CONFIG" -o ! -x "$PG_CONFIG" ; then
+        AC_MSG_ERROR([--with-dhcp-pgsql should point to a pg_config program])
+    fi
+
+    PGSQL_CPPFLAGS=`$PG_CONFIG --cppflags`
+    PGSQL_INCLUDEDIR=`$PG_CONFIG --includedir`
+    PGSQL_CPPFLAGS="$PGSQL_CPPFLAGS -I$PGSQL_INCLUDEDIR"
+    PGSQL_LIBS=`$PG_CONFIG --ldflags`
+    PGSQL_LIBS="$PGSQL_LIBS -lpq"
+    PGSQL_VERSION=`$PG_CONFIG --version`
+
+    AC_SUBST(PGSQL_CPPFLAGS)
+    AC_SUBST(PGSQL_LIBS)
+
+    # Check that a simple program using PostgreSQL functions can compile and link.
+    CPPFLAGS_SAVED="$CPPFLAGS"
+    LIBS_SAVED="$LIBS"
+
+    CPPFLAGS="$PGSQL_CPPFLAGS $CPPFLAGS"
+    LIBS="$PGSQL_LIBS $LIBS"
+
+    AC_LINK_IFELSE(
+            [AC_LANG_PROGRAM([#include <libpq-fe.h>],
+                             [PGconn * c = PQconnectdb("dbname = 'postgres'");
+                              PQfinish(c);])],
+            [AC_MSG_RESULT([checking for PostgreSQL headers and library... yes])],
+            [AC_MSG_RESULT([checking for PostgreSQL headers and library... no])
+             AC_MSG_ERROR([Needs PostgreSQL library])]
+    )
+
+    CPPFLAGS=$CPPFLAGS_SAVED
+    LIBS=$LIBS_SAVED
+
+    # Note that PostgreSQL is present in the config.h file
+    AC_DEFINE([HAVE_PGSQL], [1], [PostgreSQL is present])
+fi
+
+# ... and at the shell level, so Makefile.am can take action depending on this.
+AM_CONDITIONAL(HAVE_PGSQL, test "$PG_CONFIG" != "")
 
 
 # Check for log4cplus
 # Check for log4cplus
 log4cplus_path="yes"
 log4cplus_path="yes"
@@ -1743,6 +1795,26 @@ MySQL:
   MYSQL_CPPFLAGS:  ${MYSQL_CPPFLAGS}
   MYSQL_CPPFLAGS:  ${MYSQL_CPPFLAGS}
   MYSQL_LIBS:      ${MYSQL_LIBS}
   MYSQL_LIBS:      ${MYSQL_LIBS}
 END
 END
+else
+cat >> config.report << END
+
+MySQL: no
+END
+fi
+
+if test "$PGSQL_CPPFLAGS" != "" ; then
+cat >> config.report << END
+
+PostgreSQL:
+  PGSQL_VERSION:   ${PGSQL_VERSION}
+  PGSQL_CPPFLAGS:  ${PGSQL_CPPFLAGS}
+  PGSQL_LIBS:      ${PGSQL_LIBS}
+END
+else
+cat >> config.report << END
+
+PostgreSQL: no
+END
 fi
 fi
 
 
 if test "$enable_gtest" != "no"; then
 if test "$enable_gtest" != "no"; then

+ 11 - 2
src/lib/dhcpsrv/Makefile.am

@@ -7,6 +7,9 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
 if HAVE_MYSQL
 if HAVE_MYSQL
 AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
 AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
 endif
 endif
+if HAVE_PGSQL
+AM_CPPFLAGS += $(PGSQL_CPPFLAGS)
+endif
 
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 
@@ -54,6 +57,9 @@ libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
 if HAVE_MYSQL
 if HAVE_MYSQL
 libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
 libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
 endif
 endif
+if HAVE_PGSQL
+libb10_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
+endif
 libb10_dhcpsrv_la_SOURCES += option_space_container.h
 libb10_dhcpsrv_la_SOURCES += option_space_container.h
 libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
 libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
 libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
@@ -77,6 +83,9 @@ libb10_dhcpsrv_la_LDFLAGS  = -no-undefined -version-info 3:0:0
 if HAVE_MYSQL
 if HAVE_MYSQL
 libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
 libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
 endif
 endif
+if HAVE_PGSQL
+libb10_dhcpsrv_la_LDFLAGS += $(PGSQL_LIBS)
+endif
 
 
 if USE_CLANGPP
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
 # Disable unused parameter warning caused by some of the
@@ -88,8 +97,8 @@ endif
 EXTRA_DIST = dhcpsrv_messages.mes
 EXTRA_DIST = dhcpsrv_messages.mes
 
 
 # Distribute MySQL schema creation script and backend documentation
 # Distribute MySQL schema creation script and backend documentation
-EXTRA_DIST += dhcpdb_create.mysql database_backends.dox libdhcpsrv.dox
-dist_pkgdata_DATA = dhcpdb_create.mysql
+EXTRA_DIST += dhcpdb_create.mysql dhcpdb_create.pgsql database_backends.dox libdhcpsrv.dox
+dist_pkgdata_DATA = dhcpdb_create.mysql dhcpdb_create.pgsql
 
 
 install-data-local:
 install-data-local:
 	$(mkinstalldirs) $(DESTDIR)$(dhcp_data_dir)
 	$(mkinstalldirs) $(DESTDIR)$(dhcp_data_dir)

+ 74 - 0
src/lib/dhcpsrv/database_backends.dox

@@ -121,4 +121,78 @@
   The unit tests are run automatically when "make check" is executed (providing
   The unit tests are run automatically when "make check" is executed (providing
   that BIND 10 has been build with the \--with-dhcp-mysql switch (see the installation
   that BIND 10 has been build with the \--with-dhcp-mysql switch (see the installation
   section in the <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND 10 Guide</a>).
   section in the <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND 10 Guide</a>).
+
+ @subsection dhcp-pgsql-unittest PostgreSQL unit-tests
+
+  Conceptually, the steps required to run PostgreSQL unit-tests are the same as
+  in MySQL. First, a database called <i>keatest</i> must be created. A database
+  user, also called <i>keatest</i> (that will be allowed to log in using password
+  <i>keatest</i>) must be created and given full privileges in that database. The
+  unit tests create the schema in the database before each test and delete it
+  afterwards.
+
+  PostgreSQL set up differs from system to system. Please consult your OS-specific
+  PostgreSQL documentation. The remainder of that section uses Ubuntu 13.10 x64 as
+  example. On Ubuntu, after installing PostgreSQL (with <tt>sudo apt-get install
+  postgresql</tt>), it is installed as user <i>postgres</i>. To create new databases
+  or add new users, initial commands must be issued as user postgres:
+
+@verbatim
+$ sudo -u postgres psql postgres
+[sudo] password for thomson:
+psql (9.1.12)
+Type "help" for help.
+postgres=# CREATE USER keatest WITH PASSWORD 'keatest';
+CREATE ROLE
+postgres=# CREATE DATABASE keatest;
+CREATE DATABASE
+postgres=# GRANT ALL PRIVILEGES ON DATABASE keatest TO keatest;
+GRANT
+postgres=# \q
+@endverbatim
+
+  Now we are back to our regular, unprivileged user. Try to log into the newly
+  created database using keatest credentials:
+@verbatim
+$ psql -d keatest -U keatest
+$ psql keatest -U keatest -W
+Password for user keatest:
+psql (9.1.12)
+Type "help" for help.
+
+keatest=>
+@endverbatim
+
+  If instead of seeing keatest=> prompt, your login will be refused with error
+  code about failed peer or indent authentication, it means that PostgreSQL is
+  configured to check unix username and reject login attepts if PostgreSQL names
+  are different. To alter that, PostgreSQL configuration must be changed.
+  Alternatively, you may set up your environment, so the tests would be run from
+  unix account keatest. <tt>/etc/postgresql/9.1/main/pg_hba.conf</tt> config file
+  had to betweaked. It may be in a different location in your system. The following
+  lines:
+
+@verbatim
+local   all             all                                     peer
+host    all             all             127.0.0.1/32            md5
+host    all             all             ::1/128                 md5
+@endverbatim
+
+  were replaced with:
+
+@verbatim
+local   all             all                                     password
+host    all             all             127.0.0.1/32            password
+host    all             all             ::1/128                 password
+@endverbatim
+
+  Please consult your PostgreSQL user manual before applying those changes as
+  those changes may expose your other databases that you run on the same system.
+  In general case, it is a poor idea to run anything of value on a system
+  that runs tests. Use caution!
+
+  The unit tests are run automatically when "make check" is executed (providing
+  that BIND 10 has been build with the \--with-dhcp-pgsql switch (see the installation
+  section in the <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10 Guide</a>).
+
   */
   */

+ 1 - 1
src/lib/dhcpsrv/dbaccess_parser.cc

@@ -67,7 +67,7 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) {
 
 
     // b. Check if the 'type; keyword known and throw an exception if not.
     // b. Check if the 'type; keyword known and throw an exception if not.
     string dbtype = type_ptr->second;
     string dbtype = type_ptr->second;
-    if ((dbtype != "memfile") && (dbtype != "mysql")) {
+    if ((dbtype != "memfile") && (dbtype != "mysql") && (dbtype != "postgresql")) {
         isc_throw(BadValue, "unknown backend database type: " << dbtype);
         isc_throw(BadValue, "unknown backend database type: " << dbtype);
     }
     }
 
 

+ 129 - 0
src/lib/dhcpsrv/dhcpdb_create.pgsql

@@ -0,0 +1,129 @@
+-- Copyright (C) 2012-2013  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 PostgreSQL.
+
+-- 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:
+
+-- psql -U <user> -W <password> <database> < dhcpdb_create.pgsql
+
+-- ... at the command prompt, or log in to the PostgreSQL database and at the "postgres=#"
+-- prompt, issue the command:
+
+-- @dhcpdb_create.pgsql
+
+
+-- Holds the IPv4 leases.
+CREATE TABLE lease4 (
+    address BIGINT PRIMARY KEY NOT NULL,        -- IPv4 address
+    hwaddr BYTEA,                               -- Hardware address
+    client_id BYTEA,                            -- Client ID
+    valid_lifetime BIGINT,                      -- Length of the lease (seconds)
+    expire TIMESTAMP,                           -- Expiration time of the lease
+    subnet_id BIGINT                            -- Subnet identification
+    );
+
+
+-- Create search indexes for lease4 table
+-- index by hwaddr and subnet_id
+CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id);
+
+-- index by client_id and subnet_id
+CREATE INDEX lease4_by_client_id_subnet_id ON lease4 (client_id, subnet_id);
+
+-- 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(39) PRIMARY KEY NOT NULL,   -- IPv6 address
+    duid BYTEA,                                 -- DUID
+    valid_lifetime BIGINT,                      -- Length of the lease (seconds)
+    expire TIMESTAMP,                           -- Expiration time of the lease
+    subnet_id BIGINT,                           -- Subnet identification
+    pref_lifetime BIGINT,                       -- Preferred lifetime
+    lease_type SMALLINT,                        -- Lease type (see lease6_types
+                                                --    table for possible values)
+    iaid INT,                                   -- See Section 10 of RFC 3315
+    prefix_len SMALLINT                         -- For IA_PD only
+    );
+
+-- Create search indexes for lease4 table
+-- index by iaid, subnet_id, and duid
+CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid);
+
+-- ... 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 SMALLINT 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".
+
+-- NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h,
+--       which defines the schema for the unit tests.  If you are updating
+--       the version number, the schema has changed: please ensure that
+--       schema_copy.h has been updated as well.
+CREATE TABLE schema_version (
+    version INT PRIMARY KEY NOT NULL,       -- Major version number
+    minor INT                               -- Minor version number
+    );
+START TRANSACTION;
+INSERT INTO schema_version VALUES (1, 0);
+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
+-- ===========
+-- Some columns contain binary data so are stored as BYTEA instead of
+-- VARCHAR.  This may be non-portable between databases: in this case, the
+-- definition should be changed to VARCHAR.

+ 77 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -375,6 +375,83 @@ lease from the MySQL database for the specified address.
 A debug message issued when the server is attempting to update IPv6
 A debug message issued when the server is attempting to update IPv6
 lease from the MySQL database for the specified address.
 lease from the MySQL database for the specified address.
 
 
+% DHCPSRV_PGSQL_ADD_ADDR4 adding IPv4 lease with address %1
+A debug message issued when the server is about to add an IPv4 lease
+with the specified address to the PostgreSQL backend database.
+
+% DHCPSRV_PGSQL_ADD_ADDR6 adding IPv6 lease with address %1
+A debug message issued when the server is about to add an IPv6 lease
+with the specified address to the PostgreSQL backend database.
+
+% DHCPSRV_PGSQL_COMMIT committing to MySQL database
+The code has issued a commit call.  All outstanding transactions will be
+committed to the database.  Note that depending on the PostgreSQL settings,
+the committal may not include a write to disk.
+
+% DHCPSRV_PGSQL_DB opening PostgreSQL lease database: %1
+This informational message is logged when a DHCP server (either V4 or
+V6) is about to open a PostgreSQL lease database.  The parameters of the
+connection including database name and username needed to access it
+(but not the password if any) are logged.
+
+% DHCPSRV_PGSQL_DELETE_ADDR deleting lease for address %1
+A debug message issued when the server is attempting to delete a lease for
+the specified address from the PostgreSQL database for the specified address.
+
+% DHCPSRV_PGSQL_GET_ADDR4 obtaining IPv4 lease for address %1
+A debug message issued when the server is attempting to obtain an IPv4
+lease from the PostgreSQL database for the specified address.
+
+% DHCPSRV_PGSQL_GET_ADDR6 obtaining IPv6 lease for address %1 (lease type %2)
+A debug message issued when the server is attempting to obtain an IPv6
+lease from the PostgreSQL database for the specified address.
+
+% DHCPSRV_PGSQL_GET_CLIENTID obtaining IPv4 leases for client ID %1
+A debug message issued when the server is attempting to obtain a set
+of IPv4 leases from the PostgreSQL database for a client with the specified
+client identification.
+
+% DHCPSRV_PGSQL_GET_HWADDR obtaining IPv4 leases for hardware address %1
+A debug message issued when the server is attempting to obtain a set
+of IPv4 leases from the PostgreSQL database for a client with the specified
+hardware address.
+
+% DHCPSRV_PGSQL_GET_IAID_DUID obtaining IPv4 leases for IAID %1 and DUID %2, lease type %3
+A debug message issued when the server is attempting to obtain a set of
+IPv6 lease from the PostgreSQL database for a client with the specified IAID
+(Identity Association ID) and DUID (DHCP Unique Identifier).
+
+% DHCPSRV_PGSQL_GET_IAID_SUBID_DUID obtaining IPv4 leases for IAID %1, Subnet ID %2 and DUID %3
+A debug message issued when the server is attempting to obtain an IPv6
+lease from the PostgreSQL database for a client with the specified IAID
+(Identity Association ID), Subnet ID and DUID (DHCP Unique Identifier).
+
+% DHCPSRV_PGSQL_GET_SUBID_CLIENTID obtaining IPv4 lease for subnet ID %1 and client ID %2
+A debug message issued when the server is attempting to obtain an IPv4
+lease from the PostgreSQL database for a client with the specified subnet ID
+and client ID.
+
+% DHCPSRV_PGSQL_GET_SUBID_HWADDR obtaining IPv4 lease for subnet ID %1 and hardware address %2
+A debug message issued when the server is attempting to obtain an IPv4
+lease from the PostgreSQL database for a client with the specified subnet ID
+and hardware address.
+
+% DHCPSRV_PGSQL_GET_VERSION obtaining schema version information
+A debug message issued when the server is about to obtain schema version
+information from the PostgreSQL database.
+
+% DHCPSRV_PGSQL_ROLLBACK rolling back PostgreSQL database
+The code has issued a rollback call.  All outstanding transaction will
+be rolled back and not committed to the database.
+
+% DHCPSRV_PGSQL_UPDATE_ADDR4 updating IPv4 lease for address %1
+A debug message issued when the server is attempting to update IPv4
+lease from the PostgreSQL database for the specified address.
+
+% DHCPSRV_PGSQL_UPDATE_ADDR6 updating IPv6 lease for address %1
+A debug message issued when the server is attempting to update IPv6
+lease from the PostgreSQL database for the specified address.
+
 % DHCPSRV_NOTYPE_DB no 'type' keyword to determine database backend: %1
 % DHCPSRV_NOTYPE_DB no 'type' keyword to determine database backend: %1
 This is an error message, logged when an attempt has been made to access
 This is an error message, logged when an attempt has been made to access
 a database backend, but where no 'type' keyword has been included in
 a database backend, but where no 'type' keyword has been included in

+ 10 - 0
src/lib/dhcpsrv/lease_mgr_factory.cc

@@ -20,6 +20,9 @@
 #ifdef HAVE_MYSQL
 #ifdef HAVE_MYSQL
 #include <dhcpsrv/mysql_lease_mgr.h>
 #include <dhcpsrv/mysql_lease_mgr.h>
 #endif
 #endif
+#ifdef HAVE_PGSQL
+#include <dhcpsrv/pgsql_lease_mgr.h>
+#endif
 
 
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
@@ -125,6 +128,13 @@ LeaseMgrFactory::create(const std::string& dbaccess) {
         return;
         return;
     }
     }
 #endif
 #endif
+#ifdef HAVE_PGSQL
+    if (parameters[type] == string("postgresql")) {
+        LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_DB).arg(redacted);
+        getLeaseMgrPtr().reset(new PgSqlLeaseMgr(parameters));
+        return;
+    }
+#endif
     if (parameters[type] == string("memfile")) {
     if (parameters[type] == string("memfile")) {
         LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB).arg(redacted);
         LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_DB).arg(redacted);
         getLeaseMgrPtr().reset(new Memfile_LeaseMgr(parameters));
         getLeaseMgrPtr().reset(new Memfile_LeaseMgr(parameters));

Fichier diff supprimé car celui-ci est trop grand
+ 1185 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.cc


+ 160 - 0
src/lib/dhcpsrv/pgsql_lease_mgr.h

@@ -0,0 +1,160 @@
+// Copyright (C) 2013-2014 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 PGSQL_LEASE_MGR_H
+#define PGSQL_LEASE_MGR_H
+
+#include <dhcp/hwaddr.h>
+#include <dhcpsrv/lease_mgr.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/utility.hpp>
+#include <libpq-fe.h>
+
+namespace isc {
+namespace dhcp {
+
+struct PgSqlParam {
+    std::string value;
+    int isbinary;
+    int binarylen;
+};
+
+typedef std::vector<PgSqlParam> bindparams;
+
+struct PgSqlStatementBind {
+    const char * stmt_name;
+    int stmt_nbparams;
+};
+
+class PgSqlLease4Exchange;
+class PgSqlLease6Exchange;
+
+const uint32_t PG_CURRENT_VERSION = 1;
+const uint32_t PG_CURRENT_MINOR = 0;
+
+class PgSqlLeaseMgr : public LeaseMgr {
+public:
+    PgSqlLeaseMgr(const ParameterMap& parameters);
+    virtual ~PgSqlLeaseMgr();
+    virtual bool addLease(const Lease4Ptr& lease);
+    virtual bool addLease(const Lease6Ptr& lease);
+
+    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+    virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
+    virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+    virtual Lease4Collection getLease4(const ClientId& clientid) const;
+    virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+    virtual Lease4Ptr getLease4(const ClientId& clientid,
+                                SubnetID subnet_id) const;
+
+    virtual Lease6Ptr getLease6(Lease::Type type,
+                                const isc::asiolink::IOAddress& addr) const;
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                       uint32_t iaid) const;
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                        uint32_t iaid, SubnetID subnet_id) const;
+
+    virtual void updateLease4(const Lease4Ptr& lease4);
+    virtual void updateLease6(const Lease6Ptr& lease6);
+    virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
+    virtual std::string getType() const {
+        return (std::string("postgresql"));
+    }
+    virtual std::string getName() const;
+    virtual std::string getDescription() const;
+    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+    virtual void commit();
+    virtual void rollback();
+    enum StatementIndex {
+        DELETE_LEASE4,              // Delete from lease4 by address
+        DELETE_LEASE6,              // Delete from lease6 by address
+        GET_LEASE4_ADDR,            // Get lease4 by address
+        GET_LEASE4_CLIENTID,        // Get lease4 by client ID
+        GET_LEASE4_CLIENTID_SUBID,  // Get lease4 by client ID & subnet ID
+        GET_LEASE4_HWADDR,          // Get lease4 by HW address
+        GET_LEASE4_HWADDR_SUBID,    // Get lease4 by HW address & subnet ID
+        GET_LEASE6_ADDR,            // Get lease6 by address
+        GET_LEASE6_DUID_IAID,       // Get lease6 by DUID and IAID
+        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
+        GET_VERSION,                // Obtain version number
+        INSERT_LEASE4,              // Add entry to lease4 table
+        INSERT_LEASE6,              // Add entry to lease6 table
+        UPDATE_LEASE4,              // Update a Lease4 entry
+        UPDATE_LEASE6,              // Update a Lease6 entry
+        NUM_STATEMENTS              // Number of statements
+    };
+private:
+    void prepareStatements();
+    void openDatabase();
+    bool addLeaseCommon(StatementIndex stindex, bindparams & params);
+    template <typename Exchange, typename LeaseCollection>
+    void getLeaseCollection(StatementIndex stindex, bindparams & params,
+                            Exchange& exchange, LeaseCollection& result,
+                            bool single = false) const;
+    void getLeaseCollection(StatementIndex stindex, bindparams & params,
+                            Lease4Collection& result) const {
+        getLeaseCollection(stindex, params, exchange4_, result);
+    }
+    void getLeaseCollection(StatementIndex stindex, bindparams & params,
+                            Lease6Collection& result) const {
+        getLeaseCollection(stindex, params, exchange6_, result);
+    }
+    inline void checkError(PGresult * r, StatementIndex index,
+                           const char* what) const {
+        int s = PQresultStatus(r);
+        if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
+            PQclear(r);
+
+            isc_throw(DbOperationError, what << " for <" <<
+                      statements_[index].stmt_name << ">, " <<
+                      PQerrorMessage(status));
+        }
+    }
+    inline void convertToQuery(bindparams & params,
+                               std::vector<const char *>& params_,
+                               std::vector<int>& lengths_,
+                               std::vector<int>& formats_) const {
+        params_.reserve(params.size());
+        lengths_.reserve(params.size());
+        formats_.reserve(params.size());
+
+        for(bindparams::const_iterator it = params.begin(); it != params.end();
+                ++ it) {
+            params_.push_back((* it).value.c_str());
+            lengths_.push_back((* it).binarylen);
+            formats_.push_back((* it).isbinary);
+        }
+    }
+    void getLease(StatementIndex stindex, bindparams & params,
+                  Lease4Ptr& result) const;
+    void getLease(StatementIndex stindex, bindparams & params,
+                  Lease6Ptr& result) const;
+    template <typename LeasePtr>
+    void updateLeaseCommon(StatementIndex stindex, bindparams & params,
+                           const LeasePtr& lease);
+    bool deleteLeaseCommon(StatementIndex stindex, bindparams & params);
+
+    boost::scoped_ptr<PgSqlLease4Exchange> exchange4_;
+    boost::scoped_ptr<PgSqlLease6Exchange> exchange6_;
+    std::vector<PgSqlStatementBind> statements_;
+    PGconn * status;
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // PGSQL_LEASE_MGR_H

+ 11 - 1
src/lib/dhcpsrv/tests/Makefile.am

@@ -67,8 +67,12 @@ libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc
 if HAVE_MYSQL
 if HAVE_MYSQL
 libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
 endif
 endif
+if HAVE_PGSQL
+libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc
+endif
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
-libdhcpsrv_unittests_SOURCES += schema_copy.h
+libdhcpsrv_unittests_SOURCES += schema_mysql_copy.h
+libdhcpsrv_unittests_SOURCES += schema_pgsql_copy.h
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
 libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
 libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
 libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
 libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
@@ -78,11 +82,17 @@ libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INC
 if HAVE_MYSQL
 if HAVE_MYSQL
 libdhcpsrv_unittests_CPPFLAGS += $(MYSQL_CPPFLAGS)
 libdhcpsrv_unittests_CPPFLAGS += $(MYSQL_CPPFLAGS)
 endif
 endif
+if HAVE_PGSQL
+libdhcpsrv_unittests_CPPFLAGS += $(PGSQL_CPPFLAGS)
+endif
 
 
 libdhcpsrv_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 libdhcpsrv_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 if HAVE_MYSQL
 if HAVE_MYSQL
 libdhcpsrv_unittests_LDFLAGS  += $(MYSQL_LIBS)
 libdhcpsrv_unittests_LDFLAGS  += $(MYSQL_LIBS)
 endif
 endif
+if HAVE_PGSQL
+libdhcpsrv_unittests_LDFLAGS  += $(PGSQL_LIBS)
+endif
 
 
 libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_CLANGPP
 if USE_CLANGPP

+ 453 - 0
src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc

@@ -0,0 +1,453 @@
+// Copyright (C) 2014 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 <asiolink/io_address.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/pgsql_lease_mgr.h>
+#include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
+#include <exceptions/exceptions.h>
+
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <utility>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace std;
+
+namespace {
+
+// This holds statements to create and destroy the schema.
+#include "schema_pgsql_copy.h"
+
+// Connection strings.
+// Database: keatest
+// Host: localhost
+// Username: keatest
+// Password: keatest
+const char* VALID_TYPE = "type=postgresql";
+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 (should) fall over.
+void destroySchema() {
+    // Open database
+    PGconn * conn = 0;
+    conn = PQconnectdb("host = 'localhost' user = 'keatest'"
+                       " password = 'keatest' dbname = 'keatest'");
+
+    PGresult * r;
+    // Get rid of everything in it.
+    for (int i = 0; destroy_statement[i] != NULL; ++i) {
+        r = PQexec(conn, destroy_statement[i]);
+        PQclear(r);
+    }
+
+    PQfinish(conn);
+}
+
+// @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() {
+    // Open database
+    PGconn * conn = 0;
+    conn = PQconnectdb("host = 'localhost' user = 'keatest'"
+                       " password = 'keatest' dbname = 'keatest'");
+
+    PGresult * r;
+    // Get rid of everything in it.
+    for (int i = 0; create_statement[i] != NULL; ++i) {
+        r = PQexec(conn, create_statement[i]);
+        PQclear(r);
+    }
+}
+
+/// @brief Test fixture class for testing PostgreSQL Lease Manager
+///
+/// Opens the database prior to each test and closes it afterwards.
+/// All pending transactions are deleted prior to closure.
+
+class PgSqlLeaseMgrTest : public GenericLeaseMgrTest {
+public:
+    /// @brief Constructor
+    ///
+    /// Deletes everything from the database and opens it.
+    PgSqlLeaseMgrTest() {
+
+        // Ensure schema is the correct one.
+        destroySchema();
+        createSchema();
+
+        // Connect to the database
+        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 PostgreSQL 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 lmptr_ will close
+    /// the database.  Then reopen it and delete everything created by the test.
+    virtual ~PgSqlLeaseMgrTest() {
+        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 Check that database can be opened
+///
+/// This test checks if the PgSqlLeaseMgr can be instantiated.  This happens
+/// only if the database can be opened.  Note that this is not part of the
+/// PgSqlLeaseMgr test fixure set.  This test checks that the database can be
+/// opened: the fixtures assume that and check basic operations.
+
+TEST(PgSqlOpenTest, 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 PostgreSQL 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 the getType() method
+///
+/// getType() returns a string giving the type of the backend, which should
+/// always be "postgresql".
+TEST_F(PgSqlLeaseMgrTest, getType) {
+    EXPECT_EQ(std::string("postgresql"), lmptr_->getType());
+}
+
+/// @brief Check getName() returns correct database name
+TEST_F(PgSqlLeaseMgrTest, getName) {
+    EXPECT_EQ(std::string("keatest"), lmptr_->getName());
+}
+
+/// @brief Check that getVersion() returns the expected version
+TEST_F(PgSqlLeaseMgrTest, checkVersion) {
+    // Check version
+    pair<uint32_t, uint32_t> version;
+    ASSERT_NO_THROW(version = lmptr_->getVersion());
+    EXPECT_EQ(PG_CURRENT_VERSION, version.first);
+    EXPECT_EQ(PG_CURRENT_MINOR, version.second);
+}
+
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4 (by address) and deleteLease (with an
+/// IPv4 address) works.
+TEST_F(PgSqlLeaseMgrTest, basicLease4) {
+    // Get the leases to be used for the test.
+    vector<Lease4Ptr> leases = createLeases4();
+
+    // Start the tests.  Add three leases to the database, read them back and
+    // check they are what we think they are.
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    EXPECT_TRUE(lmptr_->addLease(leases[2]));
+    EXPECT_TRUE(lmptr_->addLease(leases[3]));
+    lmptr_->commit();
+
+    // Reopen the database to ensure that they actually got stored.
+    reopen();
+
+    Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+    l_returned = lmptr_->getLease4(ioaddress4_[3]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[3], l_returned);
+
+    // Check that we can't add a second lease with the same address
+    EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+    // Delete a lease, check that it's gone, and that we can't delete it
+    // a second time.
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    EXPECT_FALSE(l_returned);
+    EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
+
+    // Check that the second address is still there.
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+}
+
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id),
+/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
+/// (client-id is optional and may not be present)
+TEST_F(PgSqlLeaseMgrTest, lease4NullClientId) {
+    // Get the leases to be used for the test.
+    vector<Lease4Ptr> leases = createLeases4();
+
+    // Let's clear client-id pointers
+    leases[1]->client_id_ = ClientIdPtr();
+    leases[2]->client_id_ = ClientIdPtr();
+    leases[3]->client_id_ = ClientIdPtr();
+
+    // Start the tests.  Add three leases to the database, read them back and
+    // check they are what we think they are.
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    EXPECT_TRUE(lmptr_->addLease(leases[2]));
+    EXPECT_TRUE(lmptr_->addLease(leases[3]));
+    lmptr_->commit();
+
+    // Reopen the database to ensure that they actually got stored.
+    reopen();
+
+    Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+    l_returned = lmptr_->getLease4(ioaddress4_[3]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[3], l_returned);
+
+    // Check that we can't add a second lease with the same address
+    EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+    // Check that we can get the lease by HWAddr
+    HWAddr tmp(leases[2]->hwaddr_, HTYPE_ETHER);
+    Lease4Collection returned = lmptr_->getLease4(tmp);
+    ASSERT_EQ(1, returned.size());
+    detailCompareLease(leases[2], *returned.begin());
+
+    l_returned = lmptr_->getLease4(tmp, leases[2]->subnet_id_);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+
+    // Check that we can update the lease
+    // Modify some fields in lease 1 (not the address) and update it.
+    ++leases[1]->subnet_id_;
+    leases[1]->valid_lft_ *= 2;
+    lmptr_->updateLease4(leases[1]);
+
+    // ... and check that the lease is indeed updated
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+
+
+    // Delete a lease, check that it's gone, and that we can't delete it
+    // a second time.
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    EXPECT_FALSE(l_returned);
+    EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
+
+    // Check that the second address is still there.
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+}
+
+/// @brief Basic Lease6 Checks
+///
+/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
+/// IPv6 address) works.
+TEST_F(PgSqlLeaseMgrTest, 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(leasetype6_[1], ioaddress6_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+    l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+    l_returned = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[3], l_returned);
+
+    // Check that we can't add a second lease with the same address
+    EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+    // Delete a lease, check that it's gone, and that we can't delete it
+    // a second time.
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress6_[1]));
+    l_returned = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+    EXPECT_FALSE(l_returned);
+    EXPECT_FALSE(lmptr_->deleteLease(ioaddress6_[1]));
+
+    // Check that the second address is still there.
+    l_returned = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+}
+
+};

src/lib/dhcpsrv/tests/schema_copy.h → src/lib/dhcpsrv/tests/schema_mysql_copy.h


+ 89 - 0
src/lib/dhcpsrv/tests/schema_pgsql_copy.h

@@ -0,0 +1,89 @@
+// Copyright (C) 2014 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 PostgreSQL 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)
+
+// NOTE: This file mirrors the schema in src/lib/dhcpsrv/dhcpdb_create.pgsql.
+//       If this file is altered, please ensure that any change is compatible
+//       with the schema in dhcpdb_create.pgsql.
+
+// 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[] = {
+    "START TRANSACTION",
+    "CREATE TABLE lease4 ("
+        "address BIGINT PRIMARY KEY NOT NULL,"
+        "hwaddr BYTEA,"
+        "client_id BYTEA,"
+        "valid_lifetime BIGINT,"
+        "expire TIMESTAMP,"
+        "subnet_id BIGINT"
+        ")",
+
+    "CREATE TABLE lease6 ("
+        "address VARCHAR(39) PRIMARY KEY NOT NULL,"
+        "duid BYTEA,"
+        "valid_lifetime BIGINT,"
+        "expire TIMESTAMP,"
+        "subnet_id BIGINT,"
+        "pref_lifetime BIGINT,"
+        "lease_type SMALLINT,"
+        "iaid BIGINT,"
+        "prefix_len SMALLINT"
+        ")",
+
+    "CREATE TABLE lease6_types ("
+        "lease_type SMALLINT 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 (1, 0)",
+    "COMMIT",
+
+    NULL
+};
+
+};  // Anonymous namespace
+
+#endif // SCHEMA_COPY_H