blob: 9ee60a4959593a5e510d54a421b4bc1992e63145 [file] [log] [blame]
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02001#include "report.hpp"
2
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +01003#include "messages/collect_trigger_id.hpp"
4#include "messages/trigger_presence_changed_ind.hpp"
5#include "messages/update_report_ind.hpp"
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02006#include "report_manager.hpp"
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01007#include "utils/clock.hpp"
Krzysztof Grobelny51497a02021-11-09 14:56:22 +01008#include "utils/contains.hpp"
Szymon Dompke1cdd7e42022-06-08 14:43:13 +02009#include "utils/dbus_path_utils.hpp"
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +010010#include "utils/ensure.hpp"
Wludzik, Jozefe2362792020-10-27 17:23:55 +010011#include "utils/transform.hpp"
12
13#include <phosphor-logging/log.hpp>
Wludzik, Jozefb1ff1f62020-10-23 13:20:52 +020014#include <sdbusplus/vtable.hpp>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020015
Szymon Dompke3eb56862021-09-20 15:32:04 +020016#include <limits>
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020017#include <numeric>
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +010018#include <optional>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020019
20Report::Report(boost::asio::io_context& ioc,
21 const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010022 const std::string& reportId, const std::string& reportName,
Szymon Dompke3eb56862021-09-20 15:32:04 +020023 const ReportingType reportingTypeIn,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010024 std::vector<ReportAction> reportActionsIn,
Szymon Dompke3eb56862021-09-20 15:32:04 +020025 const Milliseconds intervalIn, const uint64_t appendLimitIn,
26 const ReportUpdates reportUpdatesIn,
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020027 interfaces::ReportManager& reportManager,
Wludzik, Jozefe2362792020-10-27 17:23:55 +010028 interfaces::JsonStorage& reportStorageIn,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020029 std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
Szymon Dompkefdb06a12022-02-11 11:04:44 +010030 const interfaces::ReportFactory& reportFactory,
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010031 const bool enabledIn, std::unique_ptr<interfaces::Clock> clock,
32 Readings readingsIn) :
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010033 id(reportId),
Szymon Dompke1cdd7e42022-06-08 14:43:13 +020034 path(utils::pathAppend(utils::constants::reportDirPath, id)),
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010035 name(reportName), reportingType(reportingTypeIn), interval(intervalIn),
Szymon Dompkefdb06a12022-02-11 11:04:44 +010036 reportActions(reportActionsIn.begin(), reportActionsIn.end()),
Szymon Dompke3eb56862021-09-20 15:32:04 +020037 sensorCount(getSensorCount(metricsIn)),
38 appendLimit(deduceAppendLimit(appendLimitIn)),
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010039 reportUpdates(reportUpdatesIn), readings(std::move(readingsIn)),
40 readingsBuffer(std::get<1>(readings),
41 deduceBufferSize(reportUpdates, reportingType)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000042 objServer(objServer), metrics(std::move(metricsIn)), timer(ioc),
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010043 triggerIds(collectTriggerIds(ioc)), reportStorage(reportStorageIn),
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020044 clock(std::move(clock)), messanger(ioc)
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020045{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000046 readingParameters =
47 toReadingParameters(utils::transform(metrics, [](const auto& metric) {
48 return metric->dumpConfiguration();
49 }));
50
51 readingParametersPastVersion =
52 utils::transform(readingParameters, [](const auto& item) {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010053 const auto& [sensorData, operationType, id, collectionTimeScope,
54 collectionDuration] = item;
55
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000056 return ReadingParametersPastVersion::value_type(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010057 std::get<0>(sensorData.front()), operationType, id,
58 std::get<1>(sensorData.front()));
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000059 });
60
Szymon Dompkefdb06a12022-02-11 11:04:44 +010061 reportActions.insert(ReportAction::logToMetricReportsCollection);
62
Wludzik, Jozefe2362792020-10-27 17:23:55 +010063 deleteIface = objServer->add_unique_interface(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010064 getPath(), deleteIfaceName,
65 [this, &ioc, &reportManager](auto& dbusIface) {
Wludzik, Jozefe2362792020-10-27 17:23:55 +010066 dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
67 if (persistency)
68 {
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010069 persistency = false;
70
71 reportIface->signal_property("Persistency");
Wludzik, Jozefe2362792020-10-27 17:23:55 +010072 }
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010073
Wludzik, Jozefe2362792020-10-27 17:23:55 +010074 boost::asio::post(ioc, [this, &reportManager] {
75 reportManager.removeReport(this);
76 });
77 });
78 });
79
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020080 errorMessages = verify();
81 state.set<ReportFlags::enabled, ReportFlags::valid>(enabledIn,
82 errorMessages.empty());
83
Szymon Dompkefdb06a12022-02-11 11:04:44 +010084 reportIface = makeReportInterface(reportFactory);
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020085 persistency = storeConfiguration();
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010086
87 messanger.on_receive<messages::TriggerPresenceChangedInd>(
88 [this](const auto& msg) {
89 const auto oldSize = triggerIds.size();
90
91 if (msg.presence == messages::Presence::Exist)
92 {
93 if (utils::contains(msg.reportIds, id))
94 {
95 triggerIds.insert(msg.triggerId);
96 }
97 else if (!utils::contains(msg.reportIds, id))
98 {
99 triggerIds.erase(msg.triggerId);
100 }
101 }
102 else if (msg.presence == messages::Presence::Removed)
103 {
104 triggerIds.erase(msg.triggerId);
105 }
106
107 if (triggerIds.size() != oldSize)
108 {
Szymon Dompke1cdd7e42022-06-08 14:43:13 +0200109 reportIface->signal_property("Triggers");
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100110 }
111 });
112
113 messanger.on_receive<messages::UpdateReportInd>([this](const auto& msg) {
114 if (utils::contains(msg.reportIds, id))
115 {
116 updateReadings();
117 }
118 });
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200119}
120
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100121Report::~Report()
122{
123 if (persistency)
124 {
125 if (shouldStoreMetricValues())
126 {
127 storeConfiguration();
128 }
129 }
130 else
131 {
132 reportStorage.remove(reportFileName());
133 }
134}
135
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200136void Report::activate()
137{
138 for (auto& metric : this->metrics)
139 {
140 metric->initialize();
141 }
142
143 scheduleTimer();
144
145 if (reportIface)
146 {
147 reportIface->signal_property("errors");
148 }
149}
150
151void Report::deactivate()
152{
153 for (auto& metric : metrics)
154 {
155 metric->deinitialize();
156 }
157
158 unregisterFromMetrics = nullptr;
159 timer.cancel();
160
161 if (reportIface)
162 {
163 reportIface->signal_property("Errors");
164 }
165}
166
Szymon Dompke3eb56862021-09-20 15:32:04 +0200167uint64_t Report::getSensorCount(
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100168 const std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200169{
170 uint64_t sensorCount = 0;
171 for (auto& metric : metrics)
172 {
173 sensorCount += metric->sensorCount();
174 }
175 return sensorCount;
176}
177
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100178std::optional<uint64_t>
179 Report::deduceAppendLimit(const uint64_t appendLimitIn) const
Szymon Dompke3eb56862021-09-20 15:32:04 +0200180{
181 if (appendLimitIn == std::numeric_limits<uint64_t>::max())
182 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100183 return std::nullopt;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200184 }
185 else
186 {
187 return appendLimitIn;
188 }
189}
190
191uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
192 const ReportingType reportingTypeIn) const
193{
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100194 if (reportUpdatesIn == ReportUpdates::overwrite ||
195 reportingTypeIn == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200196 {
197 return sensorCount;
198 }
199 else
200 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100201 return appendLimit.value_or(sensorCount);
Szymon Dompke3eb56862021-09-20 15:32:04 +0200202 }
203}
204
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100205void Report::setReadingBuffer(const ReportUpdates newReportUpdates)
206{
207 if (reportingType != ReportingType::onRequest &&
208 (reportUpdates == ReportUpdates::overwrite ||
209 newReportUpdates == ReportUpdates::overwrite))
210 {
211 readingsBuffer.clearAndResize(
212 deduceBufferSize(newReportUpdates, reportingType));
213 }
214}
215
Szymon Dompke3eb56862021-09-20 15:32:04 +0200216void Report::setReportUpdates(const ReportUpdates newReportUpdates)
217{
218 if (reportUpdates != newReportUpdates)
219 {
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100220 setReadingBuffer(newReportUpdates);
Szymon Dompke3eb56862021-09-20 15:32:04 +0200221 reportUpdates = newReportUpdates;
222 }
223}
224
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100225std::unique_ptr<sdbusplus::asio::dbus_interface>
226 Report::makeReportInterface(const interfaces::ReportFactory& reportFactory)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000227{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100228 auto dbusIface =
229 objServer->add_unique_interface(getPath(), reportIfaceName);
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200230 dbusIface->register_property_rw<bool>(
231 "Enabled", sdbusplus::vtable::property_::emits_change,
232 [this](bool newVal, auto& oldValue) {
233 if (newVal != state.get<ReportFlags::enabled>())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200234 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200235 state.set<ReportFlags::enabled>(oldValue = newVal);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200236
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200237 persistency = storeConfiguration();
238 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100239 return 1;
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200240 },
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200241 [this](const auto&) { return state.get<ReportFlags::enabled>(); });
242 dbusIface->register_property_r<
243 std::vector<std::tuple<std::string, std::string>>>(
244 "ErrorMessages", sdbusplus::vtable::property_::emits_change,
245 [this](const auto&) {
246 return utils::transform(errorMessages, [](const auto& em) {
247 return std::tuple<std::string, std::string>(
248 utils::enumToString(em.error), em.arg0);
249 });
250 });
251 dbusIface->register_property_rw<uint64_t>(
252 "Interval", sdbusplus::vtable::property_::emits_change,
253 [this](uint64_t newVal, auto& oldVal) {
254 const Milliseconds newValT{newVal};
255 if (newValT < ReportManager::minInterval &&
256 newValT != Milliseconds{0})
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000257 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200258 throw sdbusplus::exception::SdBusError(
259 static_cast<int>(std::errc::invalid_argument),
260 "Invalid interval");
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000261 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200262
263 if (newValT != interval)
264 {
265 oldVal = newVal;
266 interval = newValT;
267
268 errorMessages = verify();
269 if (state.set<ReportFlags::valid>(errorMessages.empty()) ==
270 StateEvent::active)
271 {
272 scheduleTimer();
273 }
274
275 persistency = storeConfiguration();
276 }
277 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000278 },
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000279 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200280 dbusIface->register_property_rw<bool>(
281 "Persistency", sdbusplus::vtable::property_::emits_change,
282 [this](bool newVal, auto& oldVal) {
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000283 if (newVal == persistency)
284 {
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100285 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000286 }
287 if (newVal)
288 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200289 persistency = oldVal = storeConfiguration();
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000290 }
291 else
292 {
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100293 reportStorage.remove(reportFileName());
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200294 persistency = oldVal = false;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000295 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100296 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000297 },
298 [this](const auto&) { return persistency; });
299
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100300 dbusIface->register_property_r("Readings", readings,
301 sdbusplus::vtable::property_::emits_change,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000302 [this](const auto&) { return readings; });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200303 dbusIface->register_property_rw<std::string>(
304 "ReportingType", sdbusplus::vtable::property_::emits_change,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100305 [this](auto newVal, auto& oldVal) {
306 ReportingType tmp = utils::toReportingType(newVal);
307 if (tmp != reportingType)
308 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200309 reportingType = tmp;
310 oldVal = std::move(newVal);
311
312 errorMessages = verify();
313 if (state.set<ReportFlags::valid>(errorMessages.empty()) ==
314 StateEvent::active)
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100315 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200316 scheduleTimer();
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100317 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100318
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100319 persistency = storeConfiguration();
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200320
321 setReadingBuffer(reportUpdates);
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100322 }
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100323 return 1;
324 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100325 [this](const auto&) { return utils::enumToString(reportingType); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000326 dbusIface->register_property_r(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000327 "ReadingParameters", readingParametersPastVersion,
328 sdbusplus::vtable::property_::const_,
329 [this](const auto&) { return readingParametersPastVersion; });
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100330 dbusIface->register_property_rw(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000331 "ReadingParametersFutureVersion", readingParameters,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100332 sdbusplus::vtable::property_::emits_change,
333 [this, &reportFactory](auto newVal, auto& oldVal) {
Szymon Dompke32305f12022-07-05 15:37:21 +0200334 auto labeledMetricParams =
335 reportFactory.convertMetricParams(newVal);
336 ReportManager::verifyMetricParameters(labeledMetricParams);
337 reportFactory.updateMetrics(metrics,
338 state.get<ReportFlags::enabled>(),
339 labeledMetricParams);
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100340 readingParameters = toReadingParameters(
341 utils::transform(metrics, [](const auto& metric) {
342 return metric->dumpConfiguration();
343 }));
344 persistency = storeConfiguration();
345 oldVal = std::move(newVal);
346 return 1;
347 },
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000348 [this](const auto&) { return readingParameters; });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200349 dbusIface->register_property_r<bool>(
350 "EmitsReadingsUpdate", sdbusplus::vtable::property_::none,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100351 [this](const auto&) {
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100352 return reportActions.contains(ReportAction::emitsReadingsUpdate);
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100353 });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200354 dbusIface->register_property_r<std::string>(
355 "Name", sdbusplus::vtable::property_::const_,
356 [this](const auto&) { return name; });
357 dbusIface->register_property_r<bool>(
358 "LogToMetricReportsCollection", sdbusplus::vtable::property_::const_,
359 [this](const auto&) {
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100360 return reportActions.contains(
361 ReportAction::logToMetricReportsCollection);
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100362 });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200363 dbusIface->register_property_rw<std::vector<std::string>>(
364 "ReportActions", sdbusplus::vtable::property_::emits_change,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100365 [this](auto newVal, auto& oldVal) {
366 auto tmp = utils::transform<std::unordered_set>(
367 newVal, [](const auto& reportAction) {
368 return utils::toReportAction(reportAction);
369 });
370 tmp.insert(ReportAction::logToMetricReportsCollection);
371
372 if (tmp != reportActions)
373 {
374 reportActions = tmp;
375 persistency = storeConfiguration();
376 oldVal = std::move(newVal);
377 }
378 return 1;
379 },
380 [this](const auto&) {
381 return utils::transform<std::vector>(
382 reportActions, [](const auto reportAction) {
383 return utils::enumToString(reportAction);
384 });
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100385 });
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100386 dbusIface->register_property_r(
387 "AppendLimit", appendLimit.value_or(sensorCount),
388 sdbusplus::vtable::property_::emits_change,
389 [this](const auto&) { return appendLimit.value_or(sensorCount); });
Szymon Dompke3eb56862021-09-20 15:32:04 +0200390 dbusIface->register_property_rw(
391 "ReportUpdates", std::string(),
392 sdbusplus::vtable::property_::emits_change,
393 [this](auto newVal, auto& oldVal) {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100394 setReportUpdates(utils::toReportUpdates(newVal));
Szymon Dompke3eb56862021-09-20 15:32:04 +0200395 oldVal = newVal;
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100396 return 1;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200397 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100398 [this](const auto&) { return utils::enumToString(reportUpdates); });
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100399 dbusIface->register_property_r(
Szymon Dompke1cdd7e42022-06-08 14:43:13 +0200400 "Triggers", std::vector<sdbusplus::message::object_path>{},
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100401 sdbusplus::vtable::property_::emits_change, [this](const auto&) {
Szymon Dompke1cdd7e42022-06-08 14:43:13 +0200402 return utils::transform<std::vector>(
403 triggerIds, [](const auto& triggerId) {
404 return utils::pathAppend(utils::constants::triggerDirPath,
405 triggerId);
406 });
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100407 });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000408 dbusIface->register_method("Update", [this] {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100409 if (reportingType == ReportingType::onRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000410 {
411 updateReadings();
412 }
413 });
414 constexpr bool skipPropertiesChangedSignal = true;
415 dbusIface->initialize(skipPropertiesChangedSignal);
416 return dbusIface;
417}
418
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100419void Report::timerProcForPeriodicReport(boost::system::error_code ec,
420 Report& self)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200421{
422 if (ec)
423 {
424 return;
425 }
426
427 self.updateReadings();
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100428 self.scheduleTimerForPeriodicReport(self.interval);
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200429}
430
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100431void Report::timerProcForOnChangeReport(boost::system::error_code ec,
432 Report& self)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200433{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100434 if (ec)
435 {
436 return;
437 }
438
439 const auto ensure =
440 utils::Ensure{[&self] { self.onChangeContext = std::nullopt; }};
441
442 self.onChangeContext.emplace(self);
443
444 const auto steadyTimestamp = self.clock->steadyTimestamp();
445
446 for (auto& metric : self.metrics)
447 {
448 metric->updateReadings(steadyTimestamp);
449 }
450
451 self.scheduleTimerForOnChangeReport();
452}
453
454void Report::scheduleTimerForPeriodicReport(Milliseconds timerInterval)
455{
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200456 timer.expires_after(timerInterval);
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100457 timer.async_wait([this](boost::system::error_code ec) {
458 timerProcForPeriodicReport(ec, *this);
459 });
460}
461
462void Report::scheduleTimerForOnChangeReport()
463{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100464 constexpr Milliseconds timerInterval{100};
465
466 timer.expires_after(timerInterval);
467 timer.async_wait([this](boost::system::error_code ec) {
468 timerProcForOnChangeReport(ec, *this);
469 });
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200470}
471
472void Report::updateReadings()
473{
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200474 if (!state.isActive())
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000475 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200476 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000477 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200478
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100479 if (reportUpdates == ReportUpdates::overwrite ||
480 reportingType == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200481 {
482 readingsBuffer.clear();
483 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000484
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200485 for (const auto& metric : metrics)
486 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200487 if (!state.isActive())
488 {
489 break;
490 }
491
Szymon Dompke3eb56862021-09-20 15:32:04 +0200492 for (const auto& [id, metadata, value, timestamp] :
Krzysztof Grobelny9e8da542022-02-17 10:40:16 +0100493 metric->getUpdatedReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200494 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100495 if (reportUpdates == ReportUpdates::appendStopsWhenFull &&
Szymon Dompke3eb56862021-09-20 15:32:04 +0200496 readingsBuffer.isFull())
497 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200498 state.set<ReportFlags::enabled>(false);
499 reportIface->signal_property("Enabled");
Szymon Dompke3eb56862021-09-20 15:32:04 +0200500 break;
501 }
502 readingsBuffer.emplace(id, metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200503 }
504 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200505
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100506 std::get<0>(readings) =
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100507 std::chrono::duration_cast<Milliseconds>(clock->systemTimestamp())
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100508 .count();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100509
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100510 if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
511 {
512 reportIface->signal_property("Readings");
513 }
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200514}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100515
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100516bool Report::shouldStoreMetricValues() const
517{
518 return reportingType != ReportingType::onRequest &&
519 reportUpdates == ReportUpdates::appendStopsWhenFull;
520}
521
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100522bool Report::storeConfiguration() const
523{
524 try
525 {
526 nlohmann::json data;
527
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200528 data["Enabled"] = state.get<ReportFlags::enabled>();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100529 data["Version"] = reportVersion;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100530 data["Id"] = id;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100531 data["Name"] = name;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100532 data["ReportingType"] = utils::toUnderlying(reportingType);
533 data["ReportActions"] =
534 utils::transform(reportActions, [](const auto reportAction) {
535 return utils::toUnderlying(reportAction);
536 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100537 data["Interval"] = interval.count();
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100538 data["AppendLimit"] =
539 appendLimit.value_or(std::numeric_limits<uint64_t>::max());
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100540 data["ReportUpdates"] = utils::toUnderlying(reportUpdates);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000541 data["ReadingParameters"] =
542 utils::transform(metrics, [](const auto& metric) {
543 return metric->dumpConfiguration();
544 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100545
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100546 if (shouldStoreMetricValues())
547 {
548 data["MetricValues"] = utils::toLabeledReadings(readings);
549 }
550
551 reportStorage.store(reportFileName(), data);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100552 }
553 catch (const std::exception& e)
554 {
555 phosphor::logging::log<phosphor::logging::level::ERR>(
556 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100557 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100558 return false;
559 }
560
561 return true;
562}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100563
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100564interfaces::JsonStorage::FilePath Report::reportFileName() const
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100565{
566 return interfaces::JsonStorage::FilePath{
567 std::to_string(std::hash<std::string>{}(id))};
568}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100569
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100570std::unordered_set<std::string>
571 Report::collectTriggerIds(boost::asio::io_context& ioc) const
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100572{
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100573 utils::Messanger tmp(ioc);
574
575 auto result = std::unordered_set<std::string>();
576
577 tmp.on_receive<messages::CollectTriggerIdResp>(
578 [&result](const auto& msg) { result.insert(msg.triggerId); });
579
580 tmp.send(messages::CollectTriggerIdReq{id});
581
582 return result;
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100583}
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100584
585void Report::metricUpdated()
586{
587 if (onChangeContext)
588 {
589 onChangeContext->metricUpdated();
590 return;
591 }
592
593 updateReadings();
594}
595
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200596void Report::scheduleTimer()
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100597{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100598 switch (reportingType)
599 {
600 case ReportingType::periodic:
601 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200602 unregisterFromMetrics = nullptr;
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100603 scheduleTimerForPeriodicReport(interval);
604 break;
605 }
606 case ReportingType::onChange:
607 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200608 if (!unregisterFromMetrics)
609 {
610 unregisterFromMetrics = [this] {
611 for (auto& metric : metrics)
612 {
613 metric->unregisterFromUpdates(*this);
614 }
615 };
616
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100617 for (auto& metric : metrics)
618 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200619 metric->registerForUpdates(*this);
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100620 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200621 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100622
623 bool isTimerRequired = false;
624
625 for (auto& metric : metrics)
626 {
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100627 if (metric->isTimerRequired())
628 {
629 isTimerRequired = true;
630 }
631 }
632
633 if (isTimerRequired)
634 {
635 scheduleTimerForOnChangeReport();
636 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200637 else
638 {
639 timer.cancel();
640 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100641 break;
642 }
643 default:
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200644 unregisterFromMetrics = nullptr;
645 timer.cancel();
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100646 break;
647 }
648}
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200649
650std::vector<ErrorMessage> Report::verify() const
651{
652 std::vector<ErrorMessage> result;
653
654 if ((reportingType == ReportingType::periodic &&
655 interval == Milliseconds{0}) ||
656 (reportingType != ReportingType::periodic &&
657 interval != Milliseconds{0}))
658 {
659 result.emplace_back(ErrorType::propertyConflict, "Interval");
660 result.emplace_back(ErrorType::propertyConflict, "ReportingType");
661 }
662
663 return result;
664}