- replaced void* with boost::any for type safety (might be just a bit less efficient)
- replaced a multi-index-container used for statement parameters ( with position index and name index ) and a vector of the same parameters with a compile-time type-based map
- refactored lease manager to use exchanges
- exchanges now support UDTs (user defined types) and collections to be used in Cassandra statements
- replaced size-ambiguous types (e.g. int) with size-explicit types (e.g. int32_t) in Cassandra-related code
- made exception messages and log messages more detailed and consistent in format
--- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+-- Copyright (C) 2015-2017 Deutsche Telekom AG.
-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
-- -----------------------------------------------------
-- Table `lease4`
-- -----------------------------------------------------
-CREATE TABLE lease4 (
+CREATE TABLE IF NOT EXISTS lease4 (
address int,
hwaddr blob,
client_id blob,
fqdn_rev boolean,
hostname varchar,
state int,
- PRIMARY KEY (address)
+ 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);
+CREATE INDEX IF NOT EXISTS lease4index1 ON lease4 (client_id);
+CREATE INDEX IF NOT EXISTS lease4index2 ON lease4 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease4index3 ON lease4 (hwaddr);
+CREATE INDEX IF NOT EXISTS lease4index4 ON lease4 (expire);
+CREATE INDEX IF NOT EXISTS lease4index5 ON lease4 (state);
-- Holds the IPv6 leases.
-- N.B. The use of a VARCHAR for the address is temporary for development:
-- -----------------------------------------------------
-- Table `lease6`
-- -----------------------------------------------------
-CREATE TABLE lease6 (
+CREATE TABLE IF NOT EXISTS lease6 (
address varchar,
- duid blob,
valid_lifetime bigint,
expire bigint,
subnet_id int,
pref_lifetime bigint,
- lease_type int,
+ duid blob,
iaid int,
+ lease_type int,
prefix_len int,
fqdn_fwd boolean,
fqdn_rev boolean,
hwtype int,
hwaddr_source int,
state int,
- PRIMARY KEY (address)
+ 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);
+CREATE INDEX IF NOT EXISTS lease6index1 ON lease6 (duid);
+CREATE INDEX IF NOT EXISTS lease6index2 ON lease6 (iaid);
+CREATE INDEX IF NOT EXISTS lease6index3 ON lease6 (lease_type);
+CREATE INDEX IF NOT EXISTS lease6index4 ON lease6 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease6index5 ON lease6 (expire);
+CREATE INDEX IF NOT EXISTS 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
-- -----------------------------------------------------
-- Table `lease6_types`
-- -----------------------------------------------------
-CREATE TABLE lease6_types (
+CREATE TABLE IF NOT EXISTS lease6_types (
lease_type int, -- Lease type code.
name varchar, -- Name of the lease type
- PRIMARY KEY (lease_type)
+ PRIMARY KEY ((lease_type))
);
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
-- -----------------------------------------------------
-- Table `lease_hwaddr_source`
-- -----------------------------------------------------
-CREATE TABLE lease_hwaddr_source (
+CREATE TABLE IF NOT EXISTS lease_hwaddr_source (
hwaddr_source int,
name varchar,
- PRIMARY KEY (hwaddr_source)
+ PRIMARY KEY ((hwaddr_source))
);
-- Hardware address obtained from raw sockets
-- -----------------------------------------------------
-- Table `lease_state`
-- -----------------------------------------------------
-CREATE TABLE lease_state (
+CREATE TABLE IF NOT EXISTS lease_state (
state int,
name varchar,
- PRIMARY KEY (state)
+ PRIMARY KEY ((state))
);
-- Insert currently defined state names.
-- 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 synchronized with
--- src/share/database/scripts/cql/dhcpdb_create.cql which defines the schema for
--- the unit tests.
+-- Note: This MUST be kept in step with src/share/database/scripts/cassandra/dhcpdb_create.cql,
+-- which defines the schema for the unit tests.
-- -----------------------------------------------------
-- Table `schema_version`
-- -----------------------------------------------------
-CREATE TABLE schema_version (
+CREATE TABLE IF NOT EXISTS schema_version (
version int,
minor int,
- PRIMARY KEY (version)
+ PRIMARY KEY ((version))
);
INSERT INTO schema_version (version, minor) VALUES (1, 0);
--- This line concludes database initalization to version 1.0.
+-- This line concludes database initialization to version 1.0.
+
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
//
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/cql_exchange.h>
+#include <dhcpsrv/db_exceptions.h>
+#include <dhcpsrv/dhcpsrv_log.h>
-#include <boost/shared_ptr.hpp>
-
-using namespace std;
+#include <memory> // for std::unique_ptr
namespace isc {
namespace dhcp {
CqlConnection::CqlConnection(const ParameterMap& parameters)
- : DatabaseConnection(parameters), cluster_(NULL), session_(NULL),
- force_consistency_(true), consistency_(CASS_CONSISTENCY_QUORUM),
- tagged_statements_(NULL) {
+ : DatabaseConnection(parameters), statements_(), cluster_(NULL),
+ session_(NULL), consistency_(CASS_CONSISTENCY_QUORUM), schema_meta_(NULL),
+ keyspace_meta_(NULL), force_consistency_(true) {
}
CqlConnection::~CqlConnection() {
CassError rc = CASS_OK;
std::string error;
- for (int i = 0; i < statements_.size(); i++) {
- if (statements_[i]) {
- cass_prepared_free(statements_[i]);
+ for (StatementMapEntry s : statements_) {
+ // typeid(s.second.first) is CassPrepared*
+ CqlTaggedStatement statement = s.second;
+ if (statement.prepared_statement_) {
+ cass_prepared_free(statement.prepared_statement_);
}
- statements_[i] = NULL;
}
if (session_) {
+ cass_schema_meta_free(schema_meta_);
CassFuture* close_future = cass_session_close(session_);
cass_future_wait(close_future);
- checkStatementError(error, close_future, "could not close connection to"
- " DB");
+ error = checkFutureError(
+ "CqlConnection::~CqlConnection(): cass_sesssion_close() != CASS_OK",
+ close_future);
rc = cass_future_error_code(close_future);
cass_future_free(close_future);
cass_session_free(session_);
}
if (port) {
- int port_number;
+ int32_t port_number;
try {
- port_number = boost::lexical_cast<int>(port);
+ port_number = boost::lexical_cast<int32_t>(port);
if (port_number < 1 || port_number > 65535) {
- isc_throw(
- DbOperationError,
- "Port outside of range, expected 1-65535, instead got "
- << port);
+ isc_throw(DbOperationError,
+ "CqlConnection::openDatabase(): "
+ "port outside of range, expected "
+ "1-65535, instead got "
+ << port);
}
} catch (const boost::bad_lexical_cast& ex) {
isc_throw(DbOperationError,
- "Invalid port, castable to int expected, instead got \""
- << port << "\", " << ex.what());
+ "CqlConnection::openDatabase(): invalid "
+ "port, expected castable to int, instead got "
+ "\"" << port
+ << "\", " << ex.what());
}
cass_cluster_set_port(cluster_, port_number);
}
if (reconnect_wait_time) {
- int reconnect_wait_time_number;
+ int32_t reconnect_wait_time_number;
try {
reconnect_wait_time_number =
- boost::lexical_cast<int>(reconnect_wait_time);
+ boost::lexical_cast<int32_t>(reconnect_wait_time);
if (reconnect_wait_time_number < 0) {
isc_throw(DbOperationError,
- "Invalid reconnect wait time, positive number "
- "expected, instead got "
+ "CqlConnection::openDatabase(): invalid reconnect "
+ "wait time, expected positive number, instead got "
<< reconnect_wait_time);
}
} catch (const boost::bad_lexical_cast& ex) {
- isc_throw(DbOperationError, "Invalid reconnect wait time, castable "
- "to int expected, instead got \""
- << reconnect_wait_time << "\", "
- << ex.what());
+ isc_throw(DbOperationError,
+ "CqlConnection::openDatabase(): "
+ "invalid reconnect wait time, expected "
+ "castable to int, instead got \""
+ << reconnect_wait_time << "\", " << ex.what());
}
cass_cluster_set_reconnect_wait_time(cluster_,
reconnect_wait_time_number);
}
if (connect_timeout) {
- int connect_timeout_number;
+ int32_t connect_timeout_number;
try {
- connect_timeout_number = boost::lexical_cast<int>(connect_timeout);
+ connect_timeout_number =
+ boost::lexical_cast<int32_t>(connect_timeout);
if (connect_timeout_number < 0) {
isc_throw(DbOperationError,
- "Invalid connect timeout, positive number expected, "
- "instead got "
+ "CqlConnection::openDatabase(): "
+ "invalid connect timeout, expected "
+ "positive number, instead got "
<< connect_timeout);
}
} catch (const boost::bad_lexical_cast& ex) {
- isc_throw(DbOperationError, "Invalid connect timeout, castable to "
- "int expected, instead got \""
- << connect_timeout << "\", "
- << ex.what());
+ isc_throw(DbOperationError,
+ "CqlConnection::openDatabase(): invalid connect timeout, "
+ "expected castable to int, instead got \""
+ << connect_timeout << "\", " << ex.what());
}
cass_cluster_set_connect_timeout(cluster_, connect_timeout_number);
}
if (request_timeout) {
- int request_timeout_number;
+ int32_t request_timeout_number;
try {
- request_timeout_number = boost::lexical_cast<int>(request_timeout);
+ request_timeout_number =
+ boost::lexical_cast<int32_t>(request_timeout);
if (request_timeout_number < 0) {
isc_throw(DbOperationError,
- "Invalid request timeout, positive number expected, "
- "instead got "
+ "CqlConnection::openDatabase(): "
+ "invalid request timeout, expected "
+ "positive number, instead got "
<< request_timeout);
}
} catch (const boost::bad_lexical_cast& ex) {
- isc_throw(DbOperationError, "Invalid request timeout, castable to "
- "int expected, instead got \""
- << request_timeout << "\", "
- << ex.what());
+ isc_throw(DbOperationError,
+ "CqlConnection::openDatabase(): invalid request timeout, "
+ "expected castable to int, instead got \""
+ << request_timeout << "\", " << ex.what());
}
cass_cluster_set_request_timeout(cluster_, request_timeout_number);
}
if (tcp_keepalive) {
- int tcp_keepalive_number;
+ int32_t tcp_keepalive_number;
try {
- tcp_keepalive_number = boost::lexical_cast<int>(tcp_keepalive);
+ tcp_keepalive_number = boost::lexical_cast<int32_t>(tcp_keepalive);
if (tcp_keepalive_number < 0) {
isc_throw(DbOperationError,
- "Invalid TCP keepalive, positive number expected, "
- "instead got "
+ "CqlConnection::openDatabase(): "
+ "invalid TCP keepalive, expected "
+ "positive number, instead got "
<< tcp_keepalive);
}
} catch (const boost::bad_lexical_cast& ex) {
- isc_throw(
- DbOperationError,
- "Invalid TCP keepalive, castable to int expected, instead got "
- "\"" << tcp_keepalive
- << "\", " << ex.what());
+ isc_throw(DbOperationError,
+ "CqlConnection::openDatabase(): invalid TCP keepalive, "
+ "expected castable to int, instead got \""
+ << tcp_keepalive << "\", " << ex.what());
}
cass_cluster_set_tcp_keepalive(cluster_, cass_true,
tcp_keepalive_number);
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");
+ const std::string error =
+ checkFutureError("CqlConnection::openDatabase(): "
+ "cass_session_connect_keyspace() != CASS_OK",
+ connect_future);
rc = cass_future_error_code(connect_future);
cass_future_free(connect_future);
if (rc != CASS_OK) {
cluster_ = NULL;
isc_throw(DbOpenError, error);
}
+
+ // Get keyspace meta.
+ schema_meta_ = cass_session_get_schema_meta(session_);
+ keyspace_meta_ = cass_schema_meta_keyspace_by_name(schema_meta_, keyspace);
+ if (!keyspace_meta_) {
+ isc_throw(DbOpenError, "CqlConnection::openDatabase(): "
+ "!cass_schema_meta_keyspace_by_name()");
+ }
}
void
-CqlConnection::prepareStatements(CqlTaggedStatement* statements) {
+CqlConnection::prepareStatements(StatementMap& statements) {
CassError rc = CASS_OK;
- 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_;
+ for (StatementMapEntry it : statements) {
+ CqlTaggedStatement& tagged_statement = it.second;
+ if (statements_.find(tagged_statement.name_) != statements_.end()) {
+ isc_throw(DbOperationError,
+ "CqlConnection::prepareStatements(): "
+ "duplicate statement with name "
+ << tagged_statement.name_);
+ }
- CassFuture* future = cass_session_prepare(session_, query);
+ CassFuture* future =
+ cass_session_prepare(session_, tagged_statement.text_);
cass_future_wait(future);
- std::string error;
- checkStatementError(error, future, i, "could not prepare statement");
+ const std::string error =
+ checkFutureError("CqlConnection::prepareStatements():"
+ " cass_session_prepare() != CASS_OK",
+ future, tagged_statement.name_);
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);
}
+ tagged_statement.prepared_statement_ = cass_future_get_prepared(future);
+ statements_.insert(it);
cass_future_free(future);
}
}
void
CqlConnection::startTransaction() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_CQL_BEGIN_TRANSACTION);
- // No-op
+ DHCPSRV_CQL_CONNECTION_BEGIN_TRANSACTION);
}
void
CqlConnection::commit() {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT);
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_CONNECTION_COMMIT);
}
void
CqlConnection::rollback() {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK);
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_CONNECTION_ROLLBACK);
}
-void
-CqlConnection::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 statement " << 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 statement " << tagged_statements_[stindex].name_
- << ". Future error: " << errorMessage
- << ". Error description: " << cass_error_desc(rc);
- }
- error = stream.str();
-}
+const std::string
+CqlConnection::checkFutureError(const std::string& what,
+ CassFuture* future,
+ StatementTag statement_tag /* = NULL */) {
+ CassError cass_error = cass_future_error_code(future);
+ const char* error_message;
+ size_t error_message_size;
+ cass_future_error_message(future, &error_message, &error_message_size);
-void
-CqlConnection::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 << ". Future error: " << errorMessage
- << ". Error description: " << cass_error_desc(rc);
+ if (statement_tag && std::strlen(statement_tag) > 0) {
+ // future is from cass_session_execute() call.
+ stream << "Statement ";
+ stream << statement_tag;
+ } else {
+ // future is from cass_session_*() call.
+ stream << "Session action ";
}
- error = stream.str();
-}
-
-CqlTransaction::CqlTransaction(CqlConnection& conn)
- : conn_(conn), committed_(false) {
- conn_.startTransaction();
-}
-
-CqlTransaction::~CqlTransaction() {
- // Rollback if commit() wasn't explicitly called.
- if (!committed_) {
- conn_.rollback();
+ if (cass_error == CASS_OK) {
+ stream << " executed succesfully.";
+ } else {
+ stream << " failed, Kea error: " << what
+ << ", Cassandra error code: " << cass_error_desc(cass_error)
+ << ", Cassandra future error: " << error_message;
}
-}
-
-void
-CqlTransaction::commit() {
- conn_.commit();
- committed_ = true;
+ return stream.str();
}
} // namespace dhcp
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
//
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#define CQL_CONNECTION_H
#include <dhcpsrv/database_connection.h>
-#include <dhcpsrv/dhcpsrv_log.h>
#include <cassandra.h>
-#include <boost/scoped_ptr.hpp>
-
-#include <inttypes.h>
-
+#include <cstring>
#include <map>
+#include <memory>
#include <string>
+#include <unordered_map>
+#include <utility>
#include <vector>
namespace isc {
namespace dhcp {
-/// @brief Defines a single statement
+/// @brief Pair containing major and minor versions
+typedef std::pair<uint32_t, uint32_t> VersionPair;
+
+/// @brief Statement index representing the statement name
+typedef char const* const StatementTag;
+
+/// @brief Define CQL backend version: 2.3
+/// @{
+constexpr uint32_t CQL_DRIVER_VERSION_MAJOR = CASS_VERSION_MAJOR;
+constexpr uint32_t CQL_DRIVER_VERSION_MINOR = CASS_VERSION_MINOR;
+/// @}
+
+/// Define CQL schema version: 2.0
+/// @{
+constexpr uint32_t CQL_SCHEMA_VERSION_MAJOR = 2u;
+constexpr uint32_t CQL_SCHEMA_VERSION_MINOR = 0u;
+/// @}
+
+/// @brief Defines a single statement or query
///
-/// @param params_ parameter names
/// @param name_ short description of the query
/// @param text_ text representation of the actual query
+/// @param prepared_statement_ internal Cassandra object representing the
+/// prepared statement
+/// @param is_raw_statement_ shows if statement should be executed rawly or with
+/// binds
struct CqlTaggedStatement {
- const char** params_;
- const char* name_;
- const char* text_;
+ StatementTag name_;
+ char const* const text_;
+ const CassPrepared* prepared_statement_;
+ bool is_raw_statement_;
+
+ /// @brief Constructor
+ CqlTaggedStatement(StatementTag name, char const* const text)
+ : name_(name), text_(text), prepared_statement_(NULL),
+ is_raw_statement_(false) {
+ }
+
+ /// @brief Constructor
+ CqlTaggedStatement(StatementTag name,
+ char const* const text,
+ bool const& is_raw_statement)
+ : name_(name), text_(text), prepared_statement_(NULL),
+ is_raw_statement_(is_raw_statement) {
+ }
};
-// Define CQL backend version: 2.3
-const uint32_t CQL_DRIVER_VERSION_MAJOR = CASS_VERSION_MAJOR;
-const uint32_t CQL_DRIVER_VERSION_MINOR = CASS_VERSION_MINOR;
+/// @brief Hash function for StatementMap keys
+///
+/// Delegates to std::hash<std::string>.
+struct StatementTagHash {
+ size_t operator()(StatementTag const& key) const {
+ return std::hash<std::string>{}(std::string(key));
+ }
+};
-/// Define CQL schema version: 1.0
-const uint32_t CQL_SCHEMA_VERSION_MAJOR = 1;
-const uint32_t CQL_SCHEMA_VERSION_MINOR = 0;
+/// @brief Equality function for StatementMap keys
+struct StatementTagEqual {
+ bool operator()(StatementTag const& lhs, StatementTag const& rhs) const {
+ return std::strcmp(lhs, rhs) == 0;
+ }
+};
+
+/// @brief Contains all statements.
+typedef std::unordered_map<StatementTag,
+ CqlTaggedStatement,
+ StatementTagHash,
+ StatementTagEqual>
+ StatementMap;
+
+typedef std::pair<StatementTag, CqlTaggedStatement> StatementMapEntry;
/// @brief Common CQL connector pool
///
/// Provides common operations for the Cassandra database connection used by
-/// CqlLeaseMgr, CqlHostDataSource and CqlSrvConfigMgr. Manages the connection
-/// to the Cassandra database and preparing of compiled statements. Its fields
-/// are public because they are used (both set and retrieved) in classes that
+/// CqlLeaseMgr, CqlHostDataSource and CqlSrvConfigMgr. Manages the
+/// connection
+/// to the Cassandra database and preparing of compiled statements. Its
+/// fields
+/// are public because they are used (both set and retrieved) in classes
+/// that
/// use instances of CqlConnection.
class CqlConnection : public DatabaseConnection {
public:
/// has failed
/// @throw isc::InvalidParameter if there is an invalid access in the
/// vector. This represents an internal error within the code.
- void prepareStatements(CqlTaggedStatement* statements);
+ void prepareStatements(StatementMap& statements);
/// @brief Open database
///
void startTransaction();
/// @brief Commit Transactions
- ///
- /// This is a no-op for Cassandra.
virtual void commit();
/// @brief Rollback Transactions
- ///
- /// This is a no-op for Cassandra.
virtual void rollback();
/// @brief Check for errors
///
/// Check for errors on the current database operation.
- void checkStatementError(std::string& error,
- CassFuture* future,
- uint32_t stindex,
- const char* what) const;
+ static const std::string
+ checkFutureError(const std::string& what,
+ CassFuture* future,
+ StatementTag statement_tag = NULL);
- /// @brief Check for errors
- ///
- /// Check for errors on the current database operation.
- void checkStatementError(std::string& error,
- CassFuture* future,
- const char* what) const;
+ /// @brief Pointer to external array of tagged statements containing
+ /// statement name, array of names of bind parameters and text query
+ StatementMap statements_;
/// @brief CQL connection handle
CassCluster* cluster_;
/// @brief CQL session handle
CassSession* session_;
- /// @brief CQL consistency enabled
- bool force_consistency_;
-
/// @brief CQL consistency
CassConsistency consistency_;
- /// @brief CQL prepared statements - used for faster statement execution
- /// using bind functionality
- std::vector<const CassPrepared*> statements_;
-
- /// @brief Pointer to external array of tagged statements containing
- /// statement name, array of names of bind parameters and text query
- CqlTaggedStatement* tagged_statements_;
-};
-
-/// @brief RAII object representing CQL transaction.
-///
-/// An instance of this class should be created in a scope where multiple
-/// INSERT statements should be executed within a single transaction. The
-/// transaction is started when the constructor of this class is invoked.
-/// The transaction is ended when the @ref CqlTransaction::commit is
-/// explicitly called or when the instance of this class is destroyed.
-/// The @ref CqlTransaction::commit commits changes to the database
-/// and the changes remain in the database when the instance of the
-/// class is destroyed. If the class instance is destroyed before the
-/// @ref CqlTransaction::commit is called, the transaction is rolled
-/// back. The rollback on destruction guarantees that partial data is
-/// not stored in the database when there is an error during any
-/// of the operations belonging to a transaction.
-class CqlTransaction : public boost::noncopyable {
-public:
- /// @brief Constructor
- ///
- /// Starts transaction by making a "START TRANSACTION" query.
- ///
- /// @param conn CQL connection to use for the transaction. This connection
- /// will be later used to commit or rollback changes.
- ///
- /// @throw DbOperationError if "START TRANSACTION" query fails.
- explicit CqlTransaction(CqlConnection& conn);
-
- /// @brief Destructor
- ///
- /// Rolls back the transaction if changes haven't been committed.
- ~CqlTransaction();
+ // @brief Schema meta information, used for UDTs
+ const CassSchemaMeta* schema_meta_;
- /// @brief Commits transaction
- ///
- /// Calls @ref CqlConnection::commit()..
- void commit();
+ /// @brief Keyspace meta information, used for UDTs
+ const CassKeyspaceMeta* keyspace_meta_;
-private:
- /// @brief Holds reference to the CQL database connection.
- CqlConnection& conn_;
-
- /// @brief Boolean flag indicating if the transaction has been committed.
- ///
- /// This flag is used in the class destructor to assess if the
- /// transaction should be rolled back.
- bool committed_;
+ /// @brief CQL consistency enabled
+ bool force_consistency_;
};
+typedef std::shared_ptr<CqlConnection> CqlConnectionPtr;
+
} // namespace dhcp
} // namespace isc
-// Copyright (C) 2016 Deutsche Telekom AG.
+// Copyright (C) 2016-2017 Deutsche Telekom AG.
//
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#include <config.h>
-#include <dhcpsrv/cql_exchange.h>
-
#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/cql_exchange.h>
#include <dhcpsrv/db_exceptions.h>
+#include <dhcpsrv/sql_common.h>
+
+namespace isc {
+namespace dhcp {
-/// @name CqlBind auxiliary functions for binding data into Cassandra format:
+/// @brief Macro to return directly from caller function
+#define KEA_CASS_CHECK(cass_error) \
+ { \
+ if (cass_error != CASS_OK) { \
+ return cass_error; \
+ } \
+ }
+
+struct ExchangeDataTypeHash {
+public:
+ size_t operator()(const ExchangeDataType& key) const {
+ return std::hash<size_t>{}(static_cast<size_t>(key));
+ }
+};
+
+typedef std::unordered_map<ExchangeDataType, CqlFunction, ExchangeDataTypeHash>
+ CqlFunctionMap;
+extern CqlFunctionMap CQL_FUNCTIONS;
+
+/// @brief hash function for CassTypeMap
+///
+/// Required by c++ versions 5 and below.
+///
+/// @param key being hashed
+///
+/// @return hash value
+std::size_t
+hash_value(const CassValueType& key) {
+ return key;
+}
+
+/// @brief Map types used to determine exchange type
/// @{
+typedef std::unordered_map<std::type_index, ExchangeDataType> AnyTypeMap;
+// Declare uint8_t as key here for compatibility with c++-5. Ideally, it would
+// be CassValueType
+typedef std::unordered_map<uint8_t, ExchangeDataType> CassTypeMap;
+/// @}
-/// @todo These void* cast are unsafe. See ticket #4525.
+/// @brief Maps C++ type to exchange type
+static AnyTypeMap ANY_TYPE_MAP = {
+ {typeid(NULL), EXCHANGE_DATA_TYPE_NONE},
+ {typeid(cass_bool_t*), EXCHANGE_DATA_TYPE_BOOL},
+ {typeid(cass_int8_t*), EXCHANGE_DATA_TYPE_INT8},
+ {typeid(cass_int16_t*), EXCHANGE_DATA_TYPE_INT16},
+ {typeid(cass_int32_t*), EXCHANGE_DATA_TYPE_INT32},
+ {typeid(cass_int64_t*), EXCHANGE_DATA_TYPE_INT64},
+ {typeid(std::string*), EXCHANGE_DATA_TYPE_STRING},
+ {typeid(CassBlob*), EXCHANGE_DATA_TYPE_BYTES},
+ {typeid(CassUuid*), EXCHANGE_DATA_TYPE_UUID},
+ {typeid(Udt*), EXCHANGE_DATA_TYPE_UDT},
+ {typeid(Collection*), EXCHANGE_DATA_TYPE_COLLECTION}};
+
+/// @brief Maps Cassandra type to exchange type
+static CassTypeMap CASS_TYPE_MAP = {
+ {CASS_VALUE_TYPE_CUSTOM, EXCHANGE_DATA_TYPE_UDT},
+ {CASS_VALUE_TYPE_ASCII, EXCHANGE_DATA_TYPE_STRING},
+ {CASS_VALUE_TYPE_BIGINT, EXCHANGE_DATA_TYPE_INT64},
+ {CASS_VALUE_TYPE_BLOB, EXCHANGE_DATA_TYPE_BYTES},
+ {CASS_VALUE_TYPE_BOOLEAN, EXCHANGE_DATA_TYPE_BOOL},
+ {CASS_VALUE_TYPE_COUNTER, EXCHANGE_DATA_TYPE_INT32},
+ {CASS_VALUE_TYPE_DECIMAL, EXCHANGE_DATA_TYPE_INT32},
+ {CASS_VALUE_TYPE_DOUBLE, EXCHANGE_DATA_TYPE_INT64},
+ {CASS_VALUE_TYPE_FLOAT, EXCHANGE_DATA_TYPE_INT32},
+ {CASS_VALUE_TYPE_INT, EXCHANGE_DATA_TYPE_INT32},
+ {CASS_VALUE_TYPE_TEXT, EXCHANGE_DATA_TYPE_STRING},
+ {CASS_VALUE_TYPE_TIMESTAMP, EXCHANGE_DATA_TYPE_INT64},
+ {CASS_VALUE_TYPE_UUID, EXCHANGE_DATA_TYPE_UUID},
+ {CASS_VALUE_TYPE_VARCHAR, EXCHANGE_DATA_TYPE_STRING},
+ {CASS_VALUE_TYPE_VARINT, EXCHANGE_DATA_TYPE_INT32},
+ {CASS_VALUE_TYPE_TIMEUUID, EXCHANGE_DATA_TYPE_INT64},
+ {CASS_VALUE_TYPE_INET, EXCHANGE_DATA_TYPE_NONE},
+ {CASS_VALUE_TYPE_DATE, EXCHANGE_DATA_TYPE_INT64},
+ {CASS_VALUE_TYPE_TIME, EXCHANGE_DATA_TYPE_INT64},
+ {CASS_VALUE_TYPE_SMALL_INT, EXCHANGE_DATA_TYPE_INT16},
+ {CASS_VALUE_TYPE_TINY_INT, EXCHANGE_DATA_TYPE_INT8},
+ {CASS_VALUE_TYPE_LIST, EXCHANGE_DATA_TYPE_COLLECTION},
+ {CASS_VALUE_TYPE_MAP, EXCHANGE_DATA_TYPE_COLLECTION},
+ {CASS_VALUE_TYPE_SET, EXCHANGE_DATA_TYPE_COLLECTION},
+ {CASS_VALUE_TYPE_UDT, EXCHANGE_DATA_TYPE_UDT},
+ {CASS_VALUE_TYPE_TUPLE, EXCHANGE_DATA_TYPE_UDT}};
+
+/// @brief Udt method implementations
+/// @{
+Udt::Udt(const CqlConnection& connection, const std::string& name)
+ : AnyArray(), connection_(connection), name_(name) {
+ // Create type.
+ cass_data_type_ = cass_keyspace_meta_user_type_by_name(
+ connection_.keyspace_meta_, name_.c_str());
+ if (!cass_data_type_) {
+ isc_throw(DbOperationError,
+ "Udt::Udt(): UDT " << name_ << " does not exist ");
+ }
+ // Create container.
+ cass_user_type_ = cass_user_type_new_from_data_type(cass_data_type_);
+ if (!cass_user_type_) {
+ isc_throw(DbOperationError,
+ "Udt::Udt(): Type " << name_
+ << " is not a UDT as expected. ");
+ }
+}
+
+Udt::~Udt() {
+ // Bug: it seems that if there is no call to
+ // cass_user_type_set_*(cass_user_type_), then
+ // cass_user_type_free(cass_user_type_) might SIGSEGV, so we never
+ // free. Udt objects should have application scope though.
+ // cass_user_type_free(cass_user_type_);
+}
+/// @}
+
+/// @brief AnyArray method implementations
+/// @{
+void
+AnyArray::add(const boost::any& value) {
+ push_back(value);
+}
+
+void
+AnyArray::remove(const size_t& index) {
+ if (size() <= index) {
+ isc_throw(BadValue,
+ "AnyArray::remove(): index "
+ << index << " out of bounds: [0, " << (size() - 1)
+ << "]");
+ }
+ erase(begin() + index);
+}
+/// @}
+
+/// @name CqlBind functions for binding data into Cassandra format for
+/// insertion:
+/// @{
static CassError
-CqlBindNone(CassStatement* statement, size_t index, void*) {
+CqlBindNone(const boost::any& /* value */,
+ const size_t& index,
+ CassStatement* statement) {
return cass_statement_bind_null(statement, index);
}
static CassError
-CqlBindBool(CassStatement* statement, size_t index, void* value) {
+CqlBindBool(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
return cass_statement_bind_bool(statement, index,
- *(static_cast<cass_bool_t*>(value)));
+ *boost::any_cast<cass_bool_t*>(value));
}
static CassError
-CqlBindInt32(CassStatement* statement, size_t index, void* value) {
- return cass_statement_bind_int32(statement, index,
- *(static_cast<cass_int32_t*>(value)));
+CqlBindInt8(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
+ return cass_statement_bind_int8(statement, index,
+ *boost::any_cast<cass_int8_t*>(value));
}
static CassError
-CqlBindInt64(CassStatement* statement, size_t index, void* value) {
- return cass_statement_bind_int64(statement, index,
- *(static_cast<cass_int64_t*>(value)));
+CqlBindInt16(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
+ return cass_statement_bind_int16(statement, index,
+ *boost::any_cast<cass_int16_t*>(value));
+}
+
+static CassError
+CqlBindInt32(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
+ return cass_statement_bind_int32(statement, index,
+ *boost::any_cast<cass_int32_t*>(value));
}
static CassError
-CqlBindTimestamp(CassStatement* statement, size_t index, void* value) {
+CqlBindInt64(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
return cass_statement_bind_int64(statement, index,
- *(static_cast<cass_int64_t*>(value)));
+ *boost::any_cast<cass_int64_t*>(value));
}
static CassError
-CqlBindString(CassStatement* statement, size_t index, void* value) {
+CqlBindString(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
return cass_statement_bind_string(
- statement, index, static_cast<std::string*>(value)->c_str());
+ statement, index, boost::any_cast<std::string*>(value)->c_str());
}
static CassError
-CqlBindBytes(CassStatement* statement, size_t index, void* value) {
- return cass_statement_bind_bytes(
- statement, index, static_cast<std::vector<cass_byte_t>*>(value)->data(),
- static_cast<std::vector<cass_byte_t>*>(value)->size());
+CqlBindBytes(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
+ CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
+ return cass_statement_bind_bytes(statement, index, blob_value->data(),
+ blob_value->size());
}
static CassError
-CqlBindUuid(CassStatement* statement, size_t index, void* value) {
+CqlBindUuid(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
return cass_statement_bind_uuid(statement, index,
- *static_cast<CassUuid*>(value));
+ *boost::any_cast<CassUuid*>(value));
+}
+
+static CassError
+CqlBindUdt(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
+ Udt* udt = boost::any_cast<Udt*>(value);
+
+ size_t i = 0u;
+ for (boost::any& element : *udt) {
+ try {
+ KEA_CASS_CHECK(
+ CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
+ element, i, udt->cass_user_type_));
+ } catch (const boost::bad_any_cast& exception) {
+ isc_throw(DbOperationError,
+ "CqlCommon::udtSetData(): "
+ << exception.what() << " when binding parameter "
+ << " of type " << element.type().name()
+ << "in UDT with function CQL_FUNCTIONS["
+ << exchangeType(element) << "].cqlUdtSetFunction_");
+ }
+ ++i;
+ }
+
+ return cass_statement_bind_user_type(statement, index,
+ udt->cass_user_type_);
+}
+
+static CassError
+CqlBindCollection(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement) {
+ Collection* elements = boost::any_cast<Collection*>(value);
+
+ CassCollection* collection =
+ cass_collection_new(CASS_COLLECTION_TYPE_SET, elements->size());
+
+ for (boost::any& element : *elements) {
+ ExchangeDataType type = exchangeType(element);
+ KEA_CASS_CHECK(CQL_FUNCTIONS[type].cqlCollectionAppendFunction_(
+ element, collection));
+ }
+
+ const CassError cass_error =
+ cass_statement_bind_collection(statement, index, collection);
+ cass_collection_free(collection);
+
+ return cass_error;
+}
+/// @}
+
+/// @name CqlUdtSet functions for binding data into Cassandra format for
+/// insertion of a UDT:
+/// @{
+static CassError
+CqlUdtSetNone(const boost::any& /* udt_member */,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_null(cass_user_type, position);
+}
+
+static CassError
+CqlUdtSetBool(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_bool(cass_user_type, position,
+ *boost::any_cast<cass_bool_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt8(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_int8(cass_user_type, position,
+ *boost::any_cast<cass_int8_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt16(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_int16(
+ cass_user_type, position, *boost::any_cast<cass_int16_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt32(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_int32(
+ cass_user_type, position, *boost::any_cast<cass_int32_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetInt64(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_int64(
+ cass_user_type, position, *boost::any_cast<cass_int64_t*>(udt_member));
+}
+
+static CassError
+CqlUdtSetString(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_string(
+ cass_user_type, position,
+ boost::any_cast<std::string*>(udt_member)->c_str());
+}
+
+static CassError
+CqlUdtSetBytes(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ CassBlob* blob_value = boost::any_cast<CassBlob*>(udt_member);
+ return cass_user_type_set_bytes(cass_user_type, position,
+ blob_value->data(), blob_value->size());
+}
+
+static CassError
+CqlUdtSetUuid(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_uuid(cass_user_type, position,
+ *boost::any_cast<CassUuid*>(udt_member));
+}
+
+static CassError
+CqlUdtSetUdt(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_user_type(
+ cass_user_type, position,
+ boost::any_cast<Udt*>(udt_member)->cass_user_type_);
+}
+
+static CassError
+CqlUdtSetCollection(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type) {
+ return cass_user_type_set_collection(
+ cass_user_type, position, boost::any_cast<CassCollection*>(udt_member));
}
/// @}
+/// @name CqlCollectionAppend functions for binding data into Cassandra format
+/// for insertion of a collection:
+/// @{
static CassError
-CqlGetNone(const CassValue*, void*) {
+CqlCollectionAppendNone(const boost::any& /* value */,
+ CassCollection* /* collection */) {
return CASS_OK;
}
static CassError
-CqlGetBool(const CassValue* value, void* data) {
- return cass_value_get_bool(value, static_cast<cass_bool_t*>(data));
+CqlCollectionAppendBool(const boost::any& value, CassCollection* collection) {
+ return cass_collection_append_bool(collection,
+ *boost::any_cast<cass_bool_t*>(value));
}
static CassError
-CqlGetInt32(const CassValue* value, void* data) {
- return cass_value_get_int32(value, static_cast<cass_int32_t*>(data));
+CqlCollectionAppendInt8(const boost::any& value, CassCollection* collection) {
+ return cass_collection_append_int8(collection,
+ *boost::any_cast<cass_int8_t*>(value));
}
static CassError
-CqlGetInt64(const CassValue* value, void* data) {
- return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+CqlCollectionAppendInt16(const boost::any& value, CassCollection* collection) {
+ return cass_collection_append_int16(collection,
+ *boost::any_cast<cass_int16_t*>(value));
}
static CassError
-CqlGetTimestamp(const CassValue* value, void* data) {
- return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+CqlCollectionAppendInt32(const boost::any& value, CassCollection* collection) {
+ return cass_collection_append_int32(collection,
+ *boost::any_cast<cass_int32_t*>(value));
}
static CassError
-CqlGetString(const CassValue* value, void* data) {
- const char* dataValue;
- size_t sizeValue;
- CassError cassError = cass_value_get_string(
- value, static_cast<const char**>(&dataValue), &sizeValue);
- static_cast<std::string*>(data)->assign(dataValue, dataValue + sizeValue);
- return cassError;
+CqlCollectionAppendInt64(const boost::any& value, CassCollection* collection) {
+ return cass_collection_append_int64(collection,
+ *boost::any_cast<cass_int64_t*>(value));
}
static CassError
-CqlGetBytes(const CassValue* value, void* data) {
- const cass_byte_t* dataValue;
- size_t sizeValue;
- CassError cassError = cass_value_get_bytes(
- value, static_cast<const cass_byte_t**>(&dataValue), &sizeValue);
- static_cast<std::vector<cass_byte_t>*>(data)->assign(dataValue,
- dataValue + sizeValue);
- return cassError;
+CqlCollectionAppendString(const boost::any& value, CassCollection* collection) {
+ return cass_collection_append_string(
+ collection, boost::any_cast<std::string*>(value)->c_str());
}
static CassError
-CqlGetUuid(const CassValue* value, void* data) {
- return cass_value_get_uuid(value, static_cast<CassUuid*>(data));
+CqlCollectionAppendBytes(const boost::any& value, CassCollection* collection) {
+ CassBlob* blob_value = boost::any_cast<CassBlob*>(value);
+ return cass_collection_append_bytes(collection, blob_value->data(),
+ blob_value->size());
}
-namespace isc {
-namespace dhcp {
+static CassError
+CqlCollectionAppendUuid(const boost::any& value, CassCollection* collection) {
+ return cass_collection_append_uuid(collection,
+ *boost::any_cast<CassUuid*>(value));
+}
-struct CqlFunctionData CqlFunctions[] = {
- {CqlBindNone, CqlGetNone}, {CqlBindBool, CqlGetBool},
- {CqlBindInt32, CqlGetInt32}, {CqlBindInt64, CqlGetInt64},
- {CqlBindTimestamp, CqlGetTimestamp}, {CqlBindString, CqlGetString},
- {CqlBindBytes, CqlGetBytes}, {CqlBindUuid, CqlGetUuid}};
+static CassError
+CqlCollectionAppendUdt(const boost::any& value, CassCollection* collection) {
+ Udt* udt = boost::any_cast<Udt*>(value);
+ size_t i = 0u;
+ for (boost::any& element : *udt) {
+ KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(element)].cqlUdtSetFunction_(
+ element, i, udt->cass_user_type_));
+ ++i;
+ }
+ return cass_collection_append_user_type(collection, udt->cass_user_type_);
+}
-ExchangeDataType
-CqlCommon::getDataType(const uint32_t stindex,
- int pindex,
- const SqlExchange& exchange,
- const CqlTaggedStatement* tagged_statements) {
- if (tagged_statements[stindex].params_ &&
- tagged_statements[stindex].params_[pindex]) {
- const ExchangeColumnInfoContainerName& idx =
- exchange.parameters_.get<1>();
- const ExchangeColumnInfoContainerNameRange& range =
- idx.equal_range(tagged_statements[stindex].params_[pindex]);
- if (std::distance(range.first, range.second) > 0) {
- return (*range.first)->type_;
+static CassError
+CqlCollectionAppendCollection(const boost::any& value,
+ CassCollection* collection) {
+ return cass_collection_append_collection(
+ collection, boost::any_cast<CassCollection*>(value));
+}
+// @}
+
+/// @name CqlGet functions for retrieving data of the proper Cassandra format:
+/// @{
+static CassError
+CqlGetNone(const boost::any& /* data */, const CassValue* /* value */) {
+ return CASS_OK;
+}
+
+static CassError
+CqlGetBool(const boost::any& data, const CassValue* value) {
+ return cass_value_get_bool(value, boost::any_cast<cass_bool_t*>(data));
+}
+
+static CassError
+CqlGetInt8(const boost::any& data, const CassValue* value) {
+ return cass_value_get_int8(value, boost::any_cast<cass_int8_t*>(data));
+}
+
+static CassError
+CqlGetInt16(const boost::any& data, const CassValue* value) {
+ return cass_value_get_int16(value, boost::any_cast<cass_int16_t*>(data));
+}
+
+static CassError
+CqlGetInt32(const boost::any& data, const CassValue* value) {
+ return cass_value_get_int32(value, boost::any_cast<cass_int32_t*>(data));
+}
+
+static CassError
+CqlGetInt64(const boost::any& data, const CassValue* value) {
+ return cass_value_get_int64(value, boost::any_cast<cass_int64_t*>(data));
+}
+
+static CassError
+CqlGetString(const boost::any& data, const CassValue* value) {
+ char const* data_value;
+ size_t size_value;
+ CassError cass_error = cass_value_get_string(
+ value, static_cast<char const**>(&data_value), &size_value);
+ boost::any_cast<std::string*>(data)->assign(data_value,
+ data_value + size_value);
+ return cass_error;
+}
+
+static CassError
+CqlGetBytes(const boost::any& data, const CassValue* value) {
+ const cass_byte_t* data_value;
+ size_t size_value;
+ CassError cass_error = cass_value_get_bytes(
+ value, static_cast<const cass_byte_t**>(&data_value), &size_value);
+ boost::any_cast<CassBlob*>(data)->assign(data_value,
+ data_value + size_value);
+ return cass_error;
+}
+
+static CassError
+CqlGetUuid(const boost::any& data, const CassValue* value) {
+ return cass_value_get_uuid(value, boost::any_cast<CassUuid*>(data));
+}
+
+static CassError
+CqlGetUdt(const boost::any& data, const CassValue* value) {
+ Udt* udt = boost::any_cast<Udt*>(data);
+
+ CassIterator* fields = cass_iterator_fields_from_user_type(value);
+ if (!fields) {
+ isc_throw(DbOperationError, "CqlGetUdt(): column is not a UDT");
+ }
+ Udt::const_iterator it = udt->begin();
+ while (cass_iterator_next(fields)) {
+ const CassValue* field_value =
+ cass_iterator_get_user_type_field_value(fields);
+ if (cass_value_is_null(field_value)) {
+ isc_throw(DbOperationError,
+ "CqlGetUdt(): null value returned in UDT");
}
+ const CassValueType& type = cass_value_type(field_value);
+ KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
+ *it, field_value));
+ ++it;
+ // If cqlGetFunction_() returns != CASS_OK, don't
+ // cass_iterator_free(items_iterator) because we're returning from this
+ // function and throwing from the callee.
}
- return EXCHANGE_DATA_TYPE_NONE;
+ cass_iterator_free(fields);
+ return CASS_OK;
}
-void
-CqlCommon::bindData(CassStatement* statement,
- uint32_t stindex,
- const CqlDataArray& data,
- const SqlExchange& exchange,
- const CqlTaggedStatement* tagged_statements) {
- if (!tagged_statements[stindex].params_) {
- return;
- }
- for (int i = 0; tagged_statements[stindex].params_[i]; i++) {
- ExchangeDataType type =
- CqlCommon::getDataType(stindex, i, exchange, tagged_statements);
- if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
- isc_throw(BadValue, "index " << stindex << " out of bounds");
+static CassError
+CqlGetCollection(const boost::any& data, const CassValue* value) {
+ Collection* collection = boost::any_cast<Collection*>(data);
+ BOOST_ASSERT(collection->size() == 1);
+
+ // @todo: Create a copy of the underlying object rather than referencing to
+ // it.
+ boost::any underlying_object = *collection->begin();
+
+ collection->clear();
+
+ CassIterator* items = cass_iterator_from_collection(value);
+ if (!items) {
+ isc_throw(DbOperationError,
+ "CqlGetCollection(): column is not a collection");
+ }
+ while (cass_iterator_next(items)) {
+ const CassValue* item_value = cass_iterator_get_value(items);
+ if (cass_value_is_null(item_value)) {
+ isc_throw(DbOperationError,
+ "CqlGetCollection(): null value returned in collection");
}
- CqlFunctions[type].cqlBindFunction_(statement, i, data.values_[i]);
+ const CassValueType& type = cass_value_type(item_value);
+
+ collection->push_back(underlying_object);
+ KEA_CASS_CHECK(CQL_FUNCTIONS[exchangeType(type)].cqlGetFunction_(
+ *collection->rbegin(), item_value));
+ // If cqlGetFunction_() returns != CASS_OK, don't
+ // cass_iterator_free(items_iterator) because we're returning from this
+ // function and throwing from the callee.
+ }
+ cass_iterator_free(items);
+ return CASS_OK;
+}
+/// @}
+
+/// @brief Functions used to interface with the Cassandra C++ driver
+CqlFunctionMap CQL_FUNCTIONS = //
+ {{EXCHANGE_DATA_TYPE_NONE,
+ {CqlBindNone, CqlUdtSetNone, CqlCollectionAppendNone, CqlGetNone}},
+ {EXCHANGE_DATA_TYPE_BOOL,
+ {CqlBindBool, CqlUdtSetBool, CqlCollectionAppendBool, CqlGetBool}},
+ {EXCHANGE_DATA_TYPE_INT8,
+ {CqlBindInt8, CqlUdtSetInt8, CqlCollectionAppendInt8, CqlGetInt8}},
+ {EXCHANGE_DATA_TYPE_INT16,
+ {CqlBindInt16, CqlUdtSetInt16, CqlCollectionAppendInt16, CqlGetInt16}},
+ {EXCHANGE_DATA_TYPE_INT32,
+ {CqlBindInt32, CqlUdtSetInt32, CqlCollectionAppendInt32, CqlGetInt32}},
+ {EXCHANGE_DATA_TYPE_INT64,
+ {CqlBindInt64, CqlUdtSetInt64, CqlCollectionAppendInt64, CqlGetInt64}},
+ {EXCHANGE_DATA_TYPE_STRING,
+ {CqlBindString, CqlUdtSetString, CqlCollectionAppendString,
+ CqlGetString}},
+ {EXCHANGE_DATA_TYPE_BYTES,
+ {CqlBindBytes, CqlUdtSetBytes, CqlCollectionAppendBytes, CqlGetBytes}},
+ {EXCHANGE_DATA_TYPE_UUID,
+ {CqlBindUuid, CqlUdtSetUuid, CqlCollectionAppendUuid, CqlGetUuid}},
+ {EXCHANGE_DATA_TYPE_UDT,
+ {CqlBindUdt, CqlUdtSetUdt, CqlCollectionAppendUdt, CqlGetUdt}},
+ {EXCHANGE_DATA_TYPE_COLLECTION,
+ {CqlBindCollection, CqlUdtSetCollection, CqlCollectionAppendCollection,
+ CqlGetCollection}}};
+
+ExchangeDataType
+exchangeType(const boost::any& object) {
+ const std::type_index type = object.type();
+ AnyTypeMap::const_iterator exchange_type_it = ANY_TYPE_MAP.find(type);
+ if (exchange_type_it == ANY_TYPE_MAP.end()) {
+ isc_throw(DbOperationError,
+ "exchangeType(): boost::any type "
+ << type.name() << " does not map to any exchange type");
}
+ const ExchangeDataType exchange_type = exchange_type_it->second;
+ if (exchange_type >= CQL_FUNCTIONS.size()) {
+ isc_throw(BadValue,
+ "exchangeType(): index " << exchange_type << " out of bounds "
+ << 0 << " - "
+ << (CQL_FUNCTIONS.size() - 1));
+ }
+ return exchange_type;
+}
+
+ExchangeDataType
+exchangeType(const CassValueType& type) {
+ CassTypeMap::const_iterator exchange_type_it = CASS_TYPE_MAP.find(type);
+ if (exchange_type_it == CASS_TYPE_MAP.end()) {
+ isc_throw(DbOperationError,
+ "exchangeType(): Cassandra value type "
+ << type << " does not map to any exchange type");
+ }
+ const ExchangeDataType exchange_type = exchange_type_it->second;
+ if (exchange_type >= CQL_FUNCTIONS.size()) {
+ isc_throw(BadValue,
+ "exchangeType(): index " << exchange_type << " out of bounds "
+ << 0 << " - "
+ << CQL_FUNCTIONS.size() - 1);
+ }
+ return exchange_type;
}
void
-CqlCommon::getData(const CassRow* row,
- int pindex,
- int dindex,
- const SqlExchange& exchange,
- CqlDataArray& data) {
- if (pindex >= exchange.parameters_.size()) {
- return;
- }
- const ExchangeColumnInfoContainerIndex& idx = exchange.parameters_.get<2>();
- const ExchangeColumnInfoContainerIndexRange& range =
- idx.equal_range(pindex);
- if (std::distance(range.first, range.second) > 0) {
- std::string name = (*range.first)->name_;
- ExchangeDataType type = (*range.first)->type_;
- const CassValue* value = cass_row_get_column_by_name(row, name.c_str());
- if (!value) {
- isc_throw(BadValue, "column name " << name << " doesn't exist");
+CqlCommon::bindData(const AnyArray& data, CassStatement* statement) {
+ size_t i = 0u;
+ for (const boost::any& element : data) {
+ CassError cass_error;
+ try {
+ cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlBindFunction_(
+ element, i, statement);
+ } catch (const boost::bad_any_cast& exception) {
+ isc_throw(DbOperationError,
+ "CqlCommon::bindData(): "
+ << exception.what() << " when binding parameter " << i
+ << " which is of type " << element.type().name()
+ << " with function CQL_FUNCTIONS["
+ << exchangeType(element) << "].cqlBindFunction_()");
+ }
+ if (cass_error != CASS_OK) {
+ isc_throw(DbOperationError,
+ "CqlCommon::bindData(): unable to bind parameter "
+ << i << " which is of type " << element.type().name()
+ << " with function CQL_FUNCTIONS["
+ << exchangeType(element)
+ << "].cqlBindFunction_(), Cassandra error code: "
+ << cass_error_desc(cass_error));
}
- if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
- isc_throw(BadValue, "index " << type << " out of bounds");
+ ++i;
+ }
+}
+
+void
+CqlCommon::getData(const CassRow* row, AnyArray& data) {
+ size_t i = 0u;
+ for (boost::any& element : data) {
+ const CassValue* value = cass_row_get_column(row, i);
+ CassError cass_error;
+ try {
+ cass_error = CQL_FUNCTIONS[exchangeType(element)].cqlGetFunction_(
+ element, value);
+ } catch (const boost::bad_any_cast& exception) {
+ isc_throw(DbOperationError,
+ "CqlCommon::getData(): "
+ << exception.what() << " when retrieving parameter "
+ << i << " which is of type " << element.type().name()
+ << " with function CQL_FUNCTIONS["
+ << exchangeType(element) << "].cqlGetFunction_()");
}
- CassError cassError =
- CqlFunctions[type].cqlGetFunction_(value, data.values_[dindex]);
- if (cassError != CASS_OK) {
- isc_throw(BadValue, "Cassandra error for column "
- << name << " with message: "
- << cass_error_desc(cassError));
+ if (cass_error != CASS_OK) {
+ isc_throw(
+ DbOperationError,
+ "CqlCommon::getData(): Cassandra error when retrieving column "
+ << i << ", Cassandra error code: "
+ << cass_error_desc(cass_error));
}
+ ++i;
}
}
cass_int64_t expire_time = static_cast<cass_int64_t>(cltt) + valid_lifetime;
if (expire_time > DatabaseConnection::MAX_DB_TIME) {
- isc_throw(BadValue, "Time value is too large: " << expire_time);
+ isc_throw(BadValue,
+ "CqlExchange(): convertToDatabaseTime(): time value "
+ << expire_time << " is too large");
}
expire = expire_time;
cltt = static_cast<time_t>(expire - valid_lifetime);
}
-void
-CqlExchange::createBindForReceive(CqlDataArray& /* data */,
- const int /* statementIndex = -1 */) {
- isc_throw(NotImplemented,
- "CqlExchange::createBindForReceive() not implemented yet.");
-}
-
-CqlDataArray
-CqlExchange::executeRead(const CqlConnection& connection,
- const CqlDataArray& data,
- const int statementIndex,
- const bool single /* = false */,
- const std::vector<std::string>&
- parameters /* = std::vector<std::string>() */) {
+AnyArray
+CqlExchange::executeSelect(const CqlConnection& connection,
+ const AnyArray& data,
+ StatementTag statement_tag,
+ const bool& single /* = false */) {
CassError rc;
CassStatement* statement = NULL;
CassFuture* future = NULL;
+ AnyArray local_data = data;
- statement = cass_prepared_bind(connection.statements_[statementIndex]);
- if (!statement) {
+ StatementMap::const_iterator it =
+ connection.statements_.find(statement_tag);
+ if (it == connection.statements_.end()) {
isc_throw(DbOperationError,
- "unable to bind statement "
- << connection.tagged_statements_[statementIndex].name_);
+ "CqlExchange::executeSelect(): Statement "
+ << statement_tag << "has not been prepared.");
+ }
+ CqlTaggedStatement tagged_statement = it->second;
+ if (tagged_statement.is_raw_statement_) {
+ // The entire query is the first element in data.
+ std::string* query = boost::any_cast<std::string*>(local_data.back());
+ local_data.pop_back();
+ statement = cass_statement_new(query->c_str(), local_data.size());
+ } else {
+ statement = cass_prepared_bind(tagged_statement.prepared_statement_);
+ if (!statement) {
+ isc_throw(DbOperationError,
+ "CqlExchange::executeSelect(): unable to bind statement "
+ << tagged_statement.name_);
+ }
}
if (connection.force_consistency_) {
rc = cass_statement_set_consistency(statement, connection.consistency_);
if (rc != CASS_OK) {
cass_statement_free(statement);
- isc_throw(
- DbOperationError,
- "unable to set statement consistency for statement "
- << connection.tagged_statements_[statementIndex].name_);
+ isc_throw(DbOperationError,
+ "CqlExchange::executeSelect(): unable to set statement "
+ "consistency for statement "
+ << tagged_statement.name_
+ << ", Cassandra error code: " << cass_error_desc(rc));
}
}
- CqlCommon::bindData(statement, statementIndex, data, *this,
- connection.tagged_statements_);
+ CqlCommon::bindData(local_data, statement);
future = cass_session_execute(connection.session_, statement);
if (!future) {
cass_statement_free(statement);
isc_throw(DbOperationError,
- "unable to execute statement "
- << connection.tagged_statements_[statementIndex].name_);
+ "CqlExchange::executeSelect(): no CassFuture for statement "
+ << tagged_statement.name_);
}
cass_future_wait(future);
- std::string error;
- connection.checkStatementError(error, future, statementIndex,
- "unable to execute statement");
+ const std::string error = connection.checkFutureError(
+ "CqlExchange::executeSelect(): cass_session_execute() != CASS_OK",
+ future, statement_tag);
rc = cass_future_error_code(future);
if (rc != CASS_OK) {
cass_future_free(future);
}
// Get column values.
- const CassResult* resultCollection = cass_future_get_result(future);
- if (single && cass_result_row_count(resultCollection) > 1) {
- cass_result_free(resultCollection);
+ const CassResult* result_collection = cass_future_get_result(future);
+ if (single && cass_result_row_count(result_collection) > 1) {
+ cass_result_free(result_collection);
cass_future_free(future);
cass_statement_free(statement);
- isc_throw(MultipleRecords,
- "multiple records were found in the "
- "database where only one was expected for statement "
- << connection.tagged_statements_[statementIndex].name_);
+ isc_throw(
+ MultipleRecords,
+ "CqlExchange::executeSelect(): multiple records were found in "
+ "the database where only one was expected for statement "
+ << tagged_statement.name_);
}
// Get results.
- CqlDataArray returnValues;
- CqlDataArray collection;
- CassIterator* rows = cass_iterator_from_result(resultCollection);
+ AnyArray return_values;
+ AnyArray collection;
+ CassIterator* rows = cass_iterator_from_result(result_collection);
while (cass_iterator_next(rows)) {
const CassRow* row = cass_iterator_get_row(rows);
- createBindForReceive(returnValues, statementIndex);
- for (size_t i = 0U; i < returnValues.size(); i++) {
- uint32_t pindex = i;
- if (!parameters.empty()) {
- const ExchangeColumnInfoContainerName& idx =
- parameters_.get<1>();
- const ExchangeColumnInfoContainerNameRange& range =
- idx.equal_range(parameters[i]);
- if (std::distance(range.first, range.second) > 0) {
- pindex = (*range.first)->index_;
- }
- }
- CqlCommon::getData(row, pindex, i, *this, returnValues);
- }
+ createBindForSelect(return_values, statement_tag);
+ CqlCommon::getData(row, return_values);
collection.add(retrieve());
}
// Free resources.
cass_iterator_free(rows);
- cass_result_free(resultCollection);
+ cass_result_free(result_collection);
cass_future_free(future);
cass_statement_free(statement);
}
void
-CqlExchange::executeWrite(const CqlConnection& connection,
- const CqlDataArray& data,
- const int statementIndex) {
+CqlExchange::executeMutation(
+ const CqlConnection& connection,
+ const AnyArray& data,
+ StatementTag statement_tag) {
CassError rc;
CassStatement* statement = NULL;
CassFuture* future = NULL;
- statement = cass_prepared_bind(connection.statements_[statementIndex]);
+ StatementMap::const_iterator it =
+ connection.statements_.find(statement_tag);
+ if (it == connection.statements_.end()) {
+ isc_throw(DbOperationError,
+ "CqlExchange::executeSelect(): Statement "
+ << statement_tag << "has not been prepared.");
+ }
+ CqlTaggedStatement tagged_statement = it->second;
+ statement = cass_prepared_bind(tagged_statement.prepared_statement_);
if (!statement) {
isc_throw(DbOperationError,
- "unable to bind statement "
- << connection.tagged_statements_[statementIndex].name_);
+ "CqlExchange::executeMutation(): unable to bind statement "
+ << tagged_statement.name_);
}
if (connection.force_consistency_) {
rc = cass_statement_set_consistency(statement, connection.consistency_);
if (rc != CASS_OK) {
cass_statement_free(statement);
- isc_throw(
- DbOperationError,
- "unable to set statement consistency for statement "
- << connection.tagged_statements_[statementIndex].name_);
+ isc_throw(DbOperationError,
+ "CqlExchange::executeMutation(): unable to set statement "
+ "consistency for statement "
+ << tagged_statement.name_
+ << ", Cassandra error code: " << cass_error_desc(rc));
}
}
- CqlCommon::bindData(statement, statementIndex, data, *this,
- connection.tagged_statements_);
+ CqlCommon::bindData(data, statement);
future = cass_session_execute(connection.session_, statement);
if (!future) {
cass_statement_free(statement);
isc_throw(DbOperationError,
- "unable to execute statement "
- << connection.tagged_statements_[statementIndex].name_);
+ "CqlExchange::executeMutation(): unable to execute statement "
+ << tagged_statement.name_);
}
cass_future_wait(future);
- std::string error;
- connection.checkStatementError(error, future, statementIndex,
- "unable to execute statement");
+ const std::string error = connection.checkFutureError(
+ "CqlExchange::executeMutation(): cass_session_execute() != CASS_OK",
+ future, statement_tag);
rc = cass_future_error_code(future);
if (rc != CASS_OK) {
cass_future_free(future);
cass_statement_free(statement);
if (!applied) {
- isc_throw(DuplicateEntry,
- "[applied] is false. Entry already exists. "
- "Statement "
- << connection.tagged_statements_[statementIndex].name_
- << "has not been applied");
+ isc_throw(
+ StatementNotApplied,
+ "CqlExchange::executeMutation(): [applied] is false for statement "
+ << tagged_statement.name_);
}
}
CqlExchange::hasStatementBeenApplied(CassFuture* future,
size_t* row_count,
size_t* column_count) {
- const CassResult* resultCollection = cass_future_get_result(future);
+ const CassResult* result_collection = cass_future_get_result(future);
if (row_count) {
- *row_count = cass_result_row_count(resultCollection);
+ *row_count = cass_result_row_count(result_collection);
}
if (column_count) {
- *column_count = cass_result_column_count(resultCollection);
+ *column_count = cass_result_column_count(result_collection);
}
- CassIterator* rows = cass_iterator_from_result(resultCollection);
- CqlDataArray data;
+ CassIterator* rows = cass_iterator_from_result(result_collection);
+ AnyArray data;
cass_bool_t applied = cass_true;
while (cass_iterator_next(rows)) {
const CassRow* row = cass_iterator_get_row(rows);
// [applied]: bool
- data.add(reinterpret_cast<void*>(&applied));
-
- const ExchangeColumnInfoContainerName& idx = parameters_.get<1>();
- const ExchangeColumnInfoContainerNameRange& range =
- idx.equal_range("[applied]");
- if (std::distance(range.first, range.second) > 0) {
- CqlCommon::getData(row, (*range.first)->index_, 0, *this, data);
- }
+ data.add(&applied);
+ CqlCommon::getData(row, data);
}
cass_iterator_free(rows);
- cass_result_free(resultCollection);
+ cass_result_free(result_collection);
return applied == cass_true;
}
-void*
-CqlExchange::retrieve() {
- isc_throw(NotImplemented, "CqlExchange::retrieve() not implemented yet.");
-}
+constexpr StatementTag CqlVersionExchange::GET_VERSION;
+
+StatementMap CqlVersionExchange::tagged_statements_ = {
+ {GET_VERSION, //
+ {GET_VERSION, //
+ "SELECT "
+ "version, minor "
+ "FROM schema_version "}} //
+};
CqlVersionExchange::CqlVersionExchange() {
- // Set the column names
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "version", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "minor", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- BOOST_ASSERT(parameters_.size() == 2U);
}
CqlVersionExchange::~CqlVersionExchange() {
}
void
-CqlVersionExchange::createBindForReceive(CqlDataArray& data,
- const int /* statementIndex = -1 */) {
+CqlVersionExchange::createBindForSelect(
+ AnyArray& data, StatementTag /* statement_tag = NULL */) {
// Start with a fresh array.
data.clear();
// id: blob
- data.add(reinterpret_cast<void*>(&version_));
+ data.add(&version_);
// host_identifier: blob
- data.add(reinterpret_cast<void*>(&minor_));
+ data.add(&minor_);
}
-void*
+boost::any
CqlVersionExchange::retrieve() {
pair_ = VersionPair(version_, minor_);
- return reinterpret_cast<void*>(&pair_);
+ return &pair_;
}
VersionPair
-CqlVersionExchange::retrieveVersion(const CqlConnection& connection,
- int statementIndex) {
+CqlVersionExchange::retrieveVersion(const CqlConnection& connection) {
// Run statement.
- const CqlDataArray whereValues;
- CqlDataArray versionCollection =
- executeRead(connection, whereValues, statementIndex, true);
-
- VersionPair result;
+ const AnyArray where_values;
+ AnyArray version_collection =
+ executeSelect(connection, where_values, GET_VERSION, true);
- if (!versionCollection.empty()) {
- result = *(reinterpret_cast<VersionPair*>(
- *versionCollection.begin()));
+ if (!version_collection.empty()) {
+ return *boost::any_cast<VersionPair*>(*version_collection.begin());
}
- return result;
+ return VersionPair();
}
} // namespace dhcp
-// Copyright (C) 2016 Deutsche Telekom AG.
+// Copyright (C) 2016-2017 Deutsche Telekom AG.
//
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#ifndef CQL_EXCHANGE_H
#define CQL_EXCHANGE_H
-#include <cassandra.h>
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/sql_common.h>
-#include <exceptions/exceptions.h>
+
+#include <boost/any.hpp> // for boost::any
#include <string>
+#include <typeinfo> // for std::type_info
+#include <unordered_map> // for std::unordered_map
#include <utility>
#include <vector>
namespace isc {
namespace dhcp {
-/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in all
-/// statements.
-typedef CassError (*CqlBindFunction)(CassStatement* statement,
- size_t index,
- void* value);
-
-/// @brief Converts a single Cassandra column value to a C++ object. Used in
-/// SELECT statements.
-typedef CassError (*CqlGetFunction)(const CassValue* value, void* data);
-
-/// @brief Pair containing major and minor versions
-typedef std::pair<uint32_t, uint32_t> VersionPair;
-
-/// @brief Wrapper over the bind and get functions that interface with Cassandra
-struct CqlFunctionData {
- /// @brief Binds a C++ object to a Cassandra statement's parameter. Used in
- /// all
- /// statements.
- CqlBindFunction cqlBindFunction_;
- /// @brief Converts a single Cassandra column value to a C++ object. Used in
- /// SELECT statements.
- CqlGetFunction cqlGetFunction_;
-};
+/// @brief Host identifier converted to Cassandra data type
+typedef std::vector<cass_byte_t> CassBlob;
-/// @brief Collection of bind and get functions, one pair for each
-/// parameter/column
-extern struct CqlFunctionData CqlFunctions[];
+/// @brief Forward declaration to @ref CqlExchange
+class CqlExchange;
/// @brief Structure used to bind C++ input values to dynamic CQL parameters
///
/// CQL statement execution. In other words, populating them with pointers that
/// go out of scope before the statement is executed results in undefined
/// behaviour.
-struct CqlDataArray {
- /// @brief Constructor
- CqlDataArray() {
- }
+class AnyArray : public std::vector<boost::any> {
+public:
+ /// @brief Add a at the end of the vector.
+ void add(const boost::any& value);
+
+ /// @brief Remove the void pointer to the data value from a specified
+ /// position inside the vector.
+ void remove(const size_t& index);
+};
- /// @brief Copy constructor
- CqlDataArray(const CqlDataArray& other) : values_(other.values_) {
- }
+// @brief Representation of a Cassandra User Defined Type
+class Udt : public AnyArray {
+public:
+ /// @brief Paramterized constructor
+ Udt(const CqlConnection& connection, const std::string& name);
/// @brief Destructor
- virtual ~CqlDataArray() {
- }
+ ~Udt();
- /// @brief Add a at the end of the vector.
- void add(void* value) {
- values_.push_back(value);
- }
+ /// @brief Frees the underlying container.
+ void freeUserType();
- /// @brief Remove the void pointer to the data value from a specified
- /// position inside the vector.
- void remove(int index) {
- if (values_.size() <= index) {
- isc_throw(BadValue, "Index " << index << " out of bounds: [0, "
- << (values_.size() - 1) << "]");
- }
- values_.erase(values_.begin() + index);
- }
-
- /// @brief Remove all data from the vector.
- void clear() {
- values_.clear();
- }
-
- /// @brief Get the number of elements inside the vector.
- size_t size() const {
- return values_.size();
- }
-
- /// @brief Check if the vector is empty.
- bool empty() const {
- return values_.empty();
- }
-
- /// @brief Square brackets operator overload
- ///
- /// Retrieves void pointer at specified position.
- void* operator[](int i) const {
- return values_[i];
- }
-
- /// @brief Iterator pointing to the first element
- std::vector<void*>::const_iterator begin() const {
- return values_.begin();
- }
-
- /// @brief Iterator pointing to the past-the-end element
- std::vector<void*>::const_iterator end() const {
- return values_.end();
- }
-
- /// @brief Vector of pointers to the data values
- std::vector<void*> values_;
+ /// @brief Creates the underlying container.
+ void newUserType();
+
+ /// @brief Connection to the Cassandra database
+ const CqlConnection& connection_;
+
+ /// @brief Name of the UDT in the schema: CREATE TYPE ___ { ... }
+ const std::string name_;
+
+ /// @brief Internal Cassandra driver object representing a Cassandra data
+ /// type
+ const CassDataType* cass_data_type_;
+
+ /// @brief Internal Cassandra driver object representing a user defined type
+ CassUserType* cass_user_type_;
+};
+
+typedef AnyArray Collection;
+
+/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in all
+/// statements.
+typedef CassError (*CqlBindFunction)(const boost::any& value,
+ const size_t& index,
+ CassStatement* statement);
+
+/// @brief Sets a member in a UDT. Used in INSERT & UPDATE statements.
+typedef CassError (*CqlUdtSetFunction)(const boost::any& udt_member,
+ const size_t& position,
+ CassUserType* cass_user_type);
+
+/// @brief Sets an item in a collection. Used in INSERT & UPDATE statements.
+typedef CassError (*CqlCollectionAppendFunction)(const boost::any& value,
+ CassCollection* collection);
+
+/// @brief Converts a single Cassandra column value to a C++ object. Used in
+/// SELECT statements.
+typedef CassError (*CqlGetFunction)(const boost::any& data,
+ const CassValue* value);
+
+/// @brief Wrapper over the bind and get functions that interface with Cassandra
+struct CqlFunction {
+ /// @brief Binds a C++ object to a Cassandra statement's parameter. Used in
+ /// all statements.
+ CqlBindFunction cqlBindFunction_;
+ /// @brief Sets a member in a UDT. Used in INSERT & UPDATE statements.
+ CqlUdtSetFunction cqlUdtSetFunction_;
+ /// @brief Sets an item in a collection. Used in INSERT & UPDATE statements.
+ CqlCollectionAppendFunction cqlCollectionAppendFunction_;
+ /// @brief Converts a single Cassandra column value to a C++ object. Used in
+ /// SELECT statements.
+ CqlGetFunction cqlGetFunction_;
};
/// @brief Cassandra Exchange
/// @brief Create BIND array to receive C++ data.
///
- /// Used in executeRead() to retrieve from database
+ /// Used in executeSelect() to retrieve from database
///
/// @param data array of bound objects representing data to be retrieved
- /// @param statementIndex prepared statement to be executed; defaults to an
+ /// @param statement_tag prepared statement being executed; defaults to an
/// invalid index
- virtual void createBindForReceive(CqlDataArray& data,
- const int statementIndex = -1);
+ virtual void createBindForSelect(AnyArray& data,
+ StatementTag statement_tag = NULL) = 0;
- /// @brief Executes select statements.
+ /// @brief Executes SELECT statements.
///
/// @param connection connection used to communicate with the Cassandra
/// database
- /// @param whereValues array of bound objects used to filter the results
- /// @param statementIndex prepared statement being executed
+ /// @param where_values array of bound objects used to filter the results
+ /// @param statement_tag prepared statement being executed
/// @param single true if a single row should be returned; by default,
/// multiple rows are allowed
- /// @param parameters Output parameters of a statement ( used in WHERE
- /// clause ); optional, needed only if parameters in the statement are
- /// in a different order than in the schema
///
- /// @return collection of void* objects
+ /// @return collection of boost::any objects
///
/// @throw DbOperationError
/// @throw MultipleRecords
- CqlDataArray executeRead(const CqlConnection& connection,
- const CqlDataArray& whereValues,
- const int statementIndex,
- const bool single = false,
- const std::vector<std::string>& parameters =
- std::vector<std::string>());
-
- /// @brief Executes insert, update, delete or other statements.
+ AnyArray executeSelect(const CqlConnection& connection,
+ const AnyArray& where_values,
+ StatementTag statement_tag,
+ const bool& single = false);
+
+ /// @brief Executes INSERT, UPDATE or DELETE statements.
///
/// @param connection connection used to communicate with the Cassandra
/// database
- /// @param assignedValues array of bound objects to be used when inserting
+ /// @param assigned_values array of bound objects to be used when inserting
/// values
- /// @param statementIndex prepared statement to be executed
- void executeWrite(const CqlConnection& connection,
- const CqlDataArray& assignedValues,
- const int statementIndex);
+ /// @param statement_tag prepared statement being executed
+ /// applied, could be different for commit than it is for rollback
+ void executeMutation(const CqlConnection& connection,
+ const AnyArray& assigned_values,
+ StatementTag statement_tag);
/// @brief Check if CQL statement has been applied.
///
/// @param row_count number of rows returned
/// @param column_count number of columns queried
///
+ /// On insert, a false [applied] means there is a duplicate entry with the
+ /// same priumary key.
+ ///
/// @return true if statement has been succesfully applied, false otherwise
bool hasStatementBeenApplied(CassFuture* future,
size_t* row_count = NULL,
/// @brief Copy received data into the derived class' object.
///
/// Copies information about the entity to be retrieved into a holistic
- /// object. Called in @ref executeRead(). Not implemented for base class
+ /// object. Called in @ref executeSelect(). Not implemented for base class
/// CqlExchange. To be implemented in derived classes.
///
/// @return a pointer to the object retrieved.
- virtual void* retrieve();
+ virtual boost::any retrieve() = 0;
};
/// @brief Exchange used to retrieve schema version from the keyspace.
/// @brief Create BIND array to receive C++ data.
///
- /// Used in executeRead() to retrieve from database
+ /// Used in executeSelect() to retrieve from database
///
/// @param data array of bound objects representing data to be retrieved
- /// @param statementIndex prepared statement to be executed; defaults to an
+ /// @param statement_tag prepared statement being executed; defaults to an
/// invalid index
- virtual void createBindForReceive(CqlDataArray& data,
- const int statementIndex = -1);
+ virtual void
+ createBindForSelect(AnyArray& data,
+ StatementTag statement_tag = NULL) override;
/// @brief Standalone method used to retrieve schema version
///
/// @param connection array of bound objects representing data to be
/// retrieved
- /// @param statementIndex prepared statement to be executed
///
/// @return version of schema specified in the prepared statement in the
/// @ref CqlConnection parameter
- virtual VersionPair
- retrieveVersion(const CqlConnection& connection, int statementIndex);
+ virtual VersionPair retrieveVersion(const CqlConnection& connection);
/// @brief Copy received data into the <version,minor> pair.
///
/// Copies information about the version to be retrieved into a pair. Called
- /// in executeRead().
+ /// in executeSelect().
///
/// @return a pointer to the object retrieved.
- virtual void* retrieve();
+ virtual boost::any retrieve() override;
+
+ /// @brief Statement tags definitions
+ /// @{
+ static constexpr StatementTag GET_VERSION = "GET_VERSION";
+ /// @}
+
+ /// @brief Cassandra statements
+ static StatementMap tagged_statements_;
private:
+ /// @brief Major version
cass_int32_t version_;
+ /// @brief Minor version
cass_int32_t minor_;
+ /// @brief Pair containing major and minor version
VersionPair pair_;
};
/// @brief Common operations in Cassandra exchanges
class CqlCommon {
public:
- /// @brief Returns type of data for specific parameter.
- ///
- /// Returns type of a given parameter of a given statement.
+ /// @brief Give values to every column of an INSERT or an UPDATE statement.
///
- /// @param stindex Index of statement being executed.
- /// @param pindex Index of the parameter for a given statement.
- /// @param exchange Exchange object to use
- /// @param tagged_statements CqlTaggedStatement array to use
- static ExchangeDataType
- getDataType(const uint32_t stindex,
- int pindex,
- const SqlExchange& exchange,
- const CqlTaggedStatement* tagged_statements);
-
- /// @brief Binds data specified in data
+ /// Calls cqlBindFunction_() for every column with it's respective type.
///
- /// Calls one of cass_value_bind_([none|bool|int32|int64|string|bytes]).
- /// It is used to bind C++ data types used by Kea into formats used by
- /// Cassandra.
- ///
- /// @param statement Statement object representing the query
- /// @param stindex Index of statement being executed
- /// @param data array that has been created for the type of lease in
- /// question.
- /// @param exchange Exchange object to use
- /// @param tagged_statements CqlTaggedStatement array to use
- static void bindData(CassStatement* statement,
- uint32_t stindex,
- const CqlDataArray& data,
- const SqlExchange& exchange,
- const CqlTaggedStatement* tagged_statements);
+ /// @param data array containing column values to be passed to the statement
+ /// being executed
+ /// @param statement internal Cassandra object representing the statement
+ /// being executed
+ static void bindData(const AnyArray& data, CassStatement* statement);
/// @brief Retrieves data returned by Cassandra.
///
- /// Calls one of cass_value_bind_([none|bool|int32|int64|string|bytes]).
- /// Used to retrieve data returned by Cassandra into standard C++ types used
- /// by Kea.
+ /// Calls cqlGetFunction_() for every column with it's respective type.
///
- /// @param row row of data returned by CQL library
- /// @param pindex Index of statement being executed
- /// @param dindex data index (specifies which entry in size array is used)
- /// @param exchange Exchange object to use
- /// @param data array that has been created for the type of lease in
- /// question.
- static void getData(const CassRow* row,
- int pindex,
- int dindex,
- const SqlExchange& exchange,
- CqlDataArray& data);
+ /// @param row internal Cassandra object containing data returned by
+ /// Cassandra
+ /// @param data array containing objects to be populated with results
+ static void getData(const CassRow* row, AnyArray& data);
};
+/// @brief Determine exchange type based on boost::any type.
+ExchangeDataType
+exchangeType(const boost::any& object);
+
+/// @brief Determine exchange type based on CassValueType.
+ExchangeDataType
+exchangeType(const CassValueType& type);
+
} // namespace dhcp
} // namespace isc
-// Copyright (C) 2015 - 2017 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
//
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#include <config.h>
-#include <asiolink/io_address.h>
-#include <dhcp/duid.h>
-#include <dhcp/hwaddr.h>
#include <dhcpsrv/cql_lease_mgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
-#include <limits>
+#include <dhcp/duid.h>
+#include <dhcp/hwaddr.h>
-using namespace isc;
-using namespace isc::dhcp;
-using namespace std;
+#include <asiolink/io_address.h>
+
+using isc::asiolink::IOAddress;
namespace isc {
namespace dhcp {
-static const size_t HOSTNAME_MAX_LEN = 255U;
-static const size_t ADDRESS6_TEXT_MAX_LEN = 39U;
-
-/// @{
-///
-/// @brief Catalog of all the CQL 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<const char*>("address"),
- NULL };
-static const char* delete_expired_lease4_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- NULL };
-static const char* delete_lease6_params[] = {
- static_cast<const char*>("address"),
- NULL };
-static const char* delete_expired_lease6_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- NULL };
-static const char* get_lease4_addr_params[] = {
- static_cast<const char*>("address"),
- NULL };
-static const char* get_lease4_clientid_params[] = {
- static_cast<const char*>("client_id"),
- NULL };
-static const char* get_lease4_clientid_subid_params[] = {
- static_cast<const char*>("client_id"),
- static_cast<const char*>("subnet_id"),
- NULL };
-static const char* get_lease4_hwaddr_params[] = {
- static_cast<const char*>("hwaddr"),
- NULL };
-static const char* get_lease4_hwaddr_subid_params[] = {
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("subnet_id"),
- NULL };
-static const char* get_lease4_expired_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("limit"),
- NULL };
-static const char* get_lease6_addr_params[] = {
- static_cast<const char*>("address"),
- static_cast<const char*>("lease_type"),
- NULL };
-static const char* get_lease6_duid_iaid_params[] = {
- static_cast<const char*>("duid"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("lease_type"),
- NULL };
-static const char* get_lease6_duid_iaid_subid_params[] = {
- static_cast<const char*>("duid"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("lease_type"),
- NULL };
-static const char* get_lease6_expired_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("limit"),
- NULL };
-static const char* get_version_params[] = {
- NULL };
-static const char* insert_lease4_params[] = {
- static_cast<const char*>("address"),
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("client_id"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("state"),
- NULL };
-static const char* insert_lease6_params[] = {
- static_cast<const char*>("address"),
- static_cast<const char*>("duid"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("pref_lifetime"),
- static_cast<const char*>("lease_type"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("prefix_len"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("hwtype"),
- static_cast<const char*>("hwaddr_source"),
- static_cast<const char*>("state"),
- NULL };
-static const char* update_lease4_params[] = {
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("client_id"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("state"),
- static_cast<const char*>("address"),
- NULL };
-static const char* update_lease6_params[] = {
- static_cast<const char*>("duid"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("pref_lifetime"),
- static_cast<const char*>("lease_type"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("prefix_len"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("hwtype"),
- static_cast<const char*>("hwaddr_source"),
- static_cast<const char*>("state"),
- static_cast<const char*>("address"),
- NULL };
-/// @}
-
-CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
- // DELETE_LEASE4
- { delete_lease4_params,
- "delete_lease4",
- "DELETE FROM lease4 WHERE address = ? "
- "IF EXISTS "
- },
-
- // DELETE_LEASE4_STATE_EXPIRED
- { delete_expired_lease4_params,
- "delete_lease4_expired",
- "SELECT address, hwaddr, client_id, "
- "valid_lifetime, expire, subnet_id, "
- "fqdn_fwd, fqdn_rev, hostname, "
- "state "
- "FROM lease4 "
- "WHERE state = ? "
- "AND expire < ? "
- "ALLOW FILTERING "
- },
-
- // DELETE_LEASE6
- { delete_lease6_params,
- "delete_lease6",
- "DELETE FROM lease6 WHERE address = ? "
- "IF EXISTS "
- },
-
- // DELETE_LEASE6_STATE_EXPIRED
- { delete_expired_lease6_params,
- "delete_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 < ? "
- "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 < ? "
- "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 < ? "
- "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 (?, ?, ?, ?, ?, ?, ?, ?, ?, "
- "?) "
- "IF NOT EXISTS "
- },
-
- // 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "
- "?) "
- "IF NOT EXISTS "
- },
-
- // 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 = ? "
- "IF EXISTS "
- },
-
- // 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 = ? "
- "IF EXISTS "
- },
-
+static constexpr size_t HOSTNAME_MAX_LEN = 255u;
+static constexpr size_t ADDRESS6_TEXT_MAX_LEN = 39u;
- // End of list sentinel
- { NULL, NULL, NULL }
-};
+static const CassBlob NULL_HWADDR({0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
+static const CassBlob NULL_CLIENTID({0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
+static const CassBlob NULL_PRIVACY_HASH({0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
/// @brief Common CQL and Lease Data Methods
///
/// base to both of them, containing some common methods.
class CqlLeaseExchange : public CqlExchange {
public:
- CqlLeaseExchange()
- : valid_lifetime_(0), expire_(0), subnet_id_(0), fqdn_fwd_(cass_false),
- fqdn_rev_(cass_false), state_(0) {
+ CqlLeaseExchange(const CqlConnection &connection)
+ : connection_(connection), valid_lifetime_(0), expire_(0),
+ subnet_id_(0), fqdn_fwd_(cass_false), fqdn_rev_(cass_false),
+ state_(0) {
}
+ /// @brief Create BIND array to receive C++ data.
+ ///
+ /// Used in executeSelect() to retrieve from database
+ ///
+ /// @param data array of bound objects representing data to be retrieved
+ /// @param statement_tag prepared statement being executed; defaults to an
+ /// invalid index
+ virtual void
+ createBindForSelect(AnyArray &data,
+ StatementTag statement_tag = NULL) override = 0;
+
+ /// @brief Copy received data into the derived class' object.
+ ///
+ /// Copies information about the entity to be retrieved into a holistic
+ /// object. Called in @ref executeSelect(). Not implemented for base class
+ /// CqlExchange. To be implemented in derived classes.
+ ///
+ /// @return a pointer to the object retrieved.
+ virtual boost::any retrieve() override = 0;
+
protected:
- std::vector<cass_byte_t> hwaddr_; ///< Hardware address
- cass_int64_t valid_lifetime_; ///< Lease time
- cass_int64_t expire_; ///< Lease expiry time
- cass_int32_t subnet_id_; ///< Subnet identification
- cass_bool_t fqdn_fwd_; ///< Has forward DNS update
- /// been performed
- cass_bool_t fqdn_rev_; ///< Has reverse DNS update
- /// been performed
- std::string hostname_; ///< Client hostname
- cass_int32_t state_; ///< Lease state
+ /// @brief Database connection
+ const CqlConnection &connection_;
+
+ /// @brief Hardware address
+ CassBlob hwaddr_;
+
+ /// @brief Lease timer
+ cass_int64_t valid_lifetime_;
+
+ /// @brief Lease expiry time
+ cass_int64_t expire_;
+
+ /// @brief Subnet identification
+ cass_int32_t subnet_id_;
+
+ /// @brief Has forward DNS update been performed
+ cass_bool_t fqdn_fwd_;
+
+ /// @brief Has reverse DNS update been performed
+ cass_bool_t fqdn_rev_;
+
+ /// @brief Client hostname
+ std::string hostname_;
+
+ /// @brief Lease state
+ cass_int32_t state_;
};
/// @brief Exchange CQL and Lease4 Data
///
/// @note There are no unit tests for this class. It is tested indirectly
/// in all CqlLeaseMgr::xxx4() calls where it is used.
-
class CqlLease4Exchange : public CqlLeaseExchange {
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.
- CqlLease4Exchange() : addr4_(0) {
- // Set the column names
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "address", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "hwaddr", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BYTES)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "client_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BYTES)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "valid_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT64)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "expire", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_TIMESTAMP)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "fqdn_fwd", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BOOL)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "fqdn_rev", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BOOL)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "hostname", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_STRING)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "state", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "limit", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "[applied]", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BOOL)));
- BOOST_ASSERT(parameters_.size() == 12U);
- }
+ explicit CqlLease4Exchange(const CqlConnection &connection);
/// @brief Create CQL_BIND objects for Lease4 Pointer
///
/// Fills in the CQL_BIND array for sending data in the Lease4 object to
- /// the database.
- void createBindForSend(const Lease4Ptr& lease, CqlDataArray& data) {
- 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: int
- // The address in the Lease structure is an IOAddress object.
- // Convert this to an integer for storage.
- addr4_ = static_cast<cass_int32_t>(lease_->addr_.toUint32());
- data.add(reinterpret_cast<void*>(&addr4_));
-
- // hwaddr: blob
- if (lease_->hwaddr_) {
- if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
- isc_throw(DbOperationError,
- "hardware address "
- << lease_->hwaddr_->toText() << " of length "
- << lease_->hwaddr_->hwaddr_.size()
- << " exceeds maximum allowed length of "
- << HWAddr::MAX_HWADDR_LEN);
- }
- hwaddr_ = lease_->hwaddr_->hwaddr_;
- } else {
- hwaddr_.clear();
- }
- data.add(reinterpret_cast<void*>(&hwaddr_));
+ /// the database. Used for INSERT statements.
+ void createBindForInsert(const Lease4Ptr &lease, AnyArray &data);
- // client_id: blob
- if (lease_->client_id_) {
- client_id_ = lease_->client_id_->getClientId();
- } else {
- client_id_.clear();
- }
- data.add(reinterpret_cast<void*>(&client_id_));
-
- // valid lifetime: bigint
- valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
- data.add(reinterpret_cast<void*>(&valid_lifetime_));
-
- // expire: bigint
- // 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_
- CqlExchange::convertToDatabaseTime(lease_->cltt_,
- lease_->valid_lft_, expire_);
- data.add(reinterpret_cast<void*>(&expire_));
-
- // subnet_id: int
- subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
- data.add(reinterpret_cast<void*>(&subnet_id_));
-
- // fqdn_fwd: boolean
- fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
- data.add(reinterpret_cast<void*>(&fqdn_fwd_));
-
- // fqdn_rev: boolean
- fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
- data.add(reinterpret_cast<void*>(&fqdn_rev_));
-
- // hostname: varchar
- if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
- isc_throw(BadValue, "hostname "
- << lease_->hostname_ << " of length "
- << lease_->hostname_.size()
- << " exceeds maximum allowed length of "
- << HOSTNAME_MAX_LEN);
- }
- hostname_ = lease_->hostname_;
- data.add(reinterpret_cast<void*>(&hostname_));
+ /// @brief Create CQL_BIND objects for Lease4 Pointer
+ ///
+ /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+ /// the database. Used for UPDATE statements.
+ void createBindForUpdate(const Lease4Ptr &lease,
+ AnyArray &data,
+ StatementTag statement_tag = NULL);
- // state: int
- state_ = static_cast<cass_int32_t>(lease_->state_);
- data.add(reinterpret_cast<void*>(&state_));
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError,
- "could not create bind array from Lease4: "
- << lease_->addr_.toText()
- << ", reason: " << ex.what());
- }
- }
+ /// @brief Create CQL_BIND objects for Lease4 Pointer
+ ///
+ /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+ /// the database. Used for DELETE statements.
+ void createBindForDelete(const IOAddress &address,
+ AnyArray &data,
+ StatementTag statement_tag = NULL);
/// @brief Create BIND array to receive data
///
/// Creates a CQL_BIND array to receive Lease4 data from the database.
- virtual void createBindForReceive(CqlDataArray& data,
- const int /* statementIndex */ = -1) {
+ virtual void
+ createBindForSelect(AnyArray &data,
+ StatementTag statement_tag = NULL) override;
+
+ virtual boost::any retrieve() override;
+
+ void getLeaseCollection(StatementTag &statement_tag,
+ AnyArray &data,
+ Lease4Collection &result);
+
+ void
+ getLease(StatementTag &statement_tag, AnyArray &data, Lease4Ptr &result);
+
+ void getExpiredLeases(const size_t &max_leases,
+ Lease4Collection &expired_leases);
+
+ /// @brief Cassandra statements
+ static StatementMap tagged_statements_;
+
+ /// @brief Statement tags definitions
+ /// @{
+ // Add entry to lease4 table
+ static constexpr StatementTag INSERT_LEASE4 = "INSERT_LEASE4";
+ // Update a Lease4 entry
+ static constexpr StatementTag UPDATE_LEASE4 = "UPDATE_LEASE4";
+ // Delete from lease4 by address
+ static constexpr StatementTag DELETE_LEASE4 = "DELETE_LEASE4";
+ // Delete expired lease4s in certain state
+ static constexpr StatementTag GET_LEASE4_EXPIRE = "GET_LEASE4_EXPIRE";
+ // Get lease4 by address
+ static constexpr StatementTag GET_LEASE4_ADDR = "GET_LEASE4_ADDR";
+ // Get lease4 by client ID
+ static constexpr StatementTag GET_LEASE4_CLIENTID = "GET_LEASE4_CLIENTID";
+ // Get lease4 by client ID & subnet ID
+ static constexpr StatementTag GET_LEASE4_CLIENTID_SUBID =
+ "GET_LEASE4_CLIENTID_SUBID";
+ // Get lease4 by HW address
+ static constexpr StatementTag GET_LEASE4_HWADDR = "GET_LEASE4_HWADDR";
+ // Get lease4 by HW address & subnet ID
+ static constexpr StatementTag GET_LEASE4_HWADDR_SUBID =
+ "GET_LEASE4_HWADDR_SUBID";
+ /// @}
+
+private:
+ // Pointer to lease object
+ Lease4Ptr lease_;
+ // IPv4 address
+ cass_int32_t address_;
+ // Client identification
+ CassBlob client_id_;
+}; // CqlLease4Exchange
+
+constexpr StatementTag CqlLease4Exchange::INSERT_LEASE4;
+constexpr StatementTag CqlLease4Exchange::UPDATE_LEASE4;
+constexpr StatementTag CqlLease4Exchange::DELETE_LEASE4;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_EXPIRE;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_ADDR;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_CLIENTID_SUBID;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR;
+constexpr StatementTag CqlLease4Exchange::GET_LEASE4_HWADDR_SUBID;
+
+StatementMap CqlLease4Exchange::tagged_statements_{
+
+ {INSERT_LEASE4, //
+ {INSERT_LEASE4, //
+ "INSERT INTO lease4( "
+ "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname, state "
+ ") VALUES ( "
+ "?, ?, ?, ?, ?, ?, ?, ?, ?, ? "
+ ") "
+ "IF NOT EXISTS "}},
+
+ {UPDATE_LEASE4, //
+ {UPDATE_LEASE4, //
+ "UPDATE lease4 SET "
+ "hwaddr = ?, "
+ "client_id = ?, "
+ "subnet_id = ?, "
+ "valid_lifetime = ?, "
+ "expire = ?, "
+ "fqdn_fwd = ?, "
+ "fqdn_rev = ?, "
+ "hostname = ?, "
+ "state = ? "
+ "WHERE address = ? "
+ "IF EXISTS "}},
+
+ {DELETE_LEASE4, //
+ {DELETE_LEASE4, //
+ "DELETE FROM lease4 "
+ "WHERE address = ? "
+ "IF EXISTS "}},
+
+ {GET_LEASE4_EXPIRE, //
+ {GET_LEASE4_EXPIRE, //
+ "SELECT "
+ "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname, state "
+ "FROM lease4 "
+ "WHERE state = ? "
+ "AND expire < ? "
+ "LIMIT ? "
+ "ALLOW FILTERING "}},
+
+ {GET_LEASE4_ADDR, //
+ {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, //
+ "SELECT "
+ "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname, state "
+ "FROM lease4 "
+ "WHERE client_id = ? "
+ "ALLOW FILTERING "}},
+
+ {GET_LEASE4_CLIENTID_SUBID, //
+ {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, //
+ "SELECT "
+ "address, hwaddr, client_id, valid_lifetime, expire, subnet_id, "
+ "fqdn_fwd, fqdn_rev, hostname, state "
+ "FROM lease4 "
+ "WHERE hwaddr = ? "
+ "ALLOW FILTERING "}},
+
+ {GET_LEASE4_HWADDR_SUBID, //
+ {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 "}},
+
+};
+
+CqlLease4Exchange::CqlLease4Exchange(const CqlConnection &connection)
+ : CqlLeaseExchange(connection), address_(0) {
+}
+
+void
+CqlLease4Exchange::createBindForInsert(const Lease4Ptr &lease, AnyArray &data) {
+ if (!lease) {
+ isc_throw(BadValue, "CqlLease4Exchange::createBindForInsert(): "
+ "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: int
- data.add(reinterpret_cast<void*>(&addr4_));
+ // The address in the Lease structure is an IOAddress object.
+ // Convert this to an integer for storage.
+ address_ = static_cast<cass_int32_t>(lease->addr_.toUint32());
// hwaddr: blob
- data.add(reinterpret_cast<void*>(&hwaddr_));
+ if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+ if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+ isc_throw(DbOperationError,
+ "hardware address "
+ << lease_->hwaddr_->toText() << " of length "
+ << lease_->hwaddr_->hwaddr_.size()
+ << " exceeds maximum allowed length of "
+ << HWAddr::MAX_HWADDR_LEN);
+ }
+ hwaddr_ = lease_->hwaddr_->hwaddr_;
+ } else {
+ hwaddr_.clear();
+ }
// client_id: blob
- data.add(reinterpret_cast<void*>(&client_id_));
+ if (lease_->client_id_ && lease->client_id_->getClientId().size() > 0) {
+ client_id_ = lease_->client_id_->getClientId();
+ } else {
+ client_id_.clear();
+ }
- // valid_lifetime: bigint
- data.add(reinterpret_cast<void*>(&valid_lifetime_));
+ // valid lifetime: bigint
+ valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
// expire: bigint
- data.add(reinterpret_cast<void*>(&expire_));
+ // 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_
+ CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+ expire_);
// subnet_id: int
- data.add(reinterpret_cast<void*>(&subnet_id_));
+ subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
// fqdn_fwd: boolean
- data.add(reinterpret_cast<void*>(&fqdn_fwd_));
+ fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
// fqdn_rev: boolean
- data.add(reinterpret_cast<void*>(&fqdn_rev_));
+ fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
// hostname: varchar
- data.add(reinterpret_cast<void*>(&hostname_));
+ if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+ isc_throw(BadValue,
+ "hostname " << lease_->hostname_ << " of length "
+ << lease_->hostname_.size()
+ << " exceeds maximum allowed length of "
+ << HOSTNAME_MAX_LEN);
+ }
+ hostname_ = lease_->hostname_;
// state: int
- data.add(reinterpret_cast<void*>(&state_));
+ state_ = static_cast<cass_int32_t>(lease_->state_);
+
+ // Start with a fresh array.
+ data.clear();
+ data.add(&address_);
+ data.add(&hwaddr_);
+ data.add(&client_id_);
+ data.add(&valid_lifetime_);
+ data.add(&expire_);
+ data.add(&subnet_id_);
+ data.add(&fqdn_fwd_);
+ data.add(&fqdn_rev_);
+ data.add(&hostname_);
+ data.add(&state_);
+
+ } catch (const Exception &ex) {
+ isc_throw(DbOperationError,
+ "CqlLease4Exchange::createBindForInsert(): "
+ "could not create bind array from Lease4: "
+ << lease_->addr_.toText() << ", reason: " << ex.what());
}
+}
- Lease4Ptr retrieveLease() {
- try {
- // Sanity checks
- if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
- isc_throw(BadValue, "hardware address "
- << HWAddr(hwaddr_, HTYPE_ETHER).toText()
- << " of length " << hwaddr_.size()
- << " exceeds maximum allowed length of "
- << HWAddr::MAX_HWADDR_LEN);
- }
- if (client_id_.size() > ClientId::MAX_CLIENT_ID_LEN) {
- isc_throw(BadValue, "client ID "
- << ClientId(client_id_).toText()
- << " of length " << client_id_.size()
- << " exceeds maximum allowed length of "
- << ClientId::MAX_CLIENT_ID_LEN);
- }
- if (hostname_.size() > HOSTNAME_MAX_LEN) {
- isc_throw(BadValue, "hostname"
- << hostname_ << " of length "
- << hostname_.size()
- << " exceeds maximum allowed length of "
- << HOSTNAME_MAX_LEN);
+void
+CqlLease4Exchange::createBindForUpdate(
+ const Lease4Ptr &lease,
+ AnyArray &data,
+ StatementTag statement_tag /* = NULL */) {
+ (void)statement_tag; // [maybe_unused]
+
+ if (!lease) {
+ isc_throw(BadValue, "CqlLease4Exchange::createBindForUpdate(): "
+ "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: int
+ // The address in the Lease structure is an IOAddress object.
+ // Convert this to an integer for storage.
+ address_ = static_cast<cass_int32_t>(lease->addr_.toUint32());
+
+ // hwaddr: blob
+ if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+ if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+ isc_throw(DbOperationError,
+ "hardware address "
+ << lease_->hwaddr_->toText() << " of length "
+ << lease_->hwaddr_->hwaddr_.size()
+ << " exceeds maximum allowed length of "
+ << HWAddr::MAX_HWADDR_LEN);
}
+ hwaddr_ = lease_->hwaddr_->hwaddr_;
+ } else {
+ hwaddr_.clear();
+ }
+
+ // client_id: blob
+ if (lease_->client_id_ && lease->client_id_->getClientId().size() > 0) {
+ client_id_ = lease_->client_id_->getClientId();
+ } else {
+ client_id_.clear();
+ }
- time_t cltt = 0;
- CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
- cltt);
+ // valid lifetime: bigint
+ valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
- // Recreate the hardware address.
- HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));
+ // expire: bigint
+ // 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_
+ CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+ expire_);
- Lease4Ptr result(new Lease4(addr4_, hwaddr, client_id_.data(),
- client_id_.size(), valid_lifetime_, 0,
- 0, cltt, subnet_id_, fqdn_fwd_,
- fqdn_rev_, hostname_));
+ // subnet_id: int
+ subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
- result->state_ = state_;
+ // fqdn_fwd: boolean
+ fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
- return result;
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError,
- "createBindForReceive(): "
- "could not convert data to Lease4, reason: "
- << ex.what());
+ // fqdn_rev: boolean
+ fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
+
+ // hostname: varchar
+ if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+ isc_throw(BadValue,
+ "hostname " << lease_->hostname_ << " of length "
+ << lease_->hostname_.size()
+ << " exceeds maximum allowed length of "
+ << HOSTNAME_MAX_LEN);
}
- return Lease4Ptr();
+ hostname_ = lease_->hostname_;
+
+ // state: int
+ state_ = static_cast<cass_int32_t>(lease_->state_);
+
+ // Start with a fresh array.
+ data.clear();
+ data.add(&hwaddr_);
+ data.add(&client_id_);
+ data.add(&subnet_id_);
+ data.add(&valid_lifetime_);
+ data.add(&expire_);
+ data.add(&fqdn_fwd_);
+ data.add(&fqdn_rev_);
+ data.add(&hostname_);
+ data.add(&state_);
+ data.add(&address_);
+
+ } catch (const Exception &ex) {
+ isc_throw(DbOperationError,
+ "CqlLease4Exchange::createBindUpdate(): "
+ "could not create bind array from Lease4: "
+ << lease_->addr_.toText() << ", reason: " << ex.what());
}
+}
+
+void
+CqlLease4Exchange::createBindForDelete(
+ const IOAddress &address,
+ AnyArray &data,
+ StatementTag statement_tag /* = NULL */) {
+ (void)statement_tag; // [maybe_unused]
+
+ // Set up the structures for the various components of the lease4
+ // structure.
- void* retrieve() {
- isc_throw(NotImplemented, "retrieve(): Not implemented yet.");
+ try {
+ // address: int
+ // The address in the Lease structure is an IOAddress object.
+ // Convert this to an integer for storage.
+ address_ = static_cast<cass_int32_t>(address.toUint32());
+
+ // Start with a fresh array.
+ data.clear();
+ data.add(&address_);
+
+ } catch (const Exception &ex) {
+ isc_throw(DbOperationError,
+ "CqlLease4Exchange::createBindForDelete(): "
+ "could not create bind array from Lease4: "
+ << lease_->addr_.toText() << ", reason: " << ex.what());
}
+}
-private:
- Lease4Ptr lease_; ///< Pointer to lease object
- cass_int32_t addr4_; ///< IPv4 address
- std::vector<cass_byte_t> client_id_; ///< Client identification
-};
+/// @brief Create BIND array to receive data
+///
+/// Creates a CQL_BIND array to receive Lease4 data from the database.
+void
+CqlLease4Exchange::createBindForSelect(
+ AnyArray &data, StatementTag /* statement_tag = NULL */) {
+
+ // Start with a fresh array.
+ data.clear();
+
+ // address: blob
+ data.add(&address_);
+
+ // hwaddr: blob
+ data.add(&hwaddr_);
+
+ // client_id: blob
+ data.add(&client_id_);
+
+ // valid_lifetime: bigint
+ data.add(&valid_lifetime_);
+
+ // expire: bigint
+ data.add(&expire_);
+
+ // subnet_id: int
+ data.add(&subnet_id_);
+
+ // fqdn_fwd: boolean
+ data.add(&fqdn_fwd_);
+
+ // fqdn_rev: boolean
+ data.add(&fqdn_rev_);
+
+ // hostname: varchar
+ data.add(&hostname_);
+
+ // state: int
+ data.add(&state_);
+}
+
+boost::any
+CqlLease4Exchange::retrieve() {
+ try {
+ // Sanity checks
+ if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+ isc_throw(BadValue,
+ "hardware address "
+ << HWAddr(hwaddr_, HTYPE_ETHER).toText()
+ << " of length " << hwaddr_.size()
+ << " exceeds maximum allowed length of "
+ << HWAddr::MAX_HWADDR_LEN);
+ }
+ if (client_id_.size() > ClientId::MAX_CLIENT_ID_LEN) {
+ isc_throw(BadValue,
+ "client ID " << ClientId(client_id_).toText()
+ << " of length " << client_id_.size()
+ << " exceeds maximum allowed length of "
+ << ClientId::MAX_CLIENT_ID_LEN);
+ }
+ if (hostname_.size() > HOSTNAME_MAX_LEN) {
+ isc_throw(BadValue,
+ "hostname" << hostname_ << " of length "
+ << hostname_.size()
+ << " exceeds maximum allowed length of "
+ << HOSTNAME_MAX_LEN);
+ }
+
+ time_t cltt = 0;
+ CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+
+ // Recreate the hardware address.
+ HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER));
+
+ uint32_t addr4 = static_cast<uint32_t>(address_);
+
+ if (hwaddr->hwaddr_ == NULL_HWADDR) {
+ hwaddr->hwaddr_.clear();
+ }
+
+ if (client_id_ == NULL_CLIENTID) {
+ client_id_.clear();
+ }
+
+ Lease4Ptr result(new Lease4(addr4, hwaddr, client_id_.data(),
+ client_id_.size(), valid_lifetime_, 0, 0,
+ cltt, subnet_id_, fqdn_fwd_, fqdn_rev_,
+ hostname_));
+
+ result->state_ = state_;
+
+ return result;
+ } catch (const Exception &ex) {
+ isc_throw(DbOperationError,
+ "CqlLease4Exchange::retrieveLease(): "
+ "could not convert data to Lease4, reason: "
+ << ex.what());
+ }
+}
+
+void
+CqlLease4Exchange::getLeaseCollection(StatementTag &statement_tag,
+ AnyArray &data,
+ Lease4Collection &result) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
+ .arg(statement_tag);
+
+ AnyArray collection = executeSelect(connection_, data, statement_tag);
+
+ // Transfer Lease4 objects to result.
+ for (boost::any &element : collection) {
+ result.push_back(boost::any_cast<Lease4Ptr>(element));
+ }
+}
+
+void
+CqlLease4Exchange::getLease(StatementTag &statement_tag,
+ AnyArray &data,
+ Lease4Ptr &result) {
+ // This particular method is called when only one or zero matches is
+ // expected.
+ Lease4Collection collection;
+ getLeaseCollection(statement_tag, data, collection);
+
+ // Return single record if present, else clear the lease.
+ const size_t collection_size = collection.size();
+ if (collection_size >= 2u) {
+ isc_throw(
+ MultipleRecords,
+ "CqlLease4Exchange::getLease(): multiple records were found in "
+ "the database where only one was expected for statement "
+ << statement_tag);
+ } else if (collection_size == 0u) {
+ result.reset();
+ } else {
+ result = *collection.begin();
+ }
+}
+
+void
+CqlLease4Exchange::getExpiredLeases(const size_t &max_leases,
+ Lease4Collection &expired_leases) {
+ // Set up the WHERE clause value
+ cass_int32_t keep_state = Lease::STATE_EXPIRED_RECLAIMED;
+ cass_int64_t timestamp = static_cast<cass_int64_t>(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.
+ cass_int32_t limit = max_leases > 0u ?
+ static_cast<cass_int32_t>(max_leases) :
+ std::numeric_limits<cass_int32_t>::max();
+
+ for (cass_int32_t state = Lease::STATE_DEFAULT;
+ state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
+ if (state == keep_state) {
+ continue;
+ }
+
+ AnyArray data;
+ data.add(&state);
+ data.add(×tamp);
+ data.add(&limit);
+
+ // Retrieve leases from the database.
+ Lease4Collection temp_collection;
+ getLeaseCollection(CqlLease4Exchange::GET_LEASE4_EXPIRE, data,
+ temp_collection);
+
+ for (Lease4Ptr &lease : temp_collection) {
+ expired_leases.push_back(lease);
+ }
+ }
+}
/// @brief Exchange CQL and Lease6 Data
///
///
/// @note There are no unit tests for this class. It is tested indirectly
/// in all CqlLeaseMgr::xxx6() calls where it is used.
-
class CqlLease6Exchange : public CqlLeaseExchange {
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.
- CqlLease6Exchange()
- : pref_lifetime_(0), lease_type_(0), iaid_(0), prefixlen_(0),
- hwtype_(0), hwaddr_source_(0) {
- // Set the column names
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "address", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_STRING)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "duid", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BYTES)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "valid_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT64)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "expire", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_TIMESTAMP)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "pref_lifetime", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT64)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "lease_type", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "iaid", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "prefix_len", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "fqdn_fwd", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BOOL)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "fqdn_rev", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BOOL)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "hostname", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_STRING)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "hwaddr", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BYTES)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "hwtype", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "hwaddr_source", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "state", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "limit", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_INT32)));
- parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
- "[applied]", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
- EXCHANGE_DATA_TYPE_BOOL)));
- BOOST_ASSERT(parameters_.size() == 18U);
- }
+ /// The initialization of the variables here is nonly to satisfy
+ /// cppcheck -
+ /// all variables are initialized/set in the methods before they are
+ /// used.
+ explicit CqlLease6Exchange(const CqlConnection &connection);
+
+ /// @brief Create CQL_BIND objects for Lease4 Pointer
+ ///
+ /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+ /// the database. Used for INSERT statements.
+ void createBindForInsert(const Lease6Ptr &lease, AnyArray &data);
+
+ /// @brief Create CQL_BIND objects for Lease4 Pointer
+ ///
+ /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+ /// the database. Used for UPDATE statements.
+ void createBindForUpdate(const Lease6Ptr &lease,
+ AnyArray &data,
+ StatementTag statement_tag = NULL);
+
+ /// @brief Create CQL_BIND objects for Lease4 Pointer
+ ///
+ /// Fills in the CQL_BIND array for sending data in the Lease4 object to
+ /// the database. Used for DELETE statements.
+ void createBindForDelete(const IOAddress &lease,
+ AnyArray &data,
+ StatementTag statement_tag = NULL);
- /// @brief Create CQL_BIND objects for Lease6 Pointer
+ /// @brief Create BIND array to receive data
///
- /// Fills in the CQL_BIND array for sending data in the Lease6 object to
- /// the database.
- void createBindForSend(const Lease6Ptr& lease, CqlDataArray& data) {
- if (!lease) {
- isc_throw(BadValue, "createBindForSend(): Lease6 object is NULL");
+ /// Creates a CQL_BIND array to receive Lease6 data from the database.
+ void createBindForSelect(AnyArray &data,
+ StatementTag statement_tag = NULL) override;
+
+ boost::any retrieve() override;
+
+ void getLeaseCollection(StatementTag &statement_tag,
+ AnyArray &data,
+ Lease6Collection &result);
+
+ void
+ getLease(StatementTag &statement_tag, AnyArray &data, Lease6Ptr &result);
+
+ void getExpiredLeases(const size_t &max_leases,
+ Lease6Collection &expired_leases);
+
+ /// @brief Cassandra statements
+ static StatementMap tagged_statements_;
+
+ /// @brief Statement tags definitions
+ /// @{
+ static constexpr StatementTag INSERT_LEASE6 = "INSERT_LEASE6";
+ static constexpr StatementTag UPDATE_LEASE6 = "UPDATE_LEASE6";
+ static constexpr StatementTag DELETE_LEASE6 = "DELETE_LEASE6";
+ static constexpr StatementTag GET_LEASE6_EXPIRE = "GET_LEASE6_EXPIRE";
+ static constexpr StatementTag GET_LEASE6_ADDR = "GET_LEASE6_ADDR";
+ static constexpr StatementTag GET_LEASE6_DUID_IAID = "GET_LEASE6_DUID_IAID";
+ static constexpr StatementTag GET_LEASE6_DUID_IAID_SUBID =
+ "GET_LEASE6_DUID_IAID_SUBID";
+ // @}
+
+private:
+ /// @brief Lease
+ Lease6Ptr lease_;
+
+ /// @brief IPv6 address
+ std::string address_;
+
+ /// @brief Preferred lifetime
+ cass_int64_t pref_lifetime_;
+
+ /// @brief Client identifier
+ CassBlob duid_;
+
+ /// @brief Identity association identifier
+ cass_int32_t iaid_;
+
+ /// @brief Lease type
+ cass_int32_t lease_type_;
+
+ /// @brief Prefix length
+ cass_int32_t prefix_len_;
+
+ /// @brief Hardware type
+ cass_int32_t hwtype_;
+
+ /// @brief Source of the hardware address
+ cass_int32_t hwaddr_source_;
+}; // CqlLease6Exchange
+
+constexpr StatementTag CqlLease6Exchange::INSERT_LEASE6;
+constexpr StatementTag CqlLease6Exchange::UPDATE_LEASE6;
+constexpr StatementTag CqlLease6Exchange::DELETE_LEASE6;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_EXPIRE;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_ADDR;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID;
+constexpr StatementTag CqlLease6Exchange::GET_LEASE6_DUID_IAID_SUBID;
+
+StatementMap CqlLease6Exchange::tagged_statements_ = {
+
+ {INSERT_LEASE6, //
+ {INSERT_LEASE6, //
+ "INSERT INTO lease6("
+ "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+ "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+ "hwaddr_source, state "
+ ") VALUES ("
+ "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?"
+ ") "
+ "IF NOT EXISTS "}},
+
+ {UPDATE_LEASE6, //
+ {UPDATE_LEASE6, //
+ "UPDATE lease6 SET "
+ "valid_lifetime = ?, "
+ "expire = ?, "
+ "pref_lifetime = ?, "
+ "duid = ?, "
+ "iaid = ?, "
+ "subnet_id = ?, "
+ "lease_type = ?, "
+ "prefix_len = ?, "
+ "fqdn_fwd = ?, "
+ "fqdn_rev = ?, "
+ "hostname = ?, "
+ "hwaddr = ?, "
+ "hwtype = ?, "
+ "hwaddr_source = ?, "
+ "state = ? "
+ "WHERE address = ? "
+ "IF EXISTS "}},
+
+ {DELETE_LEASE6, //
+ {DELETE_LEASE6, //
+ "DELETE FROM lease6 "
+ "WHERE address = ? "
+ "IF EXISTS "}},
+
+ {GET_LEASE6_EXPIRE, //
+ {GET_LEASE6_EXPIRE, //
+ "SELECT "
+ "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+ "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+ "hwaddr_source, state "
+ "FROM lease6 "
+ "WHERE state = ? "
+ "AND expire < ? "
+ "LIMIT ? "
+ "ALLOW FILTERING "}},
+
+ {GET_LEASE6_ADDR, //
+ {GET_LEASE6_ADDR, //
+ "SELECT "
+ "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+ "lease_type, 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, //
+ "SELECT "
+ "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+ "lease_type, 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, //
+ "SELECT "
+ "address, valid_lifetime, expire, subnet_id, pref_lifetime, duid, iaid, "
+ "lease_type, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, "
+ "hwaddr_source, state "
+ "FROM lease6 "
+ "WHERE duid = ? AND iaid = ? "
+ "AND lease_type = ? "
+ "AND subnet_id = ? "
+ "ALLOW FILTERING "}},
+
+};
+
+/// @brief Constructor
+///
+/// The initialization of the variables here is not only to satisfy
+/// cppcheck -
+/// all variables are initialized/set in the methods before they are
+/// used.
+CqlLease6Exchange::CqlLease6Exchange(const CqlConnection &connection)
+ : CqlLeaseExchange(connection), pref_lifetime_(0), iaid_(0), lease_type_(0),
+ prefix_len_(0), hwtype_(0), hwaddr_source_(0) {
+}
+/// @brief Create CQL_BIND objects for Lease6 Pointer
+///
+/// Fills in the CQL_BIND array for sending data in the Lease6 object to
+/// the database.
+void
+CqlLease6Exchange::createBindForInsert(const Lease6Ptr &lease, AnyArray &data) {
+ if (!lease) {
+ isc_throw(BadValue, "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
+ address_ = lease_->addr_.toText();
+ if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
+ isc_throw(BadValue,
+ "address " << address_ << " of length " << address_.size()
+ << " exceeds maximum allowed length of "
+ << ADDRESS6_TEXT_MAX_LEN);
}
- // 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
- addr6_ = lease_->addr_.toText();
- if (addr6_.size() > ADDRESS6_TEXT_MAX_LEN) {
- isc_throw(BadValue,
- "address " << addr6_ << " of length " << addr6_.size()
- << " exceeds maximum allowed length of "
- << ADDRESS6_TEXT_MAX_LEN);
- }
- data.add(reinterpret_cast<void*>(&addr6_));
- // duid: blob
- if (!lease_->duid_) {
+ // valid lifetime: bigint
+ valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
+
+ // expire: bigint
+ // 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_
+ CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+ expire_);
+
+ // subnet_id: int
+ subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
+
+ // pref_lifetime: bigint
+ pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
+
+ // duid: blob
+ if (!lease_->duid_) {
+ isc_throw(DbOperationError,
+ "lease6 with address " << address_
+ << " is missing mandatory duid");
+ }
+ duid_ = lease_->duid_->getDuid();
+
+ // iaid: int
+ iaid_ = static_cast<cass_int32_t>(lease_->iaid_);
+
+ // lease_type: int
+ lease_type_ = static_cast<cass_int32_t>(lease_->type_);
+
+ // prefix_len: int
+ prefix_len_ = static_cast<cass_int32_t>(lease_->prefixlen_);
+
+ // fqdn_fwd: boolean
+ fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
+
+ // fqdn_rev: boolean
+ fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
+
+ // hostname: varchar
+ if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+ isc_throw(BadValue,
+ "hostname" << lease_->hostname_ << " of length "
+ << lease_->hostname_.size()
+ << " exceeds maximum allowed length of "
+ << HOSTNAME_MAX_LEN);
+ }
+ hostname_ = lease_->hostname_;
+
+ // hwaddr: blob
+ if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+ if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
isc_throw(DbOperationError,
- "lease6 for address "
- << addr6_ << " is missing mandatory duid");
- }
- duid_ = lease_->duid_->getDuid();
- data.add(reinterpret_cast<void*>(&duid_));
-
- // valid lifetime: bigint
- valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
- data.add(reinterpret_cast<void*>(&valid_lifetime_));
-
- // expire: bigint
- // 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_
- CqlExchange::convertToDatabaseTime(lease_->cltt_,
- lease_->valid_lft_, expire_);
- data.add(reinterpret_cast<void*>(&expire_));
-
- // subnet_id: int
- subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
- data.add(reinterpret_cast<void*>(&subnet_id_));
-
- // pref_lifetime: bigint
- pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
- data.add(reinterpret_cast<void*>(&pref_lifetime_));
-
- // lease_type: int
- lease_type_ = static_cast<cass_int32_t>(lease_->type_);
- data.add(reinterpret_cast<void*>(&lease_type_));
-
- // iaid: int
- iaid_ = static_cast<cass_int32_t>(lease_->iaid_);
- data.add(reinterpret_cast<void*>(&iaid_));
-
- // prefix_len: int
- prefixlen_ = static_cast<cass_int32_t>(lease_->prefixlen_);
- data.add(reinterpret_cast<void*>(&prefixlen_));
-
- // fqdn_fwd: boolean
- fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
- data.add(reinterpret_cast<void*>(&fqdn_fwd_));
-
- // fqdn_rev: boolean
- fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
- data.add(reinterpret_cast<void*>(&fqdn_rev_));
-
- // hostname: varchar
- if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
- isc_throw(BadValue, "hostname"
- << lease_->hostname_ << " of length "
- << lease_->hostname_.size()
- << " exceeds maximum allowed length of "
- << HOSTNAME_MAX_LEN);
- }
- hostname_ = lease_->hostname_;
- data.add(reinterpret_cast<void*>(&hostname_));
-
- // hwaddr: blob
- if (lease_->hwaddr_) {
- if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
- isc_throw(BadValue,
- "hardware address "
- << lease_->hwaddr_->toText() << " of length "
- << lease_->hwaddr_->hwaddr_.size()
- << " exceeds maximum allowed length of "
- << HWAddr::MAX_HWADDR_LEN);
- }
- hwaddr_ = lease_->hwaddr_->hwaddr_;
- } else {
- hwaddr_.clear();
+ "hardware address "
+ << lease_->hwaddr_->toText() << " of length "
+ << lease_->hwaddr_->hwaddr_.size()
+ << " exceeds maximum allowed length of "
+ << HWAddr::MAX_HWADDR_LEN);
}
- data.add(reinterpret_cast<void*>(&hwaddr_));
+ hwaddr_ = lease_->hwaddr_->hwaddr_;
+ } else {
+ hwaddr_.clear();
+ }
- // hwtype: int
- if (lease_->hwaddr_) {
- hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
- } else {
- hwtype_ = 0;
- }
- data.add(reinterpret_cast<void*>(&hwtype_));
-
- // hwaddr_source: int
- if (lease_->hwaddr_) {
- hwaddr_source_ =
- static_cast<cass_int32_t>(lease_->hwaddr_->source_);
- } else {
- hwaddr_source_ = 0;
- }
- data.add(reinterpret_cast<void*>(&hwaddr_source_));
+ // hwtype: int
+ if (lease_->hwaddr_) {
+ hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
+ } else {
+ hwtype_ = 0;
+ }
- // state: int
- state_ = static_cast<cass_int32_t>(lease_->state_);
- data.add(reinterpret_cast<void*>(&state_));
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError,
- "createBindForSend(): "
- "could not create bind array from Lease6: "
- << lease_->addr_.toText()
- << ", reason: " << ex.what());
+ // hwaddr_source: int
+ if (lease_->hwaddr_) {
+ hwaddr_source_ =
+ static_cast<cass_int32_t>(lease_->hwaddr_->source_);
+ } else {
+ hwaddr_source_ = 0;
}
+
+ // state: int
+ state_ = static_cast<cass_int32_t>(lease_->state_);
+
+ // Start with a fresh array.
+ data.clear();
+
+ // Add them all to data.
+ data.add(&address_);
+ data.add(&valid_lifetime_);
+ data.add(&expire_);
+ data.add(&subnet_id_);
+ data.add(&pref_lifetime_);
+ data.add(&duid_);
+ data.add(&iaid_);
+ data.add(&lease_type_);
+ data.add(&prefix_len_);
+ data.add(&fqdn_fwd_);
+ data.add(&fqdn_rev_);
+ data.add(&hostname_);
+ data.add(&hwaddr_);
+ data.add(&hwtype_);
+ data.add(&hwaddr_source_);
+ data.add(&state_);
+
+ } catch (const Exception &ex) {
+ isc_throw(DbOperationError,
+ "CqlLease6Exchange::createBindForInsert(): "
+ "could not create bind array from Lease6: "
+ << lease_->addr_.toText() << ", reason: " << ex.what());
}
+}
+/// @brief Create CQL_BIND objects for Lease6 Pointer
+///
+/// Fills in the CQL_BIND array for sending data in the Lease6 object to
+/// the database.
+void
+CqlLease6Exchange::createBindForUpdate(
+ const Lease6Ptr &lease,
+ AnyArray &data,
+ StatementTag statement_tag /* = NULL */) {
+ (void)statement_tag; // [maybe_unused]
+
+ if (!lease) {
+ isc_throw(BadValue, "Lease6 object is NULL");
+ }
+ // Store lease object to ensure it remains valid.
+ lease_ = lease;
- /// @brief Create BIND array to receive data
- ///
- /// Creates a CQL_BIND array to receive Lease6 data from the database.
- void createBindForReceive(CqlDataArray& data,
- const int /* statementIndex */ = -1) {
+ // Set up the structures for the various components of the lease4
+ // structure.
+ try {
// address: varchar
- data.add(reinterpret_cast<void*>(&addr6_));
-
- // duid: blob
- data.add(reinterpret_cast<void*>(&duid_));
+ address_ = lease_->addr_.toText();
+ if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
+ isc_throw(BadValue,
+ "address " << address_ << " of length " << address_.size()
+ << " exceeds maximum allowed length of "
+ << ADDRESS6_TEXT_MAX_LEN);
+ }
- // valid_lifetime_: bigint
- data.add(reinterpret_cast<void*>(&valid_lifetime_));
+ // valid lifetime: bigint
+ valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
// expire: bigint
- data.add(reinterpret_cast<void*>(&expire_));
+ // 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_
+ CqlExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
+ expire_);
// subnet_id: int
- data.add(reinterpret_cast<void*>(&subnet_id_));
+ subnet_id_ = static_cast<cass_int32_t>(lease_->subnet_id_);
// pref_lifetime: bigint
- data.add(reinterpret_cast<void*>(&pref_lifetime_));
+ pref_lifetime_ = static_cast<cass_int64_t>(lease_->preferred_lft_);
- // lease_type: int
- data.add(reinterpret_cast<void*>(&lease_type_));
+ // duid: blob
+ if (!lease_->duid_) {
+ isc_throw(DbOperationError,
+ "lease6 with address " << address_
+ << " is missing mandatory duid");
+ }
+ duid_ = lease_->duid_->getDuid();
// iaid: int
- data.add(reinterpret_cast<void*>(&iaid_));
+ iaid_ = static_cast<cass_int32_t>(lease_->iaid_);
+
+ // lease_type: int
+ lease_type_ = static_cast<cass_int32_t>(lease_->type_);
// prefix_len: int
- data.add(reinterpret_cast<void*>(&prefixlen_));
+ prefix_len_ = static_cast<cass_int32_t>(lease_->prefixlen_);
// fqdn_fwd: boolean
- data.add(reinterpret_cast<void*>(&fqdn_fwd_));
+ fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false;
// fqdn_rev: boolean
- data.add(reinterpret_cast<void*>(&fqdn_rev_));
+ fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false;
// hostname: varchar
- data.add(reinterpret_cast<void*>(&hostname_));
+ if (lease_->hostname_.size() > HOSTNAME_MAX_LEN) {
+ isc_throw(BadValue,
+ "hostname" << lease_->hostname_ << " of length "
+ << lease_->hostname_.size()
+ << " exceeds maximum allowed length of "
+ << HOSTNAME_MAX_LEN);
+ }
+ hostname_ = lease_->hostname_;
// hwaddr: blob
- data.add(reinterpret_cast<void*>(&hwaddr_));
+ if (lease_->hwaddr_ && lease->hwaddr_->hwaddr_.size() > 0) {
+ if (lease_->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+ isc_throw(DbOperationError,
+ "hardware address "
+ << lease_->hwaddr_->toText() << " of length "
+ << lease_->hwaddr_->hwaddr_.size()
+ << " exceeds maximum allowed length of "
+ << HWAddr::MAX_HWADDR_LEN);
+ }
+ hwaddr_ = lease_->hwaddr_->hwaddr_;
+ } else {
+ hwaddr_.clear();
+ }
// hwtype: int
- data.add(reinterpret_cast<void*>(&hwtype_));
+ if (lease_->hwaddr_) {
+ hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
+ } else {
+ hwtype_ = 0;
+ }
// hwaddr_source: int
- data.add(reinterpret_cast<void*>(&hwaddr_source_));
+ if (lease_->hwaddr_) {
+ hwaddr_source_ =
+ static_cast<cass_int32_t>(lease_->hwaddr_->source_);
+ } else {
+ hwaddr_source_ = 0;
+ }
// state: int
- data.add(reinterpret_cast<void*>(&state_));
+ state_ = static_cast<cass_int32_t>(lease_->state_);
+
+ // Start with a fresh array.
+ data.clear();
+
+ // Add them all to data.
+ data.add(&valid_lifetime_);
+ data.add(&expire_);
+ data.add(&pref_lifetime_);
+ data.add(&duid_);
+ data.add(&iaid_);
+ data.add(&subnet_id_);
+ data.add(&lease_type_);
+ data.add(&prefix_len_);
+ data.add(&fqdn_fwd_);
+ data.add(&fqdn_rev_);
+ data.add(&hostname_);
+ data.add(&hwaddr_);
+ data.add(&hwtype_);
+ data.add(&hwaddr_source_);
+ data.add(&state_);
+ data.add(&address_);
+
+ } catch (const Exception &ex) {
+ isc_throw(DbOperationError,
+ "CqlLease6Exchange::createBindForUpdate(): "
+ "could not create bind array from Lease6: "
+ << lease_->addr_.toText() << ", reason: " << ex.what());
}
+}
- Lease6Ptr retrieveLease() {
- try {
- // Sanity checks
- if (addr6_.size() > ADDRESS6_TEXT_MAX_LEN) {
- isc_throw(BadValue,
- "address " << addr6_ << " of length " << addr6_.size()
- << " exceeds maximum allowed length of "
- << ADDRESS6_TEXT_MAX_LEN);
- }
- if (duid_.size() > DUID::MAX_DUID_LEN) {
- isc_throw(BadValue, "duid "
- << DUID(duid_).toText() << " of length "
- << duid_.size()
- << " exceeds maximum allowed length of "
- << DUID::MAX_DUID_LEN);
- }
- if (lease_type_ != Lease::TYPE_NA &&
- lease_type_ != Lease::TYPE_TA &&
- lease_type_ != Lease::TYPE_PD) {
- isc_throw(BadValue, "invalid lease type "
- << lease_type_
- << " for lease with address " << addr6_
- << ". Expected 0, 1 or 2.");
- }
- if (hostname_.size() > HOSTNAME_MAX_LEN) {
- isc_throw(BadValue, "hostname "
- << hostname_ << " of length "
- << hostname_.size()
- << " exceeds maximum allowed length of "
- << HOSTNAME_MAX_LEN);
- }
- if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
- isc_throw(BadValue,
- "hwaddr " << HWAddr(hwaddr_, hwtype_).toText(false)
- << " of length " << hwaddr_.size()
- << " exceeds maximum allowed length of "
- << HWAddr::MAX_HWADDR_LEN);
- }
+/// @brief Create CQL_BIND objects for Lease6 Pointer
+///
+/// Fills in the CQL_BIND array for sending data in the Lease6 object to
+/// the database.
+void
+CqlLease6Exchange::createBindForDelete(
+ const IOAddress &address,
+ AnyArray &data,
+ StatementTag statement_tag /* = NULL */) {
+ (void)statement_tag; // [maybe_unused]
+
+ // Set up the structures for the various components of the lease4
+ // structure.
+ try {
+ // address: int
+ // The address in the Lease structure is an IOAddress object.
+ // Convert this to an integer for storage.
+ address_ = address.toText();
- isc::asiolink::IOAddress addr(addr6_);
- DuidPtr duid(new DUID(duid_));
- HWAddrPtr hwaddr;
- if (hwaddr_.size()) {
- hwaddr.reset(new HWAddr(hwaddr_, hwtype_));
- hwaddr->source_ = hwaddr_source_;
- }
+ // Start with a fresh array.
+ data.clear();
+ data.add(&address_);
- // 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>(lease_type_), addr, duid, iaid_,
- pref_lifetime_, valid_lifetime_, 0, 0, subnet_id_, fqdn_fwd_,
- fqdn_rev_, hostname_, hwaddr, prefixlen_));
-
- time_t cltt = 0;
- CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_,
- cltt);
- result->cltt_ = cltt;
-
- result->state_ = state_;
- return result;
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError,
- "createBindForReceive(): "
- "could not convert data to Lease4, reason: "
- << ex.what());
- }
- return Lease6Ptr();
+ } catch (const Exception &ex) {
+ isc_throw(DbOperationError,
+ "CqlLease6Exchange::createBindForDelete(): "
+ "could not create bind array with address: "
+ << address_ << ", reason: " << ex.what());
}
+}
- void* retrieve() {
- isc_throw(NotImplemented, "retrieve(): Not implemented yet.");
- }
+/// @brief Create BIND array to receive data
+///
+/// Creates a CQL_BIND array to receive Lease6 data from the database.
+void
+CqlLease6Exchange::createBindForSelect(
+ AnyArray &data, StatementTag /* statement_tag = NULL */) {
-private:
- Lease6Ptr lease_; ///< Pointer to lease object
- std::string addr6_; ///< IPv6 address
- std::vector<cass_byte_t> duid_; ///< Client identification
- cass_int64_t pref_lifetime_; ///< Preferred lifetime
- cass_int32_t lease_type_; ///< Lease type
- cass_int32_t iaid_; ///< Identity association ID
- cass_int32_t prefixlen_; ///< Prefix length
- cass_int32_t hwtype_; ///< Hardware type
- cass_int32_t hwaddr_source_; ///< Source of the hardware
- /// address
-};
+ // Start with a fresh array.
+ data.clear();
-CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
- : LeaseMgr(), dbconn_(parameters), exchange4_(new CqlLease4Exchange()),
- exchange6_(new CqlLease6Exchange()),
- versionExchange_(new CqlVersionExchange()) {
- dbconn_.openDatabase();
- dbconn_.prepareStatements(CqlLeaseMgr::tagged_statements_);
-}
+ // address: varchar
+ data.add(&address_);
-CqlLeaseMgr::~CqlLeaseMgr() {
- // There is no need to close the database in this destructor: it is
- // closed in the destructor of the dbconn_ member variable.
-}
+ // valid_lifetime_: bigint
+ data.add(&valid_lifetime_);
-std::string
-CqlLeaseMgr::getDBVersion() {
- std::stringstream tmp;
- tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR;
- tmp << "." << CQL_SCHEMA_VERSION_MINOR;
- tmp << ", library cassandra_static";
- return tmp.str();
-}
+ // expire: bigint
+ data.add(&expire_);
-bool
-CqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
- CqlDataArray& data,
- CqlLeaseExchange& exchange) {
- CassError rc;
- CassStatement* statement = NULL;
- CassFuture* future = NULL;
-
- statement = cass_prepared_bind(dbconn_.statements_[stindex]);
- if (!statement) {
- isc_throw(DbOperationError,
- "unable to bind statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
+ // subnet_id: int
+ data.add(&subnet_id_);
- if (dbconn_.force_consistency_) {
- rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
- if (rc != CASS_OK) {
- cass_statement_free(statement);
- isc_throw(
- DbOperationError,
- "unable to set statement consistency for statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
- }
+ // pref_lifetime: bigint
+ data.add(&pref_lifetime_);
- CqlCommon::bindData(statement, stindex, data, exchange,
- CqlLeaseMgr::tagged_statements_);
+ // duid: blob
+ data.add(&duid_);
- future = cass_session_execute(dbconn_.session_, statement);
- if (!future) {
- cass_statement_free(statement);
- isc_throw(DbOperationError,
- "unable to execute statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
- 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);
- return false;
- }
+ // iaid: int
+ data.add(&iaid_);
- // Check if statement has been applied.
- bool applied = exchange.hasStatementBeenApplied(future);
+ // lease_type: int
+ data.add(&lease_type_);
- // Free resources.
- cass_future_free(future);
- cass_statement_free(statement);
- return applied;
-}
+ // prefix_len: int
+ data.add(&prefix_len_);
-bool
-CqlLeaseMgr::addLease(const Lease4Ptr& lease) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
- .arg(lease->addr_.toText());
+ // fqdn_fwd: boolean
+ data.add(&fqdn_fwd_);
- CqlDataArray data;
- exchange4_->createBindForSend(lease, data);
- return addLeaseCommon(INSERT_LEASE4, data, *exchange4_);
-}
+ // fqdn_rev: boolean
+ data.add(&fqdn_rev_);
-bool
-CqlLeaseMgr::addLease(const Lease6Ptr& lease) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR6)
- .arg(lease->addr_.toText());
+ // hostname: varchar
+ data.add(&hostname_);
+
+ // hwaddr: blob
+ data.add(&hwaddr_);
+
+ // hwtype: int
+ data.add(&hwtype_);
+
+ // hwaddr_source: int
+ data.add(&hwaddr_source_);
- CqlDataArray data;
- exchange6_->createBindForSend(lease, data);
- return addLeaseCommon(INSERT_LEASE6, data, *exchange6_);
+ // state: int
+ data.add(&state_);
}
-template <typename Exchange, typename LeaseCollection>
-void
-CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
- CqlDataArray& data,
- Exchange& exchange,
- LeaseCollection& result,
- bool single) const {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
- .arg(dbconn_.tagged_statements_[stindex].name_);
+boost::any
+CqlLease6Exchange::retrieve() {
+ try {
+ // Sanity checks
+ if (address_.size() > ADDRESS6_TEXT_MAX_LEN) {
+ isc_throw(BadValue,
+ "address " << address_ << " of length " << address_.size()
+ << " exceeds maximum allowed length of "
+ << ADDRESS6_TEXT_MAX_LEN);
+ }
+ if (duid_.size() > DUID::MAX_DUID_LEN) {
+ isc_throw(BadValue,
+ "duid " << DUID(duid_).toText() << " of length "
+ << duid_.size()
+ << " exceeds maximum allowed length of "
+ << DUID::MAX_DUID_LEN);
+ }
+ if (lease_type_ != Lease::TYPE_NA && lease_type_ != Lease::TYPE_TA &&
+ lease_type_ != Lease::TYPE_PD) {
+ isc_throw(BadValue,
+ "invalid lease type "
+ << lease_type_ << " for lease with address "
+ << address_ << ". Expected 0, 1 or 2.");
+ }
+ if (hostname_.size() > HOSTNAME_MAX_LEN) {
+ isc_throw(BadValue,
+ "hostname " << hostname_ << " of length "
+ << hostname_.size()
+ << " exceeds maximum allowed length of "
+ << HOSTNAME_MAX_LEN);
+ }
+ if (hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+ isc_throw(BadValue,
+ "hwaddr " << HWAddr(hwaddr_, hwtype_).toText(false)
+ << " of length " << hwaddr_.size()
+ << " exceeds maximum allowed length of "
+ << HWAddr::MAX_HWADDR_LEN);
+ }
- CassError rc;
- CassStatement* statement = NULL;
- CassFuture* future = NULL;
+ IOAddress addr(address_);
- statement = cass_prepared_bind(dbconn_.statements_[stindex]);
- if (!statement) {
- isc_throw(DbOperationError, "unable to bind statement");
- }
+ DuidPtr duid(new DUID(duid_));
+ HWAddrPtr hwaddr;
+ if (hwaddr_.size()) {
+ hwaddr.reset(new HWAddr(hwaddr_, hwtype_));
+ hwaddr->source_ = hwaddr_source_;
+ }
- if (dbconn_.force_consistency_) {
- rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
- if (rc != CASS_OK) {
- cass_statement_free(statement);
- isc_throw(
- DbOperationError,
- "unable to set statement consistency for statement "
- << dbconn_.tagged_statements_[stindex].name_);
+ if (hwaddr && hwaddr->hwaddr_ == NULL_HWADDR) {
+ hwaddr.reset();
}
- }
- CqlCommon::bindData(statement, stindex, data,
- static_cast<SqlExchange>(*exchange),
- CqlLeaseMgr::tagged_statements_);
+ // 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>(lease_type_), addr, duid, iaid_,
+ pref_lifetime_, valid_lifetime_, 0, 0, subnet_id_,
+ fqdn_fwd_, fqdn_rev_, hostname_, hwaddr, prefix_len_));
+
+ time_t cltt = 0;
+ CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
+ result->cltt_ = cltt;
- future = cass_session_execute(dbconn_.session_, statement);
- if (!future) {
- cass_statement_free(statement);
+ result->state_ = state_;
+ return result;
+ } catch (const Exception &ex) {
isc_throw(DbOperationError,
- "unable to execute statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
- cass_future_wait(future);
- std::string error;
- std::stringstream message;
- message << "unable to GET using statement "
- << CqlLeaseMgr::tagged_statements_[stindex].name_;
- dbconn_.checkStatementError(error, future, message.str().c_str());
- rc = cass_future_error_code(future);
- if (rc != CASS_OK) {
- cass_future_free(future);
- cass_statement_free(statement);
- isc_throw(DbOperationError, error);
+ "CqlLease6Exchange::retrieve(): "
+ "could not convert data to Lease4, reason: "
+ << ex.what());
}
+ return Lease6Ptr();
+}
- 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);
- CqlDataArray data;
- exchange->createBindForReceive(data);
- // Get data.
- for (size_t i = 0U; i < data.size(); i++) {
- CqlCommon::getData(row, i, i, *exchange, data);
- }
- result.push_back(exchange->retrieveLease());
- }
+void
+CqlLease6Exchange::getLeaseCollection(StatementTag &statement_tag,
+ AnyArray &data,
+ Lease6Collection &result) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
+ .arg(statement_tag);
+
+ AnyArray collection = executeSelect(connection_, data, statement_tag);
- 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 statement "
- << dbconn_.tagged_statements_[stindex].name_);
+ // Transfer Lease6 objects to result.
+ for (boost::any &lease : collection) {
+ result.push_back(boost::any_cast<Lease6Ptr>(lease));
}
}
void
-CqlLeaseMgr::getLease(StatementIndex stindex,
- CqlDataArray& data,
- 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, data, exchange4_, collection, true);
+CqlLease6Exchange::getLease(StatementTag &statement_tag,
+ AnyArray &data,
+ Lease6Ptr &result) {
+ // This particular method is called when only one or zero matches is
+ // expected.
+ Lease6Collection collection;
+ getLeaseCollection(statement_tag, data, collection);
// Return single record if present, else clear the lease.
- if (collection.empty()) {
+ const size_t collection_size = collection.size();
+ if (collection_size >= 2u) {
+ isc_throw(
+ MultipleRecords,
+ "CqlLease6Exchange::getLease(): multiple records were found in "
+ "the database where only one was expected for statement "
+ << statement_tag);
+ } else if (collection_size == 0u) {
result.reset();
} else {
result = *collection.begin();
}
void
-CqlLeaseMgr::getLease(StatementIndex stindex,
- CqlDataArray& data,
- 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, data, exchange6_, collection, true);
+CqlLease6Exchange::getExpiredLeases(const size_t &max_leases,
+ Lease6Collection &expired_leases) {
+ // Set up the WHERE clause value
+ cass_int32_t keep_state = Lease::STATE_EXPIRED_RECLAIMED;
+ cass_int64_t timestamp = static_cast<cass_int64_t>(time(NULL));
- // Return single record if present, else clear the lease.
- if (collection.empty()) {
- result.reset();
- } else {
- result = *collection.begin();
+ // If the number of leases is 0, we will return all leases. This is
+ // achieved by setting the limit to a very high value.
+ cass_int32_t limit = max_leases > 0u ?
+ static_cast<cass_int32_t>(max_leases) :
+ std::numeric_limits<cass_int32_t>::max();
+
+ for (cass_int32_t state = Lease::STATE_DEFAULT;
+ state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
+ if (state == keep_state) {
+ continue;
+ }
+
+ AnyArray data;
+ data.add(&state);
+ data.add(×tamp);
+ data.add(&limit);
+
+ // Retrieve leases from the database.
+ Lease6Collection temp_collection;
+ getLeaseCollection(CqlLease6Exchange::GET_LEASE6_EXPIRE, data,
+ temp_collection);
+
+ for (Lease6Ptr &lease : temp_collection) {
+ expired_leases.push_back(lease);
+ }
+ }
+}
+
+CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap ¶meters)
+ : LeaseMgr(), dbconn_(parameters) {
+ dbconn_.openDatabase();
+ dbconn_.prepareStatements(CqlLease4Exchange::tagged_statements_);
+ dbconn_.prepareStatements(CqlLease6Exchange::tagged_statements_);
+ dbconn_.prepareStatements(CqlVersionExchange::tagged_statements_);
+}
+
+CqlLeaseMgr::~CqlLeaseMgr() {
+ // There is no need to close the database in this destructor: it is
+ // closed in the destructor of the dbconn_ member variable.
+}
+
+std::string
+CqlLeaseMgr::getDBVersion() {
+ std::stringstream tmp;
+ tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR;
+ tmp << "." << CQL_SCHEMA_VERSION_MINOR;
+ tmp << ", library cassandra_static";
+ return tmp.str();
+}
+
+bool
+CqlLeaseMgr::addLease(const Lease4Ptr &lease) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
+ .arg(lease->addr_.toText());
+
+ AnyArray data;
+
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->createBindForInsert(lease, data);
+ try {
+ exchange4->executeMutation(dbconn_, data,
+ CqlLease4Exchange::INSERT_LEASE4);
+ } catch (const Exception &exception) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_LEASE_EXCEPTION_THROWN)
+ .arg(exception.what());
+ return false;
}
+ return true;
}
-// Basic lease access methods. Obtain leases from the database using various
-// criteria.
+bool
+CqlLeaseMgr::addLease(const Lease6Ptr &lease) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR6)
+ .arg(lease->addr_.toText());
+
+ AnyArray data;
+
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
+ exchange6->createBindForInsert(lease, data);
+ try {
+ exchange6->executeMutation(dbconn_, data,
+ CqlLease6Exchange::INSERT_LEASE6);
+ } catch (const Exception &exception) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_LEASE_EXCEPTION_THROWN)
+ .arg(exception.what());
+ return false;
+ }
+ return true;
+}
Lease4Ptr
-CqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
+CqlLeaseMgr::getLease4(const IOAddress &addr) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4)
.arg(addr.toText());
// Set up the WHERE clause value
- CqlDataArray data;
+ AnyArray data;
- cass_int32_t addr4_data = static_cast<cass_int32_t>(addr.toUint32());
- data.add(reinterpret_cast<void*>(&addr4_data));
+ cass_int32_t address = static_cast<cass_int32_t>(addr.toUint32());
+ data.add(&address);
- // Get the data
+ // Get the data.
Lease4Ptr result;
- getLease(GET_LEASE4_ADDR, data, result);
+
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->getLease(CqlLease4Exchange::GET_LEASE4_ADDR, data, result);
return result;
}
Lease4Collection
-CqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
+CqlLeaseMgr::getLease4(const HWAddr &hwaddr) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_HWADDR)
.arg(hwaddr.toText());
// Set up the WHERE clause value
- CqlDataArray data;
+ AnyArray data;
- std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
- data.add(reinterpret_cast<void*>(&hwaddr_data));
+ CassBlob hwaddr_data(hwaddr.hwaddr_);
+ data.add(&hwaddr_data);
- // Get the data
+ // Get the data.
Lease4Collection result;
- getLeaseCollection(GET_LEASE4_HWADDR, data, result);
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->getLeaseCollection(CqlLease4Exchange::GET_LEASE4_HWADDR, data,
+ result);
return result;
}
Lease4Ptr
-CqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
+CqlLeaseMgr::getLease4(const HWAddr &hwaddr, SubnetID subnet_id) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_GET_SUBID_HWADDR)
.arg(subnet_id)
.arg(hwaddr.toText());
// Set up the WHERE clause value
- CqlDataArray data;
+ AnyArray data;
- std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
- data.add(reinterpret_cast<void*>(&hwaddr_data));
+ CassBlob hwaddr_data(hwaddr.hwaddr_);
+ data.add(&hwaddr_data);
cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
- data.add(reinterpret_cast<void*>(&subnet_id_data));
+ data.add(&subnet_id_data);
- // Get the data
+ // Get the data.
Lease4Ptr result;
- getLease(GET_LEASE4_HWADDR_SUBID, data, result);
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->getLease(CqlLease4Exchange::GET_LEASE4_HWADDR_SUBID, data,
+ result);
return result;
}
Lease4Collection
-CqlLeaseMgr::getLease4(const ClientId& clientid) const {
+CqlLeaseMgr::getLease4(const ClientId &clientid) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_GET_CLIENTID)
.arg(clientid.toText());
// Set up the WHERE clause value
- CqlDataArray data;
+ AnyArray data;
- std::vector<cass_byte_t> client_id_data = clientid.getClientId();
- data.add(reinterpret_cast<void*>(&client_id_data));
+ CassBlob client_id_data(clientid.getClientId());
+ data.add(&client_id_data);
- // Get the data
+ // Get the data.
Lease4Collection result;
- getLeaseCollection(GET_LEASE4_CLIENTID, data, result);
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->getLeaseCollection(CqlLease4Exchange::GET_LEASE4_CLIENTID, data,
+ result);
return result;
}
Lease4Ptr
-CqlLeaseMgr::getLease4(const ClientId& clientid,
- const HWAddr& hwaddr,
+CqlLeaseMgr::getLease4(const ClientId &clientid,
+ const HWAddr &hwaddr,
SubnetID subnet_id) const {
/// This method is currently not implemented because allocation engine
/// searches for the lease using HW address or client identifier.
.arg(hwaddr.toText())
.arg(subnet_id);
- isc_throw(NotImplemented, "The CqlLeaseMgr::getLease4 method was"
- " called, but it is not implemented");
+ isc_throw(NotImplemented, "CqlLeaseMgr::getLease4() not implemented yet");
}
Lease4Ptr
-CqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
+CqlLeaseMgr::getLease4(const ClientId &clientid, SubnetID subnet_id) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_GET_SUBID_CLIENTID)
.arg(subnet_id)
.arg(clientid.toText());
// Set up the WHERE clause value
- CqlDataArray data;
+ AnyArray data;
- std::vector<uint8_t> client_id_data = clientid.getClientId();
- data.add(reinterpret_cast<void*>(&client_id_data));
+ CassBlob client_id_data(clientid.getClientId());
+ data.add(&client_id_data);
cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
- data.add(reinterpret_cast<void*>(&subnet_id_data));
+ data.add(&subnet_id_data);
- // Get the data
+ // Get the data.
Lease4Ptr result;
- getLease(GET_LEASE4_CLIENTID_SUBID, data, result);
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->getLease(CqlLease4Exchange::GET_LEASE4_CLIENTID_SUBID, data,
+ result);
return result;
}
Lease6Ptr
-CqlLeaseMgr::getLease6(Lease::Type lease_type,
- const isc::asiolink::IOAddress& addr) const {
+CqlLeaseMgr::getLease6(Lease::Type lease_type, const IOAddress &addr) const {
std::string addr_data = addr.toText();
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR6)
.arg(addr_data)
.arg(lease_type);
// Set up the WHERE clause value
- CqlDataArray data;
+ AnyArray data;
if (addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
- isc_throw(BadValue, "getLease6(): "
- "address "
- << addr_data << " of length "
- << addr_data.size()
- << " exceeds maximum allowed length of "
- << ADDRESS6_TEXT_MAX_LEN);
+ isc_throw(BadValue,
+ "CqlLeaseMgr::getLease6(): "
+ "address "
+ << addr_data << " of length " << addr_data.size()
+ << " exceeds maximum allowed length of "
+ << ADDRESS6_TEXT_MAX_LEN);
}
- data.add(reinterpret_cast<void*>(&addr_data));
+ data.add(&addr_data);
cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
- data.add(reinterpret_cast<void*>(&lease_type_data));
+ data.add(&lease_type_data);
Lease6Ptr result;
- getLease(GET_LEASE6_ADDR, data, result);
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
+ exchange6->getLease(CqlLease6Exchange::GET_LEASE6_ADDR, data, result);
return result;
}
Lease6Collection
CqlLeaseMgr::getLeases6(Lease::Type lease_type,
- const DUID& duid,
+ const DUID &duid,
uint32_t iaid) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_GET_IAID_DUID)
.arg(lease_type);
// Set up the WHERE clause value
- CqlDataArray data;
-
- std::vector<cass_byte_t> duid_data = duid.getDuid();
- data.add(reinterpret_cast<void*>(&duid_data));
+ AnyArray data;
+ CassBlob duid_data(duid.getDuid());
cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
- data.add(reinterpret_cast<void*>(&iaid_data));
+
+ data.add(&duid_data);
+ data.add(&iaid_data);
cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
- data.add(reinterpret_cast<void*>(&lease_type_data));
+ data.add(&lease_type_data);
- // ... and get the data
+ // Get the data.
Lease6Collection result;
- getLeaseCollection(GET_LEASE6_DUID_IAID, data, result);
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
+ exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_DUID_IAID, data,
+ result);
return result;
}
Lease6Collection
CqlLeaseMgr::getLeases6(Lease::Type lease_type,
- const DUID& duid,
+ const DUID &duid,
uint32_t iaid,
SubnetID subnet_id) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
.arg(lease_type);
// Set up the WHERE clause value
- CqlDataArray data;
-
- std::vector<cass_byte_t> duid_data = duid.getDuid();
- data.add(reinterpret_cast<void*>(&duid_data));
+ AnyArray data;
+ CassBlob duid_data(duid.getDuid());
cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
- data.add(reinterpret_cast<void*>(&iaid_data));
- cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
- data.add(reinterpret_cast<void*>(&subnet_id_data));
+ data.add(&duid_data);
+ data.add(&iaid_data);
cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
- data.add(reinterpret_cast<void*>(&lease_type_data));
+ data.add(&lease_type_data);
+
+ cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+ data.add(&subnet_id_data);
- // ... and get the data
+ // Get the data.
Lease6Collection result;
- getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, data, result);
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
+ exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_DUID_IAID_SUBID,
+ data, result);
return result;
}
void
-CqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
+CqlLeaseMgr::getExpiredLeases4(Lease4Collection &expired_leases,
const size_t max_leases) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_CQL_GET_EXPIRED6)
+ DHCPSRV_CQL_GET_EXPIRED4)
.arg(max_leases);
- getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
+
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->getExpiredLeases(max_leases, expired_leases);
}
void
-CqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
+CqlLeaseMgr::getExpiredLeases6(Lease6Collection &expired_leases,
const size_t max_leases) const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_CQL_GET_EXPIRED4)
+ DHCPSRV_CQL_GET_EXPIRED6)
.arg(max_leases);
- getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
-}
-
-template <typename LeaseCollection>
-void
-CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
- const size_t max_leases,
- StatementIndex statement_index) const {
- // Set up the WHERE clause value
- cass_int32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
- cass_int64_t timestamp = static_cast<cass_int64_t>(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.
- cass_int32_t limit = max_leases > 0 ?
- static_cast<cass_int32_t>(max_leases) :
- std::numeric_limits<cass_int32_t>::max();
-
- for (cass_int32_t state = Lease::STATE_DEFAULT;
- state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
- if (state == keepState) {
- continue;
- }
- LeaseCollection tempCollection;
- CqlDataArray data;
-
- data.add(reinterpret_cast<void*>(&state));
- data.add(reinterpret_cast<void*>(×tamp));
- data.add(reinterpret_cast<void*>(&limit));
-
- // Retrieve leases from the database.
- getLeaseCollection(statement_index, data, tempCollection);
-
- typedef typename LeaseCollection::iterator LeaseCollectionIt;
-
- for (LeaseCollectionIt it = tempCollection.begin();
- it != tempCollection.end(); ++it) {
- expired_leases.push_back((*it));
- }
- }
-}
-
-void
-CqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
- CqlDataArray& data,
- CqlLeaseExchange& exchange) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4)
- .arg(dbconn_.tagged_statements_[stindex].name_);
-
- CassError rc;
- CassStatement* statement = NULL;
- CassFuture* future = NULL;
-
- statement = cass_prepared_bind(dbconn_.statements_[stindex]);
- if (!statement) {
- isc_throw(DbOperationError, "unable to bind statement");
- }
-
- if (dbconn_.force_consistency_) {
- rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
- if (rc != CASS_OK) {
- cass_statement_free(statement);
- isc_throw(
- DbOperationError,
- "unable to set statement consistency for statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
- }
-
- CqlCommon::bindData(statement, stindex, data, exchange,
- CqlLeaseMgr::tagged_statements_);
-
- future = cass_session_execute(dbconn_.session_, statement);
- if (!future) {
- cass_statement_free(statement);
- isc_throw(DbOperationError,
- "unable to execute statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
- 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);
- }
-
- // Check if statement has been applied.
- size_t row_count;
- size_t column_count;
- bool applied =
- exchange.hasStatementBeenApplied(future, &row_count, &column_count);
- // Free resources.
- cass_future_free(future);
- cass_statement_free(statement);
-
- if (!applied) {
- isc_throw(NoSuchLease, "Statement has not been applied.");
- }
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
+ exchange6->getExpiredLeases(max_leases, expired_leases);
}
void
-CqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
- const StatementIndex stindex = UPDATE_LEASE4;
-
+CqlLeaseMgr::updateLease4(const Lease4Ptr &lease) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_UPDATE_ADDR4)
.arg(lease->addr_.toText());
- // Create the BIND array for the data being updated
- CqlDataArray data;
- exchange4_->createBindForSend(lease, data);
- data.remove(0);
-
- // Set up the WHERE clause and append it to the bind array.
- cass_int32_t addr4_data =
- static_cast<cass_int32_t>(lease->addr_.toUint32());
- data.add(reinterpret_cast<void*>(&addr4_data));
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
- // Drop to common update code
- updateLeaseCommon(stindex, data, *exchange4_);
+ try {
+ AnyArray data;
+ exchange4->createBindForUpdate(lease, data,
+ CqlLease4Exchange::UPDATE_LEASE4);
+ exchange4->executeMutation(dbconn_, data,
+ CqlLease4Exchange::UPDATE_LEASE4);
+ } catch (const StatementNotApplied &exception) {
+ isc_throw(NoSuchLease, exception.what());
+ }
}
void
-CqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
- const StatementIndex stindex = UPDATE_LEASE6;
- std::string lease_addr_data = lease->addr_.toText();
+CqlLeaseMgr::updateLease6(const Lease6Ptr &lease) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_UPDATE_ADDR6)
- .arg(lease_addr_data);
-
- // Create the BIND array for the data being updated
- CqlDataArray data;
- exchange6_->createBindForSend(lease, data);
- data.remove(0);
-
- // Set up the WHERE clause and append it to the BIND array
- if (lease_addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
- isc_throw(BadValue, "updateLease6(): "
- "address "
- << lease_addr_data << " of length "
- << lease_addr_data.size()
- << " exceeds maximum allowed length "
- "of "
- << ADDRESS6_TEXT_MAX_LEN);
- }
- data.add(reinterpret_cast<void*>(&lease_addr_data));
-
- // Drop to common update code
- updateLeaseCommon(stindex, data, *exchange6_);
-}
-
-bool
-CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
- CqlDataArray& data,
- CqlLeaseExchange& exchange) {
- CassError rc;
- CassStatement* statement = NULL;
- CassFuture* future = NULL;
-
- statement = cass_prepared_bind(dbconn_.statements_[stindex]);
- if (!statement) {
- isc_throw(DbOperationError, "unable to bind statement");
- }
-
- if (dbconn_.force_consistency_) {
- rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
- if (rc != CASS_OK) {
- cass_statement_free(statement);
- isc_throw(
- DbOperationError,
- "unable to set statement consistency for statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
- }
+ .arg(lease->addr_.toText());
- CqlCommon::bindData(statement, stindex, data, exchange,
- CqlLeaseMgr::tagged_statements_);
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
- future = cass_session_execute(dbconn_.session_, statement);
- if (!future) {
- cass_statement_free(statement);
- isc_throw(DbOperationError,
- "unable to execute statement "
- << dbconn_.tagged_statements_[stindex].name_);
- }
- cass_future_wait(future);
- std::string error;
- dbconn_.checkStatementError(error, future, stindex, "unable to DELETE");
- rc = cass_future_error_code(future);
- if (rc != CASS_OK) {
- cass_future_free(future);
- cass_statement_free(statement);
- isc_throw(DbOperationError, error);
+ try {
+ AnyArray data;
+ exchange6->createBindForUpdate(lease, data,
+ CqlLease6Exchange::UPDATE_LEASE6);
+ exchange6->executeMutation(dbconn_, data,
+ CqlLease6Exchange::UPDATE_LEASE6);
+ } catch (const StatementNotApplied &exception) {
+ isc_throw(NoSuchLease, exception.what());
}
-
- // Check if statement has been applied.
- bool applied = exchange.hasStatementBeenApplied(future);
-
- // Free resources.
- cass_future_free(future);
- cass_statement_free(statement);
-
- return applied;
}
bool
-CqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
+CqlLeaseMgr::deleteLease(const IOAddress &addr) {
std::string addr_data = addr.toText();
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_ADDR)
.arg(addr_data);
// Set up the WHERE clause value
- CqlDataArray data;
+ AnyArray data;
- if (addr.isV4()) {
- cass_int32_t addr4_data = static_cast<cass_int32_t>(addr.toUint32());
- data.add(reinterpret_cast<void*>(&addr4_data));
- return deleteLeaseCommon(DELETE_LEASE4, data, *exchange4_);
- } else {
- if (addr_data.size() > ADDRESS6_TEXT_MAX_LEN) {
- isc_throw(BadValue, "deleteLease(): "
- "address "
- << addr_data << " of length "
- << addr_data.size()
- << " exceeds maximum allowed length "
- "of "
- << ADDRESS6_TEXT_MAX_LEN);
+ try {
+ if (addr.isV4()) {
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->createBindForDelete(addr, data,
+ CqlLease4Exchange::DELETE_LEASE4);
+ exchange4->executeMutation(dbconn_, data,
+ CqlLease4Exchange::DELETE_LEASE4);
+ } else if (addr.isV6()) {
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
+ exchange6->createBindForDelete(addr, data,
+ CqlLease6Exchange::DELETE_LEASE6);
+ exchange6->executeMutation(dbconn_, data,
+ CqlLease6Exchange::DELETE_LEASE6);
+ } else {
+ return false;
}
- data.add(reinterpret_cast<void*>(&addr_data));
- return deleteLeaseCommon(DELETE_LEASE6, data, *exchange6_);
+ } catch (const Exception &exception) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_LEASE_EXCEPTION_THROWN)
+ .arg(exception.what());
+ return false;
}
+ return true;
}
uint64_t
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED4)
.arg(secs);
- return deleteExpiredReclaimedLeasesCommon(secs,
- DELETE_LEASE4_STATE_EXPIRED);
+ AnyArray data;
+ uint64_t n_of_deleted_leases = 0u;
+ cass_int32_t limit = 1024;
+
+ // State is reclaimed.
+ cass_int32_t state =
+ static_cast<cass_int32_t>(Lease::STATE_EXPIRED_RECLAIMED);
+ data.add(&state);
+
+ // Expiration timestamp.
+ cass_int64_t expiration =
+ static_cast<cass_int64_t>(time(NULL) - static_cast<time_t>(secs));
+ data.add(&expiration);
+
+ data.add(&limit);
+
+ // Get the data.
+ Lease4Collection leases;
+ std::unique_ptr<CqlLease4Exchange> exchange4(
+ new CqlLease4Exchange(dbconn_));
+ exchange4->getLeaseCollection(CqlLease4Exchange::GET_LEASE4_EXPIRE, data,
+ leases);
+ for (Lease4Ptr &lease : leases) {
+ if (deleteLease(lease->addr_)) {
+ ++n_of_deleted_leases;
+ }
+ }
+ return n_of_deleted_leases;
}
uint64_t
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED6)
.arg(secs);
- return deleteExpiredReclaimedLeasesCommon(secs,
- DELETE_LEASE6_STATE_EXPIRED);
-}
-
-uint64_t
-CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(
- const uint32_t secs, StatementIndex statement_index) {
- // Set up the WHERE clause value
-
- CqlDataArray data;
- uint64_t result = 0;
+ AnyArray data;
+ uint64_t n_of_deleted_leases = 0u;
+ cass_int32_t limit = 1024;
// State is reclaimed.
cass_int32_t state =
static_cast<cass_int32_t>(Lease::STATE_EXPIRED_RECLAIMED);
- data.add(reinterpret_cast<void*>(&state));
+ data.add(&state);
// Expiration timestamp.
cass_int64_t expiration =
static_cast<cass_int64_t>(time(NULL) - static_cast<time_t>(secs));
- data.add(reinterpret_cast<void*>(&expiration));
-
- // Get the data
- Lease4Collection result4Leases;
- Lease6Collection result6Leases;
- switch (statement_index) {
- case DELETE_LEASE4_STATE_EXPIRED:
- getLeaseCollection(statement_index, data, result4Leases);
- break;
- case DELETE_LEASE6_STATE_EXPIRED:
- getLeaseCollection(statement_index, data, result6Leases);
- break;
- default:
- break;
- }
- for (Lease4Collection::iterator it = result4Leases.begin();
- it != result4Leases.end(); ++it) {
- if (deleteLease((*it)->addr_)) {
- result++;
- }
- }
- for (Lease6Collection::iterator it = result6Leases.begin();
- it != result6Leases.end(); ++it) {
- if (deleteLease((*it)->addr_)) {
- result++;
+ data.add(&expiration);
+
+ data.add(&limit);
+
+ // Get the data.
+ Lease6Collection leases;
+ std::unique_ptr<CqlLease6Exchange> exchange6(
+ new CqlLease6Exchange(dbconn_));
+ exchange6->getLeaseCollection(CqlLease6Exchange::GET_LEASE6_EXPIRE, data,
+ leases);
+ for (Lease6Ptr &lease : leases) {
+ if (deleteLease(lease->addr_)) {
+ ++n_of_deleted_leases;
}
}
- return result;
+ return n_of_deleted_leases;
}
size_t
-CqlLeaseMgr::wipeLeases4(const SubnetID& /*subnet_id*/) {
- isc_throw(NotImplemented, "wipeLeases4 is not implemented for Cassandra backend");
+CqlLeaseMgr::wipeLeases4(const SubnetID & /*subnet_id*/) {
+ isc_throw(NotImplemented,
+ "wipeLeases4 is not implemented for Cassandra backend");
}
size_t
-CqlLeaseMgr::wipeLeases6(const SubnetID& /*subnet_id*/) {
- isc_throw(NotImplemented, "wipeLeases6 is not implemented for Cassandra backend");
+CqlLeaseMgr::wipeLeases6(const SubnetID & /*subnet_id*/) {
+ isc_throw(NotImplemented,
+ "wipeLeases6 is not implemented for Cassandra backend");
}
std::string
return std::string("Cassandra Database");
}
-std::pair<unsigned int, unsigned int>
+VersionPair
CqlLeaseMgr::getVersion() const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_CQL_GET_VERSION);
- cass_int32_t version;
- cass_int32_t minor;
- CassError rc;
- CassStatement* statement = NULL;
- CassFuture* future = NULL;
-
- statement = cass_prepared_bind(dbconn_.statements_[GET_VERSION]);
- if (!statement) {
- isc_throw(DbOperationError, "unable to bind statement");
- }
-
- if (dbconn_.force_consistency_) {
- rc = cass_statement_set_consistency(statement, dbconn_.consistency_);
- if (rc != CASS_OK) {
- cass_statement_free(statement);
- isc_throw(
- DbOperationError,
- "unable to set statement consistency for statement "
- << dbconn_.tagged_statements_[GET_VERSION].name_);
- }
- }
-
- future = cass_session_execute(dbconn_.session_, statement);
- if (!future) {
- cass_statement_free(statement);
- isc_throw(DbOperationError,
- "unable to execute statement "
- << dbconn_.tagged_statements_[GET_VERSION].name_);
- }
- cass_future_wait(future);
- std::string error;
- dbconn_.checkStatementError(error, future, "unable to GET version");
- rc = cass_future_error_code(future);
- if (rc != CASS_OK) {
- cass_future_free(future);
- cass_statement_free(statement);
- isc_throw(DbOperationError, error);
- }
-
- // Get major and minor versions.
- const CassResult* resultCollection = cass_future_get_result(future);
- CassIterator* rows = cass_iterator_from_result(resultCollection);
- CqlDataArray data;
- while (cass_iterator_next(rows)) {
- const CassRow* row = cass_iterator_get_row(rows);
- // version: int
- data.add(reinterpret_cast<void*>(&version));
- // minor: int
- data.add(reinterpret_cast<void*>(&minor));
- for (size_t i = 0U; i < data.size(); i++) {
- CqlCommon::getData(row, i, i, *versionExchange_, data);
- }
- }
-
- cass_iterator_free(rows);
- cass_result_free(resultCollection);
- cass_future_free(future);
- cass_statement_free(statement);
- return std::pair<unsigned int, unsigned int>(
- static_cast<unsigned int>(version), static_cast<unsigned int>(minor));
+ std::unique_ptr<CqlVersionExchange> version_exchange(
+ new CqlVersionExchange());
+ return version_exchange->retrieveVersion(dbconn_);
}
void
-// Copyright (C) 2015 - 2017 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
//
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#ifndef CQL_LEASE_MGR_H
#define CQL_LEASE_MGR_H
-#include <cassandra.h>
-
#include <dhcp/hwaddr.h>
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/cql_exchange.h>
#include <dhcpsrv/lease_mgr.h>
#include <boost/scoped_ptr.hpp>
-#include <boost/utility.hpp>
#include <string>
#include <utility>
///
/// Finally, all the CQL commands are pre-compiled.
///
- /// @param parameters A data structure relating keywords and values
+ /// @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::DbOperationError An operation on the open database has
/// failed.
- virtual bool addLease(const Lease4Ptr& lease);
+ virtual bool addLease(const Lease4Ptr& lease) override;
/// @brief Adds an IPv6 lease
///
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual bool addLease(const Lease6Ptr& lease);
+ virtual bool addLease(const Lease6Ptr& lease) override;
+
+ /// @brief Basic lease access methods. Obtain leases from the database using
+ /// various criteria.
+ /// @{
/// @brief Returns an IPv4 lease for specified IPv4 address
///
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const;
+ virtual Lease4Ptr
+ getLease4(const isc::asiolink::IOAddress& addr) const override;
/// @brief Returns existing IPv4 leases for specified hardware address.
///
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual Lease4Collection getLease4(const isc::dhcp::HWAddr& hwaddr) const;
+ virtual Lease4Collection
+ getLease4(const isc::dhcp::HWAddr& hwaddr) const override;
/// @brief Returns existing IPv4 leases for specified hardware address
/// and a subnet
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
virtual Lease4Ptr getLease4(const isc::dhcp::HWAddr& hwaddr,
- SubnetID subnet_id) const;
+ SubnetID subnet_id) const override;
/// @brief Returns existing IPv4 leases for specified client-id
///
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual Lease4Collection getLease4(const ClientId& clientid) const;
+ virtual Lease4Collection getLease4(const ClientId& clientid) const override;
/// @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 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 method is currently
+ /// not implemented for the CQL backend.
virtual Lease4Ptr getLease4(const ClientId& client_id,
const HWAddr& hwaddr,
- SubnetID subnet_id) const;
+ SubnetID subnet_id) const override;
/// @brief Returns existing IPv4 lease for specified client-id
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
virtual Lease4Ptr getLease4(const ClientId& clientid,
- SubnetID subnet_id) const;
+ SubnetID subnet_id) const override;
/// @brief Returns existing IPv6 lease for a given IPv6 address.
///
/// 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;
+ virtual Lease6Ptr
+ getLease6(Lease::Type type,
+ const isc::asiolink::IOAddress& addr) const override;
/// @brief Returns existing IPv6 leases for a given DUID+IA combination
///
/// 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;
+ virtual Lease6Collection getLeases6(Lease::Type type,
+ const DUID& duid,
+ uint32_t iaid) const override;
/// @brief Returns existing IPv6 lease for a given DUID+IA combination
///
virtual Lease6Collection getLeases6(Lease::Type type,
const DUID& duid,
uint32_t iaid,
- SubnetID subnet_id) const;
+ SubnetID subnet_id) const override;
/// @brief Returns a collection of expired DHCPv6 leases.
///
/// This method returns at most @c max_leases expired leases. The leases
/// @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& expired_leases,
- const size_t max_leases) const;
+ const size_t max_leases) const override;
/// @brief Returns a collection of expired DHCPv4 leases.
///
/// @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& expired_leases,
- const size_t max_leases) const;
+ const size_t max_leases) const override;
+
+ /// @}
/// @brief Updates IPv4 lease.
///
/// exist.
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual void updateLease4(const Lease4Ptr& lease4);
+ virtual void updateLease4(const Lease4Ptr& lease4) override;
/// @brief Updates IPv6 lease.
///
/// exist.
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual void updateLease6(const Lease6Ptr& lease6);
+ virtual void updateLease6(const Lease6Ptr& lease6) override;
/// @brief Deletes a lease.
///
/// @brief Deletes all expired and reclaimed DHCPv4 leases.
///
- /// @param secs Number of seconds since expiration of leases before
+ /// @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 secs);
+ virtual uint64_t
+ deleteExpiredReclaimedLeases4(const uint32_t secs) override;
/// @brief Deletes all expired and reclaimed DHCPv6 leases.
///
- /// @param secs Number of seconds since expiration of leases before
+ /// @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 secs);
+ virtual uint64_t
+ deleteExpiredReclaimedLeases6(const uint32_t secs) override;
/// @brief Removes specified IPv4 leases.
///
/// @brief Return backend type
///
/// @return Type of the backend.
- virtual std::string getType() const {
+ virtual std::string getType() const override {
return (std::string("cql"));
}
/// @brief Returns name of the database.
///
/// @return database name
- virtual std::string getName() const;
+ virtual std::string getName() const override;
/// @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;
+ virtual std::string getDescription() const override;
/// @brief Returns backend version.
///
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual std::pair<unsigned int, unsigned int> getVersion() const;
+ virtual VersionPair getVersion() const override;
/// @brief Commit Transactions
///
/// This is a no-op for Cassandra.
- virtual void commit();
+ virtual void commit() override;
/// @brief Rollback Transactions
///
/// This is a no-op for Cassandra.
- virtual void rollback();
-
- /// @brief Statement Tags
- ///
- /// The contents of the enum are indexes into the list of compiled CQL
- /// 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
- NUM_STATEMENTS // Number of statements
- };
+ virtual void rollback() override;
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 data array that has been created for the type
- /// of lease in question.
- /// @param exchange Exchange object to use
- ///
- /// @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,
- CqlDataArray& data,
- CqlLeaseExchange& 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 data array containing the where clause input parameters
- /// @param exchange Exchange object to use
- /// @param result Returned collection of Leases Note that any leases in
- /// the collection when this method is called are not erased: the
- /// new data is appended to the end.
- /// @param single If true, only a single data item is to be retrieved.
- /// If more than one is present, a MultipleRecords exception will
- /// be thrown.
- ///
- /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid.
- /// @throw isc::dhcp::DbOperationError An operation on the open database has
- /// failed.
- /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
- /// from the database where only one was expected.
- template <typename Exchange, typename LeaseCollection>
- void getLeaseCollection(StatementIndex stindex,
- CqlDataArray& data_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 data array containing the where clause input parameters
- /// @param result 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,
- CqlDataArray& data,
- Lease4Collection& result) const {
- getLeaseCollection(stindex, data, 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 data array containing input parameters for the query
- /// @param result 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,
- CqlDataArray& data,
- Lease6Collection& result) const {
- getLeaseCollection(stindex, data, 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 retrieving only a single lease.
- ///
- /// @param stindex Index of statement being executed
- /// @param data array containing input parameters for the query
- /// @param lease Lease4 object returned
- void getLease(StatementIndex stindex,
- CqlDataArray& data,
- 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 retrieving only a single lease.
- ///
- /// @param stindex Index of statement being executed
- /// @param data array containing input parameters for the query
- /// @param lease Lease6 object returned
- void getLease(StatementIndex stindex,
- CqlDataArray& data,
- Lease6Ptr& result) const;
-
- /// @brief Get expired leases common code.
- ///
- /// This method retrieves expired and not reclaimed leases from the
- /// lease database. The returned leases are ordered by the expiration
- /// time. The maximum number of leases to be returned is specified
- /// as an argument.
- ///
- /// @param [out] expired_leases Reference to the container where the
- /// retrieved leases are put.
- /// @param max_leases Maximum number of leases to be returned.
- /// @param statement_index One of the @c GET_LEASE4_EXPIRE or
- /// @c GET_LEASE6_EXPIRE.
- ///
- /// @tparam One of the @c Lease4Collection or @c Lease6Collection.
- template <typename LeaseCollection>
- void getExpiredLeasesCommon(LeaseCollection& expired_leases,
- const size_t max_leases,
- StatementIndex statement_index) const;
-
- /// @brief Update lease common code
- ///
- /// Holds the common code for updating a lease. It binds the parameters
- /// to the prepared statement, executes it, then checks how many rows
- /// were affected.
- ///
- /// @param stindex Index of prepared statement to be executed
- /// @param data 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.
- void updateLeaseCommon(StatementIndex stindex,
- CqlDataArray& data,
- CqlLeaseExchange& 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 data 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,
- CqlDataArray& data,
- CqlLeaseExchange& 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);
-
- /// @brief CQL queries used by CQL backend
- static CqlTaggedStatement tagged_statements_[];
-
/// @brief Database connection object
mutable CqlConnection 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".)
- /// @brief Exchange object for IPv4
- boost::scoped_ptr<CqlLease4Exchange> exchange4_;
- /// @brief Exchange object for IPv6
- boost::scoped_ptr<CqlLease6Exchange> exchange6_;
- /// @brief Exchange object for version
- boost::scoped_ptr<CqlVersionExchange> versionExchange_;
- /// @}
};
} // namespace dhcp
-// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2017 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
namespace isc {
namespace dhcp {
+/// @brief Database statement not applied
+class StatementNotApplied : public Exception {
+public:
+ StatementNotApplied(const char* file, size_t line, const char* what)
+ : isc::Exception(file, line, what) {
+ }
+};
+
/// @brief Multiple lease records found where one expected
class MultipleRecords : public Exception {
public:
isc::Exception(file, line, what) {}
};
-};
-};
+} // namespace isc
+} // namespace dhcp
#endif
A debug message issued when the server is about to add an IPv6 lease
with the specified address to the Cassandra backend database.
-% DHCPSRV_CQL_COMMIT committing to Cassandra database
+% DHCPSRV_CQL_COMMIT committing to Cassandra database.
A commit call been issued on the server. For Cassandra, this is a no-op.
-% DHCPSRV_CQL_BEGIN_TRANSACTION committing to Cassandra database.
-The server has issued a begin transaction call.
-
-% DHCPSRV_CQL_DB opening Cassandra lease database: %1
-This informational message is logged when a DHCP server (either V4 or
-V6) is about to open a Cassandra lease database. The parameters of
-the connection including database name and username needed to access it
-(but not the password if any) are logged.
+% DHCPSRV_CQL_CONNECTION_COMMIT committing to Cassandra database on current connection.
+A commit call been issued on the server. For Cassandra, this is a no-op.
% DHCPSRV_CQL_DEALLOC_ERROR An error occurred while closing the CQL connection: %1
This is an error message issued when a DHCP server (either V4 or V6) experienced
shutdown. This error is most likely a programmatic issue that is highly
unlikely to occur or negatively impact server operation.
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_START starting transaction %1.
+The server has issued a begin transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_REGISTER_TRANSACTION registering action on opened transaction %1.
+The server has issued a begin transaction call on an opened transaction.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_COMMIT applying commit on transaction %1.
+The server has issued a commit transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_REGISTER_TRANSACTION_COMMIT registering commit on opened transaction %1.
+The server has issued a commit transaction call on an opened transaction.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_ROLLBACK applying rollback on transaction %1.
+The server has issued a rollback transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_REGISTER_TRANSACTION_ROLLBACK registering rollback on opened transaction %1.
+The server has issued a rollback transaction call on an opened transaction.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_TRANSACTION_NOT_FOUND failed to select a transaction.
+The server failed to select a transaction to operate on.
+
+% DHCPSRV_CQL_BEGIN_TRANSACTION begin transaction.
+The server has issued a begin transaction call.
+
+% DHCPSRV_CQL_CONNECTION_BEGIN_TRANSACTION begin transaction on current connection.
+The server has issued a begin transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_START_TRANSACTION start transaction action has been initiated.
+The server has issued a start transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_COMMIT_TRANSACTION commit transaction action has been initiated.
+The server has issued a commit transaction call.
+
+% DHCPSRV_CQL_TRANSACTION_MGR_ROLLBACK_TRANSACTION rollback transaction action has been initiated.
+The server has issued a rollback transaction call.
+
+% DHCPSRV_CQL_DB opening Cassandra lease database: %1
+This informational message is logged when a DHCP server (either V4 or
+V6) is about to open a Cassandra lease database. The parameters of
+the connection including database name and username needed to access it
+(but not the password if any) are logged.
+
% DHCPSRV_CQL_DELETE_ADDR deleting lease for address %1
A debug message issued when the server is attempting to delete a lease from the
Cassandra database for the specified address.
A debug message issued when the server is about to obtain schema version
information from the Cassandra database.
-% DHCPSRV_CQL_ROLLBACK rolling back Cassandra database
-The code has issued a rollback call. For Cassandra, this is
-a no-op.
+% DHCPSRV_CQL_ROLLBACK rolling back Cassandra database.
+The code has issued a rollback call. For Cassandra, this is a no-op.
+
+% DHCPSRV_CQL_CONNECTION_ROLLBACK rolling back Cassandra database on current connection.
+The code has issued a rollback call. For Cassandra, this is a no-op.
% DHCPSRV_CQL_UPDATE_ADDR4 updating IPv4 lease for address %1
A debug message issued when the server is attempting to update IPv4
A debug message issued when the server is attempting to update IPv6
lease from the Cassandra database for the specified address.
+% DHCPSRV_CQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 server configuration with id %1
+A debug message issued when there is no existing DHCPv4 server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 server configuration with id %1
+A debug message issued when there is no existing DHCPv6 server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with id %1.
+A debug message issued when there already exists a DHCPv4 server configuration
+in database and this configuration is going to be updated.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with id %1.
+A debug message issued when there already exists a DHCPv6 server configuration
+in database and this configuration is going to be updated.
+
+% DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the DHCPv4 database server configuration
+A debug message issued when a DHCPv4 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_CQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the DHCPv6 database server configuration
+A debug message issued when a DHCPv6 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the DHCPv4 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCPv4 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_CQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the DHCPv6 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCPv6 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's version from database
+Obtains the DHCPv4 server configuration's version from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's version from database
+Obtains the DHCPv6 server configuration's version from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database
+Obtains the DHCPv4 server JSON configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database
+Obtains the DHCPv6 server JSON configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG4_GENERIC Obtaining the DHCPv4 server GENERIC configuration from database
+Obtains the DHCPv4 server GENERIC configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_CONFIG6_GENERIC Obtaining the DHCPv6 server GENERIC configuration from database
+Obtains the DHCPv6 server GENERIC configuration from database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database id %1
+Obtains the DHCPv4 server configuration from the master database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database id %1
+Obtains the DHCPv6 server configuration from the master database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv4 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv6 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database
+Obtains the DHCPv4 shards name from the master database
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database
+Obtains the DHCPv6 shards name from the master database
+
+% DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_CQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG4 Deleting the DHCPv4 master configuration from database id %1
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_CQL_DELETE_SRV_MASTER_CONFIG6 Deleting the DHCPv6 master configuration from database id %1
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv4 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv6 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database id %1
+Obtains the DHCPv4 master server configuration's version from database.
+
+% DHCPSRV_CQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database id %1
+Obtains the DHCPv6 master server configuration's version from database.
+
+% DHCPSRV_CQL_HOST_DB Connecting to CQL hosts database: %1
+An informational message logged when the CQL hosts database is about to be
+connected to. The parameters of the connection including database name and
+username needed to access it (but not the password if any) are logged.
+
+% DHCPSRV_CQL_HOST_DB_GET_VERSION obtaining schema version information for the CQL hosts database
+A debug message issued when the server is about to obtain schema version
+information from the CQL hosts database.
+
+% DHCPSRV_CQL_HOST_ADD Adding host information to the database
+An informational message logged when options belonging to any reservation from a
+single host are inserted.
+
+% DHCPSRV_CQL_HOST_GET_ALL Retrieving multiple hosts from a CQL database
+An informational message logged when multiple hosts from a CQL database are retrieved.
+
+% DHCPSRV_CQL_HOST_GET4 Retrieving one DHCPv4 host from a CQL database
+An informational message logged when a DHCP server is about to retrieve one
+host from a CQL database by IPv4 criteria.
+
+% DHCPSRV_CQL_HOST_GET6 Retrieving one DHCPv6 host from a CQL database
+An informational message logged when a DHCP server is about to retrieve one
+host from a CQL database by IPv6 criteria.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_ID_4S Looking up primary key "id" filtering by host identifier.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_4S Looking up primary key "id" filtering by host IPv4 address.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_6S Looking up primary key "id" filtering by host IPv4 subnet ID.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_ID_6S Looking up primary key "id" filtering by host identifier type.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_4A Looking up primary key "id" filtering by host IPv4 address.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_4S_4A Looking up primary key "id" filtering by host IPv6 subnet ID.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_6S_6A Looking up primary key "id" filtering by reserved prefix address.
+
+% DHCPSRV_CQL_HOST_LOOKUP_BY_6A_6L Looking up primary key "id" filtering by reserved prefix length.
+
+% DHCPSRV_CQL_HOST_SYNC_RESERVATIONS Syncing reservations in database with kea.conf.
+
+% DHCPSRV_CQL_LEASE_EXCEPTION_THROWN Exception thrown during Cassandra operation: %1
+
% DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET received bad DHCPv4o6 packet: %1
A bad DHCPv4o6 packet was received.
The code has issued a commit call. For the memory file database, this is
a no-op.
+% DHCPSRV_MEMFILE_BEGIN_TRANSACTION committing to memory file database
+The code has issued a begin transaction call. For the memory file database, this is
+a no-op.
+
% DHCPSRV_MEMFILE_CONVERTING_LEASE_FILES running LFC now to convert lease files to the current schema: %1.%2
A warning message issued when the server has detected lease files that need
to be either upgraded or downgraded to match the server's schema, and that
committed to the database. Note that depending on the MySQL settings,
the committal may not include a write to disk.
+% DHCPSRV_MYSQL_BEGIN_TRANSACTION committing to MySQL database
+The code has issued a begin transaction call.
+
% DHCPSRV_MYSQL_DB opening MySQL lease database: %1
This informational message is logged when a DHCP server (either V4 or
V6) is about to open a MySQL lease database. The parameters of the
A debug message issued when the server is attempting to update IPv6
lease from the MySQL database for the specified address.
+% DHCPSRV_MYSQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 configuration with id %1
+A debug message issued when there is no existing DHCPv4 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 configuration with id %1
+A debug message issued when there is no existing DHCPv6 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with id %1.
+A debug message issued when there already exists a DHCPv4 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with id %1.
+A debug message issued when there already exists a DHCPv6 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the DHCPv4 database server configuration
+A debug message issued when a DHCPv4 configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_MYSQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the DHCPv6 database server configuration
+A debug message issued when a DHCPv6 configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the DHCP4 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCP4 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_MYSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the DHCPv6 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when DHCPv6 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's timestamp from database
+Obtains the DHCPv4 server configuration's timestamp from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's timestamp from database
+Obtains the DHCPv6 server configuration's timestamp from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database
+Obtains the DHCPv4 server JSON configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database
+Obtains the DHCPv6 server JSON configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG4_GENERIC Obtaining the DCHPv4 server GENERIC configuration from database
+Obtains the DCHPv4 server GENERIC configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_CONFIG6_GENERIC Obtaining the DCHPv6 server GENERIC configuration from database
+Obtains the DCHPv6 server GENERIC configuration from database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database id %1
+Obtains the DHCPv4 server configuration from the master database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database id %1
+Obtains the DHCPv6 server configuration from the master database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv4 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv6 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database
+Obtains the DHCPv4 shards name from the master database
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database
+Obtains the DHCPv6 shards name from the master database
+
+% DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_MYSQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv4 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv6 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database id %1
+Obtains the DHCPv4 master server configuration's version from database.
+
+% DHCPSRV_MYSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database id %1
+Obtains the DHCPv6 master server configuration's version from database.
+
% DHCPSRV_NOTYPE_DB no 'type' keyword to determine database backend: %1
This is an error message, logged when an attempt has been made to access
a database backend, but where no 'type' keyword has been included in
committed to the database. Note that depending on the PostgreSQL settings,
the committal may not include a write to disk.
+% DHCPSRV_PGSQL_BEGIN_TRANSACTION committing to PostgreSQL database
+The code has issued a begin transaction call.
+
% DHCPSRV_PGSQL_DB opening PostgreSQL lease database: %1
This informational message is logged when a DHCP server (either V4 or
V6) is about to open a PostgreSQL lease database. The parameters of the
A debug message issued when the server is attempting to update IPv6
lease from the PostgreSQL database for the specified address.
+% DHCPSRV_PGSQL_INSERT_SRV_CONFIG4 Inserting a new DHCPv4 server configuration with id %1
+A debug message issued when there is no existing DHCPv4 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_INSERT_SRV_CONFIG6 Inserting a new DHCPv6 server configuration with id %1
+A debug message issued when there is no existing DHCPv6 server configuration in database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4 Updating the DHCPv4 server configuration with id %1.
+A debug message issued when there already exists a DHCPv4 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6 Updating the DHCPv6 server configuration with id %1.
+A debug message issued when there already exists a DHCPv6 server configuration in database
+and this configuration is going to be updated.
+
+% DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG4 Received request to update the IPv4 database server configuration
+A debug message issued when a IPv4 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_PGSQL_REQUEST_UPDATE_SRV_CONFIG6 Received request to update the IPv6 database server configuration
+A debug message issued when a IPv6 server configuration database update request is received.
+If there already exists a configuration in the database then the configuration will be updated.
+Otherwise a new configuration will be inserted.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG4_TIMESTAMP_CHANGED Cannot update the IPv4 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when IPv4 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_PGSQL_UPDATE_SRV_CONFIG6_TIMESTAMP_CHANGED Cannot update the IPv6 database server configuration (old timestamp is %1, new timestamp is %2)
+A warning message issued when IPv6 database server configuration cannot be updated.
+On a database server configuration update the actual timestamp of the configuration is also provided to the server.
+If in the meanwhile the database timestamp has been changed by somebody else then the update operation fails.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG4_TIMESTAMP Obtaining the DHCPv4 server configuration's version from database
+Obtains the DHCPv4 server configuration's version from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG6_TIMESTAMP Obtaining the DHCPv6 server configuration's version from database
+Obtains the DHCPv6 server configuration's version from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG4_JSON Obtaining the DHCPv4 server JSON configuration from database
+Obtains the DHCPv4 server JSON configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG6_JSON Obtaining the DHCPv6 server JSON configuration from database
+Obtains the DHCPv6 server JSON configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG4_GENERIC Obtaining the DHCPv4 server GENERIC configuration from database
+Obtains the DHCPv4 server GENERIC configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_CONFIG6_GENERIC Obtaining the DHCPv6 server GENERIC configuration from database
+Obtains the DHCPv6 server GENERIC configuration from database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4 Obtaining the DHCPv4 server configuration from the master database id %1
+Obtains the DHCPv4 server configuration from the master database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6 Obtaining the DHCPv6 server configuration from the master database id %1
+Obtains the DHCPv6 server configuration from the master database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv4 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARD_DB Obtaining the DHCPv6 server configuration from the master database for '%1' shard database
+Obtains the DHCPv6 server configuration from the master database for the specified shard database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_SHARDS_NAME Obtaining the DHCPv4 shards name from the master database
+Obtains the DHCPv4 shards name from the master database
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_SHARDS_NAME Obtaining the DHCPv6 shards name from the master database
+Obtains the DHCPv6 shards name from the master database
+
+% DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG4 Clearing the DHCPv4 master configuration from database
+Clears the DHCPv4 master configuration from database
+
+% DHCPSRV_PGSQL_CLEAR_SRV_MASTER_CONFIG6 Clearing the DHCPv6 master configuration from database
+Clears the DHCPv6 master configuration from database
+
+% DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG4 Inserting a new DHCPv4 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv4 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_INSERT_SRV_MASTER_CONFIG6 Inserting a new DHCPv6 master server configuration with id %1 and shard database %2
+A debug message issued when there is no existing DHCPv6 master server configuration in
+database and a configuration will be inserted.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG4_TIMESTAMP Obtaining the DHCPv4 master server configuration's version from database id %1
+Obtains the DHCPv4 master server configuration's version from database.
+
+% DHCPSRV_PGSQL_GET_SRV_MASTER_CONFIG6_TIMESTAMP Obtaining the DHCPv6 master server configuration's version from database id %1
+Obtains the DHCPv6 master server configuration's version from database.
+
% DHCPSRV_QUEUE_NCR %1: name change request to %2 DNS entry queued: %3
A debug message which is logged when the NameChangeRequest to add or remove
a DNS entries for a particular lease has been queued. The first argument
namespace isc {
namespace dhcp {
+/// @brief Pair containing major and minor versions
+typedef std::pair<uint32_t, uint32_t> VersionPair;
+
+
/// @brief Contains a single row of lease statistical data
///
/// The contents of the row consist of a subnet ID, a lease
///
/// @result true if the lease was added, false if not (because a lease
/// with the same address was already there).
- virtual bool addLease(const isc::dhcp::Lease4Ptr& lease) = 0;
+ virtual bool addLease(const Lease4Ptr& lease) = 0;
/// @brief Adds an IPv6 lease.
///
/// @brief Deletes a lease.
///
- /// @param addr Address of the lease to be deleted. (This can be IPv4 or
- /// IPv6.)
+ /// @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) = 0;
/// @brief Deletes all expired and reclaimed DHCPv4 leases.
/// B>=A and B=C (it is ok to have newer backend, as it should be backward
/// compatible)
/// Also if B>C, some database upgrade procedure may be triggered
- virtual std::pair<uint32_t, uint32_t> getVersion() const = 0;
+ virtual VersionPair getVersion() const = 0;
/// @brief Commit Transactions
///
/// Rolls back all pending database operations. On databases that don't
/// support transactions, this is a no-op.
virtual void rollback() = 0;
+
+ /// @todo: Add host management here
+ /// As host reservation is outside of scope for 2012, support for hosts
+ /// is currently postponed.
};
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+} // namespace dhcp
+} // namespace isc
#endif // LEASE_MGR_H
getLeaseMgrPtr().reset();
}
-bool
+bool
LeaseMgrFactory::haveInstance() {
return (getLeaseMgrPtr().get());
}
return (std::string("In memory database with leases stored in a CSV file."));
}
+bool Memfile_LeaseMgr::startTransaction() {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_MEMFILE_BEGIN_TRANSACTION);
+ return true;
+}
+
void
Memfile_LeaseMgr::commit() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
return (std::make_pair(MAJOR_VERSION, MINOR_VERSION));
}
+ /// @brief Start Transaction
+ ///
+ /// Start transaction for database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ virtual bool startTransaction();
+
/// @brief Commit Transactions
///
/// Commits all pending database operations. On databases that don't
// 5. Save the database access string in the Configuration Manager.
if (type_ == LEASE_DB) {
cfg_db->setLeaseDbAccessString(getDbAccessString());
-
- } else {
+ } else if (type_ == HOSTS_DB) {
cfg_db->setHostDbAccessString(getDbAccessString());
}
-
}
// Create the database access string
return (dbaccess);
}
-}; // namespace dhcp
-}; // namespace isc
+} // namespace dhcp
+} // namespace isc
-// Copyright (C) 2016 Deutsche Telekom AG.
+// Copyright (C) 2016-2017 Deutsche Telekom AG.
//
// Author: Cristian Secăreanu <cristian.secareanu@qualitance.com>
//
enum ExchangeDataType {
EXCHANGE_DATA_TYPE_NONE,
EXCHANGE_DATA_TYPE_BOOL,
+ EXCHANGE_DATA_TYPE_INT8,
+ EXCHANGE_DATA_TYPE_INT16,
EXCHANGE_DATA_TYPE_INT32,
EXCHANGE_DATA_TYPE_INT64,
EXCHANGE_DATA_TYPE_TIMESTAMP,
EXCHANGE_DATA_TYPE_STRING,
EXCHANGE_DATA_TYPE_BYTES,
- EXCHANGE_DATA_TYPE_UUID
+ EXCHANGE_DATA_TYPE_UUID,
+ EXCHANGE_DATA_TYPE_UDT,
+ EXCHANGE_DATA_TYPE_COLLECTION
};
-/// @brief Used to specify the direction of the data exchange between the
-/// database and the server.
-enum ExchangeDataTypeIO {
- EXCHANGE_DATA_TYPE_IO_IN,
- EXCHANGE_DATA_TYPE_IO_OUT,
- EXCHANGE_DATA_TYPE_IO_IN_OUT
-};
-
-/// @brief Used to map the column name with internal backend storage data types.
-struct ExchangeColumnInfo {
- ExchangeColumnInfo()
- : name_(""), index_(0), type_io_(EXCHANGE_DATA_TYPE_IO_IN_OUT),
- type_(EXCHANGE_DATA_TYPE_NONE){};
- ExchangeColumnInfo(const char* name,
- const uint32_t index,
- const ExchangeDataTypeIO type_io,
- const ExchangeDataType type)
- : name_(name), index_(index), type_io_(type_io), type_(type){};
- std::string name_;
- uint32_t index_;
- ExchangeDataTypeIO type_io_;
- ExchangeDataType type_;
-};
-
-/// @brief Smart pointer to an @ref ExchangeColumnInfo
-typedef boost::shared_ptr<ExchangeColumnInfo> ExchangeColumnInfoPtr;
-
-/// @brief Multimap that allows indexing @ref ExchangeColumnInfoPtr
-/// sequentially, by index and by name.
-typedef boost::multi_index_container<
- // Container comprises elements of ExchangeColumnInfoPtr type.
- ExchangeColumnInfoPtr,
- // Here we start enumerating various indexes.
- boost::multi_index::indexed_by<
- // Index #0. Sequenced index allows accessing elements in the same way
- // as in std::list.
- boost::multi_index::sequenced<>,
- // Index #1
- boost::multi_index::hashed_non_unique<
- boost::multi_index::member<
- ExchangeColumnInfo,
- std::string,
- &ExchangeColumnInfo::name_
- >
- >,
- // Index #2
- boost::multi_index::hashed_non_unique<
- boost::multi_index::member<
- ExchangeColumnInfo,
- uint32_t,
- &ExchangeColumnInfo::index_
- >
- >
- >
-> ExchangeColumnInfoContainer;
-
-/// @brief Pointer to the ExchangeColumnInfoContainer object.
-typedef boost::shared_ptr<ExchangeColumnInfoContainer>
- ExchangeColumnInfoContainerPtr;
-
-/// @brief Type of the index #1 - name.
-typedef ExchangeColumnInfoContainer::nth_index<1>::type
- ExchangeColumnInfoContainerName;
-
-/// @brief Pair of iterators to represent the range of ExchangeColumnInfo having
-/// the
-/// same name value. The first element in this pair represents
-/// the beginning of the range, the second element represents the end.
-typedef std::pair<ExchangeColumnInfoContainerName::const_iterator,
- ExchangeColumnInfoContainerName::const_iterator>
- ExchangeColumnInfoContainerNameRange;
-
-/// @brief Type of the index #2 - index.
-typedef ExchangeColumnInfoContainer::nth_index<2>::type
- ExchangeColumnInfoContainerIndex;
-
-/// @brief Pair of iterators to represent the range of ExchangeColumnInfo having
-/// the
-/// same index value. The first element in this pair represents
-/// the beginning of the range, the second element represents the end.
-typedef std::pair<ExchangeColumnInfoContainerIndex::const_iterator,
- ExchangeColumnInfoContainerIndex::const_iterator>
- ExchangeColumnInfoContainerIndexRange;
-
/// @brief Base class for backend exchanges.
class SqlExchange {
public:
/// @brief Destructor
virtual ~SqlExchange() {
}
-
- /// @brief Column names and types
- ExchangeColumnInfoContainer parameters_;
};
} // namespace dhcp
libdhcpsrv_unittests_SOURCES += pgsql_lease_mgr_unittest.cc
endif
if HAVE_CQL
+libdhcpsrv_unittests_SOURCES += cql_connection_unittest.cc
libdhcpsrv_unittests_SOURCES += cql_lease_mgr_unittest.cc
endif
libdhcpsrv_unittests_SOURCES += pool_unittest.cc
--- /dev/null
+// Copyright (C) 2017 Deutsche Telekom AG.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/cql_exchange.h>
+
+#include <cstring>
+
+#include <boost/any.hpp>
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using isc::dhcp::CqlTaggedStatement;
+using isc::dhcp::StatementMap;
+using isc::dhcp::StatementTag;
+using isc::dhcp::StatementTagHash;
+using isc::dhcp::exchangeType;
+
+class CqlConnectionTest {
+public:
+ /// @brief Constructor
+ CqlConnectionTest() {
+ }
+
+ /// @brief Destructor
+ virtual ~CqlConnectionTest() {
+ }
+};
+
+/// @brief Check that the key is properly hashed for StatementMap.
+TEST(CqlConnection, statementMapHash) {
+ // Build std::strings to prevent optimizations on underlying C string.
+ std::string tag1_s = "same";
+ std::string tag2_s = "same";
+ StatementTag tag1 = tag1_s.c_str();
+ StatementTag tag2 = tag2_s.c_str();
+ StatementMap map;
+
+ // Make sure addresses are different.
+ EXPECT_NE(tag1, tag2);
+
+ // Insert two entries with the same key value.
+ map.insert({tag1, CqlTaggedStatement(tag1, "your fancy select here")});
+ map.insert({tag2, CqlTaggedStatement(tag2, "DELETE FROM world.evil")});
+
+ // Make sure the first one was overwritten.
+ char const* const tag1_text = map.find(tag1)->second.text_;
+ char const* const tag2_text = map.find(tag2)->second.text_;
+ EXPECT_TRUE(tag1_text);
+ EXPECT_TRUE(tag2_text);
+ ASSERT_EQ(std::strcmp(tag1_text, tag2_text), 0);
+ ASSERT_EQ(map.size(), 1u);
+}
+
+/// @brief Check anything related to exchange types.
+TEST(CqlConnection, exchangeTypeCoverage) {
+ // Check that const and non-const are supported and both point to the same
+ // exchange type.
+ int i = 1;
+
+ // non-const
+ int* pi = &i;
+ boost::any bi(pi);
+
+ // const
+ int* const cpi = &i;
+ boost::any bci(cpi);
+
+ ASSERT_EQ(exchangeType(bi), exchangeType(bci));
+}
+
+} // namespace
+
-// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+// Copyright (C) 2015-2017 Deutsche Telekom AG.
//
-// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
-// Author: Andrei Pavel <andrei.pavel@qualitance.com>
+// Authors: Razvan Becheriu <razvan.becheriu@qualitance.com>
+// Andrei Pavel <andrei.pavel@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#include <config.h>
+#include <gtest/gtest.h>
+
#include <asiolink/io_address.h>
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/cql_lease_mgr.h>
#include <dhcpsrv/testutils/cql_schema.h>
#include <exceptions/exceptions.h>
-#include <gtest/gtest.h>
-
#include <algorithm>
-#include <iostream>
#include <sstream>
#include <string>
#include <utility>
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
-using namespace std;
namespace {
///
/// Opens the database prior to each test and closes it afterwards.
/// All pending transactions are deleted prior to closure.
-
class CqlLeaseMgrTest : public GenericLeaseMgrTest {
public:
- /// @brief Constructor
- ///
- /// Deletes everything from the database and opens it.
- CqlLeaseMgrTest() {
+ /// @brief Clears the database and opens connection to it.
+ void initializeTest() {
// Ensure schema is the correct one.
destroyCqlSchema(false, true);
createCqlSchema(false, true);
"*** accompanying exception output.\n";
throw;
}
+
lmptr_ = &(LeaseMgrFactory::instance());
}
+ /// @brief Destroys the LM and the schema.
+ void destroyTest() {
+ try {
+ lmptr_->rollback();
+ } catch (...) {
+ // Rollback may fail if backend is in read only mode. That's ok.
+ }
+ LeaseMgrFactory::destroy();
+ destroyCqlSchema(false, true);
+ }
+
+ /// @brief Constructor
+ ///
+ /// Deletes everything from the database and opens it.
+ CqlLeaseMgrTest() {
+ initializeTest();
+ }
+
/// @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 ~CqlLeaseMgrTest() {
- lmptr_->rollback();
- LeaseMgrFactory::destroy();
- destroyCqlSchema(false, true);
+ destroyTest();
}
/// @brief Reopen the database
// columns. Because of that, the order check has been excluded.
void testCqlGetExpiredLeases4() {
// Get the leases to be used for the test.
- vector<Lease4Ptr> leases = createLeases4();
+ std::vector<Lease4Ptr> leases = createLeases4();
// Make sure we have at least 6 leases there.
- ASSERT_GE(leases.size(), 6U);
+ ASSERT_GE(leases.size(), 6u);
// Use the same current time for all leases.
time_t current_time = time(NULL);
// Add them to the database
- for (size_t i = 0U; i < leases.size(); ++i) {
+ for (size_t i = 0u; i < leases.size(); ++i) {
// Mark every other lease as expired.
- if (i % 2U == 0U) {
+ if (i % 2u == 0u) {
// Set client last transmission time to the value older than the
// valid lifetime to make it expired. The expiration time also
// depends on the lease index, so as we can later check that the
// leases are ordered by the expiration time.
leases[i]->cltt_ =
current_time - leases[i]->valid_lft_ - 10 - i;
-
} else {
// Set current time as cltt for remaining leases. These leases
// are
// Retrieve at most 1000 expired leases.
Lease4Collection expired_leases;
ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000));
+
// Leases with even indexes should be returned as expired.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
expired_leases.size());
// Update current time for the next test.
// This time let's reverse the expiration time and see if they will be
// returned
// in the correct order.
- for (size_t i = 0U; i < leases.size(); ++i) {
+ for (size_t i = 0u; i < leases.size(); ++i) {
// Update the time of expired leases with even indexes.
- if (i % 2U == 0U) {
+ if (i % 2u == 0u) {
leases[i]->cltt_ =
current_time - leases[i]->valid_lft_ - 1000 + i;
} else {
}
// Retrieve expired leases again. The limit of 0 means return all
- // expired
- // leases.
+ // expired leases.
ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0));
+
// The same leases should be returned.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
expired_leases.size());
// Remember expired leases returned.
ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 2));
// Make sure we have exactly 2 leases returned.
- ASSERT_EQ(2U, expired_leases.size());
+ ASSERT_EQ(2u, expired_leases.size());
// Mark every other expired lease as reclaimed.
- for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
- if (i % 2U != 0U) {
+ for (size_t i = 0u; i < saved_expired_leases.size(); ++i) {
+ if (i % 2u != 0u) {
saved_expired_leases[i]->state_ =
Lease::STATE_EXPIRED_RECLAIMED;
}
// This the returned leases should exclude reclaimed ones. So the number
// of returned leases should be roughly half of the expired leases.
- ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0U));
- ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2U),
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0u));
+ ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2u),
expired_leases.size());
// Make sure that returned leases are those that are not reclaimed, i.e.
// those that have even index.
for (Lease4Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
- int index =
- static_cast<int>(std::distance(expired_leases.begin(), lease));
+ int32_t index = static_cast<int32_t>(
+ std::distance(expired_leases.begin(), lease));
EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
}
}
// columns. Because of that, the order check has been excluded.
void testCqlGetExpiredLeases6() {
// Get the leases to be used for the test.
- vector<Lease6Ptr> leases = createLeases6();
+ std::vector<Lease6Ptr> leases = createLeases6();
// Make sure we have at least 6 leases there.
- ASSERT_GE(leases.size(), 6U);
+ ASSERT_GE(leases.size(), 6u);
// Use the same current time for all leases.
time_t current_time = time(NULL);
// Add them to the database
- for (size_t i = 0U; i < leases.size(); ++i) {
+ for (size_t i = 0u; i < leases.size(); ++i) {
// Mark every other lease as expired.
- if (i % 2U == 0U) {
+ if (i % 2u == 0u) {
// Set client last transmission time to the value older than the
// valid lifetime to make it expired. The expiration time also
// depends on the lease index, so as we can later check that the
// leases are ordered by the expiration time.
leases[i]->cltt_ =
current_time - leases[i]->valid_lft_ - 10 - i;
-
} else {
// Set current time as cltt for remaining leases. These leases
// are
// Retrieve at most 1000 expired leases.
Lease6Collection expired_leases;
ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000));
+
// Leases with even indexes should be returned as expired.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
expired_leases.size());
// Update current time for the next test.
// This time let's reverse the expiration time and see if they will be
// returned
// in the correct order.
- for (size_t i = 0U; i < leases.size(); ++i) {
+ for (size_t i = 0u; i < leases.size(); ++i) {
// Update the time of expired leases with even indexes.
- if (i % 2U == 0U) {
+ if (i % 2u == 0u) {
leases[i]->cltt_ =
current_time - leases[i]->valid_lft_ - 1000 + i;
-
} else {
// Make sure remaining leases remain unexpired.
leases[i]->cltt_ = current_time + 100;
// expired
// leases.
ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+
// The same leases should be returned.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2u),
expired_leases.size());
// Remember expired leases returned.
ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 2));
// Make sure we have exactly 2 leases returned.
- ASSERT_EQ(2U, expired_leases.size());
+ ASSERT_EQ(2u, expired_leases.size());
// Mark every other expired lease as reclaimed.
- for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
- if (i % 2U != 0U) {
+ for (size_t i = 0u; i < saved_expired_leases.size(); ++i) {
+ if (i % 2u != 0u) {
saved_expired_leases[i]->state_ =
Lease::STATE_EXPIRED_RECLAIMED;
}
// those that have even index.
for (Lease6Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
- int index =
- static_cast<int>(std::distance(expired_leases.begin(), lease));
+ int32_t index = static_cast<int32_t>(
+ std::distance(expired_leases.begin(), lease));
EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
}
}
EXPECT_EQ(std::string("cql"), lmptr_->getType());
}
-/// @brief Check conversion functions
+/// @brief Check conversion methods
///
/// The server works using cltt and valid_filetime. In the database, the
/// information is stored as expire_time and valid-lifetime, which are
/// This test checks that the conversion is correct.
TEST_F(CqlLeaseMgrTest, checkTimeConversion) {
const time_t cltt = time(NULL);
- const cass_int64_t valid_lft = 86400; // 1 day
+ const cass_int64_t valid_lft = 86400; // 1 day
cass_int64_t cql_expire;
- // Convert to the database time
+ // Convert to the database time.
CqlExchange::convertToDatabaseTime(cltt, valid_lft, cql_expire);
- // Convert back
+ // Convert back.
time_t converted_cltt = 0;
CqlExchange::convertFromDatabaseTime(cql_expire, valid_lft, converted_cltt);
EXPECT_EQ(cltt, converted_cltt);
/// @brief Check that getVersion() returns the expected version
TEST_F(CqlLeaseMgrTest, checkVersion) {
// Check version
- pair<uint32_t, uint32_t> version;
+ VersionPair version;
ASSERT_NO_THROW(version = lmptr_->getVersion());
EXPECT_EQ(CQL_SCHEMA_VERSION_MAJOR, version.first);
EXPECT_EQ(CQL_SCHEMA_VERSION_MINOR, version.second);
///
/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,
/// subnet_id),
-/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
+/// updateLease4() and deleteLease can handle NULL client-id.
/// (client-id is optional and may not be present)
TEST_F(CqlLeaseMgrTest, lease4NullClientId) {
testLease4NullClientId();
}
} // namespace
-
return (make_pair(uint32_t(0), uint32_t(0)));
}
+ /// @brief Start Transaction
+ ///
+ /// Start transaction for database operations. On databases that don't
+ /// support transactions, this is a no-op.
+ virtual bool startTransaction() { return true; };
+
/// @brief Commit transactions
virtual void commit() {
}
--- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+-- Copyright (C) 2015-2017 Deutsche Telekom AG.
-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
-- -----------------------------------------------------
-- Table `lease4`
-- -----------------------------------------------------
-CREATE TABLE lease4 (
+CREATE TABLE IF NOT EXISTS lease4 (
address int,
hwaddr blob,
client_id blob,
fqdn_rev boolean,
hostname varchar,
state int,
- PRIMARY KEY (address)
+ 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);
+CREATE INDEX IF NOT EXISTS lease4index1 ON lease4 (client_id);
+CREATE INDEX IF NOT EXISTS lease4index2 ON lease4 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease4index3 ON lease4 (hwaddr);
+CREATE INDEX IF NOT EXISTS lease4index4 ON lease4 (expire);
+CREATE INDEX IF NOT EXISTS lease4index5 ON lease4 (state);
-- Holds the IPv6 leases.
-- N.B. The use of a VARCHAR for the address is temporary for development:
-- -----------------------------------------------------
-- Table `lease6`
-- -----------------------------------------------------
-CREATE TABLE lease6 (
+CREATE TABLE IF NOT EXISTS lease6 (
address varchar,
- duid blob,
valid_lifetime bigint,
expire bigint,
subnet_id int,
pref_lifetime bigint,
- lease_type int,
+ duid blob,
iaid int,
+ lease_type int,
prefix_len int,
fqdn_fwd boolean,
fqdn_rev boolean,
hwtype int,
hwaddr_source int,
state int,
- PRIMARY KEY (address)
+ 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);
+CREATE INDEX IF NOT EXISTS lease6index1 ON lease6 (duid);
+CREATE INDEX IF NOT EXISTS lease6index2 ON lease6 (iaid);
+CREATE INDEX IF NOT EXISTS lease6index3 ON lease6 (lease_type);
+CREATE INDEX IF NOT EXISTS lease6index4 ON lease6 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease6index5 ON lease6 (expire);
+CREATE INDEX IF NOT EXISTS 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
-- -----------------------------------------------------
-- Table `lease6_types`
-- -----------------------------------------------------
-CREATE TABLE lease6_types (
+CREATE TABLE IF NOT EXISTS lease6_types (
lease_type int, -- Lease type code.
name varchar, -- Name of the lease type
- PRIMARY KEY (lease_type)
+ PRIMARY KEY ((lease_type))
);
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
-- -----------------------------------------------------
-- Table `lease_hwaddr_source`
-- -----------------------------------------------------
-CREATE TABLE lease_hwaddr_source (
+CREATE TABLE IF NOT EXISTS lease_hwaddr_source (
hwaddr_source int,
name varchar,
- PRIMARY KEY (hwaddr_source)
+ PRIMARY KEY ((hwaddr_source))
);
-- Hardware address obtained from raw sockets
-- -----------------------------------------------------
-- Table `lease_state`
-- -----------------------------------------------------
-CREATE TABLE lease_state (
+CREATE TABLE IF NOT EXISTS lease_state (
state int,
name varchar,
- PRIMARY KEY (state)
+ PRIMARY KEY ((state))
);
-- Insert currently defined state names.
-- -----------------------------------------------------
-- Table `schema_version`
-- -----------------------------------------------------
-CREATE TABLE schema_version (
+CREATE TABLE IF NOT EXISTS schema_version (
version int,
minor int,
- PRIMARY KEY (version)
+ PRIMARY KEY ((version))
);
INSERT INTO schema_version (version, minor) VALUES (1, 0);
--- This line concludes database initalization to version 1.0.
+-- This line concludes database initialization to version 1.0.
+
+-- This line starts database upgrade to version 2.0
+
+-- -----------------------------------------------------
+-- Table `host_reservations`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS host_reservations (
+ host_identifier blob,
+ host_identifier_type int,
+ host_ipv4_subnet_id int,
+ host_ipv6_subnet_id int,
+ host_ipv4_address int,
+ hostname text,
+ host_ipv4_client_classes text,
+ host_ipv6_client_classes text,
+ -- reservation
+ reserved_ipv6_prefix_address text,
+ reserved_ipv6_prefix_length int,
+ reserved_ipv6_prefix_address_type int,
+ iaid int,
+ -- option
+ option_universe int,
+ option_code int,
+ option_value blob,
+ option_formatted_value text,
+ option_space text,
+ option_is_persistent boolean,
+ option_client_class text,
+ option_subnet_id int,
+ id bigint,
+ PRIMARY KEY ((id))
+);
+
+CREATE INDEX IF NOT EXISTS host_reservationsindex1 ON host_reservations (host_identifier);
+CREATE INDEX IF NOT EXISTS host_reservationsindex2 ON host_reservations (host_identifier_type);
+CREATE INDEX IF NOT EXISTS host_reservationsindex3 ON host_reservations (host_ipv4_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex4 ON host_reservations (host_ipv6_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex5 ON host_reservations (host_ipv4_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex6 ON host_reservations (reserved_ipv6_prefix_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex7 ON host_reservations (reserved_ipv6_prefix_length);
+
+TRUNCATE SCHEMA_VERSION;
+INSERT INTO schema_version (version, minor) VALUES(2, 0);
+
+-- This line concludes database upgrade to version 2.0
--- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
+-- Copyright (C) 2015-2017 Deutsche Telekom AG.
-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
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;
+DROP TABLE IF EXISTS schema_version;
+DROP TABLE IF EXISTS host_reservations;
DROP INDEX IF EXISTS lease4index1;
DROP INDEX IF EXISTS lease4index2;
DROP INDEX IF EXISTS lease6index4;
DROP INDEX IF EXISTS lease6index5;
DROP INDEX IF EXISTS lease6index6;
+
+DROP INDEX IF EXISTS host_reservationsindex1;
+DROP INDEX IF EXISTS host_reservationsindex2;
+DROP INDEX IF EXISTS host_reservationsindex3;
+DROP INDEX IF EXISTS host_reservationsindex4;
+DROP INDEX IF EXISTS host_reservationsindex5;
+DROP INDEX IF EXISTS host_reservationsindex6;
+DROP INDEX IF EXISTS host_reservationsindex7;