From d47b8dfe5a4d697a02362bfd797aa21d78368091 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Thu, 23 Jun 2016 14:30:29 +0200 Subject: [PATCH] change requests from isc # Conflicts: # src/bin/admin/tests/data/pgsql.lease6_dump_test.reference.csv --- src/bin/admin/admin-utils.sh | 19 + src/bin/admin/kea-admin.in | 35 +- src/bin/admin/tests/cql_tests.sh.in | 162 ++++++- .../data/cql.lease4_dump_test.reference.csv | 8 +- .../data/cql.lease6_dump_test.reference.csv | 8 +- src/bin/admin/tests/dhcpdb_create_1.0.cql | 3 +- src/bin/admin/tests/dhcpdb_create_1.0.mysql | 13 +- src/bin/admin/tests/dhcpdb_create_1.0.pgsql | 7 +- src/bin/admin/tests/pgsql_tests.sh.in | 2 +- src/lib/dhcpsrv/cql_connection.cc | 31 +- src/lib/dhcpsrv/cql_connection.h | 39 +- src/lib/dhcpsrv/cql_lease_mgr.cc | 411 ++++++++++++------ src/lib/dhcpsrv/cql_lease_mgr.h | 92 ++-- src/lib/dhcpsrv/lease_mgr.h | 5 + .../dhcpsrv/tests/cql_lease_mgr_unittest.cc | 25 +- src/lib/dhcpsrv/testutils/cql_schema.h | 12 +- .../database/scripts/cql/dhcpdb_create.cql | 9 - .../scripts/mysql/dhcpdb_create.mysql | 5 - .../scripts/pgsql/dhcpdb_create.pgsql | 5 - 19 files changed, 606 insertions(+), 285 deletions(-) diff --git a/src/bin/admin/admin-utils.sh b/src/bin/admin/admin-utils.sh index d6f33a4d30..5881d2b30f 100755 --- a/src/bin/admin/admin-utils.sh +++ b/src/bin/admin/admin-utils.sh @@ -106,6 +106,25 @@ cql_execute() { return $retcode } +cql_execute_script() { + file=$1 + shift + if [ $# -gt 1 ]; then + cqlsh $* -e "$file" + retcode=$? + else + cqlsh -u $db_user -p $db_password -k $db_name -f "$file" + retcode=$? + fi + + if [ $retcode -ne 0 ]; then + printf "cqlsh returned with exit status $retcode\n" + exit $retcode + fi + + return $retcode +} + 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="."` diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in index 5fa3d70b76..6c5bd1cfe3 100644 --- a/src/bin/admin/kea-admin.in +++ b/src/bin/admin/kea-admin.in @@ -208,7 +208,8 @@ cql_init() { 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 - printf "Tables are already created.\n" + log_error "Expected empty database $db_name, but the following tables are present \n$result. Aborting." + exit 2 fi version=`cql_version` @@ -485,44 +486,38 @@ cql_dump() { # Fetch the correct SQL text. Note this function will exit # if it fails. - get_dump_query $version + + select_where_clause="" + if [ $dump_type -eq 4 ]; then + dump_qry="SELECT address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state FROM keatest.lease4" + select_where_clause=" WHERE address = 0" # invalid address + elif [ $dump_type -eq 6 ]; then + dump_qry="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 keatest.lease6" + select_where_clause=" WHERE address = ::" # invalid address + fi # Make sure they specified a file if [ "$dump_file" = "" ]; then log_error "you must specify an output file for lease-dump" usage exit 1 - fi # If output file exists, notify user, allow them a chance to bail check_file_overwrite $dump_file - # Check the temp file too - tmp_file="$dump_file.tmp" - check_file_overwrite $tmp_file - - # Run the sql to output tab-delimited lease data to a temp file. - # By using a temp file we can check for MySQL errors before using - # 'tr' to translate tabs to commas. We do not use MySQL's output - # to file as that requires linux superuser privileges to execute - # the select. - cql_execute "${dump_qry}" > $tmp_file - retcode=$? - if [ $retcode -ne 0 ]; then + cql_execute "${dump_qry}${select_where_clause}" | head -n 2 | tail -n 1 | sed -e 's/\s*//g' | sed -e 's/|/,/g' > $dump_file + if [ $? -ne 0 ]; then log_error "lease-dump: cql_execute failed, exit code $retcode"; exit 1 fi - # Now translate tabs to commas. - cat $tmp_file | tr '\t' ',' >$dump_file + cql_execute "${dump_qry}" | tail -n +4 | head -n -2 | sed -e 's/\s*//g' | sed -e 's/|/,/g' | sort -r >> $dump_file if [ $? -ne 0 ]; then - log_error "lease-dump: reformatting failed"; + log_error "lease-dump: cql_execute failed, exit code $retcode"; exit 1 fi - # delete the tmp file on success - rm $tmp_file echo lease$dump_type successfully dumped to $dump_file exit 0 } diff --git a/src/bin/admin/tests/cql_tests.sh.in b/src/bin/admin/tests/cql_tests.sh.in index fc5d5b5634..c3b906f68b 100644 --- a/src/bin/admin/tests/cql_tests.sh.in +++ b/src/bin/admin/tests/cql_tests.sh.in @@ -22,7 +22,7 @@ db_name="keatest" # Set location of the kea-admin. keaadmin=@abs_top_builddir@/src/bin/admin/kea-admin -cql_init_test() { +cql_lease_init_test() { test_start "cql.init" # Wipe the database. @@ -56,7 +56,7 @@ cql_init_test() { cql_execute "SELECT state, name FROM lease_state;" assert_eq 0 $? "lease_state table check failed, expected exit code: %d, actual: %d" - # Trying to create it again should fail. This verifies the db present + # Trying to create it again should fail. This verifies the db present # check echo "" echo "Making sure keyspace creation fails the second time..." @@ -70,18 +70,18 @@ cql_init_test() { test_finish 0 } -cql_version_test() { +cql_lease_version_test() { test_start "cql.version" # Wipe the database. cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql assert_eq 0 $? "drop table query failed, exit code %d, expected %d" - # Create the database + # Create the database. $keaadmin lease-init cql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir assert_eq 0 $? "kea-admin lease-init cql failed, expected exit code: %d, actual: %d" - # Verfiy that kea-admin lease-version returns the correct version + # 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" @@ -113,6 +113,154 @@ cql_upgrade_test() { test_finish 0 } -cql_init_test -cql_version_test +cql_lease4_dump_test() { + test_start "cql.lease4_dump_test" + + test_dir="@abs_top_srcdir@/src/bin/admin/tests" + output_dir="@abs_top_builddir@/src/bin/admin/tests" + script_dir="@abs_top_srcdir@/src/bin/admin/scripts" + + output_file="$output_dir/data/cql.lease4_dump_test.output.csv" + tmp_file="$output_file.tmp" + + ref_file="$test_dir/data/cql.lease4_dump_test.reference.csv" + + # Wipe out any residuals from prior failed runs. + if [ -e $output_file ] + then + rm $output_file + fi + + if [ -e $tmp_file ] + then + rm $tmp_file + fi + + # Wipe the database. + cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql + assert_eq 0 $? "drop table query failed, exit code %d, expected %d" + + # Create the database + $keaadmin lease-init cql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir + assert_eq 0 $? "kea-admin lease-init cql failed, expected exit code: %d, actual: %d" + + # Insert the reference record. + # -1073741302 corresponds to 192.0.2.10 + # -1073741301 corresponds to 192.0.2.11 + # -1073741300 corresponds to 192.0.2.12 + # 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,\ + fqdn_fwd, fqdn_rev, hostname, state)\ + 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,\ + 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,\ + fqdn_fwd, fqdn_rev, hostname, state)\ + values(-1073741300,textAsBlob('22'),NULL,40,1436173267,50,true,true,\ + 'three.example.com', 2);" + + cql_execute "$insert_sql" + assert_eq 0 $? "insert into lease4 failed, expected exit code %d, actual %d" + + # Dump lease4 to output_file. + $keaadmin lease-dump cql -4 -u $db_user -p $db_password -n $db_name -d $db_scripts_dir -o $output_file + assert_eq 0 $? "kea-admin lease-dump -4 failed, expected exit code %d, actual %d" + + # Compare the dump output to reference file, they should be identical. + cmp -s $output_file $ref_file + assert_eq 0 $? "dump file does not match reference file, expected exit code %d, actual %d" + + # remove the output file. + rm $output_file + + # Wipe the database. + cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql + assert_eq 0 $? "drop table query failed, exit code %d, expected %d" + + test_finish 0 +} + +cql_lease6_dump_test() { + test_start "cql.lease6_dump_test" + + test_dir="@abs_top_srcdir@/src/bin/admin/tests" + output_dir="@abs_top_builddir@/src/bin/admin/tests" + script_dir="@abs_top_srcdir@/src/bin/admin/scripts" + + output_file="$output_dir/data/cql.lease6_dump_test.output.csv" + tmp_file="$output_file.tmp" + + ref_file="$test_dir/data/cql.lease6_dump_test.reference.csv" + + # Wipe out any residuals from prior failed runs. + if [ -e $output_file ] + then + rm $output_file + fi + + if [ -e $tmp_file ] + then + rm $tmp_file + fi + + # Wipe the database. + cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql + assert_eq 0 $? "drop table query failed, exit code %d, expected %d" + + # Create the database. + $keaadmin lease-init cql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir + assert_eq 0 $? "could not create database, expected exit code %d, actual %d" + + # Insert the reference record. + # 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,\ + 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,\ + 'one.example.com',textAsBlob('80'),90,16,0);\ +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,\ + '',textAsBlob('80'),90,1,1);\ +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,\ + 'three.example.com',textAsBlob('80'),90,4,2);" + + cql_execute "$insert_sql" + assert_eq 0 $? "insert into lease6 failed, expected exit code %d, actual %d" + + # Dump lease4 to output_file. + $keaadmin lease-dump cql -6 -u $db_user -p $db_password -n $db_name -d $db_scripts_dir -o $output_file + assert_eq 0 $? "kea-admin lease-dump -6 failed, status code %d" + + # Compare the dump output to reference file, they should be identical. + cmp -s $output_file $ref_file + assert_eq 0 $? "dump file does not match reference file, expected exit code %d, actual %d" + + # remove the output file. + rm $output_file + + # Wipe the database. + cql_execute_script $db_scripts_dir/cql/dhcpdb_drop.cql + assert_eq 0 $? "drop table query failed, exit code %d, expected %d" + + test_finish 0 +} + +# Run tests. +cql_lease_init_test +cql_lease_version_test cql_upgrade_test +cql_lease4_dump_test +cql_lease6_dump_test diff --git a/src/bin/admin/tests/data/cql.lease4_dump_test.reference.csv b/src/bin/admin/tests/data/cql.lease4_dump_test.reference.csv index a2917b7d9a..5316f1e734 100644 --- a/src/bin/admin/tests/data/cql.lease4_dump_test.reference.csv +++ b/src/bin/admin/tests/data/cql.lease4_dump_test.reference.csv @@ -1,4 +1,4 @@ -address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname -0.0.0.10,3230,3330,40,2015-01-01 01:15:30,50,1,1,one.example.com -0.0.0.11,,313233,40,2015-02-02 02:30:45,50,1,1, -0.0.0.12,3232,,40,2015-03-03 11:01:07,50,1,1,three.example.com +address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state +-1073741302,0x3230,0x3330,40,1430694930,50,True,True,one.example.com,0 +-1073741301,null,0x313233,40,1433464245,50,True,True,,1 +-1073741300,0x3232,null,40,1436173267,50,True,True,three.example.com,2 diff --git a/src/bin/admin/tests/data/cql.lease6_dump_test.reference.csv b/src/bin/admin/tests/data/cql.lease6_dump_test.reference.csv index 31a6c3f42a..63a966d137 100644 --- a/src/bin/admin/tests/data/cql.lease6_dump_test.reference.csv +++ b/src/bin/admin/tests/data/cql.lease6_dump_test.reference.csv @@ -1,4 +1,4 @@ -address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source -10,3230,30,2015-04-04 01:15:30,40,50,IA_TA,60,70,1,1,one.example.com,3830,90,100 -11,,30,2015-05-05 02:30:45,40,50,IA_TA,60,70,1,1,,3830,90,100 -12,3231,30,2015-06-06 11:01:07,40,50,IA_TA,60,70,1,1,three.example.com,3830,90,100 +address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state +2001:db8::12,0x3231,30,1436173267,40,50,1,60,70,True,True,three.example.com,0x3830,90,4,2 +2001:db8::11,null,30,1433464245,40,50,1,60,70,True,True,,0x3830,90,1,1 +2001:db8::10,0x3230,30,1430694930,40,50,1,60,70,True,True,one.example.com,0x3830,90,16,0 diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.cql b/src/bin/admin/tests/dhcpdb_create_1.0.cql index 06c594f9bd..e015a260f3 100644 --- a/src/bin/admin/tests/dhcpdb_create_1.0.cql +++ b/src/bin/admin/tests/dhcpdb_create_1.0.cql @@ -197,7 +197,8 @@ 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. CREATE TABLE schema_version ( version int, minor int, diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.mysql b/src/bin/admin/tests/dhcpdb_create_1.0.mysql index f48cb50466..03e8af45c0 100644 --- a/src/bin/admin/tests/dhcpdb_create_1.0.mysql +++ b/src/bin/admin/tests/dhcpdb_create_1.0.mysql @@ -36,7 +36,7 @@ CREATE TABLE lease4 ( ) ENGINE = INNODB; -# Create search indexes for lease4 table +# Create search indexes for lease4 table. # index by hwaddr and subnet_id CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id); @@ -63,8 +63,8 @@ CREATE TABLE lease6 ( ) ENGINE = INNODB; -# Create search indexes for lease4 table -# index by iaid, subnet_id, and duid +# Create search indexes for lease4 table. +# index by iaid, subnet_id, and duid CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid); # ... and a definition of lease6 types. This table is a convenience for @@ -86,11 +86,8 @@ COMMIT; # This table is only modified during schema upgrades. For historical reasons # (related to the names of the columns in the BIND 10 DNS database file), the # first column is called "version" and not "major". -# -# NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h, -# which defines the schema for the unit tests. If you are updating -# the version number, the schema has changed: please ensure that -# schema_copy.h has been updated as well. +# Note: This MUST be kept in step with src/share/database/scripts/mysql/dhcpdb_create.mysql, +# which defines the schema for the unit tests. CREATE TABLE schema_version ( version INT PRIMARY KEY NOT NULL, # Major version number minor INT # Minor version number diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.pgsql b/src/bin/admin/tests/dhcpdb_create_1.0.pgsql index 1155b60d14..90eaacf226 100644 --- a/src/bin/admin/tests/dhcpdb_create_1.0.pgsql +++ b/src/bin/admin/tests/dhcpdb_create_1.0.pgsql @@ -83,11 +83,8 @@ COMMIT; -- This table is only modified during schema upgrades. For historical reasons -- (related to the names of the columns in the BIND 10 DNS database file), the -- first column is called "version" and not "major". - --- NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h, --- which defines the schema for the unit tests. If you are updating --- the version number, the schema has changed: please ensure that --- schema_copy.h has been updated as well. +-- Note: This MUST be kept in step with src/share/database/scripts/pgsql/dhcpdb_create.pgsql, +-- which defines the schema for the unit tests. CREATE TABLE schema_version ( version INT PRIMARY KEY NOT NULL, -- Major version number minor INT -- Minor version number diff --git a/src/bin/admin/tests/pgsql_tests.sh.in b/src/bin/admin/tests/pgsql_tests.sh.in index cabd483dc3..ee842b7993 100644 --- a/src/bin/admin/tests/pgsql_tests.sh.in +++ b/src/bin/admin/tests/pgsql_tests.sh.in @@ -27,7 +27,7 @@ pgsql_wipe() { printf "Wiping whole database %s\n" $db_name export PGPASSWORD=$db_password - cat $db_scripts_dir/pgsql/dhcpdb_drop.pgsql | psql --set ON_ERROR_STOP=1 -A -t -q -U keatest -d keatest >/dev/null 2>&1 + cat $db_scripts_dir/pgsql/dhcpdb_drop.pgsql | psql --set ON_ERROR_STOP=1 -A -t -h localhost -q -U keatest -d keatest >/dev/null 2>&1 assert_eq 0 $? "pgsql_wipe drop failed, expected exit code: %d, actual: %d" } diff --git a/src/lib/dhcpsrv/cql_connection.cc b/src/lib/dhcpsrv/cql_connection.cc index 9b43486ac4..954b191776 100644 --- a/src/lib/dhcpsrv/cql_connection.cc +++ b/src/lib/dhcpsrv/cql_connection.cc @@ -22,14 +22,16 @@ 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), + tagged_statements_(NULL) { } CqlConnection::~CqlConnection() { + // Free up the prepared statements, ignoring errors. + // Session and connection resources are deallocated. CassError rc; - for (int i = 0; i < statements_.size(); i++) - { + for (int i = 0; i < statements_.size(); i++) { if (statements_[i]) { cass_prepared_free(statements_[i]); } @@ -60,7 +62,7 @@ CqlConnection::openDatabase() { 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 "localhost". } const char* port = NULL; @@ -69,7 +71,7 @@ CqlConnection::openDatabase() { sport = getParameter("port"); port = sport.c_str(); } catch (...) { - // No port. Fine, we'll use "default" + // No port. Fine, we'll use "default". } const char* user = NULL; @@ -78,7 +80,7 @@ CqlConnection::openDatabase() { suser = getParameter("user"); user = suser.c_str(); } catch (...) { - // No user. Fine, we'll use NULL + // No user. Fine, we'll use NULL. } const char* password = NULL; @@ -87,7 +89,7 @@ CqlConnection::openDatabase() { spassword = getParameter("password"); password = spassword.c_str(); } catch (...) { - // No password. Fine, we'll use NULL + // No password. Fine, we'll use NULL. } const char* keyspace = "keatest"; @@ -96,7 +98,7 @@ CqlConnection::openDatabase() { skeyspace = getParameter("keyspace"); keyspace = skeyspace.c_str(); } catch (...) { - // No database name. Fine, we'll use default 'keatest' + // No database name. Fine, we'll use default "keatest". } cluster_ = cass_cluster_new(); @@ -202,8 +204,7 @@ CqlConnection::rollback() { void CqlConnection::checkStatementError(std::string& error, CassFuture* future, - uint32_t stindex, const char* what) const -{ + uint32_t stindex, const char* what) const { CassError rc; const char* errorMessage; size_t errorMessageSize; @@ -215,15 +216,15 @@ CqlConnection::checkStatementError(std::string& error, CassFuture* future, if (rc != CASS_OK) { stream.str(std::string()); - stream << what << " for: " << tagged_statements_[stindex].name_ << " reason: " << - errorMessage << " error code: " << rc; + stream << what << " for: " << tagged_statements_[stindex].name_ << + " reason: " << errorMessage << " error code: " << rc; } error = stream.str(); } void -CqlConnection::checkStatementError(std::string& error, CassFuture* future, const char* what) const -{ +CqlConnection::checkStatementError(std::string& error, CassFuture* future, + const char* what) const { CassError rc; const char* errorMessage; size_t errorMessageSize; diff --git a/src/lib/dhcpsrv/cql_connection.h b/src/lib/dhcpsrv/cql_connection.h index 4fbb107210..644c0c72f7 100644 --- a/src/lib/dhcpsrv/cql_connection.h +++ b/src/lib/dhcpsrv/cql_connection.h @@ -27,20 +27,19 @@ namespace isc { namespace dhcp { /// @brief Defines a single query +/// +/// @param params_ Bind parameter names +/// @param name_ Short name of the query. +/// @param text_ Text representation of the actual query. struct CqlTaggedStatement { - /// Param name. const char** params_; - - /// Short name of the query. const char* name_; - - /// Text representation of the actual query. const char* text_; }; -/// Defines CQL backend version: 1.0 -const uint32_t CQL_CURRENT_VERSION = 1; -const uint32_t CQL_CURRENT_MINOR = 0; +// Defines CQL backend version: 2.3 +const uint32_t CQL_CURRENT_VERSION = CASS_VERSION_MAJOR; +const uint32_t CQL_CURRENT_MINOR = CASS_VERSION_MINOR; class CqlConnection : public DatabaseConnection { public: @@ -55,28 +54,26 @@ public: /// @brief Prepare statements /// - /// Creates the prepared statements for all of the SQL statements used + /// 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 + /// @throw isc::InvalidParameter 'index' is not valid for the vector. This /// represents an internal error within the code. void prepareStatements(CqlTaggedStatement *statements); /// @brief Open Database /// /// Opens the database using the information supplied in the parameters - /// passed to the constructor. + /// passed to the constructor. If no parameters are supplied, the default + /// values will be used (keyspace keatest). /// - /// @throw NoDatabaseName Mandatory database name not given /// @throw DbOpenError Error opening the database void openDatabase(); /// @brief Return backend type /// - /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) - /// /// @return Type of the backend. virtual std::string getType() const { return (std::string("cql")); @@ -96,7 +93,7 @@ public: /// @brief Returns backend version. /// - /// @return Version number as a pair of unsigned integers. "first" is the + /// @return Version number as a pair of unsigned integers. "first" is the /// major version number, "second" the minor number. /// /// @throw isc::dhcp::DbOperationError An operation on the open database has @@ -129,10 +126,18 @@ public: void checkStatementError(std::string& error, CassFuture* future, const char* what) const; - /// CQL connection handle + /// @brief CQL connection handle CassCluster* cluster_; + + /// @brief CQL session handle CassSession* session_; - std::vector statements_; ///< Prepared statements + + /// @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 CqlTaggedStatement* tagged_statements_; }; diff --git a/src/lib/dhcpsrv/cql_lease_mgr.cc b/src/lib/dhcpsrv/cql_lease_mgr.cc index d29ff88600..c3a347b540 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.cc +++ b/src/lib/dhcpsrv/cql_lease_mgr.cc @@ -37,34 +37,49 @@ namespace dhcp { static const size_t HOSTNAME_MAX_LEN = 255; static const size_t ADDRESS6_TEXT_MAX_LEN = 39; +/// @name CqlBind auxiliary methods for binding data into Cassandra format: +/// @{ 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 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 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 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 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 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)))[0], - static_cast*>(value)->size()); +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; @@ -86,16 +101,20 @@ 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) { +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); + 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); +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_; @@ -112,9 +131,9 @@ static struct CqlFunctionData CqlFunctions[] = { {CqlBindBytes, CqlGetBytes} }; -/// @brief Catalog of all the SQL statements currently supported. Note +/// @brief Catalog of all the SQL statements currently supported. Note /// that the order columns appear in statement body must match the order they -/// that the occur in the table. This does not apply to the where clause. +/// that the occur in the table. This does not apply to the where clause. static const char* delete_lease4_params[] = { static_cast("address"), @@ -173,7 +192,6 @@ static const char* get_lease6_expired_params[] = { static_cast("limit"), NULL }; static const char* get_version_params[] = { - static_cast("version"), NULL }; static const char* insert_lease4_params[] = { static_cast("address"), @@ -318,7 +336,6 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "valid_lifetime, expire, subnet_id, " "fqdn_fwd, fqdn_rev, hostname, state " "FROM lease4 " - //"WHERE state != ? AND expire < ? ORDER BY expire ASC " "WHERE state = ? AND expire < ? " "LIMIT ? ALLOW FILTERING" }, @@ -373,7 +390,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { { insert_lease4_params, "insert_lease4", "INSERT INTO lease4(address, hwaddr, client_id, " - "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, state) " + "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, " + "state) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " }, @@ -382,7 +400,8 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { "insert_lease6", "INSERT INTO lease6(address, duid, valid_lifetime, " "expire, subnet_id, pref_lifetime, " - "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, hwaddr_source, state) " + "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, " + "hwtype, hwaddr_source, state) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " }, @@ -410,23 +429,15 @@ CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = { { NULL, NULL, NULL } }; - -/// @brief Common CQL and Lease Data Methods -/// -/// The CqlLease4Exchange and CqlLease6Exchange classes provide the -/// functionality to set up binding information between variables in the -/// program and data extracted from the database. This class is the common -/// base to both of them, containing some common methods. - class 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. + 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); @@ -439,18 +450,24 @@ public: static void convertFromDatabaseTime(const uint64_t& expire, - const uint32_t& valid_lifetime, - time_t& cltt) { + const uint32_t& valid_lifetime, + time_t& cltt) { // Convert to local time cltt = expire - static_cast(valid_lifetime); } }; +/// @brief Common CQL and Lease Data Methods +/// +/// The CqlLease4Exchange and CqlLease6Exchange classes provide the +/// functionality to set up binding information between variables in the +/// program and data extracted from the database. This class is the common +/// base to both of them, containing some common methods. class 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) { + 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_)); } @@ -473,17 +490,38 @@ protected: }; +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 = 2; + // Set the column names (for error messages) + size_t offset = 0; + BOOST_ASSERT(2 == MAX_COLUMNS); + parameters_.resize(MAX_COLUMNS); + parameters_[offset++] = ExchangeColumnInfo("version", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("minor", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + BOOST_ASSERT(offset == MAX_COLUMNS); + } +}; + + /// @brief Exchange CQL and Lease4 Data /// /// On any CQL operation, arrays of CQL BIND structures must be built to -/// describe the parameters in the prepared statements. Where information is +/// describe the parameters in the prepared statements. Where information is /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that -/// structure is identical. This class handles the creation of that array. +/// structure is identical. This class handles the creation of that array. /// /// Owing to the CQL API, the process requires some intermediate variables -/// to hold things like data length etc. This object holds those variables. +/// to hold things like data length etc. This object holds those variables. /// -/// @note There are no unit tests for this class. It is tested indirectly +/// @note There are no unit tests for this class. It is tested indirectly /// in all CqlLeaseMgr::xxx4() calls where it is used. class CqlLease4Exchange : public CqlLeaseExchange { @@ -494,25 +532,36 @@ public: /// 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 = 11; memset(client_id_buffer_, 0, sizeof(client_id_buffer_)); // Set the column names (for error messages) - uint32_t size; - uint32_t offset = 0; - size = 12; - parameters_.resize(size); - parameters_[offset++] = ExchangeColumnInfo("address", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("hwaddr", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); - parameters_[offset++] = ExchangeColumnInfo("client_id", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); - parameters_[offset++] = ExchangeColumnInfo("valid_lifetime", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64); - parameters_[offset++] = ExchangeColumnInfo("expire", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP); - parameters_[offset++] = ExchangeColumnInfo("subnet_id", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); - parameters_[offset++] = ExchangeColumnInfo("fqdn_rev", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); - parameters_[offset++] = ExchangeColumnInfo("hostname", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING); - parameters_[offset++] = ExchangeColumnInfo("state", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("limit", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("version", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_NONE); + size_t offset = 0; + BOOST_STATIC_ASSERT(11 == MAX_COLUMNS); + parameters_.resize(MAX_COLUMNS); + parameters_[offset++] = ExchangeColumnInfo("address", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("hwaddr", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); + parameters_[offset++] = ExchangeColumnInfo("client_id", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); + parameters_[offset++] = ExchangeColumnInfo("valid_lifetime", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64); + parameters_[offset++] = ExchangeColumnInfo("expire", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP); + parameters_[offset++] = ExchangeColumnInfo("subnet_id", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); + parameters_[offset++] = ExchangeColumnInfo("fqdn_rev", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); + parameters_[offset++] = ExchangeColumnInfo("hostname", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING); + parameters_[offset++] = ExchangeColumnInfo("state", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("limit", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + BOOST_ASSERT(offset == MAX_COLUMNS); } /// @brief Create CQL_BIND objects for Lease4 Pointer @@ -530,8 +579,8 @@ public: try { // address: uint32_t - // The address in the Lease structure is an IOAddress object. Convert - // this to an integer for storage. + // The address in the Lease structure is an IOAddress object. + // Convert this to an integer for storage. addr4_ = static_cast(lease_->addr_); data.add(&addr4_); @@ -554,10 +603,11 @@ public: // expire: timestamp // The lease structure holds the client last transmission time (cltt_) // For convenience for external tools, this is converted to lease - // expiry time (expire). The relationship is given by: + // expiry time (expire). The relationship is given by: // // expire = cltt_ + valid_lft_ - CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_); + CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, + lease_->valid_lft_, expire_); data.add(&expire_); // subnet_id: unsigned int @@ -576,10 +626,12 @@ public: // hostname: varchar(255) hostname_length_ = lease_->hostname_.length(); if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str()); + isc_throw(BadValue, "hostname value is too large: " << + lease_->hostname_.c_str()); } if (hostname_length_) { - memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_); + memcpy(hostname_buffer_, lease_->hostname_.c_str(), + hostname_length_); } hostname_buffer_[hostname_length_] = '\0'; data.add(hostname_buffer_); @@ -655,9 +707,11 @@ public: hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_); // client_id: varbinary(128) - client_id_.assign(client_id_buffer, client_id_buffer + client_id_length_); + 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); + 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_); @@ -666,7 +720,8 @@ public: // hostname: varchar(255) if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << hostname_buffer); + isc_throw(BadValue, "hostname value is too large: " << + hostname_buffer); } if (hostname_length_) { memcpy(hostname_buffer_, hostname_buffer, hostname_length_); @@ -674,7 +729,8 @@ public: hostname_buffer_[hostname_length_] = '\0'; time_t cltt = 0; - CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, cltt); + CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, + cltt); // Recreate the hardware address. HWAddrPtr hwaddr(new HWAddr(hwaddr_, HTYPE_ETHER)); @@ -682,11 +738,10 @@ public: std::string hostname(hostname_buffer_, hostname_buffer_ + hostname_length_); - Lease4Ptr result(new Lease4(addr4_, hwaddr, - client_id_buffer_, client_id_length_, - valid_lifetime_, 0, 0, cltt, - subnet_id_, fqdn_fwd_, fqdn_rev_, - hostname)); + Lease4Ptr result(new Lease4(addr4_, hwaddr, client_id_buffer_, + client_id_length_, valid_lifetime_, 0, + 0, cltt, subnet_id_, fqdn_fwd_, + fqdn_rev_, hostname)); result->state_ = state_; @@ -713,19 +768,17 @@ private: bool client_id_null_; ///< Is Client ID null? }; - - /// @brief Exchange CQL and Lease6 Data /// /// On any CQL operation, arrays of CQL BIND structures must be built to -/// describe the parameters in the prepared statements. Where information is +/// describe the parameters in the prepared statements. Where information is /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that -/// structure is identical. This class handles the creation of that array. +/// structure is identical. This class handles the creation of that array. /// /// Owing to the CQL API, the process requires some intermediate variables -/// to hold things like data length etc. This object holds those variables. +/// to hold things like data length etc. This object holds those variables. /// -/// @note There are no unit tests for this class. It is tested indirectly +/// @note There are no unit tests for this class. It is tested indirectly /// in all CqlLeaseMgr::xxx6() calls where it is used. class CqlLease6Exchange : public CqlLeaseExchange { @@ -738,32 +791,49 @@ public: iaid_(0), lease_type_(0), prefixlen_(0), pref_lifetime_(0), hwaddr_null_(false), hwtype_(0), hwaddr_source_(0) { + const size_t MAX_COLUMNS = 17; memset(addr6_buffer_, 0, sizeof(addr6_buffer_)); memset(duid_buffer_, 0, sizeof(duid_buffer_)); // Set the column names (for error messages) - uint32_t size; - uint32_t offset = 0; - size = 18; - parameters_.resize(size); - parameters_[offset++] = ExchangeColumnInfo("address", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING); - parameters_[offset++] = ExchangeColumnInfo("duid", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); - parameters_[offset++] = ExchangeColumnInfo("valid_lifetime", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64); - parameters_[offset++] = ExchangeColumnInfo("expire", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP); - parameters_[offset++] = ExchangeColumnInfo("subnet_id", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("pref_lifetime", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64); - parameters_[offset++] = ExchangeColumnInfo("lease_type", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("iaid", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("prefix_len", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); - parameters_[offset++] = ExchangeColumnInfo("fqdn_rev", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); - parameters_[offset++] = ExchangeColumnInfo("hostname", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING); - parameters_[offset++] = ExchangeColumnInfo("hwaddr", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); - parameters_[offset++] = ExchangeColumnInfo("hwtype", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("hwaddr_source", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("state", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("limit", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); - parameters_[offset++] = ExchangeColumnInfo("version", EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_NONE); + size_t offset = 0; + BOOST_STATIC_ASSERT(17 == MAX_COLUMNS); + parameters_.resize(MAX_COLUMNS); + parameters_[offset++] = ExchangeColumnInfo("address", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING); + parameters_[offset++] = ExchangeColumnInfo("duid", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); + parameters_[offset++] = ExchangeColumnInfo("valid_lifetime", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64); + parameters_[offset++] = ExchangeColumnInfo("expire", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_TIMESTAMP); + parameters_[offset++] = ExchangeColumnInfo("subnet_id", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("pref_lifetime", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT64); + parameters_[offset++] = ExchangeColumnInfo("lease_type", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("iaid", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("prefix_len", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("fqdn_fwd", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); + parameters_[offset++] = ExchangeColumnInfo("fqdn_rev", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL); + parameters_[offset++] = ExchangeColumnInfo("hostname", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING); + parameters_[offset++] = ExchangeColumnInfo("hwaddr", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BYTES); + parameters_[offset++] = ExchangeColumnInfo("hwtype", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("hwaddr_source", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("state", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + parameters_[offset++] = ExchangeColumnInfo("limit", + EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32); + BOOST_ASSERT(offset == MAX_COLUMNS); } /// @brief Create CQL_BIND objects for Lease6 Pointer @@ -776,15 +846,16 @@ public: } // Store lease object to ensure it remains valid. lease_ = lease; + // Set up the structures for the various components of the lease4 // structure. - try { // address: varchar(39) std::string text_buffer = lease_->addr_.toText(); addr6_length_ = text_buffer.size(); if (addr6_length_ >= sizeof(addr6_buffer_)) { - isc_throw(BadValue, "address value is too large: " << text_buffer); + isc_throw(BadValue, "address value is too large: " << + text_buffer); } if (addr6_length_) { memcpy(addr6_buffer_, text_buffer.c_str(), addr6_length_); @@ -794,8 +865,8 @@ public: // duid: varchar(128) if (!lease_->duid_) { - isc_throw(DbOperationError, "lease6 for address " << addr6_buffer_ - << " is missing mandatory client-id."); + isc_throw(DbOperationError, "lease6 for address " << + addr6_buffer_ << " is missing mandatory client-id."); } duid_ = lease_->duid_->getDuid(); duid_length_ = duid_.size(); @@ -808,10 +879,11 @@ public: // expire: timestamp // The lease structure holds the client last transmission time (cltt_) // For convenience for external tools, this is converted to lease - // expiry time (expire). The relationship is given by: + // expiry time (expire). The relationship is given by: // // expire = cltt_ + valid_lft_ - CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_, expire_); + CqlLeaseExchange::convertToDatabaseTime(lease_->cltt_, + lease_->valid_lft_, expire_); data.add(&expire_); // subnet_id: unsigned int @@ -850,10 +922,12 @@ public: // hostname: varchar(255) hostname_length_ = lease_->hostname_.length(); if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << lease_->hostname_.c_str()); + isc_throw(BadValue, "hostname value is too large: " << + lease_->hostname_.c_str()); } if (hostname_length_) { - memcpy(hostname_buffer_, lease_->hostname_.c_str(), hostname_length_); + memcpy(hostname_buffer_, lease_->hostname_.c_str(), + hostname_length_); } hostname_buffer_[hostname_length_] = '\0'; data.add(hostname_buffer_); @@ -977,7 +1051,8 @@ public: // address: varchar(39) if (addr6_length_ >= sizeof(addr6_buffer_)) { - isc_throw(BadValue, "address value is too large: " << address_buffer); + isc_throw(BadValue, "address value is too large: " << + address_buffer); } if (addr6_length_) { memcpy(addr6_buffer_, address_buffer, addr6_length_); @@ -989,7 +1064,8 @@ public: // hostname: varchar(255) if (hostname_length_ >= sizeof(hostname_buffer_)) { - isc_throw(BadValue, "hostname value is too large: " << hostname_buffer); + isc_throw(BadValue, "hostname value is too large: " << + hostname_buffer); } if (hostname_length_) { memcpy(hostname_buffer_, hostname_buffer, hostname_length_); @@ -1021,13 +1097,15 @@ public: // 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); + CqlLeaseExchange::convertFromDatabaseTime(expire_, valid_lifetime_, + cltt); result->cltt_ = cltt; result->state_ = state_; @@ -1063,12 +1141,14 @@ private: CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) : LeaseMgr(), dbconn_(parameters), exchange4_(new CqlLease4Exchange()), - exchange6_(new CqlLease6Exchange()) { + exchange6_(new CqlLease6Exchange()), versionExchange_(new CqlVersionExchange()) { dbconn_.openDatabase(); dbconn_.prepareStatements(CqlLeaseMgr::tagged_statements_); } CqlLeaseMgr::~CqlLeaseMgr() { + // There is no need to close the database in this destructor: it is + // closed in the destructor of the dbconn_ member variable. } std::string @@ -1081,11 +1161,13 @@ CqlLeaseMgr::getDBVersion() { } void -CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, const SqlExchange& exchange, ExchangeDataType& type) { +CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, + const SqlExchange& exchange, ExchangeDataType& type) { if (CqlLeaseMgr::tagged_statements_[stindex].params_ && CqlLeaseMgr::tagged_statements_[stindex].params_[pindex]) { for (int i = 0; exchange.parameters_.size(); i++) { - if (!strcmp(CqlLeaseMgr::tagged_statements_[stindex].params_[pindex], exchange.parameters_[i].column_)) { + if (!strcmp(CqlLeaseMgr::tagged_statements_[stindex].params_[pindex], + exchange.parameters_[i].column_)) { type = exchange.parameters_[i].type_; return; } @@ -1095,7 +1177,8 @@ CqlLeaseMgr::getDataType(const StatementIndex stindex, int pindex, const SqlExch } void -CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, CqlDataArray& data, const SqlExchange& exchange) { +CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, + CqlDataArray& data, const SqlExchange& exchange) { if (CqlLeaseMgr::tagged_statements_[stindex].params_ == NULL) { return; } @@ -1107,13 +1190,19 @@ CqlLeaseMgr::bindData(CassStatement* statement, const StatementIndex stindex, Cq } void -CqlLeaseMgr::getData(const CassRow* row, int pindex, CqlDataArray& data, CqlDataArray& size, const SqlExchange& exchange) { +CqlLeaseMgr::getData(const CassRow* row, int pindex, CqlDataArray& data, + CqlDataArray& size, const SqlExchange& exchange) { const CassValue* value; if (pindex >= exchange.parameters_.size()) { return; } value = cass_row_get_column_by_name(row, exchange.parameters_[pindex].column_); - CqlFunctions[exchange.parameters_[pindex].type_].sqlGetFunction_(value, data.values_[pindex], reinterpret_cast(size.values_[pindex])); + if (NULL == value) { + isc_throw(BadValue, "Column name " + << exchange.parameters_[pindex].column_ << "doesn't exist"); + } + CqlFunctions[exchange.parameters_[pindex].type_].sqlGetFunction_(value, + data.values_[pindex], reinterpret_cast(size.values_[pindex])); } bool @@ -1200,8 +1289,7 @@ void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex, int rowCount = 0; while (cass_iterator_next(rows)) { rowCount++; - if (single && rowCount > 1) - { + if (single && rowCount > 1) { result.clear(); break; } @@ -1224,7 +1312,7 @@ void CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data, Lease4Ptr& result) const { // Create appropriate collection object and get all leases matching - // the selection criteria. The "single" parameter is true to indicate + // the selection criteria. The "single" parameter is true to indicate // that the called method should throw an exception if multiple // matching records are found: this particular method is called when only // one or zero matches is expected. @@ -1244,7 +1332,7 @@ void CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data, Lease6Ptr& result) const { // Create appropriate collection object and get all leases matching - // the selection criteria. The "single" parameter is true to indicate + // the selection criteria. The "single" parameter is true to indicate // that the called method should throw an exception if multiple // matching records are found: this particular method is called when only // one or zero matches is expected. @@ -1259,7 +1347,7 @@ CqlLeaseMgr::getLease(StatementIndex stindex, CqlDataArray& data, } } -// Basic lease access methods. Obtain leases from the database using various +// Basic lease access methods. Obtain leases from the database using various // criteria. Lease4Ptr @@ -1342,7 +1430,8 @@ CqlLeaseMgr::getLease4(const ClientId& clientid) const { } Lease4Ptr -CqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr, SubnetID subnet_id) const { +CqlLeaseMgr::getLease4(const ClientId& clientid, const HWAddr& hwaddr, + SubnetID subnet_id) const { /// This function is currently not implemented because allocation engine /// searches for the lease using HW address or client identifier. /// It never uses both parameters in the same time. We need to @@ -1498,7 +1587,8 @@ CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases, uint32_t limit = max_leases > 0 ? static_cast(max_leases) : std::numeric_limits::max(); - for (uint32_t state = Lease::STATE_DEFAULT; state <= Lease::STATE_EXPIRED_RECLAIMED; state++) { + for (uint32_t state = Lease::STATE_DEFAULT; + state <= Lease::STATE_EXPIRED_RECLAIMED; state++) { if (state == keepState) { continue; } @@ -1514,7 +1604,8 @@ CqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases, typedef typename LeaseCollection::iterator LeaseCollectionIt; - for (LeaseCollectionIt it = tempCollection.begin(); it != tempCollection.end(); ++it) { + for (LeaseCollectionIt it = tempCollection.begin(); + it != tempCollection.end(); ++it) { expired_leases.push_back((*it)); } } @@ -1673,7 +1764,8 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { } 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 //"WHERE state = ? AND expire < ? ALLOW FILTERING" @@ -1685,7 +1777,8 @@ CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIn data.add(&state); // Expiration timestamp. - uint64_t expiration = static_cast(time(NULL) - static_cast(secs)); + uint64_t expiration = static_cast(time(NULL) - + static_cast(secs)); data.add(&expiration); // Get the data @@ -1701,12 +1794,14 @@ CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIn default: break; } - for (Lease4Collection::iterator it = result4Leases.begin(); it != result4Leases.end(); ++it) { + for (Lease4Collection::iterator it = result4Leases.begin(); + it != result4Leases.end(); ++it) { if (deleteLease((*it)->addr_)) { result++; } } - for (Lease6Collection::iterator it = result6Leases.begin(); it != result6Leases.end(); ++it) { + for (Lease6Collection::iterator it = result6Leases.begin(); + it != result6Leases.end(); ++it) { if (deleteLease((*it)->addr_)) { result++; } @@ -1734,9 +1829,49 @@ pair CqlLeaseMgr::getVersion() const { LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_GET_VERSION); + uint32_t version; + uint32_t minor; + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + + statement = cass_prepared_bind(dbconn_.statements_[GET_VERSION]); + + future = cass_session_execute(dbconn_.session_, statement); + cass_future_wait(future); + std::string error; + dbconn_.checkStatementError(error, future, "unable to GET"); + rc = cass_future_error_code(future); + if (rc != CASS_OK) { + cass_future_free(future); + cass_statement_free(statement); + isc_throw(DbOperationError, error); + } - uint32_t version = CASS_VERSION_MAJOR; - uint32_t minor = CASS_VERSION_MINOR; + const CassResult* resultCollection = cass_future_get_result(future); + CassIterator* rows = cass_iterator_from_result(resultCollection); + CqlDataArray data; + CqlDataArray size; + int rowCount = 0; + while (cass_iterator_next(rows)) { + rowCount++; + const CassRow* row = cass_iterator_get_row(rows); + // version: uint32_t + data.add(reinterpret_cast(&version)); + size.add(NULL); + // minor: uint32_t + data.add(reinterpret_cast(&minor)); + size.add(NULL); + + for (int i = 0; i < 2; i++) { + CqlLeaseMgr::getData(row, i, data, size, *versionExchange_); + } + } + + cass_iterator_free(rows); + cass_result_free(resultCollection); + cass_future_free(future); + cass_statement_free(statement); return make_pair(version, minor); } diff --git a/src/lib/dhcpsrv/cql_lease_mgr.h b/src/lib/dhcpsrv/cql_lease_mgr.h index 78b34b9534..c5fcc1a490 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.h +++ b/src/lib/dhcpsrv/cql_lease_mgr.h @@ -34,7 +34,7 @@ namespace dhcp { /// CQL execute call. /// /// Note that the data values are stored as pointers. These pointers need to -/// valid for the duration of the CQL statement execution. In other +/// 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. @@ -45,20 +45,24 @@ struct CqlDataArray { values_.push_back(value); } 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); } }; -class CqlExchange; +class CqlVersionExchange; class CqlLeaseExchange; class CqlLease4Exchange; class CqlLease6Exchange; /// @brief Cassandra Lease Manager /// -/// This class provides the \ref isc::dhcp::LeaseMgr interface to the CQL - Cassandra -/// database. Use of this backend presupposes that a CQL database is -/// available and that the Kea schema has been created within it. +/// This class provides the \ref isc::dhcp::LeaseMgr interface to the Cassandra +/// database. Use of this backend presupposes that a CQL database is available +/// and that the Kea schema has been created within it. class CqlLeaseMgr : public LeaseMgr { public: @@ -314,7 +318,7 @@ public: /// @brief Deletes a lease. /// - /// @param addr Address of the lease to be deleted. This can be an IPv4 + /// @param addr Address of the lease to be deleted. This can be an IPv4 /// address or an IPv6 address. /// /// @return true if deletion was successful, false if no such lease exists @@ -343,8 +347,6 @@ public: /// @brief Return backend type /// - /// Returns the type of the backend (e.g. "mysql", "memfile" etc.) - /// /// @return Type of the backend. virtual std::string getType() const { return (std::string("cql")); @@ -364,7 +366,7 @@ public: /// @brief Returns backend version. /// - /// @return Version number as a pair of unsigned integers. "first" is the + /// @return Version number as a pair of unsigned integers. "first" is the /// major version number, "second" the minor number. /// /// @throw isc::dhcp::DbOperationError An operation on the open database has @@ -373,16 +375,12 @@ public: /// @brief Commit Transactions /// - /// Commits all pending database operations. - /// - /// @throw DbOperationError If the commit failed. + /// This is a no-op for Cassandra. virtual void commit(); /// @brief Rollback Transactions /// - /// Rolls back all pending database operations. - /// - /// @throw DbOperationError If the rollback failed. + /// This is a no-op for Cassandra. virtual void rollback(); @@ -412,30 +410,61 @@ public: UPDATE_LEASE6, // Update a Lease6 entry NUM_STATEMENTS // Number of statements }; - static void bindData(CassStatement* statement, const StatementIndex stindex, CqlDataArray& data, const SqlExchange& exchange); - static void getDataType(const StatementIndex stindex, int param, const SqlExchange& exchange, ExchangeDataType& type); + /// @brief TODO + /// + /// TODO + /// + /// @param statement TODO + /// @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 TODO + /// + /// TODO + /// + /// @param stindex Index of statement being executed + /// @param param TODO + /// @param exchange Exchange object to use + /// @param type TODO + static void getDataType(const StatementIndex stindex, int param, + const SqlExchange& exchange, ExchangeDataType& type); - static void getData(const CassRow* row, int pindex, CqlDataArray& data, CqlDataArray& size, const SqlExchange& exchange); + /// @brief TODO + /// + /// TODO + /// + /// @param row TODO + /// @param pindex Index of statement being executed + /// @param data array that has been created for the type of lease in question. + /// @param data size TODO + /// @param exchange Exchange object to use + static void getData(const CassRow* row, int pindex, CqlDataArray& data, + CqlDataArray& size, const SqlExchange& exchange); private: /// @brief Add Lease Common Code /// /// This method performs the common actions for both flavours (V4 and V6) - /// of the addLease method. It binds the contents of the lease object to + /// of the addLease method. It binds the contents of the lease object to /// the prepared statement and adds it to the database. /// /// @param stindex Index of statement being executed /// @param data array that has been created for the type /// of lease in question. + /// @param exchange Exchange object to use /// /// @return true if the lease was added, false if it was not added because /// a lease with that address already exists in the database. /// /// @throw isc::dhcp::DbOperationError An operation on the open database has /// failed. - bool addLeaseCommon(StatementIndex stindex, CqlDataArray& data, CqlLeaseExchange& exchange); + bool addLeaseCommon(StatementIndex stindex, CqlDataArray& data, + CqlLeaseExchange& exchange); /// @brief Get Lease Collection Common Code /// @@ -464,12 +493,12 @@ private: /// @brief Gets Lease4 Collection /// - /// Gets a collection of Lease4 objects. This is just an interface to + /// Gets a collection of Lease4 objects. This is just an interface to /// the get lease collection common code. /// /// @param stindex Index of statement being executed /// @param data array containing the where clause input parameters - /// @param lease LeaseCollection object returned. Note that any leases in + /// @param result LeaseCollection object returned. Note that any leases in /// the collection when this method is called are not erased: the /// new data is appended to the end. /// @@ -485,12 +514,12 @@ private: /// @brief Get Lease6 Collection /// - /// Gets a collection of Lease6 objects. This is just an interface to + /// Gets a collection of Lease6 objects. This is just an interface to /// the get lease collection common code. /// /// @param stindex Index of statement being executed /// @param data array containing input parameters for the query - /// @param lease LeaseCollection object returned. Note that any existing + /// @param result LeaseCollection object returned. Note that any existing /// data in the collection is erased first. /// /// @throw isc::dhcp::BadValue Data retrieved from the database was invalid. @@ -506,7 +535,7 @@ private: /// @brief Get Lease4 Common Code /// /// This method performs the common actions for the various getLease4() - /// methods. It acts as an interface to the getLeaseCollection() method, + /// methods. It acts as an interface to the getLeaseCollection() method, /// but retrieveing only a single lease. /// /// @param stindex Index of statement being executed @@ -518,7 +547,7 @@ private: /// @brief Get Lease6 Common Code /// /// This method performs the common actions for the various getLease4() - /// methods. It acts as an interface to the getLeaseCollection() method, + /// methods. It acts as an interface to the getLeaseCollection() method, /// but retrieveing only a single lease. /// /// @param stindex Index of statement being executed @@ -548,7 +577,7 @@ private: /// @brief Update lease common code /// - /// Holds the common code for updating a lease. It binds the parameters + /// Holds the common code for updating a lease. It binds the parameters /// to the prepared statement, executes it, then checks how many rows /// were affected. /// @@ -567,7 +596,7 @@ private: /// @brief Delete lease common code /// - /// Holds the common code for deleting a lease. It binds the parameters + /// Holds the common code for deleting a lease. It binds the parameters /// to the prepared statement, executes the statement and checks to /// see how many rows were deleted. /// @@ -579,7 +608,8 @@ 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. /// @@ -593,16 +623,18 @@ private: uint64_t deleteExpiredReclaimedLeasesCommon(const uint32_t secs, StatementIndex statement_index); + /// TODO static CqlTaggedStatement tagged_statements_[]; /// Database connection object 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 + /// 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 }; }; // end of isc::dhcp namespace diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index 660b2bf515..065416391d 100755 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -60,6 +60,8 @@ 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, @@ -70,12 +72,15 @@ enum ExchangeDataType { 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 () : column_(NULL), type_io_(EXCHANGE_DATA_TYPE_IO_IN), type_(EXCHANGE_DATA_TYPE_NONE) {}; ExchangeColumnInfo (const char *column, ExchangeDataTypeIO type_io, ExchangeDataType type) : column_(column), type_io_(type_io), type_(type) {}; diff --git a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc index a817a620a9..a82b62ea76 100644 --- a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc @@ -31,7 +31,6 @@ using namespace std; namespace { - /// @brief Test fixture class for testing Cassandra Lease Manager /// /// Opens the database prior to each test and closes it afterwards. @@ -64,8 +63,8 @@ public: /// @brief Destructor /// - /// Rolls back all pending transactions. The deletion of lmptr_ will close - /// the database. Then reopen it and delete everything created by the test. + /// Rolls back all pending transactions. The deletion of lmptr_ will close + /// the database. Then reopen it and delete everything created by the test. virtual ~CqlLeaseMgrTest() { lmptr_->rollback(); LeaseMgrFactory::destroy(); @@ -74,7 +73,7 @@ public: /// @brief Reopen the database /// - /// Closes the database and re-open it. Anything committed should be + /// Closes the database and re-open it. Anything committed should be /// visible. /// /// Parameter is ignored for CQL backend as the v4 and v6 leases share @@ -89,9 +88,9 @@ public: /// @brief Check that database can be opened /// -/// This test checks if the CqlLeaseMgr can be instantiated. This happens -/// only if the database can be opened. Note that this is not part of the -/// CqlLeaseMgr test fixure set. This test checks that the database can be +/// This test checks if the CqlLeaseMgr can be instantiated. This happens +/// only if the database can be opened. Note that this is not part of the +/// CqlLeaseMgr test fixure set. This test checks that the database can be /// opened: the fixtures assume that and check basic operations. TEST(CqlOpenTest, OpenDatabase) { @@ -114,7 +113,7 @@ TEST(CqlOpenTest, OpenDatabase) { } // Check that lease manager open the database opens correctly with a longer - // timeout. If it fails, print the error message. + // timeout. If it fails, print the error message. try { string connection_string = validCqlConnectionString() + string(" ") + string(VALID_TIMEOUT); @@ -187,7 +186,7 @@ TEST_F(CqlLeaseMgrTest, getType) { /// @brief Check conversion functions /// -/// The server works using cltt and valid_filetime. In the database, the +/// The server works using cltt and valid_filetime. In the database, the /// information is stored as expire_time and valid-lifetime, which are /// related by /// @@ -196,12 +195,18 @@ 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; + + // Convert to the database time + CqlExchange::convertToDatabaseTime(cltt, valid_lft, cql_expire); + // Convert back time_t converted_cltt = 0; + CqlExchange::convertFromDatabaseTime(cql_expire, valid_lft, converted_cltt); EXPECT_EQ(cltt, converted_cltt); } - /// @brief Check getName() returns correct database name TEST_F(CqlLeaseMgrTest, getName) { EXPECT_EQ(std::string("keatest"), lmptr_->getName()); diff --git a/src/lib/dhcpsrv/testutils/cql_schema.h b/src/lib/dhcpsrv/testutils/cql_schema.h index 7724a50f18..9b92ea1528 100644 --- a/src/lib/dhcpsrv/testutils/cql_schema.h +++ b/src/lib/dhcpsrv/testutils/cql_schema.h @@ -32,8 +32,8 @@ std::string validCqlConnectionString(); /// will fail. The output of stderr is suppressed unless the parameter, /// show_err is true. /// -/// @param force_wipe forces wipe of the database, even if KEA_TEST_CASSANDRA_WIPE -/// is set. +/// @param force_wipe forces wipe of the database, even if +/// KEA_TEST_CASSANDRA_WIPE is set. /// @param show_err flag which governs whether or not stderr is suppressed. void destroyCqlSchema(bool force_wipe, bool show_err = false); @@ -47,17 +47,17 @@ void destroyCqlSchema(bool force_wipe, bool show_err = false); /// will fail. The output of stderr is suppressed unless the parameter, /// show_err is true. /// -/// @param force_wipe forces wipe of the database, even if KEA_TEST_CASSANDRA_WIPE -/// is set. +/// @param force_wipe forces wipe of the database, even if +/// KEA_TEST_CASSANDRA_WIPE is set. /// @param show_err flag which governs whether or not stderr is suppressed. void createCqlSchema(bool force_wipe, bool show_err = false); /// @brief Run a CQL script against the CQL unit test database /// /// Submits the given SQL script to CQL via cqlsh CLI. The output of -/// stderr is suppressed unless the parameter, show_err is true. The is done +/// stderr is suppressed unless the parameter, show_err is true. The is done /// to suppress warnings that might otherwise make test output needlessly -/// noisy. A gtest assertion occurs if the script fails to execute. +/// noisy. A gtest assertion occurs if the script fails to execute. /// /// @param path - path (if not blank) of the script to execute /// @param script_name - file name of the path to execute diff --git a/src/share/database/scripts/cql/dhcpdb_create.cql b/src/share/database/scripts/cql/dhcpdb_create.cql index 6cad321bfc..eda5fe2fc9 100644 --- a/src/share/database/scripts/cql/dhcpdb_create.cql +++ b/src/share/database/scripts/cql/dhcpdb_create.cql @@ -103,11 +103,9 @@ CREATE TABLE lease6_types ( name varchar, -- Name of the lease type PRIMARY KEY (lease_type) ); ---START TRANSACTION; INSERT INTO lease6_types (lease_type, name) VALUES (0, 'IA_NA'); -- Non-temporary v6 addresses INSERT INTO lease6_types (lease_type, name) VALUES (1, 'IA_TA'); -- Temporary v6 addresses INSERT INTO lease6_types (lease_type, name) VALUES (2, 'IA_PD'); -- Prefix delegations ---COMMIT; -- Kea keeps track of the hardware/MAC address source, i.e. how the address -- was obtained. Depending on the technique and your network topology, it may @@ -198,16 +196,9 @@ 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/lib/dhcpsrv/tests/schema_copy.h, --- which defines the schema for the unit tests. If you are updating --- the version number, the schema has changed: please ensure that --- schema_copy.h has been updated as well. CREATE TABLE schema_version ( version int, minor int, PRIMARY KEY (version) ); ---START TRANSACTION; INSERT INTO schema_version (version, minor) VALUES (1, 0); ---COMMIT; diff --git a/src/share/database/scripts/mysql/dhcpdb_create.mysql b/src/share/database/scripts/mysql/dhcpdb_create.mysql index 6cc866ed08..5d6a321200 100755 --- a/src/share/database/scripts/mysql/dhcpdb_create.mysql +++ b/src/share/database/scripts/mysql/dhcpdb_create.mysql @@ -95,11 +95,6 @@ COMMIT; # This table is only modified during schema upgrades. For historical reasons # (related to the names of the columns in the BIND 10 DNS database file), the # first column is called "version" and not "major". -# -# NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h, -# which defines the schema for the unit tests. If you are updating -# the version number, the schema has changed: please ensure that -# schema_copy.h has been updated as well. CREATE TABLE schema_version ( version INT PRIMARY KEY NOT NULL, # Major version number minor INT # Minor version number diff --git a/src/share/database/scripts/pgsql/dhcpdb_create.pgsql b/src/share/database/scripts/pgsql/dhcpdb_create.pgsql index 0d98554a9f..2d5241a6c4 100644 --- a/src/share/database/scripts/pgsql/dhcpdb_create.pgsql +++ b/src/share/database/scripts/pgsql/dhcpdb_create.pgsql @@ -82,11 +82,6 @@ INSERT INTO lease6_types VALUES (2, 'IA_PD'); -- Prefix delegations -- This table is only modified during schema upgrades. For historical reasons -- (related to the names of the columns in the BIND 10 DNS database file), the -- first column is called "version" and not "major". - --- NOTE: this MUST be kept in step with src/lib/dhcpsrv/tests/schema_copy.h, --- which defines the schema for the unit tests. If you are updating --- the version number, the schema has changed: please ensure that --- schema_copy.h has been updated as well. CREATE TABLE schema_version ( version INT PRIMARY KEY NOT NULL, -- Major version number minor INT -- Minor version number -- 2.47.2