Commit c807388d authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[master] CQL now supports lease stat recounting

    Merges branch 'trac5487'
parents 86fcf22c f81e8464
......@@ -58,6 +58,24 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(override method support)
feature="override method"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[class Foo {
public:
virtual ~Foo() {};
virtual void foobar();
};
class Bar : public Foo {
public:
virtual ~Bar() {};
virtual void foobar() override;
};],[])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(aggregate initialization support)
feature="aggregate initialization"
AC_COMPILE_IFELSE(
......@@ -107,6 +125,19 @@ for retry in "none" "--std=c++11" "--std=c++0x" "--std=c++1x" "fail"; do
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(constexpr support)
feature="constexpr"
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[#include <string>
typedef char const* const Tag;
constexpr Tag FOOBAR = "FOOBAR";],
[const std::string foobar(FOOBAR);
return static_cast<int>(foobar.length());])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
continue])
AC_MSG_CHECKING(lambda support)
feature="lambda"
AC_COMPILE_IFELSE(
......
......@@ -143,8 +143,7 @@ public:
/// @brief Constructor
///
/// Specifies table columns.
/// @param connection specifies the connection to conduct this exchange on
CqlHostExchange(CqlConnection& connection);
CqlHostExchange();
/// @brief Virtual destructor.
virtual ~CqlHostExchange();
......@@ -272,9 +271,6 @@ private:
/// Pointer to Host object holding information being inserted into database.
HostPtr host_;
/// @brief Connection to the Cassandra database
CqlConnection& connection_;
/// @brief Primary key. Aggregates: host_identifier, host_identifier_type,
/// reserved_ipv6_prefix_address, reserved_ipv6_prefix_length, option_code,
/// option_space.
......@@ -602,9 +598,9 @@ StatementMap CqlHostExchange::tagged_statements_ = {
};
CqlHostExchange::CqlHostExchange(CqlConnection& connection)
: host_(NULL), connection_(connection), id_(0), host_identifier_type_(0),
host_ipv4_subnet_id_(0), host_ipv6_subnet_id_(0), host_ipv4_address_(0),
CqlHostExchange::CqlHostExchange()
: host_(NULL), id_(0), host_identifier_type_(0), host_ipv4_subnet_id_(0),
host_ipv6_subnet_id_(0), host_ipv4_address_(0),
reserved_ipv6_prefix_length_(NULL_RESERVED_IPV6_PREFIX_LENGTH),
reserved_ipv6_prefix_address_type_(NULL_RESERVED_IPV6_PREFIX_ADDRESS_TYPE),
iaid_(NULL_IAID), option_universe_(NULL_OPTION_UNIVERSE),
......@@ -1733,7 +1729,7 @@ CqlHostDataSourceImpl::getHostCollection(StatementTag statement_tag,
AnyArray& where_values) const {
// Run statement.
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange(dbconn_));
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
AnyArray collection = host_exchange->executeSelect(dbconn_, where_values,
statement_tag, false);
......@@ -1774,7 +1770,7 @@ CqlHostDataSourceImpl::insertHost(const HostPtr& host,
const OptionDescriptor& option_descriptor) {
AnyArray assigned_values;
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange(dbconn_));
std::unique_ptr<CqlHostExchange> host_exchange(new CqlHostExchange());
try {
host_exchange->createBindForMutation(
......
// Copyright (C) 2016-2017 Deutsche Telekom AG.
// Copyright (C) 2016-2018 Deutsche Telekom AG.
//
// Author: Andrei Pavel <andrei.pavel@qualitance.com>
//
......
......@@ -1435,12 +1435,300 @@ CqlLease6Exchange::getExpiredLeases(const size_t &max_leases,
}
}
/// @brief Base CQL derivation of the statistical lease data query
///
/// This class provides the functionality such as results storage and row
/// fetching common to fulfilling the statistical lease data query.
///
class CqlLeaseStatsQuery : public LeaseStatsQuery {
public:
/// @brief Constructor
///
/// @param conn An open connection to the database housing the lease data
/// @param statement The lease data SQL prepared statement tag to execute
/// @param fetch_type Indicates whether or not lease_type should be
/// fetched from the result set (should be true for v6)
CqlLeaseStatsQuery(CqlConnection& conn, StatementTag& statement,
const bool fetch_type)
: conn_(conn), statement_(statement), fetch_type_(fetch_type),
cummulative_rows_(), next_row_(cummulative_rows_.begin()),
subnet_id_(0), lease_type_(0), lease_state_(0) {
}
/// @brief Destructor
virtual ~CqlLeaseStatsQuery() {};
/// @brief Creates the lease statistical data result set
///
/// The result set is populated by executing a prepared SQL query
/// against the database which sums the leases per lease state per
/// subnet id. Positions internal row tracking to point to the
/// first row of the aggregate results.
void start();
/// @brief Executes protocol specific lease query SELECT statement
///
/// Currently we do not have a good way for Cassandra to roll up the
/// lease counts per subnet, type, and state as we do the other back
/// ends. This method executes the select statement which returns
/// a result set containing a row of data for every lease:
/// -v4 - subnet-id, lease-state
/// -v6 - subnet-id, lease-type, lease-state
///
/// It then iterates over this result set, aggregating the data into a
/// a map of LeaseStatRows.
///
/// If we didn't have to roll up the raw lease data first, we could
/// have derived this class from CqlExchange and used it's executeSelect
/// (from which this method borrows heavily). However, that would mean
/// copying all the raw lease data into a collection returned by
/// executeSelect and then aggregating that into cummulative rows.
/// The way we are now we go turn the raw lease data directly into the
/// cummulative row map.
///
/// @param connection connection used to communicate with the Cassandra
/// database
/// @param where_values array of bound objects used to filter the results
/// @param statement_tag prepared statement being executed
///
/// @throw DbOperationError
void executeSelect(const CqlConnection& connection, const AnyArray& data,
StatementTag statement_tag);
/// @brief Fetches the next row in the result set
///
/// Once the internal result set has been populated by invoking the
/// the start() method, this method is used to iterate over the
/// result set rows. Once the last row has been fetched, subsequent
/// calls will return false.
///
/// @param row Storage for the fetched row
///
/// @return True if the fetch succeeded, false if there are no more
/// rows to fetch.
bool getNextRow(LeaseStatsRow& row);
/// @brief Create BIND array to receive C++ data.
///
/// Used in executeSelect() to retrieve from database
///
/// @param data array of bound objects representing data to be retrieved
/// @param statement_tag prepared statement being executed; defaults to an
/// invalid index
virtual void
createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL);
/// @brief Statement tags definitions
/// @{
// Return recalculated lease4 lease statistics
static constexpr StatementTag RECOUNT_LEASE4_STATS = "RECOUNT_LEASE4_STATS";
// Return recalculated lease6 lease statistics
static constexpr StatementTag RECOUNT_LEASE6_STATS = "RECOUNT_LEASE6_STATS";
/// @}
/// @brief Cassandra statements
static StatementMap tagged_statements_;
private:
/// @brief Database connection to use to execute the query
CqlConnection& conn_;
/// @brief The query's prepared statement tag
StatementTag statement_;
/// @brief Indicates if query supplies lease type
bool fetch_type_;
/// @brief map containing the aggregated lease counts
std::map<LeaseStatsRow, int> cummulative_rows_;
/// @brief cursor pointing to the next row to read in aggregate map
std::map<LeaseStatsRow, int>::iterator next_row_;
/// @brief bind variable for retrieving subnet-id from a result set row
int subnet_id_;
/// @brief bind variable for retrieving lease-type from a result set row
int lease_type_;
/// @brief bind variable for retrieving lease-state from a result set row
int lease_state_;
};
constexpr StatementTag CqlLeaseStatsQuery::RECOUNT_LEASE4_STATS;
constexpr StatementTag CqlLeaseStatsQuery::RECOUNT_LEASE6_STATS;
StatementMap CqlLeaseStatsQuery::tagged_statements_{
// Return subnet_id and state of each v4 lease
{RECOUNT_LEASE4_STATS,
{RECOUNT_LEASE4_STATS,
"SELECT "
"subnet_id, state "
"FROM lease4 "
}},
// Return subnet_id, lease_type, and state of each v6 lease
{RECOUNT_LEASE6_STATS,
{RECOUNT_LEASE6_STATS,
"SELECT "
"subnet_id, lease_type, state "
"FROM lease6 "
}},
};
void
CqlLeaseStatsQuery::start() {
AnyArray data; // there are no where clause parameters
// This gets a collection of data for ALL leases, and
// then rolls them up into cummulative_rows_
executeSelect(conn_, data, statement_);
// Set our row iterator to the beginning
next_row_ = cummulative_rows_.begin();
}
bool
CqlLeaseStatsQuery::getNextRow(LeaseStatsRow& row) {
// If we're past the end, punt.
if (next_row_ == cummulative_rows_.end()) {
return (false);
}
// Start by copying from the map row key
row.subnet_id_ = next_row_->first.subnet_id_;
row.lease_type_ = next_row_->first.lease_type_;
row.lease_state_ = next_row_->first.lease_state_;
// Grab the count from the map value
row.state_count_ = next_row_->second;
// Point to the next row.
++next_row_;
return (true);
}
void
CqlLeaseStatsQuery::createBindForSelect(AnyArray& data, StatementTag) {
data.clear();
data.add(&subnet_id_);
if (fetch_type_) {
data.add(&lease_type_);
}
data.add(&lease_state_);
}
void
CqlLeaseStatsQuery::executeSelect(const CqlConnection& connection, const AnyArray& data,
StatementTag statement_tag) {
CassError rc;
CassStatement* statement = NULL;
CassFuture* future = NULL;
AnyArray local_data = data;
// Find the query statement first.
StatementMap::const_iterator it = connection.statements_.find(statement_tag);
if (it == connection.statements_.end()) {
isc_throw(DbOperationError,
"CqlLeastStatsQuery::executeSelect(): Statement "
<< statement_tag << "has not been prepared.");
}
// Bind the data before the query is executed.
CqlTaggedStatement tagged_statement = it->second;
if (tagged_statement.is_raw_) {
// The entire query is the first element in data.
std::string* query = boost::any_cast<std::string*>(local_data.back());
local_data.pop_back();
statement = cass_statement_new(query->c_str(), local_data.size());
} else {
statement = cass_prepared_bind(tagged_statement.prepared_statement_);
if (!statement) {
isc_throw(DbOperationError,
"CqlLeaseStatsQuery::executeSelect(): unable to bind statement "
<< tagged_statement.name_);
}
}
// Set specific level of consistency if we're told to do so.
if (connection.force_consistency_) {
rc = cass_statement_set_consistency(statement, connection.consistency_);
if (rc != CASS_OK) {
cass_statement_free(statement);
isc_throw(DbOperationError,
"CqlLeaseStatsQuery::executeSelect(): unable to set statement "
"consistency for statement "
<< tagged_statement.name_
<< ", Cassandra error code: " << cass_error_desc(rc));
}
}
CqlCommon::bindData(local_data, statement);
// Everything's ready. Call the actual statement.
future = cass_session_execute(connection.session_, statement);
if (!future) {
cass_statement_free(statement);
isc_throw(DbOperationError,
"CqlLeaseStatsQuery::executeSelect(): no CassFuture for statement "
<< tagged_statement.name_);
}
// Wait for the statement execution to complete.
cass_future_wait(future);
const std::string error = connection.checkFutureError(
"CqlLeaseStatsQuery::executeSelect(): cass_session_execute() != CASS_OK",
future, statement_tag);
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* result_collection = cass_future_get_result(future);
// lease type is always NA for v4
if (!fetch_type_) {
lease_type_ = Lease::TYPE_NA;
}
// Since we're currently forced to pull data for all leases, we
// iterate over them, aggregating them into cummulative LeaseStatsRows
AnyArray return_values;
CassIterator* rows = cass_iterator_from_result(result_collection);
while (cass_iterator_next(rows)) {
const CassRow* row = cass_iterator_get_row(rows);
createBindForSelect(return_values, statement_tag);
CqlCommon::getData(row, return_values);
LeaseStatsRow raw_row(subnet_id_, static_cast<Lease::Type>(lease_type_),
lease_state_, 1);
auto cum_row = cummulative_rows_.find(raw_row);
if (cum_row != cummulative_rows_.end()) {
cummulative_rows_[raw_row] = cum_row->second + 1;
} else {
cummulative_rows_.insert(std::make_pair(raw_row, 1));
}
}
// Free resources.
cass_iterator_free(rows);
cass_result_free(result_collection);
cass_future_free(future);
cass_statement_free(statement);
return;
}
CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap &parameters)
: LeaseMgr(), dbconn_(parameters) {
dbconn_.openDatabase();
dbconn_.prepareStatements(CqlLease4Exchange::tagged_statements_);
dbconn_.prepareStatements(CqlLease6Exchange::tagged_statements_);
dbconn_.prepareStatements(CqlVersionExchange::tagged_statements_);
dbconn_.prepareStatements(CqlLeaseStatsQuery::tagged_statements_);
}
CqlLeaseMgr::~CqlLeaseMgr() {
......@@ -1858,6 +2146,24 @@ CqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
return n_of_deleted_leases;
}
LeaseStatsQueryPtr
CqlLeaseMgr::startLeaseStatsQuery4() {
LeaseStatsQueryPtr query(
new CqlLeaseStatsQuery(dbconn_, CqlLeaseStatsQuery::RECOUNT_LEASE4_STATS,
false));
query->start();
return(query);
}
LeaseStatsQueryPtr
CqlLeaseMgr::startLeaseStatsQuery6() {
LeaseStatsQueryPtr query(
new CqlLeaseStatsQuery(dbconn_, CqlLeaseStatsQuery::RECOUNT_LEASE6_STATS,
true));
query->start();
return(query);
}
size_t
CqlLeaseMgr::wipeLeases4(const SubnetID & /*subnet_id*/) {
/// @todo: Need to implement this, so wipe leases would work.
......
......@@ -203,12 +203,12 @@ public:
/// @param subnet_id subnet identifier.
///
/// @return Lease collection (may be empty if no IPv4 lease found).
virtual Lease4Collection getLeases4(SubnetID subnet_id) const;
virtual Lease4Collection getLeases4(SubnetID subnet_id) const override;
/// @brief Returns all IPv4 leases.
///
/// @return Lease collection (may be empty if no IPv4 lease found).
virtual Lease4Collection getLeases4() const;
virtual Lease4Collection getLeases4() const override;
/// @brief Returns existing IPv6 lease for a given IPv6 address.
///
......@@ -352,6 +352,26 @@ public:
virtual uint64_t
deleteExpiredReclaimedLeases6(const uint32_t secs) override;
/// @brief Creates and runs the IPv4 lease stats query
///
/// It creates an instance of a CqlLeaseStatsQuery4 and then
/// invokes its start method, which fetches its statistical data
/// result set by executing the RECOUNT_LEASE_STATS4 query.
/// The query object is then returned.
///
/// @return The populated query as a pointer to an LeaseStatsQuery
virtual LeaseStatsQueryPtr startLeaseStatsQuery4() override;
/// @brief Creates and runs the IPv6 lease stats query
///
/// It creates an instance of a CqllLeaseStatsQuery and then
/// invokes its start method, which fetches its statistical data
/// result set by executing the RECOUNT_LEASE_STATS6 query.
/// The query object is then returned.
///
/// @return The populated query as a pointer to an LeaseStatsQuery
virtual LeaseStatsQueryPtr startLeaseStatsQuery6() override;
/// @brief Removes specified IPv4 leases.
///
/// This rather dangerous method is able to remove all leases from specified
......
......@@ -101,6 +101,26 @@ struct LeaseStatsRow {
lease_state_(lease_state), state_count_(state_count) {
}
/// @brief Less-than operator
bool operator< (const LeaseStatsRow &rhs) const {
if (subnet_id_ < rhs.subnet_id_) {
return (true);
}
if (subnet_id_ == rhs.subnet_id_ &&
lease_type_ < rhs.lease_type_) {
return (true);
}
if (subnet_id_ == rhs.subnet_id_ &&
lease_type_ == rhs.lease_type_ &&
lease_state_ < rhs.lease_state_) {
return (true);
}
return (false);
}
/// @brief The subnet ID to which this data applies
SubnetID subnet_id_;
/// @brief The lease_type to which the count applies
......@@ -114,7 +134,7 @@ struct LeaseStatsRow {
/// @brief Base class for fulfilling a statistical lease data query
///
/// LeaseMgr derivations implement this class such that it provides
/// upto date statistical lease data organized as rows of LeaseStatsRow
/// up to date statistical lease data organized as rows of LeaseStatsRow
/// instances. The rows must be accessible in ascending order by subnet id.
class LeaseStatsQuery {
public:
......@@ -140,9 +160,12 @@ public:
virtual bool getNextRow(LeaseStatsRow& row);
};
/// @brief Defines a pointer to an LeaseStatsQuery.
/// @brief Defines a pointer to a LeaseStatsQuery.
typedef boost::shared_ptr<LeaseStatsQuery> LeaseStatsQueryPtr;
/// @brief Defines a pointer to a LeaseStatsRow.
typedef boost::shared_ptr<LeaseStatsRow> LeaseStatsRowPtr;
/// @brief Abstract Lease Manager
///
/// This is an abstract API for lease database backends. It provides unified
......@@ -434,7 +457,7 @@ public:
///
/// LeaseMgr derivations implement this method such that it creates and
/// returns an instance of an LeaseStatsQuery whose result set has been
/// populated with upto date IPv4 lease statistical data. Each row of the
/// populated with up to date IPv4 lease statistical data. Each row of the
/// result set is an LeaseStatRow which ordered ascending by subnet ID.
///
/// @return A populated LeaseStatsQuery
......@@ -465,7 +488,7 @@ public:
///
/// LeaseMgr derivations implement this method such that it creates and
/// returns an instance of an LeaseStatsQuery whose result set has been
/// populated with upto date IPv6 lease statistical data. Each row of the
/// populated with up to date IPv6 lease statistical data. Each row of the
/// result set is an LeaseStatRow which ordered ascending by subnet ID.
///
/// @return A populated LeaseStatsQuery
......
......@@ -366,6 +366,7 @@ TEST(CqlOpenTest, OpenDatabase) {
<< "*** before the CQL tests will run correctly.\n";
}
// Check that attempting to get an instance of the lease manager when
// none is set throws an exception.
EXPECT_THROW(LeaseMgrFactory::instance(), NoLeaseManager);
......@@ -724,17 +725,13 @@ TEST_F(CqlLeaseMgrTest, deleteExpiredReclaimedLeases4) {
testDeleteExpiredReclaimedLeases4();
}
// Verifies that IPv4 lease statistics can be recalculated.
/// @todo: uncomment this once stats recalculation is implemented
/// for Cassandra (see #5487)
TEST_F(CqlLeaseMgrTest, DISABLED_recountLeaseStats4) {
/// @brief Verifies that IPv4 lease statistics can be recalculated.
TEST_F(CqlLeaseMgrTest, recountLeaseStats4) {
testRecountLeaseStats4();
}
// Verifies that IPv6 lease statistics can be recalculated.
/// @todo: uncomment this once stats recalculation is implemented
/// for Cassandra (see #5487)
TEST_F(CqlLeaseMgrTest, DISABLED_recountLeaseStats6) {
/// @brief Verifies that IPv6 lease statistics can be recalculated.
TEST_F(CqlLeaseMgrTest, recountLeaseStats6) {
testRecountLeaseStats6();
}
......@@ -753,4 +750,3 @@ TEST_F(CqlLeaseMgrTest, DISABLED_wipeLeases6) {
}
} // namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment