From: Andrei Pavel Date: Wed, 14 Dec 2016 12:47:57 +0000 (+0200) Subject: Cassandra update X-Git-Tag: trac5494_base~11^2~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=50fd79039dc7cb2fe25654c149fe43ecf91e1b57;p=thirdparty%2Fkea.git Cassandra update Replaced unrecommended backticks with $() in cql_version() in bash scripts. *_execute() and *_execute_script() functions from src/bin/admin/admin-utils.sh now pass the parameters to the underlying backend binary whenever they are given rather than when there are 2 or more. Corrected cql_version() return error in src/bin/admin/admin-utils.sh. Removed redundant "USE" from cql_init() in src/bin/admin/kea-admin.in. Inserted a newline in src/bin/admin/tests/Makefile.am to separate unrelated targets. Style changes in cql_*_test() functions in src/bin/admin/tests/cql_tests.sh.in. src/bin/admin/tests/dhcpdb_create_1.0.cql: "perfromance" typo Added comment headers Added index on expire since it is used in WHERE clauses (further performance testing may be required) Removed dhcp4_options and dhcp6_options table since they are not required for Cassandra Added DROP INDEX in src/share/database/scripts/cql/dhcpdb_drop.cql. Added sql_common.h Added cql_exchange.h and cql_exchange.cc which mediate communication with Cassandra. Added cql_lease_mgr.h and cql_lease_mgr.cc Parameterized reconnect-wait-time, connect-timeout, request-timeout, tcp-keepalive, tcp-nodelay for Cassandra in kea.conf. Changes are in src/lib/dhcpsrv/cql_connection.cc and src/lib/dhcpsrv/parsers/dbaccess_parser.cc. Reformated x != NULL into !x as specified in the Kea style guidelines src/lib/dhcpsrv/cql_connection.cc: Added range check for port Added CqlConnection:setConsistency Added CqlConnection::startTransaction which is a noop Added CqlTransaction method implementations. Corrected ending brace of namespace declaration, it doesn't need semicolon. src/lib/dhcpsrv/cql_connection.h: Added explicit on CqlConnection constructor. Unlikely that this class will ever be derived, but it's good practice. Changed some comments. Added CqlTransaction class definition. src/lib/dhcpsrv/cql_lease_mgr.cc: Formatted the entire code. Changed data types to cass_ types. Added some log messages. Moved structs, enums and typedefs from src/lib/dhcpsrv/lease_mgr.h to src/lib/dhcpsrv/sql_common.h Added some missing tests in src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc --- diff --git a/src/bin/admin/admin-utils.sh b/src/bin/admin/admin-utils.sh index 3df7d4fc4e..aa2736a057 100755 --- a/src/bin/admin/admin-utils.sh +++ b/src/bin/admin/admin-utils.sh @@ -18,7 +18,7 @@ mysql_execute() { QUERY=$1 shift - if [ $# -gt 1 ]; then + if [ $# -ge 1 ]; then mysql -N -B $* -e "${QUERY}" retcode=$? else @@ -47,7 +47,7 @@ mysql_version() { pgsql_execute() { QUERY=$1 shift - if [ $# -gt 0 ]; then + if [ $# -ge 1 ]; then echo $QUERY | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q $* retcode=$? else @@ -71,7 +71,7 @@ pgsql_execute() { pgsql_execute_script() { file=$1 shift - if [ $# -gt 0 ]; then + if [ $# -ge 1 ]; then psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -f $file $* retcode=$? else @@ -90,8 +90,8 @@ pgsql_version() { cql_execute() { query=$1 shift - if [ $# -gt 1 ]; then - cqlsh $* -e "$query" + if [ $# -ge 1 ]; then + cqlsh "$@" -e "$query" retcode=$? else cqlsh -u $db_user -p $db_password -k $db_name -e "$query" @@ -109,8 +109,8 @@ cql_execute() { cql_execute_script() { file=$1 shift - if [ $# -gt 1 ]; then - cqlsh $* -e "$file" + if [ $# -ge 1 ]; then + cqlsh "$@" -f "$file" retcode=$? else cqlsh -u $db_user -p $db_password -k $db_name -f "$file" @@ -126,8 +126,9 @@ cql_execute_script() { } cql_version() { - version=`cql_execute "SELECT version, minor FROM schema_version" "$@"` - version=`echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 --output-delimiter="."` - echo $version - return $? + version=$(cql_execute "SELECT version, minor FROM schema_version" "$@") + error=$? + version=$(echo "$version" | grep -A 1 "+" | grep -v "+" | tr -d ' ' | cut -d "|" -f 1-2 --output-delimiter=".") + echo "$version" + return $error } diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 32a27a3154..01310fd67d 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -203,8 +203,8 @@ pgsql_init() { cql_init() { printf "Checking if there is a database initialized already... Please ignore errors.\n" - result=`cql_execute "USE $db_name; DESCRIBE tables;"` - if [ "$result"="" ]; then + result=$(cql_execute "DESCRIBE tables;") + if [ $(echo "$result" | grep "" | wc -w) -gt 0 ]; then printf "Creating and initializing tables using script %s...\n" $scripts_dir/cql/dhcpdb_create.cql cql_execute_script $scripts_dir/cql/dhcpdb_create.cql else @@ -212,8 +212,8 @@ cql_init() { exit 2 fi - version=`cql_version` - printf "Lease DB version reported after initialization: $version\n" + version=$(cql_version) + printf "Lease DB version reported after initialization: %s\n" "$version" exit 0 } diff --git a/src/bin/admin/tests/Makefile.am b/src/bin/admin/tests/Makefile.am index ea0f4370e6..8092f27bd9 100644 --- a/src/bin/admin/tests/Makefile.am +++ b/src/bin/admin/tests/Makefile.am @@ -13,6 +13,7 @@ endif if HAVE_CQL SHTESTS += cql_tests.sh endif + noinst_SCRIPTS = $(SHTESTS) EXTRA_DIST = dhcpdb_create_1.0.mysql diff --git a/src/bin/admin/tests/cql_tests.sh.in b/src/bin/admin/tests/cql_tests.sh.in index 80673d1774..6d282b0683 100644 --- a/src/bin/admin/tests/cql_tests.sh.in +++ b/src/bin/admin/tests/cql_tests.sh.in @@ -44,8 +44,7 @@ cql_lease_init_test() { assert_eq 0 $? "lease4 table check failed, expected exit code: %d, actual: %d" # Check lease6 table - cql_execute "SELECT address, duid, valid_lifetime, expire, subnet_id, pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname,\ - state FROM lease6;" + cql_execute "SELECT address, duid, valid_lifetime, expire, subnet_id, pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, state FROM lease6;" assert_eq 0 $? "lease6 table check failed, expected exit code: %d, actual: %d" # Check lease6_types table @@ -83,7 +82,7 @@ cql_lease_version_test() { # Verfiy that kea-admin lease-version returns the correct version. version=$($keaadmin lease-version cql -u $db_user -p $db_password -n $db_name) - assert_str_eq "1.0" $version "Expected kea-admin to return %s, returned value was %s" + assert_str_eq "2.0" $version "Expected kea-admin to return %s, returned value was %s" # Wipe the database. cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql @@ -151,20 +150,20 @@ cql_lease4_dump_test() { # 1430694930 corresponds to 2015-04-04 01:15:30 # 1433464245 corresponds to 2015-05-05 02:30:45 # 1436173267 corresponds to 2015-06-06 11:01:07 - insert_sql="\ -insert into lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\ + insert_cql="\ +INSERT INTO lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\ fqdn_fwd, fqdn_rev, hostname, state)\ - values(-1073741302,textAsBlob('20'),textAsBlob('30'),40,1430694930,50,true,true,\ + VALUES(-1073741302,textAsBlob('20'),textAsBlob('30'),40,1430694930,50,true,true,\ 'one.example.com', 0);\ -insert into lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\ +INSERT INTO lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\ fqdn_fwd, fqdn_rev, hostname, state)\ - values(-1073741301,NULL,textAsBlob('123'),40,1433464245,50,true,true,'', 1);\ -insert into lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\ + VALUES(-1073741301,NULL,textAsBlob('123'),40,1433464245,50,true,true,'', 1);\ +INSERT INTO lease4(address, hwaddr, client_id, valid_lifetime, expire, subnet_id,\ fqdn_fwd, fqdn_rev, hostname, state)\ - values(-1073741300,textAsBlob('22'),NULL,40,1436173267,50,true,true,\ + VALUES(-1073741300,textAsBlob('22'),NULL,40,1436173267,50,true,true,\ 'three.example.com', 2);" - cql_execute "$insert_sql" + cql_execute "$insert_cql" assert_eq 0 $? "insert into lease4 failed, expected exit code %d, actual %d" # Dump lease4 to output_file. @@ -220,24 +219,24 @@ cql_lease6_dump_test() { # 1430694930 corresponds to 2015-04-04 01:15:30 # 1433464245 corresponds to 2015-05-05 02:30:45 # 1436173267 corresponds to 2015-06-06 11:01:07 - insert_sql="\ -insert into lease6(address, duid, valid_lifetime, expire, subnet_id,\ + insert_cql="\ +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('2001:db8::10',textAsBlob('20'),30,1430694930,40,50,1,60,70,true,true,\ + VALUES('2001:db8::10',textAsBlob('20'),30,1430694930,40,50,1,60,70,true,true,\ 'one.example.com',textAsBlob('80'),90,16,0);\ -insert into lease6(address, duid, valid_lifetime, expire, subnet_id,\ +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('2001:db8::11',NULL,30,1433464245,40,50,1,60,70,true,true,\ + VALUES('2001:db8::11',NULL,30,1433464245,40,50,1,60,70,true,true,\ '',textAsBlob('80'),90,1,1);\ -insert into lease6(address, duid, valid_lifetime, expire, subnet_id,\ +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('2001:db8::12',textAsBlob('21'),30,1436173267,40,50,1,60,70,true,true,\ + VALUES('2001:db8::12',textAsBlob('21'),30,1436173267,40,50,1,60,70,true,true,\ 'three.example.com',textAsBlob('80'),90,4,2);" - cql_execute "$insert_sql" + cql_execute "$insert_cql" assert_eq 0 $? "insert into lease6 failed, expected exit code %d, actual %d" # Dump lease4 to output_file. diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.cql b/src/bin/admin/tests/dhcpdb_create_1.0.cql index e015a260f3..2d6c45a93a 100644 --- a/src/bin/admin/tests/dhcpdb_create_1.0.cql +++ b/src/bin/admin/tests/dhcpdb_create_1.0.cql @@ -38,11 +38,14 @@ -- 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. +-- Since initialization is done only once, it's performance is not an issue. -- This line starts database initialization to 1.0. -- Holds the IPv4 leases. +-- ----------------------------------------------------- +-- Table `lease4` +-- ----------------------------------------------------- CREATE TABLE lease4 ( address int, hwaddr blob, @@ -61,11 +64,15 @@ CREATE TABLE lease4 ( 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); +CREATE INDEX lease4index4 ON lease4 (expire); +CREATE INDEX lease4index5 ON lease4 (state); -- Holds the IPv6 leases. -- N.B. The use of a VARCHAR for the address is temporary for development: -- it will eventually be replaced by BINARY(16). +-- ----------------------------------------------------- +-- Table `lease6` +-- ----------------------------------------------------- CREATE TABLE lease6 ( address varchar, duid blob, @@ -91,13 +98,17 @@ 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); +CREATE INDEX lease6index5 ON lease6 (expire); +CREATE INDEX lease6index6 ON lease6 (state); -- ... and a definition of lease6 types. This table is a convenience for -- users of the database - if they want to view the lease table and use the -- type names, they can join this table with the lease6 table. -- Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/ -- lease_mgr.h) +-- ----------------------------------------------------- +-- Table `lease6_types` +-- ----------------------------------------------------- CREATE TABLE lease6_types ( lease_type int, -- Lease type code. name varchar, -- Name of the lease type @@ -107,13 +118,15 @@ INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA'); -- Non-tempor 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 - -- 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. +-- ----------------------------------------------------- +-- Table `lease_hwaddr_source` +-- ----------------------------------------------------- CREATE TABLE lease_hwaddr_source ( hwaddr_source int, name varchar, @@ -141,47 +154,12 @@ INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE -- 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. +-- ----------------------------------------------------- +-- Table `lease_state` +-- ----------------------------------------------------- CREATE TABLE lease_state ( state int, name varchar, @@ -197,11 +175,18 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed'); -- 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/share/database/scripts/cassandra/dhcpdb_create.cql, --- which defines the schema for the unit tests. +-- Note: This MUST be kept synchronized with +-- src/share/database/scripts/cql/dhcpdb_create.cql which defines the schema for +-- the unit tests. +-- ----------------------------------------------------- +-- Table `schema_version` +-- ----------------------------------------------------- CREATE TABLE schema_version ( version int, minor int, PRIMARY KEY (version) ); + INSERT INTO schema_version (version, minor) VALUES (1, 0); + +-- This line concludes database initalization to version 1.0. diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 6c8f48bb0f..b62f923137 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -124,6 +124,7 @@ libkea_dhcpsrv_la_SOURCES += logging.cc logging.h libkea_dhcpsrv_la_SOURCES += logging_info.cc logging_info.h libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h libkea_dhcpsrv_la_SOURCES += memfile_lease_storage.h +libkea_dhcpsrv_la_SOURCES += sql_common.h if HAVE_MYSQL libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h @@ -139,10 +140,13 @@ libkea_dhcpsrv_la_SOURCES += pgsql_exchange.cc pgsql_exchange.h libkea_dhcpsrv_la_SOURCES += pgsql_host_data_source.cc pgsql_host_data_source.h libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h endif + if HAVE_CQL -libkea_dhcpsrv_la_SOURCES += cql_lease_mgr.cc cql_lease_mgr.h libkea_dhcpsrv_la_SOURCES += cql_connection.cc cql_connection.h +libkea_dhcpsrv_la_SOURCES += cql_exchange.cc cql_exchange.h +libkea_dhcpsrv_la_SOURCES += cql_lease_mgr.cc cql_lease_mgr.h endif + libkea_dhcpsrv_la_SOURCES += pool.cc pool.h libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h @@ -236,5 +240,3 @@ libkea_dhcpsrv_include_HEADERS = \ install-data-local: $(mkinstalldirs) $(DESTDIR)$(dhcp_data_dir) - - diff --git a/src/lib/dhcpsrv/cql_connection.cc b/src/lib/dhcpsrv/cql_connection.cc index 1dec234ebf..b4e20c7856 100644 --- a/src/lib/dhcpsrv/cql_connection.cc +++ b/src/lib/dhcpsrv/cql_connection.cc @@ -14,22 +14,27 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include -#include +#include + +#include using namespace std; namespace isc { namespace dhcp { -CqlConnection::CqlConnection(const ParameterMap& parameters) : - DatabaseConnection(parameters), cluster_(NULL), session_(NULL), - tagged_statements_(NULL) { +CqlConnection::CqlConnection(const ParameterMap& parameters) + : DatabaseConnection(parameters), cluster_(NULL), session_(NULL), + force_consistency_(true), consistency_(CASS_CONSISTENCY_QUORUM), + tagged_statements_(NULL) { } CqlConnection::~CqlConnection() { - // Free up the prepared statements, ignoring errors. - // Session and connection resources are deallocated. + // Free up the prepared statements, ignoring errors. Session and connection + // resources are deallocated. CassError rc = CASS_OK; std::string error; @@ -43,7 +48,8 @@ CqlConnection::~CqlConnection() { if (session_) { CassFuture* close_future = cass_session_close(session_); cass_future_wait(close_future); - checkStatementError(error, close_future, "could not close connection to DB"); + 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_); @@ -55,10 +61,9 @@ CqlConnection::~CqlConnection() { cluster_ = NULL; } - // We're closing the connection anyway. Let's not throw at this - // stage if (rc != CASS_OK) { - isc_throw(DbOpenError, error); + // We're closing the connection anyway. Let's not throw at this stage. + LOG_ERROR(dhcpsrv_logger, DHCPSRV_CQL_DEALLOC_ERROR).arg(error); } } @@ -67,25 +72,25 @@ CqlConnection::openDatabase() { CassError rc; // Set up the values of the parameters const char* contact_points = "127.0.0.1"; - string scontact_points; + std::string scontact_points; try { scontact_points = getParameter("contact_points"); contact_points = scontact_points.c_str(); } catch (...) { - // No host. Fine, we'll use "localhost". + // No host. Fine, we'll use "127.0.0.1". } const char* port = NULL; - string sport; + std::string sport; try { sport = getParameter("port"); port = sport.c_str(); } catch (...) { - // No port. Fine, we'll use "default". + // No port. Fine, we'll use the default "9042". } const char* user = NULL; - string suser; + std::string suser; try { suser = getParameter("user"); user = suser.c_str(); @@ -94,7 +99,7 @@ CqlConnection::openDatabase() { } const char* password = NULL; - string spassword; + std::string spassword; try { spassword = getParameter("password"); password = spassword.c_str(); @@ -103,36 +108,170 @@ CqlConnection::openDatabase() { } const char* keyspace = "keatest"; - string skeyspace; + std::string skeyspace; try { skeyspace = getParameter("keyspace"); keyspace = skeyspace.c_str(); } catch (...) { - // No keyspace name. Fine, we'll use default "keatest". + // No keyspace name. Fine, we'll use "keatest". + } + + const char* reconnect_wait_time = NULL; + std::string sreconnect_wait_time; + try { + sreconnect_wait_time = getParameter("reconnect-wait-time"); + reconnect_wait_time = sreconnect_wait_time.c_str(); + } catch (...) { + // No reconnect wait time. Fine, we'll use the default "2000". + } + + const char* connect_timeout = NULL; + std::string sconnect_timeout; + try { + sconnect_timeout = getParameter("connect-timeout"); + connect_timeout = sconnect_timeout.c_str(); + } catch (...) { + // No connect timeout. Fine, we'll use the default "5000". + } + + const char* request_timeout = NULL; + std::string srequest_timeout; + try { + srequest_timeout = getParameter("request-timeout"); + request_timeout = srequest_timeout.c_str(); + } catch (...) { + // No request timeout. Fine, we'll use the default "12000". + } + + const char* tcp_keepalive = NULL; + std::string stcp_keepalive; + try { + stcp_keepalive = getParameter("tcp-keepalive"); + tcp_keepalive = stcp_keepalive.c_str(); + } catch (...) { + // No tcp-keepalive. Fine, we'll not use TCP keepalive. + } + + std::string stcp_nodelay; + try { + stcp_nodelay = getParameter("tcp-nodelay"); + } catch (...) { + // No tcp-nodelay. Fine, we'll use the default false. } cluster_ = cass_cluster_new(); cass_cluster_set_contact_points(cluster_, contact_points); - if (user != NULL && password != NULL) { + if (user && password) { cass_cluster_set_credentials(cluster_, user, password); } - if (port != NULL) { + if (port) { int port_number; try { port_number = boost::lexical_cast(port); - } catch (const std::exception& ex) { - isc_throw(DbOperationError, "Invalid int data: " << port - << " : " << ex.what()); + if (port_number < 1 || port_number > 65535) { + isc_throw( + DbOperationError, + "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()); } cass_cluster_set_port(cluster_, port_number); } + if (reconnect_wait_time) { + int reconnect_wait_time_number; + try { + reconnect_wait_time_number = + boost::lexical_cast(reconnect_wait_time); + if (reconnect_wait_time_number < 0) { + isc_throw(DbOperationError, + "Invalid reconnect wait time, positive number " + "expected, 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()); + } + cass_cluster_set_reconnect_wait_time(cluster_, + reconnect_wait_time_number); + } + + if (connect_timeout) { + int connect_timeout_number; + try { + connect_timeout_number = boost::lexical_cast(connect_timeout); + if (connect_timeout_number < 0) { + isc_throw(DbOperationError, + "Invalid connect timeout, positive number expected, " + "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()); + } + cass_cluster_set_connect_timeout(cluster_, connect_timeout_number); + } + + if (request_timeout) { + int request_timeout_number; + try { + request_timeout_number = boost::lexical_cast(request_timeout); + if (request_timeout_number < 0) { + isc_throw(DbOperationError, + "Invalid request timeout, positive number expected, " + "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()); + } + cass_cluster_set_request_timeout(cluster_, request_timeout_number); + } + + if (tcp_keepalive) { + int tcp_keepalive_number; + try { + tcp_keepalive_number = boost::lexical_cast(tcp_keepalive); + if (tcp_keepalive_number < 0) { + isc_throw(DbOperationError, + "Invalid TCP keepalive, positive number expected, " + "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()); + } + cass_cluster_set_tcp_keepalive(cluster_, cass_true, + tcp_keepalive_number); + } + + if (stcp_nodelay == "true") { + cass_cluster_set_tcp_nodelay(cluster_, cass_true); + } + session_ = cass_session_new(); - CassFuture* connect_future = cass_session_connect_keyspace(session_, - cluster_, keyspace); + 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"); @@ -148,11 +287,12 @@ CqlConnection::openDatabase() { } void -CqlConnection::prepareStatements(CqlTaggedStatement *statements) { +CqlConnection::prepareStatements(CqlTaggedStatement* statements) { CassError rc = CASS_OK; uint32_t size = 0; tagged_statements_ = statements; - for (; tagged_statements_[size].params_; size++); + for (; tagged_statements_[size].params_; size++) { + } statements_.resize(size); for (uint32_t i = 0; i < size; i++) { const char* query = tagged_statements_[i].text_; @@ -174,6 +314,19 @@ CqlConnection::prepareStatements(CqlTaggedStatement *statements) { } } +void +CqlConnection::setConsistency(bool force, CassConsistency consistency) { + force_consistency_ = force; + consistency_ = consistency; +} + +void +CqlConnection::startTransaction() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_BEGIN_TRANSACTION); + // No-op +} + void CqlConnection::commit() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT); @@ -185,27 +338,31 @@ CqlConnection::rollback() { } void -CqlConnection::checkStatementError(std::string& error, CassFuture* future, - uint32_t stindex, const char* what) const { +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: " << tagged_statements_[stindex].name_; + 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: " << tagged_statements_[stindex].name_ - << " reason: " << errorMessage << " error code: " << rc; + stream << what << " for statement " << tagged_statements_[stindex].name_ + << ". Future error: " << errorMessage + << ". Error description: " << cass_error_desc(rc); } error = stream.str(); } void -CqlConnection::checkStatementError(std::string& error, CassFuture* future, +CqlConnection::checkStatementError(std::string& error, + CassFuture* future, const char* what) const { CassError rc; const char* errorMessage; @@ -218,10 +375,29 @@ CqlConnection::checkStatementError(std::string& error, CassFuture* future, if (rc != CASS_OK) { stream.str(std::string()); - stream << what << " reason: " << errorMessage << " error code: " << rc; + stream << what << ". Future error: " << errorMessage + << ". Error description: " << cass_error_desc(rc); } error = stream.str(); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +CqlTransaction::CqlTransaction(CqlConnection& conn) + : conn_(conn), committed_(false) { + conn_.startTransaction(); +} + +CqlTransaction::~CqlTransaction() { + // Rollback if commit() wasn't explicitly called. + if (!committed_) { + conn_.rollback(); + } +} + +void +CqlTransaction::commit() { + conn_.commit(); + committed_ = true; +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cql_connection.h b/src/lib/dhcpsrv/cql_connection.h index 30bbeed1f0..7abcfbc5e3 100644 --- a/src/lib/dhcpsrv/cql_connection.h +++ b/src/lib/dhcpsrv/cql_connection.h @@ -19,39 +19,52 @@ #include #include -#include + #include + +#include + +#include + +#include +#include #include namespace isc { namespace dhcp { -/// @brief Defines a single query +/// @brief Defines a single statement /// -/// @param params_ Bind parameter names -/// @param name_ Short name of the query. -/// @param text_ Text representation of the actual query. +/// @param params_ parameter names +/// @param name_ short description of the query +/// @param text_ text representation of the actual query struct CqlTaggedStatement { const char** params_; const char* name_; const char* text_; }; -// Defines CQL backend version: 2.3 +// 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; -/// Defines CQL schema version: 1.0 +/// Define CQL schema version: 1.0 const uint32_t CQL_SCHEMA_VERSION_MAJOR = 1; const uint32_t CQL_SCHEMA_VERSION_MINOR = 0; +/// @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 +/// use instances of CqlConnection. class CqlConnection : public DatabaseConnection { public: - /// @brief Constructor /// /// Initialize CqlConnection object with parameters needed for connection. - CqlConnection(const ParameterMap& parameters); + explicit CqlConnection(const ParameterMap& parameters); /// @brief Destructor virtual ~CqlConnection(); @@ -61,21 +74,27 @@ public: /// Creates the prepared statements for all of the CQL statements used /// by the CQL 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(CqlTaggedStatement *statements); + /// @throw isc::dhcp::DbOperationError if an operation on the open database + /// 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); - /// @brief Open Database + /// @brief Open database /// /// Opens the database using the information supplied in the parameters /// passed to the constructor. If no parameters are supplied, the default - /// values will be used (keyspace keatest). + /// values will be used (e.g. keyspace 'keatest', port 9042). /// - /// @throw DbOpenError Error opening the database + /// @throw DbOpenError error opening the database void openDatabase(); + /// @brief Set consistency + void setConsistency(bool force, CassConsistency consistency); + + /// @brief Start transaction + void startTransaction(); + /// @brief Commit Transactions /// /// This is a no-op for Cassandra. @@ -86,17 +105,20 @@ public: /// This is a no-op for Cassandra. virtual void rollback(); - /// @brief Check Error + /// @brief Check for errors /// - /// Chech error for current database operation. - void checkStatementError(std::string& error, CassFuture* future, - uint32_t stindex, const char* what) const; + /// Check for errors on the current database operation. + void checkStatementError(std::string& error, + CassFuture* future, + uint32_t stindex, + const char* what) const; - /// @brief Check Error + /// @brief Check for errors /// - /// Chech error for current database operation. - void checkStatementError(std::string& error, CassFuture* future, - const char* what) const; + /// Check for errors on the current database operation. + void checkStatementError(std::string& error, + CassFuture* future, + const char* what) const; /// @brief CQL connection handle CassCluster* cluster_; @@ -104,16 +126,69 @@ public: /// @brief CQL session handle CassSession* session_; - /// @brief CQL prepared statements - used for faster statement execution using - /// bind functionality + /// @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 statements_; - /// @brief Pointer to external array of tagged statements containing statement - /// name, array of names of bind parameters and text query + /// @brief Pointer to external array of tagged statements containing + /// statement name, array of names of bind parameters and text query CqlTaggedStatement* tagged_statements_; }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +/// @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 Commits transaction + /// + /// Calls @ref CqlConnection::commit().. + void commit(); + +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_; +}; + +} // namespace dhcp +} // namespace isc -#endif // CQL_CONNECTION_H +#endif // CQL_CONNECTION_H diff --git a/src/lib/dhcpsrv/cql_exchange.cc b/src/lib/dhcpsrv/cql_exchange.cc new file mode 100644 index 0000000000..c73f74b2fa --- /dev/null +++ b/src/lib/dhcpsrv/cql_exchange.cc @@ -0,0 +1,485 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Author: Razvan Becheriu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include + +#include +#include + +/// @name CqlBind auxiliary functions for binding data into Cassandra format: +/// @{ + +/// @todo These void* cast are unsafe. See ticket #4525. +static CassError +CqlBindNone(CassStatement* statement, size_t index, void*) { + return cass_statement_bind_null(statement, index); +} + +static CassError +CqlBindBool(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_bool(statement, index, + *(static_cast(value))); +} + +static CassError +CqlBindInt32(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_int32(statement, index, + *(static_cast(value))); +} + +static CassError +CqlBindInt64(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_int64(statement, index, + *(static_cast(value))); +} + +static CassError +CqlBindTimestamp(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_int64(statement, index, + *(static_cast(value))); +} + +static CassError +CqlBindString(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_string( + statement, index, static_cast(value)->c_str()); +} + +static CassError +CqlBindBytes(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_bytes( + statement, index, static_cast*>(value)->data(), + static_cast*>(value)->size()); +} + +static CassError +CqlBindUuid(CassStatement* statement, size_t index, void* value) { + return cass_statement_bind_uuid(statement, index, + *static_cast(value)); +} +/// @} + +static CassError +CqlGetNone(const CassValue*, void*) { + return CASS_OK; +} + +static CassError +CqlGetBool(const CassValue* value, void* data) { + return cass_value_get_bool(value, static_cast(data)); +} + +static CassError +CqlGetInt32(const CassValue* value, void* data) { + return cass_value_get_int32(value, static_cast(data)); +} + +static CassError +CqlGetInt64(const CassValue* value, void* data) { + return cass_value_get_int64(value, static_cast(data)); +} + +static CassError +CqlGetTimestamp(const CassValue* value, void* data) { + return cass_value_get_int64(value, static_cast(data)); +} + +static CassError +CqlGetString(const CassValue* value, void* data) { + const char* dataValue; + size_t sizeValue; + CassError cassError = cass_value_get_string( + value, static_cast(&dataValue), &sizeValue); + static_cast(data)->assign(dataValue, dataValue + sizeValue); + return cassError; +} + +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(&dataValue), &sizeValue); + static_cast*>(data)->assign(dataValue, + dataValue + sizeValue); + return cassError; +} + +static CassError +CqlGetUuid(const CassValue* value, void* data) { + return cass_value_get_uuid(value, static_cast(data)); +} + +namespace isc { +namespace dhcp { + +struct CqlFunctionData CqlFunctions[] = { + {CqlBindNone, CqlGetNone}, {CqlBindBool, CqlGetBool}, + {CqlBindInt32, CqlGetInt32}, {CqlBindInt64, CqlGetInt64}, + {CqlBindTimestamp, CqlGetTimestamp}, {CqlBindString, CqlGetString}, + {CqlBindBytes, CqlGetBytes}, {CqlBindUuid, CqlGetUuid}}; + +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_; + } + } + return EXCHANGE_DATA_TYPE_NONE; +} + +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"); + } + CqlFunctions[type].cqlBindFunction_(statement, i, data.values_[i]); + } +} + +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"); + } + if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) { + isc_throw(BadValue, "index " << type << " out of bounds"); + } + 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)); + } + } +} + +CqlExchange::CqlExchange() { +} + +CqlExchange::~CqlExchange() { +} + +void +CqlExchange::convertToDatabaseTime(const time_t& cltt, + const cass_int64_t& valid_lifetime, + cass_int64_t& expire) { + // Calculate expiry time. Store it in the 64-bit value so as we can + // detect overflows. + cass_int64_t expire_time = static_cast(cltt) + valid_lifetime; + + if (expire_time > DatabaseConnection::MAX_DB_TIME) { + isc_throw(BadValue, "Time value is too large: " << expire_time); + } + + expire = expire_time; +} + +void +CqlExchange::convertFromDatabaseTime(const cass_int64_t& expire, + const cass_int64_t& valid_lifetime, + time_t& cltt) { + // Convert to local time + cltt = static_cast(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& + parameters /* = std::vector() */) { + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + + statement = cass_prepared_bind(connection.statements_[statementIndex]); + if (!statement) { + isc_throw(DbOperationError, + "unable to bind statement " + << connection.tagged_statements_[statementIndex].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_); + } + } + + CqlCommon::bindData(statement, statementIndex, data, *this, + connection.tagged_statements_); + + 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_); + } + cass_future_wait(future); + std::string error; + connection.checkStatementError(error, future, statementIndex, + "unable to execute statement"); + rc = cass_future_error_code(future); + if (rc != CASS_OK) { + cass_future_free(future); + cass_statement_free(statement); + isc_throw(DbOperationError, error); + } + + // Get column values. + const CassResult* resultCollection = cass_future_get_result(future); + if (single && cass_result_row_count(resultCollection) > 1) { + cass_result_free(resultCollection); + 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_); + } + + // Get results. + CqlDataArray returnValues; + CqlDataArray collection; + CassIterator* rows = cass_iterator_from_result(resultCollection); + 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); + } + collection.add(retrieve()); + } + + // Free resources. + cass_iterator_free(rows); + cass_result_free(resultCollection); + cass_future_free(future); + cass_statement_free(statement); + + return collection; +} + +void +CqlExchange::executeWrite(const CqlConnection& connection, + const CqlDataArray& data, + const int statementIndex) { + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + + statement = cass_prepared_bind(connection.statements_[statementIndex]); + if (!statement) { + isc_throw(DbOperationError, + "unable to bind statement " + << connection.tagged_statements_[statementIndex].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_); + } + } + + CqlCommon::bindData(statement, statementIndex, data, *this, + connection.tagged_statements_); + + 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_); + } + cass_future_wait(future); + std::string error; + connection.checkStatementError(error, future, statementIndex, + "unable to execute statement"); + 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. + bool applied = hasStatementBeenApplied(future); + + // Free resources. + 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"); + } +} + +bool +CqlExchange::hasStatementBeenApplied(CassFuture* future, + size_t* row_count, + size_t* column_count) { + const CassResult* resultCollection = cass_future_get_result(future); + if (row_count) { + *row_count = cass_result_row_count(resultCollection); + } + if (column_count) { + *column_count = cass_result_column_count(resultCollection); + } + CassIterator* rows = cass_iterator_from_result(resultCollection); + CqlDataArray 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(&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); + } + } + cass_iterator_free(rows); + cass_result_free(resultCollection); + return applied == cass_true; +} + +void* +CqlExchange::retrieve() { + isc_throw(NotImplemented, "CqlExchange::retrieve() not implemented yet."); +} + +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 */) { + // Start with a fresh array. + data.clear(); + // id: blob + data.add(reinterpret_cast(&version_)); + // host_identifier: blob + data.add(reinterpret_cast(&minor_)); +} + +void* +CqlVersionExchange::retrieve() { + pair_ = VersionPair(version_, minor_); + return reinterpret_cast(&pair_); +} + +VersionPair +CqlVersionExchange::retrieveVersion(const CqlConnection& connection, + int statementIndex) { + // Run statement. + const CqlDataArray whereValues; + CqlDataArray versionCollection = + executeRead(connection, whereValues, statementIndex, true); + + VersionPair result; + + if (!versionCollection.empty()) { + result = *(reinterpret_cast( + *versionCollection.begin())); + } + + return result; +} + +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cql_exchange.h b/src/lib/dhcpsrv/cql_exchange.h new file mode 100644 index 0000000000..191dc5fcac --- /dev/null +++ b/src/lib/dhcpsrv/cql_exchange.h @@ -0,0 +1,325 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Author: Razvan Becheriu +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CQL_EXCHANGE_H +#define CQL_EXCHANGE_H + +#include +#include +#include +#include + +#include +#include +#include + +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 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 Collection of bind and get functions, one pair for each +/// parameter/column +extern struct CqlFunctionData CqlFunctions[]; + +/// @brief Structure used to bind C++ input values to dynamic CQL parameters +/// +/// The structure contains a vector which stores the input values. The vector is +/// passed directly into the CQL execute call. Note that the data values are +/// stored as pointers. These pointers need to be valid for the duration of the +/// 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() { + } + + /// @brief Copy constructor + CqlDataArray(const CqlDataArray& other) : values_(other.values_) { + } + + /// @brief Destructor + virtual ~CqlDataArray() { + } + + /// @brief Add a at the end of the vector. + void add(void* value) { + values_.push_back(value); + } + + /// @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::const_iterator begin() const { + return values_.begin(); + } + + /// @brief Iterator pointing to the past-the-end element + std::vector::const_iterator end() const { + return values_.end(); + } + + /// @brief Vector of pointers to the data values + std::vector values_; +}; + +/// @brief Cassandra Exchange +/// +/// Used to convert between Cassandra CQL and C++ data types. A different +/// exchange is made for every distinct set of columns. Multiple tables may use +/// the same exchange if they have the same columns. +class CqlExchange : public virtual SqlExchange { +public: + /// @brief Constructor + /// + /// Empty body. Derived constructors specify table columns. + CqlExchange(); + + /// @brief Destructor + virtual ~CqlExchange(); + + /// @name Time conversion: + /// @{ + static void convertToDatabaseTime(const time_t& cltt, + const cass_int64_t& valid_lifetime, + cass_int64_t& expire); + static void convertFromDatabaseTime(const cass_int64_t& expire, + const cass_int64_t& valid_lifetime, + time_t& cltt); + /// @} + + /// @brief Create BIND array to receive C++ data. + /// + /// Used in executeRead() 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 + /// invalid index + virtual void createBindForReceive(CqlDataArray& data, + const int statementIndex = -1); + + /// @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 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 + /// + /// @throw DbOperationError + /// @throw MultipleRecords + CqlDataArray executeRead(const CqlConnection& connection, + const CqlDataArray& whereValues, + const int statementIndex, + const bool single = false, + const std::vector& parameters = + std::vector()); + + /// @brief Executes insert, update, delete or other statements. + /// + /// @param connection connection used to communicate with the Cassandra + /// database + /// @param assignedValues 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); + + /// @brief Check if CQL statement has been applied. + /// + /// @param future structure used to wait on statement executions + /// @param row_count number of rows returned + /// @param column_count number of columns queried + /// + /// @return true if statement has been succesfully applied, false otherwise + bool hasStatementBeenApplied(CassFuture* future, + size_t* row_count = NULL, + size_t* column_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 + /// CqlExchange. To be implemented in derived classes. + /// + /// @return a pointer to the object retrieved. + virtual void* retrieve(); +}; + +/// @brief Exchange used to retrieve schema version from the keyspace. +class CqlVersionExchange : public virtual CqlExchange { +public: + /// @brief Constructor + /// + /// Specifies table columns. + CqlVersionExchange(); + + /// @brief Destructor + virtual ~CqlVersionExchange(); + + /// @brief Create BIND array to receive C++ data. + /// + /// Used in executeRead() 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 + /// invalid index + virtual void createBindForReceive(CqlDataArray& data, + const int statementIndex = -1); + + /// @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); + + /// @brief Copy received data into the pair. + /// + /// Copies information about the version to be retrieved into a pair. Called + /// in executeRead(). + /// + /// @return a pointer to the object retrieved. + virtual void* retrieve(); + +private: + cass_int32_t version_; + cass_int32_t minor_; + 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. + /// + /// @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 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); + + /// @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. + /// + /// @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); +}; + +} // namespace dhcp +} // namespace isc + +#endif // CQL_EXCHANGE_H diff --git a/src/lib/dhcpsrv/cql_lease_mgr.cc b/src/lib/dhcpsrv/cql_lease_mgr.cc index f7486c6054..7175dd23ea 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.cc +++ b/src/lib/dhcpsrv/cql_lease_mgr.cc @@ -15,17 +15,14 @@ // limitations under the License. #include + #include #include #include -#include #include -#include -#include -#include -#include -#include -#include +#include + +#include using namespace isc; using namespace isc::dhcp; @@ -37,247 +34,158 @@ namespace dhcp { static const size_t HOSTNAME_MAX_LEN = 255U; static const size_t ADDRESS6_TEXT_MAX_LEN = 39U; -/// @name CqlBind auxiliary methods for binding data into Cassandra format: /// @{ - -/// @todo These void* cast are unsafe. See ticket #4525. -static CassError CqlBindNone(CassStatement* statement, size_t index, void*) { - return cass_statement_bind_null(statement, index); -} - -static CassError CqlBindBool(CassStatement* statement, size_t index, - void* value) { - return cass_statement_bind_bool(statement, index, - *(static_cast(value))); -} - -static CassError CqlBindInt32(CassStatement* statement, size_t index, - void* value) { - return cass_statement_bind_int32(statement, index, - *(static_cast(value))); -} - -static CassError CqlBindInt64(CassStatement* statement, size_t index, - void* value) { - return cass_statement_bind_int64(statement, index, - *(static_cast(value))); -} - -static CassError CqlBindTimestamp(CassStatement* statement, size_t index, - void* value) { - return cass_statement_bind_int64(statement, index, - *(static_cast(value))); -} - -static CassError CqlBindString(CassStatement* statement, size_t index, - void* value) { - return cass_statement_bind_string(statement, index, - (static_cast(value))); -} - -static CassError CqlBindBytes(CassStatement* statement, size_t index, - void* value) { - return cass_statement_bind_bytes(statement, index, - static_cast*>(value)->data(), - static_cast*>(value)->size()); -} -/// @} - -static CassError CqlGetNone(const CassValue*, void*, size_t*) { - return CASS_OK; -} - -static CassError CqlGetBool(const CassValue* value, void* data, size_t*) { - return cass_value_get_bool(value, static_cast(data)); -} - -static CassError CqlGetInt32(const CassValue* value, void* data, size_t*) { - return cass_value_get_int32(value, static_cast(data)); -} - -static CassError CqlGetInt64(const CassValue* value, void* data, size_t*) { - return cass_value_get_int64(value, static_cast(data)); -} - -static CassError CqlGetTimestamp(const CassValue* value, void* data, size_t*) { - return cass_value_get_int64(value, static_cast(data)); -} - -static CassError CqlGetString(const CassValue* value, void* data, - size_t* size) { - return cass_value_get_string(value, static_cast(data), size); -} - -static CassError CqlGetBytes(const CassValue* value, void* data, size_t* size) { - return cass_value_get_bytes(value, static_cast(data), - size); -} - -typedef CassError (*CqlBindFunction)(CassStatement* statement, size_t index, - void* value); -typedef CassError (*CqlGetFunction)(const CassValue* value, void* data, - size_t* size); - -struct CqlFunctionData { - CqlBindFunction sqlBindFunction_; - CqlGetFunction sqlGetFunction_; -}; - -static struct CqlFunctionData CqlFunctions[] = { - {CqlBindNone, CqlGetNone}, - {CqlBindBool, CqlGetBool}, - {CqlBindInt32, CqlGetInt32}, - {CqlBindInt64, CqlGetInt64}, - {CqlBindTimestamp, CqlGetTimestamp}, - {CqlBindString, CqlGetString}, - {CqlBindBytes, CqlGetBytes} -}; - -/// @brief Catalog of all the SQL statements currently supported. Note +/// +/// @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("address"), - NULL }; + static_cast("address"), + NULL }; static const char* delete_expired_lease4_params[] = { - static_cast("state"), - static_cast("expire"), - NULL }; + static_cast("state"), + static_cast("expire"), + NULL }; static const char* delete_lease6_params[] = { - static_cast("address"), - NULL }; + static_cast("address"), + NULL }; static const char* delete_expired_lease6_params[] = { - static_cast("state"), - static_cast("expire"), - NULL }; + static_cast("state"), + static_cast("expire"), + NULL }; static const char* get_lease4_addr_params[] = { - static_cast("address"), - NULL }; + static_cast("address"), + NULL }; static const char* get_lease4_clientid_params[] = { - static_cast("client_id"), - NULL }; + static_cast("client_id"), + NULL }; static const char* get_lease4_clientid_subid_params[] = { - static_cast("client_id"), - static_cast("subnet_id"), - NULL }; + static_cast("client_id"), + static_cast("subnet_id"), + NULL }; static const char* get_lease4_hwaddr_params[] = { - static_cast("hwaddr"), - NULL }; + static_cast("hwaddr"), + NULL }; static const char* get_lease4_hwaddr_subid_params[] = { - static_cast("hwaddr"), - static_cast("subnet_id"), - NULL }; + static_cast("hwaddr"), + static_cast("subnet_id"), + NULL }; static const char* get_lease4_expired_params[] = { - static_cast("state"), - static_cast("expire"), - static_cast("limit"), - NULL }; + static_cast("state"), + static_cast("expire"), + static_cast("limit"), + NULL }; static const char* get_lease6_addr_params[] = { - static_cast("address"), - static_cast("lease_type"), - NULL }; + static_cast("address"), + static_cast("lease_type"), + NULL }; static const char* get_lease6_duid_iaid_params[] = { - static_cast("duid"), - static_cast("iaid"), - static_cast("lease_type"), - NULL }; + static_cast("duid"), + static_cast("iaid"), + static_cast("lease_type"), + NULL }; static const char* get_lease6_duid_iaid_subid_params[] = { - static_cast("duid"), - static_cast("iaid"), - static_cast("subnet_id"), - static_cast("lease_type"), - NULL }; + static_cast("duid"), + static_cast("iaid"), + static_cast("subnet_id"), + static_cast("lease_type"), + NULL }; static const char* get_lease6_expired_params[] = { - static_cast("state"), - static_cast("expire"), - static_cast("limit"), - NULL }; + static_cast("state"), + static_cast("expire"), + static_cast("limit"), + NULL }; static const char* get_version_params[] = { - NULL }; + NULL }; static const char* insert_lease4_params[] = { - static_cast("address"), - static_cast("hwaddr"), - static_cast("client_id"), - static_cast("valid_lifetime"), - static_cast("expire"), - static_cast("subnet_id"), - static_cast("fqdn_fwd"), - static_cast("fqdn_rev"), - static_cast("hostname"), - static_cast("state"), - NULL }; + static_cast("address"), + static_cast("hwaddr"), + static_cast("client_id"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("state"), + NULL }; static const char* insert_lease6_params[] = { - static_cast("address"), - static_cast("duid"), - static_cast("valid_lifetime"), - static_cast("expire"), - static_cast("subnet_id"), - static_cast("pref_lifetime"), - static_cast("lease_type"), - static_cast("iaid"), - static_cast("prefix_len"), - static_cast("fqdn_fwd"), - static_cast("fqdn_rev"), - static_cast("hostname"), - static_cast("hwaddr"), - static_cast("hwtype"), - static_cast("hwaddr_source"), - static_cast("state"), - NULL }; + static_cast("address"), + static_cast("duid"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("pref_lifetime"), + static_cast("lease_type"), + static_cast("iaid"), + static_cast("prefix_len"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("hwaddr"), + static_cast("hwtype"), + static_cast("hwaddr_source"), + static_cast("state"), + NULL }; static const char* update_lease4_params[] = { - static_cast("hwaddr"), - static_cast("client_id"), - static_cast("valid_lifetime"), - static_cast("expire"), - static_cast("subnet_id"), - static_cast("fqdn_fwd"), - static_cast("fqdn_rev"), - static_cast("hostname"), - static_cast("state"), - static_cast("address"), - NULL }; + static_cast("hwaddr"), + static_cast("client_id"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("state"), + static_cast("address"), + NULL }; static const char* update_lease6_params[] = { - static_cast("duid"), - static_cast("valid_lifetime"), - static_cast("expire"), - static_cast("subnet_id"), - static_cast("pref_lifetime"), - static_cast("lease_type"), - static_cast("iaid"), - static_cast("prefix_len"), - static_cast("fqdn_fwd"), - static_cast("fqdn_rev"), - static_cast("hostname"), - static_cast("hwaddr"), - static_cast("hwtype"), - static_cast("hwaddr_source"), - static_cast("state"), - static_cast("address"), - NULL }; + static_cast("duid"), + static_cast("valid_lifetime"), + static_cast("expire"), + static_cast("subnet_id"), + static_cast("pref_lifetime"), + static_cast("lease_type"), + static_cast("iaid"), + static_cast("prefix_len"), + static_cast("fqdn_fwd"), + static_cast("fqdn_rev"), + static_cast("hostname"), + static_cast("hwaddr"), + static_cast("hwtype"), + static_cast("hwaddr_source"), + static_cast("state"), + static_cast("address"), + NULL }; +/// @} CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { // DELETE_LEASE4 { delete_lease4_params, "delete_lease4", "DELETE FROM lease4 WHERE address = ? " - "IF EXISTS" }, + "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 " + "fqdn_fwd, fqdn_rev, hostname, " + "state " "FROM lease4 " - "WHERE state = ? AND expire < ? " - "ALLOW FILTERING" }, + "WHERE state = ? " + "AND expire < ? " + "ALLOW FILTERING " + }, // DELETE_LEASE6 { delete_lease6_params, "delete_lease6", "DELETE FROM lease6 WHERE address = ? " - "IF EXISTS" }, + "IF EXISTS " + }, // DELETE_LEASE6_STATE_EXPIRED { delete_expired_lease6_params, @@ -285,68 +193,86 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "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 " + "hwaddr, hwtype, hwaddr_source, " + "state " "FROM lease6 " - "WHERE state = ? AND expire < ? " - "ALLOW FILTERING" }, + "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 " + "fqdn_fwd, fqdn_rev, hostname, " + "state " "FROM lease4 " - "WHERE address = ?" }, + "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 " + "fqdn_fwd, fqdn_rev, hostname, " + "state " "FROM lease4 " - "WHERE client_id = ?" }, + "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 " + "fqdn_fwd, fqdn_rev, hostname, " + "state " "FROM lease4 " - "WHERE client_id = ? AND subnet_id = ? " - "ALLOW FILTERING" }, + "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 " + "fqdn_fwd, fqdn_rev, hostname, " + "state " "FROM lease4 " - "WHERE hwaddr = ?" }, + "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 " + "fqdn_fwd, fqdn_rev, hostname, " + "state " "FROM lease4 " - "WHERE hwaddr = ? AND subnet_id = ? " - "ALLOW FILTERING" }, + "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 " + "fqdn_fwd, fqdn_rev, hostname, " + "state " "FROM lease4 " - "WHERE state = ? AND expire < ? " + "WHERE state = ? " + "AND expire < ? " "LIMIT ? " - "ALLOW FILTERING" }, + "ALLOW FILTERING " + }, // GET_LEASE6_ADDR { get_lease6_addr_params, @@ -354,21 +280,28 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "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 " + "hwaddr, hwtype, hwaddr_source, " + "state " "FROM lease6 " - "WHERE address = ? AND lease_type = ? " - "ALLOW FILTERING" }, + "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", + "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, @@ -376,10 +309,15 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "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 " + "hwaddr, hwtype, hwaddr_source, " + "state " "FROM lease6 " - "WHERE duid = ? AND iaid = ? AND subnet_id = ? AND lease_type = ? " - "ALLOW FILTERING" }, + "WHERE duid = ? " + "AND iaid = ? " + "AND subnet_id = ? " + "AND lease_type = ? " + "ALLOW FILTERING " + }, // GET_LEASE6_EXPIRE { get_lease6_expired_params, @@ -387,16 +325,20 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "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 " + "hwaddr, hwtype, hwaddr_source, " + "state " "FROM lease6 " - "WHERE state = ? AND expire < ? " + "WHERE state = ? " + "AND expire < ? " "LIMIT ? " - "ALLOW FILTERING" }, + "ALLOW FILTERING " + }, // GET_VERSION { get_version_params, "get_version", - "SELECT version, minor FROM schema_version" }, + "SELECT version, minor FROM schema_version " + }, // INSERT_LEASE4 { insert_lease4_params, @@ -404,8 +346,10 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "INSERT INTO lease4(address, hwaddr, client_id, " "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, " "state) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " - "IF NOT EXISTS" }, + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, " + "?) " + "IF NOT EXISTS " + }, // INSERT_LEASE6 { insert_lease6_params, @@ -413,18 +357,23 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "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" }, + "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 = ? " + "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, " + "state = ? " "WHERE address = ? " - "IF EXISTS" }, + "IF EXISTS " + }, // UPDATE_LEASE6 { update_lease6_params, @@ -433,9 +382,12 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "valid_lifetime = ?, expire = ?, subnet_id = ?, " "pref_lifetime = ?, lease_type = ?, iaid = ?, " "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, " - "hwaddr = ?, hwtype = ?, hwaddr_source = ?, state = ? " + "hwaddr = ?, hwtype = ?, hwaddr_source = ?, " + "state = ? " "WHERE address = ? " - "IF EXISTS" }, + "IF EXISTS " + }, + // End of list sentinel { NULL, NULL, NULL } @@ -449,47 +401,22 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { /// base to both of them, containing some common methods. class CqlLeaseExchange : public CqlExchange { public: - CqlLeaseExchange() : hwaddr_length_(0), expire_(0), subnet_id_(0), - valid_lifetime_(0), fqdn_fwd_(false), fqdn_rev_(false), - hostname_length_(0), state_(0) { - memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_)); - memset(hostname_buffer_, 0, sizeof(hostname_buffer_)); + CqlLeaseExchange() + : valid_lifetime_(0), expire_(0), subnet_id_(0), fqdn_fwd_(cass_false), + fqdn_rev_(cass_false), state_(0) { } -protected: - std::vector hwaddr_; ///< Hardware address - uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN]; - ///< Hardware address buffer - unsigned long hwaddr_length_; ///< Hardware address length - uint64_t expire_; ///< Lease expiry time - uint32_t subnet_id_; ///< Subnet identification - uint64_t valid_lifetime_; ///< Lease time - uint32_t fqdn_fwd_; ///< Has forward DNS update been - ///< performed - uint32_t fqdn_rev_; ///< Has reverse DNS update been - ///< performed - char hostname_buffer_[HOSTNAME_MAX_LEN + 1]; - ///< Client hostname - unsigned long hostname_length_; ///< Client hostname length - uint32_t state_; ///< Lease state -}; -class CqlVersionExchange : public virtual CqlExchange { -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. - CqlVersionExchange() { - const size_t MAX_COLUMNS = 2U; - // Set the column names - size_t offset = 0U; - BOOST_STATIC_ASSERT(2U == MAX_COLUMNS); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("version", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("minor", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - BOOST_ASSERT(parameters_.size() == MAX_COLUMNS); - } +protected: + std::vector 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 Exchange CQL and Lease4 Data @@ -511,39 +438,45 @@ public: /// /// 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), client_id_length_(0), - client_id_null_(false) { - const size_t MAX_COLUMNS = 12U; - memset(client_id_buffer_, 0, sizeof(client_id_buffer_)); - + CqlLease4Exchange() : addr4_(0) { // Set the column names - size_t offset = 0U; - BOOST_STATIC_ASSERT(12U == MAX_COLUMNS); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("client_id", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("valid_lifetime", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("expire", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("subnet_id", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_fwd", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_rev", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hostname", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("state", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("[applied]", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL))); - BOOST_ASSERT(parameters_.size() == MAX_COLUMNS); + 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); } /// @brief Create CQL_BIND objects for Lease4 Pointer @@ -552,7 +485,7 @@ public: /// the database. void createBindForSend(const Lease4Ptr& lease, CqlDataArray& data) { if (!lease) { - isc_throw(BadValue, "createBindForSend:: Lease4 object is NULL"); + isc_throw(BadValue, "createBindForSend(): Lease4 object is NULL"); } // Store lease object to ensure it remains valid. lease_ = lease; @@ -563,24 +496,24 @@ public: // address: int // The address in the Lease structure is an IOAddress object. // Convert this to an integer for storage. - addr4_ = lease_->addr_.toUint32(); - data.add(&addr4_); + addr4_ = static_cast(lease_->addr_.toUint32()); + data.add(reinterpret_cast(&addr4_)); // hwaddr: blob - HWAddrPtr hwaddr = lease_->hwaddr_; - if (hwaddr) { - if (hwaddr->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) { - isc_throw(DbOperationError, "Hardware address length " << - lease_->hwaddr_->hwaddr_.size() << - " exceeds maximum allowed of " << - HWAddr::MAX_HWADDR_LEN); + 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_ = hwaddr->hwaddr_; + hwaddr_ = lease_->hwaddr_->hwaddr_; } else { hwaddr_.clear(); } - hwaddr_length_ = hwaddr_.size(); - data.add(&hwaddr_); + data.add(reinterpret_cast(&hwaddr_)); // client_id: blob if (lease_->client_id_) { @@ -588,174 +521,149 @@ public: } else { client_id_.clear(); } - client_id_length_ = client_id_.size(); - data.add(&client_id_); + data.add(reinterpret_cast(&client_id_)); // valid lifetime: bigint - valid_lifetime_ = lease_->valid_lft_; - data.add(&valid_lifetime_); + valid_lifetime_ = static_cast(lease_->valid_lft_); + data.add(reinterpret_cast(&valid_lifetime_)); // expire: bigint - // The lease structure holds the client last transmission time (cltt_) + // 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_ - CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, - lease_->valid_lft_, expire_); - data.add(&expire_); + CqlExchange::convertToDatabaseTime(lease_->cltt_, + lease_->valid_lft_, expire_); + data.add(reinterpret_cast(&expire_)); // subnet_id: int - // Can use lease_->subnet_id_ directly as it is of type uint32_t. - subnet_id_ = lease_->subnet_id_; - data.add(&subnet_id_); + subnet_id_ = static_cast(lease_->subnet_id_); + data.add(reinterpret_cast(&subnet_id_)); // fqdn_fwd: boolean - fqdn_fwd_ = lease_->fqdn_fwd_; - data.add(&fqdn_fwd_); + fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false; + data.add(reinterpret_cast(&fqdn_fwd_)); // fqdn_rev: boolean - fqdn_rev_ = lease_->fqdn_rev_; - data.add(&fqdn_rev_); + fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false; + data.add(reinterpret_cast(&fqdn_rev_)); // hostname: varchar - hostname_length_ = lease_->hostname_.length(); - if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << - lease_->hostname_.c_str()); - } - if (hostname_length_) { - memcpy(hostname_buffer_, lease_->hostname_.c_str(), - hostname_length_); + 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_buffer_[hostname_length_] = '\0'; - data.add(hostname_buffer_); + hostname_ = lease_->hostname_; + data.add(reinterpret_cast(&hostname_)); // state: int - state_ = lease_->state_; - data.add(&state_); - + state_ = static_cast(lease_->state_); + data.add(reinterpret_cast(&state_)); } catch (const std::exception& ex) { isc_throw(DbOperationError, - "Could not create bind array from Lease4: " - << lease_->addr_.toText() << ", reason: " << ex.what()); + "could not create bind array from Lease4: " + << lease_->addr_.toText() + << ", reason: " << ex.what()); } } /// @brief Create BIND array to receive data /// /// Creates a CQL_BIND array to receive Lease4 data from the database. - Lease4Ptr createBindForReceive(const CassRow* row) { - try { - unsigned char* hwaddr_buffer = NULL; - const char* client_id_buffer = NULL; - const char* hostname_buffer = NULL; - CqlDataArray data; - CqlDataArray size; - - // address: int - data.add(reinterpret_cast(&addr4_)); - size.add(NULL); - - // hwaddr: blob - data.add(reinterpret_cast(&hwaddr_buffer)); - size.add(reinterpret_cast(&hwaddr_length_)); + virtual void createBindForReceive(CqlDataArray& data, + const int /* statementIndex */ = -1) { + // address: int + data.add(reinterpret_cast(&addr4_)); - // client_id: blob - data.add(reinterpret_cast(&client_id_buffer)); - size.add(reinterpret_cast(&client_id_length_)); + // hwaddr: blob + data.add(reinterpret_cast(&hwaddr_)); - // valid_lifetime: bigint - data.add(reinterpret_cast(&valid_lifetime_)); - size.add(NULL); + // client_id: blob + data.add(reinterpret_cast(&client_id_)); - // expire: bigint - data.add(reinterpret_cast(&expire_)); - size.add(NULL); + // valid_lifetime: bigint + data.add(reinterpret_cast(&valid_lifetime_)); - // subnet_id: int - data.add(reinterpret_cast(&subnet_id_)); - size.add(NULL); + // expire: bigint + data.add(reinterpret_cast(&expire_)); - // fqdn_fwd: boolean - data.add(reinterpret_cast(&fqdn_fwd_)); - size.add(NULL); + // subnet_id: int + data.add(reinterpret_cast(&subnet_id_)); - // fqdn_rev: boolean - data.add(reinterpret_cast(&fqdn_rev_)); - size.add(NULL); + // fqdn_fwd: boolean + data.add(reinterpret_cast(&fqdn_fwd_)); - // hostname: varchar - data.add(reinterpret_cast(&hostname_buffer)); - size.add(reinterpret_cast(&hostname_length_)); - - // state: int - data.add(reinterpret_cast(&state_)); - size.add(NULL); + // fqdn_rev: boolean + data.add(reinterpret_cast(&fqdn_rev_)); - for (size_t i = 0; i < data.size(); i++) { - CqlLeaseMgr::getData(row, i, data, size, i, *this); - } + // hostname: varchar + data.add(reinterpret_cast(&hostname_)); - // hwaddr: blob - hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_); + // state: int + data.add(reinterpret_cast(&state_)); + } - // client_id: blob - client_id_.assign(client_id_buffer, client_id_buffer + - client_id_length_); - if (client_id_length_ >= sizeof(client_id_buffer_)) { - isc_throw(BadValue, "client id value is too large: " << - client_id_buffer); - } - if (client_id_length_) { - memcpy(client_id_buffer_, client_id_buffer, client_id_length_); + 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); } - client_id_buffer_[client_id_length_] = '\0'; - - // hostname: varchar - if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << - hostname_buffer); + 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_length_) { - memcpy(hostname_buffer_, hostname_buffer, hostname_length_); + if (hostname_.size() > HOSTNAME_MAX_LEN) { + isc_throw(BadValue, "hostname" + << hostname_ << " of length " + << hostname_.size() + << " exceeds maximum allowed length of " + << HOSTNAME_MAX_LEN); } - hostname_buffer_[hostname_length_] = '\0'; time_t cltt = 0; - CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, - cltt); + CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, + cltt); // Recreate the hardware address. HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER)); - std::string hostname(hostname_buffer_, - hostname_buffer_ + hostname_length_); - - Lease4Ptr result(new Lease4(addr4_, hwaddr, client_id_buffer_, - client_id_length_, valid_lifetime_, 0, + Lease4Ptr result(new Lease4(addr4_, hwaddr, client_id_.data(), + client_id_.size(), valid_lifetime_, 0, 0, cltt, subnet_id_, fqdn_fwd_, - fqdn_rev_, hostname)); + fqdn_rev_, hostname_)); result->state_ = state_; - return (result); + return result; } catch (const std::exception& ex) { isc_throw(DbOperationError, - "Could not convert data to Lease4, reason: " - << ex.what()); + "createBindForReceive(): " + "could not convert data to Lease4, reason: " + << ex.what()); } - return (Lease4Ptr()); + return Lease4Ptr(); + } + + void* retrieve() { + isc_throw(NotImplemented, "retrieve(): Not implemented yet."); } private: - Lease4Ptr lease_; ///< Pointer to lease object - uint32_t addr4_; ///< IPv4 address - std::vector client_id_; ///< Client identification - uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN]; - ///< Client ID buffer - unsigned long client_id_length_; ///< Client ID address length - bool client_id_null_; ///< Is Client ID null? + Lease4Ptr lease_; ///< Pointer to lease object + cass_int32_t addr4_; ///< IPv4 address + std::vector client_id_; ///< Client identification }; /// @brief Exchange CQL and Lease6 Data @@ -777,53 +685,65 @@ public: /// /// The initialization of the variables here is nonly to satisfy cppcheck - /// all variables are initialized/set in the methods before they are used. - CqlLease6Exchange() : addr6_length_(0), duid_length_(0), iaid_(0), - lease_type_(0), prefixlen_(0), pref_lifetime_(0), - hwaddr_null_(false), hwtype_(0), hwaddr_source_(0) { - const size_t MAX_COLUMNS = 18U; - memset(addr6_buffer_, 0, sizeof(addr6_buffer_)); - memset(duid_buffer_, 0, sizeof(duid_buffer_)); - + CqlLease6Exchange() + : pref_lifetime_(0), lease_type_(0), iaid_(0), prefixlen_(0), + hwtype_(0), hwaddr_source_(0) { // Set the column names - size_t offset = 0U; - BOOST_STATIC_ASSERT(18U == MAX_COLUMNS); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("address", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("duid", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("valid_lifetime", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("expire", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("subnet_id", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("pref_lifetime", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("lease_type", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("iaid", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("prefix_len", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_fwd", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("fqdn_rev", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hostname", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwtype", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("hwaddr_source", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("state", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("limit", - offset++, EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32))); - parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo("[applied]", - offset++, EXCHANGE_DATA_TYPE_IO_OUT, EXCHANGE_DATA_TYPE_BOOL))); - BOOST_ASSERT(parameters_.size() == MAX_COLUMNS); + 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); } /// @brief Create CQL_BIND objects for Lease6 Pointer @@ -832,7 +752,7 @@ public: /// the database. void createBindForSend(const Lease6Ptr& lease, CqlDataArray& data) { if (!lease) { - isc_throw(BadValue, "createBindForSend:: Lease6 object is NULL"); + isc_throw(BadValue, "createBindForSend(): Lease6 object is NULL"); } // Store lease object to ensure it remains valid. lease_ = lease; @@ -841,245 +761,216 @@ public: // structure. try { // address: varchar - std::string text_buffer = lease_->addr_.toText(); - addr6_length_ = text_buffer.size(); - if (addr6_length_ >= sizeof(addr6_buffer_)) { - isc_throw(BadValue, "address value is too large: " << - text_buffer); + 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); } - if (addr6_length_) { - memcpy(addr6_buffer_, text_buffer.c_str(), addr6_length_); - } - addr6_buffer_[addr6_length_] = '\0'; - data.add(addr6_buffer_); + data.add(reinterpret_cast(&addr6_)); // duid: blob if (!lease_->duid_) { - isc_throw(DbOperationError, "lease6 for address " << - addr6_buffer_ << " is missing mandatory client-id"); + isc_throw(DbOperationError, + "lease6 for address " + << addr6_ << " is missing mandatory duid"); } duid_ = lease_->duid_->getDuid(); - duid_length_ = duid_.size(); - data.add(&duid_); + data.add(reinterpret_cast(&duid_)); // valid lifetime: bigint - valid_lifetime_ = lease_->valid_lft_; - data.add(&valid_lifetime_); + valid_lifetime_ = static_cast(lease_->valid_lft_); + data.add(reinterpret_cast(&valid_lifetime_)); // expire: bigint - // The lease structure holds the client last transmission time (cltt_) + // 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_ - CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, - lease_->valid_lft_, expire_); - data.add(&expire_); + CqlExchange::convertToDatabaseTime(lease_->cltt_, + lease_->valid_lft_, expire_); + data.add(reinterpret_cast(&expire_)); // subnet_id: int - // Can use lease_->subnet_id_ directly as it is of type uint32_t. - subnet_id_ = lease_->subnet_id_; - data.add(&subnet_id_); + subnet_id_ = static_cast(lease_->subnet_id_); + data.add(reinterpret_cast(&subnet_id_)); // pref_lifetime: bigint - // Can use lease_->preferred_lft_ directly as it is of type uint32_t. - pref_lifetime_ = lease_->preferred_lft_; - data.add(&pref_lifetime_); + pref_lifetime_ = static_cast(lease_->preferred_lft_); + data.add(reinterpret_cast(&pref_lifetime_)); // lease_type: int - // Must convert to uint8_t as lease_->type_ is a LeaseType variable. - lease_type_ = lease_->type_; - data.add(&lease_type_); + lease_type_ = static_cast(lease_->type_); + data.add(reinterpret_cast(&lease_type_)); // iaid: int - // Can use lease_->iaid_ directly as it is of type uint32_t. - iaid_ = lease_->iaid_; - data.add(&iaid_); + iaid_ = static_cast(lease_->iaid_); + data.add(reinterpret_cast(&iaid_)); // prefix_len: int - // Can use lease_->prefixlen_ directly as it is uint32_t. - prefixlen_ = lease_->prefixlen_; - data.add(&prefixlen_); + prefixlen_ = static_cast(lease_->prefixlen_); + data.add(reinterpret_cast(&prefixlen_)); // fqdn_fwd: boolean - fqdn_fwd_ = lease_->fqdn_fwd_; - data.add(&fqdn_fwd_); + fqdn_fwd_ = lease_->fqdn_fwd_ ? cass_true : cass_false; + data.add(reinterpret_cast(&fqdn_fwd_)); // fqdn_rev: boolean - fqdn_rev_ = lease_->fqdn_rev_; - data.add(&fqdn_rev_); + fqdn_rev_ = lease_->fqdn_rev_ ? cass_true : cass_false; + data.add(reinterpret_cast(&fqdn_rev_)); // hostname: varchar - hostname_length_ = lease_->hostname_.length(); - if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << - lease_->hostname_.c_str()); - } - if (hostname_length_) { - memcpy(hostname_buffer_, lease_->hostname_.c_str(), - hostname_length_); + 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_buffer_[hostname_length_] = '\0'; - data.add(hostname_buffer_); + hostname_ = lease_->hostname_; + data.add(reinterpret_cast(&hostname_)); // hwaddr: blob - HWAddrPtr hwaddr = lease_->hwaddr_; - if (hwaddr) { - if (hwaddr->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) { - isc_throw(DbOperationError, "Hardware address length : " - << lease_->hwaddr_->hwaddr_.size() - << " exceeds maximum allowed of: " - << HWAddr::MAX_HWADDR_LEN); + 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_ = hwaddr->hwaddr_; + hwaddr_ = lease_->hwaddr_->hwaddr_; } else { hwaddr_.clear(); } - hwaddr_length_ = hwaddr_.size(); - data.add(&hwaddr_); + data.add(reinterpret_cast(&hwaddr_)); // hwtype: int - if (hwaddr) { - hwtype_ = lease->hwaddr_->htype_; + if (lease_->hwaddr_) { + hwtype_ = static_cast(lease_->hwaddr_->htype_); } else { hwtype_ = 0; } - data.add(&hwtype_); + data.add(reinterpret_cast(&hwtype_)); // hwaddr_source: int - if (hwaddr) { - hwaddr_source_ = lease->hwaddr_->source_; + if (lease_->hwaddr_) { + hwaddr_source_ = + static_cast(lease_->hwaddr_->source_); } else { hwaddr_source_ = 0; } - data.add(&hwaddr_source_); + data.add(reinterpret_cast(&hwaddr_source_)); // state: int - state_ = lease_->state_; - data.add(&state_); - + state_ = static_cast(lease_->state_); + data.add(reinterpret_cast(&state_)); } catch (const std::exception& ex) { isc_throw(DbOperationError, - "Could not create bind array from Lease6: " - << lease_->addr_.toText() << ", reason: " << ex.what()); + "createBindForSend(): " + "could not create bind array from Lease6: " + << lease_->addr_.toText() + << ", reason: " << ex.what()); } } /// @brief Create BIND array to receive data /// /// Creates a CQL_BIND array to receive Lease6 data from the database. - Lease6Ptr createBindForReceive(const CassRow* row) { - try { - unsigned char* duid_buffer = NULL; - unsigned char* hwaddr_buffer = NULL; - const char* address_buffer = NULL; - const char* hostname_buffer = NULL; - CqlDataArray data; - CqlDataArray size; + void createBindForReceive(CqlDataArray& data, + const int /* statementIndex */ = -1) { + // address: varchar + data.add(reinterpret_cast(&addr6_)); - // address: varchar - data.add(reinterpret_cast(&address_buffer)); - size.add(reinterpret_cast(&addr6_length_)); + // duid: blob + data.add(reinterpret_cast(&duid_)); - // duid: blob - data.add(reinterpret_cast(&duid_buffer)); - size.add(reinterpret_cast(&duid_length_)); + // valid_lifetime_: bigint + data.add(reinterpret_cast(&valid_lifetime_)); - // valid_lifetime_: bigint - data.add(reinterpret_cast(&valid_lifetime_)); - size.add(NULL); + // expire: bigint + data.add(reinterpret_cast(&expire_)); - // expire: bigint - data.add(reinterpret_cast(&expire_)); - size.add(NULL); + // subnet_id: int + data.add(reinterpret_cast(&subnet_id_)); - // subnet_id: int - data.add(reinterpret_cast(&subnet_id_)); - size.add(NULL); + // pref_lifetime: bigint + data.add(reinterpret_cast(&pref_lifetime_)); - // pref_lifetime: bigint - data.add(reinterpret_cast(&pref_lifetime_)); - size.add(NULL); + // lease_type: int + data.add(reinterpret_cast(&lease_type_)); - // lease_type: int - data.add(reinterpret_cast(&lease_type_)); - size.add(NULL); - - // iaid: int - data.add(reinterpret_cast(&iaid_)); - size.add(NULL); - - // prefix_len: int - data.add(reinterpret_cast(&prefixlen_)); - size.add(NULL); + // iaid: int + data.add(reinterpret_cast(&iaid_)); - // fqdn_fwd: boolean - data.add(reinterpret_cast(&fqdn_fwd_)); - size.add(NULL); + // prefix_len: int + data.add(reinterpret_cast(&prefixlen_)); - // fqdn_rev: boolean - data.add(reinterpret_cast(&fqdn_rev_)); - size.add(NULL); + // fqdn_fwd: boolean + data.add(reinterpret_cast(&fqdn_fwd_)); - // hostname: varchar - data.add(reinterpret_cast(&hostname_buffer)); - size.add(reinterpret_cast(&hostname_length_)); + // fqdn_rev: boolean + data.add(reinterpret_cast(&fqdn_rev_)); - // hwaddr: blob - data.add(reinterpret_cast(&hwaddr_buffer)); - size.add(reinterpret_cast(&hwaddr_length_)); + // hostname: varchar + data.add(reinterpret_cast(&hostname_)); - // hwtype: int - data.add(reinterpret_cast(&hwtype_)); - size.add(NULL); + // hwaddr: blob + data.add(reinterpret_cast(&hwaddr_)); - // hwaddr_source: int - data.add(reinterpret_cast(&hwaddr_source_)); - size.add(NULL); + // hwtype: int + data.add(reinterpret_cast(&hwtype_)); - // state: int - data.add(reinterpret_cast(&state_)); - size.add(NULL); + // hwaddr_source: int + data.add(reinterpret_cast(&hwaddr_source_)); - for (size_t i = 0; i < data.size(); i++) { - CqlLeaseMgr::getData(row, i, data, size, i, *this); - } + // state: int + data.add(reinterpret_cast(&state_)); + } - // address: varchar - if (addr6_length_ >= sizeof(addr6_buffer_)) { - isc_throw(BadValue, "address value is too large: " << - address_buffer); + 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 (addr6_length_) { - memcpy(addr6_buffer_, address_buffer, addr6_length_); + 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); } - addr6_buffer_[addr6_length_] = '\0'; - - // duid: blob - duid_.assign(duid_buffer, duid_buffer + duid_length_); - - // hostname: varchar - if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << - hostname_buffer); + 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_length_) { - memcpy(hostname_buffer_, hostname_buffer, hostname_length_); + if (hostname_.size() > HOSTNAME_MAX_LEN) { + isc_throw(BadValue, "hostname " + << hostname_ << " of length " + << hostname_.size() + << " exceeds maximum allowed length of " + << HOSTNAME_MAX_LEN); } - hostname_buffer_[hostname_length_] = '\0'; - - // hwaddr: blob - hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_); - - if (lease_type_ != Lease::TYPE_NA && lease_type_ != Lease::TYPE_TA && - lease_type_ != Lease::TYPE_PD) { - isc_throw(BadValue, "invalid lease type returned (" << - static_cast(lease_type_) << ") for lease with " - << "address " << addr6_buffer_ << ". Only 0, 1, or 2 are " - << "allowed"); + 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); } - isc::asiolink::IOAddress addr(addr6_buffer_); + isc::asiolink::IOAddress addr(addr6_); DuidPtr duid(new DUID(duid_)); HWAddrPtr hwaddr; if (hwaddr_.size()) { @@ -1087,53 +978,50 @@ public: hwaddr->source_ = hwaddr_source_; } - std::string hostname(hostname_buffer_, - hostname_buffer_ + hostname_length_); - // Create the lease and set the cltt (after converting from the // expire time retrieved from the database). - Lease6Ptr result(new Lease6(static_cast(lease_type_), - addr, duid, iaid_, pref_lifetime_, - valid_lifetime_, 0, 0, subnet_id_, - fqdn_fwd_, fqdn_rev_, hostname, hwaddr, - prefixlen_)); + Lease6Ptr result(new Lease6( + static_cast(lease_type_), addr, duid, iaid_, + pref_lifetime_, valid_lifetime_, 0, 0, subnet_id_, fqdn_fwd_, + fqdn_rev_, hostname_, hwaddr, prefixlen_)); time_t cltt = 0; - CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, - cltt); + CqlExchange::convertFromDatabaseTime(expire_, valid_lifetime_, + cltt); result->cltt_ = cltt; result->state_ = state_; - - return (result); + return result; } catch (const std::exception& ex) { isc_throw(DbOperationError, - "Could not convert data to Lease4, reason: " - << ex.what()); + "createBindForReceive(): " + "could not convert data to Lease4, reason: " + << ex.what()); } - return (Lease6Ptr()); + return Lease6Ptr(); + } + + void* retrieve() { + isc_throw(NotImplemented, "retrieve(): Not implemented yet."); } private: - Lease6Ptr lease_; ///< Pointer to lease object - char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1]; ///< Character - ///< array form of V6 address - unsigned long addr6_length_; ///< Length of the address - std::vector duid_; ///< Client identification - uint8_t duid_buffer_[DUID::MAX_DUID_LEN]; ///< Buffer form of DUID - unsigned long duid_length_; ///< Length of the DUID - uint32_t iaid_; ///< Identity association ID - uint32_t lease_type_; ///< Lease type - uint32_t prefixlen_; ///< Prefix length - uint32_t pref_lifetime_; ///< Preferred lifetime - bool hwaddr_null_; ///< Used when HWAddr is null - uint32_t hwtype_; ///< Hardware type - uint32_t hwaddr_source_; ///< Source of the hardware address + Lease6Ptr lease_; ///< Pointer to lease object + std::string addr6_; ///< IPv6 address + std::vector 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 }; CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) : LeaseMgr(), dbconn_(parameters), exchange4_(new CqlLease4Exchange()), - exchange6_(new CqlLease6Exchange()), versionExchange_(new CqlVersionExchange()) { + exchange6_(new CqlLease6Exchange()), + versionExchange_(new CqlVersionExchange()) { dbconn_.openDatabase(); dbconn_.prepareStatements(CqlLeaseMgr::tagged_statements_); } @@ -1148,81 +1036,45 @@ CqlLeaseMgr::getDBVersion() { std::stringstream tmp; tmp << "CQL backend " << CQL_SCHEMA_VERSION_MAJOR; tmp << "." << CQL_SCHEMA_VERSION_MINOR; - tmp << ", library " << "cassandra_static"; - return (tmp.str()); -} - -ExchangeDataType -CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, - const SqlExchange& exchange) { - if (CqlLeaseMgr::tagged_statements_[stindex].params_ && - CqlLeaseMgr::tagged_statements_[stindex].params_[pindex]) { - const ExchangeColumnInfoContainerName& idx = exchange.parameters_.get<1>(); - const ExchangeColumnInfoContainerNameRange& range = - idx.equal_range(CqlLeaseMgr::tagged_statements_[stindex].params_[pindex]); - if (std::distance(range.first, range.second) > 0) { - return (*range.first)->type_; - } - } - return EXCHANGE_DATA_TYPE_NONE; -} - -void -CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, - CqlDataArray& data, const SqlExchange& exchange) { - if (CqlLeaseMgr::tagged_statements_[stindex].params_ == NULL) { - return; - } - for (int i = 0; CqlLeaseMgr::tagged_statements_[stindex].params_[i]; i++) { - ExchangeDataType type = CqlLeaseMgr::getDataType(stindex, i, exchange); - if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) { - isc_throw(BadValue, "index " << stindex << " out of bounds"); - } - CqlFunctions[type].sqlBindFunction_(statement, i, data.values_[i]); - } -} - -void -CqlLeaseMgr::getData(const CassRow* row, const int pindex, CqlDataArray& data, - CqlDataArray& size, const int dindex, const SqlExchange& exchange) { - 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 (NULL == value) { - isc_throw(BadValue, "column name " << name << " doesn't exist"); - } - if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) { - isc_throw(BadValue, "index " << type << " out of bounds"); - } - CqlFunctions[type].sqlGetFunction_(value, data.values_[dindex], - reinterpret_cast(size.values_[dindex])); - } + tmp << ", library cassandra_static"; + return tmp.str(); } bool CqlLeaseMgr::addLeaseCommon(StatementIndex stindex, - CqlDataArray& data, CqlLeaseExchange& exchange) { + CqlDataArray& data, + CqlLeaseExchange& exchange) { CassError rc; CassStatement* statement = NULL; CassFuture* future = NULL; statement = cass_prepared_bind(dbconn_.statements_[stindex]); - if (NULL == statement) { - isc_throw(DbOperationError, "unable to bind statement"); + if (!statement) { + isc_throw(DbOperationError, + "unable to bind statement " + << dbconn_.tagged_statements_[stindex].name_); } - CqlLeaseMgr::bindData(statement, stindex, data, exchange); + 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 (NULL == future) { + if (!future) { cass_statement_free(statement); - isc_throw(DbOperationError, "unable to execute statement"); + isc_throw(DbOperationError, + "unable to execute statement " + << dbconn_.tagged_statements_[stindex].name_); } cass_future_wait(future); std::string error; @@ -1235,78 +1087,81 @@ CqlLeaseMgr::addLeaseCommon(StatementIndex stindex, } // Check if statement has been applied. - const CassResult* resultCollection = cass_future_get_result(future); - CassIterator* rows = cass_iterator_from_result(resultCollection); - CqlDataArray appliedData; - CqlDataArray appliedSize; - bool applied = false; - while (cass_iterator_next(rows)) { - const CassRow* row = cass_iterator_get_row(rows); - // [applied]: bool - appliedData.add(reinterpret_cast(&applied)); - appliedSize.add(NULL); - CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData, - appliedSize, 0, exchange); - } + bool applied = exchange.hasStatementBeenApplied(future); // Free resources. - cass_iterator_free(rows); - cass_result_free(resultCollection); cass_future_free(future); cass_statement_free(statement); - return applied; } bool CqlLeaseMgr::addLease(const Lease4Ptr& lease) { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_ADD_ADDR4).arg(lease->addr_.toText()); + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR4) + .arg(lease->addr_.toText()); CqlDataArray data; exchange4_->createBindForSend(lease, data); - return (addLeaseCommon(INSERT_LEASE4, data, *exchange4_)); + return addLeaseCommon(INSERT_LEASE4, data, *exchange4_); } bool CqlLeaseMgr::addLease(const Lease6Ptr& lease) { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_ADD_ADDR6).arg(lease->addr_.toText()); + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ADD_ADDR6) + .arg(lease->addr_.toText()); CqlDataArray data; exchange6_->createBindForSend(lease, data); - return (addLeaseCommon(INSERT_LEASE6, data, *exchange6_)); + return addLeaseCommon(INSERT_LEASE6, data, *exchange6_); } template -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_); +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_); CassError rc; CassStatement* statement = NULL; CassFuture* future = NULL; - const CqlLeaseExchange& leaseExchange = static_cast(*exchange); statement = cass_prepared_bind(dbconn_.statements_[stindex]); - if (NULL == statement) { + if (!statement) { isc_throw(DbOperationError, "unable to bind statement"); } - CqlLeaseMgr::bindData(statement, stindex, data, leaseExchange); + 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, + static_cast(*exchange), + CqlLeaseMgr::tagged_statements_); future = cass_session_execute(dbconn_.session_, statement); - if (NULL == future) { + if (!future) { cass_statement_free(statement); - isc_throw(DbOperationError, "unable to execute statement"); + isc_throw(DbOperationError, + "unable to execute statement " + << dbconn_.tagged_statements_[stindex].name_); } cass_future_wait(future); std::string error; - dbconn_.checkStatementError(error, future, "unable to GET"); + 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); @@ -1324,7 +1179,13 @@ void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex, break; } const CassRow* row = cass_iterator_get_row(rows); - result.push_back(exchange->createBindForReceive(row)); + 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()); } cass_iterator_free(rows); @@ -1332,15 +1193,17 @@ void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex, cass_future_free(future); cass_statement_free(statement); if (single && rowCount > 1) { - isc_throw(MultipleRecords, "multiple records were found in the " - "database where only one was expected for query " + isc_throw(MultipleRecords, + "multiple records were found in the " + "database where only one was expected for statement " << dbconn_.tagged_statements_[stindex].name_); } } void -CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data, - Lease4Ptr& result) const { +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 @@ -1358,8 +1221,9 @@ CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data, } void -CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data, - Lease6Ptr& result) const { +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 @@ -1381,246 +1245,260 @@ CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data, Lease4Ptr CqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_ADDR4).arg(addr.toText()); + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_ADDR4) + .arg(addr.toText()); // Set up the WHERE clause value CqlDataArray data; - uint32_t addr4_data = addr.toUint32(); - data.add(&addr4_data); + cass_int32_t addr4_data = static_cast(addr.toUint32()); + data.add(reinterpret_cast(&addr4_data)); // Get the data Lease4Ptr result; getLease(GET_LEASE4_ADDR, data, result); - return (result); + return result; } Lease4Collection CqlLeaseMgr::getLease4(const HWAddr& hwaddr) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_HWADDR).arg(hwaddr.toText()); + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_HWADDR) + .arg(hwaddr.toText()); // Set up the WHERE clause value CqlDataArray data; - std::vectorhwaddr_data = hwaddr.hwaddr_; - data.add(&hwaddr_data); + std::vector hwaddr_data = hwaddr.hwaddr_; + data.add(reinterpret_cast(&hwaddr_data)); // Get the data Lease4Collection result; getLeaseCollection(GET_LEASE4_HWADDR, data, result); - return (result); + return result; } Lease4Ptr 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()); + .arg(subnet_id) + .arg(hwaddr.toText()); // Set up the WHERE clause value CqlDataArray data; - std::vectorhwaddr_data = hwaddr.hwaddr_; - data.add(&hwaddr_data); + std::vector hwaddr_data = hwaddr.hwaddr_; + data.add(reinterpret_cast(&hwaddr_data)); - uint32_t subnet_id_data = subnet_id; - data.add(&subnet_id_data); + cass_int32_t subnet_id_data = static_cast(subnet_id); + data.add(reinterpret_cast(&subnet_id_data)); // Get the data Lease4Ptr result; getLease(GET_LEASE4_HWADDR_SUBID, data, result); - return (result); + return result; } Lease4Collection CqlLeaseMgr::getLease4(const ClientId& clientid) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_CLIENTID).arg(clientid.toText()); + DHCPSRV_CQL_GET_CLIENTID) + .arg(clientid.toText()); // Set up the WHERE clause value CqlDataArray data; - std::vector client_id_data = clientid.getClientId(); - data.add(&client_id_data); + std::vector client_id_data = clientid.getClientId(); + data.add(reinterpret_cast(&client_id_data)); // Get the data Lease4Collection result; getLeaseCollection(GET_LEASE4_CLIENTID, data, result); - return (result); + return result; } Lease4Ptr -CqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr, - SubnetID subnet_id) const { - /// This function is currently not implemented because allocation engine +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. /// It never uses both parameters in the same time. We need to - /// consider if this function is needed at all. + /// consider if this method is needed at all. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_CLIENTID_HWADDR_SUBID).arg(clientid.toText()) - .arg(hwaddr.toText()).arg(subnet_id); + DHCPSRV_CQL_GET_CLIENTID_HWADDR_SUBID) + .arg(clientid.toText()) + .arg(hwaddr.toText()) + .arg(subnet_id); - isc_throw(NotImplemented, "The CqlLeaseMgr::getLease4 function was" - " called, but it is not implemented"); + isc_throw(NotImplemented, "The CqlLeaseMgr::getLease4 method was" + " called, but it is not implemented"); } Lease4Ptr 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()); + .arg(subnet_id) + .arg(clientid.toText()); // Set up the WHERE clause value CqlDataArray data; std::vector client_id_data = clientid.getClientId(); - data.add(&client_id_data); + data.add(reinterpret_cast(&client_id_data)); - uint32_t subnet_id_data = subnet_id; - data.add(&subnet_id_data); + cass_int32_t subnet_id_data = static_cast(subnet_id); + data.add(reinterpret_cast(&subnet_id_data)); // Get the data Lease4Ptr result; getLease(GET_LEASE4_CLIENTID_SUBID, data, result); - return (result); + return result; } Lease6Ptr CqlLeaseMgr::getLease6(Lease::Type lease_type, - const isc::asiolink::IOAddress& addr) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_ADDR6).arg(addr.toText()) - .arg(lease_type); + const isc::asiolink::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; - std::string text_buffer = addr.toText(); - uint32_t addr6_length = text_buffer.size(); - char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1]; - if (addr6_length >= sizeof(addr6_buffer)) { - isc_throw(BadValue, "address value is too large: " << text_buffer); - } - if (addr6_length) { - memcpy(addr6_buffer, text_buffer.c_str(), addr6_length); + 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); } - addr6_buffer[addr6_length] = '\0'; - data.add(addr6_buffer); + data.add(reinterpret_cast(&addr_data)); - uint32_t lease_type_data = lease_type; - data.add(&lease_type_data); + cass_int32_t lease_type_data = static_cast(lease_type); + data.add(reinterpret_cast(&lease_type_data)); Lease6Ptr result; getLease(GET_LEASE6_ADDR, data, result); - return (result); + return result; } Lease6Collection CqlLeaseMgr::getLeases6(Lease::Type lease_type, - const DUID& duid, uint32_t iaid) const { + const DUID& duid, + uint32_t iaid) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_GET_IAID_DUID).arg(iaid).arg(duid.toText()) - .arg(lease_type); + DHCPSRV_CQL_GET_IAID_DUID) + .arg(iaid) + .arg(duid.toText()) + .arg(lease_type); // Set up the WHERE clause value CqlDataArray data; - std::vector duid_data = duid.getDuid(); - data.add(&duid_data); + std::vector duid_data = duid.getDuid(); + data.add(reinterpret_cast(&duid_data)); - uint32_t iaid_data = iaid; - data.add(&iaid_data); + cass_int32_t iaid_data = static_cast(iaid); + data.add(reinterpret_cast(&iaid_data)); - uint32_t lease_type_data = lease_type; - data.add(&lease_type_data); + cass_int32_t lease_type_data = static_cast(lease_type); + data.add(reinterpret_cast(&lease_type_data)); // ... and get the data Lease6Collection result; getLeaseCollection(GET_LEASE6_DUID_IAID, data, result); - return (result); + return result; } Lease6Collection CqlLeaseMgr::getLeases6(Lease::Type lease_type, - const DUID& duid, uint32_t iaid, - SubnetID subnet_id) const { + const DUID& duid, + uint32_t iaid, + SubnetID subnet_id) const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_IAID_SUBID_DUID) - .arg(iaid).arg(subnet_id).arg(duid.toText()) - .arg(lease_type); + .arg(iaid) + .arg(subnet_id) + .arg(duid.toText()) + .arg(lease_type); // Set up the WHERE clause value CqlDataArray data; - std::vector duid_data = duid.getDuid(); - data.add(&duid_data); + std::vector duid_data = duid.getDuid(); + data.add(reinterpret_cast(&duid_data)); - uint32_t iaid_data = iaid; - data.add(&iaid_data); + cass_int32_t iaid_data = static_cast(iaid); + data.add(reinterpret_cast(&iaid_data)); - uint32_t subnet_id_data = subnet_id; - data.add(&subnet_id_data); + cass_int32_t subnet_id_data = static_cast(subnet_id); + data.add(reinterpret_cast(&subnet_id_data)); - uint32_t lease_type_data = lease_type; - data.add(&lease_type_data); + cass_int32_t lease_type_data = static_cast(lease_type); + data.add(reinterpret_cast(&lease_type_data)); // ... and get the data Lease6Collection result; getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, data, result); - return (result); + return result; } void CqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases, - const size_t max_leases) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_EXPIRED6) + const size_t max_leases) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_EXPIRED6) .arg(max_leases); getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE); } void CqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, - const size_t max_leases) const { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_EXPIRED4) + const size_t max_leases) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_CQL_GET_EXPIRED4) .arg(max_leases); getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE); } -template +template void CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases, - const size_t max_leases, - StatementIndex statement_index) const { + const size_t max_leases, + StatementIndex statement_index) const { // Set up the WHERE clause value - uint32_t keepState = Lease::STATE_EXPIRED_RECLAIMED; - uint64_t timestamp = static_cast(time(NULL)); + cass_int32_t keepState = Lease::STATE_EXPIRED_RECLAIMED; + cass_int64_t timestamp = static_cast(time(NULL)); // If the number of leases is 0, we will return all leases. This is // achieved by setting the limit to a very high value. - uint32_t limit = max_leases > 0 ? static_cast(max_leases) : - std::numeric_limits::max(); + cass_int32_t limit = max_leases > 0 ? + static_cast(max_leases) : + std::numeric_limits::max(); - for (uint32_t state = Lease::STATE_DEFAULT; - state <= Lease::STATE_EXPIRED_RECLAIMED; state++) { + for (cass_int32_t state = Lease::STATE_DEFAULT; + state <= Lease::STATE_EXPIRED_RECLAIMED; state++) { if (state == keepState) { continue; } LeaseCollection tempCollection; CqlDataArray data; - data.add(&state); - data.add(×tamp); - data.add(&limit); + data.add(reinterpret_cast(&state)); + data.add(reinterpret_cast(×tamp)); + data.add(reinterpret_cast(&limit)); // Retrieve leases from the database. getLeaseCollection(statement_index, data, tempCollection); @@ -1628,35 +1506,48 @@ CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases, typedef typename LeaseCollection::iterator LeaseCollectionIt; for (LeaseCollectionIt it = tempCollection.begin(); - it != tempCollection.end(); ++it) { + it != tempCollection.end(); ++it) { expired_leases.push_back((*it)); } } } -template void CqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, - CqlDataArray& data, - const LeasePtr&, CqlLeaseExchange& exchange) { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_ADD_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_); + 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 (NULL == statement) { + if (!statement) { isc_throw(DbOperationError, "unable to bind statement"); } - CqlLeaseMgr::bindData(statement, stindex, data, exchange); + 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 (NULL == future) { + if (!future) { cass_statement_free(statement); - isc_throw(DbOperationError, "unable to execute statement"); + isc_throw(DbOperationError, + "unable to execute statement " + << dbconn_.tagged_statements_[stindex].name_); } cass_future_wait(future); std::string error; @@ -1669,23 +1560,12 @@ CqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, } // Check if statement has been applied. - const CassResult* resultCollection = cass_future_get_result(future); - CassIterator* rows = cass_iterator_from_result(resultCollection); - CqlDataArray appliedData; - CqlDataArray appliedSize; - bool applied = false; - while (cass_iterator_next(rows)) { - const CassRow* row = cass_iterator_get_row(rows); - // [applied]: bool - appliedData.add(reinterpret_cast(&applied)); - appliedSize.add(NULL); - CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData, - appliedSize, 0, exchange); - } + size_t row_count; + size_t column_count; + bool applied = + exchange.hasStatementBeenApplied(future, &row_count, &column_count); // Free resources. - cass_iterator_free(rows); - cass_result_free(resultCollection); cass_future_free(future); cass_statement_free(statement); @@ -1699,27 +1579,30 @@ CqlLeaseMgr::updateLease4(const Lease4Ptr& lease) { const StatementIndex stindex = UPDATE_LEASE4; LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_UPDATE_ADDR4).arg(lease->addr_.toText()); + 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 SQL_BIND array - uint32_t addr4_data = lease->addr_.toUint32(); - data.add(&addr4_data); + // Set up the WHERE clause and append it to the bind array. + cass_int32_t addr4_data = + static_cast(lease->addr_.toUint32()); + data.add(reinterpret_cast(&addr4_data)); // Drop to common update code - updateLeaseCommon(stindex, data, lease, *exchange4_); + updateLeaseCommon(stindex, data, *exchange4_); } void CqlLeaseMgr::updateLease6(const Lease6Ptr& lease) { const StatementIndex stindex = UPDATE_LEASE6; - + std::string lease_addr_data = lease->addr_.toText(); LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_UPDATE_ADDR6).arg(lease->addr_.toText()); + DHCPSRV_CQL_UPDATE_ADDR6) + .arg(lease_addr_data); // Create the BIND array for the data being updated CqlDataArray data; @@ -1727,40 +1610,54 @@ CqlLeaseMgr::updateLease6(const Lease6Ptr& lease) { data.remove(0); // Set up the WHERE clause and append it to the BIND array - std::string text_buffer = lease->addr_.toText(); - uint32_t addr6_length = text_buffer.size(); - char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1]; - if (addr6_length >= sizeof(addr6_buffer)) { - isc_throw(BadValue, "address value is too large: " << text_buffer); + if (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); } - if (addr6_length) { - memcpy(addr6_buffer, text_buffer.c_str(), addr6_length); - } - addr6_buffer[addr6_length] = '\0'; - data.add(addr6_buffer); + data.add(reinterpret_cast(&lease_addr_data)); // Drop to common update code - updateLeaseCommon(stindex, data, lease, *exchange6_); + updateLeaseCommon(stindex, data, *exchange6_); } bool CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, - CqlDataArray& data, CqlLeaseExchange& exchange) { + CqlDataArray& data, + CqlLeaseExchange& exchange) { CassError rc; CassStatement* statement = NULL; CassFuture* future = NULL; statement = cass_prepared_bind(dbconn_.statements_[stindex]); - if (NULL == statement) { + if (!statement) { isc_throw(DbOperationError, "unable to bind statement"); } - CqlLeaseMgr::bindData(statement, stindex, data, exchange); + 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 (NULL == future) { + if (!future) { cass_statement_free(statement); - isc_throw(DbOperationError, "unable to execute statement"); + isc_throw(DbOperationError, + "unable to execute statement " + << dbconn_.tagged_statements_[stindex].name_); } cass_future_wait(future); std::string error; @@ -1773,23 +1670,9 @@ CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, } // Check if statement has been applied. - const CassResult* resultCollection = cass_future_get_result(future); - CassIterator* rows = cass_iterator_from_result(resultCollection); - CqlDataArray appliedData; - CqlDataArray appliedSize; - bool applied = false; - while (cass_iterator_next(rows)) { - const CassRow* row = cass_iterator_get_row(rows); - // [applied]: bool - appliedData.add(reinterpret_cast(&applied)); - appliedSize.add(NULL); - CqlLeaseMgr::getData(row, exchange.parameters_.size() - 1, appliedData, - appliedSize, 0, exchange); - } + bool applied = exchange.hasStatementBeenApplied(future); // Free resources. - cass_iterator_free(rows); - cass_result_free(resultCollection); cass_future_free(future); cass_statement_free(statement); @@ -1798,29 +1681,29 @@ CqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, bool CqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) { - LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, - DHCPSRV_CQL_DELETE_ADDR).arg(addr.toText()); + 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; if (addr.isV4()) { - uint32_t addr4_data = addr.toUint32(); - data.add(&addr4_data); - return (deleteLeaseCommon(DELETE_LEASE4, data, *exchange4_)); + cass_int32_t addr4_data = static_cast(addr.toUint32()); + data.add(reinterpret_cast(&addr4_data)); + return deleteLeaseCommon(DELETE_LEASE4, data, *exchange4_); } else { - std::string text_buffer = addr.toText(); - uint32_t addr6_length = text_buffer.size(); - char addr6_buffer[ADDRESS6_TEXT_MAX_LEN + 1]; - if (addr6_length >= sizeof(addr6_buffer)) { - isc_throw(BadValue, "address value is too large: " << text_buffer); - } - if (addr6_length) { - memcpy(addr6_buffer, text_buffer.c_str(), addr6_length); + 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); } - addr6_buffer[addr6_length] = '\0'; - data.add(addr6_buffer); - return (deleteLeaseCommon(DELETE_LEASE6, data, *exchange6_)); + data.add(reinterpret_cast(&addr_data)); + return deleteLeaseCommon(DELETE_LEASE6, data, *exchange6_); } } @@ -1829,7 +1712,8 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED4) .arg(secs); - return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED)); + return deleteExpiredReclaimedLeasesCommon(secs, + DELETE_LEASE4_STATE_EXPIRED); } uint64_t @@ -1837,25 +1721,27 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_DELETE_EXPIRED_RECLAIMED6) .arg(secs); - return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED)); + return deleteExpiredReclaimedLeasesCommon(secs, + DELETE_LEASE6_STATE_EXPIRED); } uint64_t -CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, - StatementIndex statement_index) { +CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon( + const uint32_t secs, StatementIndex statement_index) { // Set up the WHERE clause value CqlDataArray data; - uint32_t result = 0; + uint64_t result = 0; // State is reclaimed. - uint32_t state = Lease::STATE_EXPIRED_RECLAIMED; - data.add(&state); + cass_int32_t state = + static_cast(Lease::STATE_EXPIRED_RECLAIMED); + data.add(reinterpret_cast(&state)); // Expiration timestamp. - uint64_t expiration = static_cast(time(NULL) - - static_cast(secs)); - data.add(&expiration); + cass_int64_t expiration = + static_cast(time(NULL) - static_cast(secs)); + data.add(reinterpret_cast(&expiration)); // Get the data Lease4Collection result4Leases; @@ -1871,18 +1757,18 @@ CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, break; } for (Lease4Collection::iterator it = result4Leases.begin(); - it != result4Leases.end(); ++it) { + it != result4Leases.end(); ++it) { if (deleteLease((*it)->addr_)) { result++; } } for (Lease6Collection::iterator it = result6Leases.begin(); - it != result6Leases.end(); ++it) { + it != result6Leases.end(); ++it) { if (deleteLease((*it)->addr_)) { result++; } } - return (result); + return result; } std::string @@ -1893,7 +1779,7 @@ CqlLeaseMgr::getName() const { } catch (...) { // Return an empty name } - return (name); + return name; } std::string @@ -1901,29 +1787,42 @@ CqlLeaseMgr::getDescription() const { return std::string("Cassandra Database"); } -pair +std::pair CqlLeaseMgr::getVersion() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_VERSION); - uint32_t version; - uint32_t minor; + 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 (NULL == statement) { + 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 (NULL == future) { + if (!future) { cass_statement_free(statement); - isc_throw(DbOperationError, "unable to execute 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"); + dbconn_.checkStatementError(error, future, "unable to GET version"); rc = cass_future_error_code(future); if (rc != CASS_OK) { cass_future_free(future); @@ -1935,17 +1834,14 @@ CqlLeaseMgr::getVersion() const { const CassResult* resultCollection = cass_future_get_result(future); CassIterator* rows = cass_iterator_from_result(resultCollection); CqlDataArray data; - CqlDataArray size; while (cass_iterator_next(rows)) { const CassRow* row = cass_iterator_get_row(rows); - // version: uint32_t + // version: int data.add(reinterpret_cast(&version)); - size.add(NULL); - // minor: uint32_t + // minor: int data.add(reinterpret_cast(&minor)); - size.add(NULL); - for (size_t i = 0; i < data.size(); i++) { - CqlLeaseMgr::getData(row, i, data, size, i, *versionExchange_); + for (size_t i = 0U; i < data.size(); i++) { + CqlCommon::getData(row, i, i, *versionExchange_, data); } } @@ -1954,18 +1850,21 @@ CqlLeaseMgr::getVersion() const { cass_future_free(future); cass_statement_free(statement); - return (make_pair(version, minor)); + return std::pair( + static_cast(version), static_cast(minor)); } void CqlLeaseMgr::commit() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT); + dbconn_.commit(); } void CqlLeaseMgr::rollback() { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK); + dbconn_.rollback(); } -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/cql_lease_mgr.h b/src/lib/dhcpsrv/cql_lease_mgr.h index 76f96c859e..8fcf5c8d96 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.h +++ b/src/lib/dhcpsrv/cql_lease_mgr.h @@ -17,91 +17,35 @@ #ifndef CQL_LEASE_MGR_H #define CQL_LEASE_MGR_H +#include + #include -#include #include +#include +#include + #include #include -#include + +#include +#include #include namespace isc { namespace dhcp { -/// @brief Structure used to bind C++ input values to dynamic SQL parameters -/// The structure contains a vector which store the input values, -/// This vector is passed directly into the -/// CQL execute call. -/// -/// Note that the data values are stored as pointers. These pointers need -/// to be valid for the duration of the CQL statement execution. In other -/// words populating them with pointers to values that go out of scope -/// before statement is executed is a bad idea. -struct CqlDataArray { - /// Add void pointer to a vector of pointers to the data values. - std::vector values_; - void add(void* value) { - values_.push_back(value); - } - /// Remove void pointer from a vector of pointers to the data values. - 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); - } - /// Get size. - size_t size() { - return values_.size(); - } -}; - class CqlVersionExchange; class CqlLeaseExchange; class CqlLease4Exchange; class CqlLease6Exchange; -/// @brief Cassandra Exchange -/// -/// This class provides the basic conversion functions between Cassandra CQL and -/// base types. -class CqlExchange : public virtual SqlExchange { -public: - // Time conversion methods. - static void - convertToDatabaseTime(const time_t& cltt, - const uint32_t& valid_lifetime, - uint64_t& expire) { - // Calculate expiry time. Store it in the 64-bit value so as we can - // detect overflows. - int64_t expire_time = static_cast(cltt) + - static_cast(valid_lifetime); - - if (expire_time > DatabaseConnection::MAX_DB_TIME) { - isc_throw(BadValue, "Time value is too large: " << expire_time); - } - - expire = expire_time; - } - - static void - convertFromDatabaseTime(const uint64_t& expire, - const uint32_t& valid_lifetime, - time_t& cltt) { - // Convert to local time - cltt = expire - static_cast(valid_lifetime); - } -}; - /// @brief Cassandra Lease Manager /// /// This class provides the \ref isc::dhcp::LeaseMgr interface to the Cassandra -/// database. Use of this backend presupposes that a CQL database is available +/// database. Use of this backend implies that a CQL database is available /// and that the Kea schema has been created within it. class CqlLeaseMgr : public LeaseMgr { public: - /// @brief Constructor /// /// Uses the following keywords in the parameters passed to it to @@ -115,7 +59,7 @@ public: /// schema_version table will be checked against hard-coded value in /// the implementation file. /// - /// Finally, all the SQL commands are pre-compiled. + /// Finally, all the CQL commands are pre-compiled. /// /// @param parameters A data structure relating keywords and values /// concerned with the database. @@ -124,7 +68,7 @@ public: /// @throw isc::dhcp::DbOpenError Error opening the database /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters); + explicit CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters); /// @brief Destructor (closes database) virtual ~CqlLeaseMgr(); @@ -225,9 +169,8 @@ public: /// @param subnet_id A subnet identifier. /// /// @return A pointer to the lease or NULL if the lease is not found. - /// @throw isc::NotImplemented On every call as this function is currently - /// not implemented for the PostgreSQL backend. - virtual Lease4Ptr getLease4(const ClientId& client_id, const HWAddr& hwaddr, + virtual Lease4Ptr getLease4(const ClientId& client_id, + const HWAddr& hwaddr, SubnetID subnet_id) const; /// @brief Returns existing IPv4 lease for specified client-id @@ -280,8 +223,8 @@ public: /// 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; /// @brief Returns existing IPv6 lease for a given DUID+IA combination /// @@ -296,8 +239,10 @@ public: /// lease type field. /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - virtual Lease6Collection getLeases6(Lease::Type type, const DUID& duid, - uint32_t iaid, SubnetID subnet_id) const; + virtual Lease6Collection getLeases6(Lease::Type type, + const DUID& duid, + uint32_t iaid, + SubnetID subnet_id) const; /// @brief Returns a collection of expired DHCPv6 leases. /// /// This method returns at most @c max_leases expired leases. The leases @@ -308,8 +253,8 @@ public: /// by the database backend are added. /// @param max_leases A maximum number of leases to be returned. If this /// value is set to 0, all expired (but not reclaimed) leases are returned. - virtual void getExpiredLeases6(Lease6Collection& , - const size_t ) const; + virtual void getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const; /// @brief Returns a collection of expired DHCPv4 leases. /// @@ -321,8 +266,8 @@ public: /// by the database backend are added. /// @param max_leases A maximum number of leases to be returned. If this /// value is set to 0, all expired (but not reclaimed) leases are returned. - virtual void getExpiredLeases4(Lease4Collection& , - const size_t ) const; + virtual void getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const; /// @brief Updates IPv4 lease. /// @@ -368,7 +313,7 @@ public: /// time will not be deleted. /// /// @return Number of leases deleted. - virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t ); + virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs); /// @brief Deletes all expired and reclaimed DHCPv6 leases. /// @@ -377,7 +322,7 @@ public: /// time will not be deleted. /// /// @return Number of leases deleted. - virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t ); + virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs); /// @brief Return backend type /// @@ -405,7 +350,7 @@ public: /// /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - virtual std::pair getVersion() const; + virtual std::pair getVersion() const; /// @brief Commit Transactions /// @@ -419,70 +364,35 @@ public: /// @brief Statement Tags /// - /// The contents of the enum are indexes into the list of compiled SQL + /// 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 + 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 }; - /// @brief Binds data specified in data - /// - /// This method 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 - static void bindData(CassStatement* statement, const StatementIndex stindex, - CqlDataArray& data, const SqlExchange& exchange); - - /// @brief Returns type of data for specific parameter. - /// - /// Returns type of a given parameter of a given statement. - /// - /// @param stindex Index of statement being executed. - /// @param pindex Index of the parameter for a given statement. - /// @param exchange Exchange object to use - static ExchangeDataType getDataType(const StatementIndex stindex, int pindex, - const SqlExchange& exchange); - - /// @brief Retrieves data returned by Cassandra. - /// - /// This method calls one of cass_value_get_{none,bool,int32,int64,string,bytes}. - /// It is used to retrieve data returned by Cassandra into standard C++ types - /// used by Kea. - /// - /// @param row row of data returned by CQL library - /// @param pindex Index of statement being executed - /// @param data array that has been created for the type of lease in question. - /// @param size a structure that holds information about size - /// @param dindex data index (specifies which entry in size array is used) - /// @param exchange Exchange object to use - static void getData(const CassRow* row, const int pindex, CqlDataArray& data, - CqlDataArray& size, const int dindex, const SqlExchange& exchange); - private: - /// @brief Add Lease Common Code /// /// This method performs the common actions for both flavours (V4 and V6) @@ -499,8 +409,9 @@ private: /// /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - bool addLeaseCommon(StatementIndex stindex, CqlDataArray& data, - CqlLeaseExchange& exchange); + bool addLeaseCommon(StatementIndex stindex, + CqlDataArray& data, + CqlLeaseExchange& exchange); /// @brief Get Lease Collection Common Code /// @@ -523,8 +434,10 @@ private: /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved /// from the database where only one was expected. template - void getLeaseCollection(StatementIndex stindex, CqlDataArray& data_array, - Exchange& exchange, LeaseCollection& result, + void getLeaseCollection(StatementIndex stindex, + CqlDataArray& data_array, + Exchange& exchange, + LeaseCollection& result, bool single = false) const; /// @brief Gets Lease4 Collection @@ -543,7 +456,8 @@ private: /// failed. /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved /// from the database where only one was expected. - void getLeaseCollection(StatementIndex stindex, CqlDataArray& data, + void getLeaseCollection(StatementIndex stindex, + CqlDataArray& data, Lease4Collection& result) const { getLeaseCollection(stindex, data, exchange4_, result); } @@ -563,7 +477,8 @@ private: /// failed. /// @throw isc::dhcp::MultipleRecords Multiple records were retrieved /// from the database where only one was expected. - void getLeaseCollection(StatementIndex stindex, CqlDataArray& data, + void getLeaseCollection(StatementIndex stindex, + CqlDataArray& data, Lease6Collection& result) const { getLeaseCollection(stindex, data, exchange6_, result); } @@ -577,7 +492,8 @@ private: /// @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, + void getLease(StatementIndex stindex, + CqlDataArray& data, Lease4Ptr& result) const; /// @brief Get Lease6 Common Code @@ -589,7 +505,8 @@ private: /// @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, + void getLease(StatementIndex stindex, + CqlDataArray& data, Lease6Ptr& result) const; /// @brief Get expired leases common code. @@ -606,7 +523,7 @@ private: /// @c GET_LEASE6_EXPIRE. /// /// @tparam One of the @c Lease4Collection or @c Lease6Collection. - template + template void getExpiredLeasesCommon(LeaseCollection& expired_leases, const size_t max_leases, StatementIndex statement_index) const; @@ -626,9 +543,9 @@ private: /// the address given. /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - template - void updateLeaseCommon(StatementIndex stindex, CqlDataArray& data, - const LeasePtr& lease, CqlLeaseExchange& exchange); + void updateLeaseCommon(StatementIndex stindex, + CqlDataArray& data, + CqlLeaseExchange& exchange); /// @brief Delete lease common code /// @@ -644,8 +561,9 @@ private: /// /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - bool deleteLeaseCommon(StatementIndex stindex, CqlDataArray& data, - CqlLeaseExchange& exchange); + bool deleteLeaseCommon(StatementIndex stindex, + CqlDataArray& data, + CqlLeaseExchange& exchange); /// @brief Delete expired-reclaimed leases. /// @@ -659,21 +577,27 @@ private: uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIndex statement_index); - /// CQL queries used by CQL backend + /// @brief CQL queries used by CQL backend static CqlTaggedStatement tagged_statements_[]; - /// Database connection object - CqlConnection dbconn_; + /// @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".) - boost::scoped_ptr exchange4_; ///< Exchange object - boost::scoped_ptr exchange6_; ///< Exchange object - boost::scoped_ptr versionExchange_; ///< Exchange object + /// @brief Exchange object for IPv4 + boost::scoped_ptr exchange4_; + /// @brief Exchange object for IPv4 + boost::scoped_ptr exchange6_; + /// @brief Exchange object for version + boost::scoped_ptr versionExchange_; + //// @} }; -}; // end of isc::dhcp namespace -}; // end of isc namespace +} // namespace dhcp +} // namespace isc -#endif // CQL_LEASE_MGR_H +#endif // CQL_LEASE_MGR_H diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes index 9fa51bd16d..8bb91008d8 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.mes +++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes @@ -175,12 +175,22 @@ with the specified address to the Cassandra backend 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_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 +and error freeing CQL database resources as part of closing its connection to +the Cassandra database. The connection is closed as part of normal server +shutdown. This error is most likely a programmatic issue that is highly +unlikely to occur or negatively impact server operation. + % 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. @@ -709,7 +719,7 @@ with the specified address to the PostgreSQL backend database. A debug message issued when the server is about to add an IPv6 lease with the specified address to the PostgreSQL backend database. -% DHCPSRV_PGSQL_COMMIT committing to MySQL database +% DHCPSRV_PGSQL_COMMIT committing to PostgreSQL database The code has issued a commit call. All outstanding transactions will be committed to the database. Note that depending on the PostgreSQL settings, the committal may not include a write to disk. diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index a07cd47648..8361813ad5 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -60,93 +61,6 @@ namespace isc { namespace dhcp { -/// @brief Used to map server data types with internal backend storage data -/// types. -enum ExchangeDataType { - EXCHANGE_DATA_TYPE_NONE, - EXCHANGE_DATA_TYPE_BOOL, - EXCHANGE_DATA_TYPE_INT32, - EXCHANGE_DATA_TYPE_INT64, - EXCHANGE_DATA_TYPE_TIMESTAMP, - EXCHANGE_DATA_TYPE_STRING, - EXCHANGE_DATA_TYPE_BYTES -}; - -/// @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), - 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_; -}; - -typedef boost::shared_ptr ExchangeColumnInfoPtr; - -typedef boost::multi_index_container< - // Container comprises elements of ExchangeColumnInfoPtr type. - ExchangeColumnInfoPtr, - // Here we start enumerating various indexes. - boost::multi_index::indexed_by< - // Sequenced index allows accessing elements in the same way as elements - // in std::list. - // Sequenced is an index #0. - boost::multi_index::sequenced<>, - // Start definition of index #1. - boost::multi_index::hashed_non_unique< - boost::multi_index::member< - ExchangeColumnInfo, - std::string, - &ExchangeColumnInfo::name_ - > - >, - // Start definition of index #2. - boost::multi_index::hashed_non_unique< - boost::multi_index::member< - ExchangeColumnInfo, - uint32_t, - &ExchangeColumnInfo::index_ - > - > - > -> ExchangeColumnInfoContainer; - -/// Pointer to the ExchangeColumnInfoContainer object. -typedef boost::shared_ptr ExchangeColumnInfoContainerPtr; -/// Type of the index #1 - name. -typedef ExchangeColumnInfoContainer::nth_index<1>::type ExchangeColumnInfoContainerName; -/// 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 ExchangeColumnInfoContainerNameRange; -/// Type of the index #2 - index. -typedef ExchangeColumnInfoContainer::nth_index<2>::type ExchangeColumnInfoContainerIndex; -/// 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 ExchangeColumnInfoContainerIndexRange; - -class SqlExchange { -public: - SqlExchange () {}; - virtual ~SqlExchange() {}; - ExchangeColumnInfoContainer parameters_; ///< Column names and types -}; - /// @brief Contains a single row of lease statistical data /// /// The contents of the row consist of a subnet ID, a lease diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc index 6b7fa51eec..329d0c50e5 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc @@ -53,7 +53,8 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) { // 2. Update the copy with the passed keywords. BOOST_FOREACH(ConfigPair param, config_value->mapValue()) { try { - if ((param.first == "persist") || (param.first == "readonly")) { + if ((param.first == "persist") || (param.first == "readonly") || + (param.first == "tcp-nodelay")) { values_copy[param.first] = (param.second->boolValue() ? "true" : "false"); @@ -67,6 +68,20 @@ DbAccessParser::build(isc::data::ConstElementPtr config_value) { values_copy[param.first] = boost::lexical_cast(timeout); + } else if (param.first == "reconnect-wait-time") { + timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast(timeout); + + } else if (param.first == "request-timeout") { + timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast(timeout); + + } else if (param.first == "tcp-keepalive") { + timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast(timeout); } else { values_copy[param.first] = param.second->stringValue(); } diff --git a/src/lib/dhcpsrv/sql_common.h b/src/lib/dhcpsrv/sql_common.h new file mode 100644 index 0000000000..bc23dbf2ca --- /dev/null +++ b/src/lib/dhcpsrv/sql_common.h @@ -0,0 +1,152 @@ +// Copyright (C) 2016 Deutsche Telekom AG. +// +// Author: Cristian Secăreanu +// +// 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 SQL_COMMON_H +#define SQL_COMMON_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { + +/// @brief Used to map server data types with internal backend storage data +/// types. +enum ExchangeDataType { + EXCHANGE_DATA_TYPE_NONE, + EXCHANGE_DATA_TYPE_BOOL, + EXCHANGE_DATA_TYPE_INT32, + EXCHANGE_DATA_TYPE_INT64, + EXCHANGE_DATA_TYPE_TIMESTAMP, + EXCHANGE_DATA_TYPE_STRING, + EXCHANGE_DATA_TYPE_BYTES, + EXCHANGE_DATA_TYPE_UUID +}; + +/// @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 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 + 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 + 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 + ExchangeColumnInfoContainerIndexRange; + +/// @brief Base class for backend exchanges. +class SqlExchange { +public: + /// @brief Constructor + SqlExchange() { + } + + /// @brief Destructor + virtual ~SqlExchange() { + } + + /// @brief Column names and types + ExchangeColumnInfoContainer parameters_; +}; + +} // namespace dhcp +} // namespace isc + +#endif // SQL_COMMON_H diff --git a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc index baa2f7190f..fa2edfa3a8 100644 --- a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc @@ -352,6 +352,31 @@ TEST(CqlOpenTest, OpenDatabase) { INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), InvalidType); + // Check that invalid login data does not cause an exception, CQL should use + // default values. + EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString( + CQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD))); + EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString( + CQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD))); + EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString( + CQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD))); + EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString( + CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD))); + + // Check that invalid timeouts throw DbOperationError. + EXPECT_THROW(LeaseMgrFactory::create(connectionString( + CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, + VALID_PASSWORD, INVALID_TIMEOUT_1)), + DbOperationError); + EXPECT_THROW(LeaseMgrFactory::create(connectionString( + CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, + VALID_PASSWORD, INVALID_TIMEOUT_2)), + DbOperationError); + + // Check that CQL allows the hostname to not be specified. + EXPECT_NO_THROW(LeaseMgrFactory::create(connectionString( + CQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD))); + // Tidy up after the test destroyCqlSchema(false, true); } @@ -375,8 +400,8 @@ TEST_F(CqlLeaseMgrTest, getType) { /// This test checks that the conversion is correct. TEST_F(CqlLeaseMgrTest, checkTimeConversion) { const time_t cltt = time(NULL); - const uint32_t valid_lft = 86400; // 1 day - uint64_t cql_expire; + const cass_int64_t valid_lft = 86400; // 1 day + cass_int64_t cql_expire; // Convert to the database time CqlExchange::convertToDatabaseTime(cltt, valid_lft, cql_expire); @@ -643,4 +668,4 @@ TEST_F(CqlLeaseMgrTest, deleteExpiredReclaimedLeases4) { testDeleteExpiredReclaimedLeases4(); } -}; // Of anonymous namespace +} // namespace diff --git a/src/share/database/scripts/cql/dhcpdb_create.cql b/src/share/database/scripts/cql/dhcpdb_create.cql index eda5fe2fc9..82f47fc8dd 100644 --- a/src/share/database/scripts/cql/dhcpdb_create.cql +++ b/src/share/database/scripts/cql/dhcpdb_create.cql @@ -41,6 +41,9 @@ -- This line starts database initialization to 1.0. -- Holds the IPv4 leases. +-- ----------------------------------------------------- +-- Table `lease4` +-- ----------------------------------------------------- CREATE TABLE lease4 ( address int, hwaddr blob, @@ -65,6 +68,9 @@ CREATE INDEX lease4index5 ON lease4 (state); -- Holds the IPv6 leases. -- N.B. The use of a VARCHAR for the address is temporary for development: -- it will eventually be replaced by BINARY(16). +-- ----------------------------------------------------- +-- Table `lease6` +-- ----------------------------------------------------- CREATE TABLE lease6 ( address varchar, duid blob, @@ -98,6 +104,9 @@ CREATE INDEX lease6index6 ON lease6 (state); -- 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) +-- ----------------------------------------------------- +-- Table `lease6_types` +-- ----------------------------------------------------- CREATE TABLE lease6_types ( lease_type int, -- Lease type code. name varchar, -- Name of the lease type @@ -113,6 +122,9 @@ INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD'); -- Prefix del -- 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. +-- ----------------------------------------------------- +-- Table `lease_hwaddr_source` +-- ----------------------------------------------------- CREATE TABLE lease_hwaddr_source ( hwaddr_source int, name varchar, @@ -140,47 +152,12 @@ INSERT INTO lease_hwaddr_source (hwaddr_source, name) VALUES (32, 'HWADDR_SOURCE -- 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. +-- ----------------------------------------------------- +-- Table `lease_state` +-- ----------------------------------------------------- CREATE TABLE lease_state ( state int, name varchar, @@ -196,9 +173,15 @@ INSERT INTO lease_state (state, name) VALUES (2, 'expired-reclaimed'); -- 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". +-- ----------------------------------------------------- +-- Table `schema_version` +-- ----------------------------------------------------- CREATE TABLE schema_version ( version int, minor int, PRIMARY KEY (version) ); + INSERT INTO schema_version (version, minor) VALUES (1, 0); + +-- This line concludes database initalization to version 1.0. diff --git a/src/share/database/scripts/cql/dhcpdb_drop.cql b/src/share/database/scripts/cql/dhcpdb_drop.cql index 7ec86fadc3..aa0631937a 100644 --- a/src/share/database/scripts/cql/dhcpdb_drop.cql +++ b/src/share/database/scripts/cql/dhcpdb_drop.cql @@ -25,3 +25,16 @@ 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 INDEX IF EXISTS lease4index1; +DROP INDEX IF EXISTS lease4index2; +DROP INDEX IF EXISTS lease4index3; +DROP INDEX IF EXISTS lease4index4; +DROP INDEX IF EXISTS lease4index5; + +DROP INDEX IF EXISTS lease6index1; +DROP INDEX IF EXISTS lease6index2; +DROP INDEX IF EXISTS lease6index3; +DROP INDEX IF EXISTS lease6index4; +DROP INDEX IF EXISTS lease6index5; +DROP INDEX IF EXISTS lease6index6;