else
AC_MSG_CHECKING([if $XSLTPROC works])
# run xsltproc to see if works
- $XSLTPROC --novalid --xinclude --nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
+ $XSLTPROC --novalid --xinclude http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
if test $? -ne 0 ; then
AC_MSG_ERROR("Error with $XSLTPROC using release/xsl/current/manpages/docbook.xsl")
fi
- $XSLTPROC --novalid --xinclude --nonet http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
+ $XSLTPROC --novalid --xinclude http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
if test $? -ne 0 ; then
AC_MSG_ERROR("Error with $XSLTPROC using release/xsl/current/html/docbook.xsl")
fi
<listitem>
<simpara>
<command>lease-dump</command> —
- Dumps the contents of the lease database (for MySQL or PostgreSQL
- backends) to CSV text file. The first line of the file contains
- the column names. This is meant to be used as a diagnostic
- tool that provides a portable, human-readable form of lease data.
+ Dumps the contents of the lease database (for MySQL, PostgreSQL or
+ CQL backends) to CSV text file. The first line of the file contains
+ the column names. This is meant to be used as a diagnostic tool
+ that provides a portable, human-readable form of lease data.
</simpara>
</listitem>
</itemizedlist>
database.
</simpara>
</listitem>
+
+ <listitem>
+ <simpara>
+ <command>cql</command> —
+ Lease information is stored in a CQL database.
+ </simpara>
+ </listitem>
+
</itemizedlist>
Additional parameters may be needed, depending on your setup
</para>
</section>
</section> <!-- end of PostgreSQL sections -->
+
+ <section>
+ <title>CQL</title>
+
+ <para>
+ The CQL database must be properly set up if you want Kea to store
+ information in CQL. This section can be safely ignored if you chose to
+ store the data in other backends.
+ </para>
+
+ <section id="cql-database-create">
+ <title>First Time Creation of Kea Database</title>
+
+ <para>
+ If you are setting up the CQL database for the first time, you need to
+ create the keyspace area within CQL. This needs to be done manually:
+ <command>kea-admin</command> is not able to do this for you.
+ </para>
+
+ <para>
+ To create the database:
+ <orderedlist>
+ <listitem>
+ <para>
+ Export CQLSH_HOST environemnt variable:
+<screen>
+$ <userinput>export CQLSH_HOST=localhost</userinput>
+</screen>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Log into CQL:
+<screen>
+$ <userinput>cqlsh</userinput>
+cql>
+</screen>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Create the CQL keyspace:
+<screen>
+cql> <userinput>CREATE KEYSPACE keyspace-name WITH replication = {'class' : 'SimpleStrategy','replication_factor' : 1};</userinput>
+</screen>
+ (<replaceable>keyspace-name</replaceable> is the name you have
+ chosen for the keyspace)
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ At this point, you may elect to create the database tables.
+ (Alternatively, you can exit CQL and create the tables using the
+ <command>kea-admin</command> tool, as explained below) To do this:
+<screen>
+<userinput>cqslh -k <replaceable>keyspace-name</replaceable> -f <replaceable>path-to-kea</replaceable>/share/kea/scripts/cql/dhcpdb_create.cql</userinput>
+</screen>
+ (<replaceable>path-to-kea</replaceable> is the location where you
+ installed Kea)
+ </para>
+ </listitem>
+ </orderedlist>
+ </para>
+
+ <para>
+ If you elected not to create the tables in step 4, you can do
+ so now by running the <command>kea-admin</command> tool:
+<screen>
+$ <userinput>kea-admin lease-init cql -n <replaceable>database-name</replaceable></userinput>
+</screen>
+ (Do not do this if you did create the tables in step 4.)
+ <command>kea-admin</command> implements rudimentary checks:
+ it will refuse to initialize a database that contains any
+ existing tables. If you want to start from scratch, you
+ must remove all data manually. (This process is a manual
+ operation on purpose to avoid possibly irretrievable mistakes
+ by <command>kea-admin</command>)
+ </para>
+ </section>
+
+ <section id="cql-upgrade">
+ <title>Upgrading a CQL Database from an Earlier Version of Kea</title>
+
+ <para>
+ Sometimes a new Kea version may use newer database schema, so
+ there will be a need to upgrade the existing database. This can
+ be done using the <command>kea-admin lease-upgrade</command>
+ command.
+ </para>
+
+ <para>
+ To check the current version of the database, use the following command:
+<screen>
+$ <userinput>kea-admin lease-version cql -n <replaceable>database-name</replaceable></userinput>
+</screen>
+ (See <xref linkend="kea-database-version"/> for a discussion
+ about versioning) If the version does not match the minimum
+ required for the new version of Kea (as described in the
+ release notes), the database needs to be upgraded.
+ </para>
+
+ <para>
+ Before upgrading, please make sure that the database is
+ backed up. The upgrade process does not discard any data but,
+ depending on the nature of the changes, it may be impossible
+ to subsequently downgrade to an earlier version. To perform
+ an upgrade, issue the following command:
+<screen>
+$ <userinput>kea-admin lease-upgrade cql -n <replaceable>database-name</replaceable></userinput>
+</screen>
+ </para>
+ </section>
+ </section> <!-- end of CQL sections -->
+
<section>
<title>Limitations related to the use of the SQL databases</title>
<section>
<title>Lease Storage</title>
<para>All leases issued by the server are stored in the lease database.
- Currently there are three database backends available:
- memfile (which is the default backend), MySQL and PostgreSQL.</para>
+ Currently there are four database backends available: memfile (which is the
+ default backend), MySQL, PostgreSQL and CQL.</para>
<section>
<title>Memfile, Basic Storage for Leases</title>
"Dhcp4": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
</screen>
Next, the name of the database to hold the leases must be set: this is the
- name used when the lease database was created (see <xref linkend="mysql-database-create"/>
- or <xref linkend="pgsql-database-create"/>).
+ name used when the lease database was created
+ (see <xref linkend="mysql-database-create"/>,
+ <xref linkend="pgsql-database-create"/> or
+ <xref linkend="cql-database-create"/>).
<screen>
"Dhcp4": { "lease-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
</screen>
<section>
<title>Lease Storage</title>
<para>All leases issued by the server are stored in the lease database.
- Currently there are three database backends available:
- memfile (which is the default backend), MySQL and PostgreSQL.</para>
+ Currently there are four database backends available: memfile (which is the
+ default backend), MySQL, PostgreSQL and CQL.</para>
<section>
<title>Memfile, Basic Storage for Leases</title>
<para>Lease database configuration is controlled through the
Dhcp6/lease-database parameters. The type of the database must be set to
- "memfile", "mysql" or "postgresql", e.g.
+ "memfile", "mysql", "postgresql" or "cql", e.g.
<screen>
"Dhcp6": { "lease-database": { <userinput>"type": "mysql"</userinput>, ... }, ... }
</screen>
Next, the name of the database is to hold the leases must be set: this is the
- name used when the lease database was created (see <xref linkend="mysql-database-create"/>
- or <xref linkend="pgsql-database-create"/>).
+ name used when the lease database was created
+ (see <xref linkend="mysql-database-create"/>,
+ <xref linkend="pgsql-database-create"/>
+ or <xref linkend="cql-database-create"/>).
<screen>
"Dhcp6": { "lease-database": { <userinput>"name": "<replaceable>database-name</replaceable>" </userinput>, ... }, ... }
</screen>
that are stored in the lease database. Removing non-last subnet will
cause the configuration information to mismatch data in the lease
database. It is possible to manually update subnet-id fields in
- MySQL or PostgreSQL database, but it is awkward and error prone
- process. A better reconfiguration support is planned.
+ MySQL, PostgreSQL or CQL database, but it is awkward and
+ error prone process. A better reconfiguration support is planned.
</para>
</listitem>
<para>
<userinput>./configure</userinput> when it succeeds displays a report
- with the building parameters. This report is saved into
+ with the building parameters. This report is saved into
<filename>config.report</filename> and embedded into executable
binaries, e.g., <userinput>kea-dhcp4</userinput>.
</para>
built without PostgreSQL support.
</simpara>
</listitem>
+
+ <listitem>
+ <simpara>
+ In order to store lease information in a CQL database, Kea requires CQL
+ headers and libraries. This is an optional dependency in that Kea can be
+ built without CQL support.
+ </simpara>
+ </listitem>
</itemizedlist>
</section>
select_where_clause=""
if [ $dump_type -eq 4 ]; then
- dump_qry="SELECT address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state FROM keatest.lease4"
+ dump_qry="SELECT address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state FROM lease4"
select_where_clause=" WHERE address = 0" # invalid address
elif [ $dump_type -eq 6 ]; then
- dump_qry="SELECT address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state FROM keatest.lease6"
+ dump_qry="SELECT address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,hwtype,hwaddr_source,state FROM lease6"
select_where_clause=" WHERE address = '::'" # invalid address
fi
skeyspace = getParameter("keyspace");
keyspace = skeyspace.c_str();
} catch (...) {
- // No database name. Fine, we'll use default "keatest".
+ // No keyspace name. Fine, we'll use default "keatest".
}
cluster_ = cass_cluster_new();
// DELETE_LEASE4
{ delete_lease4_params,
"delete_lease4",
- "DELETE FROM lease4 WHERE address = ?" },
+ "DELETE FROM lease4 WHERE address = ? "
+ "IF EXISTS" },
// DELETE_LEASE4_STATE_EXPIRED
{ delete_expired_lease4_params,
"valid_lifetime, expire, subnet_id, "
"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 = ?" },
+ "DELETE FROM lease6 WHERE address = ? "
+ "IF EXISTS" },
// DELETE_LEASE6_STATE_EXPIRED
{ delete_expired_lease6_params,
"lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
"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,
"valid_lifetime, expire, subnet_id, "
"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,
"valid_lifetime, expire, subnet_id, "
"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,
"fqdn_fwd, fqdn_rev, hostname, state "
"FROM lease4 "
"WHERE state = ? AND expire < ? "
- "LIMIT ? ALLOW FILTERING" },
+ "LIMIT ? "
+ "ALLOW FILTERING" },
// GET_LEASE6_ADDR
{ get_lease6_addr_params,
"lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
"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,
"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" },
+ "WHERE duid = ? AND iaid = ? AND lease_type = ? "
+ "ALLOW FILTERING" },
// GET_LEASE6_DUID_IAID_SUBID
{ get_lease6_duid_iaid_subid_params,
"lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
"hwaddr, hwtype, hwaddr_source, state "
"FROM lease6 "
- "WHERE duid = ? AND iaid = ? AND subnet_id = ? AND lease_type = ? ALLOW FILTERING" },
+ "WHERE duid = ? AND iaid = ? AND subnet_id = ? AND lease_type = ? "
+ "ALLOW FILTERING" },
// GET_LEASE6_EXPIRE
{ get_lease6_expired_params,
"lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
"hwaddr, hwtype, hwaddr_source, state "
"FROM lease6 "
- //"WHERE state != ? AND expire < ? ORDER BY expire ASC "
"WHERE state = ? AND expire < ? "
- "LIMIT ? ALLOW FILTERING" },
+ "LIMIT ? "
+ "ALLOW FILTERING" },
// GET_VERSION
{ get_version_params,
"valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, "
"state) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
- },
+ "IF NOT EXISTS" },
// INSERT_LEASE6
{ insert_lease6_params,
"lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, "
"hwtype, hwaddr_source, state) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
- },
+ "IF NOT EXISTS" },
// UPDATE_LEASE4
{ update_lease4_params,
"client_id = ?, valid_lifetime = ?, expire = ?, "
"subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, state = ? "
"WHERE address = ? "
- },
+ "IF EXISTS" },
// UPDATE_LEASE6
{ update_lease6_params,
"prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, hostname = ?, "
"hwaddr = ?, hwtype = ?, hwaddr_source = ?, state = ? "
"WHERE address = ? "
- },
+ "IF EXISTS" },
// End of list sentinel
{ NULL, NULL, NULL }
/// 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 = 11U;
+ const size_t MAX_COLUMNS = 12U;
memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
// Set the column names
size_t offset = 0U;
- BOOST_STATIC_ASSERT(11U == MAX_COLUMNS);
+ 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_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);
}
data.add(&addr4_);
// hwaddr: blob
- hwaddr_ = lease_->hwaddr_->hwaddr_;
+ 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);
+ }
+ hwaddr_ = hwaddr->hwaddr_;
+ } else {
+ hwaddr_.clear();
+ }
hwaddr_length_ = hwaddr_.size();
data.add(&hwaddr_);
/// 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;
data.add(reinterpret_cast<void*>(&state_));
size.add(NULL);
- for (int i = 0; i < 10; i++) {
- CqlLeaseMgr::getData(row, i, data, size, *this);
+ for (size_t i = 0; i < data.size(); i++) {
+ CqlLeaseMgr::getData(row, i, data, size, i, *this);
}
// hwaddr: blob
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 = 17U;
+ const size_t MAX_COLUMNS = 18U;
memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
memset(duid_buffer_, 0, sizeof(duid_buffer_));
// Set the column names
size_t offset = 0U;
- BOOST_STATIC_ASSERT(17U == MAX_COLUMNS);
+ 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_STRING)));
+ 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_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);
}
// 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);
+ }
hwaddr_ = hwaddr->hwaddr_;
} else {
hwaddr_.clear();
data.add(reinterpret_cast<void*>(&state_));
size.add(NULL);
- for (int i = 0; i < 16; i++) {
- CqlLeaseMgr::getData(row, i, data, size, *this);
+ for (size_t i = 0; i < data.size(); i++) {
+ CqlLeaseMgr::getData(row, i, data, size, i, *this);
}
// address: varchar
}
void
-CqlLeaseMgr::getData(const CassRow* row, int pindex, CqlDataArray& data,
- CqlDataArray& size, const SqlExchange& exchange) {
+CqlLeaseMgr::getData(const CassRow* row, const int pindex, CqlDataArray& data,
+ CqlDataArray& size, const int dindex, const SqlExchange& exchange) {
const CassValue* value;
if (pindex >= exchange.parameters_.size()) {
return;
if (type >= sizeof(CqlFunctions) / sizeof(CqlFunctions[0])) {
isc_throw(BadValue, "index " << type << " out of bounds");
}
- CqlFunctions[type].sqlGetFunction_(value, data.values_[pindex],
- reinterpret_cast<size_t *>(size.values_[pindex]));
+ CqlFunctions[type].sqlGetFunction_(value, data.values_[dindex],
+ reinterpret_cast<size_t *>(size.values_[dindex]));
}
}
if (rc != CASS_OK) {
cass_future_free(future);
cass_statement_free(statement);
- isc_throw(DbOperationError, error);
+ return false;
}
+
+ // 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);
+ }
+
+ // Free resources.
+ cass_iterator_free(rows);
cass_result_free(resultCollection);
cass_future_free(future);
cass_statement_free(statement);
- return (true);
+ return applied;
}
bool
const CassResult* resultCollection = cass_future_get_result(future);
CassIterator* rows = cass_iterator_from_result(resultCollection);
-
int rowCount = 0;
while (cass_iterator_next(rows)) {
rowCount++;
const size_t max_leases,
StatementIndex statement_index) const {
// Set up the WHERE clause value
- //"WHERE state != ? AND expire < ? ORDER BY expire ASC "
uint32_t keepState = Lease::STATE_EXPIRED_RECLAIMED;
uint64_t timestamp = static_cast<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<uint32_t>(max_leases) :
- std::numeric_limits<uint32_t>::max();
+ uint32_t limit = max_leases > 0 ? static_cast<int32_t>(max_leases) :
+ std::numeric_limits<int32_t>::max();
for (uint32_t state = Lease::STATE_DEFAULT;
state <= Lease::STATE_EXPIRED_RECLAIMED; state++) {
isc_throw(DbOperationError, 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);
+ }
+
+ // Free resources.
+ cass_iterator_free(rows);
cass_result_free(resultCollection);
cass_future_free(future);
cass_statement_free(statement);
+
+ if (!applied) {
+ isc_throw(NoSuchLease, "Statement has not been applied.");
+ }
}
void
std::string error;
dbconn_.checkStatementError(error, future, stindex, "unable to DELETE");
rc = cass_future_error_code(future);
- cass_future_free(future);
- cass_statement_free(statement);
if (rc != CASS_OK) {
- isc_throw(DbOperationError, error);
+ cass_future_free(future);
+ cass_statement_free(statement);
+ isc_throw(DbOperationError, 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);
}
- return (true);
+ // Free resources.
+ cass_iterator_free(rows);
+ cass_result_free(resultCollection);
+ cass_future_free(future);
+ cass_statement_free(statement);
+
+ return applied;
}
bool
CqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
StatementIndex statement_index) {
// Set up the WHERE clause value
- //"WHERE state = ? AND expire < ? ALLOW FILTERING"
CqlDataArray data;
uint32_t result = 0;
isc_throw(DbOperationError, error);
}
+ // Get major and minor versions.
const CassResult* resultCollection = cass_future_get_result(future);
CassIterator* rows = cass_iterator_from_result(resultCollection);
CqlDataArray data;
CqlDataArray size;
- int rowCount = 0;
while (cass_iterator_next(rows)) {
- rowCount++;
const CassRow* row = cass_iterator_get_row(rows);
// version: uint32_t
data.add(reinterpret_cast<void*>(&version));
// minor: uint32_t
data.add(reinterpret_cast<void*>(&minor));
size.add(NULL);
-
- for (int i = 0; i < 2; i++) {
- CqlLeaseMgr::getData(row, i, data, size, *versionExchange_);
+ for (size_t i = 0; i < data.size(); i++) {
+ CqlLeaseMgr::getData(row, i, data, size, i, *versionExchange_);
}
}
}
values_.erase(values_.begin() + index);
}
+ /// Get size.
+ size_t size() {
+ return values_.size();
+ }
};
class CqlVersionExchange;
/// @param data array that has been created for the type of lease in question.
/// @param data size TODO
/// @param exchange Exchange object to use
- static void getData(const CassRow* row, int pindex, CqlDataArray& data,
- CqlDataArray& size, const SqlExchange& exchange);
+ static void getData(const CassRow* row, const int pindex, CqlDataArray& data,
+ CqlDataArray& size, const int dindex, const SqlExchange& exchange);
private:
/// 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.
+ /// Parameter is ignored for CQL backend as the v4 and v6 leases share the
+ /// same keyspace.
void reopen(Universe) {
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(validCqlConnectionString());
lmptr_ = &(LeaseMgrFactory::instance());
}
+ // 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() {
+ // Get the leases to be used for the test.
+ vector<Lease4Ptr> leases = createLeases4();
+ // Make sure we have at least 6 leases there.
+ ASSERT_GE(leases.size(), 6U);
+
+ // Use the same current time for all leases.
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0U; i < leases.size(); ++i) {
+ // Mark every other lease as expired.
+ if (i % 2U == 0U) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. The expiration time also
+ // depends on the lease index, so as we can later check that the
+ // leases are ordered by the expiration time.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+ } else {
+ // Set current time as cltt for remaining leases. These leases are
+ // not expired.
+ leases[i]->cltt_ = current_time;
+ }
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Retrieve at most 1000 expired leases.
+ Lease4Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 1000));
+ // Leases with even indexes should be returned as expired.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), 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
+ // 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;
+ } 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
+ // 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());
+
+ // Remember expired leases returned.
+ std::vector<Lease4Ptr> saved_expired_leases = expired_leases;
+
+ // Remove expired leases again.
+ expired_leases.clear();
+
+ // Limit the number of leases to be returned to 2.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 2));
+
+ // Make sure we have exactly 2 leases returned.
+ ASSERT_EQ(2U, expired_leases.size());
+
+ // Mark every other expired lease as reclaimed.
+ for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
+ if (i % 2U != 0U) {
+ saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease4(saved_expired_leases[i]));
+ }
+
+ expired_leases.clear();
+
+ // This the returned leases should exclude reclaimed ones. So the number
+ // of returned leases should be roughly half of the expired leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases4(expired_leases, 0U));
+ ASSERT_EQ(static_cast<size_t>(saved_expired_leases.size() / 2U),
+ expired_leases.size());
+
+ // Make sure that returned leases are those that are not reclaimed, i.e.
+ // those that have even index.
+ for (Lease4Collection::iterator lease = expired_leases.begin();
+ lease != expired_leases.end(); ++lease) {
+ int index = static_cast<int>(std::distance(expired_leases.begin(), lease));
+ EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
+ }
+ }
+
+ // 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() {
+ // Get the leases to be used for the test.
+ vector<Lease6Ptr> leases = createLeases6();
+ // Make sure we have at least 6 leases there.
+ ASSERT_GE(leases.size(), 6U);
+
+ // Use the same current time for all leases.
+ time_t current_time = time(NULL);
+
+ // Add them to the database
+ for (size_t i = 0U; i < leases.size(); ++i) {
+ // Mark every other lease as expired.
+ if (i % 2U == 0U) {
+ // Set client last transmission time to the value older than the
+ // valid lifetime to make it expired. The expiration time also
+ // depends on the lease index, so as we can later check that the
+ // leases are ordered by the expiration time.
+ leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 10 - i;
+
+ } else {
+ // Set current time as cltt for remaining leases. These leases are
+ // not expired.
+ leases[i]->cltt_ = current_time;
+ }
+ ASSERT_TRUE(lmptr_->addLease(leases[i]));
+ }
+
+ // Retrieve at most 1000 expired leases.
+ Lease6Collection expired_leases;
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 1000));
+ // Leases with even indexes should be returned as expired.
+ ASSERT_EQ(static_cast<size_t>(leases.size() / 2U), 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
+ // 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;
+
+ } else {
+ // Make sure remaining leases remain unexpired.
+ leases[i]->cltt_ = current_time + 100;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease6(leases[i]));
+ }
+
+ // 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());
+
+ // Remember expired leases returned.
+ std::vector<Lease6Ptr> saved_expired_leases = expired_leases;
+
+ // Remove expired leases again.
+ expired_leases.clear();
+
+ // Limit the number of leases to be returned to 2.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 2));
+
+ // Make sure we have exactly 2 leases returned.
+ ASSERT_EQ(2U, expired_leases.size());
+
+ // Mark every other expired lease as reclaimed.
+ for (size_t i = 0U; i < saved_expired_leases.size(); ++i) {
+ if (i % 2U != 0U) {
+ saved_expired_leases[i]->state_ = Lease::STATE_EXPIRED_RECLAIMED;
+ }
+ ASSERT_NO_THROW(lmptr_->updateLease6(saved_expired_leases[i]));
+ }
+
+ expired_leases.clear();
+
+ // This the returned leases should exclude reclaimed ones. So the number
+ // of returned leases should be roughly half of the expired leases.
+ ASSERT_NO_THROW(lmptr_->getExpiredLeases6(expired_leases, 0));
+
+ // Make sure that returned leases are those that are not reclaimed, i.e.
+ // 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));
+ EXPECT_EQ(saved_expired_leases[2 * index]->addr_, (*lease)->addr_);
+ }
+ }
};
/// @brief Check that database can be opened
/// 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.
-
+/// 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.
EXPECT_EQ(cltt, converted_cltt);
}
-/// @brief Check getName() returns correct database name
+/// @brief Check getName() returns correct keyspace name.
TEST_F(CqlLeaseMgrTest, getName) {
EXPECT_EQ(std::string("keatest"), lmptr_->getName());
}
/// the order from most to least expired. It also checks that the lease
/// which is marked as 'reclaimed' is not returned.
TEST_F(CqlLeaseMgrTest, getExpiredLeases4) {
- testGetExpiredLeases4();
+ testCqlGetExpiredLeases4();
}
/// @brief Check that the expired DHCPv6 leases can be retrieved.
/// the order from most to least expired. It also checks that the lease
/// which is marked as 'reclaimed' is not returned.
TEST_F(CqlLeaseMgrTest, getExpiredLeases6) {
- testGetExpiredLeases6();
+ testCqlGetExpiredLeases6();
}
/// @brief Check that expired reclaimed DHCPv6 leases are removed.
void
GenericLeaseMgrTest::testAddGetDelete6(bool check_t1_t2) {
- IOAddress addr("2001:db8:1::456");
+ const std::string addr234("2001:db8:1::234");
+ const std::string addr456("2001:db8:1::456");
+ const std::string addr789("2001:db8:1::789");
+ IOAddress addr(addr456);
uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
DuidPtr duid(new DUID(llt, sizeof(llt)));
// should not be allowed to add a second lease with the same address
EXPECT_FALSE(lmptr_->addLease(lease));
- Lease6Ptr x = lmptr_->getLease6(Lease::TYPE_NA,
- IOAddress("2001:db8:1::234"));
+ Lease6Ptr x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr234));
EXPECT_EQ(Lease6Ptr(), x);
- x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
+ x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
ASSERT_TRUE(x);
EXPECT_EQ(x->addr_, addr);
EXPECT_FALSE(y);
// should return false - there's no such address
- EXPECT_FALSE(lmptr_->deleteLease(IOAddress("2001:db8:1::789")));
+ EXPECT_FALSE(lmptr_->deleteLease(IOAddress(addr789)));
// this one should succeed
- EXPECT_TRUE(lmptr_->deleteLease(IOAddress("2001:db8:1::456")));
+ EXPECT_TRUE(lmptr_->deleteLease(IOAddress(addr456)));
// after the lease is deleted, it should really be gone
- x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
+ x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
EXPECT_FALSE(x);
// Reopen the lease storage to make sure that lease is gone from the
// persistent storage.
reopen(V6);
- x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::456"));
+ x = lmptr_->getLease6(Lease::TYPE_NA, IOAddress(addr456));
EXPECT_FALSE(x);
}
// Update the time of expired leases with even indexes.
if (i % 2 == 0) {
leases[i]->cltt_ = current_time - leases[i]->valid_lft_ - 1000 + i;
-
} else {
// Make sure remaining leases remain unexpired.
leases[i]->cltt_ = current_time + 100;