- Andrei Pavel, Qualitance
2016-10: Support for DHCPv6 options defined in RFC6603 and RFC7598
+ 2016-12: Cassandra Host Data Source
- Vincent Legout
2016-11: Fixed serveral spelling mistakes
Boost (http://www.boost.org/) library for almost everything, and can use Botan
(http://botan.randombit.net/) or OpenSSL (https://www.openssl.org/) for
cryptographic operations. It can also optionally use PostgreSQL
-(http://www.postgresql.org/) and/or MySQL (http://www.mysql.com/) as a database.
+(http://www.postgresql.org/) and/or MySQL (http://www.mysql.com/) and/or
+Cassandra (http://cassandra.apache.org/) as a database.
Kea can use googletest for unit-tests (http://code.google.com/p/googletest/).
src/share/database/scripts/pgsql/upgrade_1.0_to_2.0.sh
src/share/database/scripts/pgsql/upgrade_2.0_to_3.0.sh
src/share/database/scripts/cql/Makefile
+ src/share/database/scripts/cql/upgrade_1.0_to_2.0.sh
tools/Makefile
tools/path_replacer.sh
])
<listitem>
<simpara>
<command>lease-init</command> —
- Initializes a new lease database. This is useful during a new
+ Initializes a new lease database. This is useful during a new
Kea installation. The database is initialized to the
latest version supported by the version of the software being
installed.
<para>
<table frame="all" id="backends">
<title>List of available backends</title>
- <tgroup cols='2'>
+ <tgroup cols='5'>
<colspec colname='feature'/>
<colspec colname='memfile'/>
<colspec colname='mysql'/>
<colspec colname='pgsql'/>
- <colspec colname='cql'/>
+ <colspec colname='cql' colwidth='1.5*'/>
<thead>
<row>
<entry>Feature</entry>
<entry>Memfile</entry>
<entry>MySQL</entry>
<entry>PostgreSQL</entry>
- <entry>CQL(Cassandra)</entry>
+ <entry>CQL (Cassandra)</entry>
</row>
</thead>
<tbody>
<entry>no</entry>
<entry>yes</entry>
<entry>yes</entry>
- <entry>no</entry>
+ <entry>yes</entry>
</row>
<row>
<entry>no</entry>
<entry>yes</entry>
<entry>yes</entry>
- <entry>no</entry>
+ <entry>yes</entry>
</row>
</tbody>
<title>PostgreSQL</title>
<para>
+ PostgreSQL is able to store leases, host reservations and options
+ defined on a per host basis.
A PostgreSQL database must be set up if you want Kea to store
lease and other information in PostgreSQL. This step can be
safely ignored if you are using other database backends.
<para>
Cassandra, or Cassandra Query Language (CQL), is the newest backend
added to Kea. Since it was added recently and has not undergone as much
- testing as other backends, it is considered experimental: please use
- with caution. The CQL backend is currently able to store leases only. The
- ability to store host reservations will likely be added some time in the
- future.
+ testing as other backends, it is considered experimental. Please use
+ with caution. The Casandra backend is currently able to store leases,
+ host reservations and options defined on a per host basis.
</para>
<para>
purpose, be it lease or hosts information. This arrangement gives the most
flexibility. Kea can be used to keep leases and host reservations
separately, but can also point to the same database. Currently the
- supported hosts database types are MySQL and PostgreSQL. The Cassandra
- backend does not support host reservations yet.</para>
+ supported hosts database types are MySQL, PostgreSQL and CQL (Cassandra).</para>
<para>Please note that usage of hosts storage is optional. A user can define
all host reservations in the configuration file. That is the recommended way
with classification using expressions.</para>
</section>
- <section id="reservations4-mysql-pgsql">
- <title>Storing Host Reservations in MySQL or PostgreSQL</title>
+ <section id="reservations4-mysql-pgsql-cql">
+ <title>Storing Host Reservations in MySQL, PostgreSQL or CQL (Cassandra)</title>
<para>
- It is possible to store host reservations in MySQL or PostgreSQL database. See
+ It is possible to store host reservations in MySQL, PostgreSQL or Cassandra. See
<xref linkend="hosts4-storage"/> for information on how to configure Kea to use
- reservations stored in MySQL or PostgreSQL. Kea does not provide any dedicated
+ reservations stored in MySQL, PostgreSQL or Cassandra. Kea does not provide any dedicated
tools for managing reservations in a database. The Kea wiki <ulink
url="http://kea.isc.org/wiki/HostReservationsHowTo" /> provides detailed
information and examples of how reservations can be inserted into the
arbitrarily set to 4096 bytes.</simpara></note>
</section>
- <section id="reservations4-cql">
- <title>Storing host reservations in CQL (Cassandra)</title>
- <para>Kea currently does not support storing reservations in
- Cassandra (CQL).</para>
- </section>
-
-
<section id="reservations4-tuning">
<title>Fine Tuning DHCPv4 Host Reservation</title>
<row><entry>s46-br</entry><entry>90</entry><entry>ipv6-address</entry><entry>false</entry></row>
<row><entry>s46-dmr</entry><entry>91</entry><entry>ipv6-prefix</entry><entry>false</entry></row>
<row><entry>s46-v4v6bind</entry><entry>92</entry><entry>record (ipv4-address, ipv6-prefix)</entry><entry>false</entry></row>
-<row><entry>s46-portparams</entry><entry>93</entry><entry>record(uint8, psid)</entry><entry>false</entry></row>
+<row><entry>s46-portparams</entry><entry>93</entry><entry>record (uint8, psid)</entry><entry>false</entry></row>
<row><entry>s46-cont-mape</entry><entry>94</entry><entry>empty</entry><entry>false</entry></row>
<row><entry>s46-cont-mapt</entry><entry>95</entry><entry>empty</entry><entry>false</entry></row>
<row><entry>s46-cont-lw</entry><entry>96</entry><entry>empty</entry><entry>false</entry></row>
with classification using expressions.</para>
</section>
- <section id="reservations6-mysql-pgsql">
- <title>Storing Host Reservations in MySQL or PostgreSQL</title>
+ <section id="reservations6-mysql-pgsql-cql">
+ <title>Storing Host Reservations in MySQL, PostgreSQL or CQL (Cassandra)</title>
<para>
- It is possible to store host reservations in MySQL or PostgreSQL. See <xref
- linkend="hosts6-storage" /> for information on how to configure Kea to use
- reservations stored in MySQL or PostgreSQL. Kea does not provide any dedicated
+ It is possible to store host reservations in MySQL, PostgreSQL or Cassandra. See
+ <xref linkend="hosts6-storage" /> for information on how to configure Kea to use
+ reservations stored in MySQL, PostgreSQL or Cassandra. Kea does not provide any dedicated
tools for managing reservations in a database. The Kea wiki <ulink
url="http://kea.isc.org/wiki/HostReservationsHowTo" /> provides detailed
information and examples of how reservations can be inserted into the
arbitrarily set to 4096 bytes.</simpara></note>
</section>
- <section id="reservations6-cql">
- <title>Storing Host Reservations in CQL (Cassandra)</title>
- <para>Kea currently does not support storing reservations in
- Cassandra (CQL).</para>
- </section>
-
<section id="reservations6-tuning">
<title>Fine Tuning DHCPv6 Host Reservation</title>
mysql_execute() {
QUERY=$1
shift
- if [ $# -gt 1 ]; then
+ if [ $# -ge 1 ]; then
mysql -N -B $* -e "${QUERY}"
retcode=$?
else
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
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
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"
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"
}
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
}
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"="<empty>" ]; then
+ result=$(cql_execute "DESCRIBE tables;")
+ if [ $(echo "$result" | grep "<empty>" | 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
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
}
if HAVE_CQL
SHTESTS += cql_tests.sh
endif
+
noinst_SCRIPTS = $(SHTESTS)
EXTRA_DIST = dhcpdb_create_1.0.mysql
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
# 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
# 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.
# 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.
-- 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,
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,
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
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,
-- 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,
-- 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.
+-- -----------------------------------------------------
+-- 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.
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
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_host_data_source.cc cql_host_data_source.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
install-data-local:
$(mkinstalldirs) $(DESTDIR)$(dhcp_data_dir)
-
-
// If there is no such option in the destination container,
// add one.
if (std::distance(range.first, range.second) == 0) {
- dest_container.addItem(OptionDescriptor(src_opt->option_,
- src_opt->persistent_),
- *it);
+ dest_container.addItem(OptionDescriptor(
+ src_opt->option_, src_opt->persistent_,
+ src_opt->formatted_value_), *it);
}
}
}
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <config.h>
+
#include <dhcpsrv/cql_connection.h>
-#include <string>
+#include <dhcpsrv/cql_exchange.h>
+
+#include <boost/shared_ptr.hpp>
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;
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_);
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);
}
}
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();
}
const char* password = NULL;
- string spassword;
+ std::string spassword;
try {
spassword = getParameter("password");
password = spassword.c_str();
}
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<int>(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<int>(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<int>(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<int>(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<int>(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");
}
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_;
}
}
+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);
}
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;
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
#include <dhcpsrv/database_connection.h>
#include <dhcpsrv/dhcpsrv_log.h>
-#include <inttypes.h>
+
#include <cassandra.h>
+
+#include <boost/scoped_ptr.hpp>
+
+#include <inttypes.h>
+
+#include <map>
+#include <string>
#include <vector>
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
-const uint32_t CQL_SCHEMA_VERSION_MAJOR = 1;
+/// Define CQL schema version: 2.0
+const uint32_t CQL_SCHEMA_VERSION_MAJOR = 2;
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();
/// 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.
/// 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_;
/// @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<const CassPrepared*> 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
--- /dev/null
+// Copyright (C) 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <config.h>
+
+#include <dhcpsrv/cql_exchange.h>
+
+#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/db_exceptions.h>
+
+/// @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<cass_bool_t*>(value)));
+}
+
+static CassError
+CqlBindInt32(CassStatement* statement, size_t index, void* value) {
+ return cass_statement_bind_int32(statement, index,
+ *(static_cast<cass_int32_t*>(value)));
+}
+
+static CassError
+CqlBindInt64(CassStatement* statement, size_t index, void* value) {
+ return cass_statement_bind_int64(statement, index,
+ *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError
+CqlBindTimestamp(CassStatement* statement, size_t index, void* value) {
+ return cass_statement_bind_int64(statement, index,
+ *(static_cast<cass_int64_t*>(value)));
+}
+
+static CassError
+CqlBindString(CassStatement* statement, size_t index, void* value) {
+ return cass_statement_bind_string(
+ statement, index, static_cast<std::string*>(value)->c_str());
+}
+
+static CassError
+CqlBindBytes(CassStatement* statement, size_t index, void* value) {
+ return cass_statement_bind_bytes(
+ statement, index, static_cast<std::vector<cass_byte_t>*>(value)->data(),
+ static_cast<std::vector<cass_byte_t>*>(value)->size());
+}
+
+static CassError
+CqlBindUuid(CassStatement* statement, size_t index, void* value) {
+ return cass_statement_bind_uuid(statement, index,
+ *static_cast<CassUuid*>(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<cass_bool_t*>(data));
+}
+
+static CassError
+CqlGetInt32(const CassValue* value, void* data) {
+ return cass_value_get_int32(value, static_cast<cass_int32_t*>(data));
+}
+
+static CassError
+CqlGetInt64(const CassValue* value, void* data) {
+ return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+}
+
+static CassError
+CqlGetTimestamp(const CassValue* value, void* data) {
+ return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
+}
+
+static CassError
+CqlGetString(const CassValue* value, void* data) {
+ const char* dataValue;
+ size_t sizeValue;
+ CassError cassError = cass_value_get_string(
+ value, static_cast<const char**>(&dataValue), &sizeValue);
+ static_cast<std::string*>(data)->assign(dataValue, dataValue + sizeValue);
+ return cassError;
+}
+
+static CassError
+CqlGetBytes(const CassValue* value, void* data) {
+ const cass_byte_t* dataValue;
+ size_t sizeValue;
+ CassError cassError = cass_value_get_bytes(
+ value, static_cast<const cass_byte_t**>(&dataValue), &sizeValue);
+ static_cast<std::vector<cass_byte_t>*>(data)->assign(dataValue,
+ dataValue + sizeValue);
+ return cassError;
+}
+
+static CassError
+CqlGetUuid(const CassValue* value, void* data) {
+ return cass_value_get_uuid(value, static_cast<CassUuid*>(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<cass_int64_t>(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<time_t>(expire - valid_lifetime);
+}
+
+void
+CqlExchange::createBindForReceive(CqlDataArray& /* data */,
+ const int /* statementIndex = -1 */) {
+ isc_throw(NotImplemented,
+ "CqlExchange::createBindForReceive() not implemented yet.");
+}
+
+CqlDataArray
+CqlExchange::executeRead(const CqlConnection& connection,
+ const CqlDataArray& data,
+ const int statementIndex,
+ const bool single /* = false */,
+ const std::vector<std::string>&
+ parameters /* = std::vector<std::string>() */) {
+ 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<void*>(&applied));
+
+ const ExchangeColumnInfoContainerName& idx = parameters_.get<1>();
+ const ExchangeColumnInfoContainerNameRange& range =
+ idx.equal_range("[applied]");
+ if (std::distance(range.first, range.second) > 0) {
+ CqlCommon::getData(row, (*range.first)->index_, 0, *this, data);
+ }
+ }
+ 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<void*>(&version_));
+ // host_identifier: blob
+ data.add(reinterpret_cast<void*>(&minor_));
+}
+
+void*
+CqlVersionExchange::retrieve() {
+ pair_ = VersionPair(version_, minor_);
+ return reinterpret_cast<void*>(&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<VersionPair*>(
+ *versionCollection.begin()));
+ }
+
+ return result;
+}
+
+} // namespace dhcp
+} // namespace isc
--- /dev/null
+// Copyright (C) 2016 Deutsche Telekom AG.
+//
+// Author: Razvan Becheriu <razvan.becheriu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CQL_EXCHANGE_H
+#define CQL_EXCHANGE_H
+
+#include <cassandra.h>
+#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/sql_common.h>
+#include <exceptions/exceptions.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Binds a C++ object to a Cassandra statement's parameter. Used in all
+/// statements.
+typedef CassError (*CqlBindFunction)(CassStatement* statement,
+ size_t index,
+ void* value);
+
+/// @brief Converts a single Cassandra column value to a C++ object. Used in
+/// SELECT statements.
+typedef CassError (*CqlGetFunction)(const CassValue* value, void* data);
+
+/// @brief Pair containing major and minor versions
+typedef std::pair<uint32_t, uint32_t> VersionPair;
+
+/// @brief Wrapper over the bind and get functions that interface with Cassandra
+struct CqlFunctionData {
+ /// @brief Binds a C++ object to a Cassandra statement's parameter. Used in
+ /// all
+ /// statements.
+ CqlBindFunction cqlBindFunction_;
+ /// @brief Converts a single Cassandra column value to a C++ object. Used in
+ /// SELECT statements.
+ CqlGetFunction cqlGetFunction_;
+};
+
+/// @brief 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<void*>::const_iterator begin() const {
+ return values_.begin();
+ }
+
+ /// @brief Iterator pointing to the past-the-end element
+ std::vector<void*>::const_iterator end() const {
+ return values_.end();
+ }
+
+ /// @brief Vector of pointers to the data values
+ std::vector<void*> values_;
+};
+
+/// @brief 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<std::string>& parameters =
+ std::vector<std::string>());
+
+ /// @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 <version,minor> 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
--- /dev/null
+// Copyright (C) 2016 Deutsche Telekom AG.
+//
+// Author: Andrei Pavel <andrei.pavel@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// 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 <config.h>
+
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp/option_definition.h>
+#include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cql_host_data_source.h>
+#include <dhcpsrv/cql_lease_mgr.h>
+#include <dhcpsrv/db_exceptions.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <util/buffer.h>
+#include <util/optional_value.h>
+
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/unordered_map.hpp>
+
+#include <openssl/md5.h>
+
+#include <list>
+#include <vector>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Host identifier consisting of DUID or hardware address
+typedef std::vector<uint8_t> HostIdentifier;
+
+/// @brief Host identifier converted to Cassandra data type
+typedef std::vector<cass_byte_t> CassHostIdentifier;
+
+/// @brief Host identifier converted to Cassandra data type
+typedef std::vector<cass_byte_t> CassOptionBuffer;
+
+/// @brief key for HostMap containing objects which uniquely identify a
+/// host: host identifier, host identifier type, subnets for IPv4 and IPv6
+typedef boost::tuple<HostIdentifier,
+ Host::IdentifierType,
+ SubnetID,
+ SubnetID,
+ asiolink::IOAddress>
+ HostKey;
+
+/// @brief Map used to merge reservations and options into a single host on
+/// retrieve from database
+typedef boost::unordered_map<HostKey, HostPtr, boost::hash<HostKey> > HostMap;
+
+/// @brief Maximum size of an IPv4 address represented as a text string. 12
+/// digits plus 3 full stops (dots).
+static const size_t ADDRESS4_TEXT_MAX_LENGTH = 15U;
+
+/// @brief Maximum size of an IPv6 address represented as a text string. 32
+/// hexadecimal characters written in 8 groups of four, plus 7 colon
+/// separators.
+static const size_t ADDRESS6_TEXT_MAX_LENGTH = 39U;
+
+/// @brief Maximum length of classes stored in a host_ipv4/6_client_classes
+/// column.
+static const size_t CLIENT_CLASSES_MAX_LENGTH = 255U;
+
+/// @brief Maximum length of the hostname stored in DNS. This length is
+/// restricted by the length of the domain-name carried in the Client FQDN
+/// Option (see RFC4702 and RFC4704).
+static const size_t HOSTNAME_MAX_LENGTH = 255U;
+
+/// @brief Maximum length of option value
+static const size_t OPTION_VALUE_MAX_LENGTH = 4096U;
+
+/// @brief Maximum length of option value specified in textual format
+static const size_t OPTION_FORMATTED_VALUE_MAX_LENGTH = 8192U;
+
+/// @brief Maximum length of option space name
+static const size_t OPTION_SPACE_MAX_LENGTH = 128U;
+
+/// @brief Numeric value representing the last supported identifier. This value
+/// is used to validate whether the identifier type stored in a database is
+/// within bounds of supported identifiers.
+static const cass_int8_t MAX_IDENTIFIER_TYPE =
+ static_cast<cass_int8_t>(Host::IDENT_CIRCUIT_ID);
+
+/// @{
+/// @brief Invalid values in the Cassandra database
+static const char NULL_RESERVED_IPV6_PREFIX_ADDRESS[] = "";
+static const cass_int32_t NULL_RESERVED_IPV6_PREFIX_LENGTH = 0;
+static const cass_int32_t NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE = -1;
+static const cass_int32_t NULL_IAID = -1;
+static const cass_int32_t NULL_OPTION_UNIVERSE = -1;
+static const cass_int32_t NULL_OPTION_CODE = -1;
+static const CassOptionBuffer NULL_OPTION_VALUE = CassOptionBuffer();
+static const char NULL_OPTION_FORMATTED_VALUE[] = "";
+static const char NULL_OPTION_SPACE[] = "";
+static const cass_bool_t NULL_OPTION_IS_PERSISTENT = cass_false;
+static const char NULL_OPTION_CLIENT_CLASS[] = "";
+static const cass_int32_t NULL_OPTION_SUBNET_ID = -1;
+/// @}
+
+/// @brief Invalid reservation used to check for an invalid IPv6Resrv formed
+/// from database values.
+static const IPv6Resrv NULL_IPV6_RESERVATION =
+ IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("::"), 128);
+
+} // anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+/// @{
+/// @brief Statement parameters
+// INSERT_HOST
+static const char* INSERT_HOST_PARAMS[] = {
+ static_cast<const char*>("id"),
+ static_cast<const char*>("host_identifier"),
+ static_cast<const char*>("host_identifier_type"),
+ static_cast<const char*>("host_ipv4_subnet_id"),
+ static_cast<const char*>("host_ipv6_subnet_id"),
+ static_cast<const char*>("host_ipv4_address"),
+ static_cast<const char*>("hostname"),
+ static_cast<const char*>("host_ipv4_client_classes"),
+ static_cast<const char*>("host_ipv6_client_classes"),
+ static_cast<const char*>("reserved_ipv6_prefix_address"),
+ static_cast<const char*>("reserved_ipv6_prefix_length"),
+ static_cast<const char*>("reserved_ipv6_prefix_address_type"),
+ static_cast<const char*>("iaid"),
+ static_cast<const char*>("option_universe"),
+ static_cast<const char*>("option_code"),
+ static_cast<const char*>("option_value"),
+ static_cast<const char*>("option_formatted_value"),
+ static_cast<const char*>("option_space"),
+ static_cast<const char*>("option_is_persistent"),
+ static_cast<const char*>("option_client_class"),
+ static_cast<const char*>("option_subnet_id"),
+ NULL};
+// GET_HOST_BY_HOST_ID
+static const char* GET_HOST_BY_HOST_ID_PARAMS[] = {
+ static_cast<const char*>("host_identifier"),
+ static_cast<const char*>("host_identifier_type"),
+ NULL};
+// GET_HOST_BY_IPV4_ADDRESS
+static const char* GET_HOST_BY_IPV4_ADDRESS_PARAMS[] = {
+ static_cast<const char*>("host_ipv4_address"),
+ NULL};
+// GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID
+static const char* GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID_PARAMS[] = {
+ static_cast<const char*>("host_ipv4_subnet_id"),
+ static_cast<const char*>("host_identifier"),
+ static_cast<const char*>("host_identifier_type"),
+ NULL};
+// GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID,
+static const char* GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID_PARAMS[] = {
+ static_cast<const char*>("host_ipv6_subnet_id"),
+ static_cast<const char*>("host_identifier"),
+ static_cast<const char*>("host_identifier_type"),
+ NULL};
+// GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS,
+static const char* GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS_PARAMS[] = {
+ static_cast<const char*>("host_ipv4_subnet_id"),
+ static_cast<const char*>("host_ipv4_address"),
+ NULL};
+// GET_HOST_BY_IPV6_PREFIX
+static const char* GET_HOST_BY_IPV6_PREFIX_PARAMS[] = {
+ static_cast<const char*>("reserved_ipv6_prefix_address"),
+ static_cast<const char*>("reserved_ipv6_prefix_length"),
+ NULL};
+// GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS
+static const char* GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS_PARAMS[] = {
+ static_cast<const char*>("host_ipv6_subnet_id"),
+ static_cast<const char*>("reserved_ipv6_prefix_address"),
+ NULL};
+// GET_VERSION
+static const char* GET_VERSION_PARAMS[] = {NULL};
+/// @}
+
+/// @brief Prepared CQL statements used by the backend to insert and retrieve
+/// hosts from the database.
+static CqlTaggedStatement tagged_statements[] = {
+ // INSERT_HOST
+ {INSERT_HOST_PARAMS, "INSERT_HOST",
+ "INSERT INTO host_reservations ( "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ ") "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
+ "IF NOT EXISTS "
+ },
+
+ // GET_HOST_BY_HOST_ID
+ {GET_HOST_BY_HOST_ID_PARAMS, "GET_HOST_BY_HOST_ID",
+ "SELECT "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ "FROM host_reservations "
+ "WHERE host_identifier = ? "
+ "AND host_identifier_type = ? "
+ "ALLOW FILTERING "
+ },
+
+ // GET_HOST_BY_IPV4_ADDRESS
+ {GET_HOST_BY_IPV4_ADDRESS_PARAMS, "GET_HOST_BY_IPV4_ADDRESS",
+ "SELECT "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ "FROM host_reservations "
+ "WHERE host_ipv4_address = ? "
+ "ALLOW FILTERING "
+ },
+
+ // GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID
+ {GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID_PARAMS,
+ "GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID",
+ "SELECT "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ "FROM host_reservations "
+ "WHERE host_ipv4_subnet_id = ? "
+ "AND host_identifier = ? "
+ "AND host_identifier_type = ? "
+ "ALLOW FILTERING "
+ },
+
+ // GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID
+ {GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID_PARAMS,
+ "GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID",
+ "SELECT "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ "FROM host_reservations "
+ "WHERE host_ipv6_subnet_id = ? "
+ "AND host_identifier = ? "
+ "AND host_identifier_type = ? "
+ "ALLOW FILTERING "
+ },
+
+ // GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS
+ {GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS_PARAMS,
+ "GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS",
+ "SELECT "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ "FROM host_reservations "
+ "WHERE host_ipv4_subnet_id = ? "
+ "AND host_ipv4_address = ? "
+ "ALLOW FILTERING "
+ },
+
+ // GET_HOST_BY_IPV6_PREFIX
+ {GET_HOST_BY_IPV6_PREFIX_PARAMS, "GET_HOST_BY_IPV6_PREFIX",
+ "SELECT "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ "FROM host_reservations "
+ "WHERE reserved_ipv6_prefix_address = ? "
+ "AND reserved_ipv6_prefix_length = ? "
+ "ALLOW FILTERING "
+ },
+
+ // GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS
+ {GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS_PARAMS,
+ "GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS",
+ "SELECT "
+ "id, "
+ "host_identifier, "
+ "host_identifier_type, "
+ "host_ipv4_subnet_id, "
+ "host_ipv6_subnet_id, "
+ "host_ipv4_address, "
+ "hostname, "
+ "host_ipv4_client_classes, "
+ "host_ipv6_client_classes, "
+ "reserved_ipv6_prefix_address, "
+ "reserved_ipv6_prefix_length, "
+ "reserved_ipv6_prefix_address_type, "
+ "iaid, "
+ "option_universe, "
+ "option_code, "
+ "option_value, "
+ "option_formatted_value, "
+ "option_space, "
+ "option_is_persistent, "
+ "option_client_class, "
+ "option_subnet_id "
+ "FROM host_reservations "
+ "WHERE host_ipv6_subnet_id = ? "
+ "AND reserved_ipv6_prefix_address = ? "
+ "ALLOW FILTERING "
+ },
+
+ // GET_VERSION
+ {GET_VERSION_PARAMS, "GET_VERSION",
+ "SELECT version, minor FROM schema_version"},
+
+ // End of list sentinel
+ {NULL, NULL, NULL}}; // tagged_statements
+
+/// @brief Provides mechanisms for sending and retrieving data from the
+/// host_reservations table.
+class CqlHostExchange : public virtual CqlExchange {
+public:
+ /// @brief Statement Tags
+ ///
+ /// The contents of the enum are indexes into the list of CQL statements
+ enum StatementIndex {
+ // Inserts all options belonging to any reservation from a single host.
+ INSERT_HOST,
+ // Retrieves host information, IPv6 reservations and both IPv4 and IPv6
+ // options associated with the host.
+ GET_HOST_BY_HOST_ID,
+ // Retrieves host information along with the IPv4 options associated
+ // with it.
+ GET_HOST_BY_IPV4_ADDRESS,
+ // Retrieves host information and IPv4 options using subnet identifier
+ // and client's identifier (i.e. hardware address or DUID).
+ GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID,
+ // Retrieves host information, IPv6 reservations and IPv6 options
+ // associated with a host using subnet identifier and client's
+ // identifier (i.e. hardware address or DUID).
+ GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID,
+ // Retrieves host information and IPv4 options for the host using subnet
+ // identifier and IPv4 reservation.
+ GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS,
+ // Retrieves host information, IPv6 reservations and IPv6 options
+ // associated with a host using prefix and prefix length. This query
+ // returns host information for a single host. However, multiple rows
+ // are returned due to left joining IPv6 reservations and IPv6 options.
+ // The number of rows returned is multiplication of number of existing
+ // IPv6 reservations and IPv6 options.
+ GET_HOST_BY_IPV6_PREFIX,
+ // Retrieves host information and IPv6 options for the host using subnet
+ // identifier and IPv6 reservation.
+ GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS,
+ // Get CQL schema version.
+ GET_VERSION,
+ // Number of statements
+ NUM_STATEMENTS
+ };
+
+ /// @brief Constructor
+ ///
+ /// Specifies table columns.
+ CqlHostExchange();
+
+ /// @brief Virtual destructor.
+ virtual ~CqlHostExchange();
+
+ /// @brief Binds member variables to data array to receive @ref Host data.
+ ///
+ /// Creates a bind array to receive @ref Host data from the Cassandra
+ /// database. After data is successfully received, @ref retrieve() can be
+ /// called to retrieve the @ref Host object. Called in @ref
+ /// CqlExchange::executeRead().
+ ///
+ /// @param data array of objects representing data being retrieved
+ /// @param statement_index prepared statement being executed; defaults to an
+ /// invalid statement
+ virtual void createBindForReceive(CqlDataArray& data,
+ const int statement_index = -1);
+
+ /// @brief Binds @ref Host to data array to send data to the Cassandra
+ /// database.
+ ///
+ /// Fills in the bind array for sending data stored in the @ref Host object
+ /// to the database.
+ ///
+ /// @param host @ref Host object being added to the Cassandra database
+ /// @param subnet_id identifier of the subnet to which the host belongs
+ /// @param reservation reservation belonging to the host
+ /// @param option_space option space
+ /// @param option_descriptor structure used to hold option information
+ /// @param data array being filled with data from to the Host object
+ void createBindForSend(const HostPtr& host,
+ const OptionalValue<SubnetID>& subnet_id,
+ const IPv6Resrv* const reservation,
+ const std::string& option_space,
+ const OptionDescriptor& option_descriptor,
+ CqlDataArray& data);
+
+ /// @brief Create unique hash for storage in table id.
+ ///
+ /// Hash function used for creating a pseudo-unique hash from member
+ /// values which uniquely determine an entry in the table. Uses OpenSSL's
+ /// MD5 implementation.
+ ///
+ /// The primary key aggregates: host_ipv4_subnet_id, host_ipv6_subnet_id,
+ /// host_ipv4_address, reserved_ipv6_prefix_address,
+ /// reserved_ipv6_prefix_length, option_code, option_space.
+ cass_int64_t hashIntoId() const;
+
+ /// @brief Copy received data into Host object
+ ///
+ /// Copies information about the host into a newly created @ref Host object
+ /// Called in @ref executeRead after @ref createBindForReceive().
+ ///
+ /// @return Host Pointer to a @ref HostPtr object holding a pointer to the
+ /// @ref Host object returned.
+ virtual void* retrieve();
+
+ /// @brief Creates IPv6 reservation from the data contained in the
+ /// currently processed row.
+ ///
+ /// Called after createBindForReceive().
+ ///
+ /// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
+ const IPv6Resrv retrieveReservation() const;
+
+ /// @brief Retrieves option from members.
+ OptionDescriptorPtr retrieveOption() const;
+
+private:
+ /// Pointer to Host object holding information being inserted into database.
+ HostPtr host_;
+
+ /// @brief Primary key. Aggregates: host_identifier, host_identifier_type,
+ /// reserved_ipv6_prefix_address, reserved_ipv6_prefix_length, option_code,
+ /// option_space.
+ cass_int64_t id_;
+
+ /// @brief Client's identifier (e.g. DUID, HW address) in binary format
+ CassHostIdentifier host_identifier_;
+
+ /// @brief Type of the identifier in the host_identifier_
+ /// This value corresponds to the @ref Host::IdentifierType value.
+ cass_int32_t host_identifier_type_;
+
+ /// @brief IPv4 subnet identifier
+ cass_int32_t host_ipv4_subnet_id_;
+
+ /// @brief Ipv6 subnet identifier
+ cass_int32_t host_ipv6_subnet_id_;
+
+ /// @brief Reserved IPv4 address
+ cass_int32_t host_ipv4_address_;
+
+ /// @brief Name reserved for the host
+ std::string hostname_;
+
+ /// @brief A string holding comma separated list of IPv4 client classes
+ std::string host_ipv4_client_classes_;
+
+ /// @brief A string holding comma separated list of IPv6 client classes
+ std::string host_ipv6_client_classes_;
+
+ /// @brief Address belonging to the reserved IPv6 prefix
+ std::string reserved_ipv6_prefix_address_;
+
+ /// @brief Length of the reserved IPv6 prefix
+ cass_int32_t reserved_ipv6_prefix_length_;
+
+ /// @brief Reserver IPv6 prefix type
+ /// This value corresponds to the @ref Host::IdentifierType value.
+ cass_int32_t reserved_ipv6_prefix_address_type_;
+
+ /// @brief The reservation's IAID
+ cass_int32_t iaid_;
+
+ /// @brief Version of DHCP (i.e. 0 for DHCPv4 and 1 for DHCPv6) to which the
+ /// option belongs.
+ cass_int32_t option_universe_;
+
+ /// @brief Option code
+ cass_int32_t option_code_;
+
+ /// @brief Option value
+ CassOptionBuffer option_value_;
+
+ /// @brief The textual value of an option
+ std::string option_formatted_value_;
+
+ /// @brief Option space name
+ std::string option_space_;
+
+ /// @brief Flag indicating if option is always sent or only on request
+ cass_bool_t option_is_persistent_;
+
+ /// @brief Option client class
+ std::string option_client_class_;
+
+ /// @brief Subnet identifier
+ cass_int32_t option_subnet_id_;
+}; // CqlHostExchange
+
+CqlHostExchange::CqlHostExchange()
+ : host_identifier_type_(0), host_ipv4_subnet_id_(0),
+ host_ipv6_subnet_id_(0), host_ipv4_address_(0),
+ reserved_ipv6_prefix_length_(0), reserved_ipv6_prefix_address_type_(0),
+ iaid_(0), option_universe_(0), option_code_(0),
+ option_is_persistent_(cass_false), option_subnet_id_(0) {
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_INT64)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "host_identifier", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_BYTES)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "host_identifier_type", parameters_.size(),
+ EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "host_ipv4_subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_INT32)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "host_ipv6_subnet_id", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_INT32)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "host_ipv4_address", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_INT32)));
+ 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(
+ "host_ipv4_client_classes", parameters_.size(),
+ EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "host_ipv6_client_classes", parameters_.size(),
+ EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "reserved_ipv6_prefix_address", parameters_.size(),
+ EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "reserved_ipv6_prefix_length", parameters_.size(),
+ EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_INT32)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "reserved_ipv6_prefix_address_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(
+ "option_universe", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_INT32)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "option_code", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_INT32)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "option_value", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_BYTES)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "option_formatted_value", parameters_.size(),
+ EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_STRING)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "option_space", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_STRING)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "option_is_persistent", parameters_.size(),
+ EXCHANGE_DATA_TYPE_IO_IN_OUT, EXCHANGE_DATA_TYPE_BOOL)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "option_client_class", parameters_.size(), EXCHANGE_DATA_TYPE_IO_IN_OUT,
+ EXCHANGE_DATA_TYPE_STRING)));
+ parameters_.push_back(ExchangeColumnInfoPtr(new ExchangeColumnInfo(
+ "option_subnet_id", 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() == 22U);
+}
+
+CqlHostExchange::~CqlHostExchange() {
+}
+
+void
+CqlHostExchange::createBindForReceive(CqlDataArray& data,
+ const int /* statement_index = -1 */) {
+ // Start with a fresh array.
+ data.clear();
+ // id: blob
+ data.add(reinterpret_cast<void*>(&id_));
+ // host_identifier: blob
+ data.add(reinterpret_cast<void*>(&host_identifier_));
+ // host_identifier_type: int
+ data.add(reinterpret_cast<void*>(&host_identifier_type_));
+ // host_ipv4_subnet_id: int
+ data.add(reinterpret_cast<void*>(&host_ipv4_subnet_id_));
+ // host_ipv6_subnet_id: int
+ data.add(reinterpret_cast<void*>(&host_ipv6_subnet_id_));
+ // host_ipv4_address: int
+ data.add(reinterpret_cast<void*>(&host_ipv4_address_));
+ // hostname: text
+ data.add(reinterpret_cast<void*>(&hostname_));
+ // host_ipv4_client_classes: text
+ data.add(reinterpret_cast<void*>(&host_ipv4_client_classes_));
+ // host_ipv6_client_classes: text
+ data.add(reinterpret_cast<void*>(&host_ipv6_client_classes_));
+ // reserved_ipv6_prefix_address: text
+ data.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_address_));
+ // reserved_ipv6_prefix_length: int
+ data.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_length_));
+ // reserved_ipv6_prefix_length: int
+ data.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_address_type_));
+ // iaid: int
+ data.add(reinterpret_cast<void*>(&iaid_));
+ // option_universe: int
+ data.add(reinterpret_cast<void*>(&option_universe_));
+ // option_code: int
+ data.add(reinterpret_cast<void*>(&option_code_));
+ // option_value: blob
+ data.add(reinterpret_cast<void*>(&option_value_));
+ // option_formatted_value: text
+ data.add(reinterpret_cast<void*>(&option_formatted_value_));
+ // option_space: text
+ data.add(reinterpret_cast<void*>(&option_space_));
+ // option_is_persistent: boolean
+ data.add(reinterpret_cast<void*>(&option_is_persistent_));
+ // option_client_class: text
+ data.add(reinterpret_cast<void*>(&option_client_class_));
+ // option_subnet_id: int
+ data.add(reinterpret_cast<void*>(&option_subnet_id_));
+}
+
+void
+CqlHostExchange::createBindForSend(const HostPtr& host,
+ const OptionalValue<SubnetID>& subnet_id,
+ const IPv6Resrv* const reservation,
+ const std::string& option_space,
+ const OptionDescriptor& option_descriptor,
+ CqlDataArray& data) {
+ // Store host object to ensure it remains valid.
+ host_ = host;
+
+ // Set up the structures for the various components of the host
+ // structure.
+ try {
+ // host_identifier: blob
+ // Convert from std::vector<uint8_t> to std::vector<cass_byte_t>.
+ HostIdentifier host_identifier = host->getIdentifier();
+ host_identifier_ = CassHostIdentifier(host_identifier.begin(),
+ host_identifier.end());
+ if (host_identifier_.size() > DUID::MAX_DUID_LEN) {
+ isc_throw(BadValue, "host identifier "
+ << host_identifier_.data() << " of length "
+ << host_identifier_.size()
+ << " is greater than allowed of "
+ << DUID::MAX_DUID_LEN);
+ }
+
+ // host_identifier_type: int
+ host_identifier_type_ =
+ static_cast<cass_int32_t>(host->getIdentifierType());
+ if (host_identifier_type_ > MAX_IDENTIFIER_TYPE) {
+ isc_throw(BadValue, "invalid host identifier type returned: "
+ << host_identifier_type_);
+ }
+
+ // host_ipv4_subnet_id: int
+ host_ipv4_subnet_id_ =
+ static_cast<cass_int32_t>(host->getIPv4SubnetID());
+
+ // host_ipv6_subnet_id: int
+ host_ipv6_subnet_id_ =
+ static_cast<cass_int32_t>(host->getIPv6SubnetID());
+
+ // host_ipv4_address: int
+ host_ipv4_address_ =
+ static_cast<cass_int32_t>(host->getIPv4Reservation().toUint32());
+
+ // hostname: text
+ hostname_ = host->getHostname();
+ if (hostname_.size() > HOSTNAME_MAX_LENGTH) {
+ isc_throw(BadValue, "hostname " << hostname_ << " of length "
+ << hostname_.size()
+ << " is greater than allowed of "
+ << HOSTNAME_MAX_LENGTH);
+ }
+
+ // host_ipv4_client_classes: text
+ host_ipv4_client_classes_ = host->getClientClasses4().toText(",");
+ if (host_ipv4_client_classes_.size() > CLIENT_CLASSES_MAX_LENGTH) {
+ isc_throw(BadValue, "IPv4 client classes "
+ << host_ipv4_client_classes_
+ << " of length "
+ << host_ipv4_client_classes_.size()
+ << " is greater than allowed of "
+ << CLIENT_CLASSES_MAX_LENGTH);
+ }
+
+ // host_ipv6_client_classes: text
+ host_ipv6_client_classes_ = host->getClientClasses6().toText(",");
+ if (host_ipv6_client_classes_.size() > CLIENT_CLASSES_MAX_LENGTH) {
+ isc_throw(BadValue, "IPv6 client classes "
+ << host_ipv6_client_classes_
+ << " of length "
+ << host_ipv6_client_classes_.size()
+ << " is greater than allowed of "
+ << CLIENT_CLASSES_MAX_LENGTH);
+ }
+
+ if (reservation == NULL) {
+ reserved_ipv6_prefix_address_ = NULL_RESERVED_IPV6_PREFIX_ADDRESS;
+ reserved_ipv6_prefix_length_ = NULL_RESERVED_IPV6_PREFIX_LENGTH;
+ reserved_ipv6_prefix_address_type_ =
+ NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE;
+ iaid_ = NULL_IAID;
+ } else {
+ // reserved_ipv6_prefix_address: text
+ reserved_ipv6_prefix_address_ = reservation->getPrefix().toText();
+
+ // reserved_ipv6_prefix_length: int
+ reserved_ipv6_prefix_length_ =
+ static_cast<cass_int32_t>(reservation->getPrefixLen());
+
+ // reserved_ipv6_prefix_address: int
+ reserved_ipv6_prefix_address_type_ =
+ reservation->getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
+
+ // iaid: int
+ /// @todo: We don't support iaid in the IPv6Resrv yet.
+ iaid_ = 0;
+ }
+
+ if (option_descriptor.option_ == NULL) {
+ option_universe_ = NULL_OPTION_UNIVERSE;
+ option_code_ = NULL_OPTION_CODE;
+ option_value_ = NULL_OPTION_VALUE;
+ option_formatted_value_ = NULL_OPTION_FORMATTED_VALUE;
+ option_space_ = NULL_OPTION_SPACE;
+ option_is_persistent_ = NULL_OPTION_IS_PERSISTENT;
+ option_client_class_ = NULL_OPTION_CLIENT_CLASS;
+ option_subnet_id_ = NULL_OPTION_SUBNET_ID;
+ } else {
+ // option_universe: int
+ option_universe_ = option_descriptor.option_->getUniverse();
+
+ // option_code: int
+ option_code_ = option_descriptor.option_->getType();
+
+ // option_value: blob
+ // option_formatted_value: text
+ if (option_descriptor.formatted_value_.empty()) {
+ if (option_descriptor.option_->len() >
+ option_descriptor.option_->getHeaderLen()) {
+ // The formatted_value is empty and the option value
+ // is not empty so we need to prepare on-wire format
+ // for the option and store it in the database as a
+ // blob.
+ OutputBuffer buffer(option_descriptor.option_->len());
+ option_descriptor.option_->pack(buffer);
+ const char* buffer_ptr =
+ static_cast<const char*>(buffer.getData());
+ option_value_.assign(
+ buffer_ptr + option_descriptor.option_->getHeaderLen(),
+ buffer_ptr + buffer.getLength());
+ } else {
+ option_value_.clear();
+ }
+ option_formatted_value_.clear();
+ } else {
+ option_value_.clear();
+ option_formatted_value_ = option_descriptor.formatted_value_;
+ }
+
+ // option_space: text
+ option_space_ = option_space;
+
+ // option_is_persistent: boolean
+ option_is_persistent_ =
+ option_descriptor.persistent_ ? cass_true : cass_false;
+
+ // option_client_class: text
+ /// @todo Assign actual value to client class string.
+ option_client_class_.clear();
+
+ // option_subnet_id: int
+ if (subnet_id.isSpecified()) {
+ option_subnet_id_ = subnet_id;
+ } else {
+ option_subnet_id_ = 0;
+ }
+ }
+
+ // id: bigint
+ id_ = hashIntoId();
+
+ // Add all parameters to bind array.
+ data.clear();
+ data.add(reinterpret_cast<void*>(&id_));
+ data.add(reinterpret_cast<void*>(&host_identifier_));
+ data.add(reinterpret_cast<void*>(&host_identifier_type_));
+ data.add(reinterpret_cast<void*>(&host_ipv4_subnet_id_));
+ data.add(reinterpret_cast<void*>(&host_ipv6_subnet_id_));
+ data.add(reinterpret_cast<void*>(&host_ipv4_address_));
+ data.add(reinterpret_cast<void*>(&hostname_));
+ data.add(reinterpret_cast<void*>(&host_ipv4_client_classes_));
+ data.add(reinterpret_cast<void*>(&host_ipv6_client_classes_));
+ data.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_address_));
+ data.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_length_));
+ data.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_address_type_));
+ data.add(reinterpret_cast<void*>(&iaid_));
+ data.add(reinterpret_cast<void*>(&option_universe_));
+ data.add(reinterpret_cast<void*>(&option_code_));
+ data.add(reinterpret_cast<void*>(&option_value_));
+ data.add(reinterpret_cast<void*>(&option_formatted_value_));
+ data.add(reinterpret_cast<void*>(&option_space_));
+ data.add(reinterpret_cast<void*>(&option_is_persistent_));
+ data.add(reinterpret_cast<void*>(&option_client_class_));
+ data.add(reinterpret_cast<void*>(&option_subnet_id_));
+ } catch (const std::exception& ex) {
+ isc_throw(DbOperationError, "could not create bind array from host "
+ << host->getHostname()
+ << ", reason: " << ex.what());
+ }
+}
+
+uint64_t
+md5Hash(const std::string& input) {
+ // Prepare structures for MD5().
+ const size_t word_size = MD5_DIGEST_LENGTH / sizeof(uint64_t);
+ uint64_t hash[word_size];
+ unsigned char* digest = reinterpret_cast<unsigned char*>(hash);
+ unsigned char* string =
+ reinterpret_cast<unsigned char*>(const_cast<char*>(input.c_str()));
+ std::fill(hash, hash + word_size, 0);
+
+ // Get MD5 hash value.
+ MD5(string, input.size(), digest);
+
+ // Return the first part of the hash value which still retains all
+ // properties of the full hash value.
+ return hash[0];
+}
+
+cass_int64_t
+CqlHostExchange::hashIntoId() const {
+ // Allocates a fixed maximum length in the stringstream for each
+ // aggregated field to avoid collisions between distinct entries.
+
+ // Get key.
+ std::stringstream key_stream;
+ key_stream << std::setw(10) << std::setfill('0') << host_ipv4_subnet_id_;
+ key_stream << std::setw(10) << std::setfill('0') << host_ipv6_subnet_id_;
+ key_stream << std::setw(ADDRESS4_TEXT_MAX_LENGTH) << host_ipv4_address_;
+ key_stream << std::setw(ADDRESS6_TEXT_MAX_LENGTH)
+ << reserved_ipv6_prefix_address_;
+ key_stream << std::setw(4) << std::setfill('0')
+ << reserved_ipv6_prefix_length_;
+ key_stream << std::setw(4) << std::setfill('0') << option_code_;
+ key_stream << std::setw(OPTION_SPACE_MAX_LENGTH) << option_space_;
+ const std::string key = key_stream.str();
+
+ const uint64_t md5 = md5Hash(key);
+
+ return static_cast<cass_int64_t>(md5);
+}
+
+void*
+CqlHostExchange::retrieve() {
+ uint64_t id = static_cast<uint64_t>(id_);
+
+ HostIdentifier host_identifier =
+ HostIdentifier(host_identifier_.begin(), host_identifier_.end());
+
+ // Set the host identifier type in a variable of the appropriate data type.
+ Host::IdentifierType host_identifier_type =
+ static_cast<Host::IdentifierType>(host_identifier_type_);
+
+ // Set IPv4 subnet ID to the value returned.
+ SubnetID ipv4_subnet_id = static_cast<SubnetID>(host_ipv4_subnet_id_);
+
+ // Set IPv6 subnet ID to the value returned.
+ SubnetID ipv6_subnet_id = static_cast<SubnetID>(host_ipv6_subnet_id_);
+
+ // Set IPv4 address reservation.
+ asiolink::IOAddress ipv4_reservation =
+ asiolink::IOAddress(host_ipv4_address_);
+
+ Host* host = new Host(host_identifier.data(), host_identifier.size(),
+ host_identifier_type, ipv4_subnet_id, ipv6_subnet_id,
+ ipv4_reservation, hostname_,
+ host_ipv4_client_classes_, host_ipv6_client_classes_);
+ host->setHostId(id);
+
+ const IPv6Resrv reservation = retrieveReservation();
+ if (reservation != NULL_IPV6_RESERVATION &&
+ !host->hasReservation(reservation)) {
+ host->addReservation(reservation);
+ }
+
+ OptionDescriptorPtr option_descriptor_ptr = retrieveOption();
+ if (option_descriptor_ptr) {
+ if (option_descriptor_ptr->option_->getUniverse() == Option::V4) {
+ host->getCfgOption4()->add(*option_descriptor_ptr, option_space_);
+ } else if (option_descriptor_ptr->option_->getUniverse() ==
+ Option::V6) {
+ host->getCfgOption6()->add(*option_descriptor_ptr, option_space_);
+ }
+ }
+
+ return reinterpret_cast<void*>(host);
+}
+
+const IPv6Resrv
+CqlHostExchange::retrieveReservation() const {
+ // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
+ IPv6Resrv::Type type;
+ switch (reserved_ipv6_prefix_address_type_) {
+ case 0:
+ type = IPv6Resrv::TYPE_NA;
+ break;
+ case 2:
+ type = IPv6Resrv::TYPE_PD;
+ break;
+ case NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE:
+ return NULL_IPV6_RESERVATION;
+ default:
+ isc_throw(BadValue,
+ "invalid IPv6 reservation type returned: "
+ << reserved_ipv6_prefix_address_type_
+ << ". Only 0 (IA_NA) or 2 (IA_PD) are allowed.");
+ }
+
+ return IPv6Resrv(type, IOAddress(reserved_ipv6_prefix_address_),
+ reserved_ipv6_prefix_length_);
+}
+
+OptionDescriptorPtr
+CqlHostExchange::retrieveOption() const {
+ // Options are held in a binary or textual format in the database.
+ // This is similar to having an option specified in a server
+ // configuration file. Such option is converted to appropriate C++
+ // class, using option definition. Thus, we need to find the
+ // option definition for this option code and option space.
+
+ // If the option space is a standard DHCPv4 or DHCPv6 option space,
+ // this is most likely a standard option, for which we have a
+ // definition created within libdhcp++.
+ if (option_space_.empty() || option_universe_ == NULL_OPTION_UNIVERSE) {
+ return OptionDescriptorPtr();
+ }
+
+ OptionDefinitionPtr option_definition_ptr =
+ LibDHCP::getOptionDef(option_space_, option_code_);
+
+ // Otherwise, we may check if this an option encapsulated within the
+ // vendor space.
+ if (!option_definition_ptr && option_space_ != DHCP4_OPTION_SPACE &&
+ option_space_ != DHCP6_OPTION_SPACE) {
+ uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space_);
+ if (vendor_id > 0) {
+ option_definition_ptr = LibDHCP::getVendorOptionDef(
+ static_cast<Option::Universe>(option_universe_), vendor_id,
+ option_code_);
+ }
+ }
+
+ // In all other cases, we use runtime option definitions, which
+ // should be also registered within the libdhcp++.
+ if (!option_definition_ptr) {
+ option_definition_ptr =
+ LibDHCP::getRuntimeOptionDef(option_space_, option_code_);
+ }
+
+ OptionPtr option;
+ if (!option_definition_ptr) {
+ // If no definition found, we use generic option type.
+ OptionBuffer option_buffer(option_value_.begin(), option_value_.end());
+ option.reset(new Option(static_cast<Option::Universe>(option_universe_),
+ static_cast<uint16_t>(option_code_),
+ option_buffer.begin(), option_buffer.end()));
+ } else {
+ // The option value may be specified in textual or binary format in the
+ // database. If formatted_value is empty, the binary format is used.
+ // Depending on the format we use a different variant of @ref
+ // optionFactory().
+ if (option_formatted_value_.empty()) {
+ OptionBuffer option_buffer(option_value_.begin(),
+ option_value_.end());
+ option = option_definition_ptr->optionFactory(
+ static_cast<Option::Universe>(option_universe_),
+ static_cast<uint16_t>(option_code_), option_buffer.begin(),
+ option_buffer.end());
+ } else {
+ // Spit the value specified in comma separated values format.
+ std::vector<std::string> split_vector;
+ boost::split(split_vector, option_formatted_value_,
+ boost::is_any_of(","));
+ option = option_definition_ptr->optionFactory(
+ static_cast<Option::Universe>(option_universe_),
+ static_cast<uint16_t>(option_code_), split_vector);
+ }
+ }
+
+ return OptionDescriptorPtr(new OptionDescriptor(
+ option, option_is_persistent_, option_formatted_value_));
+}
+
+/// @brief Implementation of the @ref CqlHostDataSource.
+class CqlHostDataSourceImpl {
+public:
+ /// @brief Constructor.
+ ///
+ /// This constructor opens database connection and initializes prepared
+ /// statements used in the queries.
+ explicit CqlHostDataSourceImpl(
+ const CqlConnection::ParameterMap& parameters);
+
+ /// @brief Destructor.
+ virtual ~CqlHostDataSourceImpl();
+
+ /// @brief Implementation of @ref CqlHostDataSource::add()
+ virtual void add(const HostPtr& host);
+
+ /// @brief Implementation of @ref CqlHostDataSource::get4()
+ virtual ConstHostPtr get4(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::get4()
+ virtual ConstHostPtr get4(const SubnetID& subnet_id,
+ const HWAddrPtr& hwaddr,
+ const DuidPtr& duid = DuidPtr()) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::get4()
+ virtual ConstHostPtr get4(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::get6()
+ virtual ConstHostPtr get6(const asiolink::IOAddress& prefix,
+ const uint8_t prefix_len) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::get6()
+ virtual ConstHostPtr get6(const SubnetID& subnet_id,
+ const DuidPtr& duid,
+ const HWAddrPtr& hwaddr = HWAddrPtr()) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::get6()
+ virtual ConstHostPtr get6(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::get6()
+ virtual ConstHostPtr get6(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::getAll
+ virtual ConstHostCollection getAll(const HWAddrPtr& hwaddr,
+ const DuidPtr& duid = DuidPtr()) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::getAll()
+ virtual ConstHostCollection
+ getAll(const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::getAll4()
+ virtual ConstHostCollection
+ getAll4(const asiolink::IOAddress& address) const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::getName()
+ virtual std::string getName() const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::getVersion()
+ virtual VersionPair getVersion() const;
+
+ /// @brief Implementation of @ref CqlHostDataSource::commit()
+ virtual void commit();
+
+ /// @brief Implementation of @ref CqlHostDataSource::rollback()
+ virtual void rollback();
+
+protected:
+ /// @brief Adds any options found in the @ref Host object to a separate
+ /// table entry.
+ ///
+ /// @param host @ref Host object from which options are retrieved and
+ /// inserted into the Cassandra database
+ /// @param reservation reservation for the current denormalized table entry
+ /// @param option_spaces list of option spaces to search for
+ /// @param cfg_option option configuration used to match option spaces in
+ /// order to obtain actual options
+ virtual void insertHostWithOptions(
+ const HostPtr& host,
+ const IPv6Resrv* const reservation = NULL,
+ const std::list<std::string>& option_spaces = std::list<std::string>(),
+ const ConstCfgOptionPtr cfg_option = ConstCfgOptionPtr());
+
+ /// @brief Adds any reservations found in the @ref Host object to a separate
+ /// table entry.
+ ///
+ /// @param host @ref Host object from which reservations are retrieved and
+ /// inserted into the Cassandra database
+ /// @param reservation reservation for the current denormalized table entry
+ /// @param option_spaces4 list of option spaces for universe Option::V4 to
+ /// search in
+ /// @param cfg_option4 option configuration for universe Option::V4 used to
+ /// match option spaces in order to obtain actual options
+ /// @param option_spaces6 list of option spaces for universe Option::V6 to
+ /// search in
+ /// @param cfg_option6 option configuration for universe Option::V6 used to
+ /// match option spaces in order to obtain actual options
+ virtual void
+ insertHostWithReservations(const HostPtr& host,
+ const IPv6Resrv* const reservation,
+ const std::list<std::string>& option_spaces4,
+ const ConstCfgOptionPtr cfg_option4,
+ const std::list<std::string>& option_spaces6,
+ const ConstCfgOptionPtr cfg_option6);
+
+ /// @brief Retrieves a single host.
+ ///
+ /// Calls @ref getHostCollection() and checks if a single host was returned.
+ ///
+ /// @param where_values array of bound objects used to filter the results
+ /// @param statement_index prepared statement being executed
+ ///
+ /// @return one host or a null pointer to a host
+ ///
+ /// @throw MultipleRecords exception if two or more hosts are returned
+ virtual ConstHostPtr
+ getHost(const CqlDataArray& where_values,
+ const CqlHostExchange::StatementIndex statement_index) const;
+
+ /// @brief Retrieves a collection of hosts.
+ ///
+ /// Calls @ref CqlExchange::executeRead().
+ ///
+ /// @param where_values array of bound objects used to filter the results
+ /// @param statement_index prepared statement being executed
+ ///
+ /// @return a collection of hosts containing one or more hosts
+ virtual ConstHostCollection getHostCollection(
+ const CqlDataArray& where_values,
+ const CqlHostExchange::StatementIndex statement_index) const;
+
+ /// @brief Inserts a single host.
+ ///
+ /// All information is available here. Calls @ref
+ /// CqlExchange::executeWrite().
+ ///
+ /// @param host @ref Host object from which options are retrieved and
+ /// inserted into the Cassandra database
+ /// @param subnet_id identifier of the subnet to which the host belongs
+ /// @param reservation reservation for the current denormalized table entry
+ /// @param option_space option space for the current denormalized table
+ /// entry's option
+ /// @param option_descriptor option descriptor containing information for
+ /// the current denormalized table entry's option
+ virtual void insertHost(
+ const HostPtr& host,
+ const OptionalValue<SubnetID>& subnet_id = OptionalValue<SubnetID>(),
+ const IPv6Resrv* const reservation = NULL,
+ const std::string& option_space = NULL_OPTION_SPACE,
+ const OptionDescriptor& option_descriptor = OptionDescriptor(false));
+
+ /// @brief Merge denormalized table entries that belong to the same host
+ /// into a single host, one by one.
+ ///
+ /// @param target_host host which can contain multiple reservations and
+ /// options to which other distinct reservations and options are added.
+ /// @param source_host host that is being search for new reservations and
+ /// options that will be merged into the old host.
+ virtual void mergeHosts(HostPtr& target_host,
+ const ConstHostPtr& source_host) const;
+
+private:
+ /// @brief pointer to the exchange responsible for trading host information
+ boost::shared_ptr<CqlHostExchange> host_exchange_;
+ /// @brief pointer to the exchange responsible for retrieving schema version
+ boost::shared_ptr<CqlVersionExchange> version_exchange_;
+
+ /// @brief CQL connection
+ mutable CqlConnection dbconn_;
+}; // class CqlHostDataSourceImpl
+
+
+/// @brief hash function for HostMap
+///
+/// @param key being hashed
+///
+/// @return hash value
+std::size_t
+hash_value(const HostKey& key) {
+ // Get key.
+ std::stringstream key_stream;
+ HostIdentifier host_identifier = key.get<0>();
+ key_stream << std::setw(DUID::MAX_DUID_LEN) << std::setfill('0')
+ << DUID(host_identifier).toText();
+ key_stream << std::setw(2) << std::setfill('0') << key.get<1>();
+ key_stream << std::setw(10) << std::setfill('0') << key.get<2>();
+ key_stream << std::setw(10) << std::setfill('0') << key.get<3>();
+ key_stream << std::setw(ADDRESS4_TEXT_MAX_LENGTH) << std::setfill('0')
+ << key.get<4>();
+ const std::string key_string = key_stream.str();
+
+ const uint64_t md5 = md5Hash(key_string);
+
+ return static_cast<std::size_t>(md5);
+}
+
+/// @brief equals operator for HostKey
+///
+/// @param key1 left hand side operand
+/// @param key2 right hand side operand
+///
+/// @return true if keys are equal. Deep comparison is made.
+bool
+operator==(const HostKey& key1, const HostKey& key2) {
+ return key1.get<0>() == key2.get<0>() && key1.get<1>() == key2.get<1>() &&
+ key1.get<2>() == key2.get<2>() && key1.get<3>() == key2.get<3>() &&
+ key1.get<4>() == key2.get<4>();
+}
+
+CqlHostDataSourceImpl::CqlHostDataSourceImpl(
+ const CqlConnection::ParameterMap& parameters)
+ : host_exchange_(new CqlHostExchange()),
+ version_exchange_(new CqlVersionExchange()), dbconn_(parameters) {
+ // Open the database.
+ dbconn_.openDatabase();
+ // Prepare all possible statements.
+ dbconn_.prepareStatements(tagged_statements);
+}
+
+CqlHostDataSourceImpl::~CqlHostDataSourceImpl() {
+ // There is no need to close the database in this destructor: it is
+ // closed in the destructor of the dbconn_ member variable.
+}
+
+void
+CqlHostDataSourceImpl::add(const HostPtr& host) {
+ // Start transaction.
+ CqlTransaction transaction(dbconn_);
+
+ // Get option space names and vendor space names and combine them within a
+ // single list.
+ // For IPv4:
+ ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
+ std::list<std::string> option_spaces4 = cfg_option4->getOptionSpaceNames();
+ std::list<std::string> vendor_spaces4 =
+ cfg_option4->getVendorIdsSpaceNames();
+ option_spaces4.insert(option_spaces4.end(), vendor_spaces4.begin(),
+ vendor_spaces4.end());
+
+ // For IPv6:
+ ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
+ std::list<std::string> option_spaces6 = cfg_option6->getOptionSpaceNames();
+ std::list<std::string> vendor_spaces6 =
+ cfg_option6->getVendorIdsSpaceNames();
+ option_spaces6.insert(option_spaces6.end(), vendor_spaces6.begin(),
+ vendor_spaces6.end());
+
+ // For every IPv6 reservation, add each of their options to the database.
+ IPv6ResrvRange reservations = host->getIPv6Reservations();
+ if (std::distance(reservations.first, reservations.second) > 0) {
+ for (IPv6ResrvIterator it = reservations.first;
+ it != reservations.second; ++it) {
+ insertHostWithReservations(host, &it->second, option_spaces4,
+ cfg_option4, option_spaces6,
+ cfg_option6);
+ }
+ } else {
+ // If host has no reservation, add entries with null reservation.
+ // Options could still be present.
+ insertHostWithReservations(host, NULL, option_spaces4, cfg_option4,
+ option_spaces6, cfg_option6);
+ }
+ transaction.commit();
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::get4(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const {
+ if (!address.isV4()) {
+ isc_throw(BadValue, "CqlHostDataSource::get4(2): wrong address type, "
+ "address supplied is not an IPv4 address");
+ }
+
+ // Convert to CQL data types.
+ cass_int32_t host_ipv4_subnet_id = static_cast<cass_int32_t>(subnet_id);
+ cass_int32_t host_ipv4_address =
+ static_cast<cass_int32_t>(address.toUint32());
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&host_ipv4_subnet_id));
+ where_values.add(reinterpret_cast<void*>(&host_ipv4_address));
+
+ // Run statement.
+ return getHost(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_AND_ADDRESS);
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::get4(const SubnetID& subnet_id,
+ const HWAddrPtr& hwaddr,
+ const DuidPtr& duid) const {
+ /// @todo: Rethink the logic in BaseHostDataSource::get4(subnet, hwaddr,
+ /// duid)
+ if (hwaddr && duid) {
+ isc_throw(BadValue, "CqlHostDataSource::get4(3) called with both "
+ "hwaddr and duid, only one of them is allowed");
+ } else if (!hwaddr && !duid) {
+ isc_throw(BadValue, "CqlHostDataSource::get4(3) called with neither "
+ "hwaddr or duid specified, one of them is "
+ "required");
+ } else if (duid) {
+ HostIdentifier duid_vector = duid->getDuid();
+ // Delegate to get4(4).
+ return get4(subnet_id, Host::IDENT_DUID, duid_vector.data(),
+ duid_vector.size());
+ } else if (hwaddr) {
+ // Delegate to get4(4).
+ return get4(subnet_id, Host::IDENT_HWADDR, hwaddr->hwaddr_.data(),
+ hwaddr->hwaddr_.size());
+ }
+ return ConstHostPtr();
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::get4(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ // Convert to CQL data types.
+ cass_int32_t host_ipv4_subnet_id = static_cast<cass_int32_t>(subnet_id);
+ CassHostIdentifier host_identifier(identifier_begin,
+ identifier_begin + identifier_len);
+ cass_int32_t host_identifier_type =
+ static_cast<cass_int32_t>(identifier_type);
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&host_ipv4_subnet_id));
+ where_values.add(reinterpret_cast<void*>(&host_identifier));
+ where_values.add(reinterpret_cast<void*>(&host_identifier_type));
+
+ // Run statement.
+ return getHost(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_IPV4_SUBNET_ID_AND_HOST_ID);
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::get6(const asiolink::IOAddress& prefix,
+ const uint8_t prefix_len) const {
+ // Convert to CQL data types.
+ std::string reserved_ipv6_prefix_address = prefix.toText();
+ cass_int32_t reserved_ipv6_prefix_length = prefix_len;
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_address));
+ where_values.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_length));
+
+ // Get host id.
+ ConstHostPtr host = getHost(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_IPV6_PREFIX);
+
+ if (host == ConstHostPtr()) {
+ return ConstHostPtr();
+ }
+
+ // Get host.
+ HostIdentifier host_identifier = host->getIdentifier();
+ ConstHostCollection collection =
+ getAll(host->getIdentifierType(), host_identifier.data(),
+ host_identifier.size());
+
+ if (collection.empty()) {
+ return ConstHostPtr();
+ }
+
+ if (collection.size() >= 2U) {
+ isc_throw(
+ MultipleRecords,
+ "CqlHostDataSource::get6(2): multiple records were found in the "
+ "database where only one was expected for statement "
+ << dbconn_
+ .tagged_statements_[CqlHostExchange::GET_HOST_BY_HOST_ID]
+ .name_);
+ }
+
+ return *collection.begin();
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::get6(const SubnetID& subnet_id,
+ const DuidPtr& duid,
+ const HWAddrPtr& hwaddr) const {
+ /// @todo: Rethink the logic in BaseHostDataSource::get6(subnet, hwaddr,
+ /// duid)
+ if (hwaddr && duid) {
+ isc_throw(BadValue, "CqlHostDataSource::get6(3): both hardware address "
+ "and DUID are specified, only one of them is "
+ "allowed");
+ } else if (!hwaddr && !duid) {
+ isc_throw(BadValue, "CqlHostDataSource::get6(3): both hardware address "
+ "and DUID are specified, one of them is required");
+ } else if (duid) {
+ HostIdentifier duid_vector = duid->getDuid();
+ // Delegate to get6(4).
+ return get6(subnet_id, Host::IDENT_DUID, duid_vector.data(),
+ duid_vector.size());
+ } else if (hwaddr) {
+ // Delegate to get6(4).
+ return get6(subnet_id, Host::IDENT_HWADDR, hwaddr->hwaddr_.data(),
+ hwaddr->hwaddr_.size());
+ }
+ return ConstHostPtr();
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::get6(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ // Convert to CQL data types.
+ cass_int32_t host_ipv6_subnet_id = static_cast<cass_int32_t>(subnet_id);
+ CassHostIdentifier host_identifier(identifier_begin,
+ identifier_begin + identifier_len);
+ cass_int32_t host_identifier_type =
+ static_cast<cass_int32_t>(identifier_type);
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&host_ipv6_subnet_id));
+ where_values.add(reinterpret_cast<void*>(&host_identifier));
+ where_values.add(reinterpret_cast<void*>(&host_identifier_type));
+
+ // Run statement.
+ return getHost(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_AND_HOST_ID);
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::get6(const SubnetID& subnet_id,
+ const IOAddress& address) const {
+ // Convert to CQL data types.
+ cass_int32_t host_ipv6_subnet_id = static_cast<cass_int32_t>(subnet_id);
+ std::string reserved_ipv6_prefix_address = address.toText();
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&host_ipv6_subnet_id));
+ where_values.add(reinterpret_cast<void*>(&reserved_ipv6_prefix_address));
+
+ // Run statement.
+ return getHost(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_IPV6_SUBNET_ID_AND_ADDRESS);
+}
+
+ConstHostCollection
+CqlHostDataSourceImpl::getAll(const HWAddrPtr& hwaddr,
+ const DuidPtr& duid) const {
+ if (!duid && !hwaddr) {
+ return ConstHostCollection();
+ }
+
+ // Convert to CQL data types.
+ CassHostIdentifier host_identifier;
+ cass_int32_t host_identifier_type;
+ if (duid) {
+ HostIdentifier duid_vector = duid->getDuid();
+ host_identifier =
+ CassHostIdentifier(duid_vector.begin(), duid_vector.end());
+ host_identifier_type = static_cast<cass_int32_t>(Host::IDENT_DUID);
+ } else if (hwaddr) {
+ host_identifier = CassHostIdentifier(hwaddr->hwaddr_.begin(),
+ hwaddr->hwaddr_.end());
+ host_identifier_type = static_cast<cass_int32_t>(Host::IDENT_HWADDR);
+ }
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&host_identifier));
+ where_values.add(reinterpret_cast<void*>(&host_identifier_type));
+
+ // Run statement.
+ return getHostCollection(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_HOST_ID);
+}
+
+ConstHostCollection
+CqlHostDataSourceImpl::getAll(const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ // Convert to CQL data types.
+ CassHostIdentifier host_identifier(identifier_begin,
+ identifier_begin + identifier_len);
+ cass_int32_t host_identifier_type =
+ static_cast<cass_int32_t>(identifier_type);
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&host_identifier));
+ where_values.add(reinterpret_cast<void*>(&host_identifier_type));
+
+ // Run statement.
+ return getHostCollection(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_HOST_ID);
+}
+
+ConstHostCollection
+CqlHostDataSourceImpl::getAll4(const asiolink::IOAddress& address) const {
+ // Convert to CQL data types.
+ cass_int32_t host_ipv4_address =
+ static_cast<cass_int32_t>(address.toUint32());
+
+ // Bind to array.
+ CqlDataArray where_values;
+ where_values.add(reinterpret_cast<void*>(&host_ipv4_address));
+
+ // Run statement.
+ return getHostCollection(*const_cast<const CqlDataArray*>(&where_values),
+ CqlHostExchange::GET_HOST_BY_IPV4_ADDRESS);
+}
+
+std::string
+CqlHostDataSourceImpl::getName() const {
+ std::string name = "";
+ try {
+ name = dbconn_.getParameter("name");
+ } catch (...) {
+ // Return an empty name
+ }
+ return name;
+}
+
+VersionPair
+CqlHostDataSourceImpl::getVersion() const {
+ return version_exchange_->retrieveVersion(dbconn_,
+ CqlHostExchange::GET_VERSION);
+}
+
+void
+CqlHostDataSourceImpl::commit() {
+ dbconn_.commit();
+}
+
+void
+CqlHostDataSourceImpl::rollback() {
+ dbconn_.rollback();
+}
+
+void
+CqlHostDataSourceImpl::insertHostWithOptions(
+ const HostPtr& host,
+ const IPv6Resrv* const reservation /* = NULL */,
+ const std::list<std::string>& option_spaces
+ /* = std::list<std::string>() */,
+ const ConstCfgOptionPtr cfg_option /* = ConstCfgOptionPtr() */) {
+ // For each option space retrieve all options and insert them into the
+ // database.
+ bool option_found = false;
+ for (std::list<std::string>::const_iterator space = option_spaces.begin();
+ space != option_spaces.end(); ++space) {
+ OptionContainerPtr options = cfg_option->getAll(*space);
+ if (options && !options->empty()) {
+ for (OptionContainer::const_iterator option = options->begin();
+ option != options->end(); ++option) {
+ option_found = true;
+ insertHost(host, OptionalValue<SubnetID>(), reservation, *space,
+ *option);
+ }
+ }
+ }
+ if (!option_found) {
+ insertHost(host, OptionalValue<SubnetID>(), reservation);
+ }
+}
+
+void
+CqlHostDataSourceImpl::insertHostWithReservations(
+ const HostPtr& host,
+ const IPv6Resrv* const reservation,
+ const std::list<std::string>& option_spaces4,
+ const ConstCfgOptionPtr cfg_option4,
+ const std::list<std::string>& option_spaces6,
+ const ConstCfgOptionPtr cfg_option6) {
+ // If host has no reservation, add entries with null reservation.
+ // Options could still be present.
+ if (cfg_option4 && !cfg_option4->empty()) {
+ insertHostWithOptions(host, reservation, option_spaces4, cfg_option4);
+ }
+ if (cfg_option6 && !cfg_option6->empty()) {
+ insertHostWithOptions(host, reservation, option_spaces6, cfg_option6);
+ }
+ if ((!cfg_option4 || cfg_option4->empty()) &&
+ (!cfg_option6 || cfg_option6->empty())) {
+ insertHostWithOptions(host, reservation);
+ }
+}
+
+ConstHostPtr
+CqlHostDataSourceImpl::getHost(
+ const CqlDataArray& where_values,
+ const CqlHostExchange::StatementIndex statement_index) const {
+ ConstHostCollection collection =
+ getHostCollection(where_values, statement_index);
+
+ if (collection.empty()) {
+ return ConstHostPtr();
+ }
+
+ if (collection.size() >= 2U) {
+ isc_throw(MultipleRecords,
+ "multiple records were found in the database where only one "
+ "was expected for statement "
+ << dbconn_.tagged_statements_[statement_index].name_);
+ }
+
+ return *collection.begin();
+}
+
+ConstHostCollection
+CqlHostDataSourceImpl::getHostCollection(
+ const CqlDataArray& where_values,
+ const CqlHostExchange::StatementIndex statement_index) const {
+
+ // Run statement.
+ CqlDataArray collection = host_exchange_->executeRead(
+ dbconn_, where_values, statement_index, false);
+
+ // Form HostPtr objects.
+ HostCollection host_collection;
+ for (std::vector<void*>::const_iterator it = collection.begin();
+ it != collection.end(); ++it) {
+ host_collection.push_back(HostPtr(reinterpret_cast<Host*>(*it)));
+ }
+
+ // Merge the denormalized table entries that belong to the same host into a
+ // single host.
+ HostMap map;
+ for (HostCollection::iterator it = host_collection.begin();
+ it != host_collection.end(); ++it) {
+ HostKey key =
+ HostKey((*it)->getIdentifier(), (*it)->getIdentifierType(),
+ (*it)->getIPv4SubnetID(), (*it)->getIPv6SubnetID(),
+ (*it)->getIPv4Reservation());
+ if (map.find(key) == map.end()) {
+ map[key] = *it;
+ } else {
+ mergeHosts(map[key], *it);
+ }
+ }
+
+ ConstHostCollection result_collection;
+ for (HostMap::const_iterator it = map.begin(); it != map.end(); ++it) {
+ result_collection.push_back(it->second);
+ }
+ return result_collection;
+}
+
+void
+CqlHostDataSourceImpl::insertHost(
+ const HostPtr& host,
+ const OptionalValue<SubnetID>& subnet_id
+ /* = OptionalValue<SubnetID>() */,
+ const IPv6Resrv* const reservation /* = NULL */,
+ const std::string& option_space /* = NULL_OPTION_SPACE */,
+ const OptionDescriptor& option_descriptor
+ /* = OptionDescriptorPtr */) {
+ CqlDataArray assigned_values;
+ // @todo: Assign actual value to subnet id.
+ host_exchange_->createBindForSend(host, subnet_id, reservation,
+ option_space, option_descriptor,
+ assigned_values);
+ host_exchange_->executeWrite(dbconn_, assigned_values,
+ CqlHostExchange::INSERT_HOST);
+}
+
+void
+CqlHostDataSourceImpl::mergeHosts(HostPtr& target_host,
+ const ConstHostPtr& source_host) const {
+ // Merge reservations.
+ const IPv6ResrvRange reservations_range =
+ source_host->getIPv6Reservations();
+ if (std::distance(reservations_range.first, reservations_range.second) >
+ 0) {
+ for (IPv6ResrvIterator reservations_iterator = reservations_range.first;
+ reservations_iterator != reservations_range.second;
+ ++reservations_iterator) {
+ if (!target_host->hasReservation(reservations_iterator->second)) {
+ target_host->addReservation(reservations_iterator->second);
+ }
+ }
+ }
+
+ // Merge DHCPv4 options.
+ source_host->getCfgOption4()->mergeTo(*target_host->getCfgOption4());
+
+ // Merge DHCPv6 options.
+ source_host->getCfgOption6()->mergeTo(*target_host->getCfgOption6());
+}
+
+CqlHostDataSource::CqlHostDataSource(
+ const CqlConnection::ParameterMap& parameters)
+ : impl_(new CqlHostDataSourceImpl(parameters)) {
+}
+
+CqlHostDataSource::~CqlHostDataSource() {
+ delete impl_;
+}
+
+void
+CqlHostDataSource::add(const HostPtr& host) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_ADD);
+
+ impl_->add(host);
+}
+
+ConstHostCollection
+CqlHostDataSource::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_HOST_GET_ALL);
+
+ return impl_->getAll(hwaddr, duid);
+}
+
+ConstHostCollection
+CqlHostDataSource::getAll(const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_HOST_GET_ALL);
+
+ return impl_->getAll(identifier_type, identifier_begin, identifier_len);
+}
+
+ConstHostCollection
+CqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_HOST_GET_ALL);
+
+ return impl_->getAll4(address);
+}
+
+ConstHostPtr
+CqlHostDataSource::get4(const SubnetID& subnet_id,
+ const HWAddrPtr& hwaddr,
+ const DuidPtr& duid) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET4);
+
+ return impl_->get4(subnet_id, hwaddr, duid);
+}
+
+ConstHostPtr
+CqlHostDataSource::get4(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET4);
+
+ return impl_->get4(subnet_id, identifier_type, identifier_begin,
+ identifier_len);
+}
+
+ConstHostPtr
+CqlHostDataSource::get4(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET4);
+
+ return impl_->get4(subnet_id, address);
+}
+
+ConstHostPtr
+CqlHostDataSource::get6(const SubnetID& subnet_id,
+ const DuidPtr& duid,
+ const HWAddrPtr& hwaddr) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET6);
+
+ return impl_->get6(subnet_id, duid, hwaddr);
+}
+
+ConstHostPtr
+CqlHostDataSource::get6(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET6);
+
+ return impl_->get6(subnet_id, identifier_type, identifier_begin,
+ identifier_len);
+}
+
+ConstHostPtr
+CqlHostDataSource::get6(const asiolink::IOAddress& prefix,
+ const uint8_t prefix_len) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET6);
+
+ return impl_->get6(prefix, prefix_len);
+}
+
+ConstHostPtr
+CqlHostDataSource::get6(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_HOST_GET6);
+
+ return impl_->get6(subnet_id, address);
+}
+
+std::string
+CqlHostDataSource::getType() const {
+ return std::string("cql");
+}
+
+std::string
+CqlHostDataSource::getName() const {
+ return impl_->getName();
+}
+
+std::string
+CqlHostDataSource::getDescription() const {
+ return std::string(
+ "Host data source that stores host information in the CQL database");
+}
+
+VersionPair
+CqlHostDataSource::getVersion() const {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_CQL_HOST_DB_GET_VERSION);
+
+ return impl_->getVersion();
+}
+
+void
+CqlHostDataSource::commit() {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_COMMIT);
+
+ impl_->commit();
+}
+
+void
+CqlHostDataSource::rollback() {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_CQL_ROLLBACK);
+
+ impl_->rollback();
+}
+
+}; // namespace dhcp
+}; // namespace isc
--- /dev/null
+// Copyright (C) 2016 Deutsche Telekom AG.
+//
+// Author: Andrei Pavel <andrei.pavel@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// 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_HOST_DATA_SOURCE_H
+#define CQL_HOST_DATA_SOURCE_H
+
+#include <dhcpsrv/base_host_data_source.h>
+#include <dhcpsrv/cql_connection.h>
+
+#include <stdint.h>
+
+#include <string>
+#include <utility>
+
+namespace isc {
+namespace dhcp {
+
+/// Forward declaration to the implementation of @ref CqlHostDataSource.
+class CqlHostDataSourceImpl;
+
+/// @brief Cassandra host data source
+///
+/// Implements @ref isc::dhcp::BaseHostDataSource interface customized to
+/// Cassandra. Use of this backend implies that a Cassandra database is
+/// available and that the Kea schema has been created within it.
+class CqlHostDataSource : public BaseHostDataSource {
+public:
+ /// @brief Constructor
+ ///
+ /// Uses the following keywords in the parameters passed to it to connect
+ /// to the database:
+ /// - name - Name of the database to which to connect (mandatory)
+ /// - host - Host to which to connect (optional, defaults to "localhost")
+ /// - user - Username under which to connect (optional)
+ /// - password - Password for "user" on the database (optional)
+ ///
+ /// If the database is successfully opened, the version number in the
+ /// schema_version table will be checked against hard-coded value in the
+ /// implementation file.
+ ///
+ /// Finally, all the CQL commands are pre-compiled.
+ ///
+ /// @param parameters A data structure relating keywords and values
+ /// concerned with the database.
+ ///
+ /// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
+ /// @throw isc::dhcp::DbOpenError Error opening the database
+ /// @throw isc::dhcp::DbOperationError An operation on the open database has
+ /// failed.
+ explicit CqlHostDataSource(
+ const DatabaseConnection::ParameterMap& parameters);
+
+ /// @brief Virtual destructor.
+ ///
+ /// Releases prepared CQL statements used by the backend.
+ virtual ~CqlHostDataSource();
+
+ /// @brief Adds a new @ref Host to the Cassandra database along with it's
+ /// reservations and options.
+ ///
+ /// The implementations of this method should guard against duplicate
+ /// reservations for the same @ref Host, where possible. For example, when
+ /// the reservation for the same @ref HWAddr and @ref SubnetID is added
+ /// twice, @ref add() should throw a @ref DuplicateEntry exception. Note,
+ /// that usually it is impossible to guard against adding duplicated @ref
+ /// Host, where one instance is identified by @ref HWAddr, another one by
+ /// @ref DUID.
+ ///
+ /// @param host pointer to the new @ref Host being added.
+ virtual void add(const HostPtr& host);
+
+ /// @brief Retrieves a single @ref Host connected to an IPv4 subnet.
+ ///
+ /// Implementations of this method should guard against the case when
+ /// multiple instances of the @ref Host are present, e.g. when two @ref
+ /// Host objects are found, one for the @ref DUID, another one for the @ref
+ /// HWAddr. In such case, throw a @ref MultipleRecords exception.
+ ///
+ /// @param subnet_id subnet identifier to filter by
+ /// @param hwaddr hardware address of the client to filter by or NULL if not
+ /// available
+ /// @param duid client identifier to filter by or NULL if not available
+ ///
+ /// @return @ref ConstHostPtr to a @ref Host object using a specified @ref
+ /// HWAddr or @ref DUID
+ ///
+ /// @throw BadValue if both or neither of subnet_id and duid are specified
+ virtual ConstHostPtr get4(const SubnetID& subnet_id,
+ const HWAddrPtr& hwaddr,
+ const DuidPtr& duid = DuidPtr()) const;
+
+ /// @brief Retrieves a @ref Host connected to an IPv4 subnet.
+ ///
+ /// @param subnet_id subnet identifier to filter by
+ /// @param identifier_type identifier type to filter by
+ /// @param identifier_begin pointer to the beginning of a buffer containing a
+ /// host identifier to filter by
+ /// @param identifier_len length of the host identifier buffer
+ ///
+ /// @return @ref Host object for which a reservation has been made using the
+ /// specified identifier
+ virtual ConstHostPtr get4(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const;
+
+ /// @brief Retrieves a @ref Host connected to an IPv4 subnet.
+ ///
+ /// Note that dynamically allocated addresses and reserved addresses can
+ /// come into conflict. When the new address is assigned to a client, the
+ /// allocation mechanism should check if this address is not reserved for
+ /// some other @ref Host and not allocate it if a reservation is present.
+ ///
+ /// @param subnet_id Subnet identifier.
+ /// @param address reserved IPv4 address.
+ ///
+ /// @return Const @ref Host object
+ ///
+ /// @throw BadValue if address in not a valid IPv4address
+ virtual ConstHostPtr get4(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const;
+
+ /// @brief Retrieves a @ref Host connected to an IPv6 subnet.
+ ///
+ /// Implementations of this method should guard against the case when
+ /// multiple instances of the @ref Host are present, e.g. when two
+ /// @ref Host objects are found, one for the @ref DUID, another one for the
+ /// @ref HWAddr. In such case, throw a @ref MultipleRecords exception.
+ ///
+ /// @param subnet_id subnet identifier to filter by
+ /// @param hwaddr hardware address of the client to filter by or NULL if not
+ /// available
+ /// @param duid client identifier to filter by or NULL if not available
+ ///
+ /// @return @ref Host object using a specified @ref HWAddr or @ref DUID
+ ///
+ /// @throw BadValue if both or neither of subnet_id and duid are specified
+ virtual ConstHostPtr get6(const SubnetID& subnet_id,
+ const DuidPtr& duid,
+ const HWAddrPtr& hwaddr = HWAddrPtr()) const;
+
+ /// @brief Returns a @ref Host connected to an IPv6 subnet.
+ ///
+ /// @param subnet_id subnet identifier to filter by
+ /// @param identifier_type identifier type to filter by
+ /// @param identifier_begin pointer to the beginning of a buffer containing a
+ /// host identifier to filter by
+ /// @param identifier_len length of the host identifier buffer
+ ///
+ /// @return Const @ref Host object for which reservation has been made using
+ /// the specified identifier.
+ virtual ConstHostPtr get6(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const;
+
+ /// @brief Returns a @ref Host with the specified reservation prefix.
+ ///
+ /// @param prefix IPv6 prefix for which the @ref Host object is searched.
+ /// @param prefix_len IPv6 prefix length.
+ ///
+ /// @return Const @ref Host object using a specified HW address or DUID.
+ ///
+ /// @throw MultipleRecords if two or more rows are returned from the
+ /// Cassandra database
+ virtual ConstHostPtr get6(const asiolink::IOAddress& prefix,
+ const uint8_t prefix_len) const;
+
+ /// @brief Returns a host connected to the IPv6 subnet and having
+ /// a reservation for a specified IPv6 address or prefix.
+ ///
+ /// @param subnet_id Subnet identifier.
+ /// @param address reserved IPv6 address/prefix.
+ ///
+ /// @return Const @c Host object using a specified IPv6 address/prefix.
+ virtual ConstHostPtr get6(const SubnetID& subnet_id,
+ const asiolink::IOAddress& address) const;
+
+ /// @brief Return all @ref Host objects for the specified @ref HWAddr or
+ /// @ref DUID.
+ ///
+ /// Returns all @ref Host objects which represent reservations
+ /// for the specified HW address or DUID. Note, that this method may
+ /// return multiple reservations because a particular client may have
+ /// reservations in multiple subnets and the same client may be identified
+ /// by HW address or DUID. The server is unable to verify that the specific
+ /// DUID and HW address belong to the same client, until the client sends
+ /// a DHCP message.
+ ///
+ /// Specifying both @ref HWAddr and @ref DUID is allowed for this method
+ /// and results in returning all objects that are associated with hardware
+ /// address OR duid. For example: if one @ref Host is associated with the
+ /// specified @ref HWAddr and another @ref Host is associated with the
+ /// specified @ref DUID, two hosts will be returned.
+ ///
+ /// @param hwaddr HW address of the client or NULL if no HW address
+ /// available.
+ /// @param duid client id or NULL if not available, e.g. DHCPv4 client case.
+ ///
+ /// @return collection of const @ref Host objects.
+ virtual ConstHostCollection getAll(const HWAddrPtr& hwaddr,
+ const DuidPtr& duid = DuidPtr()) const;
+
+ /// @brief Return all hosts connected to any subnet for which reservations
+ /// have been made using a specified identifier.
+ ///
+ /// This method returns all @ref Host objects which represent reservations
+ /// for a specified identifier. This method may return multiple hosts
+ /// because a particular client may have reservations in multiple subnets.
+ ///
+ /// @param identifier_type Identifier type.
+ /// @param identifier_begin Pointer to a beginning of a buffer containing
+ /// an identifier.
+ /// @param identifier_len Identifier length.
+ ///
+ /// @return Collection of const @ref Host objects.
+ virtual ConstHostCollection
+ getAll(const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin,
+ const size_t identifier_len) const;
+
+ /// @brief Returns a collection of hosts using the specified IPv4 address.
+ ///
+ /// This method may return multiple @ref Host objects if they are connected
+ /// to different subnets.
+ ///
+ /// @param address IPv4 address for which the @ref Host object is searched.
+ ///
+ /// @return Collection of const @ref Host objects.
+ virtual ConstHostCollection
+ getAll4(const asiolink::IOAddress& address) const;
+
+ /// @brief Returns description of the backend.
+ ///
+ /// This description may be multiline text that describes the backend.
+ ///
+ /// @return Description of the backend.
+ virtual std::string getDescription() const;
+
+ /// @brief Returns the name of the database.
+ ///
+ /// @return database name
+ virtual std::string getName() const;
+
+ /// @brief Return backend type
+ ///
+ /// @return backend type "cql"
+ virtual std::string getType() const;
+
+ /// @brief Retrieves schema version.
+ ///
+ /// @return Version number stored in the database, as a pair of unsigned
+ /// integers. "first" is the major version number, "second" is the
+ /// minor version number.
+ ///
+ /// @throw isc::dhcp::DbOperationError An operation on the open database
+ /// has failed.
+ virtual std::pair<uint32_t, uint32_t> getVersion() const;
+
+ /// @brief Commit Transactions
+ ///
+ /// Commits all pending database operations.
+ virtual void commit();
+
+ /// @brief Rollback Transactions
+ ///
+ /// Rolls back all pending database operations.
+ virtual void rollback();
+
+private:
+ /// @brief Pointer to the implementation of the @ref CqlHostDataSource.
+ CqlHostDataSourceImpl* impl_;
+}; // class CqlHostDataSource
+
+}; // namespace dhcp
+}; // namespace isc
+
+#endif // CQL_HOST_DATA_SOURCE_H
// limitations under the License.
#include <config.h>
+
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
-#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/cql_lease_mgr.h>
-#include <boost/static_assert.hpp>
-#include <iostream>
-#include <iomanip>
-#include <sstream>
-#include <string>
-#include <ctime>
+#include <dhcpsrv/dhcpsrv_log.h>
+
+#include <limits>
using namespace isc;
using namespace isc::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<cass_bool_t*>(value)));
-}
-
-static CassError CqlBindInt32(CassStatement* statement, size_t index,
- void* value) {
- return cass_statement_bind_int32(statement, index,
- *(static_cast<cass_int32_t*>(value)));
-}
-
-static CassError CqlBindInt64(CassStatement* statement, size_t index,
- void* value) {
- return cass_statement_bind_int64(statement, index,
- *(static_cast<cass_int64_t*>(value)));
-}
-
-static CassError CqlBindTimestamp(CassStatement* statement, size_t index,
- void* value) {
- return cass_statement_bind_int64(statement, index,
- *(static_cast<cass_int64_t*>(value)));
-}
-
-static CassError CqlBindString(CassStatement* statement, size_t index,
- void* value) {
- return cass_statement_bind_string(statement, index,
- (static_cast<const char*>(value)));
-}
-
-static CassError CqlBindBytes(CassStatement* statement, size_t index,
- void* value) {
- return cass_statement_bind_bytes(statement, index,
- static_cast<std::vector<cass_byte_t>*>(value)->data(),
- static_cast<std::vector<cass_byte_t>*>(value)->size());
-}
-/// @}
-
-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<cass_bool_t*>(data));
-}
-
-static CassError CqlGetInt32(const CassValue* value, void* data, size_t*) {
- return cass_value_get_int32(value, static_cast<cass_int32_t*>(data));
-}
-
-static CassError CqlGetInt64(const CassValue* value, void* data, size_t*) {
- return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
-}
-
-static CassError CqlGetTimestamp(const CassValue* value, void* data, size_t*) {
- return cass_value_get_int64(value, static_cast<cass_int64_t*>(data));
-}
-
-static CassError CqlGetString(const CassValue* value, void* data,
- size_t* size) {
- return cass_value_get_string(value, static_cast<const char**>(data), size);
-}
-
-static CassError CqlGetBytes(const CassValue* value, void* data, size_t* size) {
- return cass_value_get_bytes(value, static_cast<const cass_byte_t**>(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<const char*>("address"),
- NULL };
+ static_cast<const char*>("address"),
+ NULL };
static const char* delete_expired_lease4_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- NULL };
+ static_cast<const char*>("state"),
+ static_cast<const char*>("expire"),
+ NULL };
static const char* delete_lease6_params[] = {
- static_cast<const char*>("address"),
- NULL };
+ static_cast<const char*>("address"),
+ NULL };
static const char* delete_expired_lease6_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- NULL };
+ static_cast<const char*>("state"),
+ static_cast<const char*>("expire"),
+ NULL };
static const char* get_lease4_addr_params[] = {
- static_cast<const char*>("address"),
- NULL };
+ static_cast<const char*>("address"),
+ NULL };
static const char* get_lease4_clientid_params[] = {
- static_cast<const char*>("client_id"),
- NULL };
+ static_cast<const char*>("client_id"),
+ NULL };
static const char* get_lease4_clientid_subid_params[] = {
- static_cast<const char*>("client_id"),
- static_cast<const char*>("subnet_id"),
- NULL };
+ static_cast<const char*>("client_id"),
+ static_cast<const char*>("subnet_id"),
+ NULL };
static const char* get_lease4_hwaddr_params[] = {
- static_cast<const char*>("hwaddr"),
- NULL };
+ static_cast<const char*>("hwaddr"),
+ NULL };
static const char* get_lease4_hwaddr_subid_params[] = {
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("subnet_id"),
- NULL };
+ static_cast<const char*>("hwaddr"),
+ static_cast<const char*>("subnet_id"),
+ NULL };
static const char* get_lease4_expired_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("limit"),
- NULL };
+ static_cast<const char*>("state"),
+ static_cast<const char*>("expire"),
+ static_cast<const char*>("limit"),
+ NULL };
static const char* get_lease6_addr_params[] = {
- static_cast<const char*>("address"),
- static_cast<const char*>("lease_type"),
- NULL };
+ static_cast<const char*>("address"),
+ static_cast<const char*>("lease_type"),
+ NULL };
static const char* get_lease6_duid_iaid_params[] = {
- static_cast<const char*>("duid"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("lease_type"),
- NULL };
+ static_cast<const char*>("duid"),
+ static_cast<const char*>("iaid"),
+ static_cast<const char*>("lease_type"),
+ NULL };
static const char* get_lease6_duid_iaid_subid_params[] = {
- static_cast<const char*>("duid"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("lease_type"),
- NULL };
+ static_cast<const char*>("duid"),
+ static_cast<const char*>("iaid"),
+ static_cast<const char*>("subnet_id"),
+ static_cast<const char*>("lease_type"),
+ NULL };
static const char* get_lease6_expired_params[] = {
- static_cast<const char*>("state"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("limit"),
- NULL };
+ static_cast<const char*>("state"),
+ static_cast<const char*>("expire"),
+ static_cast<const char*>("limit"),
+ NULL };
static const char* get_version_params[] = {
- NULL };
+ NULL };
static const char* insert_lease4_params[] = {
- static_cast<const char*>("address"),
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("client_id"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("state"),
- NULL };
+ static_cast<const char*>("address"),
+ static_cast<const char*>("hwaddr"),
+ static_cast<const char*>("client_id"),
+ static_cast<const char*>("valid_lifetime"),
+ static_cast<const char*>("expire"),
+ static_cast<const char*>("subnet_id"),
+ static_cast<const char*>("fqdn_fwd"),
+ static_cast<const char*>("fqdn_rev"),
+ static_cast<const char*>("hostname"),
+ static_cast<const char*>("state"),
+ NULL };
static const char* insert_lease6_params[] = {
- static_cast<const char*>("address"),
- static_cast<const char*>("duid"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("pref_lifetime"),
- static_cast<const char*>("lease_type"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("prefix_len"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("hwtype"),
- static_cast<const char*>("hwaddr_source"),
- static_cast<const char*>("state"),
- NULL };
+ static_cast<const char*>("address"),
+ static_cast<const char*>("duid"),
+ static_cast<const char*>("valid_lifetime"),
+ static_cast<const char*>("expire"),
+ static_cast<const char*>("subnet_id"),
+ static_cast<const char*>("pref_lifetime"),
+ static_cast<const char*>("lease_type"),
+ static_cast<const char*>("iaid"),
+ static_cast<const char*>("prefix_len"),
+ static_cast<const char*>("fqdn_fwd"),
+ static_cast<const char*>("fqdn_rev"),
+ static_cast<const char*>("hostname"),
+ static_cast<const char*>("hwaddr"),
+ static_cast<const char*>("hwtype"),
+ static_cast<const char*>("hwaddr_source"),
+ static_cast<const char*>("state"),
+ NULL };
static const char* update_lease4_params[] = {
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("client_id"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("state"),
- static_cast<const char*>("address"),
- NULL };
+ static_cast<const char*>("hwaddr"),
+ static_cast<const char*>("client_id"),
+ static_cast<const char*>("valid_lifetime"),
+ static_cast<const char*>("expire"),
+ static_cast<const char*>("subnet_id"),
+ static_cast<const char*>("fqdn_fwd"),
+ static_cast<const char*>("fqdn_rev"),
+ static_cast<const char*>("hostname"),
+ static_cast<const char*>("state"),
+ static_cast<const char*>("address"),
+ NULL };
static const char* update_lease6_params[] = {
- static_cast<const char*>("duid"),
- static_cast<const char*>("valid_lifetime"),
- static_cast<const char*>("expire"),
- static_cast<const char*>("subnet_id"),
- static_cast<const char*>("pref_lifetime"),
- static_cast<const char*>("lease_type"),
- static_cast<const char*>("iaid"),
- static_cast<const char*>("prefix_len"),
- static_cast<const char*>("fqdn_fwd"),
- static_cast<const char*>("fqdn_rev"),
- static_cast<const char*>("hostname"),
- static_cast<const char*>("hwaddr"),
- static_cast<const char*>("hwtype"),
- static_cast<const char*>("hwaddr_source"),
- static_cast<const char*>("state"),
- static_cast<const char*>("address"),
- NULL };
+ static_cast<const char*>("duid"),
+ static_cast<const char*>("valid_lifetime"),
+ static_cast<const char*>("expire"),
+ static_cast<const char*>("subnet_id"),
+ static_cast<const char*>("pref_lifetime"),
+ static_cast<const char*>("lease_type"),
+ static_cast<const char*>("iaid"),
+ static_cast<const char*>("prefix_len"),
+ static_cast<const char*>("fqdn_fwd"),
+ static_cast<const char*>("fqdn_rev"),
+ static_cast<const char*>("hostname"),
+ static_cast<const char*>("hwaddr"),
+ static_cast<const char*>("hwtype"),
+ static_cast<const char*>("hwaddr_source"),
+ static_cast<const char*>("state"),
+ static_cast<const char*>("address"),
+ NULL };
+/// @}
CqlTaggedStatement CqlLeaseMgr::tagged_statements_[] = {
// DELETE_LEASE4
{ delete_lease4_params,
"delete_lease4",
"DELETE FROM lease4 WHERE address = ? "
- "IF EXISTS" },
+ "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,
"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,
"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,
"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,
"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,
"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,
"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,
"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 }
/// 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<uint8_t> 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<cass_byte_t> hwaddr_; ///< Hardware address
+ cass_int64_t valid_lifetime_; ///< Lease time
+ cass_int64_t expire_; ///< Lease expiry time
+ cass_int32_t subnet_id_; ///< Subnet identification
+ cass_bool_t fqdn_fwd_; ///< Has forward DNS update
+ /// been performed
+ cass_bool_t fqdn_rev_; ///< Has reverse DNS update
+ /// been performed
+ std::string hostname_; ///< Client hostname
+ cass_int32_t state_; ///< Lease state
};
/// @brief Exchange CQL and Lease4 Data
///
/// 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
/// 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;
// 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<cass_int32_t>(lease_->addr_.toUint32());
+ data.add(reinterpret_cast<void*>(&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<void*>(&hwaddr_));
// client_id: blob
if (lease_->client_id_) {
} else {
client_id_.clear();
}
- client_id_length_ = client_id_.size();
- data.add(&client_id_);
+ data.add(reinterpret_cast<void*>(&client_id_));
// valid lifetime: bigint
- valid_lifetime_ = lease_->valid_lft_;
- data.add(&valid_lifetime_);
+ valid_lifetime_ = static_cast<cass_int32_t>(lease_->valid_lft_);
+ data.add(reinterpret_cast<void*>(&valid_lifetime_));
// expire: bigint
- // The lease structure holds the client last transmission time (cltt_)
+ // 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<void*>(&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<cass_int32_t>(lease_->subnet_id_);
+ data.add(reinterpret_cast<void*>(&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<void*>(&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<void*>(&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<void*>(&hostname_));
// state: int
- state_ = lease_->state_;
- data.add(&state_);
-
+ state_ = static_cast<cass_int32_t>(lease_->state_);
+ data.add(reinterpret_cast<void*>(&state_));
} catch (const std::exception& ex) {
isc_throw(DbOperationError,
- "Could not create bind array from Lease4: "
- << lease_->addr_.toText() << ", reason: " << ex.what());
+ "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<void*>(&addr4_));
- size.add(NULL);
-
- // hwaddr: blob
- data.add(reinterpret_cast<void*>(&hwaddr_buffer));
- size.add(reinterpret_cast<void*>(&hwaddr_length_));
+ virtual void createBindForReceive(CqlDataArray& data,
+ const int /* statementIndex */ = -1) {
+ // address: int
+ data.add(reinterpret_cast<void*>(&addr4_));
- // client_id: blob
- data.add(reinterpret_cast<void*>(&client_id_buffer));
- size.add(reinterpret_cast<void*>(&client_id_length_));
+ // hwaddr: blob
+ data.add(reinterpret_cast<void*>(&hwaddr_));
- // valid_lifetime: bigint
- data.add(reinterpret_cast<void*>(&valid_lifetime_));
- size.add(NULL);
+ // client_id: blob
+ data.add(reinterpret_cast<void*>(&client_id_));
- // expire: bigint
- data.add(reinterpret_cast<void*>(&expire_));
- size.add(NULL);
+ // valid_lifetime: bigint
+ data.add(reinterpret_cast<void*>(&valid_lifetime_));
- // subnet_id: int
- data.add(reinterpret_cast<void*>(&subnet_id_));
- size.add(NULL);
+ // expire: bigint
+ data.add(reinterpret_cast<void*>(&expire_));
- // fqdn_fwd: boolean
- data.add(reinterpret_cast<void*>(&fqdn_fwd_));
- size.add(NULL);
+ // subnet_id: int
+ data.add(reinterpret_cast<void*>(&subnet_id_));
- // fqdn_rev: boolean
- data.add(reinterpret_cast<void*>(&fqdn_rev_));
- size.add(NULL);
+ // fqdn_fwd: boolean
+ data.add(reinterpret_cast<void*>(&fqdn_fwd_));
- // hostname: varchar
- data.add(reinterpret_cast<void*>(&hostname_buffer));
- size.add(reinterpret_cast<void*>(&hostname_length_));
-
- // state: int
- data.add(reinterpret_cast<void*>(&state_));
- size.add(NULL);
+ // fqdn_rev: boolean
+ data.add(reinterpret_cast<void*>(&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<void*>(&hostname_));
- // hwaddr: blob
- hwaddr_.assign(hwaddr_buffer, hwaddr_buffer + hwaddr_length_);
+ // state: int
+ data.add(reinterpret_cast<void*>(&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<uint8_t> 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<cass_byte_t> client_id_; ///< Client identification
};
/// @brief Exchange CQL and Lease6 Data
///
/// 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
/// 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;
// 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<void*>(&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<void*>(&duid_));
// valid lifetime: bigint
- valid_lifetime_ = lease_->valid_lft_;
- data.add(&valid_lifetime_);
+ valid_lifetime_ = static_cast<cass_int64_t>(lease_->valid_lft_);
+ data.add(reinterpret_cast<void*>(&valid_lifetime_));
// expire: bigint
- // The lease structure holds the client last transmission time (cltt_)
+ // 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<void*>(&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<cass_int32_t>(lease_->subnet_id_);
+ data.add(reinterpret_cast<void*>(&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<cass_int64_t>(lease_->preferred_lft_);
+ data.add(reinterpret_cast<void*>(&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<cass_int32_t>(lease_->type_);
+ data.add(reinterpret_cast<void*>(&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<cass_int32_t>(lease_->iaid_);
+ data.add(reinterpret_cast<void*>(&iaid_));
// prefix_len: int
- // Can use lease_->prefixlen_ directly as it is uint32_t.
- prefixlen_ = lease_->prefixlen_;
- data.add(&prefixlen_);
+ prefixlen_ = static_cast<cass_int32_t>(lease_->prefixlen_);
+ data.add(reinterpret_cast<void*>(&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<void*>(&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<void*>(&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<void*>(&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<void*>(&hwaddr_));
// hwtype: int
- if (hwaddr) {
- hwtype_ = lease->hwaddr_->htype_;
+ if (lease_->hwaddr_) {
+ hwtype_ = static_cast<cass_int32_t>(lease_->hwaddr_->htype_);
} else {
hwtype_ = 0;
}
- data.add(&hwtype_);
+ data.add(reinterpret_cast<void*>(&hwtype_));
// hwaddr_source: int
- if (hwaddr) {
- hwaddr_source_ = lease->hwaddr_->source_;
+ if (lease_->hwaddr_) {
+ hwaddr_source_ =
+ static_cast<cass_int32_t>(lease_->hwaddr_->source_);
} else {
hwaddr_source_ = 0;
}
- data.add(&hwaddr_source_);
+ data.add(reinterpret_cast<void*>(&hwaddr_source_));
// state: int
- state_ = lease_->state_;
- data.add(&state_);
-
+ state_ = static_cast<cass_int32_t>(lease_->state_);
+ data.add(reinterpret_cast<void*>(&state_));
} catch (const std::exception& ex) {
isc_throw(DbOperationError,
- "Could not create bind array from 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<void*>(&addr6_));
- // address: varchar
- data.add(reinterpret_cast<void*>(&address_buffer));
- size.add(reinterpret_cast<void*>(&addr6_length_));
+ // duid: blob
+ data.add(reinterpret_cast<void*>(&duid_));
- // duid: blob
- data.add(reinterpret_cast<void*>(&duid_buffer));
- size.add(reinterpret_cast<void*>(&duid_length_));
+ // valid_lifetime_: bigint
+ data.add(reinterpret_cast<void*>(&valid_lifetime_));
- // valid_lifetime_: bigint
- data.add(reinterpret_cast<void*>(&valid_lifetime_));
- size.add(NULL);
+ // expire: bigint
+ data.add(reinterpret_cast<void*>(&expire_));
- // expire: bigint
- data.add(reinterpret_cast<void*>(&expire_));
- size.add(NULL);
+ // subnet_id: int
+ data.add(reinterpret_cast<void*>(&subnet_id_));
- // subnet_id: int
- data.add(reinterpret_cast<void*>(&subnet_id_));
- size.add(NULL);
+ // pref_lifetime: bigint
+ data.add(reinterpret_cast<void*>(&pref_lifetime_));
- // pref_lifetime: bigint
- data.add(reinterpret_cast<void*>(&pref_lifetime_));
- size.add(NULL);
+ // lease_type: int
+ data.add(reinterpret_cast<void*>(&lease_type_));
- // lease_type: int
- data.add(reinterpret_cast<void*>(&lease_type_));
- size.add(NULL);
-
- // iaid: int
- data.add(reinterpret_cast<void*>(&iaid_));
- size.add(NULL);
-
- // prefix_len: int
- data.add(reinterpret_cast<void*>(&prefixlen_));
- size.add(NULL);
+ // iaid: int
+ data.add(reinterpret_cast<void*>(&iaid_));
- // fqdn_fwd: boolean
- data.add(reinterpret_cast<void*>(&fqdn_fwd_));
- size.add(NULL);
+ // prefix_len: int
+ data.add(reinterpret_cast<void*>(&prefixlen_));
- // fqdn_rev: boolean
- data.add(reinterpret_cast<void*>(&fqdn_rev_));
- size.add(NULL);
+ // fqdn_fwd: boolean
+ data.add(reinterpret_cast<void*>(&fqdn_fwd_));
- // hostname: varchar
- data.add(reinterpret_cast<void*>(&hostname_buffer));
- size.add(reinterpret_cast<void*>(&hostname_length_));
+ // fqdn_rev: boolean
+ data.add(reinterpret_cast<void*>(&fqdn_rev_));
- // hwaddr: blob
- data.add(reinterpret_cast<void*>(&hwaddr_buffer));
- size.add(reinterpret_cast<void*>(&hwaddr_length_));
+ // hostname: varchar
+ data.add(reinterpret_cast<void*>(&hostname_));
- // hwtype: int
- data.add(reinterpret_cast<void*>(&hwtype_));
- size.add(NULL);
+ // hwaddr: blob
+ data.add(reinterpret_cast<void*>(&hwaddr_));
- // hwaddr_source: int
- data.add(reinterpret_cast<void*>(&hwaddr_source_));
- size.add(NULL);
+ // hwtype: int
+ data.add(reinterpret_cast<void*>(&hwtype_));
- // state: int
- data.add(reinterpret_cast<void*>(&state_));
- size.add(NULL);
+ // hwaddr_source: int
+ data.add(reinterpret_cast<void*>(&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<void*>(&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<int>(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()) {
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>(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>(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<uint8_t> 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<cass_byte_t> duid_; ///< Client identification
+ cass_int64_t pref_lifetime_; ///< Preferred lifetime
+ cass_int32_t lease_type_; ///< Lease type
+ cass_int32_t iaid_; ///< Identity association ID
+ cass_int32_t prefixlen_; ///< Prefix length
+ cass_int32_t hwtype_; ///< Hardware type
+ cass_int32_t hwaddr_source_; ///< Source of the hardware
+ /// address
};
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_);
}
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_t *>(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;
}
// 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<void*>(&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 <typename Exchange, typename LeaseCollection>
-void CqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
- CqlDataArray& data,
- Exchange& exchange,
- LeaseCollection& result,
- bool single) const {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
- DHCPSRV_CQL_GET_ADDR4).arg(dbconn_.tagged_statements_[stindex].name_);
+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<CqlLeaseExchange>(*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<SqlExchange>(*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);
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);
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
}
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
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<cass_int32_t>(addr.toUint32());
+ data.add(reinterpret_cast<void*>(&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::vector<uint8_t>hwaddr_data = hwaddr.hwaddr_;
- data.add(&hwaddr_data);
+ std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
+ data.add(reinterpret_cast<void*>(&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::vector<uint8_t>hwaddr_data = hwaddr.hwaddr_;
- data.add(&hwaddr_data);
+ std::vector<cass_byte_t> hwaddr_data = hwaddr.hwaddr_;
+ data.add(reinterpret_cast<void*>(&hwaddr_data));
- uint32_t subnet_id_data = subnet_id;
- data.add(&subnet_id_data);
+ cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+ data.add(reinterpret_cast<void*>(&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<uint8_t> client_id_data = clientid.getClientId();
- data.add(&client_id_data);
+ std::vector<cass_byte_t> client_id_data = clientid.getClientId();
+ data.add(reinterpret_cast<void*>(&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<uint8_t> client_id_data = clientid.getClientId();
- data.add(&client_id_data);
+ data.add(reinterpret_cast<void*>(&client_id_data));
- uint32_t subnet_id_data = subnet_id;
- data.add(&subnet_id_data);
+ cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+ data.add(reinterpret_cast<void*>(&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<void*>(&addr_data));
- uint32_t lease_type_data = lease_type;
- data.add(&lease_type_data);
+ cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
+ data.add(reinterpret_cast<void*>(&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<uint8_t> duid_data = duid.getDuid();
- data.add(&duid_data);
+ std::vector<cass_byte_t> duid_data = duid.getDuid();
+ data.add(reinterpret_cast<void*>(&duid_data));
- uint32_t iaid_data = iaid;
- data.add(&iaid_data);
+ cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
+ data.add(reinterpret_cast<void*>(&iaid_data));
- uint32_t lease_type_data = lease_type;
- data.add(&lease_type_data);
+ cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
+ data.add(reinterpret_cast<void*>(&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<uint8_t> duid_data = duid.getDuid();
- data.add(&duid_data);
+ std::vector<cass_byte_t> duid_data = duid.getDuid();
+ data.add(reinterpret_cast<void*>(&duid_data));
- uint32_t iaid_data = iaid;
- data.add(&iaid_data);
+ cass_int32_t iaid_data = static_cast<cass_int32_t>(iaid);
+ data.add(reinterpret_cast<void*>(&iaid_data));
- uint32_t subnet_id_data = subnet_id;
- data.add(&subnet_id_data);
+ cass_int32_t subnet_id_data = static_cast<cass_int32_t>(subnet_id);
+ data.add(reinterpret_cast<void*>(&subnet_id_data));
- uint32_t lease_type_data = lease_type;
- data.add(&lease_type_data);
+ cass_int32_t lease_type_data = static_cast<cass_int32_t>(lease_type);
+ data.add(reinterpret_cast<void*>(&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<typename LeaseCollection>
+template <typename LeaseCollection>
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<int64_t>(time(NULL));
+ cass_int32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
+ cass_int64_t timestamp = static_cast<cass_int64_t>(time(NULL));
// If the number of leases is 0, we will return all leases. This is
// achieved by setting the limit to a very high value.
- uint32_t limit = max_leases > 0 ? static_cast<int32_t>(max_leases) :
- std::numeric_limits<int32_t>::max();
+ cass_int32_t limit = max_leases > 0 ?
+ static_cast<cass_int32_t>(max_leases) :
+ std::numeric_limits<cass_int32_t>::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<void*>(&state));
+ data.add(reinterpret_cast<void*>(×tamp));
+ data.add(reinterpret_cast<void*>(&limit));
// Retrieve leases from the database.
getLeaseCollection(statement_index, data, tempCollection);
typedef typename LeaseCollection::iterator LeaseCollectionIt;
for (LeaseCollectionIt it = tempCollection.begin();
- it != tempCollection.end(); ++it) {
+ it != tempCollection.end(); ++it) {
expired_leases.push_back((*it));
}
}
}
-template <typename LeasePtr>
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;
}
// 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<void*>(&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);
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<cass_int32_t>(lease->addr_.toUint32());
+ data.add(reinterpret_cast<void*>(&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;
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<void*>(&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;
}
// 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<void*>(&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);
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<cass_int32_t>(addr.toUint32());
+ data.add(reinterpret_cast<void*>(&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<void*>(&addr_data));
+ return deleteLeaseCommon(DELETE_LEASE6, data, *exchange6_);
}
}
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
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<cass_int32_t>(Lease::STATE_EXPIRED_RECLAIMED);
+ data.add(reinterpret_cast<void*>(&state));
// Expiration timestamp.
- uint64_t expiration = static_cast<int64_t>(time(NULL) -
- static_cast<time_t>(secs));
- data.add(&expiration);
+ cass_int64_t expiration =
+ static_cast<cass_int64_t>(time(NULL) - static_cast<time_t>(secs));
+ data.add(reinterpret_cast<void*>(&expiration));
// Get the data
Lease4Collection result4Leases;
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
} catch (...) {
// Return an empty name
}
- return (name);
+ return name;
}
std::string
return std::string("Cassandra Database");
}
-pair<uint32_t, uint32_t>
+std::pair<unsigned int, unsigned int>
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);
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<void*>(&version));
- size.add(NULL);
- // minor: uint32_t
+ // minor: int
data.add(reinterpret_cast<void*>(&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);
}
}
cass_future_free(future);
cass_statement_free(statement);
- return (make_pair(version, minor));
+ return std::pair<unsigned int, unsigned int>(
+ static_cast<unsigned int>(version), static_cast<unsigned int>(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
#ifndef CQL_LEASE_MGR_H
#define CQL_LEASE_MGR_H
+#include <cassandra.h>
+
#include <dhcp/hwaddr.h>
-#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/cql_exchange.h>
+#include <dhcpsrv/lease_mgr.h>
+
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
-#include <cassandra.h>
+
+#include <string>
+#include <utility>
#include <vector>
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<void*> 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<int64_t>(cltt) +
- static_cast<int64_t>(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<int64_t>(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
/// 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.
/// @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();
/// @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
/// 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
///
/// 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
/// 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.
///
/// 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.
///
/// 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.
///
/// 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
///
///
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- virtual std::pair<uint32_t, uint32_t> getVersion() const;
+ virtual std::pair<unsigned int, unsigned int> getVersion() const;
/// @brief Commit Transactions
///
/// @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)
///
/// @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
///
/// @throw isc::dhcp::MultipleRecords Multiple records were retrieved
/// from the database where only one was expected.
template <typename Exchange, typename LeaseCollection>
- void getLeaseCollection(StatementIndex stindex, CqlDataArray& data_array,
- Exchange& exchange, LeaseCollection& result,
+ void getLeaseCollection(StatementIndex stindex,
+ CqlDataArray& data_array,
+ Exchange& exchange,
+ LeaseCollection& result,
bool single = false) const;
/// @brief Gets Lease4 Collection
/// 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);
}
/// 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);
}
/// @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
/// @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.
/// @c GET_LEASE6_EXPIRE.
///
/// @tparam One of the @c Lease4Collection or @c Lease6Collection.
- template<typename LeaseCollection>
+ template <typename LeaseCollection>
void getExpiredLeasesCommon(LeaseCollection& expired_leases,
const size_t max_leases,
StatementIndex statement_index) const;
/// the address given.
/// @throw isc::dhcp::DbOperationError An operation on the open database has
/// failed.
- template <typename LeasePtr>
- void updateLeaseCommon(StatementIndex stindex, CqlDataArray& data,
- const LeasePtr& lease, CqlLeaseExchange& exchange);
+ void updateLeaseCommon(StatementIndex stindex,
+ CqlDataArray& data,
+ CqlLeaseExchange& exchange);
/// @brief Delete lease common code
///
///
/// @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.
///
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<CqlLease4Exchange> exchange4_; ///< Exchange object
- boost::scoped_ptr<CqlLease6Exchange> exchange6_; ///< Exchange object
- boost::scoped_ptr<CqlVersionExchange> versionExchange_; ///< Exchange object
+ /// @brief Exchange object for IPv4
+ boost::scoped_ptr<CqlLease4Exchange> exchange4_;
+ /// @brief Exchange object for IPv6
+ boost::scoped_ptr<CqlLease6Exchange> exchange6_;
+ /// @brief Exchange object for version
+ boost::scoped_ptr<CqlVersionExchange> versionExchange_;
+ /// @}
};
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+} // namespace dhcp
+} // namespace isc
-#endif // CQL_LEASE_MGR_H
+#endif // CQL_LEASE_MGR_H
A debug message issued when the server is about to add an IPv6 lease
with the specified address to the Cassandra backend database.
+% DHCPSRV_CQL_BEGIN_TRANSACTION committing to Cassandra database.
+The server has issued a begin transaction call.
+
% DHCPSRV_CQL_COMMIT committing to Cassandra database
A commit call been issued on the server. For Cassandra, this is a no-op.
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.
A debug message issued when the server is attempting to update IPv6
lease from the Cassandra database for the specified address.
+% DHCPSRV_CQL_HOST_DB Connecting to CQL hosts database: %1
+An informational message logged when the CQL hosts database is about to be
+connected to. The parameters of the connection including database name and
+username needed to access it (but not the password if any) are logged.
+
+% DHCPSRV_CQL_HOST_DB_GET_VERSION obtaining schema version information for the CQL hosts database
+A debug message issued when the server is about to obtain schema version
+information from the CQL hosts database.
+
+% DHCPSRV_CQL_HOST_ADD Adding host information to the database
+An informational message logged when options belonging to any reservation from a
+single host are inserted.
+
+% DHCPSRV_CQL_HOST_GET_ALL Retrieving multiple hosts from a CQL database
+An informational message logged when multiple hosts from a CQL database are retrieved.
+
+% DHCPSRV_CQL_HOST_GET4 Retrieving one DHCPv4 host from a CQL database
+An informational message logged when a DHCP server is about to retrieve one
+host from a CQL database by IPv4 criteria.
+
+% DHCPSRV_CQL_HOST_GET6 Retrieving one DHCPv6 host from a CQL database
+An informational message logged when a DHCP server is about to retrieve one
+host from a CQL database by IPv6 criteria.
+
% DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET received bad DHCPv4o6 packet: %1
A bad DHCPv4o6 packet was received.
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.
namespace isc {
namespace dhcp {
-/// @brief HostID (used only when storing in MySQL or Postgres)
+/// @brief HostID (used only when storing in MySQL, PostgreSQL or Cassandra)
typedef uint64_t HostID;
/// @brief IPv6 reservation for a host.
/// @brief Returns information about the host in the textual format.
std::string toText() const;
- /// @brief Sets Host ID (primary key in MySQL and Postgres backends)
+ /// @brief Sets Host ID (primary key in MySQL, PostgreSQL and Cassandra backends)
/// @param id HostId value
void setHostId(HostID id) {
host_id_ = id;
}
- /// @brief Returns Host ID (primary key in MySQL and Postgres backends)
+ /// @brief Returns Host ID (primary key in MySQL, PostgreSQL and Cassandra backends)
/// @return id HostId value (or 0 if not set)
HostID getHostId() const {
return (host_id_);
std::string boot_file_name_;
/// @brief HostID (a unique identifier assigned when the host is stored in
- /// MySQL or Pgsql)
+ /// MySQL, PostgreSQL or Cassandra)
uint64_t host_id_;
/// @brief Pointer to the DHCPv4 option data configuration for this host.
#include <dhcpsrv/pgsql_host_data_source.h>
#endif
+#ifdef HAVE_CQL
+#include <dhcpsrv/cql_host_data_source.h>
+#endif
+
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
#ifdef HAVE_CQL
if (db_type == "cql") {
- isc_throw(NotImplemented, "Sorry, CQL backend for host reservations "
- "is not implemented yet.");
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_CQL_HOST_DB)
+ .arg(DatabaseConnection::redactedAccessString(parameters));
+ getHostDataSourcePtr().reset(new CqlHostDataSource(parameters));
+ return;
}
#endif
#include <dhcpsrv/lease.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/db_exceptions.h>
+#include <dhcpsrv/sql_common.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
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<ExchangeColumnInfo> 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<ExchangeColumnInfoContainer> 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<ExchangeColumnInfoContainerName::const_iterator,
- ExchangeColumnInfoContainerName::const_iterator> 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<ExchangeColumnInfoContainerIndex::const_iterator,
- ExchangeColumnInfoContainerIndex::const_iterator> 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
ConstHostPtr
MySqlHostDataSource::get4(const SubnetID& subnet_id,
const asiolink::IOAddress& address) const {
- /// @todo: check that address is really v4, not v6.
+ // Check that address is IPv4, not IPv6.
+ if (!address.isV4()) {
+ isc_throw(BadValue, "MySqlHostDataSource::get4(2): wrong address type, "
+ "address supplied is not an IPv4 address");
+ }
// Set up the WHERE clause value
MYSQL_BIND inbind[2];
// 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");
values_copy[param.first] =
boost::lexical_cast<std::string>(timeout);
+ } else if (param.first == "reconnect-wait-time") {
+ timeout = param.second->intValue();
+ values_copy[param.first] =
+ boost::lexical_cast<std::string>(timeout);
+
+ } else if (param.first == "request-timeout") {
+ timeout = param.second->intValue();
+ values_copy[param.first] =
+ boost::lexical_cast<std::string>(timeout);
+
+ } else if (param.first == "tcp-keepalive") {
+ timeout = param.second->intValue();
+ values_copy[param.first] =
+ boost::lexical_cast<std::string>(timeout);
+
} else {
values_copy[param.first] = param.second->stringValue();
}
--- /dev/null
+// Copyright (C) 2016 Deutsche Telekom AG.
+//
+// Author: Cristian Secăreanu <cristian.secareanu@qualitance.com>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SQL_COMMON_H
+#define SQL_COMMON_H
+
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+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<ExchangeColumnInfo> ExchangeColumnInfoPtr;
+
+/// @brief Multimap that allows indexing @ref ExchangeColumnInfoPtr
+/// sequentially, by index and by name.
+typedef boost::multi_index_container<
+ // Container comprises elements of ExchangeColumnInfoPtr type.
+ ExchangeColumnInfoPtr,
+ // Here we start enumerating various indexes.
+ boost::multi_index::indexed_by<
+ // Index #0. Sequenced index allows accessing elements in the same way
+ // as in std::list.
+ boost::multi_index::sequenced<>,
+ // Index #1
+ boost::multi_index::hashed_non_unique<
+ boost::multi_index::member<
+ ExchangeColumnInfo,
+ std::string,
+ &ExchangeColumnInfo::name_
+ >
+ >,
+ // Index #2
+ boost::multi_index::hashed_non_unique<
+ boost::multi_index::member<
+ ExchangeColumnInfo,
+ uint32_t,
+ &ExchangeColumnInfo::index_
+ >
+ >
+ >
+> ExchangeColumnInfoContainer;
+
+/// @brief Pointer to the ExchangeColumnInfoContainer object.
+typedef boost::shared_ptr<ExchangeColumnInfoContainer>
+ ExchangeColumnInfoContainerPtr;
+
+/// @brief Type of the index #1 - name.
+typedef ExchangeColumnInfoContainer::nth_index<1>::type
+ ExchangeColumnInfoContainerName;
+
+/// @brief Pair of iterators to represent the range of ExchangeColumnInfo having
+/// the
+/// same name value. The first element in this pair represents
+/// the beginning of the range, the second element represents the end.
+typedef std::pair<ExchangeColumnInfoContainerName::const_iterator,
+ ExchangeColumnInfoContainerName::const_iterator>
+ ExchangeColumnInfoContainerNameRange;
+
+/// @brief Type of the index #2 - index.
+typedef ExchangeColumnInfoContainer::nth_index<2>::type
+ ExchangeColumnInfoContainerIndex;
+
+/// @brief Pair of iterators to represent the range of ExchangeColumnInfo having
+/// the
+/// same index value. The first element in this pair represents
+/// the beginning of the range, the second element represents the end.
+typedef std::pair<ExchangeColumnInfoContainerIndex::const_iterator,
+ ExchangeColumnInfoContainerIndex::const_iterator>
+ ExchangeColumnInfoContainerIndexRange;
+
+/// @brief Base class for backend exchanges.
+class SqlExchange {
+public:
+ /// @brief Constructor
+ SqlExchange() {
+ }
+
+ /// @brief Destructor
+ virtual ~SqlExchange() {
+ }
+
+ /// @brief Column names and types
+ ExchangeColumnInfoContainer parameters_;
+};
+
+} // namespace dhcp
+} // namespace isc
+
+#endif // SQL_COMMON_H
endif
if HAVE_CQL
libdhcpsrv_unittests_SOURCES += cql_lease_mgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += cql_host_data_source_unittest.cc
endif
libdhcpsrv_unittests_SOURCES += pool_unittest.cc
libdhcpsrv_unittests_SOURCES += srv_config_unittest.cc
--- /dev/null
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/cql_connection.h>
+#include <dhcpsrv/cql_host_data_source.h>
+#include <dhcpsrv/cql_lease_mgr.h>
+#include <dhcpsrv/host.h>
+#include <dhcpsrv/host_data_source_factory.h>
+#include <dhcpsrv/tests/generic_host_data_source_unittest.h>
+#include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/testutils/cql_schema.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <utility>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace std;
+
+namespace {
+
+class CqlHostDataSourceTest : public GenericHostDataSourceTest {
+public:
+ /// @brief Clears the database and opens connection to it.
+ void initializeTest() {
+ // Ensure schema is the correct one.
+ destroyCqlSchema(false, true);
+ createCqlSchema(false, true);
+
+ // Connect to the database
+ try {
+ HostDataSourceFactory::create(validCqlConnectionString());
+ } catch (...) {
+ std::cerr << "*** ERROR: unable to open database. The test"
+ "*** environment is broken and must be fixed before"
+ "*** the CQL tests will run correctly."
+ "*** The reason for the problem is described in the"
+ "*** accompanying exception output.";
+ throw;
+ }
+
+ hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
+ }
+
+ /// @brief Destroys the HDS and the schema.
+ void destroyTest() {
+ hdsptr_->rollback();
+ HostDataSourceFactory::destroy();
+ destroyCqlSchema(false, true);
+ }
+
+ /// @brief Constructor
+ ///
+ /// Deletes everything from the database and opens it.
+ CqlHostDataSourceTest(){
+ initializeTest();
+ }
+
+ /// @brief Destructor
+ ///
+ /// Rolls back all pending transactions. The deletion of myhdsptr_ will
+ /// close the database. Then reopen it and delete everything created by the
+ /// test.
+ virtual ~CqlHostDataSourceTest() {
+ destroyTest();
+ }
+
+ /// @brief Reopen the database
+ ///
+ /// 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
+ /// the same database.
+ void reopen(Universe) {
+ HostDataSourceFactory::destroy();
+ HostDataSourceFactory::create(validCqlConnectionString());
+ hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
+ }
+};
+
+/// @brief Check that database can be opened
+///
+/// This test checks if the CqlHostDataSource 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(CqlHostDataSource, OpenDatabase) {
+ // Schema needs to be created for the test to work.
+ destroyCqlSchema(false, true);
+ createCqlSchema(false, true);
+
+ // Check that lease manager open the database opens correctly and tidy up.
+ // If it fails, print the error message.
+ try {
+ HostDataSourceFactory::create(validCqlConnectionString());
+ EXPECT_NO_THROW((void)HostDataSourceFactory::getHostDataSourcePtr());
+ HostDataSourceFactory::destroy();
+ } catch (const isc::Exception& ex) {
+ FAIL() << "*** ERROR: unable to open database, reason:\n"
+ << " " << ex.what() << "\n"
+ << "*** The test environment is broken and must be fixed\n"
+ << "*** before the CQL tests will run correctly.\n";
+ }
+
+ // Check that lease manager open the database opens correctly with a longer
+ // timeout. If it fails, print the error message.
+ try {
+ std::string connection_string = validCqlConnectionString() +
+ std::string(" ") +
+ std::string(VALID_TIMEOUT);
+ HostDataSourceFactory::create(connection_string);
+ EXPECT_NO_THROW((void)HostDataSourceFactory::getHostDataSourcePtr());
+ HostDataSourceFactory::destroy();
+ } catch (const isc::Exception& ex) {
+ FAIL() << "*** ERROR: unable to open database, reason:\n"
+ << " " << ex.what() << "\n"
+ << "*** The test environment is broken and must be fixed\n"
+ << "*** before the CQL tests will run correctly.\n";
+ }
+
+ // Check that attempting to get an instance of the host data source when
+ // none is set throws an exception.
+ EXPECT_FALSE(HostDataSourceFactory::getHostDataSourcePtr());
+
+ // Check that wrong specification of backend throws an exception.
+ // (This is really a check on HostDataSourceFactory, but is convenient to
+ // perform here.)
+ EXPECT_THROW(
+ HostDataSourceFactory::create(connectionString(
+ NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+ InvalidParameter);
+ EXPECT_THROW(
+ HostDataSourceFactory::create(connectionString(
+ 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(HostDataSourceFactory::create(connectionString(
+ CQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)));
+ EXPECT_NO_THROW(HostDataSourceFactory::create(connectionString(
+ CQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)));
+ EXPECT_NO_THROW(HostDataSourceFactory::create(connectionString(
+ CQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)));
+ EXPECT_NO_THROW(HostDataSourceFactory::create(connectionString(
+ CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)));
+
+ // Check that invalid timeouts throw DbOperationError.
+ EXPECT_THROW(HostDataSourceFactory::create(connectionString(
+ CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER,
+ VALID_PASSWORD, INVALID_TIMEOUT_1)),
+ DbOperationError);
+ EXPECT_THROW(HostDataSourceFactory::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(HostDataSourceFactory::create(connectionString(
+ CQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)));
+
+ // Tidy up after the test
+ destroyCqlSchema(false, true);
+}
+
+/// @brief Check conversion functions
+///
+/// The server works using cltt and valid_filetime. In the database, the
+/// information is stored as expire_time and valid-lifetime, which are
+/// related by
+///
+/// expire_time = cltt + valid_lifetime
+///
+/// This test checks that the conversion is correct. It does not check that the
+/// data is entered into the database correctly, only that the CQL_TIME
+/// structure used for the entry is correctly set up.
+TEST(CqlConnection, checkTimeConversion) {
+ const time_t cltt = time(NULL);
+ const uint32_t valid_lft = 86400; // 1 day
+ cass_int64_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);
+}
+// Test verifies if a host reservation can be added and later retrieved by IPv4
+// address. Host uses hw address as identifier.
+TEST_F(CqlHostDataSourceTest, basic4HWAddr) {
+ testBasic4(Host::IDENT_HWADDR);
+}
+
+// Test verifies if a host reservation can be added and later retrieved by IPv4
+// address. Host uses client-id (DUID) as identifier.
+TEST_F(CqlHostDataSourceTest, basic4ClientId) {
+ testBasic4(Host::IDENT_DUID);
+}
+
+// Test verifies that multiple hosts can be added and later retrieved by their
+// reserved IPv4 address. This test uses HW addresses as identifiers.
+TEST_F(CqlHostDataSourceTest, getByIPv4HWaddr) {
+ testGetByIPv4(Host::IDENT_HWADDR);
+}
+
+// Test verifies that multiple hosts can be added and later retrieved by their
+// reserved IPv4 address. This test uses client-id (DUID) as identifiers.
+TEST_F(CqlHostDataSourceTest, getByIPv4ClientId) {
+ testGetByIPv4(Host::IDENT_DUID);
+}
+
+// Test verifies if a host reservation can be added and later retrieved by
+// hardware address.
+TEST_F(CqlHostDataSourceTest, get4ByHWaddr) {
+ testGet4ByIdentifier(Host::IDENT_HWADDR);
+}
+
+// Test verifies if a host reservation can be added and later retrieved by
+// DUID.
+TEST_F(CqlHostDataSourceTest, get4ByDUID) {
+ testGet4ByIdentifier(Host::IDENT_DUID);
+}
+
+// Test verifies if a host reservation can be added and later retrieved by
+// circuit id.
+TEST_F(CqlHostDataSourceTest, get4ByCircuitId) {
+ testGet4ByIdentifier(Host::IDENT_CIRCUIT_ID);
+}
+
+// Test verifies if hardware address and client identifier are not confused.
+TEST_F(CqlHostDataSourceTest, hwaddrNotClientId1) {
+ testHWAddrNotClientId();
+}
+
+// Test verifies if hardware address and client identifier are not confused.
+TEST_F(CqlHostDataSourceTest, hwaddrNotClientId2) {
+ testClientIdNotHWAddr();
+}
+
+// Test verifies if a host with FQDN hostname can be stored and later retrieved.
+TEST_F(CqlHostDataSourceTest, hostnameFQDN) {
+ testHostname("foo.example.org", 1);
+}
+
+// Test verifies if 100 hosts with unique FQDN hostnames can be stored and later
+// retrieved.
+TEST_F(CqlHostDataSourceTest, hostnameFQDN100) {
+ testHostname("foo.example.org", 100);
+}
+
+// Test verifies if a host without any hostname specified can be stored and
+// later retrieved.
+TEST_F(CqlHostDataSourceTest, noHostname) {
+ testHostname("", 1);
+}
+
+// Test verifies if the hardware or client-id query can match hardware address.
+TEST_F(CqlHostDataSourceTest, DISABLED_hwaddrOrClientId1) {
+ /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to
+ /// be discussed.
+ ///
+ /// @todo: Add host reservation with hardware address X, try to retrieve
+ /// host for hardware address X or client identifier Y, verify that the
+ /// reservation is returned.
+}
+
+// Test verifies if the hardware or client-id query can match client-id.
+TEST_F(CqlHostDataSourceTest, DISABLED_hwaddrOrClientId2) {
+ /// @todo: The logic behind ::get4(subnet_id, hwaddr, duid) call needs to
+ /// be discussed.
+ ///
+ /// @todo: Add host reservation with client identifier Y, try to retrieve
+ /// host for hardware address X or client identifier Y, verify that the
+ /// reservation is returned.
+}
+
+// Test verifies that host with IPv6 address and DUID can be added and
+// later retrieved by IPv6 address.
+TEST_F(CqlHostDataSourceTest, get6AddrWithDuid) {
+ testGetByIPv6(Host::IDENT_DUID, false);
+}
+
+// Test verifies that host with IPv6 address and HWAddr can be added and
+// later retrieved by IPv6 address.
+TEST_F(CqlHostDataSourceTest, get6AddrWithHWAddr) {
+ testGetByIPv6(Host::IDENT_HWADDR, false);
+}
+
+// Test verifies that host with IPv6 prefix and DUID can be added and
+// later retrieved by IPv6 prefix.
+TEST_F(CqlHostDataSourceTest, get6PrefixWithDuid) {
+ testGetByIPv6(Host::IDENT_DUID, true);
+}
+
+// Test verifies that host with IPv6 prefix and HWAddr can be added and
+// later retrieved by IPv6 prefix.
+TEST_F(CqlHostDataSourceTest, get6PrefixWithHWaddr) {
+ testGetByIPv6(Host::IDENT_HWADDR, true);
+}
+
+// Test verifies that host with IPv6 prefix reservation can be retrieved
+// by subnet id and prefix value.
+TEST_F(CqlHostDataSourceTest, get6SubnetPrefix) {
+ testGetBySubnetIPv6();
+}
+
+// Test verifies if a host reservation can be added and later retrieved by
+// hardware address.
+TEST_F(CqlHostDataSourceTest, get6ByHWaddr) {
+ testGet6ByHWAddr();
+}
+
+// Test verifies if a host reservation can be added and later retrieved by
+// client identifier.
+TEST_F(CqlHostDataSourceTest, get6ByClientId) {
+ testGet6ByClientId();
+}
+
+// Test verifies if a host reservation can be stored with both IPv6 address and
+// prefix.
+TEST_F(CqlHostDataSourceTest, addr6AndPrefix) {
+ testAddr6AndPrefix();
+}
+
+// Tests if host with multiple IPv6 reservations can be added and then
+// retrieved correctly. Test checks reservations comparing.
+TEST_F(CqlHostDataSourceTest, multipleReservations) {
+ testMultipleReservations();
+}
+
+// Tests if compareIPv6Reservations() method treats same pool of reservations
+// but added in different order as equal.
+TEST_F(CqlHostDataSourceTest, multipleReservationsDifferentOrder) {
+ testMultipleReservationsDifferentOrder();
+}
+
+// Test verifies if multiple client classes for IPv4 can be stored.
+TEST_F(CqlHostDataSourceTest, DISABLED_multipleClientClasses4) {
+ /// @todo: Implement this test as part of #4213.
+
+ /// Add host reservation with a multiple v4 client-classes, retrieve it and
+ /// make sure that all client classes are retrieved properly.
+}
+
+// Test verifies if multiple client classes for IPv6 can be stored.
+TEST_F(CqlHostDataSourceTest, DISABLED_multipleClientClasses6) {
+ /// @todo: Implement this test as part of #4213.
+
+ /// Add host reservation with a multiple v6 client-classes, retrieve it and
+ /// make sure that all client classes are retrieved properly.
+}
+
+// Test verifies if multiple client classes for both IPv4 and IPv6 can be
+// stored.
+TEST_F(CqlHostDataSourceTest, DISABLED_multipleClientClassesBoth) {
+ /// @todo: Implement this test as part of #4213.
+
+ /// Add host reservation with a multiple v4 and v6 client-classes, retrieve
+ /// it and make sure that all client classes are retrieved properly. Also,
+ /// check that the classes are not confused.
+}
+
+// Test if the same host can have reservations in different subnets (with the
+// same hardware address). The test logic is as follows:
+// Insert 10 host reservations for a given physical host (the same
+// hardware address), but for different subnets (different subnet-ids).
+// Make sure that getAll() returns them all correctly.
+TEST_F(CqlHostDataSourceTest, multipleSubnetsHWAddr) {
+ testMultipleSubnets(10, Host::IDENT_HWADDR);
+}
+
+// Test if the same host can have reservations in different subnets (with the
+// same client identifier). The test logic is as follows:
+//
+// Insert 10 host reservations for a given physical host (the same
+// client-identifier), but for different subnets (different subnet-ids).
+// Make sure that getAll() returns them correctly.
+TEST_F(CqlHostDataSourceTest, multipleSubnetsClientId) {
+ testMultipleSubnets(10, Host::IDENT_DUID);
+}
+
+// Test if host reservations made for different IPv6 subnets are handled
+// correctly. The test logic is as follows:
+//
+// Insert 10 host reservations for different subnets. Make sure that
+// get6(subnet-id, ...) calls return correct reservation.
+TEST_F(CqlHostDataSourceTest, subnetId6) {
+ testSubnetId6(10, Host::IDENT_HWADDR);
+}
+
+// Test if the duplicate host instances can't be inserted. The test logic is as
+// follows: try to add multiple instances of the same host reservation and
+// verify that the second and following attempts will throw exceptions.
+// Hosts with same DUID.
+TEST_F(CqlHostDataSourceTest, addDuplicate6WithDUID) {
+ testAddDuplicate6WithSameDUID();
+}
+
+// Test if the duplicate host instances can't be inserted. The test logic is as
+// follows: try to add multiple instances of the same host reservation and
+// verify that the second and following attempts will throw exceptions.
+// Hosts with same HWAddr.
+TEST_F(CqlHostDataSourceTest, addDuplicate6WithHWAddr) {
+ testAddDuplicate6WithSameHWAddr();
+}
+
+// Test if the duplicate IPv4 host instances can't be inserted. The test logic
+// is as follows: try to add multiple instances of the same host reservation and
+// verify that the second and following attempts will throw exceptions.
+TEST_F(CqlHostDataSourceTest, addDuplicate4) {
+ testAddDuplicate4();
+}
+
+// This test verifies that DHCPv4 options can be inserted in a binary format
+/// and retrieved from the CQL host database.
+TEST_F(CqlHostDataSourceTest, optionsReservations4) {
+ testOptionsReservations4(false);
+}
+
+// This test verifies that DHCPv6 options can be inserted in a binary format
+/// and retrieved from the CQL host database.
+TEST_F(CqlHostDataSourceTest, optionsReservations6) {
+ testOptionsReservations6(false);
+}
+
+// This test verifies that DHCPv4 and DHCPv6 options can be inserted in a
+/// binary format and retrieved with a single query to the database.
+TEST_F(CqlHostDataSourceTest, optionsReservations46) {
+ testOptionsReservations46(false);
+}
+
+// This test verifies that DHCPv4 options can be inserted in a textual format
+/// and retrieved from the CQL host database.
+TEST_F(CqlHostDataSourceTest, formattedOptionsReservations4) {
+ testOptionsReservations4(true);
+}
+
+// This test verifies that DHCPv6 options can be inserted in a textual format
+/// and retrieved from the CQL host database.
+TEST_F(CqlHostDataSourceTest, formattedOptionsReservations6) {
+ testOptionsReservations6(true);
+}
+
+// This test verifies that DHCPv4 and DHCPv6 options can be inserted in a
+// textual format and retrieved with a single query to the database.
+TEST_F(CqlHostDataSourceTest, formattedOptionsReservations46) {
+ testOptionsReservations46(true);
+}
+
+// This test checks transactional insertion of the host information
+// into the database. The failure to insert host information at
+// any stage should cause the whole transaction to be rolled back.
+TEST_F(CqlHostDataSourceTest, testAddRollback) {
+ // Make sure we have the pointer to the host data source.
+ ASSERT_TRUE(hdsptr_);
+
+ // To test the transaction rollback mechanism we need to cause the
+ // insertion of host information to fail at some stage. The 'hosts'
+ // table should be updated correctly but the failure should occur
+ // when inserting reservations or options. The simplest way to
+ // achieve that is to simply drop one of the tables. To do so, we
+ // connect to the database and issue a DROP query.
+ CqlConnection::ParameterMap params;
+ params["name"] = "keatest";
+ params["user"] = "keatest";
+ params["password"] = "keatest";
+ CqlConnection connection(params);
+ ASSERT_NO_THROW(connection.openDatabase());
+
+ // Drop every table so we make sure host_reservations doesn't exist anymore.
+ destroyCqlSchema(false, true);
+
+ // Create a host with a reservation.
+ HostPtr host = initializeHost6("2001:db8:1::1", Host::IDENT_HWADDR, false);
+ // Let's assign some DHCPv4 subnet to the host, because we will use the
+ // DHCPv4 subnet to try to retrieve the host after failed insertion.
+ host->setIPv4SubnetID(SubnetID(4));
+
+ // There is no ipv6_reservations table, so the insertion should fail.
+ ASSERT_THROW(hdsptr_->add(host), DbOperationError);
+
+ // Even though we have created a DHCPv6 host, we can't use get6()
+ // method to retrieve the host from the database, because the
+ // query would expect that the ipv6_reservations table is present.
+ // Therefore, the query would fail. Instead, we use the get4 method
+ // which uses the same client identifier, but doesn't attempt to
+ // retrieve the data from ipv6_reservations table. The query should
+ // pass but return no host because the (insert) transaction is expected
+ // to be rolled back.
+ ASSERT_THROW(
+ hdsptr_->get4(host->getIPv4SubnetID(), host->getIdentifierType(),
+ &host->getIdentifier()[0], host->getIdentifier().size()),
+ DbOperationError);
+}
+
+TEST_F(CqlHostDataSourceTest, DISABLED_stressTest) {
+ // Run with 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4092, 8192,
+ // 16384 & 32768 hosts.
+ for (unsigned int i = 0X0001U; i < 0xfffdU; i <<= 1) {
+ initializeTest();
+ stressTest(i);
+ destroyTest();
+ }
+}
+
+} // namespace
#include <config.h>
#include <asiolink/io_address.h>
-#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/cql_lease_mgr.h>
-#include <dhcpsrv/tests/test_utils.h>
+#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
+#include <dhcpsrv/tests/test_utils.h>
#include <dhcpsrv/testutils/cql_schema.h>
#include <exceptions/exceptions.h>
///
/// Deletes everything from the database and opens it.
CqlLeaseMgrTest() {
-
// Ensure schema is the correct one.
destroyCqlSchema(false, true);
createCqlSchema(false, true);
lmptr_ = &(LeaseMgrFactory::instance());
}
- // This is the CQL implementation for GenericLeaseMgrTest::testGetExpiredLeases4().
+ // This is the CQL implementation for
+ // GenericLeaseMgrTest::testGetExpiredLeases4().
// The GenericLeaseMgrTest implementation checks for the order of expired
// leases to be from the most expired to the least expired. Cassandra
// doesn't support ORDER BY without imposing a EQ / IN restriction on the
// columns. Because of that, the order check has been excluded.
- void
- testCqlGetExpiredLeases4() {
+ void testCqlGetExpiredLeases4() {
// Get the leases to be used for the test.
vector<Lease4Ptr> leases = createLeases4();
// Make sure we have at least 6 leases there.
// valid lifetime to make it expired. The expiration time also
// depends on the lease index, so as we can later check that the
// leases are ordered by the expiration time.
- leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+ leases[i]->cltt_ =
+ current_time - leases[i]->valid_lft_ - 10 - i;
} else {
- // Set current time as cltt for remaining leases. These leases are
+ // Set current time as cltt for remaining leases. These leases
+ // are
// not expired.
leases[i]->cltt_ = current_time;
}
Lease4Collection expired_leases;
ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000));
// Leases with even indexes should be returned as expired.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ expired_leases.size());
// Update current time for the next test.
current_time = time(NULL);
// Also, remove expired leases collected during the previous test.
expired_leases.clear();
- // This time let's reverse the expiration time and see if they will be returned
+ // This time let's reverse the expiration time and see if they will be
+ // returned
// in the correct order.
for (size_t i = 0U; i < leases.size(); ++i) {
// Update the time of expired leases with even indexes.
if (i % 2U == 0U) {
- leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+ leases[i]->cltt_ =
+ current_time - leases[i]->valid_lft_ - 1000 + i;
} else {
// Make sure remaining leases remain unexpired.
leases[i]->cltt_ = current_time + 100;
ASSERT_NO_THROW(lmptr_->updateLease4(leases[i]));
}
- // Retrieve expired leases again. The limit of 0 means return all expired
+ // Retrieve expired leases again. The limit of 0 means return all
+ // expired
// leases.
ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0));
// The same leases should be returned.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ expired_leases.size());
// Remember expired leases returned.
std::vector<Lease4Ptr> saved_expired_leases = expired_leases;
// Mark every other expired lease as reclaimed.
for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
if (i % 2U != 0U) {
- saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+ saved_expired_leases[i]->state_ =
+ Lease::STATE_EXPIRED_RECLAIMED;
}
ASSERT_NO_THROW(lmptr_->updateLease4(saved_expired_leases[i]));
}
// those that have even index.
for (Lease4Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
- int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ int index =
+ static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
}
}
- // This is the CQL implementation for GenericLeaseMgrTest::testGetExpiredLeases4().
+ // This is the CQL implementation for
+ // GenericLeaseMgrTest::testGetExpiredLeases4().
// The GenericLeaseMgrTest implementation checks for the order of expired
// leases to be from the most expired to the least expired. Cassandra
// doesn't support ORDER BY without imposing a EQ / IN restriction on the
// columns. Because of that, the order check has been excluded.
- void
- testCqlGetExpiredLeases6() {
+ void testCqlGetExpiredLeases6() {
// Get the leases to be used for the test.
vector<Lease6Ptr> leases = createLeases6();
// Make sure we have at least 6 leases there.
// valid lifetime to make it expired. The expiration time also
// depends on the lease index, so as we can later check that the
// leases are ordered by the expiration time.
- leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+ leases[i]->cltt_ =
+ current_time - leases[i]->valid_lft_ - 10 - i;
} else {
- // Set current time as cltt for remaining leases. These leases are
+ // Set current time as cltt for remaining leases. These leases
+ // are
// not expired.
leases[i]->cltt_ = current_time;
}
Lease6Collection expired_leases;
ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000));
// Leases with even indexes should be returned as expired.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ expired_leases.size());
// Update current time for the next test.
current_time = time(NULL);
// Also, remove expired leases collected during the previous test.
expired_leases.clear();
- // This time let's reverse the expiration time and see if they will be returned
+ // This time let's reverse the expiration time and see if they will be
+ // returned
// in the correct order.
for (size_t i = 0U; i < leases.size(); ++i) {
// Update the time of expired leases with even indexes.
if (i % 2U == 0U) {
- leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
+ leases[i]->cltt_ =
+ current_time - leases[i]->valid_lft_ - 1000 + i;
} else {
// Make sure remaining leases remain unexpired.
ASSERT_NO_THROW(lmptr_->updateLease6(leases[i]));
}
- // Retrieve expired leases again. The limit of 0 means return all expired
+ // Retrieve expired leases again. The limit of 0 means return all
+ // expired
// leases.
ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
// The same leases should be returned.
- ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), expired_leases.size());
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2U),
+ expired_leases.size());
// Remember expired leases returned.
std::vector<Lease6Ptr> saved_expired_leases = expired_leases;
// Mark every other expired lease as reclaimed.
for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
if (i % 2U != 0U) {
- saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+ saved_expired_leases[i]->state_ =
+ Lease::STATE_EXPIRED_RECLAIMED;
}
ASSERT_NO_THROW(lmptr_->updateLease6(saved_expired_leases[i]));
}
// those that have even index.
for (Lease6Collection::iterator lease = expired_leases.begin();
lease != expired_leases.end(); ++lease) {
- int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ int index =
+ static_cast<int>(std::distance(expired_leases.begin(), lease));
EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
}
}
/// Unlike other backend implementations, this one doesn't check for lacking
/// parameters. In that scenario, Cassandra defaults to configured parameters.
TEST(CqlOpenTest, OpenDatabase) {
-
// Schema needs to be created for the test to work.
destroyCqlSchema(false, true);
createCqlSchema(false, true);
// If it fails, print the error message.
try {
LeaseMgrFactory::create(validCqlConnectionString());
- EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
+ EXPECT_NO_THROW((void)LeaseMgrFactory::instance());
LeaseMgrFactory::destroy();
} catch (const isc::Exception& ex) {
FAIL() << "*** ERROR: unable to open database, reason:\n"
// Check that lease manager open the database opens correctly with a longer
// timeout. If it fails, print the error message.
try {
- string connection_string = validCqlConnectionString() + string(" ") +
- string(VALID_TIMEOUT);
+ std::string connection_string = validCqlConnectionString() +
+ std::string(" ") +
+ std::string(VALID_TIMEOUT);
LeaseMgrFactory::create(connection_string);
- EXPECT_NO_THROW((void) LeaseMgrFactory::instance());
+ EXPECT_NO_THROW((void)LeaseMgrFactory::instance());
LeaseMgrFactory::destroy();
} catch (const isc::Exception& ex) {
FAIL() << "*** ERROR: unable to open database, reason:\n"
// Check that wrong specification of backend throws an exception.
// (This is really a check on LeaseMgrFactory, but is convenient to
// perform here.)
- EXPECT_THROW(LeaseMgrFactory::create(connectionString(
- NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
+ EXPECT_THROW(
+ LeaseMgrFactory::create(connectionString(NULL, VALID_NAME, VALID_HOST,
+ INVALID_USER, VALID_PASSWORD)),
InvalidParameter);
+ EXPECT_THROW(
+ LeaseMgrFactory::create(connectionString(
+ INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
+ InvalidType);
+ // Check that invalid login data 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(
- INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
- InvalidType);
+ 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);
/// 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);
/// @brief Basic Lease4 Checks
///
-/// Checks that the addLease, getLease4(by address), getLease4(hwaddr, subnet_id),
+/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,
+/// subnet_id),
/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
/// (client-id is optional and may not be present)
TEST_F(CqlLeaseMgrTest, lease4NullClientId) {
testDeleteExpiredReclaimedLeases4();
}
-}; // Of anonymous namespace
+} // namespace
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#include <config.h>
#include <dhcp/dhcp6.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option4_addrlst.h>
return (host);
}
+bool
+GenericHostDataSourceTest::reservationExists(const IPv6Resrv& resrv,
+ const IPv6ResrvRange& range) {
+ for (IPv6ResrvIterator it = range.first; it != range.second; ++it) {
+ if (resrv == it->second) {
+ return true;
+ }
+ }
+ return false;
+}
+
void
GenericHostDataSourceTest::compareHwaddrs(const ConstHostPtr& host1,
const ConstHostPtr& host2,
compareOptions(host1->getCfgOption6(), host2->getCfgOption6());
}
+bool GenericHostDataSourceTest::compareHostsForSort4(
+ const ConstHostPtr& host1,
+ const ConstHostPtr& host2) {
+ if (host1->getIPv4SubnetID() < host2->getIPv4SubnetID()) {
+ return true;
+ }
+ return false;
+}
+
+bool GenericHostDataSourceTest::compareHostsForSort6(
+ const ConstHostPtr& host1,
+ const ConstHostPtr& host2) {
+ if (host1->getIPv6SubnetID() < host2->getIPv6SubnetID()) {
+ return true;
+ }
+ return false;
+}
+
DuidPtr
GenericHostDataSourceTest::HWAddrToDuid(const HWAddrPtr& hwaddr) {
if (!hwaddr) {
// Verify that the values returned are proper.
int i = 0;
+ if (hdsptr_->getType() == "cql") {
+ // There is no ORDER BY in Cassandra. Order here. Remove this if entries
+ // are eventually implemented as ordered in the Cassandra host data
+ // source.
+ std::sort(all_by_addr.begin(), all_by_addr.end(), compareHostsForSort4);
+ }
for (ConstHostCollection::const_iterator it = all_by_addr.begin();
it != all_by_addr.end(); ++it) {
EXPECT_EQ(IOAddress("192.0.2.1"), (*it)->getIPv4Reservation());
// Check that the returned values are as expected.
i = 0;
+ if (hdsptr_->getType() == "cql") {
+ // There is no ORDER BY in Cassandra. Order here. Remove this if entries
+ // are eventually implemented as ordered in the Cassandra host data
+ // source.
+ std::sort(all_by_id.begin(), all_by_id.end(), compareHostsForSort4);
+ }
for (ConstHostCollection::const_iterator it = all_by_id.begin();
it != all_by_id.end(); ++it) {
EXPECT_EQ(IOAddress("192.0.2.1"), (*it)->getIPv4Reservation());
// Check that the returned values are as expected.
int i = 0;
+ if (hdsptr_->getType() == "cql") {
+ // There is no ORDER BY in Cassandra. Order here. Remove this if entries
+ // are implemented as ordered in the Cassandra host data source.
+ std::sort(all_by_id.begin(), all_by_id.end(), compareHostsForSort6);
+ }
for (ConstHostCollection::const_iterator it = all_by_id.begin();
it != all_by_id.end(); ++it) {
EXPECT_EQ(IOAddress("0.0.0.0"), (*it)->getIPv4Reservation());
ASSERT_NO_FATAL_FAILURE(compareHosts(host, from_hds));
}
-}; // namespace test
-}; // namespace dhcp
-}; // namespace isc
+void
+GenericHostDataSourceTest::stressTest(unsigned int nOfHosts /* = 0xfffdU */) {
+ // Make sure we have a pointer to the host data source.
+ ASSERT_TRUE(hdsptr_);
+
+ // Make sure the variable part of the generated address fits in a 16-bit
+ // field.
+ ASSERT_LE(nOfHosts, 0xfffdU);
+
+ // Create hosts.
+ std::vector<HostPtr> hosts;
+ hosts.reserve(nOfHosts);
+ for (unsigned int i = 0x0001U; i < 0x0001U + nOfHosts; ++i) {
+ /// @todo: Check if this is written in hexadecimal format.
+ std::stringstream ss;
+ std::string n_host;
+ ss << std::hex << i;
+ ss >> n_host;
+
+ const std::string prefix = std::string("2001:db8::") + n_host;
+ hosts.push_back(initializeHost6(prefix, Host::IDENT_HWADDR, false));
+ IPv6ResrvRange range = hosts.back()->getIPv6Reservations();
+ ASSERT_EQ(1, std::distance(range.first, range.second));
+ EXPECT_TRUE(reservationExists(
+ IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress(prefix)), range));
+ }
+ const size_t hosts_size = hosts.size();
+
+ std::cout << "Starting to add hosts..." << std::endl;
+ struct timespec start, end;
+ start = (struct timespec){0, 0};
+ end = (struct timespec){0, 0};
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
+ for (std::vector<HostPtr>::const_iterator it = hosts.begin();
+ it != hosts.end(); it++) {
+ ASSERT_NO_THROW(hdsptr_->add(*it));
+ }
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
+ double s = static_cast<double>(end.tv_sec - start.tv_sec) +
+ static_cast<double>(end.tv_nsec - start.tv_nsec) / 1e9;
+ std::cout << "Adding " << hosts_size
+ << (hosts_size == 1 ? " host" : " hosts") << " took "
+ << std::fixed << std::setprecision(2) << s << " seconds."
+ << std::endl;
+
+ // And then try to retrieve them back.
+ std::cout << "Starting to retrieve hosts..." << std::endl;
+ start = (struct timespec){0, 0};
+ end = (struct timespec){0, 0};
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
+ for (std::vector<HostPtr>::const_iterator it = hosts.begin();
+ it != hosts.end(); it++) {
+ IPv6ResrvRange range = (*it)->getIPv6Reservations();
+ // This get6() call is particularly useful to test because it involves a
+ // subquery for MySQL and PostgreSQL and two separate queries for
+ // Cassandra.
+ ConstHostPtr from_hds =
+ hdsptr_->get6(range.first->second.getPrefix(), 128);
+ ASSERT_TRUE(from_hds);
+ compareHosts(*it, from_hds);
+ }
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
+ s = static_cast<double>(end.tv_sec - start.tv_sec) +
+ static_cast<double>(end.tv_nsec - start.tv_nsec) / 1e9;
+ std::cout << "Retrieving " << hosts_size
+ << (hosts_size == 1 ? " host" : " hosts") << " took "
+ << std::fixed << std::setprecision(2) << s << " seconds."
+ << std::endl;
+}
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
/// @return Identifier in textual form acceptable by Host constructor
std::vector<uint8_t> generateIdentifier(const bool new_identifier = true);
+ /// @brief Checks if the reservation is in the range of reservations.
+ ///
+ /// @param resrv Reservation to be searched for.
+ /// @param range Range of reservations returned by the @c Host object
+ /// in which the reservation will be searched
+ bool reservationExists(const IPv6Resrv& resrv, const IPv6ResrvRange& range);
+
/// @brief Compares hardware addresses of the two hosts.
///
/// This method compares two hwardware address and uses gtest
/// @param host2 second host to compare
void compareHosts(const ConstHostPtr& host1, const ConstHostPtr& host2);
+ /// @ brief Used to sort a host collection by IPv4 subnet id.
+ static bool compareHostsForSort4(const ConstHostPtr& host1,
+ const ConstHostPtr& host2);
+
+ /// @ brief Used to sort a host collection by IPv6 subnet id.
+ static bool compareHostsForSort6(const ConstHostPtr& host1,
+ const ConstHostPtr& host2);
+
/// @brief Compares two IPv6 reservation lists.
///
/// This method uses gtest macros to signal errors.
///
void testMessageFields4();
+ /// @brief Stress test on adding and retrieving hosts
+ ///
+ /// Rather than checking for correctness, this test gives interpretable
+ /// performance results.
+ ///
+ /// @param n_of_hosts number of hosts to insert into and retrieve from the
+ /// database; defaults to the maximum number of allowed hosts.
+ void stressTest(uint32_t n_of_hosts /* = 0xfffdU */);
+
/// @brief Returns DUID with identical content as specified HW address
///
/// This method does not have any sense in real life and is only useful
};
-}; // namespace test
-}; // namespace dhcp
-}; // namespace isc
+} // namespace test
+} // namespace dhcp
+} // namespace isc
#endif
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
-#include <string>
+
#include <cassandra.h>
+
#include <dhcpsrv/cql_connection.h>
#include <dhcpsrv/testutils/cql_schema.h>
+
+#include <stdlib.h>
+
#include <gtest/gtest.h>
#include <fstream>
#include <sstream>
-#include <stdlib.h>
+#include <string>
using namespace std;
string
validCqlConnectionString() {
- return (connectionString(CQL_VALID_TYPE, VALID_NAME, VALID_HOST,
- VALID_USER, VALID_PASSWORD));
+ return (connectionString(CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER,
+ VALID_PASSWORD));
}
-bool softWipeEnabled() {
+bool
+softWipeEnabled() {
const char* const env = getenv("KEA_TEST_CASSANDRA_WIPE");
if (env && (string(env) == string("soft"))) {
return (true);
return (false);
}
-void destroyCqlSchema(bool force_wipe, bool show_err) {
+void
+destroyCqlSchema(bool force_wipe, bool show_err) {
if (force_wipe || !softWipeEnabled()) {
// Do full wipe
runCqlScript(DATABASE_SCRIPTS_DIR, "cql/dhcpdb_drop.cql", show_err);
} else {
-
// do soft wipe (just remove the data, not the structures)
runCqlScript(DATABASE_SCRIPTS_DIR, "cql/soft_wipe.cql", show_err);
}
}
-void createCqlSchema(bool force_wipe, bool show_err) {
+void
+createCqlSchema(bool force_wipe, bool show_err) {
if (force_wipe || !softWipeEnabled()) {
- runCqlScript(DATABASE_SCRIPTS_DIR, "cql/dhcpdb_create.cql",
- show_err);
+ runCqlScript(DATABASE_SCRIPTS_DIR, "cql/dhcpdb_create.cql", show_err);
}
}
-void runCqlScript(const std::string& path, const std::string& script_name,
- bool show_err) {
+void
+runCqlScript(const std::string& path,
+ const std::string& script_name,
+ bool show_err) {
std::ostringstream cmd;
cmd << "cqlsh -u keatest -p keatest -k keatest";
if (!show_err) {
ASSERT_EQ(0, retval) << "runCqlSchema failed:" << cmd.str();
}
-
-};
-};
-};
+} // namespace test
+} // namespace dhcp
+} // namespace isc
/// @brief Run a CQL script against the CQL unit test database
///
-/// Submits the given SQL script to CQL via cqlsh CLI. The output of
+/// Submits the given CQL script to CQL via cqlsh CLI. The output of
/// stderr is suppressed unless the parameter, show_err is true. The is done
/// to suppress warnings that might otherwise make test output needlessly
/// noisy. A gtest assertion occurs if the script fails to execute.
sqlscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts/cql
sqlscripts_DATA = dhcpdb_create.cql
sqlscripts_DATA += dhcpdb_drop.cql
+sqlscripts_DATA += upgrade_1.0_to_2.0.sh
EXTRA_DIST = ${sqlscripts_DATA}
-- This line starts database initialization to 1.0.
-- Holds the IPv4 leases.
-CREATE TABLE lease4 (
+-- -----------------------------------------------------
+-- Table `lease4`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS lease4 (
address int,
hwaddr blob,
client_id blob,
);
-- Create search indexes for lease4 table
-CREATE INDEX lease4index1 ON lease4 (client_id);
-CREATE INDEX lease4index2 ON lease4 (subnet_id);
-CREATE INDEX lease4index3 ON lease4 (hwaddr);
-CREATE INDEX lease4index4 ON lease4 (expire);
-CREATE INDEX lease4index5 ON lease4 (state);
+CREATE INDEX IF NOT EXISTS lease4index1 ON lease4 (client_id);
+CREATE INDEX IF NOT EXISTS lease4index2 ON lease4 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease4index3 ON lease4 (hwaddr);
+CREATE INDEX IF NOT EXISTS lease4index4 ON lease4 (expire);
+CREATE INDEX IF NOT EXISTS lease4index5 ON lease4 (state);
-- Holds the IPv6 leases.
-- N.B. The use of a VARCHAR for the address is temporary for development:
-- it will eventually be replaced by BINARY(16).
-CREATE TABLE lease6 (
+-- -----------------------------------------------------
+-- Table `lease6`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS lease6 (
address varchar,
duid blob,
valid_lifetime bigint,
);
-- Create search indexes for lease6 table
-CREATE INDEX lease6index1 ON lease6 (lease_type);
-CREATE INDEX lease6index2 ON lease6 (duid);
-CREATE INDEX lease6index3 ON lease6 (iaid);
-CREATE INDEX lease6index4 ON lease6 (subnet_id);
-CREATE INDEX lease6index5 ON lease6 (expire);
-CREATE INDEX lease6index6 ON lease6 (state);
+CREATE INDEX IF NOT EXISTS lease6index1 ON lease6 (lease_type);
+CREATE INDEX IF NOT EXISTS lease6index2 ON lease6 (duid);
+CREATE INDEX IF NOT EXISTS lease6index3 ON lease6 (iaid);
+CREATE INDEX IF NOT EXISTS lease6index4 ON lease6 (subnet_id);
+CREATE INDEX IF NOT EXISTS lease6index5 ON lease6 (expire);
+CREATE INDEX IF NOT EXISTS lease6index6 ON lease6 (state);
-- ... and a definition of lease6 types. This table is a convenience for
-- users of the database - if they want to view the lease table and use the
-- type names, they can join this table with the lease6 table.
-- Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/
-- lease_mgr.h)
-CREATE TABLE lease6_types (
+-- -----------------------------------------------------
+-- Table `lease6_types`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS lease6_types (
lease_type int, -- Lease type code.
name varchar, -- Name of the lease type
PRIMARY KEY (lease_type)
-- users of the database - if they want to view the lease table and use the
-- type names, they can join this table with the lease6 table. For details,
-- see constants defined in src/lib/dhcp/dhcp/pkt.h for detailed explanation.
-CREATE TABLE lease_hwaddr_source (
+-- -----------------------------------------------------
+-- Table `lease_hwaddr_source`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS lease_hwaddr_source (
hwaddr_source int,
name varchar,
PRIMARY KEY (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.
-CREATE TABLE lease_state (
+-- -----------------------------------------------------
+-- Table `lease_state`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS lease_state (
state int,
name varchar,
PRIMARY KEY (state)
-- 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".
-CREATE TABLE schema_version (
+-- -----------------------------------------------------
+-- Table `schema_version`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS 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.
+
+-- This line starts database upgrade to version 2.0
+
+-- -----------------------------------------------------
+-- Table `host_reservations`
+-- -----------------------------------------------------
+CREATE TABLE IF NOT EXISTS host_reservations (
+ id bigint,
+ host_identifier blob,
+ host_identifier_type int,
+ host_ipv4_subnet_id int,
+ host_ipv6_subnet_id int,
+ host_ipv4_address int,
+ hostname text,
+ host_ipv4_client_classes text,
+ host_ipv6_client_classes text,
+ reserved_ipv6_prefix_address text,
+ reserved_ipv6_prefix_length int,
+ reserved_ipv6_prefix_address_type int,
+ iaid int,
+ option_universe int,
+ option_code int,
+ option_value blob,
+ option_formatted_value text,
+ option_space text,
+ option_is_persistent boolean,
+ option_client_class text,
+ option_subnet_id int,
+ PRIMARY KEY (id)
+);
+CREATE INDEX IF NOT EXISTS host_reservationsindex1 ON host_reservations (host_identifier);
+CREATE INDEX IF NOT EXISTS host_reservationsindex2 ON host_reservations (host_identifier_type);
+CREATE INDEX IF NOT EXISTS host_reservationsindex3 ON host_reservations (host_ipv4_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex4 ON host_reservations (host_ipv6_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex5 ON host_reservations (host_ipv4_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex6 ON host_reservations (reserved_ipv6_prefix_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex7 ON host_reservations (reserved_ipv6_prefix_length);
+
+DELETE FROM schema_version WHERE version=1;
+INSERT INTO schema_version (version, minor) VALUES(2, 0);
+
+-- This line concludes database upgrade to version 2.0
DROP TABLE IF EXISTS lease6;
DROP TABLE IF EXISTS lease6_types;
DROP TABLE IF EXISTS lease_hwaddr_source;
-DROP TABLE IF EXISTS schema_version;
-DROP TABLE IF EXISTS ipv6_reservations;
-DROP TABLE IF EXISTS hosts;
-DROP TABLE IF EXISTS dhcp4_options;
-DROP TABLE IF EXISTS dhcp6_options;
-DROP TABLE IF EXISTS host_identifier_type;
DROP TABLE IF EXISTS lease_state;
+DROP TABLE IF EXISTS schema_version;
+DROP TABLE IF EXISTS host_reservations;
+
+DROP INDEX IF EXISTS lease4index1;
+DROP INDEX IF EXISTS lease4index2;
+DROP INDEX IF EXISTS 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;
+
+DROP INDEX IF EXISTS host_reservationsindex1;
+DROP INDEX IF EXISTS host_reservationsindex2;
+DROP INDEX IF EXISTS host_reservationsindex3;
+DROP INDEX IF EXISTS host_reservationsindex4;
+DROP INDEX IF EXISTS host_reservationsindex5;
+DROP INDEX IF EXISTS host_reservationsindex6;
+DROP INDEX IF EXISTS host_reservationsindex7;
TRUNCATE TABLE lease4;
TRUNCATE TABLE lease6;
-TRUNCATE TABLE hosts;
-TRUNCATE TABLE dhcp4_options;
-TRUNCATE TABLE dhcp6_options;
+TRUNCATE TABLE lease6_types;
+TRUNCATE TABLE lease_hwaddr_source;
+TRUNCATE TABLE lease_state;
+TRUNCATE TABLE schema_version;
+TRUNCATE TABLE host_reservations;
--- /dev/null
+#!/bin/sh
+
+prefix=@prefix@
+# Include utilities. Use installed version if available and
+# use build version if it isn't.
+if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then
+ . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh
+else
+ . @abs_top_builddir@/src/bin/admin/admin-utils.sh
+fi
+
+version=$(cql_version "$@")
+
+if [ "${version}" != "1.0" ]; then
+ printf "This script upgrades 1.0 to 2.0. Reported version is %s. Skipping upgrade.\n" "${version}"
+ exit 0
+fi
+
+cqlsh "$@" <<EOF
+-- This line starts database upgrade to version 2.0
+
+-- -----------------------------------------------------
+-- Table \`host_reservations\`
+-- -----------------------------------------------------
+CREATE TABLE host_reservations (
+ id bigint,
+ host_identifier blob,
+ host_identifier_type int,
+ host_ipv4_subnet_id int,
+ host_ipv6_subnet_id int,
+ host_ipv4_address int,
+ hostname text,
+ host_ipv4_client_classes text,
+ host_ipv6_client_classes text,
+ reserved_ipv6_prefix_address text,
+ reserved_ipv6_prefix_length int,
+ reserved_ipv6_prefix_address_type int,
+ iaid int,
+ option_universe int,
+ option_code int,
+ option_value blob,
+ option_formatted_value text,
+ option_space text,
+ option_is_persistent boolean,
+ option_client_class text,
+ option_subnet_id int,
+ PRIMARY KEY (id)
+);
+CREATE INDEX IF NOT EXISTS host_reservationsindex1 ON host_reservations (host_identifier);
+CREATE INDEX IF NOT EXISTS host_reservationsindex2 ON host_reservations (host_identifier_type);
+CREATE INDEX IF NOT EXISTS host_reservationsindex3 ON host_reservations (host_ipv4_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex4 ON host_reservations (host_ipv6_subnet_id);
+CREATE INDEX IF NOT EXISTS host_reservationsindex5 ON host_reservations (host_ipv4_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex6 ON host_reservations (reserved_ipv6_prefix_address);
+CREATE INDEX IF NOT EXISTS host_reservationsindex7 ON host_reservations (reserved_ipv6_prefix_length);
+
+DELETE FROM schema_version WHERE version=1;
+INSERT INTO schema_version (version, minor) VALUES(2, 0);
+
+-- This line concludes database upgrade to version 2.0
+EOF
+
+exit $?
sqlscripts_DATA += upgrade_4.0_to_4.1.sh
sqlscripts_DATA += upgrade_4.1_to_5.0.sh
-
EXTRA_DIST = ${sqlscripts_DATA}