blob: c2813c0c2a162fb224bf894735b71236dd024ade [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,
24 const bool enabledIn) :
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),
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020033 reportStorage(reportStorageIn), enabled(enabledIn)
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 }
164 return true;
165 },
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 }
179 return true;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000180 }
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200181 return false;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000182 },
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000183 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000184 dbusIface->register_property_rw(
185 "Persistency", persistency, sdbusplus::vtable::property_::emits_change,
186 [this](bool newVal, const auto&) {
187 if (newVal == persistency)
188 {
189 return true;
190 }
191 if (newVal)
192 {
193 persistency = storeConfiguration();
194 }
195 else
196 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100197 reportStorage.remove(fileName());
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000198 persistency = false;
199 }
200 return true;
201 },
202 [this](const auto&) { return persistency; });
203
204 auto readingsFlag = sdbusplus::vtable::property_::none;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100205 if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000206 {
207 readingsFlag = sdbusplus::vtable::property_::emits_change;
208 }
209 dbusIface->register_property_r("Readings", readings, readingsFlag,
210 [this](const auto&) { return readings; });
211 dbusIface->register_property_r(
Szymon Dompke3eb56862021-09-20 15:32:04 +0200212 "ReportingType", std::string(), sdbusplus::vtable::property_::const_,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100213 [this](const auto&) { return utils::enumToString(reportingType); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000214 dbusIface->register_property_r(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000215 "ReadingParameters", readingParametersPastVersion,
216 sdbusplus::vtable::property_::const_,
217 [this](const auto&) { return readingParametersPastVersion; });
218 dbusIface->register_property_r(
219 "ReadingParametersFutureVersion", readingParameters,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000220 sdbusplus::vtable::property_::const_,
221 [this](const auto&) { return readingParameters; });
222 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100223 "EmitsReadingsUpdate", bool{}, sdbusplus::vtable::property_::const_,
224 [this](const auto&) {
225 return utils::contains(reportActions,
226 ReportAction::emitsReadingsUpdate);
227 });
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100228 dbusIface->register_property_r("Name", std::string{},
229 sdbusplus::vtable::property_::const_,
230 [this](const auto&) { return name; });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000231 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100232 "LogToMetricReportsCollection", bool{},
233 sdbusplus::vtable::property_::const_, [this](const auto&) {
234 return utils::contains(reportActions,
235 ReportAction::logToMetricReportsCollection);
236 });
237 dbusIface->register_property_r(
238 "ReportActions", std::vector<std::string>{},
239 sdbusplus::vtable::property_::const_, [this](const auto&) {
240 return utils::transform(reportActions, [](const auto reportAction) {
241 return utils::enumToString(reportAction);
242 });
243 });
Szymon Dompke3eb56862021-09-20 15:32:04 +0200244 dbusIface->register_property_r("AppendLimit", appendLimit,
245 sdbusplus::vtable::property_::emits_change,
246 [this](const auto&) { return appendLimit; });
247 dbusIface->register_property_rw(
248 "ReportUpdates", std::string(),
249 sdbusplus::vtable::property_::emits_change,
250 [this](auto newVal, auto& oldVal) {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100251 ReportManager::verifyReportUpdates(utils::toReportUpdates(newVal));
252 setReportUpdates(utils::toReportUpdates(newVal));
Szymon Dompke3eb56862021-09-20 15:32:04 +0200253 oldVal = newVal;
254 return true;
255 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100256 [this](const auto&) { return utils::enumToString(reportUpdates); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000257 dbusIface->register_method("Update", [this] {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100258 if (reportingType == ReportingType::onRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000259 {
260 updateReadings();
261 }
262 });
263 constexpr bool skipPropertiesChangedSignal = true;
264 dbusIface->initialize(skipPropertiesChangedSignal);
265 return dbusIface;
266}
267
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200268void Report::timerProc(boost::system::error_code ec, Report& self)
269{
270 if (ec)
271 {
272 return;
273 }
274
275 self.updateReadings();
276 self.scheduleTimer(self.interval);
277}
278
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000279void Report::scheduleTimer(Milliseconds timerInterval)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200280{
281 timer.expires_after(timerInterval);
282 timer.async_wait(
283 [this](boost::system::error_code ec) { timerProc(ec, *this); });
284}
285
286void Report::updateReadings()
287{
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200288 if (!enabled)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000289 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200290 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000291 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200292
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100293 if (reportUpdates == ReportUpdates::overwrite ||
294 reportingType == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200295 {
296 readingsBuffer.clear();
297 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000298
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200299 for (const auto& metric : metrics)
300 {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200301 for (const auto& [id, metadata, value, timestamp] :
302 metric->getReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200303 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100304 if (reportUpdates == ReportUpdates::appendStopsWhenFull &&
Szymon Dompke3eb56862021-09-20 15:32:04 +0200305 readingsBuffer.isFull())
306 {
307 enabled = false;
308 for (auto& metric : metrics)
309 {
310 metric->deinitialize();
311 }
312 break;
313 }
314 readingsBuffer.emplace(id, metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200315 }
316 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200317
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100318 readings = {
319 Clock().timestamp(),
320 std::vector<ReadingData>(readingsBuffer.begin(), readingsBuffer.end())};
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100321
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200322 reportIface->signal_property("Readings");
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200323}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100324
325bool Report::storeConfiguration() const
326{
327 try
328 {
329 nlohmann::json data;
330
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200331 data["Enabled"] = enabled;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100332 data["Version"] = reportVersion;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100333 data["Id"] = id;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100334 data["Name"] = name;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100335 data["ReportingType"] = utils::toUnderlying(reportingType);
336 data["ReportActions"] =
337 utils::transform(reportActions, [](const auto reportAction) {
338 return utils::toUnderlying(reportAction);
339 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100340 data["Interval"] = interval.count();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200341 data["AppendLimit"] = appendLimit;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100342 data["ReportUpdates"] = utils::toUnderlying(reportUpdates);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000343 data["ReadingParameters"] =
344 utils::transform(metrics, [](const auto& metric) {
345 return metric->dumpConfiguration();
346 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100347
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100348 reportStorage.store(fileName(), data);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100349 }
350 catch (const std::exception& e)
351 {
352 phosphor::logging::log<phosphor::logging::level::ERR>(
353 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100354 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100355 return false;
356 }
357
358 return true;
359}
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100360interfaces::JsonStorage::FilePath Report::fileName() const
361{
362 return interfaces::JsonStorage::FilePath{
363 std::to_string(std::hash<std::string>{}(id))};
364}