diff options
Diffstat (limited to 'src/hooks/dhcp/stat_cmds/stat_cmds.cc')
-rw-r--r-- | src/hooks/dhcp/stat_cmds/stat_cmds.cc | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/src/hooks/dhcp/stat_cmds/stat_cmds.cc b/src/hooks/dhcp/stat_cmds/stat_cmds.cc new file mode 100644 index 0000000..8950bc9 --- /dev/null +++ b/src/hooks/dhcp/stat_cmds/stat_cmds.cc @@ -0,0 +1,746 @@ +// Copyright (C) 2018-2021 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 <config/command_mgr.h> +#include <config/cmds_impl.h> +#include <cc/command_interpreter.h> +#include <cc/data.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/lease_mgr.h> +#include <dhcpsrv/lease_mgr_factory.h> +#include <dhcpsrv/subnet_id.h> +#include <hooks/hooks.h> +#include <exceptions/exceptions.h> +#include <stat_cmds.h> +#include <stat_cmds_log.h> +#include <stats/stats_mgr.h> +#include <util/boost_time_utils.h> +#include <util/multi_threading_mgr.h> + +#include <boost/date_time/posix_time/posix_time.hpp> +#include <string> + +using namespace isc::dhcp; +using namespace isc::data; +using namespace isc::config; +using namespace isc::asiolink; +using namespace isc::hooks; +using namespace isc::stats; +using namespace isc::util; +using namespace isc::log; +using namespace std; + +namespace isc { +namespace stat_cmds { + +/// @brief Exception thrown no subnets fall within the selection criteria +/// This exception is thrown when a valid combination of query parameters +/// excludes all known (i.e. configured) subnets. +class NotFound: public isc::Exception { +public: + NotFound (const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Implements command handling for stat-leaseX-get commands +class LeaseStatCmdsImpl : private CmdsImpl { +public: + + /// @brief Wrapper class for stat-leaseX-get command parameters. + class Parameters { + public: + /// @brief Specifies the subnet-id for a single subnet, or + /// the first subnet for a subnet range + SubnetID first_subnet_id_; + + /// @brief Specifies the last subnet for subnet range + SubnetID last_subnet_id_; + + /// @brief Denotes the query selection mode all, subnet, + /// or subnet range + LeaseStatsQuery::SelectMode select_mode_; + + /// @brief Generate a string version of the contents + std::string toText() { + std::stringstream os; + switch (select_mode_) { + case LeaseStatsQuery::ALL_SUBNETS: + os << "[all subnets]"; + break; + case LeaseStatsQuery::SINGLE_SUBNET: + os << "[subnet-id=" << first_subnet_id_ << "]"; + break; + case LeaseStatsQuery::SUBNET_RANGE: + os << "[subnets " << first_subnet_id_ + << " through " << last_subnet_id_ << "]"; + break; + } + + return (os.str()); + } + }; + +public: + + /// @brief Provides the implementation for stat-lease4-get, + /// @ref isc::stat_cmds::StatCmds::statLease4GetHandler + /// + /// It parses the command arguments, and then invokes makeResult4() + /// to fulfil the lease4 statistics fetch. It then constructs the outbound + /// response based on those results. If a NotFound exception is caught, + /// a CONTROL_RESULT_EMTPY response is generated. + /// + /// @param handle Callout context - which is expected to contain the + /// command JSON text in the "command" argument + /// @return 0 upon success, non-zero otherwise + int + statLease4GetHandler(CalloutHandle& handle); + + /// @brief Provides the implementation for stat-lease6-get, + /// @ref isc::stat_cmds::StatCmds::statLease6GetHandler + /// + /// It parses the command arguments, and then invokes makeResult6() + /// to fulfil the lease6 statistics fetch. It then constructs the outbound + /// response based on those results. If a NotFound exception is caught, + /// a CONTROL_RESULT_EMTPY response is generated. + /// + /// @param handle Callout context - which is expected to contain the + /// command JSON text in the "command" argument + /// @return 0 upon success, non-zero otherwise + int + statLease6GetHandler(CalloutHandle& handle); + + /// @brief Parses command arguments into stat-leaseX-get parameters + /// @param cmd_args Element form of command arguments to parse + /// @throw BadValue if any of the following rules are broken: + /// + /// -# If subnet-id is specified it must be an integer > 0 + /// -# If subnet-range is specified it must contain both + /// first-subnet-id and last-subnet-id and their values + /// must fulfill: 0 < first-subnet-id < last-subnet-id + /// -# subnet-id and subnet-range are mutually exclusive + Parameters getParameters(const ConstElementPtr& cmd_args); + + /// @brief Executes the lease4 query and constructs the outbound result set + /// + /// This method uses the command parameters to identify the range + /// of configured subnets. If the range contains no known subnets + /// then a NotFound exception is thrown. Having determined the range + /// to be valid, it then executes the appropriate Lease4 stats query via + /// the LeaseMgr. + /// + /// Lastly, it iterates over the qualifying subnets adding a row + /// of statistics for each one to the result-set. Each row combines + /// the totals from StatsMgr with the type and state counts from the + /// query results. For subnets with no query data (i.e. no leases), + /// their rows have non-zero values for totals only. + /// + /// @param result Element to which the constructed result-set will be added. + /// @param params Parsed stat-lease4-cmd parameters + /// @throw NotFound if the selection criteria eliminates all known subnets + uint64_t makeResultSet4(const ElementPtr& result, const Parameters& params); + + /// @brief Executes the lease4 query and constructs the outbound result set + /// This method uses the command parameters to identify the range + /// of configured subnets. If the range contains no known subnets + /// then a NotFound exception is thrown. Having determined the range + /// to be valid, it then executes the appropriate Lease6 stats query via + /// the LeaseMgr. + /// + /// Lastly, it iterates over the qualifying subnets adding a row + /// of statistics for each one to the result-set. Each row combines + /// the totals from StatsMgr with the type and state counts from the + /// query results. For subnets with no query data (i.e. no leases), + /// their rows have non-zero values for totals only. + /// + /// @param result Element to which the constructed result-set will be added. + /// @param params Parsed stat-lease6-cmd parameters + /// @throw NotFound if the selection criteria eliminates all known subnets + uint64_t makeResultSet6(const ElementPtr& result, const Parameters& params); + + /// @brief Instantiates a new "empty" result-set Element + /// + /// Constructs a ElementPtr tree of an empty result set + /// for holding rows of the given column labels. In JSON + /// it appears as follows: + /// + /// "result-set": { + /// "timestamp": "2018-03-22 09:43:30.815371", + /// "columns": ["<label-1>, <label-2>, ... ], + /// "rows": [] + /// } + /// + /// And then adds it to the given wrapper element. + /// + /// @param wrapper Element to which the newly constructed result-set + /// will be added. + /// @param column_labels list of the column labels in the order the values + /// for each column will appear in the result-set rows + /// @return A reference to the writable list of rows of the result-set + ElementPtr createResultSet(const ElementPtr& wrapper, + const std::vector<std::string>& column_labels); + + /// @brief Adds a row of Lease4 stat values to a list of value rows + /// + /// @param[out] value_rows list of rows to which to add + /// @param[out] subnet_id id of the subnet of the new row. This value is + /// also used for fetching the total addresses in the subnet + /// @param assigned number of assigned addresses in the subnet + /// @param declined number of declined addresses in the subnet + void addValueRow4(ElementPtr value_rows, const SubnetID &subnet_id, + int64_t assigned, int64_t declined); + + /// @brief Adds a row of Lease6 stat values to a list of value rows + /// + /// @param[out] value_rows list of rows to which to add + /// @param[out] subnet_id id of the subnet of the new row. This value is + /// also used for fetching the total NAs and PDs in the subnet + /// @param assigned number of assigned NAs in the subnet + /// @param declined number of declined NAs in the subnet + /// @param assigned_pds number of assigned PDs the subnet + void addValueRow6(ElementPtr value_rows, const SubnetID &subnet_id, + int64_t assigned, int64_t declined, int64_t assigned_pds); + + /// @brief Fetches a single statistic for a subnet from StatsMgr + /// + /// Uses the given id and name to query the StatsMgr for the desired value. + /// + /// @param subnet_id id of the desired subnet + /// @param name name of the desired statistic + int64_t getSubnetStat(const SubnetID& subnet_id, const std::string& name); +}; + +int +LeaseStatCmdsImpl::statLease4GetHandler(CalloutHandle& handle) { + ElementPtr result = Element::createMap(); + Parameters params; + ConstElementPtr response; + + // Extract the command and then the parameters + try { + extractCommand(handle); + params = getParameters(cmd_args_); + } catch (const std::exception& ex) { + LOG_ERROR(stat_cmds_logger, STAT_CMDS_LEASE4_GET_INVALID) + .arg(ex.what()); + setErrorResponse(handle, ex.what()); + return (1); + } + + try { + // Now build the result set + uint64_t rows = makeResultSet4(result, params); + LOG_INFO(stat_cmds_logger, STAT_CMDS_LEASE4_GET) + .arg(params.toText()) + .arg(rows); + std::stringstream os; + os << "stat-lease4-get" << params.toText() << ": " << rows << " rows found"; + response = createAnswer(CONTROL_RESULT_SUCCESS, os.str(), result); + } catch (const NotFound& ex) { + // Criteria was valid but included no known subnets, + // so we return a not found response. + LOG_INFO(stat_cmds_logger, STAT_CMDS_LEASE4_GET_NO_SUBNETS) + .arg(params.toText()) + .arg(ex.what()); + std::stringstream os; + os << "stat-lease4-get" << params.toText() << ": no matching data, " << ex.what(); + response = createAnswer(CONTROL_RESULT_EMPTY, os.str(), result); + } catch (const std::exception& ex) { + LOG_ERROR(stat_cmds_logger, STAT_CMDS_LEASE4_GET_FAILED) + .arg(params.toText()) + .arg(ex.what()); + setErrorResponse(handle, ex.what()); + return (1); + } + + setResponse(handle, response); + return (0); +} + +int +LeaseStatCmdsImpl::statLease6GetHandler(CalloutHandle& handle) { + ElementPtr result = Element::createMap(); + Parameters params; + ConstElementPtr response; + + // Extract the command and then the parameters + try { + extractCommand(handle); + params = getParameters(cmd_args_); + } catch (const std::exception& ex) { + LOG_ERROR(stat_cmds_logger, STAT_CMDS_LEASE6_GET_INVALID) + .arg(ex.what()); + setErrorResponse(handle, ex.what()); + return (1); + } + + try { + // Now build the result set + uint64_t rows = makeResultSet6(result, params); + LOG_INFO(stat_cmds_logger, STAT_CMDS_LEASE6_GET) + .arg(params.toText()) + .arg(rows); + std::stringstream os; + os << "stat-lease6-get" << params.toText() << ": " << rows << " rows found"; + response = createAnswer(CONTROL_RESULT_SUCCESS, os.str(), result); + } catch (const NotFound& ex) { + // Criteria was valid but included no known subnets, + // so we return a not found response. + LOG_INFO(stat_cmds_logger, STAT_CMDS_LEASE6_GET_NO_SUBNETS) + .arg(params.toText()) + .arg(ex.what()); + std::stringstream os; + os << "stat-lease6-get" << params.toText() << ": no matching data, " << ex.what(); + response = createAnswer(CONTROL_RESULT_EMPTY, os.str(), result); + } catch (const std::exception& ex) { + LOG_ERROR(stat_cmds_logger, STAT_CMDS_LEASE6_GET_FAILED) + .arg(params.toText()) + .arg(ex.what()); + setErrorResponse(handle, ex.what()); + return (1); + } + + setResponse(handle, response); + return (0); +} + +LeaseStatCmdsImpl::Parameters +LeaseStatCmdsImpl::getParameters(const ConstElementPtr& cmd_args) { + Parameters params; + + params.select_mode_ = LeaseStatsQuery::ALL_SUBNETS; + params.first_subnet_id_ = 0; + params.last_subnet_id_ = 0; + if (!cmd_args ) { + // No arguments defaults to ALL_SUBNETS. + return (params); + } + + if (cmd_args->getType() != Element::map) { + isc_throw(BadValue, "'arguments' parameter is not a map"); + } + + params.select_mode_ = LeaseStatsQuery::ALL_SUBNETS; + if (cmd_args->contains("subnet-id")) { + + ConstElementPtr value = cmd_args->get("subnet-id"); + if (value->getType() != Element::integer) { + isc_throw(BadValue, "'subnet-id' parameter is not integer"); + } + + if (value->intValue() <= 0) { + isc_throw(BadValue, "'subnet-id' parameter must be > 0"); + } + + params.first_subnet_id_ = value->intValue(); + params.select_mode_ = LeaseStatsQuery::SINGLE_SUBNET; + } + + if (cmd_args->contains("subnet-range")) { + if (params.select_mode_ == LeaseStatsQuery::SINGLE_SUBNET) { + isc_throw(BadValue, "cannot specify both subnet-id and subnet-range"); + } + + ConstElementPtr range = cmd_args->get("subnet-range"); + if (range->getType() != Element::map) { + isc_throw(BadValue, "subnet-range parameter is not a map"); + } + + ConstElementPtr value = range->get("first-subnet-id"); + if (!value || value->getType() != Element::integer) { + isc_throw(BadValue, "'first-subnet-id' parameter missing or not an integer"); + } + + if (value->intValue() <= 0) { + isc_throw(BadValue, "'first-subnet-id' parameter must be > 0"); + } + + params.first_subnet_id_ = value->intValue(); + + value = range->get("last-subnet-id"); + if (!value || value->getType() != Element::integer) { + isc_throw(BadValue, "'last-subnet-id' parameter missing or not an integer"); + } + + if (value->intValue() <= 0) { + isc_throw(BadValue, "'last-subnet-id' parameter must be > 0"); + } + + params.last_subnet_id_ = value->intValue(); + + if (params.last_subnet_id_ < params.first_subnet_id_) { + isc_throw(BadValue, "'last-subnet-id' must be greater than 'first-subnet-id'"); + } + + params.select_mode_ = LeaseStatsQuery::SUBNET_RANGE; + } + + return (params); +} + +uint64_t +LeaseStatCmdsImpl::makeResultSet4(const ElementPtr& result_wrapper, + const Parameters& params) { + // First we need to determine the range of configured subnets + // which meet the selection criteria. If the range contains + // no subnets we punt. + // Iterate over the selected range of configured subnets generating + // a result-set row for each one. If a subnet has data in the query + // content use it, otherwise, it gets a row with totals only. This + // way we send back a row for every selected subnet. + const Subnet4Collection* subnets = + CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->getAll(); + + // Set the bounds on the selected subnet range + const auto& idx = subnets->get<SubnetSubnetIdIndexTag>(); + + // Init to ALL so we can use auto + auto lower = idx.begin(); + auto upper = idx.end(); + switch (params.select_mode_) { + case LeaseStatsQuery::SINGLE_SUBNET: + lower = idx.find(params.first_subnet_id_); + // If it's an unknown subnet, punt. + if (lower == idx.end()) { + isc_throw(NotFound, "subnet-id: " + << params.first_subnet_id_ << " does not exist"); + } + + upper = idx.upper_bound(params.first_subnet_id_); + break; + case LeaseStatsQuery::SUBNET_RANGE: + lower = idx.lower_bound(params.first_subnet_id_); + upper = idx.upper_bound(params.last_subnet_id_); + break; + default: + break; + } + + // If it's an empty range, punt. + if (lower == upper) { + isc_throw(NotFound, "selected ID range: " + << params.first_subnet_id_ << " through " + << params.last_subnet_id_ << " includes no known subnets"); + } + + // Now we can run the stats query. + LeaseStatsQueryPtr query; + switch (params.select_mode_) { + case LeaseStatsQuery::ALL_SUBNETS: + query = LeaseMgrFactory::instance().startLeaseStatsQuery4(); + break; + case LeaseStatsQuery::SINGLE_SUBNET: + query = LeaseMgrFactory::instance() + .startSubnetLeaseStatsQuery4(params.first_subnet_id_); + break; + case LeaseStatsQuery::SUBNET_RANGE: + query = LeaseMgrFactory::instance() + .startSubnetRangeLeaseStatsQuery4(params.first_subnet_id_, + params.last_subnet_id_); + break; + } + + // Create the result-set map. + // labels could be class statics? + std::vector<std::string>column_labels = { "subnet-id", "total-addresses", + "cumulative-assigned-addresses", + "assigned-addresses", + "declined-addresses" }; + ElementPtr value_rows = createResultSet(result_wrapper, column_labels); + + // Get the first query row + LeaseStatsRow query_row; + bool query_eof = !(query->getNextRow(query_row)); + + // Now we iterate over the selected range, building rows accordingly. + bool orphaned_stats = false; + for (auto cur_subnet = lower; cur_subnet != upper; ++cur_subnet) { + SubnetID cur_id = (*cur_subnet)->getID(); + + // Skip any unexpected result set rows. These occur when + // subnets no longer exist but either their leases (memfile) + // or their leaseX-stat rows (db lease backends) still do. + while ((cur_id > query_row.subnet_id_) && (!query_eof)) { + orphaned_stats = true; + query_eof = !(query->getNextRow(query_row)); + } + + // Add total only rows for subnets that occur before + // or after the subnets in the query content. These are + // subnets which exist but for which there is not yet any + // lease data. + if ((cur_id < query_row.subnet_id_) || (query_eof)) { + // Generate a totals only row + addValueRow4(value_rows, cur_id, 0, 0); + continue; + } + + // Current subnet matches query row, so iterate over its + // query rows (one per state) and accumulate them + // into a result-set row. + int64_t assigned = 0; + int64_t declined = 0; + bool add_row = false; + while (!query_eof && (query_row.subnet_id_ == cur_id)) { + if (query_row.lease_state_ == Lease::STATE_DEFAULT) { + add_row = true; + assigned = query_row.state_count_; + } else if (query_row.lease_state_ == Lease::STATE_DECLINED) { + add_row = true; + declined = query_row.state_count_; + } + + // Get next query row + query_eof = !(query->getNextRow(query_row)); + } + // Add the row for the current subnet + if (add_row) { + addValueRow4(value_rows, cur_id, assigned, declined); + } + } + + // If there are any orphaned statistics log it. + if (!(query_eof) || orphaned_stats) { + LOG_DEBUG(stat_cmds_logger, DBGLVL_TRACE_BASIC, STAT_CMDS_LEASE4_ORPHANED_STATS); + } + + return (value_rows->size()); +} + +uint64_t +LeaseStatCmdsImpl::makeResultSet6(const ElementPtr& result_wrapper, + const Parameters& params) { + // First we need to determine the range of configured subnets + // which meet the selection criteria. If the range contains + // no subnets we punt. + // Iterate over the selected range of configured subnets generating + // a result-set row for each one. If a subnet has data in the query + // content use it, otherwise, it gets a row with totals only. This + // way we send back a row for every selected subnet. + const Subnet6Collection* subnets = + CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll(); + + // Set the bounds on the selected subnet range + const auto& idx = subnets->get<SubnetSubnetIdIndexTag>(); + + // Init to ALL so we can use auto + auto lower = idx.begin(); + auto upper = idx.end(); + switch (params.select_mode_) { + case LeaseStatsQuery::SINGLE_SUBNET: + lower = idx.find(params.first_subnet_id_); + // If it's an unknown subnet, punt. + if (lower == idx.end()) { + isc_throw(NotFound, "subnet-id: " + << params.first_subnet_id_ << " does not exist"); + } + + upper = idx.upper_bound(params.first_subnet_id_); + break; + case LeaseStatsQuery::SUBNET_RANGE: + lower = idx.lower_bound(params.first_subnet_id_); + upper = idx.upper_bound(params.last_subnet_id_); + break; + default: + break; + } + + // If it's an empty range, punt. + if (lower == upper) { + isc_throw(NotFound, "selected ID range: " + << params.first_subnet_id_ << " through " + << params.last_subnet_id_ << " includes no known subnets"); + } + + // Now we can run the stats query. + LeaseStatsQueryPtr query; + switch (params.select_mode_) { + case LeaseStatsQuery::ALL_SUBNETS: + query = LeaseMgrFactory::instance().startLeaseStatsQuery6(); + break; + case LeaseStatsQuery::SINGLE_SUBNET: + query = LeaseMgrFactory::instance() + .startSubnetLeaseStatsQuery6(params.first_subnet_id_); + break; + case LeaseStatsQuery::SUBNET_RANGE: + query = LeaseMgrFactory::instance() + .startSubnetRangeLeaseStatsQuery6(params.first_subnet_id_, + params.last_subnet_id_); + break; + } + + // Create the result-set map. + // labels could be class statics? + std::vector<std::string>column_labels = { "subnet-id", "total-nas", + "cumulative-assigned-nas", + "assigned-nas", + "declined-nas", "total-pds", + "cumulative-assigned-pds", + "assigned-pds" }; + ElementPtr value_rows = createResultSet(result_wrapper, column_labels); + + // Get the first query row + LeaseStatsRow query_row; + bool query_eof = !(query->getNextRow(query_row)); + + // Now we iterate over the selected range, building rows accordingly. + bool orphaned_stats = false; + for (auto cur_subnet = lower; cur_subnet != upper; ++cur_subnet) { + SubnetID cur_id = (*cur_subnet)->getID(); + + // Skip any unexpected result set rows. These occur when + // subnets no longer exist but either their leases (memfile) + // or their leaseX-stat rows (db lease backends) still do. + while ((cur_id > query_row.subnet_id_) && (!query_eof)) { + orphaned_stats = true; + query_eof = !(query->getNextRow(query_row)); + } + + // Add total only rows for subnets that occur before + // or after the subnets in the query content. These are + // subnets which exist but for which there is not yet any + // lease data. + if ((cur_id < query_row.subnet_id_) || (query_eof)) { + // Generate a totals only row + addValueRow6(value_rows, cur_id, 0, 0, 0); + continue; + } + + // Current subnet matches query row, so iterate over its + // query rows (one per state) and accumulate them + // into a result-set row. + int64_t assigned = 0; + int64_t declined = 0; + int64_t assigned_pds = 0; + bool add_row = false; + while (!query_eof && (query_row.subnet_id_ == cur_id)) { + if (query_row.lease_state_ == Lease::STATE_DEFAULT) { + add_row = true; + if (query_row.lease_type_ == Lease::TYPE_NA) { + assigned = query_row.state_count_; + } else { + assigned_pds = query_row.state_count_; + } + } else if (query_row.lease_state_ == Lease::STATE_DECLINED) { + add_row = true; + declined = query_row.state_count_; + } + + // Get next query row + query_eof = !(query->getNextRow(query_row)); + } + // Add the row for the current subnet + if (add_row) { + addValueRow6(value_rows, cur_id, assigned, declined, assigned_pds); + } + } + + // If there are any orphaned statistics log it. + if (!(query_eof) || orphaned_stats) { + LOG_DEBUG(stat_cmds_logger, DBGLVL_TRACE_BASIC, STAT_CMDS_LEASE6_ORPHANED_STATS); + } + + return (value_rows->size()); +} + +ElementPtr +LeaseStatCmdsImpl::createResultSet(const ElementPtr &result_wrapper, + const std::vector<std::string>& column_labels) { + // Create the result-set map and add it to the wrapper. + ElementPtr result_set = Element::createMap(); + result_wrapper->set("result-set", result_set); + + // Create the timestamp based on time now and add it to the result set. + boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); + + ElementPtr timestamp = Element::create(isc::util::ptimeToText(now)); + result_set->set("timestamp", timestamp); + + // Create the list of column names and add it to the result set. + ElementPtr columns = Element::createList(); + for (auto label = column_labels.begin(); label != column_labels.end(); ++label) { + columns->add(Element::create(*label)); + } + result_set->set("columns", columns); + + // Create the empty value_rows list, add it and then return it. + ElementPtr value_rows = Element::createList(); + result_set->set("rows", value_rows); + return (value_rows); +} + + +void +LeaseStatCmdsImpl::addValueRow4(ElementPtr value_rows, const SubnetID &subnet_id, + int64_t assigned, int64_t declined) { + ElementPtr row = Element::createList(); + row->add(Element::create(static_cast<int64_t>(subnet_id))); + row->add(Element::create(getSubnetStat(subnet_id, "total-addresses"))); + row->add(Element::create(getSubnetStat(subnet_id, "cumulative-assigned-addresses"))); + row->add(Element::create(assigned)); + row->add(Element::create(declined)); + value_rows->add(row); +} + +void +LeaseStatCmdsImpl::addValueRow6(ElementPtr value_rows, const SubnetID &subnet_id, + int64_t assigned, int64_t declined, int64_t assigned_pds) { + ElementPtr row = Element::createList(); + row->add(Element::create(static_cast<int64_t>(subnet_id))); + row->add(Element::create(getSubnetStat(subnet_id, "total-nas"))); + row->add(Element::create(getSubnetStat(subnet_id, "cumulative-assigned-nas"))); + row->add(Element::create(assigned)); + row->add(Element::create(declined)); + row->add(Element::create(getSubnetStat(subnet_id, "total-pds"))); + row->add(Element::create(getSubnetStat(subnet_id, "cumulative-assigned-pds"))); + row->add(Element::create(assigned_pds)); + value_rows->add(row); +} + +int64_t +LeaseStatCmdsImpl::getSubnetStat(const SubnetID& subnet_id, const std::string& name) { + ObservationPtr stat = StatsMgr::instance(). + getObservation(StatsMgr::generateName("subnet", subnet_id, name)); + if (stat) { + return (stat->getInteger().first); + } + + return (0); +} + +// Using a critical section to avoid any changes in parallel. + +int +StatCmds::statLease4GetHandler(CalloutHandle& handle) { + try { + LeaseStatCmdsImpl impl; + MultiThreadingCriticalSection cs; + return (impl.statLease4GetHandler(handle)); + } catch (const std::exception& ex) { + + LOG_ERROR(stat_cmds_logger, STAT_CMDS_LEASE4_FAILED) + .arg(ex.what()); + } + return (1); +} + +int +StatCmds::statLease6GetHandler(CalloutHandle& handle) { + try { + LeaseStatCmdsImpl impl; + MultiThreadingCriticalSection cs; + return (impl.statLease6GetHandler(handle)); + } catch (const std::exception& ex) { + + LOG_ERROR(stat_cmds_logger, STAT_CMDS_LEASE6_FAILED) + .arg(ex.what()); + } + return (1); +} + +}; +}; |