blob: 170f50d37d6506d70a9d529ee58088444b63fb30 [file] [log] [blame]
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02001#include "report.hpp"
2
3#include "report_manager.hpp"
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01004#include "utils/clock.hpp"
Krzysztof Grobelny51497a02021-11-09 14:56:22 +01005#include "utils/contains.hpp"
Wludzik, Jozefe2362792020-10-27 17:23:55 +01006#include "utils/transform.hpp"
7
8#include <phosphor-logging/log.hpp>
Wludzik, Jozefb1ff1f62020-10-23 13:20:52 +02009#include <sdbusplus/vtable.hpp>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020010
Szymon Dompke3eb56862021-09-20 15:32:04 +020011#include <limits>
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020012#include <numeric>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020013
14Report::Report(boost::asio::io_context& ioc,
15 const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010016 const std::string& reportId, const std::string& reportName,
Szymon Dompke3eb56862021-09-20 15:32:04 +020017 const ReportingType reportingTypeIn,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010018 std::vector<ReportAction> reportActionsIn,
Szymon Dompke3eb56862021-09-20 15:32:04 +020019 const Milliseconds intervalIn, const uint64_t appendLimitIn,
20 const ReportUpdates reportUpdatesIn,
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020021 interfaces::ReportManager& reportManager,
Wludzik, Jozefe2362792020-10-27 17:23:55 +010022 interfaces::JsonStorage& reportStorageIn,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020023 std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010024 const bool enabledIn, std::unique_ptr<interfaces::Clock> clock) :
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010025 id(reportId),
26 name(reportName), reportingType(reportingTypeIn), interval(intervalIn),
27 reportActions(std::move(reportActionsIn)),
Szymon Dompke3eb56862021-09-20 15:32:04 +020028 sensorCount(getSensorCount(metricsIn)),
29 appendLimit(deduceAppendLimit(appendLimitIn)),
30 reportUpdates(reportUpdatesIn),
31 readingsBuffer(deduceBufferSize(reportUpdates, reportingType)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000032 objServer(objServer), metrics(std::move(metricsIn)), timer(ioc),
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010033 reportStorage(reportStorageIn), enabled(enabledIn), clock(std::move(clock))
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020034{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000035 readingParameters =
36 toReadingParameters(utils::transform(metrics, [](const auto& metric) {
37 return metric->dumpConfiguration();
38 }));
39
40 readingParametersPastVersion =
41 utils::transform(readingParameters, [](const auto& item) {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010042 const auto& [sensorData, operationType, id, collectionTimeScope,
43 collectionDuration] = item;
44
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000045 return ReadingParametersPastVersion::value_type(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010046 std::get<0>(sensorData.front()), operationType, id,
47 std::get<1>(sensorData.front()));
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000048 });
49
Wludzik, Jozefe2362792020-10-27 17:23:55 +010050 deleteIface = objServer->add_unique_interface(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010051 getPath(), deleteIfaceName,
52 [this, &ioc, &reportManager](auto& dbusIface) {
Wludzik, Jozefe2362792020-10-27 17:23:55 +010053 dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
54 if (persistency)
55 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010056 reportStorage.remove(fileName());
Wludzik, Jozefe2362792020-10-27 17:23:55 +010057 }
58 boost::asio::post(ioc, [this, &reportManager] {
59 reportManager.removeReport(this);
60 });
61 });
62 });
63
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +000064 persistency = storeConfiguration();
65 reportIface = makeReportInterface();
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020066
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010067 if (reportingType == ReportingType::periodic)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020068 {
69 scheduleTimer(interval);
70 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000071
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020072 if (enabled)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000073 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020074 for (auto& metric : this->metrics)
75 {
76 metric->initialize();
77 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000078 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020079}
80
Szymon Dompke3eb56862021-09-20 15:32:04 +020081uint64_t Report::getSensorCount(
82 std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
83{
84 uint64_t sensorCount = 0;
85 for (auto& metric : metrics)
86 {
87 sensorCount += metric->sensorCount();
88 }
89 return sensorCount;
90}
91
92uint64_t Report::deduceAppendLimit(const uint64_t appendLimitIn) const
93{
94 if (appendLimitIn == std::numeric_limits<uint64_t>::max())
95 {
96 return sensorCount;
97 }
98 else
99 {
100 return appendLimitIn;
101 }
102}
103
104uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
105 const ReportingType reportingTypeIn) const
106{
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100107 if (reportUpdatesIn == ReportUpdates::overwrite ||
108 reportingTypeIn == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200109 {
110 return sensorCount;
111 }
112 else
113 {
114 return appendLimit;
115 }
116}
117
118void Report::setReportUpdates(const ReportUpdates newReportUpdates)
119{
120 if (reportUpdates != newReportUpdates)
121 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100122 if (reportingType != ReportingType::onRequest &&
123 (reportUpdates == ReportUpdates::overwrite ||
124 newReportUpdates == ReportUpdates::overwrite))
Szymon Dompke3eb56862021-09-20 15:32:04 +0200125 {
126 readingsBuffer.clearAndResize(
127 deduceBufferSize(newReportUpdates, reportingType));
128 }
129 reportUpdates = newReportUpdates;
130 }
131}
132
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000133std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
134{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100135 auto dbusIface =
136 objServer->add_unique_interface(getPath(), reportIfaceName);
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000137 dbusIface->register_property_rw(
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200138 "Enabled", enabled, sdbusplus::vtable::property_::emits_change,
139 [this](bool newVal, const auto&) {
140 if (newVal != enabled)
141 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100142 if (true == newVal && ReportingType::periodic == reportingType)
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200143 {
144 scheduleTimer(interval);
145 }
146 if (newVal)
147 {
148 for (auto& metric : metrics)
149 {
150 metric->initialize();
151 }
152 }
153 else
154 {
155 for (auto& metric : metrics)
156 {
157 metric->deinitialize();
158 }
159 }
160
161 enabled = newVal;
162 persistency = storeConfiguration();
163 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100164 return 1;
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200165 },
166 [this](const auto&) { return enabled; });
167 dbusIface->register_property_rw(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000168 "Interval", interval.count(),
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000169 sdbusplus::vtable::property_::emits_change,
170 [this](uint64_t newVal, auto&) {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200171 if (Milliseconds newValT{newVal};
172 newValT >= ReportManager::minInterval)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000173 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200174 if (newValT != interval)
175 {
176 interval = newValT;
177 persistency = storeConfiguration();
178 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100179 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000180 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100181 throw sdbusplus::exception::SdBusError(
182 static_cast<int>(std::errc::invalid_argument),
183 "Invalid interval");
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000184 },
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000185 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000186 dbusIface->register_property_rw(
187 "Persistency", persistency, sdbusplus::vtable::property_::emits_change,
188 [this](bool newVal, const auto&) {
189 if (newVal == persistency)
190 {
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100191 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000192 }
193 if (newVal)
194 {
195 persistency = storeConfiguration();
196 }
197 else
198 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100199 reportStorage.remove(fileName());
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000200 persistency = false;
201 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100202 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000203 },
204 [this](const auto&) { return persistency; });
205
206 auto readingsFlag = sdbusplus::vtable::property_::none;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100207 if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000208 {
209 readingsFlag = sdbusplus::vtable::property_::emits_change;
210 }
211 dbusIface->register_property_r("Readings", readings, readingsFlag,
212 [this](const auto&) { return readings; });
213 dbusIface->register_property_r(
Szymon Dompke3eb56862021-09-20 15:32:04 +0200214 "ReportingType", std::string(), sdbusplus::vtable::property_::const_,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100215 [this](const auto&) { return utils::enumToString(reportingType); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000216 dbusIface->register_property_r(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000217 "ReadingParameters", readingParametersPastVersion,
218 sdbusplus::vtable::property_::const_,
219 [this](const auto&) { return readingParametersPastVersion; });
220 dbusIface->register_property_r(
221 "ReadingParametersFutureVersion", readingParameters,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000222 sdbusplus::vtable::property_::const_,
223 [this](const auto&) { return readingParameters; });
224 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100225 "EmitsReadingsUpdate", bool{}, sdbusplus::vtable::property_::const_,
226 [this](const auto&) {
227 return utils::contains(reportActions,
228 ReportAction::emitsReadingsUpdate);
229 });
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100230 dbusIface->register_property_r("Name", std::string{},
231 sdbusplus::vtable::property_::const_,
232 [this](const auto&) { return name; });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000233 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100234 "LogToMetricReportsCollection", bool{},
235 sdbusplus::vtable::property_::const_, [this](const auto&) {
236 return utils::contains(reportActions,
237 ReportAction::logToMetricReportsCollection);
238 });
239 dbusIface->register_property_r(
240 "ReportActions", std::vector<std::string>{},
241 sdbusplus::vtable::property_::const_, [this](const auto&) {
242 return utils::transform(reportActions, [](const auto reportAction) {
243 return utils::enumToString(reportAction);
244 });
245 });
Szymon Dompke3eb56862021-09-20 15:32:04 +0200246 dbusIface->register_property_r("AppendLimit", appendLimit,
247 sdbusplus::vtable::property_::emits_change,
248 [this](const auto&) { return appendLimit; });
249 dbusIface->register_property_rw(
250 "ReportUpdates", std::string(),
251 sdbusplus::vtable::property_::emits_change,
252 [this](auto newVal, auto& oldVal) {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100253 ReportManager::verifyReportUpdates(utils::toReportUpdates(newVal));
254 setReportUpdates(utils::toReportUpdates(newVal));
Szymon Dompke3eb56862021-09-20 15:32:04 +0200255 oldVal = newVal;
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100256 return 1;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200257 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100258 [this](const auto&) { return utils::enumToString(reportUpdates); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000259 dbusIface->register_method("Update", [this] {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100260 if (reportingType == ReportingType::onRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000261 {
262 updateReadings();
263 }
264 });
265 constexpr bool skipPropertiesChangedSignal = true;
266 dbusIface->initialize(skipPropertiesChangedSignal);
267 return dbusIface;
268}
269
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200270void Report::timerProc(boost::system::error_code ec, Report& self)
271{
272 if (ec)
273 {
274 return;
275 }
276
277 self.updateReadings();
278 self.scheduleTimer(self.interval);
279}
280
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000281void Report::scheduleTimer(Milliseconds timerInterval)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200282{
283 timer.expires_after(timerInterval);
284 timer.async_wait(
285 [this](boost::system::error_code ec) { timerProc(ec, *this); });
286}
287
288void Report::updateReadings()
289{
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200290 if (!enabled)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000291 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200292 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000293 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200294
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100295 if (reportUpdates == ReportUpdates::overwrite ||
296 reportingType == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200297 {
298 readingsBuffer.clear();
299 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000300
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200301 for (const auto& metric : metrics)
302 {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200303 for (const auto& [id, metadata, value, timestamp] :
304 metric->getReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200305 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100306 if (reportUpdates == ReportUpdates::appendStopsWhenFull &&
Szymon Dompke3eb56862021-09-20 15:32:04 +0200307 readingsBuffer.isFull())
308 {
309 enabled = false;
310 for (auto& metric : metrics)
311 {
312 metric->deinitialize();
313 }
314 break;
315 }
316 readingsBuffer.emplace(id, metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200317 }
318 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200319
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100320 readings = {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100321 std::chrono::duration_cast<Milliseconds>(clock->systemTimestamp())
322 .count(),
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100323 std::vector<ReadingData>(readingsBuffer.begin(), readingsBuffer.end())};
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100324
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200325 reportIface->signal_property("Readings");
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200326}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100327
328bool Report::storeConfiguration() const
329{
330 try
331 {
332 nlohmann::json data;
333
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200334 data["Enabled"] = enabled;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100335 data["Version"] = reportVersion;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100336 data["Id"] = id;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100337 data["Name"] = name;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100338 data["ReportingType"] = utils::toUnderlying(reportingType);
339 data["ReportActions"] =
340 utils::transform(reportActions, [](const auto reportAction) {
341 return utils::toUnderlying(reportAction);
342 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100343 data["Interval"] = interval.count();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200344 data["AppendLimit"] = appendLimit;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100345 data["ReportUpdates"] = utils::toUnderlying(reportUpdates);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000346 data["ReadingParameters"] =
347 utils::transform(metrics, [](const auto& metric) {
348 return metric->dumpConfiguration();
349 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100350
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100351 reportStorage.store(fileName(), data);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100352 }
353 catch (const std::exception& e)
354 {
355 phosphor::logging::log<phosphor::logging::level::ERR>(
356 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100357 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100358 return false;
359 }
360
361 return true;
362}
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100363interfaces::JsonStorage::FilePath Report::fileName() const
364{
365 return interfaces::JsonStorage::FilePath{
366 std::to_string(std::hash<std::string>{}(id))};
367}