blob: 90b6b8a4df22a3b45e91b76f85fa665bf99527ee [file] [log] [blame]
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02001#include "report.hpp"
2
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +02003#include "errors.hpp"
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +01004#include "messages/collect_trigger_id.hpp"
5#include "messages/trigger_presence_changed_ind.hpp"
6#include "messages/update_report_ind.hpp"
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02007#include "report_manager.hpp"
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01008#include "utils/clock.hpp"
Krzysztof Grobelny51497a02021-11-09 14:56:22 +01009#include "utils/contains.hpp"
Szymon Dompke1cdd7e42022-06-08 14:43:13 +020010#include "utils/dbus_path_utils.hpp"
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +010011#include "utils/ensure.hpp"
Wludzik, Jozefe2362792020-10-27 17:23:55 +010012#include "utils/transform.hpp"
13
14#include <phosphor-logging/log.hpp>
Wludzik, Jozefb1ff1f62020-10-23 13:20:52 +020015#include <sdbusplus/vtable.hpp>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020016
Szymon Dompke3eb56862021-09-20 15:32:04 +020017#include <limits>
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020018#include <numeric>
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +010019#include <optional>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020020
Patrick Williams583ba442025-02-03 14:28:19 -050021Report::Report(
22 boost::asio::io_context& ioc,
23 const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
24 const std::string& reportId, const std::string& reportName,
25 const ReportingType reportingTypeIn,
26 std::vector<ReportAction> reportActionsIn, const Milliseconds intervalIn,
27 const uint64_t appendLimitIn, const ReportUpdates reportUpdatesIn,
28 interfaces::ReportManager& reportManager,
29 interfaces::JsonStorage& reportStorageIn,
30 std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
31 const interfaces::ReportFactory& reportFactory, const bool enabledIn,
32 std::unique_ptr<interfaces::Clock> clock, Readings readingsIn) :
Patrick Williamsf535cad2024-08-16 15:21:20 -040033 id(reportId), path(utils::pathAppend(utils::constants::reportDirPath, id)),
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010034 name(reportName), reportingType(reportingTypeIn), interval(intervalIn),
Szymon Dompkefdb06a12022-02-11 11:04:44 +010035 reportActions(reportActionsIn.begin(), reportActionsIn.end()),
Krzysztof Grobelny18e71012022-11-02 13:17:01 +000036 metricCount(getMetricCount(metricsIn)), appendLimit(appendLimitIn),
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010037 reportUpdates(reportUpdatesIn), readings(std::move(readingsIn)),
38 readingsBuffer(std::get<1>(readings),
39 deduceBufferSize(reportUpdates, reportingType)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000040 objServer(objServer), metrics(std::move(metricsIn)), timer(ioc),
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010041 triggerIds(collectTriggerIds(ioc)), reportStorage(reportStorageIn),
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020042 clock(std::move(clock)), messanger(ioc)
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020043{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000044 readingParameters =
45 toReadingParameters(utils::transform(metrics, [](const auto& metric) {
Patrick Williams583ba442025-02-03 14:28:19 -050046 return metric->dumpConfiguration();
47 }));
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000048
Szymon Dompkefdb06a12022-02-11 11:04:44 +010049 reportActions.insert(ReportAction::logToMetricReportsCollection);
50
Wludzik, Jozefe2362792020-10-27 17:23:55 +010051 deleteIface = objServer->add_unique_interface(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010052 getPath(), deleteIfaceName,
53 [this, &ioc, &reportManager](auto& dbusIface) {
Patrick Williams583ba442025-02-03 14:28:19 -050054 dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
55 if (persistency)
56 {
57 persistency = false;
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010058
Patrick Williams583ba442025-02-03 14:28:19 -050059 reportIface->signal_property("Persistency");
60 }
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010061
Patrick Williams583ba442025-02-03 14:28:19 -050062 boost::asio::post(ioc, [this, &reportManager] {
63 reportManager.removeReport(this);
64 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +010065 });
66 });
67
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +000068 auto errorMessages = verify(reportingType, interval);
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020069 state.set<ReportFlags::enabled, ReportFlags::valid>(enabledIn,
70 errorMessages.empty());
71
Szymon Dompkefdb06a12022-02-11 11:04:44 +010072 reportIface = makeReportInterface(reportFactory);
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020073 persistency = storeConfiguration();
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010074
75 messanger.on_receive<messages::TriggerPresenceChangedInd>(
76 [this](const auto& msg) {
Patrick Williams583ba442025-02-03 14:28:19 -050077 const auto oldSize = triggerIds.size();
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010078
Patrick Williams583ba442025-02-03 14:28:19 -050079 if (msg.presence == messages::Presence::Exist)
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010080 {
Patrick Williams583ba442025-02-03 14:28:19 -050081 if (utils::contains(msg.reportIds, id))
82 {
83 triggerIds.insert(msg.triggerId);
84 }
85 else if (!utils::contains(msg.reportIds, id))
86 {
87 triggerIds.erase(msg.triggerId);
88 }
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010089 }
Patrick Williams583ba442025-02-03 14:28:19 -050090 else if (msg.presence == messages::Presence::Removed)
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010091 {
92 triggerIds.erase(msg.triggerId);
93 }
94
Patrick Williams583ba442025-02-03 14:28:19 -050095 if (triggerIds.size() != oldSize)
96 {
97 reportIface->signal_property("Triggers");
98 }
99 });
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100100
101 messanger.on_receive<messages::UpdateReportInd>([this](const auto& msg) {
102 if (utils::contains(msg.reportIds, id))
103 {
104 updateReadings();
105 }
106 });
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200107}
108
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100109Report::~Report()
110{
111 if (persistency)
112 {
113 if (shouldStoreMetricValues())
114 {
115 storeConfiguration();
116 }
117 }
118 else
119 {
120 reportStorage.remove(reportFileName());
121 }
122}
123
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200124void Report::activate()
125{
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000126 for (auto& metric : metrics)
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200127 {
128 metric->initialize();
129 }
130
131 scheduleTimer();
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200132}
133
134void Report::deactivate()
135{
136 for (auto& metric : metrics)
137 {
138 metric->deinitialize();
139 }
140
141 unregisterFromMetrics = nullptr;
142 timer.cancel();
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200143}
144
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000145uint64_t Report::getMetricCount(
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100146 const std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200147{
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000148 uint64_t metricCount = 0;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200149 for (auto& metric : metrics)
150 {
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000151 metricCount += metric->metricCount();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200152 }
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000153 return metricCount;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200154}
155
156uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
157 const ReportingType reportingTypeIn) const
158{
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100159 if (reportUpdatesIn == ReportUpdates::overwrite ||
160 reportingTypeIn == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200161 {
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000162 return metricCount;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200163 }
164 else
165 {
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000166 return appendLimit;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200167 }
168}
169
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100170void Report::setReadingBuffer(const ReportUpdates newReportUpdates)
171{
Patrick Williams583ba442025-02-03 14:28:19 -0500172 const auto newBufferSize =
173 deduceBufferSize(newReportUpdates, reportingType);
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200174 if (readingsBuffer.size() != newBufferSize)
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100175 {
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200176 readingsBuffer.clearAndResize(newBufferSize);
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100177 }
178}
179
Szymon Dompke3eb56862021-09-20 15:32:04 +0200180void Report::setReportUpdates(const ReportUpdates newReportUpdates)
181{
182 if (reportUpdates != newReportUpdates)
183 {
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100184 setReadingBuffer(newReportUpdates);
Szymon Dompke3eb56862021-09-20 15:32:04 +0200185 reportUpdates = newReportUpdates;
186 }
187}
188
Patrick Williams583ba442025-02-03 14:28:19 -0500189std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface(
190 const interfaces::ReportFactory& reportFactory)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000191{
Patrick Williams583ba442025-02-03 14:28:19 -0500192 auto dbusIface =
193 objServer->add_unique_interface(getPath(), reportIfaceName);
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200194 dbusIface->register_property_rw<bool>(
195 "Enabled", sdbusplus::vtable::property_::emits_change,
196 [this](bool newVal, auto& oldValue) {
Patrick Williams583ba442025-02-03 14:28:19 -0500197 if (newVal != state.get<ReportFlags::enabled>())
198 {
199 state.set<ReportFlags::enabled>(oldValue = newVal);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200200
Patrick Williams583ba442025-02-03 14:28:19 -0500201 persistency = storeConfiguration();
202 }
203 return 1;
204 },
205 [this](const auto&) { return state.get<ReportFlags::enabled>(); });
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000206 dbusIface->register_method(
207 "SetReportingProperties",
208 [this](std::string newReportingType, uint64_t newInterval) {
Patrick Williams583ba442025-02-03 14:28:19 -0500209 ReportingType newReportingTypeT = reportingType;
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000210
Patrick Williams583ba442025-02-03 14:28:19 -0500211 if (!newReportingType.empty())
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000212 {
Patrick Williams583ba442025-02-03 14:28:19 -0500213 newReportingTypeT = utils::toReportingType(newReportingType);
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000214 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200215
Patrick Williams583ba442025-02-03 14:28:19 -0500216 Milliseconds newIntervalT = interval;
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000217
Patrick Williams583ba442025-02-03 14:28:19 -0500218 if (newInterval != std::numeric_limits<uint64_t>::max())
219 {
220 newIntervalT = Milliseconds(newInterval);
221 }
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000222
Patrick Williams583ba442025-02-03 14:28:19 -0500223 auto errorMessages = verify(newReportingTypeT, newIntervalT);
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000224
Patrick Williams583ba442025-02-03 14:28:19 -0500225 if (!errorMessages.empty())
226 {
227 if (newIntervalT != interval)
228 {
229 throw errors::InvalidArgument("Interval");
230 }
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000231
Patrick Williams583ba442025-02-03 14:28:19 -0500232 throw errors::InvalidArgument("ReportingType");
233 }
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000234
Patrick Williams583ba442025-02-03 14:28:19 -0500235 if (reportingType != newReportingTypeT)
236 {
237 reportingType = newReportingTypeT;
238 reportIface->signal_property("ReportingType");
239 }
240
241 if (interval != newIntervalT)
242 {
243 interval = newIntervalT;
244 reportIface->signal_property("Interval");
245 }
246
247 if (state.set<ReportFlags::valid>(errorMessages.empty()) ==
248 StateEvent::active)
249 {
250 scheduleTimer();
251 }
252
253 persistency = storeConfiguration();
254
255 setReadingBuffer(reportUpdates);
256 });
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000257 dbusIface->register_property_r<uint64_t>(
258 "Interval", sdbusplus::vtable::property_::emits_change,
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000259 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200260 dbusIface->register_property_rw<bool>(
261 "Persistency", sdbusplus::vtable::property_::emits_change,
262 [this](bool newVal, auto& oldVal) {
Patrick Williams583ba442025-02-03 14:28:19 -0500263 if (newVal == persistency)
264 {
265 return 1;
266 }
267 if (newVal)
268 {
269 persistency = oldVal = storeConfiguration();
270 }
271 else
272 {
273 reportStorage.remove(reportFileName());
274 persistency = oldVal = false;
275 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100276 return 1;
Patrick Williams583ba442025-02-03 14:28:19 -0500277 },
278 [this](const auto&) { return persistency; });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000279
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100280 dbusIface->register_property_r("Readings", readings,
281 sdbusplus::vtable::property_::emits_change,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000282 [this](const auto&) { return readings; });
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000283 dbusIface->register_property_r<std::string>(
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200284 "ReportingType", sdbusplus::vtable::property_::emits_change,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100285 [this](const auto&) { return utils::enumToString(reportingType); });
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100286 dbusIface->register_property_rw(
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000287 "ReadingParameters", readingParameters,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100288 sdbusplus::vtable::property_::emits_change,
289 [this, &reportFactory](auto newVal, auto& oldVal) {
Patrick Williams583ba442025-02-03 14:28:19 -0500290 auto labeledMetricParams =
291 reportFactory.convertMetricParams(newVal);
292 ReportManager::verifyMetricParams(labeledMetricParams);
293 reportFactory.updateMetrics(metrics,
294 state.get<ReportFlags::enabled>(),
295 labeledMetricParams);
296 readingParameters = toReadingParameters(
297 utils::transform(metrics, [](const auto& metric) {
298 return metric->dumpConfiguration();
299 }));
300 metricCount = getMetricCount(metrics);
301 setReadingBuffer(reportUpdates);
302 persistency = storeConfiguration();
303 oldVal = std::move(newVal);
304 return 1;
305 },
306 [this](const auto&) { return readingParameters; });
307 dbusIface->register_property_r<bool>(
308 "EmitsReadingsUpdate", sdbusplus::vtable::property_::none,
309 [this](const auto&) {
310 return reportActions.contains(ReportAction::emitsReadingsUpdate);
311 });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200312 dbusIface->register_property_r<std::string>(
313 "Name", sdbusplus::vtable::property_::const_,
314 [this](const auto&) { return name; });
Patrick Williams583ba442025-02-03 14:28:19 -0500315 dbusIface->register_property_r<bool>(
316 "LogToMetricReportsCollection", sdbusplus::vtable::property_::const_,
317 [this](const auto&) {
318 return reportActions.contains(
319 ReportAction::logToMetricReportsCollection);
320 });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200321 dbusIface->register_property_rw<std::vector<std::string>>(
322 "ReportActions", sdbusplus::vtable::property_::emits_change,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100323 [this](auto newVal, auto& oldVal) {
Patrick Williams583ba442025-02-03 14:28:19 -0500324 auto tmp = utils::transform<std::unordered_set>(
325 newVal, [](const auto& reportAction) {
326 return utils::toReportAction(reportAction);
327 });
328 tmp.insert(ReportAction::logToMetricReportsCollection);
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100329
Patrick Williams583ba442025-02-03 14:28:19 -0500330 if (tmp != reportActions)
331 {
332 reportActions = tmp;
333 persistency = storeConfiguration();
334 oldVal = std::move(newVal);
335 }
336 return 1;
337 },
338 [this](const auto&) {
339 return utils::transform<std::vector>(
340 reportActions, [](const auto reportAction) {
341 return utils::enumToString(reportAction);
342 });
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100343 });
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000344 dbusIface->register_property_r<uint64_t>(
345 "AppendLimit", sdbusplus::vtable::property_::emits_change,
346 [this](const auto&) { return appendLimit; });
Patrick Williams583ba442025-02-03 14:28:19 -0500347 dbusIface->register_property_rw(
348 "ReportUpdates", std::string(),
349 sdbusplus::vtable::property_::emits_change,
350 [this](auto newVal, auto& oldVal) {
351 setReportUpdates(utils::toReportUpdates(newVal));
352 oldVal = newVal;
353 return 1;
354 },
355 [this](const auto&) { return utils::enumToString(reportUpdates); });
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100356 dbusIface->register_property_r(
Szymon Dompke1cdd7e42022-06-08 14:43:13 +0200357 "Triggers", std::vector<sdbusplus::message::object_path>{},
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100358 sdbusplus::vtable::property_::emits_change, [this](const auto&) {
Patrick Williams583ba442025-02-03 14:28:19 -0500359 return utils::transform<std::vector>(
360 triggerIds, [](const auto& triggerId) {
361 return utils::pathAppend(utils::constants::triggerDirPath,
362 triggerId);
363 });
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100364 });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000365 dbusIface->register_method("Update", [this] {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100366 if (reportingType == ReportingType::onRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000367 {
368 updateReadings();
369 }
370 });
371 constexpr bool skipPropertiesChangedSignal = true;
372 dbusIface->initialize(skipPropertiesChangedSignal);
373 return dbusIface;
374}
375
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100376void Report::timerProcForPeriodicReport(boost::system::error_code ec,
377 Report& self)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200378{
379 if (ec)
380 {
381 return;
382 }
383
384 self.updateReadings();
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100385 self.scheduleTimerForPeriodicReport(self.interval);
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200386}
387
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100388void Report::timerProcForOnChangeReport(boost::system::error_code ec,
389 Report& self)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200390{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100391 if (ec)
392 {
393 return;
394 }
395
396 const auto ensure =
397 utils::Ensure{[&self] { self.onChangeContext = std::nullopt; }};
398
399 self.onChangeContext.emplace(self);
400
401 const auto steadyTimestamp = self.clock->steadyTimestamp();
402
403 for (auto& metric : self.metrics)
404 {
405 metric->updateReadings(steadyTimestamp);
406 }
407
408 self.scheduleTimerForOnChangeReport();
409}
410
411void Report::scheduleTimerForPeriodicReport(Milliseconds timerInterval)
412{
Wojciech Tempczyk38c64092023-10-05 12:28:55 +0200413 try
414 {
415 timer.expires_after(timerInterval);
416 timer.async_wait([this](boost::system::error_code ec) {
417 timerProcForPeriodicReport(ec, *this);
418 });
419 }
420 catch (const boost::system::system_error& exception)
421 {
422 phosphor::logging::log<phosphor::logging::level::ERR>(
423 "Failed to schedule timer for periodic report: ",
424 phosphor::logging::entry("EXCEPTION_MSG=%s", exception.what()));
425 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100426}
427
428void Report::scheduleTimerForOnChangeReport()
429{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100430 constexpr Milliseconds timerInterval{100};
431
432 timer.expires_after(timerInterval);
433 timer.async_wait([this](boost::system::error_code ec) {
434 timerProcForOnChangeReport(ec, *this);
435 });
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200436}
437
438void Report::updateReadings()
439{
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200440 if (!state.isActive())
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000441 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200442 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000443 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200444
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100445 if (reportUpdates == ReportUpdates::overwrite ||
446 reportingType == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200447 {
448 readingsBuffer.clear();
449 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000450
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200451 for (const auto& metric : metrics)
452 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200453 if (!state.isActive())
454 {
455 break;
456 }
457
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000458 for (const auto& [metadata, value, timestamp] :
Krzysztof Grobelny9e8da542022-02-17 10:40:16 +0100459 metric->getUpdatedReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200460 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100461 if (reportUpdates == ReportUpdates::appendStopsWhenFull &&
Szymon Dompke3eb56862021-09-20 15:32:04 +0200462 readingsBuffer.isFull())
463 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200464 state.set<ReportFlags::enabled>(false);
465 reportIface->signal_property("Enabled");
Szymon Dompke3eb56862021-09-20 15:32:04 +0200466 break;
467 }
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000468 readingsBuffer.emplace(metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200469 }
470 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200471
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100472 std::get<0>(readings) =
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100473 std::chrono::duration_cast<Milliseconds>(clock->systemTimestamp())
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100474 .count();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100475
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100476 if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
477 {
478 reportIface->signal_property("Readings");
479 }
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200480}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100481
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100482bool Report::shouldStoreMetricValues() const
483{
484 return reportingType != ReportingType::onRequest &&
485 reportUpdates == ReportUpdates::appendStopsWhenFull;
486}
487
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100488bool Report::storeConfiguration() const
489{
490 try
491 {
492 nlohmann::json data;
493
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200494 data["Enabled"] = state.get<ReportFlags::enabled>();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100495 data["Version"] = reportVersion;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100496 data["Id"] = id;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100497 data["Name"] = name;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100498 data["ReportingType"] = utils::toUnderlying(reportingType);
Patrick Williams583ba442025-02-03 14:28:19 -0500499 data["ReportActions"] =
500 utils::transform(reportActions, [](const auto reportAction) {
501 return utils::toUnderlying(reportAction);
502 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100503 data["Interval"] = interval.count();
Krzysztof Grobelny18e71012022-11-02 13:17:01 +0000504 data["AppendLimit"] = appendLimit;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100505 data["ReportUpdates"] = utils::toUnderlying(reportUpdates);
Patrick Williams583ba442025-02-03 14:28:19 -0500506 data["ReadingParameters"] =
507 utils::transform(metrics, [](const auto& metric) {
508 return metric->dumpConfiguration();
509 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100510
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100511 if (shouldStoreMetricValues())
512 {
513 data["MetricValues"] = utils::toLabeledReadings(readings);
514 }
515
516 reportStorage.store(reportFileName(), data);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100517 }
518 catch (const std::exception& e)
519 {
520 phosphor::logging::log<phosphor::logging::level::ERR>(
521 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100522 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100523 return false;
524 }
525
526 return true;
527}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100528
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100529interfaces::JsonStorage::FilePath Report::reportFileName() const
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100530{
531 return interfaces::JsonStorage::FilePath{
532 std::to_string(std::hash<std::string>{}(id))};
533}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100534
Patrick Williams583ba442025-02-03 14:28:19 -0500535std::unordered_set<std::string> Report::collectTriggerIds(
536 boost::asio::io_context& ioc) const
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100537{
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100538 utils::Messanger tmp(ioc);
539
540 auto result = std::unordered_set<std::string>();
541
Patrick Williams583ba442025-02-03 14:28:19 -0500542 tmp.on_receive<messages::CollectTriggerIdResp>([&result](const auto& msg) {
543 result.insert(msg.triggerId);
544 });
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100545
546 tmp.send(messages::CollectTriggerIdReq{id});
547
548 return result;
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100549}
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100550
551void Report::metricUpdated()
552{
553 if (onChangeContext)
554 {
555 onChangeContext->metricUpdated();
556 return;
557 }
558
559 updateReadings();
560}
561
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200562void Report::scheduleTimer()
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100563{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100564 switch (reportingType)
565 {
566 case ReportingType::periodic:
567 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200568 unregisterFromMetrics = nullptr;
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100569 scheduleTimerForPeriodicReport(interval);
570 break;
571 }
572 case ReportingType::onChange:
573 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200574 if (!unregisterFromMetrics)
575 {
576 unregisterFromMetrics = [this] {
577 for (auto& metric : metrics)
578 {
579 metric->unregisterFromUpdates(*this);
580 }
581 };
582
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100583 for (auto& metric : metrics)
584 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200585 metric->registerForUpdates(*this);
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100586 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200587 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100588
589 bool isTimerRequired = false;
590
591 for (auto& metric : metrics)
592 {
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100593 if (metric->isTimerRequired())
594 {
595 isTimerRequired = true;
596 }
597 }
598
599 if (isTimerRequired)
600 {
601 scheduleTimerForOnChangeReport();
602 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200603 else
604 {
605 timer.cancel();
606 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100607 break;
608 }
609 default:
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200610 unregisterFromMetrics = nullptr;
611 timer.cancel();
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100612 break;
613 }
614}
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200615
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000616std::vector<ErrorMessage> Report::verify(ReportingType reportingType,
617 Milliseconds interval)
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200618{
Krzysztof Grobelnycff70c12022-10-27 07:16:08 +0000619 if (interval != Milliseconds{0} && interval < ReportManager::minInterval)
620 {
621 throw errors::InvalidArgument("Interval");
622 }
623
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200624 std::vector<ErrorMessage> result;
625
626 if ((reportingType == ReportingType::periodic &&
627 interval == Milliseconds{0}) ||
628 (reportingType != ReportingType::periodic &&
629 interval != Milliseconds{0}))
630 {
631 result.emplace_back(ErrorType::propertyConflict, "Interval");
632 result.emplace_back(ErrorType::propertyConflict, "ReportingType");
633 }
634
635 return result;
636}