Commit 77843631 authored by Razvan Becheriu's avatar Razvan Becheriu Committed by Tomek Mrugalski

added support for datastax cassandra

parent 53c1c6aa
address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname
0.0.0.10,3230,3330,40,2015-01-01 01:15:30,50,1,1,one.example.com
0.0.0.11,,313233,40,2015-02-02 02:30:45,50,1,1,
0.0.0.12,3232,,40,2015-03-03 11:01:07,50,1,1,three.example.com
address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source
10,3230,30,2015-04-04 01:15:30,40,50,IA_TA,60,70,1,1,one.example.com,3830,90,100
11,,30,2015-05-05 02:30:45,40,50,IA_TA,60,70,1,1,,3830,90,100
12,3231,30,2015-06-06 11:01:07,40,50,IA_TA,60,70,1,1,three.example.com,3830,90,100
-- Copyright (C) 2015 - 2016 Deutsche Telekom AG.
-- Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
-- http://www.apache.org/licenses/LICENSE-2.0
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- This is the Kea schema 1.0 specification for DataStax Cassandra SQL.
-- Note: this is outdated version on purpose and it used to test upgrade
-- process. Do not update this file to 2.0 or any later.
-- The schema is reasonably portable (with the exception of the engine
-- specification, which is Datastax Cassandra SQL-specific). Minor changes might be needed for
-- other databases.
-- To create the schema, either type the command:
-- cqlsh -u <user> -p <password> -k <database> -f dhcpdb_create.cql
-- ... at the command prompt, or log in to the DSC SQL database and at the "cqlsh>"
-- prompt, issue the command:
-- SOURCE dhcpdb_create.cql
-- This script is also called from kea-admin, see kea-admin init dscsql
-- Over time, Kea database schema will evolve. Each version is marked with
-- major.minor version. This file is organized sequentially, i.e. database
-- is initialized to 1.0, then upgraded to 2.0 etc. This may be somewhat
-- sub-optimal, but it ensues consistency with upgrade scripts. (It is much
-- easier to maintain init and upgrade scripts if they look the same).
-- Since initialization is done only once, it's perfromance is not an issue.
-- This line starts database initialization to 1.0.
-- Holds the IPv4 leases.
CREATE TABLE lease4 (
address int,
hwaddr blob,
client_id blob,
valid_lifetime bigint,
expire bigint,
subnet_id int,
fqdn_fwd boolean,
fqdn_rev boolean,
hostname varchar,
state int,
PRIMARY KEY (address)
);
-- Create search indexes for lease4 table
CREATE INDEX lease4index1 ON lease4 (client_id);
CREATE INDEX lease4index2 ON lease4 (subnet_id);
CREATE INDEX lease4index3 ON lease4 (hwaddr);
CREATE INDEX lease4index4 ON lease4 (state);
-- Holds the IPv6 leases.
-- N.B. The use of a VARCHAR for the address is temporary for development:
-- it will eventually be replaced by BINARY(16).
CREATE TABLE lease6 (
address varchar,
duid blob,
valid_lifetime bigint,
expire bigint,
subnet_id int,
pref_lifetime bigint,
lease_type int,
iaid int,
prefix_len int,
fqdn_fwd boolean,
fqdn_rev boolean,
hostname varchar,
hwaddr blob,
hwtype int,
hwaddr_source int,
state int,
PRIMARY KEY (address)
);
-- Create search indexes for lease6 table
CREATE INDEX lease6index1 ON lease6 (lease_type);
CREATE INDEX lease6index2 ON lease6 (duid);
CREATE INDEX lease6index3 ON lease6 (iaid);
CREATE INDEX lease6index4 ON lease6 (subnet_id);
CREATE INDEX lease6index5 ON lease6 (state);
-- ... and a definition of lease6 types. This table is a convenience for
-- users of the database - if they want to view the lease table and use the
-- type names, they can join this table with the lease6 table.
-- Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/
-- lease_mgr.h)
CREATE TABLE lease6_types (
lease_type int, -- Lease type code.
name varchar, -- Name of the lease type
PRIMARY KEY (lease_type)
);
--START TRANSACTION;
INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA'); -- Non-temporary v6 addresses
INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA'); -- Temporary v6 addresses
INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD'); -- Prefix delegations
--COMMIT;
-- Kea keeps track of the hardware/MAC address source, i.e. how the address
-- was obtained. Depending on the technique and your network topology, it may
-- be more or less trustworthy. This table is a convenience for
-- users of the database - if they want to view the lease table and use the
-- type names, they can join this table with the lease6 table. For details,
-- see constants defined in src/lib/dhcp/dhcp/pkt.h for detailed explanation.
CREATE TABLE lease_hwaddr_source (
hwaddr_source int,
name varchar,
PRIMARY KEY (hwaddr_source)
);
-- Hardware address obtained from raw sockets
INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (1, 'HWADDR_SOURCE_RAW');
-- Hardware address converted from IPv6 link-local address with EUI-64
INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (2, 'HWADDR_SOURCE_IPV6_LINK_LOCAL');
-- Hardware address extracted from client-id (duid)
INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (4, 'HWADDR_SOURCE_DUID');
-- Hardware address extracted from client address relay option (RFC6939)
INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (8, 'HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION');
-- Hardware address extracted from remote-id option (RFC4649)
INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (16, 'HWADDR_SOURCE_REMOTE_ID');
-- Hardware address extracted from subscriber-id option (RFC4580)
INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE_SUBSCRIBER_ID');
-- Hardware address extracted from docsis options
INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (64, 'HWADDR_SOURCE_DOCSIS_CMTS');
-- -----------------------------------------------------
-- Table `dhcp4_options`
-- -----------------------------------------------------
CREATE TABLE dhcp4_options (
option_id int,
code int,
value blob,
formatted_value varchar,
space varchar,
persistent int,
dhcp_client_class varchar,
dhcp4_subnet_id int,
host_id int,
PRIMARY KEY (option_id)
);
-- Create search indexes for dhcp4_options table
CREATE INDEX dhcp4_optionsindex1 ON dhcp4_options (host_id);
-- -----------------------------------------------------
-- Table `dhcp6_options`
-- -----------------------------------------------------
CREATE TABLE dhcp6_options (
option_id int,
code int,
value blob,
formatted_value varchar,
space varchar,
persistent int,
dhcp_client_class varchar,
dhcp6_subnet_id int,
host_id int,
PRIMARY KEY (option_id)
);
-- Create search indexes for dhcp6_options table
CREATE INDEX dhcp6_optionsindex1 ON dhcp6_options (host_id);
-- Create table holding mapping of the lease states to their names.
-- This is not used in queries from the DHCP server but rather in
-- direct queries from the lease database management tools.
CREATE TABLE lease_state (
state int,
name varchar,
PRIMARY KEY (state)
);
-- Insert currently defined state names.
INSERT INTO lease_state (state, name) VALUES (0, 'default');
INSERT INTO lease_state (state, name) VALUES (1, 'declined');
INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed');
-- Finally, the version of the schema. We start at 1.0 during development.
-- This table is only modified during schema upgrades. For historical reasons
-- (related to the names of the columns in the BIND 10 DNS database file), the
-- first column is called "version" and not "major".
-- NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h,
-- which defines the schema for the unit tests. If you are updating
-- the version number, the schema has changed: please ensure that
-- schema_copy.h has been updated as well.
CREATE TABLE schema_version (
version int,
minor int,
PRIMARY KEY (version)
);
--START TRANSACTION;
INSERT INTO schema_version (version, minor) VALUES (1, 0);
--COMMIT;
#!/bin/sh
# Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# Include common test library.
. @abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh
dscsql_init_test() {
test_start "dscsql.init"
# @todo: Implement this
test_finish 0
}
dscsql_version_test() {
test_start "dscsql.version"
# @todo: Implement this
test_finish 0
}
dscsql_upgrade_test() {
test_start "dscsql.upgrade"
# @todo: Implement this
test_finish 0
}
dscsql_init_test
dscsql_version_test
dscsql_upgrade_test
// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
//
// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <dhcpsrv/dscsql_connection.h>
#include <string>
using namespace std;
namespace isc {
namespace dhcp {
DSCSqlConnection::DSCSqlConnection(const ParameterMap& parameters) : DatabaseConnection(parameters),
cluster_(NULL), session_(NULL), tagged_statements_(NULL) {
}
DSCSqlConnection::~DSCSqlConnection() {
CassError rc;
for (int i = 0; i < statements_.size(); i++)
{
if (statements_[i]) {
cass_prepared_free(statements_[i]);
}
statements_[i] = NULL;
}
CassFuture* close_future = cass_session_close(session_);
cass_future_wait(close_future);
std::string error;
checkStatementError(error, close_future, "could not close connection to DB");
rc = cass_future_error_code(close_future);
cass_future_free(close_future);
cass_session_free(session_);
session_ = NULL;
cass_cluster_free(cluster_);
cluster_ = NULL;
if (rc != CASS_OK) {
isc_throw(DbOpenError, error);
}
}
void
DSCSqlConnection::openDatabase() {
CassError rc;
// Set up the values of the parameters
const char* contact_points = "127.0.0.1";
string scontact_points;
try {
scontact_points = getParameter("contact_points");
contact_points = scontact_points.c_str();
} catch (...) {
// No host. Fine, we'll use "localhost"
}
const char* port = NULL;
string sport;
try {
sport = getParameter("port");
port = sport.c_str();
} catch (...) {
// No port. Fine, we'll use "default"
}
const char* user = NULL;
string suser;
try {
suser = getParameter("user");
user = suser.c_str();
} catch (...) {
// No user. Fine, we'll use NULL
}
const char* password = NULL;
string spassword;
try {
spassword = getParameter("password");
password = spassword.c_str();
} catch (...) {
// No password. Fine, we'll use NULL
}
const char* keyspace = "keatest";
string skeyspace;
try {
skeyspace = getParameter("keyspace");
keyspace = skeyspace.c_str();
} catch (...) {
// No database name. Fine, we'll use default 'keatest'
}
cluster_ = cass_cluster_new();
cass_cluster_set_contact_points(cluster_, contact_points);
if (user != NULL && password != NULL) {
cass_cluster_set_credentials(cluster_, user, password);
}
if (port != NULL) {
int port_nr;
try {
port_nr = boost::lexical_cast<int>(port);
} catch (const std::exception& ex) {
isc_throw(DbOperationError, "Invalid int data: " << port
<< " : " << ex.what());
}
cass_cluster_set_port(cluster_, port_nr);
}
session_ = cass_session_new();
CassFuture* connect_future = cass_session_connect_keyspace(session_, cluster_, keyspace);
cass_future_wait(connect_future);
std::string error;
checkStatementError(error, connect_future, "could not connect to DB");
rc = cass_future_error_code(connect_future);
cass_future_free(connect_future);
if (rc != CASS_OK) {
cass_session_free(session_);
session_ = NULL;
cass_cluster_free(cluster_);
cluster_ = NULL;
isc_throw(DbOpenError, error);
}
}
void
DSCSqlConnection::prepareStatements(DSCSqlTaggedStatement *statements) {
CassError rc = CASS_OK;
CassFuture* future = NULL;
uint32_t size = 0;
tagged_statements_ = statements;
for (; tagged_statements_[size].params_; size++);
statements_.resize(size);
for (uint32_t i = 0; i < size; i++) {
const char* query = tagged_statements_[i].text_;
future = cass_session_prepare(session_, query);
cass_future_wait(future);
std::string error;
checkStatementError(error, future, i, "could not prepare statement");
rc = cass_future_error_code(future);
if (rc != CASS_OK) {
cass_future_free(future);
statements_[i] = NULL;
isc_throw(DbOperationError, error);
} else {
statements_[i] = cass_future_get_prepared(future);
}
cass_future_free(future);
}
}
string
DSCSqlConnection::getName() const {
string name = "";
try {
name = getParameter("name");
} catch (...) {
// Return an empty name
}
return (name);
}
string
DSCSqlConnection::getDescription() const {
return (string("DataStax Cassandra Database"));
}
pair<uint32_t, uint32_t>
DSCSqlConnection::getVersion() const {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
DHCPSRV_DSCSQL_GET_VERSION);
uint32_t version = CASS_VERSION_MAJOR;
uint32_t minor = CASS_VERSION_MINOR;
return make_pair<uint32_t, uint32_t>(version, minor);
}
void
DSCSqlConnection::commit() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_COMMIT);
}
void
DSCSqlConnection::rollback() {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_DSCSQL_ROLLBACK);
}
void
DSCSqlConnection::checkStatementError(std::string& error, CassFuture* future, uint32_t stindex, const char* what) const
{
CassError rc;
const char* errorMessage;
size_t errorMessageSize;
std::stringstream stream;
stream << "no error for: " << tagged_statements_[stindex].name_;
rc = cass_future_error_code(future);
cass_future_error_message(future, &errorMessage, &errorMessageSize);
if (rc != CASS_OK) {
stream.str(std::string());
stream << what << " for: " << tagged_statements_[stindex].name_ << " reason: " <<
errorMessage << " error code: " << rc;
}
error = stream.str();
}
void
DSCSqlConnection::checkStatementError(std::string& error, CassFuture* future, const char* what) const
{
CassError rc;
const char* errorMessage;
size_t errorMessageSize;
std::stringstream stream;
stream << "no error";
rc = cass_future_error_code(future);
cass_future_error_message(future, &errorMessage, &errorMessageSize);
if (rc != CASS_OK) {
stream.str(std::string());
stream << what << " reason: " << errorMessage << " error code: " << rc;
}
error = stream.str();
}
}; // end of isc::dhcp namespace
}; // end of isc namespace
// Copyright (C) 2015 - 2016 Deutsche Telekom AG.
//
// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef DSCSQL_CONNECTION_H
#define DSCSQL_CONNECTION_H
#include <dhcpsrv/database_connection.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <inttypes.h>
#include <cassandra.h>
#include <vector>
namespace isc {
namespace dhcp {
/// @brief Defines a single query
struct DSCSqlTaggedStatement {
/// Param name.
const char** params_;
/// Short name of the query.
const char* name_;
/// Text representation of the actual query.
const char* text_;
};
/// Defines DSC SQL backend version: 1.0
const uint32_t DSCSQL_CURRENT_VERSION = 1;
const uint32_t DSCSQL_CURRENT_MINOR = 0;
class DSCSqlConnection : public DatabaseConnection {
public:
/// @brief Constructor
///
/// Initialize DSCSqlConnection object with parameters needed for connection.
DSCSqlConnection(const ParameterMap& parameters);
/// @brief Destructor
virtual ~DSCSqlConnection();
/// @brief Prepare statements
///
/// Creates the prepared statements for all of the SQL statements used
/// by the PostgreSQL backend.
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
/// @throw isc::InvalidParameter 'index' is not valid for the vector. This
/// represents an internal error within the code.
void prepareStatements(DSCSqlTaggedStatement *statements);
/// @brief Open Database
///
/// Opens the database using the information supplied in the parameters
/// passed to the constructor.
///
/// @throw NoDatabaseName Mandatory database name not given
/// @throw DbOpenError Error opening the database
void openDatabase();
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
///
/// @return Type of the backend.
virtual std::string getType() const {
return (std::string("cassandra"));
}
/// @brief Returns name of the database.
///
/// @return database name
virtual std::string getName() const;
/// @brief Returns description of the backend.
///
/// This description may be multiline text that describes the backend.
///
/// @return Description of the backend.
virtual std::string getDescription() const;
/// @brief Returns backend version.
///
/// @return Version number as a pair of unsigned integers. "first" is the
/// major version number, "second" the minor number.
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
virtual std::pair<uint32_t, uint32_t> getVersion() const;
/// @brief Commit Transactions
///
/// Commits all pending database operations.
///
/// @throw DbOperationError Iif the commit failed.
virtual void commit();
/// @brief Rollback Transactions
///
/// Rolls back all pending database operations.
///
/// @throw DbOperationError If the rollback failed.
virtual void rollback();
/// @brief Check Error
///
/// Chech error for current database operation.
void checkStatementError(std::string& error, CassFuture* future, uint32_t stindex, const char* what) const;
/// @brief Check Error
///
/// Chech error for current database operation.
void checkStatementError(std::string& error, CassFuture* future, const char* what) const;
/// DSC SQL connection handle
CassCluster* cluster_;
CassSession* session_;
std::vector<const CassPrepared*> statements_; ///< Prepared statements
DSCSqlTaggedStatement *tagged_statements_;
};
}; // end of isc::dhcp namespace
}; // end of isc namespace
#endif // DSCSQL_CONNECTION_H
This diff is collapsed.
This diff is collapsed.
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <asiolink/io_address.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/dscsql_connection.h>
#include <dhcpsrv/dscsql_lease_mgr.h>
#include <dhcpsrv/tests/test_utils.h>
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
#include <dhcpsrv/testutils/dscsql_schema.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace std;
namespace {
/// @brief Test fixture class for testing DataStax Cassandra Lease Manager
///
/// Opens the database prior to each test and closes it afterwards.
/// All pending transactions are deleted prior to closure.
class DSCSqlLeaseMgrTest : public GenericLeaseMgrTest {
public:
/// @brief Constructor
///
/// Deletes everything from the database and opens it.
DSCSqlLeaseMgrTest() {
// Ensure schema is the correct one.
destroyDSCSQLSchema();
createDSCSQLSchema();
// Connect to the database
try {
LeaseMgrFactory::create(validDSCSQLConnectionString());
} catch (...) {
std::cerr << "*** ERROR: unable to open database. The test\n"
"*** environment is broken and must be fixed before\n"
"*** the DSC SQL tests will run correctly.\n"
"*** The reason for the problem is described in the\n"
"*** accompanying exception output.\n";
throw;
}
lmptr_ = &(LeaseMgrFactory::instance());
}
/// @brief Destructor
///
/// Rolls back all pending transactions. The deletion of lmptr_ will close
/// the database. Then reopen it and delete everything created by the test.
virtual ~DSCSqlLeaseMgrTest() {
lmptr_->rollback();
LeaseMgrFactory::destroy();
destroyDSCSQLSchema();