From: Razvan Becheriu Date: Thu, 19 May 2016 14:10:36 +0000 (+0300) Subject: added support for datastax cassandra X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=24115f3f4bb6de35a759badc6375419fba328504;p=thirdparty%2Fkea.git added support for datastax cassandra --- diff --git a/src/bin/admin/tests/data/dscsql.lease4_dump_test.reference.csv b/src/bin/admin/tests/data/dscsql.lease4_dump_test.reference.csv new file mode 100644 index 0000000000..a2917b7d9a --- /dev/null +++ b/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 diff --git a/src/bin/admin/tests/data/dscsql.lease6_dump_test.reference.csv b/src/bin/admin/tests/data/dscsql.lease6_dump_test.reference.csv new file mode 100644 index 0000000000..31a6c3f42a --- /dev/null +++ b/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 diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.cql b/src/bin/admin/tests/dhcpdb_create_1.0.cql new file mode 100644 index 0000000000..303d437c05 --- /dev/null +++ b/src/bin/admin/tests/dhcpdb_create_1.0.cql @@ -0,0 +1,213 @@ +-- Copyright (C) 2015 - 2016 Deutsche Telekom AG. + +-- Author: Razvan Becheriu + +-- 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 -p -k -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; diff --git a/src/bin/admin/tests/dscsql_tests.sh.in b/src/bin/admin/tests/dscsql_tests.sh.in new file mode 100644 index 0000000000..d7cc195d80 --- /dev/null +++ b/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 diff --git a/src/lib/dhcpsrv/dscsql_connection.cc b/src/lib/dhcpsrv/dscsql_connection.cc new file mode 100644 index 0000000000..8c260650c0 --- /dev/null +++ b/src/lib/dhcpsrv/dscsql_connection.cc @@ -0,0 +1,243 @@ +// Copyright (C) 2015 - 2016 Deutsche Telekom AG. +// +// Author: Razvan Becheriu +// +// 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 +#include + +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(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 +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(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 diff --git a/src/lib/dhcpsrv/dscsql_connection.h b/src/lib/dhcpsrv/dscsql_connection.h new file mode 100644 index 0000000000..a2fd4d13f3 --- /dev/null +++ b/src/lib/dhcpsrv/dscsql_connection.h @@ -0,0 +1,140 @@ +// Copyright (C) 2015 - 2016 Deutsche Telekom AG. +// +// Author: Razvan Becheriu +// +// 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 +#include +#include +#include +#include + +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 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 statements_; ///< Prepared statements + DSCSqlTaggedStatement *tagged_statements_; +}; + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif // DSCSQL_CONNECTION_H diff --git a/src/lib/dhcpsrv/dscsql_lease_mgr.cc b/src/lib/dhcpsrv/dscsql_lease_mgr.cc new file mode 100644 index 0000000000..6e3bbe619c --- /dev/null +++ b/src/lib/dhcpsrv/dscsql_lease_mgr.cc @@ -0,0 +1,1910 @@ +// Copyright (C) 2015 - 2016 Deutsche Telekom AG. +// +// Author: Razvan Becheriu +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace isc; +using namespace isc::dhcp; +using namespace std; + +namespace isc { +namespace dhcp { + +static const size_t HOSTNAME_MAX_LEN = 255; +static const size_t ADDRESS6_TEXT_MAX_LEN = 39; + +static CassError DSCSqlBindNone(CassStatement* statement, size_t index, void*) { + return cass_statement_bind_null(statement, index); +} + +static CassError DSCSqlBindBool(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_bool(statement, index, *(static_cast(value))); +} + +static CassError DSCSqlBindInt32(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_int32(statement, index, *(static_cast(value))); +} + +static CassError DSCSqlBindInt64(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_int64(statement, index, *(static_cast(value))); +} + +static CassError DSCSqlBindTimestamp(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_int64(statement, index, *(static_cast(value))); +} + +static CassError DSCSqlBindString(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_string(statement, index, (static_cast(value))); +} + +static CassError DSCSqlBindBytes(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_bytes(statement, index, &(*(static_cast*>(value)))[0], + static_cast*>(value)->size()); +} + +static CassError (*DSCSqlBindFunctions[])(CassStatement* statement, size_t index, void* value) = { + DSCSqlBindNone, + DSCSqlBindBool, + DSCSqlBindInt32, + DSCSqlBindInt64, + DSCSqlBindTimestamp, + DSCSqlBindString, + DSCSqlBindBytes +}; + +struct ColumnInfo { + ColumnInfo () : column_(NULL), type_(DSCSQL_DATA_TYPE_NONE) {} + ColumnInfo (const char *column, DSCSqlDataType type) : column_(column), type_(type) {} + const char* column_; + DSCSqlDataType type_; +}; + +/// @brief Catalog of all the SQL statements currently supported. Note +/// that the order columns appear in statement body must match the order they +/// that the occur in the table. This does not apply to the where clause. + +static const char* delete_lease4_params[] = { + static_cast("address"), + NULL }; +static const char* delete_expired_lease4_params[] = { + static_cast("state"), + NULL }; +static const char* delete_lease6_params[] = { + static_cast("address"), + NULL }; +static const char* delete_expired_lease6_params[] = { + static_cast("state"), + NULL }; +static const char* get_lease4_addr_params[] = { + static_cast("address"), + NULL }; +static const char* get_lease4_clientid_params[] = { + static_cast("client_id"), + NULL }; +static const char* get_lease4_clientid_subid_params[] = { + static_cast("client_id"), + static_cast("subnet_id"), + NULL }; +static const char* get_lease4_hwaddr_params[] = { + static_cast("hwaddr"), + NULL }; +static const char* get_lease4_hwaddr_subid_params[] = { + static_cast("hwaddr"), + static_cast("subnet_id"), + NULL }; +static const char* get_lease4_expired_params[] = { + static_cast("state"), + static_cast("limit"), + NULL }; +static const char* get_lease6_addr_params[] = { + static_cast("address"), + static_cast("lease_type"), + NULL }; +static const char* get_lease6_duid_iaid_params[] = { + static_cast("duid"), + static_cast("iaid"), + static_cast("lease_type"), + NULL }; +static const char* get_lease6_duid_iaid_subid_params[] = { + static_cast("duid"), + static_cast("iaid"), + static_cast("subnet_id"), + static_cast("lease_type"), + NULL }; +static const char* get_lease6_expired_params[] = { + static_cast("state"), + static_cast("limit"), + NULL }; +static const char* get_version_params[] = { + static_cast("version"), + NULL }; +static const char* insert_lease4_params[] = { + static_cast("address"), + static_cast("hwaddr"), + static_cast("client_id"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("state"), + NULL }; +static const char* insert_lease6_params[] = { + static_cast("address"), + static_cast("duid"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("pref_lifetime"), + static_cast("lease_type"), + static_cast("iaid"), + static_cast("prefix_len"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("hwaddr"), + static_cast("hwtype"), + static_cast("hwaddr_source"), + static_cast("state"), + NULL }; +static const char* update_lease4_params[] = { + static_cast("hwaddr"), + static_cast("client_id"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("state"), + static_cast("address"), +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + static_cast("expire"), +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + NULL }; +static const char* update_lease6_params[] = { + static_cast("duid"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("pref_lifetime"), + static_cast("lease_type"), + static_cast("iaid"), + static_cast("prefix_len"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("hwaddr"), + static_cast("hwtype"), + static_cast("hwaddr_source"), + static_cast("state"), + static_cast("address"), +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + static_cast("expire"), +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + NULL }; + +DSCSqlTaggedStatement DSCSqlLeaseMgr::tagged_statements_[] = { + // DELETE_LEASE4 + { delete_lease4_params, + "delete_lease4", + "DELETE FROM lease4 WHERE address = ?" }, + + // DELETE_LEASE4_STATE_EXPIRED + { delete_expired_lease4_params, + "delete_lease4_expired", + //"WHERE state = ? AND expire < ? ALLOW FILTERING" + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE state = ? ALLOW FILTERING" }, + + // DELETE_LEASE6 + { delete_lease6_params, + "delete_lease6", + "DELETE FROM lease6 WHERE address = ?" }, + + // DELETE_LEASE6_STATE_EXPIRED + { delete_expired_lease6_params, + "delete_lease6_expired", + //"WHERE state = ? AND expire < ? ALLOW FILTERING" + "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 " + "WHERE state = ? ALLOW FILTERING" }, + + // GET_LEASE4_ADDR + { get_lease4_addr_params, + "get_lease4_addr", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE address = ?" }, + + // GET_LEASE4_CLIENTID + { get_lease4_clientid_params, + "get_lease4_clientid", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE client_id = ?" }, + + // GET_LEASE4_CLIENTID_SUBID + { get_lease4_clientid_subid_params, + "get_lease4_clientid_subid", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE client_id = ? AND subnet_id = ? ALLOW FILTERING" }, + + // GET_LEASE4_HWADDR + { get_lease4_hwaddr_params, + "get_lease4_hwaddr", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE hwaddr = ?" }, + + // GET_LEASE4_HWADDR_SUBID + { get_lease4_hwaddr_subid_params, + "get_lease4_hwaddr_subid", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + "WHERE hwaddr = ? AND subnet_id = ? ALLOW FILTERING" }, + + // GET_LEASE4_EXPIRE + { get_lease4_expired_params, + "get_lease4_expired", + "SELECT address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, " + "fqdn_fwd, fqdn_rev, hostname, state " + "FROM lease4 " + //"WHERE state != ? AND expire < ? ORDER BY expire ASC " + "WHERE state = ? " + "LIMIT ? ALLOW FILTERING" }, + + // GET_LEASE6_ADDR + { get_lease6_addr_params, + "get_lease6_addr", + "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 " + "WHERE address = ? AND lease_type = ? ALLOW FILTERING" }, + + // GET_LEASE6_DUID_IAID + { get_lease6_duid_iaid_params, + "get_lease6_duid_iaid", + "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 " + "WHERE duid = ? AND iaid = ? AND lease_type = ? ALLOW FILTERING" }, + + // GET_LEASE6_DUID_IAID_SUBID + { get_lease6_duid_iaid_subid_params, + "get_lease6_duid_iaid_subid", + "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 " + "WHERE duid = ? AND iaid = ? AND subnet_id = ? AND lease_type = ? ALLOW FILTERING" }, + + // GET_LEASE6_EXPIRE + { get_lease6_expired_params, + "get_lease6_expired", + "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 " + //"WHERE state != ? AND expire < ? ORDER BY expire ASC " + "WHERE state = ? " + "LIMIT ? ALLOW FILTERING" }, + + // GET_VERSION + { get_version_params, + "get_version", + "SELECT version, minor FROM schema_version" }, + + // INSERT_LEASE4 + { insert_lease4_params, + "insert_lease4", + "INSERT INTO lease4(address, hwaddr, client_id, " + "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, state) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + "IF NOT EXISTS" +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + }, + + // INSERT_LEASE6 + { insert_lease6_params, + "insert_lease6", + "INSERT INTO lease6(address, duid, valid_lifetime, " + "expire, subnet_id, pref_lifetime, " + "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, hwaddr_source, state) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + "IF NOT EXISTS" +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + }, + + // UPDATE_LEASE4 + { update_lease4_params, + "update_lease4", + "UPDATE lease4 SET hwaddr = ?, " + "client_id = ?, valid_lifetime = ?, expire = ?, " + "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, state = ? " + "WHERE address = ? " +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + "IF expire = ?" +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + }, + + // UPDATE_LEASE6 + { update_lease6_params, + "update_lease6", + "UPDATE lease6 SET duid = ?, " + "valid_lifetime = ?, expire = ?, subnet_id = ?, " + "pref_lifetime = ?, lease_type = ?, iaid = ?, " + "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, " + "hwaddr = ?, hwtype = ?, hwaddr_source = ?, state = ? " + "WHERE address = ? " +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + "IF expire = ?" +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + }, + + // End of list sentinel + { NULL, NULL, NULL } +}; + + +/// @brief Common DSC and Lease Data Methods +/// +/// The DSCSqlLease4Exchange and DSCSqlLease6Exchange classes provide the +/// functionality to set up binding information between variables in the +/// program and data extracted from the database. This class is the common +/// base to both of them, containing some common methods. + +class DSCSqlLeaseExchange { +public: + DSCSqlLeaseExchange() : hwaddr_length_(0), expire_(0), + subnet_id_(0), valid_lifetime_(0), + fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0), state_(0) { + memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_)); + memset(hostname_buffer_, 0, sizeof(hostname_buffer_)); + } + // Time conversion methods. + static void + convertToDatabaseTime(const time_t& cltt, + const uint32_t& valid_lifetime, + uint64_t& expire) { + // Calculate expiry time. Store it in the 64-bit value so as we can detect + // overflows. + int64_t expire_time = static_cast(cltt) + + static_cast(valid_lifetime); + + if (expire_time > DatabaseConnection::MAX_DB_TIME) { + isc_throw(BadValue, "Time value is too large: " << expire_time); + } + + expire = expire_time; + } + + static void + convertFromDatabaseTime(const uint64_t& expire, + const uint32_t& valid_lifetime, + time_t& cltt) { + // Convert to local time + cltt = expire - static_cast(valid_lifetime); + } + + std::vector columns_; ///< Column names and types +protected: + std::vector hwaddr_; ///< Hardware address + uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN]; + ///< Hardware address buffer + unsigned long hwaddr_length_; ///< Hardware address length + uint64_t expire_; ///< Lease expiry time + uint32_t subnet_id_; ///< Subnet identification + uint64_t valid_lifetime_; ///< Lease time + uint32_t fqdn_fwd_; ///< Has forward DNS update been + ///< performed + uint32_t fqdn_rev_; ///< Has reverse DNS update been + ///< performed + char hostname_buffer_[HOSTNAME_MAX_LEN]; + ///< Client hostname + unsigned long hostname_length_; ///< Client hostname length + uint32_t state_; ///< Lease state +}; + + +/// @brief Exchange DSC and Lease4 Data +/// +/// On any DSC SQL operation, arrays of DSC SQL BIND structures must be built to +/// describe the parameters in the prepared statements. Where information is +/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that +/// structure is identical. This class handles the creation of that array. +/// +/// Owing to the DSC SQL API, the process requires some intermediate variables +/// to hold things like data length etc. This object holds those variables. +/// +/// @note There are no unit tests for this class. It is tested indirectly +/// in all DSCSqlLeaseMgr::xxx4() calls where it is used. + +class DSCSqlLease4Exchange : public DSCSqlLeaseExchange { +public: + /// @brief Constructor + /// + /// The initialization of the variables here is only to satisfy cppcheck - + /// all variables are initialized/set in the methods before they are used. + DSCSqlLease4Exchange() : addr4_(0), client_id_length_(0), + client_id_null_(false) { + memset(client_id_buffer_, 0, sizeof(client_id_buffer_)); + + // Set the column names (for error messages) + uint32_t size; + uint32_t offset = 0; + size = 12; + columns_.resize(size); + columns_[offset++] = ColumnInfo("address", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("hwaddr", DSCSQL_DATA_TYPE_BYTES); + columns_[offset++] = ColumnInfo("client_id", DSCSQL_DATA_TYPE_BYTES); + columns_[offset++] = ColumnInfo("valid_lifetime", DSCSQL_DATA_TYPE_INT64); + columns_[offset++] = ColumnInfo("expire", DSCSQL_DATA_TYPE_TIMESTAMP); + columns_[offset++] = ColumnInfo("subnet_id", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("fqdn_fwd", DSCSQL_DATA_TYPE_BOOL); + columns_[offset++] = ColumnInfo("fqdn_rev", DSCSQL_DATA_TYPE_BOOL); + columns_[offset++] = ColumnInfo("hostname", DSCSQL_DATA_TYPE_STRING); + columns_[offset++] = ColumnInfo("state", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("limit", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("version", DSCSQL_DATA_TYPE_NONE); + } + + /// @brief Create DSCSQL_BIND objects for Lease4 Pointer + /// + /// Fills in the DSCSQL_BIND array for sending data in the Lease4 object to + /// the database. + void createBindForSend(const Lease4Ptr& lease, DSCSqlBindArray& bind_array) { + if (!lease) { + isc_throw(BadValue, "createBindForSend:: Lease4 object is NULL"); + } + // Store lease object to ensure it remains valid. + lease_ = lease; + // Set up the structures for the various components of the lease4 + // structure. + + try { + // address: uint32_t + // The address in the Lease structure is an IOAddress object. Convert + // this to an integer for storage. + addr4_ = static_cast(lease_->addr_); + bind_array.add(&addr4_); + + hwaddr_ = lease_->hwaddr_->hwaddr_; + hwaddr_length_ = hwaddr_.size(); + bind_array.add(&hwaddr_); + + // client_id: varbinary(128) + if (lease_->client_id_) { + client_id_ = lease_->client_id_->getClientId(); + } else { + client_id_.clear(); + } + client_id_length_ = client_id_.size(); + bind_array.add(&client_id_); + + // valid lifetime: unsigned int + valid_lifetime_ = lease_->valid_lft_; + bind_array.add(&valid_lifetime_); + // expire: timestamp + // The lease structure holds the client last transmission time (cltt_) + // For convenience for external tools, this is converted to lease + // expiry time (expire). The relationship is given by: + // + // expire = cltt_ + valid_lft_ + DSCSqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_); + bind_array.add(&expire_); + + // subnet_id: unsigned int + // Can use lease_->subnet_id_ directly as it is of type uint32_t. + subnet_id_ = lease_->subnet_id_; + bind_array.add(&subnet_id_); + + // fqdn_fwd: boolean + fqdn_fwd_ = lease_->fqdn_fwd_; + bind_array.add(&fqdn_fwd_); + + // fqdn_rev: boolean + fqdn_rev_ = lease_->fqdn_rev_; + bind_array.add(&fqdn_rev_); + + // hostname: varchar(255) + hostname_length_ = lease_->hostname_.length(); + if (hostname_length_ >= sizeof(hostname_buffer_)) { + isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str()); + } + if (hostname_length_) { + memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_); + } + hostname_buffer_[hostname_length_] = '\0'; + bind_array.add(hostname_buffer_); + + // state: uint32_t + state_ = lease_->state_; + bind_array.add(&state_); + + } catch (const std::exception& ex) { + isc_throw(DbOperationError, + "Could not create bind array from Lease4: " + << lease_->addr_.toText() << ", reason: " << ex.what()); + } + } + + /// @brief Create BIND array to receive data + /// + /// Creates a DSCSQL_BIND array to receive Lease4 data from the database. + Lease4Ptr createBindForReceive(const CassRow* row) { + try { + const CassValue* value; + unsigned char* buffer = NULL; + const char* text_buffer = NULL; + + // address: uint32_t + value = cass_row_get_column_by_name(row, columns_[0].column_); + cass_value_get_int32(value, reinterpret_cast(&addr4_)); + + // hwaddr: varbinary(20) + value = cass_row_get_column_by_name(row, columns_[1].column_); + cass_value_get_bytes(value, const_cast(&buffer), &hwaddr_length_); + hwaddr_.assign(buffer, buffer + hwaddr_length_); + + // client_id: varbinary(128) + value = cass_row_get_column_by_name(row, columns_[2].column_); + cass_value_get_string(value, &text_buffer, &client_id_length_); + client_id_.assign(text_buffer, text_buffer + client_id_length_); + + // lease_time: unsigned int + value = cass_row_get_column_by_name(row, columns_[3].column_); + cass_value_get_int64(value, reinterpret_cast(&valid_lifetime_)); + + // expire: timestamp + value = cass_row_get_column_by_name(row, columns_[4].column_); + cass_value_get_int64(value, reinterpret_cast(&expire_)); + + // subnet_id: unsigned int + value = cass_row_get_column_by_name(row, columns_[5].column_); + cass_value_get_int32(value, reinterpret_cast(&subnet_id_)); + + // fqdn_fwd: boolean + value = cass_row_get_column_by_name(row, columns_[6].column_); + cass_value_get_bool(value, reinterpret_cast(&fqdn_fwd_)); + + // fqdn_rev: boolean + value = cass_row_get_column_by_name(row, columns_[7].column_); + cass_value_get_bool(value, reinterpret_cast(&fqdn_rev_)); + + // hostname: varchar(255) + value = cass_row_get_column_by_name(row, columns_[8].column_); + cass_value_get_string(value, &text_buffer, &hostname_length_); + if (hostname_length_ >= sizeof(hostname_buffer_)) { + isc_throw(BadValue, "hostname value is too large: " << text_buffer); + } + if (hostname_length_) { + memcpy(hostname_buffer_, text_buffer, hostname_length_); + } + hostname_buffer_[hostname_length_] = '\0'; + + // state: uint32_t + value = cass_row_get_column_by_name(row, columns_[9].column_); + cass_value_get_int32(value, reinterpret_cast(&state_)); + + time_t cltt = 0; + DSCSqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt); + + // Recreate the hardware address. + HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER)); + + std::string hostname(hostname_buffer_, + hostname_buffer_ + hostname_length_); + + Lease4Ptr result(new Lease4(addr4_, hwaddr, + client_id_buffer_, client_id_length_, + valid_lifetime_, 0, 0, cltt, + subnet_id_, fqdn_fwd_, fqdn_rev_, + hostname)); + + result->state_ = state_; + + return (result); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, + "Could not convert data to Lease4, reason: " + << ex.what()); + } + return (Lease4Ptr()); + } + +private: + + // Note: All array lengths are equal to the corresponding variable in the + // schema. + // Note: Arrays are declared fixed length for speed of creation + Lease4Ptr lease_; ///< Pointer to lease object + uint32_t addr4_; ///< IPv4 address + std::vector client_id_; ///< Client identification + uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN]; + ///< Client ID buffer + unsigned long client_id_length_; ///< Client ID address length + bool client_id_null_; ///< Is Client ID null? +}; + + + +/// @brief Exchange DSC and Lease6 Data +/// +/// On any DSC SQL operation, arrays of DSC SQL BIND structures must be built to +/// describe the parameters in the prepared statements. Where information is +/// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that +/// structure is identical. This class handles the creation of that array. +/// +/// Owing to the DSC SQL API, the process requires some intermediate variables +/// to hold things like data length etc. This object holds those variables. +/// +/// @note There are no unit tests for this class. It is tested indirectly +/// in all DSCSqlLeaseMgr::xxx6() calls where it is used. + +class DSCSqlLease6Exchange : public DSCSqlLeaseExchange { +public: + /// @brief Constructor + /// + /// The initialization of the variables here is nonly to satisfy cppcheck - + /// all variables are initialized/set in the methods before they are used. + DSCSqlLease6Exchange() : addr6_length_(0), duid_length_(0), + iaid_(0), lease_type_(0), prefixlen_(0), + pref_lifetime_(0), hwaddr_null_(false), hwtype_(0), + hwaddr_source_(0) { + memset(addr6_buffer_, 0, sizeof(addr6_buffer_)); + memset(duid_buffer_, 0, sizeof(duid_buffer_)); + + // Set the column names (for error messages) + uint32_t size; + uint32_t offset = 0; + size = 18; + columns_.resize(size); + columns_[offset++] = ColumnInfo("address", DSCSQL_DATA_TYPE_STRING); + columns_[offset++] = ColumnInfo("duid", DSCSQL_DATA_TYPE_BYTES); + columns_[offset++] = ColumnInfo("valid_lifetime", DSCSQL_DATA_TYPE_INT64); + columns_[offset++] = ColumnInfo("expire", DSCSQL_DATA_TYPE_TIMESTAMP); + columns_[offset++] = ColumnInfo("subnet_id", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("pref_lifetime", DSCSQL_DATA_TYPE_INT64); + columns_[offset++] = ColumnInfo("lease_type", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("iaid", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("prefix_len", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("fqdn_fwd", DSCSQL_DATA_TYPE_BOOL); + columns_[offset++] = ColumnInfo("fqdn_rev", DSCSQL_DATA_TYPE_BOOL); + columns_[offset++] = ColumnInfo("hostname", DSCSQL_DATA_TYPE_STRING); + columns_[offset++] = ColumnInfo("hwaddr", DSCSQL_DATA_TYPE_BYTES); + columns_[offset++] = ColumnInfo("hwtype", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("hwaddr_source", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("state", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("limit", DSCSQL_DATA_TYPE_INT32); + columns_[offset++] = ColumnInfo("version", DSCSQL_DATA_TYPE_NONE); + } + + /// @brief Create DSCSQL_BIND objects for Lease6 Pointer + /// + /// Fills in the DSCSQL_BIND array for sending data in the Lease6 object to + /// the database. + void createBindForSend(const Lease6Ptr& lease, DSCSqlBindArray& bind_array) { + if (!lease) { + isc_throw(BadValue, "createBindForSend:: Lease6 object is NULL"); + } + // Store lease object to ensure it remains valid. + lease_ = lease; + // Set up the structures for the various components of the lease4 + // structure. + + try { + // address: varchar(39) + std::string text_buffer = lease_->addr_.toText(); + addr6_length_ = text_buffer.size(); + if (addr6_length_ >= sizeof(addr6_buffer_)) { + isc_throw(BadValue, "address value is too large: " << text_buffer); + } + if (addr6_length_) { + memcpy(addr6_buffer_, text_buffer.c_str(), addr6_length_); + } + addr6_buffer_[addr6_length_] = '\0'; + bind_array.add(addr6_buffer_); + + // duid: varchar(128) + if (!lease_->duid_) { + isc_throw(DbOperationError, "lease6 for address " << addr6_buffer_ + << " is missing mandatory client-id."); + } + duid_ = lease_->duid_->getDuid(); + duid_length_ = duid_.size(); + bind_array.add(&duid_); + + // valid lifetime: unsigned int + valid_lifetime_ = lease_->valid_lft_; + bind_array.add(&valid_lifetime_); + + // expire: timestamp + // The lease structure holds the client last transmission time (cltt_) + // For convenience for external tools, this is converted to lease + // expiry time (expire). The relationship is given by: + // + // expire = cltt_ + valid_lft_ + DSCSqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_); + bind_array.add(&expire_); + + // subnet_id: unsigned int + // Can use lease_->subnet_id_ directly as it is of type uint32_t. + subnet_id_ = lease_->subnet_id_; + bind_array.add(&subnet_id_); + + // pref_lifetime: unsigned int + // Can use lease_->preferred_lft_ directly as it is of type uint32_t. + pref_lifetime_ = lease_->preferred_lft_; + bind_array.add(&pref_lifetime_); + + // lease_type: tinyint + // Must convert to uint8_t as lease_->type_ is a LeaseType variable. + lease_type_ = lease_->type_; + bind_array.add(&lease_type_); + + // iaid: unsigned int + // Can use lease_->iaid_ directly as it is of type uint32_t. + iaid_ = lease_->iaid_; + bind_array.add(&iaid_); + + // prefix_len: unsigned tinyint + // Can use lease_->prefixlen_ directly as it is uint32_t. + prefixlen_ = lease_->prefixlen_; + bind_array.add(&prefixlen_); + + // fqdn_fwd: boolean + fqdn_fwd_ = lease_->fqdn_fwd_; + bind_array.add(&fqdn_fwd_); + + // fqdn_rev: boolean + fqdn_rev_ = lease_->fqdn_rev_; + bind_array.add(&fqdn_rev_); + + // hostname: varchar(255) + hostname_length_ = lease_->hostname_.length(); + if (hostname_length_ >= sizeof(hostname_buffer_)) { + isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str()); + } + if (hostname_length_) { + memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_); + } + hostname_buffer_[hostname_length_] = '\0'; + bind_array.add(hostname_buffer_); + + // hwaddr: varbinary(20) - hardware/MAC address + HWAddrPtr hwaddr = lease_->hwaddr_; + if (hwaddr) { + hwaddr_ = hwaddr->hwaddr_; + } else { + hwaddr_.clear(); + } + hwaddr_length_ = hwaddr_.size(); + bind_array.add(&hwaddr_); + + // hwtype + if (hwaddr) { + hwtype_ = lease->hwaddr_->htype_; + } else { + hwtype_ = 0; + } + bind_array.add(&hwtype_); + + /// Hardware source + if (hwaddr) { + hwaddr_source_ = lease->hwaddr_->source_; + } else { + hwaddr_source_ = 0; + } + bind_array.add(&hwaddr_source_); + + // state: uint32_t + state_ = lease_->state_; + bind_array.add(&state_); + + } catch (const std::exception& ex) { + isc_throw(DbOperationError, + "Could not create bind array from Lease6: " + << lease_->addr_.toText() << ", reason: " << ex.what()); + } + } + + /// @brief Create BIND array to receive data + /// + /// Creates a DSCSQL_BIND array to receive Lease6 data from the database. + Lease6Ptr createBindForReceive(const CassRow* row) { + try { + const CassValue* value; + unsigned char* buffer = NULL; + const char* text_buffer = NULL; + + // address: uint32_t + value = cass_row_get_column_by_name(row, columns_[0].column_); + cass_value_get_string(value, &text_buffer, &addr6_length_); + if (addr6_length_ >= sizeof(addr6_buffer_)) { + isc_throw(BadValue, "address value is too large: " << text_buffer); + } + if (addr6_length_) { + memcpy(addr6_buffer_, text_buffer, addr6_length_); + } + addr6_buffer_[addr6_length_] = '\0'; + + // client_id: varbinary(128) + value = cass_row_get_column_by_name(row, columns_[1].column_); + cass_value_get_bytes(value, const_cast(&buffer), &duid_length_); + duid_.assign(buffer, buffer + duid_length_); + + // lease_time: unsigned int + value = cass_row_get_column_by_name(row, columns_[2].column_); + cass_value_get_int64(value, reinterpret_cast(&valid_lifetime_)); + + // expire: timestamp + value = cass_row_get_column_by_name(row, columns_[3].column_); + cass_value_get_int64(value, reinterpret_cast(&expire_)); + + // subnet_id: unsigned int + value = cass_row_get_column_by_name(row, columns_[4].column_); + cass_value_get_int32(value, reinterpret_cast(&subnet_id_)); + + // pref_lifetime: unsigned int + value = cass_row_get_column_by_name(row, columns_[5].column_); + cass_value_get_int32(value, reinterpret_cast(&pref_lifetime_)); + + // lease_type: tinyint + value = cass_row_get_column_by_name(row, columns_[6].column_); + cass_value_get_int32(value, reinterpret_cast(&lease_type_)); + + // iaid: unsigned int + value = cass_row_get_column_by_name(row, columns_[7].column_); + cass_value_get_int32(value, reinterpret_cast(&iaid_)); + + // prefix_len: unsigned tinyint + value = cass_row_get_column_by_name(row, columns_[8].column_); + cass_value_get_int32(value, reinterpret_cast(&prefixlen_)); + + // fqdn_fwd: boolean + value = cass_row_get_column_by_name(row, columns_[9].column_); + cass_value_get_bool(value, reinterpret_cast(&fqdn_fwd_)); + + // fqdn_rev: boolean + value = cass_row_get_column_by_name(row, columns_[10].column_); + cass_value_get_bool(value, reinterpret_cast(&fqdn_rev_)); + + // hostname: varchar(255) + value = cass_row_get_column_by_name(row, columns_[11].column_); + cass_value_get_string(value, &text_buffer, &hostname_length_); + if (hostname_length_ >= sizeof(hostname_buffer_)) { + isc_throw(BadValue, "hostname value is too large: " << text_buffer); + } + if (hostname_length_) { + memcpy(hostname_buffer_, text_buffer, hostname_length_); + } + hostname_buffer_[hostname_length_] = '\0'; + + // hardware address + // hwaddr: varbinary(20) + value = cass_row_get_column_by_name(row, columns_[12].column_); + cass_value_get_bytes(value, const_cast(&buffer), &hwaddr_length_); + hwaddr_.assign(buffer, buffer + hwaddr_length_); + + // hardware type: unsigned short int (16 bits) + value = cass_row_get_column_by_name(row, columns_[13].column_); + cass_value_get_int32(value, reinterpret_cast(&hwtype_)); + + // hardware source: unsigned int (32 bits) + value = cass_row_get_column_by_name(row, columns_[14].column_); + cass_value_get_int32(value, reinterpret_cast(&hwaddr_source_)); + + // state: uint32_t + value = cass_row_get_column_by_name(row, columns_[15].column_); + cass_value_get_int32(value, reinterpret_cast(&state_)); + + if (lease_type_ != Lease::TYPE_NA && lease_type_ != Lease::TYPE_TA && + lease_type_ != Lease::TYPE_PD) { + isc_throw(BadValue, "invalid lease type returned (" << + static_cast(lease_type_) << ") for lease with " + << "address " << addr6_buffer_ << ". Only 0, 1, or 2 are " + << "allowed."); + } + + isc::asiolink::IOAddress addr(addr6_buffer_); + DuidPtr duid(new DUID(duid_)); + HWAddrPtr hwaddr; + if (hwaddr_.size()) { + hwaddr.reset(new HWAddr(hwaddr_, hwtype_)); + hwaddr->source_ = hwaddr_source_; + } + + std::string hostname(hostname_buffer_, + hostname_buffer_ + hostname_length_); + + // Create the lease and set the cltt (after converting from the + // expire time retrieved from the database). + Lease6Ptr result(new Lease6(static_cast(lease_type_), addr, duid, iaid_, + pref_lifetime_, valid_lifetime_, 0, 0, + subnet_id_, fqdn_fwd_, fqdn_rev_, + hostname, hwaddr, prefixlen_)); + + time_t cltt = 0; + DSCSqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt); + result->cltt_ = cltt; +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + result->old_cltt_ = cltt; +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + + result->state_ = state_; + + return (result); + } catch (const std::exception& ex) { + isc_throw(DbOperationError, + "Could not convert data to Lease4, reason: " + << ex.what()); + } + return (Lease6Ptr()); + } + +private: + // Note: All array lengths are equal to the corresponding variable in the + // schema. + // Note: arrays are declared fixed length for speed of creation + Lease6Ptr lease_; ///< Pointer to lease object + char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1]; ///< Character + ///< array form of V6 address + unsigned long addr6_length_; ///< Length of the address + std::vector duid_; ///< Client identification + uint8_t duid_buffer_[DUID::MAX_DUID_LEN]; ///< Buffer form of DUID + unsigned long duid_length_; ///< Length of the DUID + uint32_t iaid_; ///< Identity association ID + uint32_t lease_type_; ///< Lease type + uint32_t prefixlen_; ///< Prefix length + uint32_t pref_lifetime_; ///< Preferred lifetime + bool hwaddr_null_; ///< Used when HWAddr is null + uint32_t hwtype_; ///< Hardware type + uint32_t hwaddr_source_; ///< Source of the hardware address +}; + +#ifdef TERASTREAM_DB_LOGIC +class DSCSqlCustomLeaseExchange : public virtual SqlLeaseExchange { +public: + DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlCustomLeaseExchange() {} + virtual void executeInternal(LeaseExchangeData& exchange, bool has_allocated_ip, bool has_requested_ip, bool has_reserved_ip) { + if (has_allocated_ip) { + } + if (has_requested_ip) { + } + if (has_reserved_ip) { + } + if (exchange.in_fake_) { + } + } +}; + +class DSCSqlLease4DiscoverNoReqNoResExchange : public SqlLease4DiscoverNoReqNoResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4DiscoverNoReqNoResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4DiscoverNoReqNoResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, true, false, false); + } +private: +}; + +/// @brief Supports exchanging IPv4 leases with SQL for discover. +class DSCSqlLease4DiscoverNoReqResExchange : public SqlLease4DiscoverNoReqResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4DiscoverNoReqResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4DiscoverNoReqResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, true, false, true); + } +private: +}; + +/// @brief Supports exchanging IPv4 leases with SQL for discover. +class DSCSqlLease4DiscoverReqNoResExchange : public SqlLease4DiscoverReqNoResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4DiscoverReqNoResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4DiscoverReqNoResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, true, true, false); + } +private: +}; + +/// @brief Supports exchanging IPv4 leases with SQL for discover. +class DSCSqlLease4DiscoverReqResExchange : public SqlLease4DiscoverReqResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4DiscoverReqResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4DiscoverReqResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, true, true, true); + } +private: +}; + +/// @brief Supports exchanging IPv4 leases with SQL for request. +class DSCSqlLease4RequestNoReqNoResExchange : public SqlLease4RequestNoReqNoResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4RequestNoReqNoResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4RequestNoReqNoResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, true, false, false); + } +private: +}; + +/// @brief Supports exchanging IPv4 leases with SQL for request. +class DSCSqlLease4RequestNoReqResExchange : public SqlLease4RequestNoReqResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4RequestNoReqResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4RequestNoReqResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, false, false, true); + } +private: +}; + +/// @brief Supports exchanging IPv4 leases with SQL for request. +class DSCSqlLease4RequestReqNoResExchange : public SqlLease4RequestReqNoResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4RequestReqNoResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4RequestReqNoResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, false, true, false); + } +private: +}; + +/// @brief Supports exchanging IPv4 leases with SQL for request. +class DSCSqlLease4RequestReqResExchange : public SqlLease4RequestReqResExchange, public DSCSqlCustomLeaseExchange { +public: + DSCSqlLease4RequestReqResExchange() : DSCSqlCustomLeaseExchange() {} + virtual ~DSCSqlLease4RequestReqResExchange() {} + virtual void execute(LeaseExchangeData& exchange) { + executeInternal(exchange, false, true, true); + } +private: +}; +#endif // TERASTREAM_DB_LOGIC + +DSCSqlLeaseMgr::DSCSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) + : LeaseMgr(), dbconn_(parameters), exchange4_(new DSCSqlLease4Exchange()), + exchange6_(new DSCSqlLease6Exchange()) { + dbconn_.openDatabase(); + dbconn_.prepareStatements(DSCSqlLeaseMgr::tagged_statements_); +} + +DSCSqlLeaseMgr::~DSCSqlLeaseMgr() { +} + +std::string +DSCSqlLeaseMgr::getDBVersion() { + std::stringstream tmp; + tmp << "DSC SQL backend " << DSCSQL_CURRENT_VERSION; + tmp << "." << DSCSQL_CURRENT_MINOR; + tmp << ", library " << "cassandra_static"; + return (tmp.str()); +} + + +void +DSCSqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, const DSCSqlLeaseExchange& exchange, DSCSqlDataType& type) { + if (DSCSqlLeaseMgr::tagged_statements_[stindex].params_ && + DSCSqlLeaseMgr::tagged_statements_[stindex].params_[pindex]) { + for (int i = 0; exchange.columns_.size(); i++) { + if (!strcmp(DSCSqlLeaseMgr::tagged_statements_[stindex].params_[pindex], exchange.columns_[i].column_)) { + type = exchange.columns_[i].type_; + return; + } + } + } + type = DSCSQL_DATA_TYPE_NONE; +} + +void +DSCSqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, DSCSqlBindArray& bind_array, const DSCSqlLeaseExchange& exchange) { + if (DSCSqlLeaseMgr::tagged_statements_[stindex].params_ == NULL) { + return; + } + for (int i = 0; DSCSqlLeaseMgr::tagged_statements_[stindex].params_[i]; i++) { + DSCSqlDataType type; + DSCSqlLeaseMgr::getDataType(stindex, i, exchange, type); + DSCSqlBindFunctions[type](statement, i, bind_array.values_[i]); + } +} + +bool +DSCSqlLeaseMgr::addLeaseCommon(StatementIndex stindex, + DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& exchange) { + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + + statement = cass_prepared_bind(dbconn_.statements_[stindex]); + + DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, exchange); + + future = cass_session_execute(dbconn_.session_, statement); + cass_future_wait(future); + std::string error; + dbconn_.checkStatementError(error, future, stindex, "unable to INSERT"); + rc = cass_future_error_code(future); + if (rc != CASS_OK) { + cass_future_free(future); + cass_statement_free(statement); + isc_throw(DbOperationError, error); + } + const CassResult* resultCollection = cass_future_get_result(future); + int row_count = cass_result_row_count(resultCollection); + int column_count = cass_result_column_count(resultCollection); + cass_result_free(resultCollection); + cass_future_free(future); + cass_statement_free(statement); +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + if (row_count != 1) { + return (false); + } else if (column_count != 1) { + isc_throw(TransactionException, "add transaction failed"); + } +#else + if (row_count && column_count) { + } +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + + return (true); +} + +bool +DSCSqlLeaseMgr::addLease(const Lease4Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_ADD_ADDR4).arg(lease->addr_.toText()); + + DSCSqlBindArray bind_array; + exchange4_->createBindForSend(lease, bind_array); + return (addLeaseCommon(INSERT_LEASE4, bind_array, *exchange4_)); +} + +bool +DSCSqlLeaseMgr::addLease(const Lease6Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_ADD_ADDR6).arg(lease->addr_.toText()); + + DSCSqlBindArray bind_array; + exchange6_->createBindForSend(lease, bind_array); + return (addLeaseCommon(INSERT_LEASE6, bind_array, *exchange6_)); +} + +template +void DSCSqlLeaseMgr::getLeaseCollection(StatementIndex stindex, + DSCSqlBindArray& bind_array, + Exchange& exchange, + LeaseCollection& result, + bool single) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_); + + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + const DSCSqlLeaseExchange& leaseExchange = static_cast(*exchange); + + statement = cass_prepared_bind(dbconn_.statements_[stindex]); + + DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, leaseExchange); + + future = cass_session_execute(dbconn_.session_, statement); + cass_future_wait(future); + std::string error; + dbconn_.checkStatementError(error, future, "unable to GET"); + rc = cass_future_error_code(future); + if (rc != CASS_OK) { + cass_future_free(future); + cass_statement_free(statement); + isc_throw(DbOperationError, error); + } + + const CassResult* resultCollection = cass_future_get_result(future); + CassIterator* rows = cass_iterator_from_result(resultCollection); + + int rowCount = 0; + while (cass_iterator_next(rows)) { + rowCount++; + if (single && rowCount > 1) + { + result.clear(); + break; + } + const CassRow* row = cass_iterator_get_row(rows); + result.push_back(exchange->createBindForReceive(row)); + } + + cass_iterator_free(rows); + cass_result_free(resultCollection); + cass_future_free(future); + cass_statement_free(statement); + if (single && rowCount > 1) { + isc_throw(MultipleRecords, "multiple records were found in the " + "database where only one was expected for query " + << dbconn_.tagged_statements_[stindex].name_); + } +} + +void +DSCSqlLeaseMgr::getLease(StatementIndex stindex, DSCSqlBindArray& bind_array, + Lease4Ptr& result) const { + // Create appropriate collection object and get all leases matching + // the selection criteria. The "single" parameter is true to indicate + // that the called method should throw an exception if multiple + // matching records are found: this particular method is called when only + // one or zero matches is expected. + Lease4Collection collection; + getLeaseCollection(stindex, bind_array, exchange4_, collection, true); + + // Return single record if present, else clear the lease. + if (collection.empty()) { + result.reset(); + } else { + result = *collection.begin(); + } +} + + +void +DSCSqlLeaseMgr::getLease(StatementIndex stindex, DSCSqlBindArray& bind_array, + Lease6Ptr& result) const { + // Create appropriate collection object and get all leases matching + // the selection criteria. The "single" parameter is true to indicate + // that the called method should throw an exception if multiple + // matching records are found: this particular method is called when only + // one or zero matches is expected. + Lease6Collection collection; + getLeaseCollection(stindex, bind_array, exchange6_, collection, true); + + // Return single record if present, else clear the lease. + if (collection.empty()) { + result.reset(); + } else { + result = *collection.begin(); + } +} + +// Basic lease access methods. Obtain leases from the database using various +// criteria. + +Lease4Ptr +DSCSqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_ADDR4).arg(addr.toText()); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + uint32_t addr4_data = static_cast(addr); + bind_array.add(&addr4_data); + + // Get the data + Lease4Ptr result; + getLease(GET_LEASE4_ADDR, bind_array, result); + + return (result); +} + + +Lease4Collection +DSCSqlLeaseMgr::getLease4(const HWAddr& hwaddr) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_HWADDR).arg(hwaddr.toText()); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + std::vectorhwaddr_data = hwaddr.hwaddr_; + bind_array.add(&hwaddr_data); + + // Get the data + Lease4Collection result; + getLeaseCollection(GET_LEASE4_HWADDR, bind_array, result); + + return (result); +} + + +Lease4Ptr +DSCSqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_SUBID_HWADDR) + .arg(subnet_id).arg(hwaddr.toText()); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + std::vectorhwaddr_data = hwaddr.hwaddr_; + bind_array.add(&hwaddr_data); + + uint32_t subnet_id_data = subnet_id; + bind_array.add(&subnet_id_data); + + // Get the data + Lease4Ptr result; + getLease(GET_LEASE4_HWADDR_SUBID, bind_array, result); + + return (result); +} + + +Lease4Collection +DSCSqlLeaseMgr::getLease4(const ClientId& clientid) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_CLIENTID).arg(clientid.toText()); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + std::vector client_id_data = clientid.getClientId(); + bind_array.add(&client_id_data); + + // Get the data + Lease4Collection result; + getLeaseCollection(GET_LEASE4_CLIENTID, bind_array, result); + + return (result); +} + +Lease4Ptr +DSCSqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr, SubnetID subnet_id) const { + /// This function is currently not implemented because allocation engine + /// searches for the lease using HW address or client identifier. + /// It never uses both parameters in the same time. We need to + /// consider if this function is needed at all. + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_CLIENTID_HWADDR_SUBID).arg(clientid.toText()) + .arg(hwaddr.toText()).arg(subnet_id); + + isc_throw(NotImplemented, "The DSCSqlLeaseMgr::getLease4 function was" + " called, but it is not implemented"); +} + +Lease4Ptr +DSCSqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_SUBID_CLIENTID) + .arg(subnet_id).arg(clientid.toText()); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + std::vector client_id_data = clientid.getClientId(); + bind_array.add(&client_id_data); + + uint32_t subnet_id_data = subnet_id; + bind_array.add(&subnet_id_data); + + // Get the data + Lease4Ptr result; + getLease(GET_LEASE4_CLIENTID_SUBID, bind_array, result); + + return (result); +} + + +Lease6Ptr +DSCSqlLeaseMgr::getLease6(Lease::Type lease_type, + const isc::asiolink::IOAddress& addr) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_ADDR6).arg(addr.toText()) + .arg(lease_type); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + std::string text_buffer = addr.toText(); + uint32_t addr6_length = text_buffer.size(); + char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1]; + if (addr6_length >= sizeof(addr6_buffer)) { + isc_throw(BadValue, "address value is too large: " << text_buffer); + } + if (addr6_length) { + memcpy(addr6_buffer, text_buffer.c_str(), addr6_length); + } + addr6_buffer[addr6_length] = '\0'; + bind_array.add(addr6_buffer); + + uint32_t lease_type_data = lease_type; + bind_array.add(&lease_type_data); + + Lease6Ptr result; + getLease(GET_LEASE6_ADDR, bind_array, result); + + return (result); +} + + +Lease6Collection +DSCSqlLeaseMgr::getLeases6(Lease::Type lease_type, + const DUID& duid, uint32_t iaid) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_IAID_DUID).arg(iaid).arg(duid.toText()) + .arg(lease_type); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + std::vector duid_data = duid.getDuid(); + bind_array.add(&duid_data); + + uint32_t iaid_data = iaid; + bind_array.add(&iaid_data); + + uint32_t lease_type_data = lease_type; + bind_array.add(&lease_type_data); + + // ... and get the data + Lease6Collection result; + getLeaseCollection(GET_LEASE6_DUID_IAID, bind_array, result); + + return (result); +} + +Lease6Collection +DSCSqlLeaseMgr::getLeases6(Lease::Type lease_type, + const DUID& duid, uint32_t iaid, + SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_GET_IAID_SUBID_DUID) + .arg(iaid).arg(subnet_id).arg(duid.toText()) + .arg(lease_type); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + std::vector duid_data = duid.getDuid(); + bind_array.add(&duid_data); + + uint32_t iaid_data = iaid; + bind_array.add(&iaid_data); + + uint32_t subnet_id_data = subnet_id; + bind_array.add(&subnet_id_data); + + uint32_t lease_type_data = lease_type; + bind_array.add(&lease_type_data); + + // ... and get the data + Lease6Collection result; + getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, bind_array, result); + + return (result); +} + +void +DSCSqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_GET_EXPIRED6) + .arg(max_leases); + getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE); +} + +void +DSCSqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_GET_EXPIRED4) + .arg(max_leases); + getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE); +} + +template +void +DSCSqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases, + const size_t max_leases, + StatementIndex statement_index) const { + // Set up the WHERE clause value + //"WHERE state != ? AND expire < ? ORDER BY expire ASC " + uint32_t keepState = Lease::STATE_EXPIRED_RECLAIMED; + uint64_t timestamp = static_cast(time(NULL)); + + // If the number of leases is 0, we will return all leases. This is + // achieved by setting the limit to a very high value. + uint32_t limit = max_leases > 0 ? static_cast(max_leases) : + std::numeric_limits::max(); + + for (uint32_t state = Lease::STATE_DEFAULT; state <= Lease::STATE_EXPIRED_RECLAIMED; state++) { + if (state == keepState) { + continue; + } + LeaseCollection tempCollection; + DSCSqlBindArray bind_array; + + bind_array.add(&state); + bind_array.add(&limit); + + // Retrieve leases from the database. + getLeaseCollection(statement_index, bind_array, tempCollection); + + typedef typename LeaseCollection::iterator LeaseCollectionIt; + + for (LeaseCollectionIt it = tempCollection.begin(); it != tempCollection.end(); ++it) { + if ((*it)->getExpirationTime() < timestamp) { + expired_leases.push_back((*it)); + } + } + } +} + +template +void +DSCSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, + DSCSqlBindArray& bind_array, + const LeasePtr& lease, DSCSqlLeaseExchange& exchange) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_ADD_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_); + + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + + statement = cass_prepared_bind(dbconn_.statements_[stindex]); + + DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, exchange); + + future = cass_session_execute(dbconn_.session_, statement); + cass_future_wait(future); + std::string error; + dbconn_.checkStatementError(error, future, stindex, "unable to UPDATE"); + rc = cass_future_error_code(future); + if (rc != CASS_OK) { + cass_future_free(future); + cass_statement_free(statement); + isc_throw(DbOperationError, error); + } + + const CassResult* resultCollection = cass_future_get_result(future); + int row_count = cass_result_row_count(resultCollection); + int column_count = cass_result_column_count(resultCollection); + cass_result_free(resultCollection); + cass_future_free(future); + cass_statement_free(statement); + +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + // Check success case first as it is the most likely outcome. + if (row_count > 1) { + // Should not happen - primary key constraint should only have selected + // one row. + isc_throw(DbOperationError, "apparently updated more than one lease " + "that had the address " << lease->addr_.toText()); + } + + // If no rows affected, lease doesn't exist. + if (row_count == 0) { + isc_throw(NoSuchLease, "unable to update lease for address " << + lease->addr_.toText() << " as it does not exist"); + } + + if (row_count != 1) { + isc_throw(DbOperationError, "apparently updated transaction failed for the lease " + "that had the address " << lease->addr_.toText()); + } else if (column_count != 1) { + isc_throw(TransactionException, "update transaction failed"); + } +#else + if (row_count && column_count && lease) { + } +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS +} + + +void +DSCSqlLeaseMgr::updateLease4(const Lease4Ptr& lease) { + const StatementIndex stindex = UPDATE_LEASE4; + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_UPDATE_ADDR4).arg(lease->addr_.toText()); + + // Create the BIND array for the data being updated + DSCSqlBindArray bind_array; + exchange4_->createBindForSend(lease, bind_array); + bind_array.remove(0); + + // Set up the WHERE clause and append it to the SQL_BIND array + uint32_t addr4_data = static_cast(lease->addr_); + bind_array.add(&addr4_data); + +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + uint64_t old_expire; + DSCSqlLeaseExchange::convertToDatabaseTime(lease->old_cltt_, lease->old_valid_lft_, old_expire); + bind_array.add(&old_expire); +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + + // Drop to common update code + updateLeaseCommon(stindex, bind_array, lease, *exchange4_); +} + +void +DSCSqlLeaseMgr::updateLease6(const Lease6Ptr& lease) { + const StatementIndex stindex = UPDATE_LEASE6; + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_UPDATE_ADDR6).arg(lease->addr_.toText()); + + // Create the BIND array for the data being updated + DSCSqlBindArray bind_array; + exchange6_->createBindForSend(lease, bind_array); + bind_array.remove(0); + + // Set up the WHERE clause and append it to the BIND array + std::string text_buffer = lease->addr_.toText(); + uint32_t addr6_length = text_buffer.size(); + char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1]; + if (addr6_length >= sizeof(addr6_buffer)) { + isc_throw(BadValue, "address value is too large: " << text_buffer); + } + if (addr6_length) { + memcpy(addr6_buffer, text_buffer.c_str(), addr6_length); + } + addr6_buffer[addr6_length] = '\0'; + bind_array.add(addr6_buffer); + +#ifdef TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + uint64_t old_expire; + DSCSqlLeaseExchange::convertToDatabaseTime(lease->old_cltt_, lease->old_valid_lft_, old_expire); + bind_array.add(&old_expire); +#endif // TERASTREAM_LIGHTWEIGHT_TRANSACTIONS + + // Drop to common update code + updateLeaseCommon(stindex, bind_array, lease, *exchange6_); +} + +bool +DSCSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, + DSCSqlBindArray& bind_array, DSCSqlLeaseExchange& exchange) { + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + + statement = cass_prepared_bind(dbconn_.statements_[stindex]); + + DSCSqlLeaseMgr::bindData(statement, stindex, bind_array, exchange); + + future = cass_session_execute(dbconn_.session_, statement); + cass_future_wait(future); + std::string error; + dbconn_.checkStatementError(error, future, stindex, "unable to DELETE"); + rc = cass_future_error_code(future); + cass_future_free(future); + cass_statement_free(statement); + if (rc != CASS_OK) { + isc_throw(DbOperationError, error); + } + + return (true); +} + +bool +DSCSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_DELETE_ADDR).arg(addr.toText()); + + // Set up the WHERE clause value + DSCSqlBindArray bind_array; + + if (addr.isV4()) { + uint32_t addr4_data = static_cast(addr); + bind_array.add(&addr4_data); + return (deleteLeaseCommon(DELETE_LEASE4, bind_array, *exchange4_)); + } else { + std::string text_buffer = addr.toText(); + uint32_t addr6_length = text_buffer.size(); + char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1]; + if (addr6_length >= sizeof(addr6_buffer)) { + isc_throw(BadValue, "address value is too large: " << text_buffer); + } + if (addr6_length) { + memcpy(addr6_buffer, text_buffer.c_str(), addr6_length); + } + addr6_buffer[addr6_length] = '\0'; + bind_array.add(addr6_buffer); + return (deleteLeaseCommon(DELETE_LEASE6, bind_array, *exchange6_)); + } +} + +uint64_t +DSCSqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_DELETE_EXPIRED_RECLAIMED4) + .arg(secs); + return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED)); +} + +uint64_t +DSCSqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_DSCSQL_DELETE_EXPIRED_RECLAIMED6) + .arg(secs); + return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED)); +} + +uint64_t +DSCSqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIndex statement_index) { + // Set up the WHERE clause value + //"WHERE state = ? AND expire < ? ALLOW FILTERING" + + DSCSqlBindArray bind_array; + uint32_t result = 0; + + // State is reclaimed. + uint32_t state = Lease::STATE_EXPIRED_RECLAIMED; + bind_array.add(&state); + + // Expiration timestamp. + uint64_t expiration = static_cast(time(NULL) - static_cast(secs)); + + // Get the data + Lease4Collection result4Leases; + Lease6Collection result6Leases; + switch (statement_index) { + case DELETE_LEASE4_STATE_EXPIRED: + getLeaseCollection(statement_index, bind_array, result4Leases); + break; + case DELETE_LEASE6_STATE_EXPIRED: + getLeaseCollection(statement_index, bind_array, result6Leases); + break; + default: + break; + } + for (Lease4Collection::iterator it = result4Leases.begin(); it != result4Leases.end(); ++it) { + if ((*it)->getExpirationTime() < expiration) { + if(deleteLease((*it)->addr_)) { + result++; + } + } + } + for (Lease6Collection::iterator it = result6Leases.begin(); it != result6Leases.end(); ++it) { + if ((*it)->getExpirationTime() < expiration) { + if(deleteLease((*it)->addr_)) { + result++; + } + } + } + return (result); +} + +string +DSCSqlLeaseMgr::getName() const { + string name = ""; + try { + name = dbconn_.getParameter("name"); + } catch (...) { + // Return an empty name + } + return (name); +} + +string +DSCSqlLeaseMgr::getDescription() const { + return (string("DataStax Cassandra Database")); +} + +pair +DSCSqlLeaseMgr::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(version, minor); +} + +void +DSCSqlLeaseMgr::commit() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_COMMIT); +} + +void +DSCSqlLeaseMgr::rollback() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_ROLLBACK); +} + +#ifdef TERASTREAM_DB_LOGIC +void +DSCSqlLeaseMgr::discoverLease4NoReqNoRes(LeaseExchangeData &data) { + DSCSqlLease4DiscoverNoReqNoResExchange exchange; + exchange.execute(data); +} + +void +DSCSqlLeaseMgr::discoverLease4NoReqRes(LeaseExchangeData &data) { + DSCSqlLease4DiscoverNoReqResExchange exchange; + exchange.execute(data); +} + +void +DSCSqlLeaseMgr::discoverLease4ReqNoRes(LeaseExchangeData &data) { + DSCSqlLease4DiscoverReqNoResExchange exchange; + exchange.execute(data); +} + +void +DSCSqlLeaseMgr::discoverLease4ReqRes(LeaseExchangeData &data) { + DSCSqlLease4DiscoverReqResExchange exchange; + exchange.execute(data); +} + +void +DSCSqlLeaseMgr::requestLease4NoReqNoRes(LeaseExchangeData &data) { + DSCSqlLease4RequestNoReqNoResExchange exchange; + exchange.execute(data); +} + +void +DSCSqlLeaseMgr::requestLease4NoReqRes(LeaseExchangeData &data) { + DSCSqlLease4RequestNoReqResExchange exchange; + exchange.execute(data); +} + +void +DSCSqlLeaseMgr::requestLease4ReqNoRes(LeaseExchangeData &data) { + DSCSqlLease4RequestReqNoResExchange exchange; + exchange.execute(data); +} + +void +DSCSqlLeaseMgr::requestLease4ReqRes(LeaseExchangeData &data) { + DSCSqlLease4RequestReqResExchange exchange; + exchange.execute(data); +} +#endif // TERASTREAM_DB_LOGIC + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/dhcpsrv/dscsql_lease_mgr.h b/src/lib/dhcpsrv/dscsql_lease_mgr.h new file mode 100644 index 0000000000..1b7becf17a --- /dev/null +++ b/src/lib/dhcpsrv/dscsql_lease_mgr.h @@ -0,0 +1,640 @@ +// Copyright (C) 2015 - 2016 Deutsche Telekom AG. +// +// Author: Razvan Becheriu +// +// 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 +#include +#include +#include +#include +#include +#include + +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 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 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 + 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 + 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 + 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 exchange4_; ///< Exchange object + boost::scoped_ptr exchange6_; ///< Exchange object +}; + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif // DSCSQL_LEASE_MGR_H diff --git a/src/lib/dhcpsrv/tests/dscsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/dscsql_lease_mgr_unittest.cc new file mode 100644 index 0000000000..e35f7c0e4e --- /dev/null +++ b/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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +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 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 diff --git a/src/lib/dhcpsrv/testutils/dscsql_schema.cc b/src/lib/dhcpsrv/testutils/dscsql_schema.cc new file mode 100644 index 0000000000..579dd28590 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include + +#include +#include +#include + +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(); +} + + +}; +}; +}; diff --git a/src/lib/dhcpsrv/testutils/dscsql_schema.h b/src/lib/dhcpsrv/testutils/dscsql_schema.h new file mode 100644 index 0000000000..1f927c153a --- /dev/null +++ b/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 +#include +#include + +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: +/// +/// /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: +/// +/// /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 diff --git a/src/share/database/scripts/dscsql/.gitignore b/src/share/database/scripts/dscsql/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/share/database/scripts/dscsql/Makefile.am b/src/share/database/scripts/dscsql/Makefile.am new file mode 100644 index 0000000000..fd2831bbaa --- /dev/null +++ b/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} diff --git a/src/share/database/scripts/dscsql/dhcpdb_create.cql b/src/share/database/scripts/dscsql/dhcpdb_create.cql new file mode 100644 index 0000000000..c8924ca4af --- /dev/null +++ b/src/share/database/scripts/dscsql/dhcpdb_create.cql @@ -0,0 +1,213 @@ +-- Copyright (C) 2015 - 2016 Deutsche Telekom AG. + +-- Author: Razvan Becheriu + +-- 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 -p -k -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; diff --git a/src/share/database/scripts/dscsql/dhcpdb_drop.cql b/src/share/database/scripts/dscsql/dhcpdb_drop.cql new file mode 100755 index 0000000000..7ec86fadc3 --- /dev/null +++ b/src/share/database/scripts/dscsql/dhcpdb_drop.cql @@ -0,0 +1,27 @@ +-- Copyright (C) 2015 - 2016 Deutsche Telekom AG. + +-- Author: Razvan Becheriu + +-- 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;