Browse Source

added support for datastax cassandra

Razvan Becheriu 9 years ago
parent
commit
77843631ab

+ 4 - 0
src/bin/admin/tests/data/dscsql.lease4_dump_test.reference.csv

@@ -0,0 +1,4 @@
+address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname
+0.0.0.10,3230,3330,40,2015-01-01 01:15:30,50,1,1,one.example.com
+0.0.0.11,,313233,40,2015-02-02 02:30:45,50,1,1,
+0.0.0.12,3232,,40,2015-03-03 11:01:07,50,1,1,three.example.com

+ 4 - 0
src/bin/admin/tests/data/dscsql.lease6_dump_test.reference.csv

@@ -0,0 +1,4 @@
+address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source
+10,3230,30,2015-04-04 01:15:30,40,50,IA_TA,60,70,1,1,one.example.com,3830,90,100
+11,,30,2015-05-05 02:30:45,40,50,IA_TA,60,70,1,1,,3830,90,100
+12,3231,30,2015-06-06 11:01:07,40,50,IA_TA,60,70,1,1,three.example.com,3830,90,100

+ 213 - 0
src/bin/admin/tests/dhcpdb_create_1.0.cql

@@ -0,0 +1,213 @@
+-- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+
+-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+
+--           http://www.apache.org/licenses/LICENSE-2.0
+
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- This is the Kea schema 1.0 specification for DataStax Cassandra SQL.
+-- Note: this is outdated version on purpose and it used to test upgrade
+-- process. Do not update this file to 2.0 or any later.
+
+-- The schema is reasonably portable (with the exception of the engine
+-- specification, which is Datastax Cassandra SQL-specific).  Minor changes might be needed for
+-- other databases.
+
+-- To create the schema, either type the command:
+
+-- cqlsh -u <user> -p <password> -k <database> -f dhcpdb_create.cql
+
+-- ... at the command prompt, or log in to the DSC SQL database and at the "cqlsh>"
+-- prompt, issue the command:
+
+-- SOURCE dhcpdb_create.cql
+
+-- This script is also called from kea-admin, see kea-admin init dscsql
+
+-- Over time, Kea database schema will evolve. Each version is marked with
+-- major.minor version. This file is organized sequentially, i.e. database
+-- is initialized to 1.0, then upgraded to 2.0 etc. This may be somewhat
+-- sub-optimal, but it ensues consistency with upgrade scripts. (It is much
+-- easier to maintain init and upgrade scripts if they look the same).
+-- Since initialization is done only once, it's perfromance is not an issue.
+
+-- This line starts database initialization to 1.0.
+
+-- Holds the IPv4 leases.
+CREATE TABLE lease4 (
+    address int,
+    hwaddr blob,
+    client_id blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    state int,
+    PRIMARY KEY (address)
+);
+
+-- Create search indexes for lease4 table
+CREATE INDEX lease4index1 ON lease4 (client_id);
+CREATE INDEX lease4index2 ON lease4 (subnet_id);
+CREATE INDEX lease4index3 ON lease4 (hwaddr);
+CREATE INDEX lease4index4 ON lease4 (state);
+
+-- 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,
+    duid blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    pref_lifetime bigint,
+    lease_type int,
+    iaid int,
+    prefix_len int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    hwaddr blob,
+    hwtype int,
+    hwaddr_source int,
+    state int,
+    PRIMARY KEY (address)
+);
+
+-- Create search indexes for lease6 table
+CREATE INDEX lease6index1 ON lease6 (lease_type);
+CREATE INDEX lease6index2 ON lease6 (duid);
+CREATE INDEX lease6index3 ON lease6 (iaid);
+CREATE INDEX lease6index4 ON lease6 (subnet_id);
+CREATE INDEX lease6index5 ON lease6 (state);
+
+-- ... 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.
+-- Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/
+-- lease_mgr.h)
+CREATE TABLE lease6_types (
+    lease_type int,                             -- Lease type code.
+    name varchar,                               -- Name of the lease type
+    PRIMARY KEY (lease_type)
+);
+--START TRANSACTION;
+INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
+INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA');   -- Temporary v6 addresses
+INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix delegations
+--COMMIT;
+
+-- Kea keeps track of the hardware/MAC address source, i.e. how the address
+-- was obtained. Depending on the technique and your network topology, it may
+-- be more or less trustworthy. 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. For details,
+-- see constants defined in src/lib/dhcp/dhcp/pkt.h for detailed explanation.
+CREATE TABLE lease_hwaddr_source (
+    hwaddr_source int,
+    name varchar,
+    PRIMARY KEY (hwaddr_source)
+);
+
+-- Hardware address obtained from raw sockets
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (1, 'HWADDR_SOURCE_RAW');
+
+-- Hardware address converted from IPv6 link-local address with EUI-64
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (2, 'HWADDR_SOURCE_IPV6_LINK_LOCAL');
+
+-- Hardware address extracted from client-id (duid)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (4, 'HWADDR_SOURCE_DUID');
+
+-- Hardware address extracted from client address relay option (RFC6939)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (8, 'HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION');
+
+-- Hardware address extracted from remote-id option (RFC4649)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (16, 'HWADDR_SOURCE_REMOTE_ID');
+
+-- Hardware address extracted from subscriber-id option (RFC4580)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE_SUBSCRIBER_ID');
+
+-- Hardware address extracted from docsis options
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS');
+
+-- -----------------------------------------------------
+-- Table `dhcp4_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp4_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp4_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp4_options table
+CREATE INDEX dhcp4_optionsindex1 ON dhcp4_options (host_id);
+
+-- -----------------------------------------------------
+-- Table `dhcp6_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp6_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp6_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp6_options table
+CREATE INDEX dhcp6_optionsindex1 ON dhcp6_options (host_id);
+
+-- Create table holding mapping of the lease states to their names.
+-- This is not used in queries from the DHCP server but rather in
+-- direct queries from the lease database management tools.
+CREATE TABLE lease_state (
+    state int,
+    name varchar,
+    PRIMARY KEY (state)
+);
+
+-- Insert currently defined state names.
+INSERT INTO lease_state (state, name) VALUES (0, 'default');
+INSERT INTO lease_state (state, name) VALUES (1, 'declined');
+INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
+
+-- Finally, the version of the schema.  We start at 1.0 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,
+    minor int,
+    PRIMARY KEY (version)
+);
+--START TRANSACTION;
+INSERT INTO schema_version (version, minor) VALUES (1, 0);
+--COMMIT;

+ 38 - 0
src/bin/admin/tests/dscsql_tests.sh.in

@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Include common test library.
+. @abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh
+
+dscsql_init_test() {
+    test_start "dscsql.init"
+    
+    # @todo: Implement this
+
+    test_finish 0
+}
+
+dscsql_version_test() {
+    test_start "dscsql.version"
+    
+    # @todo: Implement this
+
+    test_finish 0
+}
+
+dscsql_upgrade_test() {
+    test_start "dscsql.upgrade"
+    
+    # @todo: Implement this
+
+    test_finish 0
+}
+
+dscsql_init_test
+dscsql_version_test
+dscsql_upgrade_test

+ 243 - 0
src/lib/dhcpsrv/dscsql_connection.cc

@@ -0,0 +1,243 @@
+// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <dhcpsrv/dscsql_connection.h>
+#include <string>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+DSCSqlConnection::DSCSqlConnection(const ParameterMap& parameters) : DatabaseConnection(parameters),
+        cluster_(NULL), session_(NULL), tagged_statements_(NULL) {
+}
+
+DSCSqlConnection::~DSCSqlConnection() {
+    CassError rc;
+    for (int i = 0; i < statements_.size(); i++)
+    {
+        if (statements_[i]) {
+            cass_prepared_free(statements_[i]);
+        }
+        statements_[i] = NULL;
+    }
+    CassFuture* close_future = cass_session_close(session_);
+    cass_future_wait(close_future);
+    std::string error;
+    checkStatementError(error, close_future, "could not close connection to DB");
+    rc = cass_future_error_code(close_future);
+    cass_future_free(close_future);
+    cass_session_free(session_);
+    session_ = NULL;
+    cass_cluster_free(cluster_);
+    cluster_ = NULL;
+    if (rc != CASS_OK) {
+        isc_throw(DbOpenError, error);
+    }
+}
+
+void
+DSCSqlConnection::openDatabase() {
+    CassError rc;
+    // Set up the values of the parameters
+    const char* contact_points = "127.0.0.1";
+    string scontact_points;
+    try {
+        scontact_points = getParameter("contact_points");
+        contact_points = scontact_points.c_str();
+    } catch (...) {
+        // No host.  Fine, we'll use "localhost"
+    }
+
+    const char* port = NULL;
+    string sport;
+    try {
+        sport = getParameter("port");
+        port = sport.c_str();
+    } catch (...) {
+        // No port.  Fine, we'll use "default"
+    }
+
+    const char* user = NULL;
+    string suser;
+    try {
+        suser = getParameter("user");
+        user = suser.c_str();
+    } catch (...) {
+        // No user.  Fine, we'll use NULL
+    }
+
+    const char* password = NULL;
+    string spassword;
+    try {
+        spassword = getParameter("password");
+        password = spassword.c_str();
+    } catch (...) {
+        // No password.  Fine, we'll use NULL
+    }
+
+    const char* keyspace = "keatest";
+    string skeyspace;
+    try {
+        skeyspace = getParameter("keyspace");
+        keyspace = skeyspace.c_str();
+    } catch (...) {
+        // No database name.  Fine, we'll use default 'keatest'
+    }
+
+    cluster_ = cass_cluster_new();
+    cass_cluster_set_contact_points(cluster_, contact_points);
+
+    if (user != NULL && password != NULL) {
+        cass_cluster_set_credentials(cluster_, user, password);
+    }
+
+    if (port != NULL) {
+        int port_nr;
+        try {
+            port_nr = boost::lexical_cast<int>(port);
+        } catch (const std::exception& ex) {
+            isc_throw(DbOperationError, "Invalid int data: " << port
+                      << " : " << ex.what());
+        }
+        cass_cluster_set_port(cluster_, port_nr);
+    }
+
+    session_ = cass_session_new();
+
+    CassFuture* connect_future = cass_session_connect_keyspace(session_, cluster_, keyspace);
+    cass_future_wait(connect_future);
+    std::string error;
+    checkStatementError(error, connect_future, "could not connect to DB");
+    rc = cass_future_error_code(connect_future);
+    cass_future_free(connect_future);
+    if (rc != CASS_OK) {
+        cass_session_free(session_);
+        session_ = NULL;
+        cass_cluster_free(cluster_);
+        cluster_ = NULL;
+        isc_throw(DbOpenError, error);
+    }
+}
+
+void
+DSCSqlConnection::prepareStatements(DSCSqlTaggedStatement *statements) {
+    CassError rc = CASS_OK;
+    CassFuture* future = NULL;
+    uint32_t size = 0;
+    tagged_statements_ = statements;
+    for (; tagged_statements_[size].params_; size++);
+    statements_.resize(size);
+    for (uint32_t i = 0; i < size; i++) {
+        const char* query = tagged_statements_[i].text_;
+
+        future = cass_session_prepare(session_, query);
+        cass_future_wait(future);
+        std::string error;
+        checkStatementError(error, future, i, "could not prepare statement");
+        rc = cass_future_error_code(future);
+        if (rc != CASS_OK) {
+            cass_future_free(future);
+            statements_[i] = NULL;
+            isc_throw(DbOperationError, error);
+        } else {
+            statements_[i] = cass_future_get_prepared(future);
+        }
+
+        cass_future_free(future);
+    }
+}
+
+string
+DSCSqlConnection::getName() const {
+    string name = "";
+    try {
+        name = getParameter("name");
+    } catch (...) {
+        // Return an empty name
+    }
+    return (name);
+}
+
+string
+DSCSqlConnection::getDescription() const {
+    return (string("DataStax Cassandra Database"));
+}
+
+pair<uint32_t, uint32_t>
+DSCSqlConnection::getVersion() const {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+              DHCPSRV_DSCSQL_GET_VERSION);
+
+    uint32_t version = CASS_VERSION_MAJOR;
+    uint32_t minor = CASS_VERSION_MINOR;
+
+    return make_pair<uint32_t, uint32_t>(version, minor);
+}
+
+void
+DSCSqlConnection::commit() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_COMMIT);
+}
+
+void
+DSCSqlConnection::rollback() {
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_ROLLBACK);
+}
+
+
+void
+DSCSqlConnection::checkStatementError(std::string& error, CassFuture* future, uint32_t stindex, const char* what) const
+{
+    CassError rc;
+    const char* errorMessage;
+    size_t errorMessageSize;
+    std::stringstream stream;
+    stream << "no error for: " << tagged_statements_[stindex].name_;
+
+    rc = cass_future_error_code(future);
+    cass_future_error_message(future, &errorMessage, &errorMessageSize);
+
+    if (rc != CASS_OK) {
+        stream.str(std::string());
+        stream << what << " for: " << tagged_statements_[stindex].name_ << " reason: " <<
+            errorMessage << " error code: " << rc;
+    }
+    error = stream.str();
+}
+
+void
+DSCSqlConnection::checkStatementError(std::string& error, CassFuture* future, const char* what) const
+{
+    CassError rc;
+    const char* errorMessage;
+    size_t errorMessageSize;
+    std::stringstream stream;
+    stream << "no error";
+
+    rc = cass_future_error_code(future);
+    cass_future_error_message(future, &errorMessage, &errorMessageSize);
+
+    if (rc != CASS_OK) {
+        stream.str(std::string());
+        stream << what << " reason: " << errorMessage << " error code: " << rc;
+    }
+    error = stream.str();
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace

+ 140 - 0
src/lib/dhcpsrv/dscsql_connection.h

@@ -0,0 +1,140 @@
+// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DSCSQL_CONNECTION_H
+#define DSCSQL_CONNECTION_H
+
+#include <dhcpsrv/database_connection.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <inttypes.h>
+#include <cassandra.h>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief  Defines a single query
+struct DSCSqlTaggedStatement {
+    /// Param name.
+    const char** params_;
+
+    /// Short name of the query.
+    const char* name_;
+
+    /// Text representation of the actual query.
+    const char* text_;
+};
+
+/// Defines DSC SQL backend version: 1.0
+const uint32_t DSCSQL_CURRENT_VERSION = 1;
+const uint32_t DSCSQL_CURRENT_MINOR = 0;
+
+class DSCSqlConnection : public DatabaseConnection {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Initialize DSCSqlConnection object with parameters needed for connection.
+    DSCSqlConnection(const ParameterMap& parameters);
+
+    /// @brief Destructor
+    virtual ~DSCSqlConnection();
+
+    /// @brief Prepare statements
+    ///
+    /// Creates the prepared statements for all of the SQL statements used
+    /// by the PostgreSQL backend.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::InvalidParameter 'index' is not valid for the vector.  This
+    ///        represents an internal error within the code.
+    void prepareStatements(DSCSqlTaggedStatement *statements);
+
+    /// @brief Open Database
+    ///
+    /// Opens the database using the information supplied in the parameters
+    /// passed to the constructor.
+    ///
+    /// @throw NoDatabaseName Mandatory database name not given
+    /// @throw DbOpenError Error opening the database
+    void openDatabase();
+
+    /// @brief Return backend type
+    ///
+    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+    ///
+    /// @return Type of the backend.
+    virtual std::string getType() const {
+        return (std::string("cassandra"));
+    }
+
+    /// @brief Returns name of the database.
+    ///
+    /// @return database name
+    virtual std::string getName() const;
+
+    /// @brief Returns description of the backend.
+    ///
+    /// This description may be multiline text that describes the backend.
+    ///
+    /// @return Description of the backend.
+    virtual std::string getDescription() const;
+
+    /// @brief Returns backend version.
+    ///
+    /// @return Version number as a pair of unsigned integers.  "first" is the
+    ///         major version number, "second" the minor number.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+    /// @brief Commit Transactions
+    ///
+    /// Commits all pending database operations.
+    ///
+    /// @throw DbOperationError Iif the commit failed.
+    virtual void commit();
+
+    /// @brief Rollback Transactions
+    ///
+    /// Rolls back all pending database operations.
+    ///
+    /// @throw DbOperationError If the rollback failed.
+    virtual void rollback();
+
+    /// @brief Check Error
+    ///
+    /// Chech error for current database operation.
+    void checkStatementError(std::string& error, CassFuture* future, uint32_t stindex, const char* what) const;
+
+    /// @brief Check Error
+    ///
+    /// Chech error for current database operation.
+    void checkStatementError(std::string& error, CassFuture* future, const char* what) const;
+
+    /// DSC SQL connection handle
+    CassCluster* cluster_;
+    CassSession* session_;
+    std::vector<const CassPrepared*> statements_;       ///< Prepared statements
+    DSCSqlTaggedStatement *tagged_statements_;
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DSCSQL_CONNECTION_H

File diff suppressed because it is too large
+ 1910 - 0
src/lib/dhcpsrv/dscsql_lease_mgr.cc


+ 640 - 0
src/lib/dhcpsrv/dscsql_lease_mgr.h

@@ -0,0 +1,640 @@
+// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//           http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DSCSQL_LEASE_MGR_H
+#define DSCSQL_LEASE_MGR_H
+
+#include <dhcp/hwaddr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/dscsql_connection.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/utility.hpp>
+#include <cassandra.h>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Structure used to bind C++ input values to dynamic SQL parameters
+/// The structure contains a vector which store the input values,
+/// This vector is passed directly into the
+/// DSC SQL execute call.
+///
+/// Note that the data values are stored as pointers. These pointers need to
+/// valid for the duration of the DSC SQL statement execution.  In other
+/// words populating them with pointers to values that go out of scope before
+/// statement is executed is a bad idea.
+
+struct DSCSqlBindArray {
+    /// @brief Vector of pointers to the data values.
+    std::vector<void*> values_;
+    void add(void* value) {
+        values_.push_back(value);
+    }
+    void remove(int index) {
+        values_.erase(values_.begin() + index);
+    }
+};
+
+class DSCSqlLeaseExchange;
+class DSCSqlLease4Exchange;
+class DSCSqlLease6Exchange;
+
+enum DSCSqlDataType {
+    DSCSQL_DATA_TYPE_NONE,
+    DSCSQL_DATA_TYPE_BOOL,
+    DSCSQL_DATA_TYPE_INT32,
+    DSCSQL_DATA_TYPE_INT64,
+    DSCSQL_DATA_TYPE_TIMESTAMP,
+    DSCSQL_DATA_TYPE_STRING,
+    DSCSQL_DATA_TYPE_BYTES
+};
+
+/// @brief DataStax Cassandra Lease Manager
+///
+/// This class provides the \ref isc::dhcp::LeaseMgr interface to the DSC - DataStax Cassandra
+/// database.  Use of this backend presupposes that a DSC database is
+/// available and that the Kea schema has been created within it.
+class DSCSqlLeaseMgr : public LeaseMgr {
+public:
+
+    /// @brief Constructor
+    ///
+    /// Uses the following keywords in the parameters passed to it to
+    /// connect to the database:
+    /// - name - Name of the database to which to connect (mandatory)
+    /// - host - Host to which to connect (optional, defaults to "localhost")
+    /// - user - Username under which to connect (optional)
+    /// - password - Password for "user" on the database (optional)
+    ///
+    /// If the database is successfully opened, the version number in the
+    /// schema_version table will be checked against hard-coded value in
+    /// the implementation file.
+    ///
+    /// Finally, all the SQL commands are pre-compiled.
+    ///
+    /// @param parameters A data structure relating keywords and values
+    ///        concerned with the database.
+    ///
+    /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
+    /// @throw isc::dhcp::DbOpenError Error opening the database
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    DSCSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters);
+
+    /// @brief Destructor (closes database)
+    virtual ~DSCSqlLeaseMgr();
+
+    /// @brief Local version of getDBVersion() class method
+    static std::string getDBVersion();
+
+    /// @brief Adds an IPv4 lease
+    ///
+    /// @param lease lease to be added
+    ///
+    /// @result true if the lease was added, false if not (because a lease
+    ///         with the same address was already there).
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual bool addLease(const Lease4Ptr& lease);
+
+    /// @brief Adds an IPv6 lease
+    ///
+    /// @param lease lease to be added
+    ///
+    /// @result true if the lease was added, false if not (because a lease
+    ///         with the same address was already there).
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual bool addLease(const Lease6Ptr& lease);
+
+    /// @brief Returns an IPv4 lease for specified IPv4 address
+    ///
+    /// This method return a lease that is associated with a given address.
+    /// For other query types (by hardware addr, by Client ID) there can be
+    /// several leases in different subnets (e.g. for mobile clients that
+    /// got address in different subnets). However, for a single address
+    /// there can be only one lease, so this method returns a pointer to
+    /// a single lease, not a container of leases.
+    ///
+    /// @param addr address of the searched lease
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+
+    /// @brief Returns existing IPv4 leases for specified hardware address.
+    ///
+    /// Although in the usual case there will be only one lease, for mobile
+    /// clients or clients with multiple static/fixed/reserved leases there
+    /// can be more than one. Thus return type is a container, not a single
+    /// pointer.
+    ///
+    /// @param hwaddr hardware address of the client
+    ///
+    /// @return lease collection
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
+
+    /// @brief Returns existing IPv4 leases for specified hardware address
+    ///        and a subnet
+    ///
+    /// There can be at most one lease for a given HW address in a single
+    /// pool, so this method with either return a single lease or NULL.
+    ///
+    /// @param hwaddr hardware address of the client
+    /// @param subnet_id identifier of the subnet that lease must belong to
+    ///
+    /// @return a pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv4 leases for specified client-id
+    ///
+    /// Although in the usual case there will be only one lease, for mobile
+    /// clients or clients with multiple static/fixed/reserved leases there
+    /// can be more than one. Thus return type is a container, not a single
+    /// pointer.
+    ///
+    /// @param clientid client identifier
+    ///
+    /// @return lease collection
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Collection getLease4(const ClientId& clientid) const;
+
+    /// @brief Returns IPv4 lease for the specified client identifier, HW
+    /// address and subnet identifier.
+    ///
+    /// @param client_id A client identifier.
+    /// @param hwaddr Hardware address.
+    /// @param subnet_id A subnet identifier.
+    ///
+    /// @return A pointer to the lease or NULL if the lease is not found.
+    /// @throw isc::NotImplemented On every call as this function is currently
+    /// not implemented for the PostgreSQL backend.
+    virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv4 lease for specified client-id
+    ///
+    /// There can be at most one lease for a given HW address in a single
+    /// pool, so this method with either return a single lease or NULL.
+    ///
+    /// @param clientid client identifier
+    /// @param subnet_id identifier of the subnet that lease must belong to
+    ///
+    /// @return a pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease4Ptr getLease4(const ClientId& clientid,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv6 lease for a given IPv6 address.
+    ///
+    /// For a given address, we assume that there will be only one lease.
+    /// The assumption here is that there will not be site or link-local
+    /// addresses used, so there is no way of having address duplication.
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
+    /// @param addr address of the searched lease
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        lease type field.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease6Ptr getLease6(Lease::Type type,
+                                const isc::asiolink::IOAddress& addr) const;
+
+    /// @brief Returns existing IPv6 leases for a given DUID+IA combination
+    ///
+    /// Although in the usual case there will be only one lease, for mobile
+    /// clients or clients with multiple static/fixed/reserved leases there
+    /// can be more than one. Thus return type is a container, not a single
+    /// pointer.
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
+    /// @param duid client DUID
+    /// @param iaid IA identifier
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        lease type field.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                       uint32_t iaid) const;
+
+    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+    ///
+    /// @param type specifies lease type: (NA, TA or PD)
+    /// @param duid client DUID
+    /// @param iaid IA identifier
+    /// @param subnet_id subnet id of the subnet the lease belongs to
+    ///
+    /// @return lease collection (may be empty if no lease is found)
+    ///
+    /// @throw isc::BadValue record retrieved from database had an invalid
+    ///        lease type field.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid,
+                                        uint32_t iaid, SubnetID subnet_id) const;
+    /// @brief Returns a collection of expired DHCPv6 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases6(Lease6Collection& ,
+                                   const size_t ) const;
+
+
+    /// @brief Returns a collection of expired DHCPv4 leases.
+    ///
+    /// This method returns at most @c max_leases expired leases. The leases
+    /// returned haven't been reclaimed, i.e. the database query must exclude
+    /// reclaimed leases from the results returned.
+    ///
+    /// @param [out] expired_leases A container to which expired leases returned
+    /// by the database backend are added.
+    /// @param max_leases A maximum number of leases to be returned. If this
+    /// value is set to 0, all expired (but not reclaimed) leases are returned.
+    virtual void getExpiredLeases4(Lease4Collection& ,
+                                   const size_t ) const;
+
+
+    /// @brief Updates IPv4 lease.
+    ///
+    /// Updates the record of the lease in the database (as identified by the
+    /// address) with the data in the passed lease object.
+    ///
+    /// @param lease4 The lease to be updated.
+    ///
+    /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+    ///        exist.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual void updateLease4(const Lease4Ptr& lease4);
+
+    /// @brief Updates IPv6 lease.
+    ///
+    /// Updates the record of the lease in the database (as identified by the
+    /// address) with the data in the passed lease object.
+    ///
+    /// @param lease6 The lease to be updated.
+    ///
+    /// @throw isc::dhcp::NoSuchLease Attempt to update a lease that did not
+    ///        exist.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual void updateLease6(const Lease6Ptr& lease6);
+
+    /// @brief Deletes a lease.
+    ///
+    /// @param addr Address of the lease to be deleted.  This can be an IPv4
+    ///             address or an IPv6 address.
+    ///
+    /// @return true if deletion was successful, false if no such lease exists
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual bool deleteLease(const isc::asiolink::IOAddress& addr);
+
+    /// @brief Deletes all expired and reclaimed DHCPv4 leases.
+    ///
+    /// @param secs Number of seconds since expiration of leases before
+    /// they can be removed. Leases which have expired later than this
+    /// time will not be deleted.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t );
+
+    /// @brief Deletes all expired and reclaimed DHCPv6 leases.
+    ///
+    /// @param secs Number of seconds since expiration of leases before
+    /// they can be removed. Leases which have expired later than this
+    /// time will not be deleted.
+    ///
+    /// @return Number of leases deleted.
+    virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t );
+
+    /// @brief Return backend type
+    ///
+    /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+    ///
+    /// @return Type of the backend.
+    virtual std::string getType() const {
+        return (std::string("cassandra"));
+    }
+
+    /// @brief Returns name of the database.
+    ///
+    /// @return database name
+    virtual std::string getName() const;
+
+    /// @brief Returns description of the backend.
+    ///
+    /// This description may be multiline text that describes the backend.
+    ///
+    /// @return Description of the backend.
+    virtual std::string getDescription() const;
+
+    /// @brief Returns backend version.
+    ///
+    /// @return Version number as a pair of unsigned integers.  "first" is the
+    ///         major version number, "second" the minor number.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+    /// @brief Commit Transactions
+    ///
+    /// Commits all pending database operations.
+    ///
+    /// @throw DbOperationError Iif the commit failed.
+    virtual void commit();
+
+    /// @brief Rollback Transactions
+    ///
+    /// Rolls back all pending database operations.
+    ///
+    /// @throw DbOperationError If the rollback failed.
+    virtual void rollback();
+
+
+    /// @brief Statement Tags
+    ///
+    /// The contents of the enum are indexes into the list of compiled SQL
+    /// statements
+    enum StatementIndex {
+        DELETE_LEASE4,              // Delete from lease4 by address
+        DELETE_LEASE4_STATE_EXPIRED,// Delete expired lease4s in certain state
+        DELETE_LEASE6,              // Delete from lease6 by address
+        DELETE_LEASE6_STATE_EXPIRED,// Delete expired lease6s in certain state
+        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_LEASE4_EXPIRE,          // Get expired lease4
+        GET_LEASE6_ADDR,            // Get lease6 by address
+        GET_LEASE6_DUID_IAID,       // Get lease6 by DUID and IAID
+        GET_LEASE6_DUID_IAID_SUBID, // Get lease6 by DUID, IAID and subnet ID
+        GET_LEASE6_EXPIRE,          // Get expired lease6
+        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
+#ifdef TERASTREAM_DB_LOGIC
+        DISCOVER_LEASE4_NOREQNORES,
+        DISCOVER_LEASE4_NOREQRES,
+        DISCOVER_LEASE4_REQNORES,
+        DISCOVER_LEASE4_REQRES,
+        REQUEST_LEASE4_NOREQNORES,
+        REQUEST_LEASE4_NOREQRES,
+        REQUEST_LEASE4_REQNORES,
+        REQUEST_LEASE4_REQRES,
+#endif // TERASTREAM_DB_LOGIC
+        NUM_STATEMENTS              // Number of statements
+    };
+
+#ifdef TERASTREAM_DB_LOGIC
+    virtual void discoverLease4NoReqNoRes(LeaseExchangeData &data);
+    virtual void discoverLease4NoReqRes(LeaseExchangeData &data);
+    virtual void discoverLease4ReqNoRes(LeaseExchangeData &data);
+    virtual void discoverLease4ReqRes(LeaseExchangeData &data);
+    virtual void requestLease4NoReqNoRes(LeaseExchangeData &data);
+    virtual void requestLease4NoReqRes(LeaseExchangeData &data);
+    virtual void requestLease4ReqNoRes(LeaseExchangeData &data);
+    virtual void requestLease4ReqRes(LeaseExchangeData &data);
+#endif // TERASTREAM_DB_LOGIC
+
+private:
+
+    /// @brief Add Lease Common Code
+    ///
+    /// This method performs the common actions for both flavours (V4 and V6)
+    /// of the addLease method.  It binds the contents of the lease object to
+    /// the prepared statement and adds it to the database.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array that has been created for the type
+    ///        of lease in question.
+    ///
+    /// @return true if the lease was added, false if it was not added because
+    ///         a lease with that address already exists in the database.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    bool addLeaseCommon(StatementIndex stindex, DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& exchange);
+
+    /// @brief Get Lease Collection Common Code
+    ///
+    /// This method performs the common actions for obtaining multiple leases
+    /// from the database.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array containing the where clause input parameters
+    /// @param exchange Exchange object to use
+    /// @param result Returned collection of Leases Note that any leases in
+    ///        the collection when this method is called are not erased: the
+    ///        new data is appended to the end.
+    /// @param single If true, only a single data item is to be retrieved.
+    ///        If more than one is present, a MultipleRecords exception will
+    ///        be thrown.
+    ///
+    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+    ///        from the database where only one was expected.
+    template <typename Exchange, typename LeaseCollection>
+    void getLeaseCollection(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                            Exchange& exchange, LeaseCollection& result,
+                            bool single = false) const;
+
+    /// @brief Gets Lease4 Collection
+    ///
+    /// Gets a collection of Lease4 objects.  This is just an interface to
+    /// the get lease collection common code.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array containing the where clause input parameters
+    /// @param lease LeaseCollection object returned.  Note that any leases in
+    ///        the collection when this method is called are not erased: the
+    ///        new data is appended to the end.
+    ///
+    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+    ///        from the database where only one was expected.
+    void getLeaseCollection(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                            Lease4Collection& result) const {
+        getLeaseCollection(stindex, bind_array, exchange4_, result);
+    }
+
+    /// @brief Get Lease6 Collection
+    ///
+    /// Gets a collection of Lease6 objects.  This is just an interface to
+    /// the get lease collection common code.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array containing input parameters for the query
+    /// @param lease LeaseCollection object returned.  Note that any existing
+    ///        data in the collection is erased first.
+    ///
+    /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
+    ///        from the database where only one was expected.
+    void getLeaseCollection(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                            Lease6Collection& result) const {
+        getLeaseCollection(stindex, bind_array, exchange6_, result);
+    }
+
+    /// @brief Get Lease4 Common Code
+    ///
+    /// This method performs the common actions for the various getLease4()
+    /// methods.  It acts as an interface to the getLeaseCollection() method,
+    /// but retrieveing only a single lease.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array containing input parameters for the query
+    /// @param lease Lease4 object returned
+    void getLease(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                  Lease4Ptr& result) const;
+
+    /// @brief Get Lease6 Common Code
+    ///
+    /// This method performs the common actions for the various getLease4()
+    /// methods.  It acts as an interface to the getLeaseCollection() method,
+    /// but retrieveing only a single lease.
+    ///
+    /// @param stindex Index of statement being executed
+    /// @param bind_array array containing input parameters for the query
+    /// @param lease Lease6 object returned
+    void getLease(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                  Lease6Ptr& result) const;
+
+    /// @brief Get expired leases common code.
+    ///
+    /// This method retrieves expired and not reclaimed leases from the
+    /// lease database. The returned leases are ordered by the expiration
+    /// time. The maximum number of leases to be returned is specified
+    /// as an argument.
+    ///
+    /// @param [out] expired_leases Reference to the container where the
+    ///        retrieved leases are put.
+    /// @param max_leases Maximum number of leases to be returned.
+    /// @param statement_index One of the @c GET_LEASE4_EXPIRE or
+    ///        @c GET_LEASE6_EXPIRE.
+    ///
+    /// @tparam One of the @c Lease4Collection or @c Lease6Collection.
+    template<typename LeaseCollection>
+    void getExpiredLeasesCommon(LeaseCollection& expired_leases,
+                                const size_t max_leases,
+                                StatementIndex statement_index) const;
+
+    /// @brief Update lease common code
+    ///
+    /// Holds the common code for updating a lease.  It binds the parameters
+    /// to the prepared statement, executes it, then checks how many rows
+    /// were affected.
+    ///
+    /// @param stindex Index of prepared statement to be executed
+    /// @param bind_array array containing lease values and where clause
+    /// parameters for the update.
+    /// @param lease Pointer to the lease object whose record is being updated.
+    ///
+    /// @throw NoSuchLease Could not update a lease because no lease matches
+    ///        the address given.
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    template <typename LeasePtr>
+    void updateLeaseCommon(StatementIndex stindex, DSCSqlBindArray& bind_array,
+                           const LeasePtr& lease, DSCSqlLeaseExchange& exchange);
+
+    /// @brief Delete lease common code
+    ///
+    /// Holds the common code for deleting a lease.  It binds the parameters
+    /// to the prepared statement, executes the statement and checks to
+    /// see how many rows were deleted.
+    ///
+    /// @param stindex Index of prepared statement to be executed
+    /// @param bind_array array containing lease values and where clause
+    /// parameters for the delete
+    ///
+    /// @return Number of deleted leases.
+    ///
+    /// @throw isc::dhcp::DbOperationError An operation on the open database has
+    ///        failed.
+    bool deleteLeaseCommon(StatementIndex stindex, DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& exchange);
+
+    /// @brief Delete expired-reclaimed leases.
+    ///
+    /// @param secs Number of seconds since expiration of leases before
+    /// they can be removed. Leases which have expired later than this
+    /// time will not be deleted.
+    /// @param statement_index One of the @c DELETE_LEASE4_STATE_EXPIRED or
+    ///        @c DELETE_LEASE6_STATE_EXPIRED.
+    ///
+    /// @return Number of leases deleted.
+    uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
+                                                StatementIndex statement_index);
+
+    static void bindData(CassStatement* statement, const StatementIndex stindex, DSCSqlBindArray& bind_array, const DSCSqlLeaseExchange& exchange);
+
+    static void getDataType(const StatementIndex stindex, int param, const DSCSqlLeaseExchange& exchange, DSCSqlDataType& type);
+
+    static DSCSqlTaggedStatement tagged_statements_[];
+    /// Database connection object
+    DSCSqlConnection dbconn_;
+
+    /// The exchange objects are used for transfer of data to/from the database.
+    /// They are pointed-to objects as the contents may change in "const" calls,
+    /// while the rest of this object does not.  (At alternative would be to
+    /// declare them as "mutable".)
+    boost::scoped_ptr<DSCSqlLease4Exchange> exchange4_; ///< Exchange object
+    boost::scoped_ptr<DSCSqlLease6Exchange> exchange6_; ///< Exchange object
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DSCSQL_LEASE_MGR_H

+ 434 - 0
src/lib/dhcpsrv/tests/dscsql_lease_mgr_unittest.cc

@@ -0,0 +1,434 @@
+// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/dscsql_connection.h>
+#include <dhcpsrv/dscsql_lease_mgr.h>
+#include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
+#include <dhcpsrv/testutils/dscsql_schema.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 {
+
+
+/// @brief Test fixture class for testing DataStax Cassandra Lease Manager
+///
+/// Opens the database prior to each test and closes it afterwards.
+/// All pending transactions are deleted prior to closure.
+
+class DSCSqlLeaseMgrTest : public GenericLeaseMgrTest {
+public:
+    /// @brief Constructor
+    ///
+    /// Deletes everything from the database and opens it.
+    DSCSqlLeaseMgrTest() {
+
+        // Ensure schema is the correct one.
+        destroyDSCSQLSchema();
+        createDSCSQLSchema();
+
+        // Connect to the database
+        try {
+            LeaseMgrFactory::create(validDSCSQLConnectionString());
+        } catch (...) {
+            std::cerr << "*** ERROR: unable to open database. The test\n"
+                         "*** environment is broken and must be fixed before\n"
+                         "*** the DSC SQL 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 ~DSCSqlLeaseMgrTest() {
+        lmptr_->rollback();
+        LeaseMgrFactory::destroy();
+        destroyDSCSQLSchema();
+    }
+
+    /// @brief Reopen the database
+    ///
+    /// Closes the database and re-open it.  Anything committed should be
+    /// visible.
+    ///
+    /// Parameter is ignored for DSC SQL backend as the v4 and v6 leases share
+    /// the same database.
+    void reopen(Universe) {
+        LeaseMgrFactory::destroy();
+        LeaseMgrFactory::create(validConnectionString());
+        lmptr_ = &(LeaseMgrFactory::instance());
+    }
+
+};
+
+/// @brief Check that database can be opened
+///
+/// This test checks if the DSCSqlLeaseMgr can be instantiated.  This happens
+/// only if the database can be opened.  Note that this is not part of the
+/// DSCSqlLeaseMgr test fixure set.  This test checks that the database can be
+/// opened: the fixtures assume that and check basic operations.
+
+TEST(DSCSqlOpenTest, OpenDatabase) {
+
+    // Schema needs to be created for the test to work.
+    destroyDSCSQLSchema();
+    createDSCSQLSchema();
+
+    // Check that lease manager open the database opens correctly and tidy up.
+    //  If it fails, print the error message.
+    try {
+        LeaseMgrFactory::create(validDSCSQLConnectionString());
+        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 DSC SQL 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(
+        DSCSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
+        DbOpenError);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
+        DbOpenError);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+        DbOpenError);
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)),
+        DbOpenError);
+
+    // Check for missing parameters
+    EXPECT_THROW(LeaseMgrFactory::create(connectionString(
+        DSCSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+        NoDatabaseName);
+
+    // Tidy up after the test
+    destroyDSCSQLSchema();
+}
+
+/// @brief Check the getType() method
+///
+/// getType() returns a string giving the type of the backend, which should
+/// always be "cassandra".
+TEST_F(DSCSqlLeaseMgrTest, getType) {
+    EXPECT_EQ(std::string("cassandra"), lmptr_->getType());
+}
+
+/// @brief Check conversion functions
+///
+/// The server works using cltt and valid_filetime.  In the database, the
+/// information is stored as expire_time and valid-lifetime, which are
+/// related by
+///
+/// expire_time = cltt + valid_lifetime
+///
+/// This test checks that the conversion is correct.
+TEST_F(DSCSqlLeaseMgrTest, checkTimeConversion) {
+    const time_t cltt = time(NULL);
+
+    time_t converted_cltt = 0;
+    EXPECT_EQ(cltt, converted_cltt);
+}
+
+
+/// @brief Check getName() returns correct database name
+TEST_F(DSCSqlLeaseMgrTest, getName) {
+    EXPECT_EQ(std::string("keatest"), lmptr_->getName());
+}
+
+/// @brief Check that getVersion() returns the expected version
+TEST_F(DSCSqlLeaseMgrTest, checkVersion) {
+    // Check version
+    pair<uint32_t, uint32_t> version;
+    ASSERT_NO_THROW(version = lmptr_->getVersion());
+    EXPECT_EQ(CURRENT_VERSION_VERSION, version.first);
+    EXPECT_EQ(CURRENT_VERSION_MINOR, version.second);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// LEASE4 /////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4 (by address) and deleteLease (with an
+/// IPv4 address) works.
+TEST_F(DSCSqlLeaseMgrTest, basicLease4) {
+    testBasicLease4();
+}
+
+/// @brief Check that Lease4 code safely handles invalid dates.
+TEST_F(DSCSqlLeaseMgrTest, maxDate4) {
+    testMaxDate4();
+}
+
+/// @brief Lease4 update tests
+///
+/// Checks that we are able to update a lease in the database.
+TEST_F(DSCSqlLeaseMgrTest, updateLease4) {
+    testUpdateLease4();
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddr1) {
+    testGetLease4HWAddr1();
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddr2) {
+    testGetLease4HWAddr2();
+}
+
+// @brief Get lease4 by hardware address (2)
+//
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddrSize) {
+    testGetLease4HWAddrSize();
+}
+
+/// @brief Check GetLease4 methods - access by Hardware Address & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of hardware address and subnet ID
+TEST_F(DSCSqlLeaseMgrTest, getLease4HwaddrSubnetId) {
+    testGetLease4HWAddrSubnetId();
+}
+
+// @brief Get lease4 by hardware address and subnet ID (2)
+//
+// Check that the system can cope with getting a hardware address of
+// any size.
+TEST_F(DSCSqlLeaseMgrTest, getLease4HWAddrSubnetIdSize) {
+    testGetLease4HWAddrSubnetIdSize();
+}
+
+// This test was derived from memfile.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientId) {
+    testGetLease4ClientId();
+}
+
+/// @brief Check GetLease4 methods - access by Client ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// the Client ID.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientId2) {
+    testGetLease4ClientId2();
+}
+
+// @brief Get Lease4 by client ID (2)
+//
+// Check that the system can cope with a client ID of any size.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientIdSize) {
+    testGetLease4ClientIdSize();
+}
+
+/// @brief Check GetLease4 methods - access by Client ID & Subnet ID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of client and subnet IDs.
+TEST_F(DSCSqlLeaseMgrTest, getLease4ClientIdSubnetId) {
+    testGetLease4ClientIdSubnetId();
+}
+
+/// @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(DSCSqlLeaseMgrTest, lease4NullClientId) {
+    testLease4NullClientId();
+}
+
+/// @brief Verify that too long hostname for Lease4 is not accepted.
+///
+/// Checks that the it is not possible to create a lease when the hostname
+/// length exceeds 255 characters.
+TEST_F(DSCSqlLeaseMgrTest, lease4InvalidHostname) {
+    testLease4InvalidHostname();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// LEASE6 /////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// Test checks whether simple add, get and delete operations are possible
+// on Lease6
+TEST_F(DSCSqlLeaseMgrTest, testAddGetDelete6) {
+    testAddGetDelete6(false);
+}
+
+/// @brief Basic Lease6 Checks
+///
+/// Checks that the addLease, getLease6 (by address) and deleteLease (with an
+/// IPv6 address) works.
+TEST_F(DSCSqlLeaseMgrTest, basicLease6) {
+    testBasicLease6();
+}
+
+/// @brief Check that Lease6 code safely handles invalid dates.
+TEST_F(DSCSqlLeaseMgrTest, maxDate6) {
+    testMaxDate6();
+}
+
+/// @brief Verify that too long hostname for Lease6 is not accepted.
+///
+/// Checks that the it is not possible to create a lease when the hostname
+/// length exceeds 255 characters.
+TEST_F(DSCSqlLeaseMgrTest, lease6InvalidHostname) {
+    testLease6InvalidHostname();
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DUID and IAID.
+TEST_F(DSCSqlLeaseMgrTest, getLeases6DuidIaid) {
+    testGetLeases6DuidIaid();
+}
+
+// Check that the system can cope with a DUID of allowed size.
+TEST_F(DSCSqlLeaseMgrTest, getLeases6DuidSize) {
+    testGetLeases6DuidSize();
+}
+
+/// @brief Check that getLease6 methods discriminate by lease type.
+///
+/// Adds six leases, two per lease type all with the same duid and iad but
+/// with alternating subnet_ids.
+/// It then verifies that all of getLeases6() method variants correctly
+/// discriminate between the leases based on lease type alone.
+TEST_F(DSCSqlLeaseMgrTest, lease6LeaseTypeCheck) {
+    testLease6LeaseTypeCheck();
+}
+
+/// @brief Check GetLease6 methods - access by DUID/IAID/SubnetID
+///
+/// Adds leases to the database and checks that they can be accessed via
+/// a combination of DIUID and IAID.
+TEST_F(DSCSqlLeaseMgrTest, getLease6DuidIaidSubnetId) {
+    testGetLease6DuidIaidSubnetId();
+}
+
+// Test checks that getLease6() works with different DUID sizes
+TEST_F(DSCSqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
+    testGetLease6DuidIaidSubnetIdSize();
+}
+
+/// @brief Lease6 update tests
+///
+/// Checks that we are able to update a lease in the database.
+TEST_F(DSCSqlLeaseMgrTest, updateLease6) {
+    testUpdateLease6();
+}
+
+/// @brief DHCPv4 Lease recreation tests
+///
+/// Checks that the lease can be created, deleted and recreated with
+/// different parameters. It also checks that the re-created lease is
+/// correctly stored in the lease database.
+TEST_F(DSCSqlLeaseMgrTest, testRecreateLease4) {
+    testRecreateLease4();
+}
+
+/// @brief DHCPv6 Lease recreation tests
+///
+/// Checks that the lease can be created, deleted and recreated with
+/// different parameters. It also checks that the re-created lease is
+/// correctly stored in the lease database.
+TEST_F(DSCSqlLeaseMgrTest, testRecreateLease6) {
+    testRecreateLease6();
+}
+
+/// @brief Checks that null DUID is not allowed.
+TEST_F(DSCSqlLeaseMgrTest, nullDuid) {
+    testNullDuid();
+}
+
+/// @brief Tests whether memfile can store and retrieve hardware addresses
+TEST_F(DSCSqlLeaseMgrTest, testLease6Mac) {
+    testLease6MAC();
+}
+
+/// @brief Tests whether memfile can store and retrieve hardware addresses
+TEST_F(DSCSqlLeaseMgrTest, testLease6HWTypeAndSource) {
+    testLease6HWTypeAndSource();
+}
+
+/// @brief Check that the expired DHCPv4 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(DSCSqlLeaseMgrTest, getExpiredLeases4) {
+    testGetExpiredLeases4();
+}
+
+/// @brief Check that the expired DHCPv6 leases can be retrieved.
+///
+/// This test adds a number of leases to the lease database and marks
+/// some of them as expired. Then it queries for expired leases and checks
+/// whether only expired leases are returned, and that they are returned in
+/// the order from most to least expired. It also checks that the lease
+/// which is marked as 'reclaimed' is not returned.
+TEST_F(DSCSqlLeaseMgrTest, getExpiredLeases6) {
+    testGetExpiredLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv6 leases are removed.
+TEST_F(DSCSqlLeaseMgrTest, deleteExpiredReclaimedLeases6) {
+    testDeleteExpiredReclaimedLeases6();
+}
+
+/// @brief Check that expired reclaimed DHCPv4 leases are removed.
+TEST_F(DSCSqlLeaseMgrTest, deleteExpiredReclaimedLeases4) {
+    testDeleteExpiredReclaimedLeases4();
+}
+
+}; // Of anonymous namespace

+ 62 - 0
src/lib/dhcpsrv/testutils/dscsql_schema.cc

@@ -0,0 +1,62 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <string>
+#include <cassandra.h>
+#include <dhcpsrv/dscsql_connection.h>
+#include <dhcpsrv/testutils/dscsql_schema.h>
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <sstream>
+#include <stdlib.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+const char* DSCSQL_VALID_TYPE = "type=cassandra";
+
+string
+validDSCSQLConnectionString() {
+    return (connectionString(DSCSQL_VALID_TYPE, VALID_NAME, VALID_HOST,
+                             VALID_USER, VALID_PASSWORD));
+}
+
+void destroyDSCSQLSchema(bool show_err) {
+    runDSCSQLScript(DATABASE_SCRIPTS_DIR, "dscsql/dhcpdb_drop.cql", show_err);
+}
+
+void createDSCSQLSchema(bool show_err) {
+    runDSCSQLScript(DATABASE_SCRIPTS_DIR, "dscsql/dhcpdb_create.cql",
+                   show_err);
+}
+
+void runDSCSQLScript(const std::string& path, const std::string& script_name,
+                    bool show_err) {
+    std::ostringstream cmd;
+    cmd << "cqlsh -u keatest -p keatest -k keatest -f";
+    if (!show_err) {
+        cmd << " 2>/dev/null ";
+    }
+
+    if (!path.empty()) {
+        cmd << " < " << path << "/";
+    }
+
+    cmd << script_name;
+
+    int retval = ::system(cmd.str().c_str());
+    ASSERT_EQ(0, retval) << "runDSCSQLSchema failed:" << cmd.str();
+}
+
+
+};
+};
+};

+ 68 - 0
src/lib/dhcpsrv/testutils/dscsql_schema.h

@@ -0,0 +1,68 @@
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TEST_DSCSQL_SCHEMA_H
+#define TEST_DSCSQL_SCHEMA_H
+
+#include <config.h>
+#include <dhcpsrv/testutils/schema.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+extern const char* DSCSQL_VALID_TYPE;
+
+/// Return valid connection string
+///
+/// @return valid DSCSQL connection string.
+std::string validDSCSQLConnectionString();
+
+/// @brief Clear everything from the database
+///
+/// Submits the current schema drop script:
+///
+///  <TEST_ADMIN_SCRIPTS_DIR>/dscsql/dhcpdb_drop.cql
+///
+/// to the unit test DSCSQL database. If the script fails, the invoking test
+/// will fail. The output of stderr is suppressed unless the parameter,
+/// show_err is true.
+///
+/// @param show_err flag which governs whether or not stderr is suppressed.
+void destroyDSCSQLSchema(bool show_err = false);
+
+/// @brief Create the DSCSQL Schema
+///
+/// Submits the current schema creation script:
+///
+///  <TEST_ADMIN_SCRIPTS_DIR>/dscsql/dhcpdb_create.cql
+///
+/// to the unit test DSCSQL database. If the script fails, the invoking test
+/// will fail. The output of stderr is suppressed unless the parameter,
+/// show_err is true.
+///
+/// @param show_err flag which governs whether or not stderr is suppressed.
+void createDSCSQLSchema(bool show_err = false);
+
+/// @brief Run a DSCSQL SQL script against the DSCSQL unit test database
+///
+/// Submits the given SQL script to DSCSQL via cqlsh CLI. The output of
+/// stderr is suppressed unless the parameter, show_err is true.  The is done
+/// to suppress warnings that might otherwise make test output needlessly
+/// noisy.  A gtest assertion occurs if the script fails to execute.
+///
+/// @param path - path (if not blank) of the script to execute
+/// @param script_name - file name of the path to execute
+/// @param show_err flag which governs whether or not stderr is suppressed.
+void runDSCSQLScript(const std::string& path, const std::string& script_name,
+                    bool show_err);
+
+};
+};
+};
+
+#endif

+ 0 - 0
src/share/database/scripts/dscsql/.gitignore


+ 6 - 0
src/share/database/scripts/dscsql/Makefile.am

@@ -0,0 +1,6 @@
+SUBDIRS = .
+
+sqlscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts/dscsql
+sqlscripts_DATA = dhcpdb_create.cql
+
+EXTRA_DIST = ${sqlscripts_DATA}

+ 213 - 0
src/share/database/scripts/dscsql/dhcpdb_create.cql

@@ -0,0 +1,213 @@
+-- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+
+-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+
+--           http://www.apache.org/licenses/LICENSE-2.0
+
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+-- This is the Kea schema specification for DataStax Cassandra SQL.
+
+-- The schema is reasonably portable (with the exception of the engine
+-- specification, which is Datastax Cassandra SQL-specific).  Minor changes might be needed for
+-- other databases.
+
+-- To create the schema, either type the command:
+
+-- cqlsh -u <user> -p <password> -k <database> -f dhcpdb_create.cql
+
+-- ... at the command prompt, or log in to the DSC SQL database and at the "cqlsh>"
+-- prompt, issue the command:
+
+-- SOURCE dhcpdb_create.cql
+
+-- This script is also called from kea-admin, see kea-admin init dscsql
+
+-- Over time, Kea database schema will evolve. Each version is marked with
+-- major.minor version. This file is organized sequentially, i.e. database
+-- is initialized to 1.0, then upgraded to 2.0 etc. This may be somewhat
+-- sub-optimal, but it ensues consistency with upgrade scripts. (It is much
+-- easier to maintain init and upgrade scripts if they look the same).
+-- Since initialization is done only once, it's performance is not an issue.
+
+-- This line starts database initialization to 1.0.
+
+-- Holds the IPv4 leases.
+CREATE TABLE lease4 (
+    address int,
+    hwaddr blob,
+    client_id blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    state int,
+    PRIMARY KEY (address)
+);
+
+-- Create search indexes for lease4 table
+CREATE INDEX lease4index1 ON lease4 (client_id);
+CREATE INDEX lease4index2 ON lease4 (subnet_id);
+CREATE INDEX lease4index3 ON lease4 (hwaddr);
+CREATE INDEX lease4index4 ON lease4 (expire);
+CREATE INDEX lease4index5 ON lease4 (state);
+
+-- 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,
+    duid blob,
+    valid_lifetime bigint,
+    expire bigint,
+    subnet_id int,
+    pref_lifetime bigint,
+    lease_type int,
+    iaid int,
+    prefix_len int,
+    fqdn_fwd boolean,
+    fqdn_rev boolean,
+    hostname varchar,
+    hwaddr blob,
+    hwtype int,
+    hwaddr_source int,
+    state int,
+    PRIMARY KEY (address)
+);
+
+-- Create search indexes for lease6 table
+CREATE INDEX lease6index1 ON lease6 (lease_type);
+CREATE INDEX lease6index2 ON lease6 (duid);
+CREATE INDEX lease6index3 ON lease6 (iaid);
+CREATE INDEX lease6index4 ON lease6 (subnet_id);
+CREATE INDEX lease6index5 ON lease6 (expire);
+CREATE INDEX lease6index6 ON lease6 (state);
+
+-- ... 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.
+-- Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/
+-- lease_mgr.h)
+CREATE TABLE lease6_types (
+    lease_type int,                             -- Lease type code.
+    name varchar,                               -- Name of the lease type
+    PRIMARY KEY (lease_type)
+);
+--START TRANSACTION;
+INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA');   -- Non-temporary v6 addresses
+INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA');   -- Temporary v6 addresses
+INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD');   -- Prefix delegations
+--COMMIT;
+
+-- Kea keeps track of the hardware/MAC address source, i.e. how the address
+-- was obtained. Depending on the technique and your network topology, it may
+-- be more or less trustworthy. 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. For details,
+-- see constants defined in src/lib/dhcp/dhcp/pkt.h for detailed explanation.
+CREATE TABLE lease_hwaddr_source (
+    hwaddr_source int,
+    name varchar,
+    PRIMARY KEY (hwaddr_source)
+);
+
+-- Hardware address obtained from raw sockets
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (1, 'HWADDR_SOURCE_RAW');
+
+-- Hardware address converted from IPv6 link-local address with EUI-64
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (2, 'HWADDR_SOURCE_IPV6_LINK_LOCAL');
+
+-- Hardware address extracted from client-id (duid)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (4, 'HWADDR_SOURCE_DUID');
+
+-- Hardware address extracted from client address relay option (RFC6939)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (8, 'HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION');
+
+-- Hardware address extracted from remote-id option (RFC4649)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (16, 'HWADDR_SOURCE_REMOTE_ID');
+
+-- Hardware address extracted from subscriber-id option (RFC4580)
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE_SUBSCRIBER_ID');
+
+-- Hardware address extracted from docsis options
+INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS');
+
+-- -----------------------------------------------------
+-- Table `dhcp4_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp4_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp4_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp4_options table
+CREATE INDEX dhcp4_optionsindex1 ON dhcp4_options (host_id);
+
+-- -----------------------------------------------------
+-- Table `dhcp6_options`
+-- -----------------------------------------------------
+CREATE TABLE dhcp6_options (
+    option_id int,
+    code int,
+    value blob,
+    formatted_value varchar,
+    space varchar,
+    persistent int,
+    dhcp_client_class varchar,
+    dhcp6_subnet_id int,
+    host_id int,
+    PRIMARY KEY (option_id)
+);
+
+-- Create search indexes for dhcp6_options table
+CREATE INDEX dhcp6_optionsindex1 ON dhcp6_options (host_id);
+
+-- Create table holding mapping of the lease states to their names.
+-- This is not used in queries from the DHCP server but rather in
+-- direct queries from the lease database management tools.
+CREATE TABLE lease_state (
+    state int,
+    name varchar,
+    PRIMARY KEY (state)
+);
+
+-- Insert currently defined state names.
+INSERT INTO lease_state (state, name) VALUES (0, 'default');
+INSERT INTO lease_state (state, name) VALUES (1, 'declined');
+INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
+
+-- Finally, the version of the schema.  We start at 1.0 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,
+    minor int,
+    PRIMARY KEY (version)
+);
+--START TRANSACTION;
+INSERT INTO schema_version (version, minor) VALUES (1, 0);
+--COMMIT;

+ 27 - 0
src/share/database/scripts/dscsql/dhcpdb_drop.cql

@@ -0,0 +1,27 @@
+-- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+
+-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+
+--           http://www.apache.org/licenses/LICENSE-2.0
+
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+
+DROP TABLE IF EXISTS lease4;
+DROP TABLE IF EXISTS lease6;
+DROP TABLE IF EXISTS lease6_types;
+DROP TABLE IF EXISTS lease_hwaddr_source;
+DROP TABLE IF EXISTS schema_version;
+DROP TABLE IF EXISTS ipv6_reservations;
+DROP TABLE IF EXISTS hosts;
+DROP TABLE IF EXISTS dhcp4_options;
+DROP TABLE IF EXISTS dhcp6_options;
+DROP TABLE IF EXISTS host_identifier_type;
+DROP TABLE IF EXISTS lease_state;