blob: 8acf1827074c99fc84eb871724fad9cdddbc3f3a [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
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +010092std::optional<uint64_t>
93 Report::deduceAppendLimit(const uint64_t appendLimitIn) const
Szymon Dompke3eb56862021-09-20 15:32:04 +020094{
95 if (appendLimitIn == std::numeric_limits<uint64_t>::max())
96 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +010097 return std::nullopt;
Szymon Dompke3eb56862021-09-20 15:32:04 +020098 }
99 else
100 {
101 return appendLimitIn;
102 }
103}
104
105uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
106 const ReportingType reportingTypeIn) const
107{
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100108 if (reportUpdatesIn == ReportUpdates::overwrite ||
109 reportingTypeIn == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200110 {
111 return sensorCount;
112 }
113 else
114 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100115 return appendLimit.value_or(sensorCount);
Szymon Dompke3eb56862021-09-20 15:32:04 +0200116 }
117}
118
119void Report::setReportUpdates(const ReportUpdates newReportUpdates)
120{
121 if (reportUpdates != newReportUpdates)
122 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100123 if (reportingType != ReportingType::onRequest &&
124 (reportUpdates == ReportUpdates::overwrite ||
125 newReportUpdates == ReportUpdates::overwrite))
Szymon Dompke3eb56862021-09-20 15:32:04 +0200126 {
127 readingsBuffer.clearAndResize(
128 deduceBufferSize(newReportUpdates, reportingType));
129 }
130 reportUpdates = newReportUpdates;
131 }
132}
133
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000134std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
135{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100136 auto dbusIface =
137 objServer->add_unique_interface(getPath(), reportIfaceName);
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000138 dbusIface->register_property_rw(
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200139 "Enabled", enabled, sdbusplus::vtable::property_::emits_change,
140 [this](bool newVal, const auto&) {
141 if (newVal != enabled)
142 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100143 if (true == newVal && ReportingType::periodic == reportingType)
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200144 {
145 scheduleTimer(interval);
146 }
147 if (newVal)
148 {
149 for (auto& metric : metrics)
150 {
151 metric->initialize();
152 }
153 }
154 else
155 {
156 for (auto& metric : metrics)
157 {
158 metric->deinitialize();
159 }
160 }
161
162 enabled = newVal;
163 persistency = storeConfiguration();
164 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100165 return 1;
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200166 },
167 [this](const auto&) { return enabled; });
168 dbusIface->register_property_rw(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000169 "Interval", interval.count(),
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000170 sdbusplus::vtable::property_::emits_change,
171 [this](uint64_t newVal, auto&) {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200172 if (Milliseconds newValT{newVal};
173 newValT >= ReportManager::minInterval)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000174 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200175 if (newValT != interval)
176 {
177 interval = newValT;
178 persistency = storeConfiguration();
179 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100180 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000181 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100182 throw sdbusplus::exception::SdBusError(
183 static_cast<int>(std::errc::invalid_argument),
184 "Invalid interval");
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000185 },
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000186 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000187 dbusIface->register_property_rw(
188 "Persistency", persistency, sdbusplus::vtable::property_::emits_change,
189 [this](bool newVal, const auto&) {
190 if (newVal == persistency)
191 {
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100192 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000193 }
194 if (newVal)
195 {
196 persistency = storeConfiguration();
197 }
198 else
199 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100200 reportStorage.remove(fileName());
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000201 persistency = false;
202 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100203 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000204 },
205 [this](const auto&) { return persistency; });
206
207 auto readingsFlag = sdbusplus::vtable::property_::none;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100208 if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000209 {
210 readingsFlag = sdbusplus::vtable::property_::emits_change;
211 }
212 dbusIface->register_property_r("Readings", readings, readingsFlag,
213 [this](const auto&) { return readings; });
214 dbusIface->register_property_r(
Szymon Dompke3eb56862021-09-20 15:32:04 +0200215 "ReportingType", std::string(), sdbusplus::vtable::property_::const_,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100216 [this](const auto&) { return utils::enumToString(reportingType); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000217 dbusIface->register_property_r(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000218 "ReadingParameters", readingParametersPastVersion,
219 sdbusplus::vtable::property_::const_,
220 [this](const auto&) { return readingParametersPastVersion; });
221 dbusIface->register_property_r(
222 "ReadingParametersFutureVersion", readingParameters,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000223 sdbusplus::vtable::property_::const_,
224 [this](const auto&) { return readingParameters; });
225 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100226 "EmitsReadingsUpdate", bool{}, sdbusplus::vtable::property_::const_,
227 [this](const auto&) {
228 return utils::contains(reportActions,
229 ReportAction::emitsReadingsUpdate);
230 });
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100231 dbusIface->register_property_r("Name", std::string{},
232 sdbusplus::vtable::property_::const_,
233 [this](const auto&) { return name; });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000234 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100235 "LogToMetricReportsCollection", bool{},
236 sdbusplus::vtable::property_::const_, [this](const auto&) {
237 return utils::contains(reportActions,
238 ReportAction::logToMetricReportsCollection);
239 });
240 dbusIface->register_property_r(
241 "ReportActions", std::vector<std::string>{},
242 sdbusplus::vtable::property_::const_, [this](const auto&) {
243 return utils::transform(reportActions, [](const auto reportAction) {
244 return utils::enumToString(reportAction);
245 });
246 });
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100247 dbusIface->register_property_r(
248 "AppendLimit", appendLimit.value_or(sensorCount),
249 sdbusplus::vtable::property_::emits_change,
250 [this](const auto&) { return appendLimit.value_or(sensorCount); });
Szymon Dompke3eb56862021-09-20 15:32:04 +0200251 dbusIface->register_property_rw(
252 "ReportUpdates", std::string(),
253 sdbusplus::vtable::property_::emits_change,
254 [this](auto newVal, auto& oldVal) {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100255 setReportUpdates(utils::toReportUpdates(newVal));
Szymon Dompke3eb56862021-09-20 15:32:04 +0200256 oldVal = newVal;
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100257 return 1;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200258 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100259 [this](const auto&) { return utils::enumToString(reportUpdates); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000260 dbusIface->register_method("Update", [this] {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100261 if (reportingType == ReportingType::onRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000262 {
263 updateReadings();
264 }
265 });
266 constexpr bool skipPropertiesChangedSignal = true;
267 dbusIface->initialize(skipPropertiesChangedSignal);
268 return dbusIface;
269}
270
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200271void Report::timerProc(boost::system::error_code ec, Report& self)
272{
273 if (ec)
274 {
275 return;
276 }
277
278 self.updateReadings();
279 self.scheduleTimer(self.interval);
280}
281
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000282void Report::scheduleTimer(Milliseconds timerInterval)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200283{
284 timer.expires_after(timerInterval);
285 timer.async_wait(
286 [this](boost::system::error_code ec) { timerProc(ec, *this); });
287}
288
289void Report::updateReadings()
290{
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200291 if (!enabled)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000292 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200293 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000294 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200295
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100296 if (reportUpdates == ReportUpdates::overwrite ||
297 reportingType == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200298 {
299 readingsBuffer.clear();
300 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000301
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200302 for (const auto& metric : metrics)
303 {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200304 for (const auto& [id, metadata, value, timestamp] :
305 metric->getReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200306 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100307 if (reportUpdates == ReportUpdates::appendStopsWhenFull &&
Szymon Dompke3eb56862021-09-20 15:32:04 +0200308 readingsBuffer.isFull())
309 {
310 enabled = false;
Krzysztof Grobelnyfbeb5bf2022-01-03 09:41:29 +0100311 for (auto& m : metrics)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200312 {
Krzysztof Grobelnyfbeb5bf2022-01-03 09:41:29 +0100313 m->deinitialize();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200314 }
315 break;
316 }
317 readingsBuffer.emplace(id, metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200318 }
319 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200320
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100321 readings = {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100322 std::chrono::duration_cast<Milliseconds>(clock->systemTimestamp())
323 .count(),
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100324 std::vector<ReadingData>(readingsBuffer.begin(), readingsBuffer.end())};
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100325
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200326 reportIface->signal_property("Readings");
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200327}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100328
329bool Report::storeConfiguration() const
330{
331 try
332 {
333 nlohmann::json data;
334
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200335 data["Enabled"] = enabled;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100336 data["Version"] = reportVersion;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100337 data["Id"] = id;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100338 data["Name"] = name;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100339 data["ReportingType"] = utils::toUnderlying(reportingType);
340 data["ReportActions"] =
341 utils::transform(reportActions, [](const auto reportAction) {
342 return utils::toUnderlying(reportAction);
343 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100344 data["Interval"] = interval.count();
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100345 data["AppendLimit"] =
346 appendLimit.value_or(std::numeric_limits<uint64_t>::max());
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100347 data["ReportUpdates"] = utils::toUnderlying(reportUpdates);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000348 data["ReadingParameters"] =
349 utils::transform(metrics, [](const auto& metric) {
350 return metric->dumpConfiguration();
351 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100352
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100353 reportStorage.store(fileName(), data);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100354 }
355 catch (const std::exception& e)
356 {
357 phosphor::logging::log<phosphor::logging::level::ERR>(
358 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100359 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100360 return false;
361 }
362
363 return true;
364}
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100365interfaces::JsonStorage::FilePath Report::fileName() const
366{
367 return interfaces::JsonStorage::FilePath{
368 std::to_string(std::hash<std::string>{}(id))};
369}