Browse Source

solved Cassandra unit tests & added documentation

Andrei Pavel 9 years ago
parent
commit
08343129b4

+ 2 - 2
configure.ac

@@ -1329,11 +1329,11 @@ if test "x$enable_generate_docs" != xno ; then
   else
   else
     AC_MSG_CHECKING([if $XSLTPROC works])
     AC_MSG_CHECKING([if $XSLTPROC works])
     # run xsltproc to see if works
     # run xsltproc to see if works
-    $XSLTPROC --novalid --xinclude --nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
+    $XSLTPROC --novalid --xinclude http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
     if test $? -ne 0 ; then
     if test $? -ne 0 ; then
       AC_MSG_ERROR("Error with $XSLTPROC using release/xsl/current/manpages/docbook.xsl")
       AC_MSG_ERROR("Error with $XSLTPROC using release/xsl/current/manpages/docbook.xsl")
     fi
     fi
-    $XSLTPROC --novalid --xinclude --nonet http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
+    $XSLTPROC --novalid --xinclude http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
     if test $? -ne 0 ; then
     if test $? -ne 0 ; then
       AC_MSG_ERROR("Error with $XSLTPROC using release/xsl/current/html/docbook.xsl")
       AC_MSG_ERROR("Error with $XSLTPROC using release/xsl/current/html/docbook.xsl")
     fi
     fi

+ 128 - 4
doc/guide/admin.xml

@@ -94,10 +94,10 @@
         <listitem>
         <listitem>
           <simpara>
           <simpara>
             <command>lease-dump</command> &mdash;
             <command>lease-dump</command> &mdash;
-            Dumps the contents of the lease database (for MySQL or PostgreSQL
-            backends) to CSV text file. The first line of the file contains
-            the column names.  This is meant to be used as a diagnostic
-            tool that provides a portable, human-readable form of lease data.
+            Dumps the contents of the lease database (for MySQL, PostgreSQL or
+            CQL backends) to CSV text file. The first line of the file contains
+            the column names.  This is meant to be used as a diagnostic tool
+            that provides a portable, human-readable form of lease data.
           </simpara>
           </simpara>
         </listitem>
         </listitem>
       </itemizedlist>
       </itemizedlist>
@@ -128,6 +128,14 @@
             database.
             database.
           </simpara>
           </simpara>
         </listitem>
         </listitem>
+
+        <listitem>
+          <simpara>
+            <command>cql</command> &mdash;
+            Lease information is stored in a CQL database.
+          </simpara>
+        </listitem>
+
       </itemizedlist>
       </itemizedlist>
 
 
       Additional parameters may be needed, depending on your setup
       Additional parameters may be needed, depending on your setup
@@ -484,6 +492,122 @@ $ <userinput>kea-admin lease-init pgsql -u <replaceable>database-user</replaceab
         </para>
         </para>
       </section>
       </section>
     </section> <!-- end of PostgreSQL sections -->
     </section> <!-- end of PostgreSQL sections -->
+
+    <section>
+      <title>CQL</title>
+
+      <para>
+        The CQL database must be properly set up if you want Kea to store
+        information in CQL. This section can be safely ignored if you chose to
+        store the data in other backends.
+      </para>
+
+    <section id="cql-database-create">
+      <title>First Time Creation of Kea Database</title>
+
+      <para>
+        If you are setting up the CQL database for the first time, you need to
+        create the keyspace area within CQL. This needs to be done manually:
+        <command>kea-admin</command> is not able to do this for you.
+      </para>
+
+      <para>
+        To create the database:
+        <orderedlist>
+          <listitem>
+            <para>
+              Export CQLSH_HOST environemnt variable:
+<screen>
+$ <userinput>export CQLSH_HOST=localhost</userinput>
+</screen>
+              </para>
+            </listitem>
+          <listitem>
+            <para>
+              Log into CQL:
+<screen>
+$ <userinput>cqlsh</userinput>
+cql>
+</screen>
+              </para>
+            </listitem>
+
+          <listitem>
+            <para>
+              Create the CQL keyspace:
+<screen>
+cql> <userinput>CREATE KEYSPACE keyspace-name WITH replication = {'class' : 'SimpleStrategy','replication_factor' : 1};</userinput>
+</screen>
+              (<replaceable>keyspace-name</replaceable> is the name you have
+              chosen for the keyspace)
+            </para>
+          </listitem>
+
+         <listitem>
+            <para>
+              At this point, you may elect to create the database tables.
+              (Alternatively, you can exit CQL and create the tables using the
+              <command>kea-admin</command> tool, as explained below)  To do this:
+<screen>
+<userinput>cqslh -k <replaceable>keyspace-name</replaceable> -f <replaceable>path-to-kea</replaceable>/share/kea/scripts/cql/dhcpdb_create.cql</userinput>
+</screen>
+              (<replaceable>path-to-kea</replaceable> is the location where you
+              installed Kea)
+            </para>
+          </listitem>
+        </orderedlist>
+      </para>
+
+      <para>
+        If you elected not to create the tables in step 4, you can do
+        so now by running the <command>kea-admin</command> tool:
+<screen>
+$ <userinput>kea-admin lease-init cql -n <replaceable>database-name</replaceable></userinput>
+</screen>
+        (Do not do this if you did create the tables in step 4.)
+        <command>kea-admin</command> implements rudimentary checks:
+        it will refuse to initialize a database that contains any
+        existing tables. If you want to start from scratch, you
+        must remove all data manually. (This process is a manual
+        operation on purpose to avoid possibly irretrievable mistakes
+        by <command>kea-admin</command>)
+      </para>
+    </section>
+
+    <section id="cql-upgrade">
+      <title>Upgrading a CQL Database from an Earlier Version of Kea</title>
+
+      <para>
+        Sometimes a new Kea version may use newer database schema, so
+        there will be a need to upgrade the existing database. This can
+        be done using the <command>kea-admin lease-upgrade</command>
+        command.
+      </para>
+
+      <para>
+        To check the current version of the database, use the following command:
+<screen>
+$ <userinput>kea-admin lease-version cql -n <replaceable>database-name</replaceable></userinput>
+</screen>
+        (See <xref linkend="kea-database-version"/> for a discussion
+        about versioning)  If the version does not match the minimum
+        required for the new version of Kea (as described in the
+        release notes), the database needs to be upgraded.
+      </para>
+
+      <para>
+        Before upgrading, please make sure that the database is
+        backed up.  The upgrade process does not discard any data but,
+        depending on the nature of the changes, it may be impossible
+        to subsequently downgrade to an earlier version.  To perform
+        an upgrade, issue the following command:
+<screen>
+$ <userinput>kea-admin lease-upgrade cql -n <replaceable>database-name</replaceable></userinput>
+</screen>
+      </para>
+    </section>
+  </section> <!-- end of CQL sections -->
+
     <section>
     <section>
       <title>Limitations related to the use of the SQL databases</title>
       <title>Limitations related to the use of the SQL databases</title>
 
 

+ 6 - 4
doc/guide/dhcp4-srv.xml

@@ -301,8 +301,8 @@ be followed by a comma and another object definition.</para>
 <section>
 <section>
   <title>Lease Storage</title>
   <title>Lease Storage</title>
   <para>All leases issued by the server are stored in the lease database.
   <para>All leases issued by the server are stored in the lease database.
-  Currently there are three database backends available:
-  memfile (which is the default backend), MySQL and PostgreSQL.</para>
+  Currently there are four database backends available:  memfile (which is the
+  default backend), MySQL, PostgreSQL and CQL.</para>
 <section>
 <section>
   <title>Memfile, Basic Storage for Leases</title>
   <title>Memfile, Basic Storage for Leases</title>
 
 
@@ -441,8 +441,10 @@ be followed by a comma and another object definition.</para>
 "Dhcp4": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
 "Dhcp4": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
 </screen>
 </screen>
   Next, the name of the database to hold the leases must be set: this is the
   Next, the name of the database to hold the leases must be set: this is the
-  name used when the lease database was created (see <xref linkend="mysql-database-create"/>
-  or <xref linkend="pgsql-database-create"/>).
+  name used when the lease database was created
+  (see <xref linkend="mysql-database-create"/>,
+  <xref linkend="pgsql-database-create"/> or
+  <xref linkend="cql-database-create"/>).
 <screen>
 <screen>
 "Dhcp4": { "lease-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
 "Dhcp4": { "lease-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
 </screen>
 </screen>

+ 9 - 7
doc/guide/dhcp6-srv.xml

@@ -306,8 +306,8 @@ be followed by a comma and another object definition.</para>
 <section>
 <section>
   <title>Lease Storage</title>
   <title>Lease Storage</title>
   <para>All leases issued by the server are stored in the lease database.
   <para>All leases issued by the server are stored in the lease database.
-  Currently there are three database backends available:
-  memfile (which is the default backend), MySQL and PostgreSQL.</para>
+  Currently there are four database backends available:  memfile (which is the
+  default backend), MySQL, PostgreSQL and CQL.</para>
 <section>
 <section>
   <title>Memfile, Basic Storage for Leases</title>
   <title>Memfile, Basic Storage for Leases</title>
 
 
@@ -435,13 +435,15 @@ be followed by a comma and another object definition.</para>
 
 
   <para>Lease database configuration is controlled through the
   <para>Lease database configuration is controlled through the
   Dhcp6/lease-database parameters. The type of the database must be set to
   Dhcp6/lease-database parameters. The type of the database must be set to
-  "memfile", "mysql" or "postgresql", e.g.
+  "memfile", "mysql", "postgresql" or "cql", e.g.
 <screen>
 <screen>
 "Dhcp6": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
 "Dhcp6": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
 </screen>
 </screen>
   Next, the name of the database is to hold the leases must be set: this is the
   Next, the name of the database is to hold the leases must be set: this is the
-  name used when the lease database was created (see <xref linkend="mysql-database-create"/>
-  or <xref linkend="pgsql-database-create"/>).
+  name used when the lease database was created
+  (see <xref linkend="mysql-database-create"/>,
+  <xref linkend="pgsql-database-create"/>
+  or <xref linkend="cql-database-create"/>).
 <screen>
 <screen>
 "Dhcp6": { "lease-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
 "Dhcp6": { "lease-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
 </screen>
 </screen>
@@ -3568,8 +3570,8 @@ should include options from the isc option space:
               that are stored in the lease database. Removing non-last subnet will
               that are stored in the lease database. Removing non-last subnet will
               cause the configuration information to mismatch data in the lease
               cause the configuration information to mismatch data in the lease
               database. It is possible to manually update subnet-id fields in
               database. It is possible to manually update subnet-id fields in
-              MySQL or PostgreSQL database, but it is awkward and error prone
-              process. A better reconfiguration support is planned.
+              MySQL, PostgreSQL or CQL database, but it is awkward and
+              error prone process. A better reconfiguration support is planned.
             </para>
             </para>
           </listitem>
           </listitem>
 
 

+ 1 - 1
doc/guide/install.xml

@@ -367,7 +367,7 @@ Debian and Ubuntu:
 
 
         <para>
         <para>
           <userinput>./configure</userinput> when it succeeds displays a report
           <userinput>./configure</userinput> when it succeeds displays a report
-          with the building parameters. This report is saved into 
+          with the building parameters. This report is saved into
           <filename>config.report</filename> and embedded into executable
           <filename>config.report</filename> and embedded into executable
           binaries, e.g., <userinput>kea-dhcp4</userinput>.
           binaries, e.g., <userinput>kea-dhcp4</userinput>.
         </para>
         </para>

+ 8 - 0
doc/guide/intro.xml

@@ -88,6 +88,14 @@
     built without PostgreSQL support.
     built without PostgreSQL support.
             </simpara>
             </simpara>
         </listitem>
         </listitem>
+
+        <listitem>
+            <simpara>
+	In order to store lease information in a CQL database, Kea requires CQL
+    headers and libraries. This is an optional dependency in that Kea can be
+    built without CQL support.
+            </simpara>
+        </listitem>
       </itemizedlist>
       </itemizedlist>
     </section>
     </section>
 
 

+ 2 - 2
src/bin/admin/kea-admin.in

@@ -489,10 +489,10 @@ cql_dump() {
 
 
     select_where_clause=""
     select_where_clause=""
     if [ $dump_type -eq 4 ]; then
     if [ $dump_type -eq 4 ]; then
-        dump_qry="SELECT address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state FROM keatest.lease4"
+        dump_qry="SELECT address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state FROM lease4"
         select_where_clause=" WHERE address = 0" # invalid address
         select_where_clause=" WHERE address = 0" # invalid address
     elif [ $dump_type -eq 6 ]; then
     elif [ $dump_type -eq 6 ]; then
-        dump_qry="SELECT address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state FROM keatest.lease6"
+        dump_qry="SELECT address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state FROM lease6"
         select_where_clause=" WHERE address = '::'" # invalid address
         select_where_clause=" WHERE address = '::'" # invalid address
     fi
     fi
 
 

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

@@ -98,7 +98,7 @@ CqlConnection::openDatabase() {
         skeyspace = getParameter("keyspace");
         skeyspace = getParameter("keyspace");
         keyspace = skeyspace.c_str();
         keyspace = skeyspace.c_str();
     } catch (...) {
     } catch (...) {
-        // No database name. Fine, we'll use default "keatest".
+        // No keyspace name. Fine, we'll use default "keatest".
     }
     }
 
 
     cluster_ = cass_cluster_new();
     cluster_ = cass_cluster_new();

+ 130 - 47
src/lib/dhcpsrv/cql_lease_mgr.cc

@@ -258,7 +258,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
     // DELETE_LEASE4
     // DELETE_LEASE4
     { delete_lease4_params,
     { delete_lease4_params,
       "delete_lease4",
       "delete_lease4",
-      "DELETE FROM lease4 WHERE address = ?" },
+      "DELETE FROM lease4 WHERE address = ? "
+      "IF EXISTS" },
 
 
     // DELETE_LEASE4_STATE_EXPIRED
     // DELETE_LEASE4_STATE_EXPIRED
     { delete_expired_lease4_params,
     { delete_expired_lease4_params,
@@ -267,12 +268,14 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "valid_lifetime, expire, subnet_id, "
         "valid_lifetime, expire, subnet_id, "
         "fqdn_fwd, fqdn_rev, hostname, state "
         "fqdn_fwd, fqdn_rev, hostname, state "
       "FROM lease4 "
       "FROM lease4 "
-      "WHERE state = ? AND expire < ? ALLOW FILTERING" },
+      "WHERE state = ? AND expire < ? "
+      "ALLOW FILTERING" },
 
 
     // DELETE_LEASE6
     // DELETE_LEASE6
     { delete_lease6_params,
     { delete_lease6_params,
       "delete_lease6",
       "delete_lease6",
-      "DELETE FROM lease6 WHERE address = ?" },
+      "DELETE FROM lease6 WHERE address = ? "
+      "IF EXISTS" },
 
 
     // DELETE_LEASE6_STATE_EXPIRED
     // DELETE_LEASE6_STATE_EXPIRED
     { delete_expired_lease6_params,
     { delete_expired_lease6_params,
@@ -282,7 +285,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "hwaddr, hwtype, hwaddr_source, state "
         "hwaddr, hwtype, hwaddr_source, state "
       "FROM lease6 "
       "FROM lease6 "
-      "WHERE state = ? AND expire < ? ALLOW FILTERING" },
+      "WHERE state = ? AND expire < ? "
+      "ALLOW FILTERING" },
 
 
     // GET_LEASE4_ADDR
     // GET_LEASE4_ADDR
     { get_lease4_addr_params,
     { get_lease4_addr_params,
@@ -309,7 +313,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "valid_lifetime, expire, subnet_id, "
         "valid_lifetime, expire, subnet_id, "
         "fqdn_fwd, fqdn_rev, hostname, state "
         "fqdn_fwd, fqdn_rev, hostname, state "
       "FROM lease4 "
       "FROM lease4 "
-      "WHERE client_id = ? AND subnet_id = ? ALLOW FILTERING" },
+      "WHERE client_id = ? AND subnet_id = ? "
+      "ALLOW FILTERING" },
 
 
     // GET_LEASE4_HWADDR
     // GET_LEASE4_HWADDR
     { get_lease4_hwaddr_params,
     { get_lease4_hwaddr_params,
@@ -327,7 +332,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "valid_lifetime, expire, subnet_id, "
         "valid_lifetime, expire, subnet_id, "
         "fqdn_fwd, fqdn_rev, hostname, state "
         "fqdn_fwd, fqdn_rev, hostname, state "
       "FROM lease4 "
       "FROM lease4 "
-      "WHERE hwaddr = ? AND subnet_id = ? ALLOW FILTERING" },
+      "WHERE hwaddr = ? AND subnet_id = ? "
+      "ALLOW FILTERING" },
 
 
     // GET_LEASE4_EXPIRE
     // GET_LEASE4_EXPIRE
     { get_lease4_expired_params,
     { get_lease4_expired_params,
@@ -337,7 +343,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "fqdn_fwd, fqdn_rev, hostname, state "
         "fqdn_fwd, fqdn_rev, hostname, state "
       "FROM lease4 "
       "FROM lease4 "
       "WHERE state = ? AND expire < ? "
       "WHERE state = ? AND expire < ? "
-      "LIMIT ? ALLOW FILTERING" },
+      "LIMIT ? "
+      "ALLOW FILTERING" },
 
 
     // GET_LEASE6_ADDR
     // GET_LEASE6_ADDR
     { get_lease6_addr_params,
     { get_lease6_addr_params,
@@ -347,7 +354,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "hwaddr, hwtype, hwaddr_source, state "
         "hwaddr, hwtype, hwaddr_source, state "
       "FROM lease6 "
       "FROM lease6 "
-      "WHERE address = ? AND lease_type = ? ALLOW FILTERING" },
+      "WHERE address = ? AND lease_type = ? "
+      "ALLOW FILTERING" },
 
 
     // GET_LEASE6_DUID_IAID
     // GET_LEASE6_DUID_IAID
     { get_lease6_duid_iaid_params,
     { get_lease6_duid_iaid_params,
@@ -357,7 +365,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
          "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
          "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
          "hwaddr, hwtype, hwaddr_source, state "
          "hwaddr, hwtype, hwaddr_source, state "
        "FROM lease6 "
        "FROM lease6 "
-       "WHERE duid = ? AND iaid = ? AND lease_type = ? ALLOW FILTERING" },
+       "WHERE duid = ? AND iaid = ? AND lease_type = ? "
+       "ALLOW FILTERING" },
 
 
     // GET_LEASE6_DUID_IAID_SUBID
     // GET_LEASE6_DUID_IAID_SUBID
     { get_lease6_duid_iaid_subid_params,
     { get_lease6_duid_iaid_subid_params,
@@ -367,7 +376,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "hwaddr, hwtype, hwaddr_source, state "
         "hwaddr, hwtype, hwaddr_source, state "
       "FROM lease6 "
       "FROM lease6 "
-      "WHERE duid = ? AND iaid = ? AND subnet_id = ? AND lease_type = ? ALLOW FILTERING" },
+      "WHERE duid = ? AND iaid = ? AND subnet_id = ? AND lease_type = ? "
+      "ALLOW FILTERING" },
 
 
     // GET_LEASE6_EXPIRE
     // GET_LEASE6_EXPIRE
     { get_lease6_expired_params,
     { get_lease6_expired_params,
@@ -377,9 +387,9 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
         "hwaddr, hwtype, hwaddr_source, state "
         "hwaddr, hwtype, hwaddr_source, state "
       "FROM lease6 "
       "FROM lease6 "
-      //"WHERE state != ? AND expire < ? ORDER BY expire ASC "
       "WHERE state = ? AND expire < ? "
       "WHERE state = ? AND expire < ? "
-      "LIMIT ? ALLOW FILTERING" },
+      "LIMIT ? "
+      "ALLOW FILTERING" },
 
 
     // GET_VERSION
     // GET_VERSION
     { get_version_params,
     { get_version_params,
@@ -393,7 +403,7 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, "
         "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, "
         "state) "
         "state) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
-    },
+      "IF NOT EXISTS" },
 
 
     // INSERT_LEASE6
     // INSERT_LEASE6
     { insert_lease6_params,
     { insert_lease6_params,
@@ -403,7 +413,7 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, "
         "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, "
         "hwtype, hwaddr_source, state) "
         "hwtype, hwaddr_source, state) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
-    },
+      "IF NOT EXISTS" },
 
 
     // UPDATE_LEASE4
     // UPDATE_LEASE4
     { update_lease4_params,
     { update_lease4_params,
@@ -412,7 +422,7 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "client_id = ?, valid_lifetime = ?, expire = ?, "
         "client_id = ?, valid_lifetime = ?, expire = ?, "
         "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, state = ? "
         "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, state = ? "
       "WHERE address = ? "
       "WHERE address = ? "
-    },
+      "IF EXISTS" },
 
 
     // UPDATE_LEASE6
     // UPDATE_LEASE6
     { update_lease6_params,
     { update_lease6_params,
@@ -423,7 +433,7 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
         "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, "
         "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, "
         "hwaddr = ?, hwtype = ?, hwaddr_source = ?, state = ? "
         "hwaddr = ?, hwtype = ?, hwaddr_source = ?, state = ? "
       "WHERE address = ? "
       "WHERE address = ? "
-    },
+      "IF EXISTS" },
 
 
     // End of list sentinel
     // End of list sentinel
     { NULL, NULL, NULL }
     { NULL, NULL, NULL }
@@ -501,12 +511,12 @@ public:
     /// all variables are initialized/set in the methods before they are used.
     /// all variables are initialized/set in the methods before they are used.
     CqlLease4Exchange() : addr4_(0), client_id_length_(0),
     CqlLease4Exchange() : addr4_(0), client_id_length_(0),
                             client_id_null_(false) {
                             client_id_null_(false) {
-        const size_t MAX_COLUMNS = 11U;
+        const size_t MAX_COLUMNS = 12U;
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
 
 
         // Set the column names
         // Set the column names
         size_t offset = 0U;
         size_t offset = 0U;
-        BOOST_STATIC_ASSERT(11U == MAX_COLUMNS);
+        BOOST_STATIC_ASSERT(12U == MAX_COLUMNS);
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address",
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr",
@@ -529,6 +539,8 @@ public:
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit",
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("[applied]",
+            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL)));
         BOOST_ASSERT(parameters_.size() == MAX_COLUMNS);
         BOOST_ASSERT(parameters_.size() == MAX_COLUMNS);
     }
     }
 
 
@@ -553,7 +565,18 @@ public:
             data.add(&addr4_);
             data.add(&addr4_);
 
 
             // hwaddr: blob
             // hwaddr: blob
-            hwaddr_ = lease_->hwaddr_->hwaddr_;
+            HWAddrPtr hwaddr = lease_->hwaddr_;
+            if (hwaddr) {
+                if (hwaddr->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                    isc_throw(DbOperationError, "Hardware address length " <<
+                              lease_->hwaddr_->hwaddr_.size() <<
+                              " exceeds maximum allowed of " <<
+                              HWAddr::MAX_HWADDR_LEN);
+                }
+                hwaddr_ = hwaddr->hwaddr_;
+            } else {
+                hwaddr_.clear();
+            }
             hwaddr_length_ = hwaddr_.size();
             hwaddr_length_ = hwaddr_.size();
             data.add(&hwaddr_);
             data.add(&hwaddr_);
 
 
@@ -622,7 +645,6 @@ public:
     /// Creates a CQL_BIND array to receive Lease4 data from the database.
     /// Creates a CQL_BIND array to receive Lease4 data from the database.
     Lease4Ptr createBindForReceive(const CassRow* row) {
     Lease4Ptr createBindForReceive(const CassRow* row) {
         try {
         try {
-
             unsigned char* hwaddr_buffer = NULL;
             unsigned char* hwaddr_buffer = NULL;
             const char* client_id_buffer = NULL;
             const char* client_id_buffer = NULL;
             const char* hostname_buffer = NULL;
             const char* hostname_buffer = NULL;
@@ -669,8 +691,8 @@ public:
             data.add(reinterpret_cast<void*>(&state_));
             data.add(reinterpret_cast<void*>(&state_));
             size.add(NULL);
             size.add(NULL);
 
 
-            for (int i = 0; i < 10; i++) {
-                CqlLeaseMgr::getData(row, i, data, size, *this);
+            for (size_t i = 0; i < data.size(); i++) {
+                CqlLeaseMgr::getData(row, i, data, size, i, *this);
             }
             }
 
 
             // hwaddr: blob
             // hwaddr: blob
@@ -756,17 +778,17 @@ public:
     CqlLease6Exchange() : addr6_length_(0), duid_length_(0), iaid_(0),
     CqlLease6Exchange() : addr6_length_(0), duid_length_(0), iaid_(0),
                           lease_type_(0), prefixlen_(0), pref_lifetime_(0),
                           lease_type_(0), prefixlen_(0), pref_lifetime_(0),
                           hwaddr_null_(false), hwtype_(0), hwaddr_source_(0) {
                           hwaddr_null_(false), hwtype_(0), hwaddr_source_(0) {
-        const size_t MAX_COLUMNS = 17U;
+        const size_t MAX_COLUMNS = 18U;
         memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
         memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
 
 
         // Set the column names
         // Set the column names
         size_t offset = 0U;
         size_t offset = 0U;
-        BOOST_STATIC_ASSERT(17U == MAX_COLUMNS);
+        BOOST_STATIC_ASSERT(18U == MAX_COLUMNS);
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address",
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("duid",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("duid",
-            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
+            offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES)));
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("valid_lifetime",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("valid_lifetime",
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64)));
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64)));
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("expire",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("expire",
@@ -797,6 +819,8 @@ public:
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit",
         parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit",
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
             offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
+        parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("[applied]",
+            offset++, EXCHANGE_DATA_TYPE_IO_OUT, EXCHANGE_DATA_TYPE_BOOL)));
         BOOST_ASSERT(parameters_.size() == MAX_COLUMNS);
         BOOST_ASSERT(parameters_.size() == MAX_COLUMNS);
     }
     }
 
 
@@ -899,6 +923,12 @@ public:
             // hwaddr: blob
             // hwaddr: blob
             HWAddrPtr hwaddr = lease_->hwaddr_;
             HWAddrPtr hwaddr = lease_->hwaddr_;
             if (hwaddr) {
             if (hwaddr) {
+                if (hwaddr->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                    isc_throw(DbOperationError, "Hardware address length : "
+                              << lease_->hwaddr_->hwaddr_.size()
+                              << " exceeds maximum allowed of: "
+                              << HWAddr::MAX_HWADDR_LEN);
+                }
                 hwaddr_ = hwaddr->hwaddr_;
                 hwaddr_ = hwaddr->hwaddr_;
             } else {
             } else {
                 hwaddr_.clear();
                 hwaddr_.clear();
@@ -1009,8 +1039,8 @@ public:
             data.add(reinterpret_cast<void*>(&state_));
             data.add(reinterpret_cast<void*>(&state_));
             size.add(NULL);
             size.add(NULL);
 
 
-            for (int i = 0; i < 16; i++) {
-                CqlLeaseMgr::getData(row, i, data, size, *this);
+            for (size_t i = 0; i < data.size(); i++) {
+                CqlLeaseMgr::getData(row, i, data, size, i, *this);
             }
             }
 
 
             // address: varchar
             // address: varchar
@@ -1151,8 +1181,8 @@ CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex,
 }
 }
 
 
 void
 void
-CqlLeaseMgr::getData(const CassRow* row, int pindex, CqlDataArray& data,
-        CqlDataArray& size, const SqlExchange& exchange) {
+CqlLeaseMgr::getData(const CassRow* row, const int pindex, CqlDataArray& data,
+        CqlDataArray& size, const int dindex, const SqlExchange& exchange) {
     const CassValue* value;
     const CassValue* value;
     if (pindex >= exchange.parameters_.size()) {
     if (pindex >= exchange.parameters_.size()) {
         return;
         return;
@@ -1169,8 +1199,8 @@ CqlLeaseMgr::getData(const CassRow* row, int pindex, CqlDataArray& data,
         if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
         if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
             isc_throw(BadValue, "index " << type << " out of bounds");
             isc_throw(BadValue, "index " << type << " out of bounds");
         }
         }
-        CqlFunctions[type].sqlGetFunction_(value, data.values_[pindex],
-            reinterpret_cast<size_t *>(size.values_[pindex]));
+        CqlFunctions[type].sqlGetFunction_(value, data.values_[dindex],
+            reinterpret_cast<size_t *>(size.values_[dindex]));
     }
     }
 }
 }
 
 
@@ -1200,14 +1230,31 @@ CqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
     if (rc != CASS_OK) {
     if (rc != CASS_OK) {
         cass_future_free(future);
         cass_future_free(future);
         cass_statement_free(statement);
         cass_statement_free(statement);
-        isc_throw(DbOperationError, error);
+        return false;
     }
     }
+
+    // Check if statement has been applied.
     const CassResult* resultCollection = cass_future_get_result(future);
     const CassResult* resultCollection = cass_future_get_result(future);
+    CassIterator* rows = cass_iterator_from_result(resultCollection);
+    CqlDataArray appliedData;
+    CqlDataArray appliedSize;
+    bool applied = false;
+    while (cass_iterator_next(rows)) {
+        const CassRow* row = cass_iterator_get_row(rows);
+        // [applied]: bool
+        appliedData.add(reinterpret_cast<void*>(&applied));
+        appliedSize.add(NULL);
+        CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData,
+                             appliedSize, 0, exchange);
+    }
+
+    // Free resources.
+    cass_iterator_free(rows);
     cass_result_free(resultCollection);
     cass_result_free(resultCollection);
     cass_future_free(future);
     cass_future_free(future);
     cass_statement_free(statement);
     cass_statement_free(statement);
 
 
-    return (true);
+    return applied;
 }
 }
 
 
 bool
 bool
@@ -1268,7 +1315,6 @@ void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
 
 
     const CassResult* resultCollection = cass_future_get_result(future);
     const CassResult* resultCollection = cass_future_get_result(future);
     CassIterator* rows = cass_iterator_from_result(resultCollection);
     CassIterator* rows = cass_iterator_from_result(resultCollection);
-
     int rowCount = 0;
     int rowCount = 0;
     while (cass_iterator_next(rows)) {
     while (cass_iterator_next(rows)) {
         rowCount++;
         rowCount++;
@@ -1555,14 +1601,13 @@ CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
                                        const size_t max_leases,
                                        const size_t max_leases,
                                        StatementIndex statement_index) const {
                                        StatementIndex statement_index) const {
     // Set up the WHERE clause value
     // Set up the WHERE clause value
-    //"WHERE state != ? AND expire < ? ORDER BY expire ASC "
     uint32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
     uint32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
     uint64_t timestamp = static_cast<int64_t>(time(NULL));
     uint64_t timestamp = static_cast<int64_t>(time(NULL));
 
 
     // If the number of leases is 0, we will return all leases. This is
     // If the number of leases is 0, we will return all leases. This is
     // achieved by setting the limit to a very high value.
     // achieved by setting the limit to a very high value.
-    uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
-        std::numeric_limits<uint32_t>::max();
+    uint32_t limit = max_leases > 0 ? static_cast<int32_t>(max_leases) :
+        std::numeric_limits<int32_t>::max();
 
 
     for (uint32_t state = Lease::STATE_DEFAULT;
     for (uint32_t state = Lease::STATE_DEFAULT;
             state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
             state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
@@ -1622,10 +1667,30 @@ CqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
         isc_throw(DbOperationError, error);
         isc_throw(DbOperationError, error);
     }
     }
 
 
+    // Check if statement has been applied.
     const CassResult* resultCollection = cass_future_get_result(future);
     const CassResult* resultCollection = cass_future_get_result(future);
+    CassIterator* rows = cass_iterator_from_result(resultCollection);
+    CqlDataArray appliedData;
+    CqlDataArray appliedSize;
+    bool applied = false;
+    while (cass_iterator_next(rows)) {
+        const CassRow* row = cass_iterator_get_row(rows);
+        // [applied]: bool
+        appliedData.add(reinterpret_cast<void*>(&applied));
+        appliedSize.add(NULL);
+        CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData,
+                             appliedSize, 0, exchange);
+    }
+
+    // Free resources.
+    cass_iterator_free(rows);
     cass_result_free(resultCollection);
     cass_result_free(resultCollection);
     cass_future_free(future);
     cass_future_free(future);
     cass_statement_free(statement);
     cass_statement_free(statement);
+
+    if (!applied) {
+        isc_throw(NoSuchLease, "Statement has not been applied.");
+    }
 }
 }
 
 
 void
 void
@@ -1700,13 +1765,34 @@ CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
     std::string error;
     std::string error;
     dbconn_.checkStatementError(error, future, stindex, "unable to DELETE");
     dbconn_.checkStatementError(error, future, stindex, "unable to DELETE");
     rc = cass_future_error_code(future);
     rc = cass_future_error_code(future);
-    cass_future_free(future);
-    cass_statement_free(statement);
     if (rc != CASS_OK) {
     if (rc != CASS_OK) {
-       isc_throw(DbOperationError, error);
+        cass_future_free(future);
+        cass_statement_free(statement);
+        isc_throw(DbOperationError, error);
+    }
+
+    // Check if statement has been applied.
+    const CassResult* resultCollection = cass_future_get_result(future);
+    CassIterator* rows = cass_iterator_from_result(resultCollection);
+    CqlDataArray appliedData;
+    CqlDataArray appliedSize;
+    bool applied = false;
+    while (cass_iterator_next(rows)) {
+        const CassRow* row = cass_iterator_get_row(rows);
+        // [applied]: bool
+        appliedData.add(reinterpret_cast<void*>(&applied));
+        appliedSize.add(NULL);
+        CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData,
+                             appliedSize, 0, exchange);
     }
     }
 
 
-    return (true);
+    // Free resources.
+    cass_iterator_free(rows);
+    cass_result_free(resultCollection);
+    cass_future_free(future);
+    cass_statement_free(statement);
+
+    return applied;
 }
 }
 
 
 bool
 bool
@@ -1757,7 +1843,6 @@ uint64_t
 CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
 CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
         StatementIndex statement_index) {
         StatementIndex statement_index) {
     // Set up the WHERE clause value
     // Set up the WHERE clause value
-    //"WHERE state = ? AND expire < ? ALLOW FILTERING"
 
 
     CqlDataArray data;
     CqlDataArray data;
     uint32_t result = 0;
     uint32_t result = 0;
@@ -1845,13 +1930,12 @@ CqlLeaseMgr::getVersion() const {
         isc_throw(DbOperationError, error);
         isc_throw(DbOperationError, error);
     }
     }
 
 
+    // Get major and minor versions.
     const CassResult* resultCollection = cass_future_get_result(future);
     const CassResult* resultCollection = cass_future_get_result(future);
     CassIterator* rows = cass_iterator_from_result(resultCollection);
     CassIterator* rows = cass_iterator_from_result(resultCollection);
     CqlDataArray data;
     CqlDataArray data;
     CqlDataArray size;
     CqlDataArray size;
-    int rowCount = 0;
     while (cass_iterator_next(rows)) {
     while (cass_iterator_next(rows)) {
-        rowCount++;
         const CassRow* row = cass_iterator_get_row(rows);
         const CassRow* row = cass_iterator_get_row(rows);
         // version: uint32_t
         // version: uint32_t
         data.add(reinterpret_cast<void*>(&version));
         data.add(reinterpret_cast<void*>(&version));
@@ -1859,9 +1943,8 @@ CqlLeaseMgr::getVersion() const {
         // minor: uint32_t
         // minor: uint32_t
         data.add(reinterpret_cast<void*>(&minor));
         data.add(reinterpret_cast<void*>(&minor));
         size.add(NULL);
         size.add(NULL);
-
-        for (int i = 0; i < 2; i++) {
-            CqlLeaseMgr::getData(row, i, data, size, *versionExchange_);
+        for (size_t i = 0; i < data.size(); i++) {
+            CqlLeaseMgr::getData(row, i, data, size, i, *versionExchange_);
         }
         }
     }
     }
 
 

+ 6 - 2
src/lib/dhcpsrv/cql_lease_mgr.h

@@ -51,6 +51,10 @@ struct CqlDataArray {
         }
         }
         values_.erase(values_.begin() + index);
         values_.erase(values_.begin() + index);
     }
     }
+    /// Get size.
+    size_t size() {
+        return values_.size();
+    }
 };
 };
 
 
 class CqlVersionExchange;
 class CqlVersionExchange;
@@ -471,8 +475,8 @@ public:
     /// @param data array that has been created for the type of lease in question.
     /// @param data array that has been created for the type of lease in question.
     /// @param data size TODO
     /// @param data size TODO
     /// @param exchange Exchange object to use
     /// @param exchange Exchange object to use
-    static void getData(const CassRow* row, int pindex, CqlDataArray& data,
-        CqlDataArray& size, const SqlExchange& exchange);
+    static void getData(const CassRow* row, const int pindex, CqlDataArray& data,
+        CqlDataArray& size, const int dindex, const SqlExchange& exchange);
 
 
 private:
 private:
 
 

+ 205 - 6
src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc

@@ -87,14 +87,212 @@ public:
     /// Closes the database and re-open it. Anything committed should be
     /// Closes the database and re-open it. Anything committed should be
     /// visible.
     /// visible.
     ///
     ///
-    /// Parameter is ignored for CQL backend as the v4 and v6 leases share
-    /// the same database.
+    /// Parameter is ignored for CQL backend as the v4 and v6 leases share the
+    /// same keyspace.
     void reopen(Universe) {
     void reopen(Universe) {
         LeaseMgrFactory::destroy();
         LeaseMgrFactory::destroy();
         LeaseMgrFactory::create(validCqlConnectionString());
         LeaseMgrFactory::create(validCqlConnectionString());
         lmptr_ = &(LeaseMgrFactory::instance());
         lmptr_ = &(LeaseMgrFactory::instance());
     }
     }
 
 
+    // This is the CQL implementation for GenericLeaseMgrTest::testGetExpiredLeases4().
+    // The GenericLeaseMgrTest implementation checks for the order of expired
+    // leases to be from the most expired to the least expired. Cassandra
+    // doesn't support ORDER BY without imposing a EQ / IN restriction on the
+    // columns. Because of that, the order check has been excluded.
+    void
+    testCqlGetExpiredLeases4() {
+        // Get the leases to be used for the test.
+        vector<Lease4Ptr> leases = createLeases4();
+        // Make sure we have at least 6 leases there.
+        ASSERT_GE(leases.size(), 6U);
+
+        // Use the same current time for all leases.
+        time_t current_time = time(NULL);
+
+        // Add them to the database
+        for (size_t i = 0U; i < leases.size(); ++i) {
+            // Mark every other lease as expired.
+            if (i % 2U == 0U) {
+                // Set client last transmission time to the value older than the
+                // valid lifetime to make it expired. The expiration time also
+                // depends on the lease index, so as we can later check that the
+                // leases are ordered by the expiration time.
+                leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+            } else {
+                // Set current time as cltt for remaining leases. These leases are
+                // not expired.
+                leases[i]->cltt_ = current_time;
+            }
+            ASSERT_TRUE(lmptr_->addLease(leases[i]));
+        }
+
+        // Retrieve at most 1000 expired leases.
+        Lease4Collection expired_leases;
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000));
+        // Leases with even indexes should be returned as expired.
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+
+        // Update current time for the next test.
+        current_time = time(NULL);
+        // Also, remove expired leases collected during the previous test.
+        expired_leases.clear();
+
+        // This time let's reverse the expiration time and see if they will be returned
+        // in the correct order.
+        for (size_t i = 0U; i < leases.size(); ++i) {
+            // Update the time of expired leases with even indexes.
+            if (i % 2U == 0U) {
+                leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+            } else {
+                // Make sure remaining leases remain unexpired.
+                leases[i]->cltt_ = current_time + 100;
+            }
+            ASSERT_NO_THROW(lmptr_->updateLease4(leases[i]));
+        }
+
+        // Retrieve expired leases again. The limit of 0 means return all expired
+        // leases.
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0));
+        // The same leases should be returned.
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+
+        // Remember expired leases returned.
+        std::vector<Lease4Ptr> saved_expired_leases = expired_leases;
+
+        // Remove expired leases again.
+        expired_leases.clear();
+
+        // Limit the number of leases to be returned to 2.
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 2));
+
+        // Make sure we have exactly 2 leases returned.
+        ASSERT_EQ(2U, expired_leases.size());
+
+        // Mark every other expired lease as reclaimed.
+        for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
+            if (i % 2U != 0U) {
+                saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+            }
+            ASSERT_NO_THROW(lmptr_->updateLease4(saved_expired_leases[i]));
+        }
+
+        expired_leases.clear();
+
+        // This the returned leases should exclude reclaimed ones. So the number
+        // of returned leases should be roughly half of the expired leases.
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0U));
+        ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2U),
+                  expired_leases.size());
+
+        // Make sure that returned leases are those that are not reclaimed, i.e.
+        // those that have even index.
+        for (Lease4Collection::iterator lease = expired_leases.begin();
+             lease != expired_leases.end(); ++lease) {
+            int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+            EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
+        }
+    }
+
+    // This is the CQL implementation for GenericLeaseMgrTest::testGetExpiredLeases4().
+    // The GenericLeaseMgrTest implementation checks for the order of expired
+    // leases to be from the most expired to the least expired. Cassandra
+    // doesn't support ORDER BY without imposing a EQ / IN restriction on the
+    // columns. Because of that, the order check has been excluded.
+    void
+    testCqlGetExpiredLeases6() {
+        // Get the leases to be used for the test.
+        vector<Lease6Ptr> leases = createLeases6();
+        // Make sure we have at least 6 leases there.
+        ASSERT_GE(leases.size(), 6U);
+
+        // Use the same current time for all leases.
+        time_t current_time = time(NULL);
+
+        // Add them to the database
+        for (size_t i = 0U; i < leases.size(); ++i) {
+            // Mark every other lease as expired.
+            if (i % 2U == 0U) {
+                // Set client last transmission time to the value older than the
+                // valid lifetime to make it expired. The expiration time also
+                // depends on the lease index, so as we can later check that the
+                // leases are ordered by the expiration time.
+                leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+            } else {
+                // Set current time as cltt for remaining leases. These leases are
+                // not expired.
+                leases[i]->cltt_ = current_time;
+            }
+            ASSERT_TRUE(lmptr_->addLease(leases[i]));
+        }
+
+        // Retrieve at most 1000 expired leases.
+        Lease6Collection expired_leases;
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000));
+        // Leases with even indexes should be returned as expired.
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+
+        // Update current time for the next test.
+        current_time = time(NULL);
+        // Also, remove expired leases collected during the previous test.
+        expired_leases.clear();
+
+        // This time let's reverse the expiration time and see if they will be returned
+        // in the correct order.
+        for (size_t i = 0U; i < leases.size(); ++i) {
+            // Update the time of expired leases with even indexes.
+            if (i % 2U == 0U) {
+                leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+
+            } else {
+                // Make sure remaining leases remain unexpired.
+                leases[i]->cltt_ = current_time + 100;
+            }
+            ASSERT_NO_THROW(lmptr_->updateLease6(leases[i]));
+        }
+
+        // Retrieve expired leases again. The limit of 0 means return all expired
+        // leases.
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+        // The same leases should be returned.
+        ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+
+        // Remember expired leases returned.
+        std::vector<Lease6Ptr> saved_expired_leases = expired_leases;
+
+        // Remove expired leases again.
+        expired_leases.clear();
+
+        // Limit the number of leases to be returned to 2.
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 2));
+
+        // Make sure we have exactly 2 leases returned.
+        ASSERT_EQ(2U, expired_leases.size());
+
+        // Mark every other expired lease as reclaimed.
+        for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
+            if (i % 2U != 0U) {
+                saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+            }
+            ASSERT_NO_THROW(lmptr_->updateLease6(saved_expired_leases[i]));
+        }
+
+        expired_leases.clear();
+
+        // This the returned leases should exclude reclaimed ones. So the number
+        // of returned leases should be roughly half of the expired leases.
+        ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+
+        // Make sure that returned leases are those that are not reclaimed, i.e.
+        // those that have even index.
+        for (Lease6Collection::iterator lease = expired_leases.begin();
+             lease != expired_leases.end(); ++lease) {
+            int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+            EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
+        }
+    }
 };
 };
 
 
 /// @brief Check that database can be opened
 /// @brief Check that database can be opened
@@ -103,7 +301,8 @@ public:
 /// only if the database can be opened. Note that this is not part of the
 /// only if the database can be opened. Note that this is not part of the
 /// CqlLeaseMgr test fixure set. This test checks that the database can be
 /// CqlLeaseMgr test fixure set. This test checks that the database can be
 /// opened: the fixtures assume that and check basic operations.
 /// opened: the fixtures assume that and check basic operations.
-
+/// Unlike other backend implementations, this one doesn't check for lacking
+/// parameters. In that scenario, Cassandra defaults to configured parameters.
 TEST(CqlOpenTest, OpenDatabase) {
 TEST(CqlOpenTest, OpenDatabase) {
 
 
     // Schema needs to be created for the test to work.
     // Schema needs to be created for the test to work.
@@ -188,7 +387,7 @@ TEST_F(CqlLeaseMgrTest, checkTimeConversion) {
     EXPECT_EQ(cltt, converted_cltt);
     EXPECT_EQ(cltt, converted_cltt);
 }
 }
 
 
-/// @brief Check getName() returns correct database name
+/// @brief Check getName() returns correct keyspace name.
 TEST_F(CqlLeaseMgrTest, getName) {
 TEST_F(CqlLeaseMgrTest, getName) {
     EXPECT_EQ(std::string("keatest"), lmptr_->getName());
     EXPECT_EQ(std::string("keatest"), lmptr_->getName());
 }
 }
@@ -420,7 +619,7 @@ TEST_F(CqlLeaseMgrTest, testLease6HWTypeAndSource) {
 /// the order from most to least expired. It also checks that the lease
 /// the order from most to least expired. It also checks that the lease
 /// which is marked as 'reclaimed' is not returned.
 /// which is marked as 'reclaimed' is not returned.
 TEST_F(CqlLeaseMgrTest, getExpiredLeases4) {
 TEST_F(CqlLeaseMgrTest, getExpiredLeases4) {
-    testGetExpiredLeases4();
+    testCqlGetExpiredLeases4();
 }
 }
 
 
 /// @brief Check that the expired DHCPv6 leases can be retrieved.
 /// @brief Check that the expired DHCPv6 leases can be retrieved.
@@ -431,7 +630,7 @@ TEST_F(CqlLeaseMgrTest, getExpiredLeases4) {
 /// the order from most to least expired. It also checks that the lease
 /// the order from most to least expired. It also checks that the lease
 /// which is marked as 'reclaimed' is not returned.
 /// which is marked as 'reclaimed' is not returned.
 TEST_F(CqlLeaseMgrTest, getExpiredLeases6) {
 TEST_F(CqlLeaseMgrTest, getExpiredLeases6) {
-    testGetExpiredLeases6();
+    testCqlGetExpiredLeases6();
 }
 }
 
 
 /// @brief Check that expired reclaimed DHCPv6 leases are removed.
 /// @brief Check that expired reclaimed DHCPv6 leases are removed.

+ 10 - 9
src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc

@@ -589,7 +589,10 @@ GenericLeaseMgrTest::testGetLease4ClientIdHWAddrSubnetId() {
 
 
 void
 void
 GenericLeaseMgrTest::testAddGetDelete6(bool check_t1_t2) {
 GenericLeaseMgrTest::testAddGetDelete6(bool check_t1_t2) {
-    IOAddress addr("2001:db8:1::456");
+    const std::string addr234("2001:db8:1::234");
+    const std::string addr456("2001:db8:1::456");
+    const std::string addr789("2001:db8:1::789");
+    IOAddress addr(addr456);
 
 
     uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
     uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
     DuidPtr duid(new DUID(llt, sizeof(llt)));
     DuidPtr duid(new DUID(llt, sizeof(llt)));
@@ -606,11 +609,10 @@ GenericLeaseMgrTest::testAddGetDelete6(bool check_t1_t2) {
     // should not be allowed to add a second lease with the same address
     // should not be allowed to add a second lease with the same address
     EXPECT_FALSE(lmptr_->addLease(lease));
     EXPECT_FALSE(lmptr_->addLease(lease));
 
 
-    Lease6Ptr x = lmptr_->getLease6(Lease::TYPE_NA,
-                                    IOAddress("2001:db8:1::234"));
+    Lease6Ptr x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr234));
     EXPECT_EQ(Lease6Ptr(), x);
     EXPECT_EQ(Lease6Ptr(), x);
 
 
-    x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
+    x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
     ASSERT_TRUE(x);
     ASSERT_TRUE(x);
 
 
     EXPECT_EQ(x->addr_, addr);
     EXPECT_EQ(x->addr_, addr);
@@ -655,19 +657,19 @@ GenericLeaseMgrTest::testAddGetDelete6(bool check_t1_t2) {
     EXPECT_FALSE(y);
     EXPECT_FALSE(y);
 
 
     // should return false - there's no such address
     // should return false - there's no such address
-    EXPECT_FALSE(lmptr_->deleteLease(IOAddress("2001:db8:1::789")));
+    EXPECT_FALSE(lmptr_->deleteLease(IOAddress(addr789)));
 
 
     // this one should succeed
     // this one should succeed
-    EXPECT_TRUE(lmptr_->deleteLease(IOAddress("2001:db8:1::456")));
+    EXPECT_TRUE(lmptr_->deleteLease(IOAddress(addr456)));
 
 
     // after the lease is deleted, it should really be gone
     // after the lease is deleted, it should really be gone
-    x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
+    x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
     EXPECT_FALSE(x);
     EXPECT_FALSE(x);
 
 
     // Reopen the lease storage to make sure that lease is gone from the
     // Reopen the lease storage to make sure that lease is gone from the
     // persistent storage.
     // persistent storage.
     reopen(V6);
     reopen(V6);
-    x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
+    x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
     EXPECT_FALSE(x);
     EXPECT_FALSE(x);
 }
 }
 
 
@@ -1699,7 +1701,6 @@ GenericLeaseMgrTest::testGetExpiredLeases4() {
         // Update the time of expired leases with even indexes.
         // Update the time of expired leases with even indexes.
         if (i % 2 == 0) {
         if (i % 2 == 0) {
             leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
             leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
-
         } else {
         } else {
             // Make sure remaining leases remain unexpired.
             // Make sure remaining leases remain unexpired.
             leases[i]->cltt_ = current_time + 100;
             leases[i]->cltt_ = current_time + 100;