From: Thomas Markwalder Date: Wed, 12 Jun 2024 14:09:26 +0000 (-0400) Subject: [#3328] Output usecs; add permon-get-all-durations X-Git-Tag: Kea-2.7.0~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4889c3f9a30c2b4957e05e2be9d8159a6c8710f8;p=thirdparty%2Fkea.git [#3328] Output usecs; add permon-get-all-durations Statistics and raw data return durations in microseconds instead of milliseconds. Add initial support for perfmon-get-all-durations. /doc/sphinx/arm/hooks-perfmon.rst Updated to microseconds Updated perfmon-control command /src/hooks/dhcp/perfmon/monitored_duration.* DurationKey::toElement() MonitoredDuration::toElement() - new functions /src/hooks/dhcp/perfmon/perfmon_callouts.cc int perfmon_control() int perfmon_get_all_durations() - new functions int load() - register commands /src/hooks/dhcp/perfmon/perfmon_config.cc PerfMonConfig::parse() - replace use of copy ctor PerfMonConfig::enable_monitoring_ PerfMonConfig::stats_mgr_reporting_ - made std::atomic /src/hooks/dhcp/perfmon/perfmon_messages.mes PERFMON_CMDS_CONTROL_ERROR PERFMON_CMDS_CONTROL_OK PERFMON_CMDS_GET_ALL_DURATIONS_ERROR PERFMON_CMDS_GET_ALL_DURATIONS_OK - new messages /src/hooks/dhcp/perfmon/perfmon_mgr.cc PerfMonMgr::perfmonControlHandler() PerfMonMgr::perfmonGetAllDurationsHandler() PerfMonMgr::formatDurationDataAsElements() PerfMonMgr::formatDurationDataAsResultSet() - new functions /src/hooks/dhcp/perfmon/tests/Makefile.am Added perfmon_cmds_unittests.cc /src/hooks/dhcp/perfmon/tests/perfmon_config_unittests.cc Replaced use of copy ctor /src/hooks/dhcp/perfmon/tests/perfmon_mgr_unittests.cc Updated tests for microseconds --- diff --git a/doc/sphinx/arm/hooks-perfmon.rst b/doc/sphinx/arm/hooks-perfmon.rst index 2da6343805..494c89f416 100644 --- a/doc/sphinx/arm/hooks-perfmon.rst +++ b/doc/sphinx/arm/hooks-perfmon.rst @@ -145,7 +145,7 @@ duration updates for each of the above: +--------------------------------------------------------------+--------------+ | Global Duration Keys | Update in | - | | milliseconds | + | | microseconds | +==============================================================+==============+ | DHCPDISCOVER.DHCPOFFER.socket_received-buffer_read.0 | 247 | +--------------------------------------------------------------+--------------+ @@ -170,29 +170,29 @@ statistic employs the following naming convention: {subnet-id[x]}.perfmon.-.-. There is both a global and a subnet-specific value for each. Currently, the only -value reported for a given duration key is ``average-ms``; this statistic is the average time +value reported for a given duration key is ``averages-usecs``; this statistic is the average time between the duration's event pair over the most recently completed interval. In other words, if during a given interval there were seven occurrences (i.e. updates) totaling -350ms, the ``average-ms`` reported would be 50ms. Continuing with the example above, the +3500us, the ``average-usecs`` reported would be 500us. Continuing with the example above, the statistics reported are named as follows for the subnet-level values: :: - subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.socket_received-buffer_read.average-ms - subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.buffer_read-mt_queue.average-ms - subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.mt_queued-process_started.average-ms - subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.process_started-process_completed.average-ms - subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.composite-total_response.average-ms + subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.socket_received-buffer_read.average-usecs + subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.buffer_read-mt_queue.average-usecs + subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.mt_queued-process_started.average-usecs + subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.process_started-process_completed.average-usecs + subnet[100].perfmon.DHCPDISCOVER.DHCPOFFER.composite-total_response.average-usecs and as shown for global values: :: - perfmon.DHCPDISCOVER.DHCPOFFER.socket_received-buffer_read.average-ms - perfmon.DHCPDISCOVER.DHCPOFFER.buffer_read-mt_queue.average-ms - perfmon.DHCPDISCOVER.DHCPOFFER.mt_queued-process_started.average-ms - perfmon.DHCPDISCOVER.DHCPOFFER.process_started-process_completed.average-ms - perfmon.DHCPDISCOVER.DHCPOFFER.composite-total_response.average-ms + perfmon.DHCPDISCOVER.DHCPOFFER.socket_received-buffer_read.average-usecs + perfmon.DHCPDISCOVER.DHCPOFFER.buffer_read-mt_queue.average-usecs + perfmon.DHCPDISCOVER.DHCPOFFER.mt_queued-process_started.average-usecs + perfmon.DHCPDISCOVER.DHCPOFFER.process_started-process_completed.average-usecs + perfmon.DHCPDISCOVER.DHCPOFFER.composite-total_response.average-usecs The results are reported to StatsMgr, an internal Kea component that reports data as statistics that can be retrieved using statistics commands. They can be fetched using the commands @@ -225,8 +225,34 @@ The alarm-cleared INFO log looks like this: API Commands ~~~~~~~~~~~~ - Commands to enable or disable monitoring, clear or alter alarms, and fetch duration data - are anticipated but not yet supported. +.. _command-perfmon-control: + +This command can be used to enable or disable active monitoring and statistics +reporting at runtime without altering or reloading configuration. + +:: + + { + "command": "perfmon-control" + "arguments": { + "enable-monitoring": true, + "stats-mgr-reporting": false" + } + } + +Regardless of the arguments (if any) are supplied, the current values of both +flags are always returned: + +:: + + { + "result": 0, + "text": "perfmon-control success", + "arguments": { + "enable-monitoring": true, + "stats-mgr-reporting": false" + } + } .. _perfmon-configuration: diff --git a/src/hooks/dhcp/perfmon/monitored_duration.cc b/src/hooks/dhcp/perfmon/monitored_duration.cc index af51f2eb18..090da75876 100644 --- a/src/hooks/dhcp/perfmon/monitored_duration.cc +++ b/src/hooks/dhcp/perfmon/monitored_duration.cc @@ -11,8 +11,11 @@ #include #include #include +#include using namespace isc::dhcp; +using namespace isc::data; +using namespace isc::util; using namespace boost::posix_time; namespace isc { @@ -194,6 +197,17 @@ DurationKey::getStatName(const std::string& value_name) const { return (oss.str()); } +ElementPtr +DurationKey::toElement() const { + ElementPtr element = Element::createMap(); + element->set("subnet-id", Element::create(static_cast(subnet_id_))); + element->set("query-type", Element::create(getMessageTypeLabel(family_, query_type_))); + element->set("response-type", Element::create(getMessageTypeLabel(family_, response_type_))); + element->set("start-event", Element::create(start_event_label_)); + element->set("stop-event", Element::create(stop_event_label_)); + return (element); +} + bool DurationKey::operator==(const DurationKey& other) const { return ( @@ -309,5 +323,26 @@ MonitoredDuration::clear() { previous_interval_.reset(); } +ElementPtr +MonitoredDuration::toElement() const { + ElementPtr element = Element::createMap(); + element->set("duration-key", DurationKey::toElement()); + if (previous_interval_) { + element->set("start-time", Element::create(ptimeToText(previous_interval_->getStartTime()))); + element->set("occurrences", Element::create(static_cast(previous_interval_->getOccurrences()))); + element->set("min-duration-usecs", Element::create(previous_interval_->getMinDuration().total_microseconds())); + element->set("max-duration-usecs", Element::create(previous_interval_->getMaxDuration().total_microseconds())); + element->set("total-duration-usecs", Element::create(previous_interval_->getTotalDuration().total_microseconds())); + } else { + element->set("start-time", Element::create("")); + element->set("occurrences", Element::create(0)); + element->set("min-duration-usecs", Element::create(0)); + element->set("max-duration-usecs", Element::create(0)); + element->set("total-duration-usecs", Element::create(0)); + } + + return (element); +} + } // end of namespace perfmon } // end of namespace isc diff --git a/src/hooks/dhcp/perfmon/monitored_duration.h b/src/hooks/dhcp/perfmon/monitored_duration.h index 893544493e..d7e1a5b19a 100644 --- a/src/hooks/dhcp/perfmon/monitored_duration.h +++ b/src/hooks/dhcp/perfmon/monitored_duration.h @@ -7,6 +7,7 @@ #ifndef _MONITORED_DURATION_H #define _MONITORED_DURATION_H +#include #include #include @@ -226,7 +227,7 @@ public: /// @brief Get the StatsMgr formatted compatible name. /// - /// @param value_name name of the specific value (e.g. "average-ms", "min-duration-ms"). + /// @param value_name name of the specific value (e.g. "average-usecs", "min-duration-usecs"). /// The format of the string: /// /// @code @@ -235,15 +236,32 @@ public: /// /// Examples: /// - /// perfmon.discover-offer.socket_received-buffer_read.average-ms + /// perfmon.discover-offer.socket_received-buffer_read.average-usecs /// - /// subnet[9].perfmon.discover-offer.socket_received-buffer_read.average-ms + /// subnet[9].perfmon.discover-offer.socket_received-buffer_read.average-usecs /// /// @endcode /// /// @return the statistic name. std::string getStatName(const std::string& value_name) const; + /// @brief Renders the the duration key as an Element. + /// + /// The element will appear as follows: + /// + /// @code + /// { + /// "query-type": "discover", + /// "response-type": "offer", + /// "start-event": "socket_received", + /// "stop-event": "buffer_read", + /// "subnet-id": 10 + /// } + /// @endcode + /// + /// @return Element::map containing the duration key values. + virtual data::ElementPtr toElement() const; + /// @brief Validates that a query and response message type pair is sane. /// /// @param family Protocol family of the key (AF_INET or AF_INET6) @@ -379,6 +397,49 @@ public: /// @brief Deletes the current and previous intervals. void clear(); + /// @brief Renders the the duration as an Element. + /// + /// The element includes the duration key and the previous interval + /// content(if one) as follows: + /// @code + /// { + /// "duration-key": { + /// "query-type": "discover", + /// "response-type": "offer", + /// "start-event": "socket_received", + /// "stop-event": "buffer_read", + /// "subnet-id": 10 + /// }, + /// "start-time": "2024-01-18 10:11:19.498739", + /// "occurrences": 105, + /// "min-duration-usecs": 5300, + /// "max-duration-usecs": 9000, + /// "total-duration-usecs": 786500 + /// } + /// @endcode + /// + /// If there is no previous interval, it will appears as follows: + /// + /// @code + /// { + /// "duration-key": { + /// "query-type": "discover", + /// "response-type": "offer", + /// "start-event": "socket_received", + /// "stop-event": "buffer_read", + /// "subnet-id": 10 + /// }, + /// "start-time": "", + /// "occurrences": 0, + /// "min-duration-usecs": 0, + /// "max-duration-usecs": 0, + /// "total-duration-usecs": 0 + /// } + /// @endcode + /// + /// @return Element::map containing the duration key values. + virtual data::ElementPtr toElement() const; + private: /// @brief Length of the time of a single data interval. Duration interval_duration_; diff --git a/src/hooks/dhcp/perfmon/perfmon_callouts.cc b/src/hooks/dhcp/perfmon/perfmon_callouts.cc index 09df3badbf..b995fe47c0 100644 --- a/src/hooks/dhcp/perfmon/perfmon_callouts.cc +++ b/src/hooks/dhcp/perfmon/perfmon_callouts.cc @@ -115,6 +115,26 @@ int pkt6_send(CalloutHandle& handle) { return (0); } +/// @brief This is a command callout for 'perfmon-control' command. +/// +/// @param handle Callout handle used to retrieve a command and +/// provide a response. +/// @return 0 if this callout has been invoked successfully, +/// 1 otherwise. +int perfmon_control(CalloutHandle& handle) { + return (mgr->perfmonControlHandler(handle)); +} + +/// @brief This is a command callout for 'perfmon-get-all-durations' command. +/// +/// @param handle Callout handle used to retrieve a command and +/// provide a response. +/// @return 0 if this callout has been invoked successfully, +/// 1 otherwise. +int perfmon_get_all_durations(CalloutHandle& handle) { + return (mgr->perfmonGetAllDurationsHandler(handle)); +} + /// @brief This function is called when the library is loaded. /// /// @param handle library handle @@ -141,8 +161,9 @@ int load(LibraryHandle& handle) { ConstElementPtr json = handle.getParameters(); mgr->configure(json); - /// @todo register commands - /// handle.registerCommandCallout("command-here", handler_here); + /// Register commands. + handle.registerCommandCallout("perfmon-control", perfmon_control); + handle.registerCommandCallout("perfmon-get-all-durations", perfmon_get_all_durations); } catch (const std::exception& ex) { LOG_ERROR(perfmon_logger, PERFMON_INIT_FAILED) .arg(ex.what()); diff --git a/src/hooks/dhcp/perfmon/perfmon_config.cc b/src/hooks/dhcp/perfmon/perfmon_config.cc index 99ab61c1d4..6a97ceaac5 100644 --- a/src/hooks/dhcp/perfmon/perfmon_config.cc +++ b/src/hooks/dhcp/perfmon/perfmon_config.cc @@ -324,8 +324,13 @@ PerfMonConfig::parse(data::ConstElementPtr config) { local.parseAlarms(elem); } - // All values good, shallow copy from local instance. - *this = local; + // All values good, copy them from local instance. + family_= local.getFamily(); + enable_monitoring_ = local.getEnableMonitoring(); + interval_width_secs_ = local.getIntervalWidthSecs(); + stats_mgr_reporting_ = local.getStatsMgrReporting(); + alarm_report_secs_ = local.getAlarmReportSecs(); + alarm_store_= local.getAlarmStore(); } void diff --git a/src/hooks/dhcp/perfmon/perfmon_config.h b/src/hooks/dhcp/perfmon/perfmon_config.h index cc03616a8b..9d62782bbf 100644 --- a/src/hooks/dhcp/perfmon/perfmon_config.h +++ b/src/hooks/dhcp/perfmon/perfmon_config.h @@ -12,6 +12,8 @@ #include #include +#include + namespace isc { namespace perfmon { @@ -238,7 +240,7 @@ protected: /// true. If false the library loads and configures but does nothing. /// Gives users a way to keep the library loaded without it being active. /// Should be accessible via explicit API command. - bool enable_monitoring_; + std::atomic enable_monitoring_; /// @brief Number of seconds a duration accumulates samples until reporting. /// Defaults to 60. @@ -246,7 +248,7 @@ protected: /// @brief If true durations report to StatsMgr at the end of each interval. /// Defaults to true. - bool stats_mgr_reporting_; + std::atomic stats_mgr_reporting_; /// @brief Number of seconds between reports of a raised alarm. /// Defaults to 300. A value of zero disables alarms. diff --git a/src/hooks/dhcp/perfmon/perfmon_messages.cc b/src/hooks/dhcp/perfmon/perfmon_messages.cc index 7c390503ca..5fee39676e 100644 --- a/src/hooks/dhcp/perfmon/perfmon_messages.cc +++ b/src/hooks/dhcp/perfmon/perfmon_messages.cc @@ -6,6 +6,10 @@ extern const isc::log::MessageID PERFMON_ALARM_CLEARED = "PERFMON_ALARM_CLEARED"; extern const isc::log::MessageID PERFMON_ALARM_TRIGGERED = "PERFMON_ALARM_TRIGGERED"; +extern const isc::log::MessageID PERFMON_CMDS_CONTROL_ERROR = "PERFMON_CMDS_CONTROL_ERROR"; +extern const isc::log::MessageID PERFMON_CMDS_CONTROL_OK = "PERFMON_CMDS_CONTROL_OK"; +extern const isc::log::MessageID PERFMON_CMDS_GET_ALL_DURATIONS_ERROR = "PERFMON_CMDS_GET_ALL_DURATIONS_ERROR"; +extern const isc::log::MessageID PERFMON_CMDS_GET_ALL_DURATIONS_OK = "PERFMON_CMDS_GET_ALL_DURATIONS_OK"; extern const isc::log::MessageID PERFMON_DEINIT_FAILED = "PERFMON_DEINIT_FAILED"; extern const isc::log::MessageID PERFMON_DEINIT_OK = "PERFMON_DEINIT_OK"; extern const isc::log::MessageID PERFMON_DHCP4_PKT_EVENTS = "PERFMON_DHCP4_PKT_EVENTS"; @@ -22,6 +26,10 @@ namespace { const char* values[] = { "PERFMON_ALARM_CLEARED", "Alarm for %1 has been cleared, reported average duration %2 is now below low-water-ms: %3", "PERFMON_ALARM_TRIGGERED", "Alarm for %1 has been triggered since %2, reported average duration %3 exceeds high-water-ms: %4", + "PERFMON_CMDS_CONTROL_ERROR", "perfmon-control command processing failed: %1", + "PERFMON_CMDS_CONTROL_OK", "perfmon-control command success: active monitoring: %1, stats-mgr-reporting: %2", + "PERFMON_CMDS_GET_ALL_DURATIONS_ERROR", "perfmon-get-all-durations command processing failed: %1", + "PERFMON_CMDS_GET_ALL_DURATIONS_OK", "perfmon-get-all-durations returning %1 durations", "PERFMON_DEINIT_FAILED", "unloading PerfMon hooks library failed: %1", "PERFMON_DEINIT_OK", "unloading PerfMon hooks library successful", "PERFMON_DHCP4_PKT_EVENTS", "query: %1 events=[%2]", diff --git a/src/hooks/dhcp/perfmon/perfmon_messages.h b/src/hooks/dhcp/perfmon/perfmon_messages.h index 494bbc9dc3..8ba726f80e 100644 --- a/src/hooks/dhcp/perfmon/perfmon_messages.h +++ b/src/hooks/dhcp/perfmon/perfmon_messages.h @@ -7,6 +7,10 @@ extern const isc::log::MessageID PERFMON_ALARM_CLEARED; extern const isc::log::MessageID PERFMON_ALARM_TRIGGERED; +extern const isc::log::MessageID PERFMON_CMDS_CONTROL_ERROR; +extern const isc::log::MessageID PERFMON_CMDS_CONTROL_OK; +extern const isc::log::MessageID PERFMON_CMDS_GET_ALL_DURATIONS_ERROR; +extern const isc::log::MessageID PERFMON_CMDS_GET_ALL_DURATIONS_OK; extern const isc::log::MessageID PERFMON_DEINIT_FAILED; extern const isc::log::MessageID PERFMON_DEINIT_OK; extern const isc::log::MessageID PERFMON_DHCP4_PKT_EVENTS; diff --git a/src/hooks/dhcp/perfmon/perfmon_messages.mes b/src/hooks/dhcp/perfmon/perfmon_messages.mes index fe5ad1b6a2..f747033b11 100644 --- a/src/hooks/dhcp/perfmon/perfmon_messages.mes +++ b/src/hooks/dhcp/perfmon/perfmon_messages.mes @@ -70,3 +70,23 @@ the log message. % PERFMON_INIT_OK loading PerfMon hooks library successful This info message indicates that the PerfMon hooks library has been loaded successfully. Enjoy! + +% PERFMON_CMDS_CONTROL_ERROR perfmon-control command processing failed: %1 +This error message is issued when the PerfMon hook library encounters an +error processing a perfmon-control command. The argument explains the +command error. + +% PERFMON_CMDS_CONTROL_OK perfmon-control command success: active monitoring: %1, stats-mgr-reporting: %2 +This info log is issued when perfmon-control command has successfully +enabled/disabled active monitoring and/or statistics mgr reporting. +Arguments reflect the current state of both. + +% PERFMON_CMDS_GET_ALL_DURATIONS_ERROR perfmon-get-all-durations command processing failed: %1 +This error message is issued when the PerfMon hook library encounters an +error processing a perfmon-get-all-durations command. The argument explains the +command error. + +% PERFMON_CMDS_GET_ALL_DURATIONS_OK perfmon-get-all-durations returning %1 durations +This info log is issued when perfmon-get-all-durations command has +completed successfully. The argument contains the number of +durations returned. diff --git a/src/hooks/dhcp/perfmon/perfmon_mgr.cc b/src/hooks/dhcp/perfmon/perfmon_mgr.cc index edff6a3f01..d7e8dda054 100644 --- a/src/hooks/dhcp/perfmon/perfmon_mgr.cc +++ b/src/hooks/dhcp/perfmon/perfmon_mgr.cc @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -18,6 +20,7 @@ namespace isc { namespace perfmon { +using namespace isc::config; using namespace isc::data; using namespace isc::dhcp; using namespace isc::log; @@ -26,7 +29,7 @@ using namespace isc::util; using namespace boost::posix_time; PerfMonMgr::PerfMonMgr(uint16_t family_) - : PerfMonConfig(family_) { + : PerfMonConfig(family_), mutex_(new std::mutex) { init(); } @@ -173,8 +176,8 @@ PerfMonMgr::reportToStatsMgr(MonitoredDurationPtr duration) { auto average = previous_interval->getAverageDuration(); if (getStatsMgrReporting()) { - StatsMgr::instance().setValue(duration->getStatName("average-ms"), - static_cast(average.total_milliseconds())); + StatsMgr::instance().setValue(duration->getStatName("average-usecs"), + static_cast(average.total_microseconds())); } /// @todo - decide if we want to report min and max values too. @@ -219,5 +222,160 @@ PerfMonMgr::setNextReportExpiration() { isc_throw (NotImplemented, __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__); } +int +PerfMonMgr::perfmonControlHandler(hooks::CalloutHandle& handle) { + static SimpleKeywords keywords = { + { "enable-monitoring", Element::boolean }, + { "stats-mgr-reporting", Element::boolean } + }; + + std::string txt = "(missing parameters)"; + ElementPtr result = Element::createMap(); + ConstElementPtr response; + + // Extract the command and then the parameters + try { + extractCommand(handle); + if (cmd_args_) { + txt = cmd_args_->str(); + } + + if (cmd_args_) { + SimpleParser::checkKeywords(keywords, cmd_args_); + + ConstElementPtr elem = cmd_args_->get("enable-monitoring"); + if (elem) { + enable_monitoring_ = elem->boolValue(); + } + + elem = cmd_args_->get("stats-mgr-reporting"); + if (elem) { + stats_mgr_reporting_ = elem->boolValue(); + } + } + + LOG_INFO(perfmon_logger, PERFMON_CMDS_CONTROL_OK) + .arg(enable_monitoring_ ? "enabled" : "disabled") + .arg(stats_mgr_reporting_ ? "enabled" : "disabled"); + + result->set("enable-monitoring", Element::create(enable_monitoring_)); + result->set("stats-mgr-reporting", Element::create(stats_mgr_reporting_)); + response = createAnswer(CONTROL_RESULT_SUCCESS, "perfmon-control success", result); + } catch (const std::exception& ex) { + LOG_ERROR(perfmon_logger, PERFMON_CMDS_CONTROL_ERROR) + .arg(ex.what()); + setErrorResponse(handle, ex.what()); + return (1); + } + + setResponse(handle, response); + return (0); +} + +int +PerfMonMgr::perfmonGetAllDurationsHandler(hooks::CalloutHandle& handle) { + static SimpleKeywords keywords = { + { "result-set-format", Element::boolean } + }; + + ElementPtr result = Element::createMap(); + ConstElementPtr response; + + try { + // Extract the command and then the parameters + bool result_set_format = false; + extractCommand(handle); + if (cmd_args_) { + SimpleParser::checkKeywords(keywords, cmd_args_); + + ConstElementPtr elem = cmd_args_->get("result-set-format"); + if (elem) { + result_set_format = elem->boolValue(); + } + } + + // Fetch the durations from the store. + auto durations = duration_store_->getAll(); + auto rows = durations->size(); + ElementPtr formatted_durations; + + // Format them either as a list of elements or as a result set + if (!result_set_format) { + formatted_durations = formatDurationDataAsElements(durations); + } else { + formatted_durations = formatDurationDataAsResultSet(durations); + } + + // Construct the result + result->set("interval-width-secs", Element::create(getIntervalWidthSecs())); + result->set("timestamp", Element::create(isc::util::ptimeToText(PktEvent::now()))); + result->set((result_set_format ? "durations-result-set" : "durations"), formatted_durations); + + std::ostringstream oss; + oss << "perfmon-get-all-durations: " << rows << " found"; + + response = createAnswer(CONTROL_RESULT_SUCCESS, oss.str(), result); + LOG_INFO(perfmon_logger, PERFMON_CMDS_GET_ALL_DURATIONS_OK) + .arg(rows); + } catch (const std::exception& ex) { + LOG_ERROR(perfmon_logger, PERFMON_CMDS_GET_ALL_DURATIONS_ERROR) + .arg(ex.what()); + setErrorResponse(handle, ex.what()); + return (1); + } + + setResponse(handle, response); + return (0); +} + +ElementPtr +PerfMonMgr::formatDurationDataAsElements(MonitoredDurationCollectionPtr durations) const { + // Create the list. + ElementPtr duration_list = Element::createList(); + + // Add in the duration elements. + for (auto const& d : *durations) { + ElementPtr element = d->toElement(); + duration_list->add(element); + } + + return (duration_list); +} + +ElementPtr +PerfMonMgr::formatDurationDataAsResultSet(MonitoredDurationCollectionPtr /*durations */) const{ + isc_throw (NotImplemented, "Not Implemented - " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__); +#if 0 + // 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 list of column names and add it to the result set. + ElementPtr columns = Element::createList(); + for (auto const& label : column_labels) { + 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); + if (durations.empty()) { + return; + } + + return (value_rows); + for (auto const& d : *durations) { + const auto& reported_interval = d->getPreviousInterval(); + if (reported_interval) { + std::string label = d->getLabel(); + + } +#endif +} + + + + } // end of namespace perfmon } // end of namespace isc diff --git a/src/hooks/dhcp/perfmon/perfmon_mgr.h b/src/hooks/dhcp/perfmon/perfmon_mgr.h index ef95d96b9d..eb4e2768ce 100644 --- a/src/hooks/dhcp/perfmon/perfmon_mgr.h +++ b/src/hooks/dhcp/perfmon/perfmon_mgr.h @@ -12,6 +12,8 @@ #define PERFMON_MGR_H #include +#include +#include #include #include #include @@ -24,7 +26,7 @@ namespace perfmon { /// the PerfMon hook library. It owns the MonitoredDurationStore and AlarmStore /// instances and supplies callout and command API handlers. It derives from /// PerfMonConfig. -class PerfMonMgr : public PerfMonConfig { +class PerfMonMgr : public PerfMonConfig, private config::CmdsImpl { public: /// @brief Constructor. /// @@ -46,7 +48,15 @@ public: /// @brief Processes the event stack of a query packet. /// - /// @todo DETAILS TO FOLLOW + /// -# Emit a dump of packet stack to perfmon debug log at detail level. + /// -# Iterates over the query's event stack creating a DurationKey for each + /// adjacent event pair, computing the elapsed time between them and then calls + /// addDurationSample(). + /// -# Generates composite duration updates. Durations that monitor total response + /// time for a given query/response pair will be computed using the first and last + /// events in the stack, with a begin event label of "composite" and an end label + /// of "total_response" for the DurationKey. These duration updates will be passed + /// into addDurationSample(). Other composite durations may be added in the future. /// /// @param query query packet whose stack is to be processed. /// @param response response packet generated for the query. @@ -105,6 +115,151 @@ public: /// cancel report timer void setNextReportExpiration(); + /// @brief perfmon-control command handler + /// + /// This command sets enable-monitoring and/or stats-mgr-reporting (affects + /// in memory value(s) only). + /// + /// @code + /// { + /// "command": "perfmon-control", + /// "arguments": { + /// "enable-monitoring": true, + /// "stats-mgr-reporting": true + /// } + /// } + /// @endcode + /// + /// It extracts the command name and arguments from the given CalloutHandle, + /// attempts to process them, and then set's the handle's "response" + /// arguments accordingly. Regardless of which parameters were specified + /// in the command arguments (if any), it returns the values for both + /// parameters: + /// + /// @code + /// "arguments": { + /// "enable-monitoring": false, + /// "stats-mgr-reporting": false + /// }, + /// "result": 0, + /// "text": "perfmon-control success" + /// } + /// @endcode + /// + /// @param handle Callout context - which is expected to contain the + /// command JSON text in the "command" argument + /// @return result of the operation + int perfmonControlHandler(hooks::CalloutHandle& handle); + + /// @brief perfmon-get-all-durations handler + /// + /// This command fetches all of the monitored durations and their preivous + /// intervals (if one). + /// + /// @code + /// { + /// "command": "perfmon-get-all-duations", + /// "arguments": { + /// "result-set-format": true + /// } + /// } + /// @endcode + /// + /// It extracts the command name and arguments from the given CalloutHandle, + /// attempts to process them, and then set's the handle's "response" + /// arguments accordingly. If result-set-format is false (the default) the + /// durations are returned as a list of Elements: + /// + /// @code + /// { + /// "result": 0, + /// "text": "perfmon-get-all-durations: n rows found", + /// "arguments": { + /// "result-set-format": false, + /// "interval-width-secs": 5, + /// "timestamp": "2024-01-18 10:11:20.594800" + /// "durations": [{ + /// "duration-key": { + /// "query-type": "discover", + /// "response-type": "offer", + /// "start-event": "socket_received", + /// "stop-event": "buffer_read", + /// "subnet-id": 10 + /// }, + /// "start-time": "2024-01-18 10:11:19.498739", + /// "occurrences": 105, + /// "min-duration-usecs": 5300, + /// "max-duration-usecs": 9000, + /// "total-duration-usecs": 786500 + /// }, + /// .. + /// ] + /// }, + /// } + /// @endcode + /// + /// If result-set-format is true, the durations are returned in a more compact format, + /// patterned after an SQL result set: + /// + /// @code + /// { + /// "result": 0, + /// "text": "perfmon-get-all-durations: n rows found", + /// "arguments": { + /// "result-set-format": true, + /// "interval-width-secs": 5, + /// "timestamp": "2024-01-18 10:11:20.594800" + /// "durations-result-set": { + /// "columns": [ + /// "subnet-id", "query-type", "response-type", "start-event", "end-event", + /// "interval start", "occurences", "min-duration-usecs", "max-duration-usecs", + /// "total-duration-usecs" + /// ], + /// "rows": [ + /// [ + /// 10, "discover", "offer", "socket_received", "buffer_read", + /// "2024-01-18 10:11:19.498739", 105, 5300, 9000, 786500 + /// ], + /// .. + /// ] + /// } + /// } + /// } + /// @endcode + /// + /// @param handle Callout context - which is expected to contain the + /// command JSON text in the "command" argument + /// @return result of the operation + int perfmonGetAllDurationsHandler(hooks::CalloutHandle& handle); + + /// @brief Renders a list of MonitoredDurations as a map of individual Elements + /// + /// @param durations collection of durations to convert + data::ElementPtr formatDurationDataAsElements(MonitoredDurationCollectionPtr durations) const; + + /// @brief Renders a list of MonitoredDurations as a result set + /// + /// The result set Element will be as shown below: + /// + /// @code + /// "durations-result-set": { + /// "columns": [ + /// "subnet-id", "query-type", "response-type", "start-event", "end-event", + /// "interval start", "occurences", "min-duration-usecs", "max-duration-usecs", + /// "total-duration-usecs" + /// ], + /// "rows": [ + /// [ + /// 10, "discover", "offer", "socket_received", "buffer_read", + /// "2024-01-18 10:11:19.498739", 105, 5300, 9000, 786500 + /// ], + /// .. + /// ] + /// @endcode + /// + /// @param durations collection of durations to convert + data::ElementPtr formatDurationDataAsResultSet(MonitoredDurationCollectionPtr durations) const; + /// @brief Get the interval duration. /// /// @return interval-width-secs as a Duration. diff --git a/src/hooks/dhcp/perfmon/tests/Makefile.am b/src/hooks/dhcp/perfmon/tests/Makefile.am index b3e474535d..69bcf8b223 100644 --- a/src/hooks/dhcp/perfmon/tests/Makefile.am +++ b/src/hooks/dhcp/perfmon/tests/Makefile.am @@ -33,6 +33,7 @@ perfmon_unittests_SOURCES += monitored_duration_store_unittests.cc perfmon_unittests_SOURCES += alarm_store_unittests.cc perfmon_unittests_SOURCES += perfmon_config_unittests.cc perfmon_unittests_SOURCES += perfmon_mgr_unittests.cc +perfmon_unittests_SOURCES += perfmon_cmds_unittests.cc perfmon_unittests_SOURCES += duration_key_parser_unittests.cc perfmon_unittests_SOURCES += alarm_parser_unittests.cc diff --git a/src/hooks/dhcp/perfmon/tests/perfmon_cmds_unittests.cc b/src/hooks/dhcp/perfmon/tests/perfmon_cmds_unittests.cc new file mode 100644 index 0000000000..02bce8512f --- /dev/null +++ b/src/hooks/dhcp/perfmon/tests/perfmon_cmds_unittests.cc @@ -0,0 +1,441 @@ +// Copyright (C) 2024 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/. + +/// @file This file contains tests which exercise the PerfmonMgr class. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::asiolink; +using namespace isc::config; +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::hooks; +using namespace isc::perfmon; +using namespace isc::stats; +using namespace isc::test; +using namespace isc::dhcp::test; +using namespace boost::posix_time; + +namespace { + +/// @brief Test fixture for testing PerfMonMgr +class PerfMonCmdTest : public LogContentTest { +public: + /// @brief Constructor. + explicit PerfMonCmdTest(uint16_t family) : family_(family) { + StatsMgr::instance(); + StatsMgr::instance().removeAll(); + StatsMgr::instance().setMaxSampleCountAll(1); + if (family_ == AF_INET) { + subnet22_.reset(new Subnet4(IOAddress("192.0.22.0"), 8, 100, 200, 300, 22)); + subnet33_.reset(new Subnet4(IOAddress("192.0.33.0"), 8, 100, 200, 300, 33)); + } else { + subnet22_.reset(new Subnet6(IOAddress("3001:22::"), 64, 100, 200, 300, 300, 22)); + subnet33_.reset(new Subnet6(IOAddress("3002:33::"), 64, 100, 200, 300, 300, 33)); + } + } + + void SetUp() { + std::string valid_config = + R"({ + "enable-monitoring": false, + "interval-width-secs": 5000, + "stats-mgr-reporting": false, + "alarm-report-secs": 600000, + "alarms": [{ + "duration-key": { + "query-type": "", + "response-type": "", + "start-event": "process-started", + "stop-event": "process-completed", + "subnet-id": 70 + }, + "enable-alarm": true, + "high-water-ms": 500, + "low-water-ms": 25 + }] + })"; + + ASSERT_NO_THROW_LOG(createMgr(valid_config)); + } + + /// @brief Destructor. + virtual ~PerfMonCmdTest() = default; + + /// @brief Re-creates and then configures the PerfMonMgr instance with a + /// given configuration. + /// + /// @param config JSON configuration text + void createMgr(const std::string& config) { + mgr_.reset(new PerfMonMgr(family_)); + ConstElementPtr json_elements; + json_elements = Element::fromJSON(config); + mgr_->configure(json_elements); + } + + /// @brief Make a valid family-specific query. + /// + /// @return if family is AF_INET return a pointer to a DHCPDISCOVER + /// otherwise a pointer to a DHCPV6_SOLICIT. + PktPtr makeFamilyQuery() { + if (family_ == AF_INET) { + return (PktPtr(new Pkt4(DHCPDISCOVER, 7788))); + } + + return (PktPtr(new Pkt6(DHCPV6_SOLICIT, 7788))); + } + + /// @brief Make a valid family-specific response. + /// + /// @return if family is AF_INET return a pointer to a DHCPOFFER + /// otherwise a pointer to a DHCPV6_ADVERTISE. + PktPtr makeFamilyResponse() { + if (family_ == AF_INET) { + return (PktPtr(new Pkt4(DHCPOFFER, 7788))); + } + + return (PktPtr(new Pkt6(DHCPV6_ADVERTISE, 7788))); + } + + /// @brief Tests specified command and verifies response. + /// + /// This method processes supplied command by invoking the + /// corresponding PerfMonMgr command handler and checks + /// if the expected response was returned. + /// + /// @param cmd_txt JSON text command to be sent (must be valid JSON) + /// @param exp_result 0 - success, 1 - error, 2 - ... + /// @param exp_txt expected text response (optional) + /// @return full response returned by the command execution. + ConstElementPtr testCommand(string cmd_txt, int exp_result, string exp_txt, + ConstElementPtr exp_args = ConstElementPtr()) { + ConstElementPtr cmd; + EXPECT_NO_THROW(cmd = Element::fromJSON(cmd_txt)); + if (!cmd) { + ADD_FAILURE() << cmd_txt << " is not a valid JSON, test broken"; + return (ConstElementPtr()); + } + return (testCommand(cmd, exp_result, exp_txt, exp_args)); + } + + /// @brief Tests specified command and verifies response. + /// + /// This method processes supplied command by invoking the + /// corresponding PerfMonMgr command handler. + /// + /// @param cmd JSON command to be sent + /// @param exp_result 0 - success, 1 - error, 2 - ... + /// @param exp_txt expected text response (optional) + /// @return full response returned by the command execution. + ConstElementPtr testCommand(ConstElementPtr cmd, + int exp_result, + string exp_txt, + ConstElementPtr exp_args = ConstElementPtr()) { + string cmd_txt("..."); + if (cmd) { + cmd_txt = prettyPrint(cmd); + } + SCOPED_TRACE(cmd_txt); + + // Command must be a map. + if (!cmd || (cmd->getType() != Element::map)) { + ADD_FAILURE() << cmd_txt << " is not a map, test broken"; + return (ConstElementPtr()); + } + + // We need to extract command name to select appropriate handler. + ConstElementPtr command_element = cmd->get("command"); + if (!command_element || (command_element->getType() != Element::string)) { + ADD_FAILURE() << cmd_txt << " does not contain command parameter"; + return (ConstElementPtr()); + } + + // Command name found. + std::string command_name = command_element->stringValue(); + + // Need to encapsulate the command in CalloutHandle. + CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle(); + callout_handle->setArgument("command", cmd); + + // Run the command handler appropriate for the given command name. + if (command_name == "perfmon-control") { + static_cast(mgr_->perfmonControlHandler(*callout_handle)); + } else { + ADD_FAILURE() << "unrecognized command '" << command_name << "'"; + } + + // Get the response. + ConstElementPtr rsp; + callout_handle->getArgument("response", rsp); + + // Response must be present. + if (!rsp) { + ADD_FAILURE() << "no response returned for command '" + << command_name << "'"; + return (ConstElementPtr()); + } + + // Verify the response against expected values. + checkAnswer(rsp, exp_result, exp_txt, exp_args); + + return (rsp); + } + + /// @brief Compares the status in the given parse result to a given value. + /// + /// @param answer Element set containing an integer response and string + /// comment. + /// @param exp_status is an integer against which to compare the status. + /// @param exp_txt is expected text (not checked if "") + /// + void checkAnswer(isc::data::ConstElementPtr answer, + int exp_status, + string exp_txt = "", + ConstElementPtr exp_args = ConstElementPtr()) { + int rcode = 0; + isc::data::ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + + if (rcode != exp_status) { + ADD_FAILURE() << "Expected status code " << exp_status + << " but received " << rcode << ", comment: " + << (comment ? comment->str() : "(none)"); + } + + // Ok, parseAnswer interface is weird. If there are no arguments, + // it returns content of text. But if there is an argument, + // it returns the argument and it's not possible to retrieve + // "text" (i.e. comment). + if (comment->getType() != Element::string) { + comment = answer->get("text"); + } + + if (!exp_txt.empty()) { + EXPECT_EQ(exp_txt, comment->stringValue()); + } + + if (exp_args) { + ConstElementPtr args = answer->get("arguments"); + ASSERT_TRUE(args); + EXPECT_EQ(*exp_args, *args); + } + } + + // Verify that invalid perfmon-control commands are caught. + void testInvalidPerfMonControl() { + struct Scenario { + int line_; // Scenario line number + std::string cmd_; // JSON command text + int exp_result_; // Expected result code + std::string exp_text_; // Expected result text + }; + + std::list scenarios = { + { + __LINE__, + R"({ + "command": "perfmon-control", + "arguments": { + "enable-monitoring": "bogus" + } + })", + CONTROL_RESULT_ERROR, + "'enable-monitoring' parameter is not a boolean" + }, + { + __LINE__, + R"({ + "command": "perfmon-control", + "arguments": { + "bogus": 23 + } + })", + CONTROL_RESULT_ERROR, + "spurious 'bogus' parameter" + } + }; + + for (const auto& scenario : scenarios) { + stringstream oss; + oss << "scenario at line: " << scenario.line_; + SCOPED_TRACE(oss.str()); + ConstElementPtr answer = testCommand(scenario.cmd_, + scenario.exp_result_, + scenario.exp_text_); + } + } + + // Verify that valid perfmon-control are processed correctly. + void testValidPerfMonControl() { + struct Scenario { + int line_; // Scenario line number + std::string cmd_; // JSON command text + int exp_result_; // Expected result code + std::string exp_text_; // Expected result text + bool exp_monitor_enabled_; // Expected state of monitoring + bool exp_stats_mgr_reporting_; // Expected state of monitoring + }; + + // Verify that monitoring is enabled. + ASSERT_EQ(mgr_->getEnableMonitoring(), false); + + // Define valid scenarios. + std::list scenarios = { + { + // No arguments element should be ok. + __LINE__, + R"({ + "command": "perfmon-control" + })", + CONTROL_RESULT_SUCCESS, + "perfmon-control success", + false, + false + }, + { + // Empty arguments element should be ok. + __LINE__, + R"({ + "command": "perfmon-control", + "arguments": { + } + })", + CONTROL_RESULT_SUCCESS, + "perfmon-control success", + false, + false + }, + { + // Only enable-monitoring should be ok. + __LINE__, + R"({ + "command": "perfmon-control", + "arguments": { + "enable-monitoring": true + } + })", + CONTROL_RESULT_SUCCESS, + "perfmon-control success", + true, + false + }, + { + // Only stats-mgr-reporting should be ok. + __LINE__, + R"({ + "command": "perfmon-control", + "arguments": { + "stats-mgr-reporting": true + } + })", + CONTROL_RESULT_SUCCESS, + "perfmon-control success", + true, + true + }, + { + // Both enable-monitoring and stats-mgr-reporting should be ok. + __LINE__, + R"({ + "command": "perfmon-control", + "arguments": { + "enable-monitoring": false, + "stats-mgr-reporting": false + } + })", + CONTROL_RESULT_SUCCESS, + "perfmon-control success", + false, + false + } + }; + + for (const auto& scenario : scenarios) { + stringstream oss; + oss << "scenario at line: " << scenario.line_; + SCOPED_TRACE(oss.str()); + ElementPtr exp_args(Element::createMap()); + exp_args->set("enable-monitoring", + Element::create(scenario.exp_monitor_enabled_)); + exp_args->set("stats-mgr-reporting", + Element::create(scenario.exp_stats_mgr_reporting_)); + ConstElementPtr answer = testCommand(scenario.cmd_, + scenario.exp_result_, + scenario.exp_text_, + exp_args); + + EXPECT_EQ(mgr_->getEnableMonitoring(), scenario.exp_monitor_enabled_); + EXPECT_EQ(mgr_->getStatsMgrReporting(), scenario.exp_stats_mgr_reporting_); + } + } + + + /// @brief Protocol family AF_INET or AF_INET6 + uint16_t family_; + + /// @brief PerfMonMgr instance used in test functions. + PerfMonMgrPtr mgr_; + + /// @brief Family specific subnets. + SubnetPtr subnet22_; + SubnetPtr subnet33_; +}; + +/// @brief Test fixture for testing PerfMonConfig for DHCPV4. +class PerfMonCmdTest4: public PerfMonCmdTest { +public: + /// @brief Constructor. + explicit PerfMonCmdTest4() : PerfMonCmdTest(AF_INET) { + } + + /// @brief Destructor. + virtual ~PerfMonCmdTest4() = default; +}; + +/// @brief Test fixture for testing PerfMonConfig for DHCPV6. +class PerfMonCmdTest6: public PerfMonCmdTest { +public: + /// @brief Constructor. + explicit PerfMonCmdTest6() : PerfMonCmdTest(AF_INET6) { + } + + /// @brief Destructor. + virtual ~PerfMonCmdTest6() = default; +}; + +TEST_F(PerfMonCmdTest4, invalidPerfMonControl) { + testInvalidPerfMonControl(); +} + +TEST_F(PerfMonCmdTest6, invalidPerfMonControl) { + testInvalidPerfMonControl(); +} + +TEST_F(PerfMonCmdTest4, validPerfMonControl) { + testValidPerfMonControl(); +} + +TEST_F(PerfMonCmdTest6, validPerfMonControl) { + testValidPerfMonControl(); +} + + +} // end of anonymous namespace diff --git a/src/hooks/dhcp/perfmon/tests/perfmon_config_unittests.cc b/src/hooks/dhcp/perfmon/tests/perfmon_config_unittests.cc index f99b24ba15..13a88e1bee 100644 --- a/src/hooks/dhcp/perfmon/tests/perfmon_config_unittests.cc +++ b/src/hooks/dhcp/perfmon/tests/perfmon_config_unittests.cc @@ -62,14 +62,6 @@ public: EXPECT_NO_THROW_LOG(config->setAlarmReportSecs(120)); EXPECT_EQ(config->getAlarmReportSecs(), 120); - - // Verify shallow copy construction. - PerfMonConfigPtr config2(new PerfMonConfig(*config)); - EXPECT_TRUE(config2->getEnableMonitoring()); - EXPECT_EQ(config2->getIntervalWidthSecs(), 4); - EXPECT_FALSE(config2->getStatsMgrReporting()); - EXPECT_EQ(config2->getAlarmReportSecs(), 120); - EXPECT_EQ(config2->getAlarmStore(), config->getAlarmStore()); } /// @brief Exercises PerfMonConfig parameter parsing with valid configuration diff --git a/src/hooks/dhcp/perfmon/tests/perfmon_mgr_unittests.cc b/src/hooks/dhcp/perfmon/tests/perfmon_mgr_unittests.cc index 11af36dfc2..cd1ad3477a 100644 --- a/src/hooks/dhcp/perfmon/tests/perfmon_mgr_unittests.cc +++ b/src/hooks/dhcp/perfmon/tests/perfmon_mgr_unittests.cc @@ -332,16 +332,16 @@ public: ASSERT_NO_THROW_LOG(average = mgr_->reportToStatsMgr(mond)); EXPECT_EQ(milliseconds(175), average); - auto obs = StatsMgr::instance().getObservation(mond->getStatName("average-ms")); + auto obs = StatsMgr::instance().getObservation(mond->getStatName("average-usecs")); ASSERT_TRUE(obs); - EXPECT_EQ(175, obs->getInteger().first); + EXPECT_EQ(175000, obs->getInteger().first); StatsMgr::instance().removeAll(); mgr_->setStatsMgrReporting(false); ASSERT_NO_THROW_LOG(average = mgr_->reportToStatsMgr(mond)); EXPECT_EQ(milliseconds(175), average); - obs = StatsMgr::instance().getObservation(mond->getStatName("average-ms")); + obs = StatsMgr::instance().getObservation(mond->getStatName("average-usecs")); ASSERT_FALSE(obs); } @@ -411,9 +411,9 @@ public: // Should have one stat reported with a average value of 80. EXPECT_EQ(1, StatsMgr::instance().count()); - auto obs = StatsMgr::instance().getObservation(key->getStatName("average-ms")); + auto obs = StatsMgr::instance().getObservation(key->getStatName("average-usecs")); ASSERT_TRUE(obs); - EXPECT_EQ(80, obs->getInteger().first); + EXPECT_EQ(80000, obs->getInteger().first); // The alarm should have triggered and reported. beforeAndAfterAlarm(__LINE__, before_alarm, Alarm::TRIGGERED, true); @@ -450,9 +450,9 @@ public: // Should have one stat reported with a value of 100. EXPECT_EQ(1, StatsMgr::instance().count()); - obs = StatsMgr::instance().getObservation(key->getStatName("average-ms")); + obs = StatsMgr::instance().getObservation(key->getStatName("average-usecs")); ASSERT_TRUE(obs); - EXPECT_EQ(100, obs->getInteger().first); + EXPECT_EQ(100000, obs->getInteger().first); // Sleep 100ms second to make sure the current interval duration elapses. usleep(100 * 1000); @@ -470,9 +470,9 @@ public: // Should have one stat reported with a value of 10. EXPECT_EQ(1, StatsMgr::instance().count()); - obs = StatsMgr::instance().getObservation(key->getStatName("average-ms")); + obs = StatsMgr::instance().getObservation(key->getStatName("average-usecs")); ASSERT_TRUE(obs); - EXPECT_EQ(10, obs->getInteger().first); + EXPECT_EQ(10000, obs->getInteger().first); // Lastly, verify the log entries. EXPECT_TRUE(checkFile()); diff --git a/src/share/api/perfmon-control.json b/src/share/api/perfmon-control.json new file mode 100644 index 0000000000..b5e3d8173f --- /dev/null +++ b/src/share/api/perfmon-control.json @@ -0,0 +1,38 @@ +{ + "access": "write", + "avail": "2.7.0", + "brief": [ + "This command enables/disables active monitoring and statistics reporting." + ], + "cmd-syntax": [ + "{", + " \"command\": \"perfmon-control\",", + " \"arguments\": {", + " \"enable-monitoring\": true,", + " \"stats-mgr-reporting\": false", + " }", + "}", + "" + ], + "description": "See ", + "hook": "perfmon", + "name": "perfmon-control", + "resp-comment": [ + "Result 0 is returned if command succeeds along with the resultant values of both flags.", + "Result is 1 when parameters are malformed or missing." + ], + "resp-syntax": [ + " {", + " \"arguments\": {", + " \"enable-monitoring\": true,", + " \"stats-mgr-reporting\": false", + " },", + " \"result\": 0,", + " \"text\": \"perfmon-control success.\"", + " }" + ], + "support": [ + "kea-dhcp4", + "kea-dhcp6" + ] +}