Kea 3.3.3
perfmon_mgr.cc
Go to the documentation of this file.
1// Copyright (C) 2024-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7// Functions accessed by the hooks framework use C linkage to avoid the name
8// mangling that accompanies use of the C++ compiler as well as to avoid
9// issues related to namespaces.
10#include <config.h>
11
12#include <cc/simple_parser.h>
14#include <dhcp/dhcp6.h>
15#include <perfmon/perfmon_mgr.h>
16#include <stats/stats_mgr.h>
18
19#include <perfmon_log.h>
20
21namespace isc {
22namespace perfmon {
23
24using namespace isc::config;
25using namespace isc::data;
26using namespace isc::dhcp;
27using namespace isc::log;
28using namespace isc::stats;
29using namespace isc::util;
30using namespace boost::posix_time;
31
32PerfMonMgr::PerfMonMgr(uint16_t family)
33 : PerfMonConfig(family), mutex_(new std::mutex) {
34 init();
35}
36
37void
39 // Set convenience values.
42
43 // Re-create the duration store.
45}
46
47void
49 if (!params) {
50 // User wants passive logging only.
52 return;
53 }
54
55 if (params->getType() != Element::map) {
56 isc_throw(dhcp::DhcpConfigError, "params must be an Element::map");
57 return;
58 }
59
60 // Parse 'parameters' map.
61 try {
62 parse(params);
63 } catch (std::exception& ex) {
65 "PerfMonMgr::configure failed - " << ex.what());
66 }
67
68 init();
69}
70
71void
73 ConstSubnetPtr subnet) {
74 if (!query) {
75 isc_throw(Unexpected, "PerfMonMgr::processPktEventStack - query is empty!");
76 }
77 uint16_t query_type = query->getType();
78
79 // Response is optional to allow for future support of responseless queries
80 // such as declines or releases.
81 uint16_t response_type;
82 if (!response) {
83 response_type = (family_ == AF_INET ? static_cast<uint16_t>(DHCP_NOTYPE)
84 : static_cast<uint16_t>(DHCPV6_NOTYPE));
85 } else {
86 response_type = response->getType();
87 }
88
89 // Sanity check the message types.
90 DurationKey::validateMessagePair(family_, query_type, response_type);
91
92 auto events = query->getPktEvents();
93 if (events.size() < 2) {
94 isc_throw(Unexpected, "PerfMonMgr::processPtkEventStack - incomplete stack, size: "
95 << events.size());
96 }
97
98 // If we're here without a selected subnet, use global subnet id.
99 SubnetID subnet_id = (subnet ? subnet->getID() : SUBNET_ID_GLOBAL);
100
103 .arg(query->getLabel())
104 .arg(query->dumpPktEvents());
105
106 // If monitoring is disabled, then punt.
107 if (!enable_monitoring_) {
108 return;
109 }
110
111 boost::posix_time::ptime start_time;
112 boost::posix_time::ptime prev_time;
113 std::string prev_event_label;
114 bool first_pass = true;
115 for (auto const& event : events) {
116 if (first_pass) {
117 prev_event_label = event.label_;
118 prev_time = event.timestamp_;
119 start_time = prev_time;
120 first_pass = false;
121 } else {
122 Duration sample = event.timestamp_ - prev_time;
123 DurationKeyPtr key(new DurationKey(family_, query_type, response_type,
124 prev_event_label, event.label_, subnet_id));
125 addDurationSample(key, sample);
126
127 // Update global duration.
128 if (subnet_id != SUBNET_ID_GLOBAL) {
129 key->setSubnetId(SUBNET_ID_GLOBAL);
130 addDurationSample(key, sample);
131 }
132
133 prev_event_label = event.label_;
134 prev_time = event.timestamp_;
135 }
136 }
137
138 // Generate composite total.
139 Duration sample = prev_time - start_time;
140 DurationKeyPtr key(new DurationKey(family_, query_type, response_type,
141 "composite", "total_response", subnet_id));
142 addDurationSample(key, sample);
143 // Update global duration.
144 if (subnet_id != SUBNET_ID_GLOBAL) {
145 key->setSubnetId(SUBNET_ID_GLOBAL);
146 addDurationSample(key, sample);
147 }
148}
149
150void
152 // Update duration - duration is only returned if its time to report.
153 MonitoredDurationPtr duration = duration_store_->addDurationSample(key, sample);
154 if (duration) {
155 // Report to stat mgr, returns mean duration.
156 Duration mean = reportToStatsMgr(duration);
157
158 // Check the mean against an alarm, if one exists.
159 AlarmPtr alarm = alarm_store_->checkDurationSample(duration, mean, alarm_report_interval_);
160
161 // If an alarm had a reportable outcome, report it.
162 if (alarm) {
163 reportAlarm(alarm, mean);
164 }
165 }
166}
167
170 if (!duration) {
171 isc_throw(BadValue, "reportToStatsMgr - duration is empty!");
172 }
173
174 auto previous_interval = duration->getPreviousInterval();
175 if (!previous_interval) {
176 isc_throw(BadValue, "reportToStatsMgr - duration previous interval is empty!");
177 }
178
179 auto mean = previous_interval->getMeanDuration();
180 if (getStatsMgrReporting()) {
181 StatsMgr::instance().setValue(duration->getStatName("mean-usecs"),
182 static_cast<int64_t>(mean.total_microseconds()));
183 }
184
186
187 return (mean);
188}
189
190void
192 std::string label = alarm->getLabel();
193 switch(alarm->getState()) {
194 case Alarm::CLEAR:
196 .arg(alarm->getLabel())
197 .arg(mean)
198 .arg(alarm->getLowWater().total_milliseconds());
199 break;
200
201 case Alarm::TRIGGERED:
203 .arg(alarm->getLabel())
204 .arg(ptimeToText(alarm->getStosTime(), 3))
205 .arg(mean)
206 .arg(alarm->getHighWater().total_milliseconds());
207 alarm->setLastHighWaterReport();
208 alarm_store_->updateAlarm(alarm);
209 break;
210
211 case Alarm::DISABLED:
212 // Shouldn't happen. We'll silently ignore for now.
213 break;
214 }
215}
216
217void
219 isc_throw (NotImplemented, __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__);
220}
221
222void
224 isc_throw (NotImplemented, __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__);
225}
226
227int
229 static SimpleKeywords keywords = {
230 { "enable-monitoring", Element::boolean },
231 { "stats-mgr-reporting", Element::boolean }
232 };
233
234 std::string txt = "(missing parameters)";
236 ConstElementPtr response;
237
238 // Extract the command and then the parameters
239 try {
240 extractCommand(handle);
241 if (cmd_args_) {
242 txt = cmd_args_->str();
243 }
244
245
246 // Both arguments are optional.
247 if (cmd_args_) {
249
250 ConstElementPtr elem = cmd_args_->get("enable-monitoring");
251 if (elem) {
252 enable_monitoring_ = elem->boolValue();
253 }
254
255 elem = cmd_args_->get("stats-mgr-reporting");
256 if (elem) {
257 stats_mgr_reporting_ = elem->boolValue();
258 }
259 }
260
262 .arg(enable_monitoring_ ? "enabled" : "disabled")
263 .arg(stats_mgr_reporting_ ? "enabled" : "disabled");
264
265 // Always return the new/current values for both settings.
266 result->set("enable-monitoring", Element::create(enable_monitoring_));
267 result->set("stats-mgr-reporting", Element::create(stats_mgr_reporting_));
268 response = createAnswer(CONTROL_RESULT_SUCCESS, "perfmon-control success", result);
269 } catch (const std::exception& ex) {
271 .arg(ex.what());
272 setErrorResponse(handle, ex.what());
273 return (1);
274 }
275
276 setResponse(handle, response);
277 return (0);
278}
279
280int
282 static SimpleKeywords keywords = {
283 { "result-set-format", Element::boolean }
284 };
285
287 ConstElementPtr response;
288
289 try {
290 // Extract the command and then the parameters
291 bool result_set_format = false;
292 extractCommand(handle);
293 if (cmd_args_) {
295 ConstElementPtr elem = cmd_args_->get("result-set-format");
296 if (elem) {
297 result_set_format = elem->boolValue();
298 }
299 }
300
301 // Fetch the durations from the store.
302 auto durations = duration_store_->getAll();
303 auto rows = durations->size();
304 ElementPtr formatted_durations;
305
306 // Format them either as a list of elements or as a result set
307 if (!result_set_format) {
308 formatted_durations = formatDurationDataAsElements(durations);
309 } else {
310 formatted_durations = formatDurationDataAsResultSet(durations);
311 }
312
313 // Construct the result
314 result->set("interval-width-secs", Element::create(getIntervalWidthSecs()));
315 result->set("timestamp", Element::create(isc::util::ptimeToText(PktEvent::now())));
316 result->set("result-set-format", Element::create(result_set_format));
317 result->set((result_set_format ? "durations-result-set" : "durations"), formatted_durations);
318
319 std::ostringstream oss;
320 oss << "perfmon-get-all-durations: " << rows << " found";
321
323 oss.str(), result);
325 .arg(rows);
326 } catch (const std::exception& ex) {
328 .arg(ex.what());
329 setErrorResponse(handle, ex.what());
330 return (1);
331 }
332
333 setResponse(handle, response);
334 return (0);
335}
336
339 // Create the list.
340 ElementPtr duration_list = Element::createList();
341
342 // Add in the duration elements.
343 for (auto const& d : *durations) {
344 ElementPtr element = d->toElement();
345 duration_list->add(element);
346 }
347
348 return (duration_list);
349}
350
353 // Create the result-set map and add it to the wrapper.
354 ElementPtr result_set = Element::createMap();
355
356 // Create the list of column names and add it to the result set.
357 result_set->set("columns", MonitoredDuration::valueRowColumns());
358
359 // Create the rows list and add it to the result set.
361 result_set->set("rows", rows);
362
363 for (auto const& d : *durations) {
364 // Create the value row.
365 ElementPtr row = d->toValueRow();
366 rows->add(row);
367 }
368
369 return (result_set);
370}
371
372} // end of namespace perfmon
373} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown when a function is not implemented.
A generic exception that is thrown when an unexpected error condition occurs.
void setErrorResponse(hooks::CalloutHandle &handle, const std::string &text, int status=CONTROL_RESULT_ERROR)
Set the callout argument "response" to indicate an error.
Definition cmds_impl.h:54
data::ConstElementPtr cmd_args_
Stores the command arguments extracted by a call to extractCommand.
Definition cmds_impl.h:72
void extractCommand(hooks::CalloutHandle &handle)
Extracts the command name and arguments from a Callout handle.
Definition cmds_impl.h:29
void setResponse(hooks::CalloutHandle &handle, data::ConstElementPtr &response)
Set the callout argument "response" to the given response.
Definition cmds_impl.h:64
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:300
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:355
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:350
static void checkKeywords(const SimpleKeywords &keywords, isc::data::ConstElementPtr scope)
Checks acceptable keywords with their expected type.
To be removed. Please use ConfigError instead.
static boost::posix_time::ptime now()
Fetch the current UTC system time, microsecond precision.
Definition pkt.h:118
Per-packet callout handle.
Houses the composite key that uniquely identifies a duration:
static void validateMessagePair(uint16_t family, uint8_t query_type, uint8_t response_type)
Validates that a query and response message type pair is sane.
Maintains an in-memory store of durations.
static data::ConstElementPtr valueRowColumns()
Fetches an Element::list of value row column names.
PerfMonConfig(uint16_t family)
Constructor.
uint32_t interval_width_secs_
Number of seconds a duration accumulates samples until reporting.
uint32_t alarm_report_secs_
Number of seconds between reports of a raised alarm.
void setEnableMonitoring(bool value)
Sets the value of enable-monitoring.
uint16_t family_
Protocol family AF_INET or AF_INET6.
bool getStatsMgrReporting() const
Fetches the value of stats-mgr-reporting.
void parse(data::ConstElementPtr config)
Extracts member values from an Element::map.
AlarmStorePtr alarm_store_
Stores the configured alarms.
std::atomic< bool > stats_mgr_reporting_
If true durations report to StatsMgr at the end of each interval.
uint32_t getIntervalWidthSecs() const
Fetches the value of interval-width-secs.
std::atomic< bool > enable_monitoring_
If true, performance data is processed/reported.
void reportTimerExpired()
Handler invoked when the report timer expires.
Duration interval_duration_
Length of time a MonitoredDuration accumulates samples until reporting.
int perfmonGetAllDurationsHandler(hooks::CalloutHandle &handle)
perfmon-get-all-durations handler
const boost::scoped_ptr< std::mutex > mutex_
The mutex used to protect internal state.
void setNextReportExpiration()
Updates the report timer.
void addDurationSample(DurationKeyPtr key, const Duration &sample)
Adds a duration sample to a MonitoredDuration.
data::ElementPtr formatDurationDataAsElements(MonitoredDurationCollectionPtr durations) const
Renders a list of MonitoredDurations as a map of individual Elements.
void processPktEventStack(isc::dhcp::PktPtr query, isc::dhcp::PktPtr response, const isc::dhcp::ConstSubnetPtr subnet)
Processes the event stack of a query packet.
int perfmonControlHandler(hooks::CalloutHandle &handle)
perfmon-control command handler
PerfMonMgr(uint16_t family)
Constructor.
Duration reportToStatsMgr(MonitoredDurationPtr duration)
Emits an entry to StatsMgr for a given duration.
void configure(const isc::data::ConstElementPtr &params)
Parses the hook library 'parameters' element.
MonitoredDurationStorePtr duration_store_
In-memory store of MonitoredDurations.
data::ElementPtr formatDurationDataAsResultSet(MonitoredDurationCollectionPtr durations) const
Renders a list of MonitoredDurations as a result set.
Duration alarm_report_interval_
Length of time between raised Alarm reports.
virtual void init()
Sets convenience values and (re)creates the duration store.
void reportAlarm(AlarmPtr alarm, const Duration &mean)
Emits a report for a given alarm.
static StatsMgr & instance()
Statistics Manager accessor method.
@ DHCPV6_NOTYPE
Definition dhcp6.h:207
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void setValue(const std::string &name, const int64_t value)
Records absolute integer observation.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const int CONTROL_RESULT_EMPTY
Status code indicating that the specified command was completed correctly, but failed to produce any ...
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
std::map< std::string, isc::data::Element::types > SimpleKeywords
This specifies all accepted keywords with their types.
boost::shared_ptr< isc::dhcp::Pkt > PktPtr
A pointer to either Pkt4 or Pkt6 packet.
Definition pkt.h:1006
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
@ DHCP_NOTYPE
Message Type option missing.
Definition dhcp4.h:234
boost::shared_ptr< const Subnet > ConstSubnetPtr
A generic pointer to either const Subnet4 or const Subnet6 object.
Definition subnet.h:449
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
boost::posix_time::time_duration Duration
boost::shared_ptr< MonitoredDurationCollection > MonitoredDurationCollectionPtr
Type for a pointer to a collection of MonitoredDurationPtrs.
boost::shared_ptr< DurationKey > DurationKeyPtr
Defines a pointer to a DurationKey instance.
boost::shared_ptr< MonitoredDuration > MonitoredDurationPtr
boost::shared_ptr< Alarm > AlarmPtr
Defines a pointer to an Alarm instance.
Definition alarm.h:170
isc::log::Logger perfmon_logger("perfmon-hooks")
Definition perfmon_log.h:17
std::string ptimeToText(boost::posix_time::ptime t, size_t fsecs_precision=MAX_FSECS_PRECISION)
Converts ptime structure to text.
Defines the logger used by the top-level component of kea-lfc.
const isc::log::MessageID PERFMON_ALARM_CLEARED
const isc::log::MessageID PERFMON_CMDS_GET_ALL_DURATIONS_OK
const isc::log::MessageID PERFMON_CMDS_CONTROL_OK
const isc::log::MessageID PERFMON_ALARM_TRIGGERED
const isc::log::MessageID PERFMON_DHCP4_PKT_EVENTS
const isc::log::MessageID PERFMON_CMDS_GET_ALL_DURATIONS_ERROR
const isc::log::MessageID PERFMON_DHCP6_PKT_EVENTS
const isc::log::MessageID PERFMON_CMDS_CONTROL_ERROR