blob: 6439d41c4b8687bef3cc435c5220e60ae346612d [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,
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +010024 const bool enabledIn, std::unique_ptr<interfaces::Clock> clock,
25 const std::vector<std::string>& triggerIdsIn) :
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010026 id(reportId),
27 name(reportName), reportingType(reportingTypeIn), interval(intervalIn),
28 reportActions(std::move(reportActionsIn)),
Szymon Dompke3eb56862021-09-20 15:32:04 +020029 sensorCount(getSensorCount(metricsIn)),
30 appendLimit(deduceAppendLimit(appendLimitIn)),
31 reportUpdates(reportUpdatesIn),
32 readingsBuffer(deduceBufferSize(reportUpdates, reportingType)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000033 objServer(objServer), metrics(std::move(metricsIn)), timer(ioc),
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +010034 triggerIds(triggerIdsIn.begin(), triggerIdsIn.end()),
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +010035 reportStorage(reportStorageIn), enabled(enabledIn), clock(std::move(clock))
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020036{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000037 readingParameters =
38 toReadingParameters(utils::transform(metrics, [](const auto& metric) {
39 return metric->dumpConfiguration();
40 }));
41
42 readingParametersPastVersion =
43 utils::transform(readingParameters, [](const auto& item) {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010044 const auto& [sensorData, operationType, id, collectionTimeScope,
45 collectionDuration] = item;
46
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000047 return ReadingParametersPastVersion::value_type(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010048 std::get<0>(sensorData.front()), operationType, id,
49 std::get<1>(sensorData.front()));
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000050 });
51
Wludzik, Jozefe2362792020-10-27 17:23:55 +010052 deleteIface = objServer->add_unique_interface(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010053 getPath(), deleteIfaceName,
54 [this, &ioc, &reportManager](auto& dbusIface) {
Wludzik, Jozefe2362792020-10-27 17:23:55 +010055 dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
56 if (persistency)
57 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010058 reportStorage.remove(fileName());
Wludzik, Jozefe2362792020-10-27 17:23:55 +010059 }
60 boost::asio::post(ioc, [this, &reportManager] {
61 reportManager.removeReport(this);
62 });
63 });
64 });
65
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +000066 persistency = storeConfiguration();
67 reportIface = makeReportInterface();
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020068
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010069 if (reportingType == ReportingType::periodic)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020070 {
71 scheduleTimer(interval);
72 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000073
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020074 if (enabled)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000075 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020076 for (auto& metric : this->metrics)
77 {
78 metric->initialize();
79 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000080 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020081}
82
Szymon Dompke3eb56862021-09-20 15:32:04 +020083uint64_t Report::getSensorCount(
84 std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
85{
86 uint64_t sensorCount = 0;
87 for (auto& metric : metrics)
88 {
89 sensorCount += metric->sensorCount();
90 }
91 return sensorCount;
92}
93
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +010094std::optional<uint64_t>
95 Report::deduceAppendLimit(const uint64_t appendLimitIn) const
Szymon Dompke3eb56862021-09-20 15:32:04 +020096{
97 if (appendLimitIn == std::numeric_limits<uint64_t>::max())
98 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +010099 return std::nullopt;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200100 }
101 else
102 {
103 return appendLimitIn;
104 }
105}
106
107uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
108 const ReportingType reportingTypeIn) const
109{
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100110 if (reportUpdatesIn == ReportUpdates::overwrite ||
111 reportingTypeIn == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200112 {
113 return sensorCount;
114 }
115 else
116 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100117 return appendLimit.value_or(sensorCount);
Szymon Dompke3eb56862021-09-20 15:32:04 +0200118 }
119}
120
121void Report::setReportUpdates(const ReportUpdates newReportUpdates)
122{
123 if (reportUpdates != newReportUpdates)
124 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100125 if (reportingType != ReportingType::onRequest &&
126 (reportUpdates == ReportUpdates::overwrite ||
127 newReportUpdates == ReportUpdates::overwrite))
Szymon Dompke3eb56862021-09-20 15:32:04 +0200128 {
129 readingsBuffer.clearAndResize(
130 deduceBufferSize(newReportUpdates, reportingType));
131 }
132 reportUpdates = newReportUpdates;
133 }
134}
135
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000136std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
137{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100138 auto dbusIface =
139 objServer->add_unique_interface(getPath(), reportIfaceName);
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000140 dbusIface->register_property_rw(
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200141 "Enabled", enabled, sdbusplus::vtable::property_::emits_change,
142 [this](bool newVal, const auto&) {
143 if (newVal != enabled)
144 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100145 if (true == newVal && ReportingType::periodic == reportingType)
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200146 {
147 scheduleTimer(interval);
148 }
149 if (newVal)
150 {
151 for (auto& metric : metrics)
152 {
153 metric->initialize();
154 }
155 }
156 else
157 {
158 for (auto& metric : metrics)
159 {
160 metric->deinitialize();
161 }
162 }
163
164 enabled = newVal;
165 persistency = storeConfiguration();
166 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100167 return 1;
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200168 },
169 [this](const auto&) { return enabled; });
170 dbusIface->register_property_rw(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000171 "Interval", interval.count(),
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000172 sdbusplus::vtable::property_::emits_change,
173 [this](uint64_t newVal, auto&) {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200174 if (Milliseconds newValT{newVal};
175 newValT >= ReportManager::minInterval)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000176 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200177 if (newValT != interval)
178 {
179 interval = newValT;
180 persistency = storeConfiguration();
181 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100182 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000183 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100184 throw sdbusplus::exception::SdBusError(
185 static_cast<int>(std::errc::invalid_argument),
186 "Invalid interval");
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000187 },
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000188 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000189 dbusIface->register_property_rw(
190 "Persistency", persistency, sdbusplus::vtable::property_::emits_change,
191 [this](bool newVal, const auto&) {
192 if (newVal == persistency)
193 {
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100194 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000195 }
196 if (newVal)
197 {
198 persistency = storeConfiguration();
199 }
200 else
201 {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100202 reportStorage.remove(fileName());
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000203 persistency = false;
204 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100205 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000206 },
207 [this](const auto&) { return persistency; });
208
209 auto readingsFlag = sdbusplus::vtable::property_::none;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100210 if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000211 {
212 readingsFlag = sdbusplus::vtable::property_::emits_change;
213 }
214 dbusIface->register_property_r("Readings", readings, readingsFlag,
215 [this](const auto&) { return readings; });
216 dbusIface->register_property_r(
Szymon Dompke3eb56862021-09-20 15:32:04 +0200217 "ReportingType", std::string(), sdbusplus::vtable::property_::const_,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100218 [this](const auto&) { return utils::enumToString(reportingType); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000219 dbusIface->register_property_r(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000220 "ReadingParameters", readingParametersPastVersion,
221 sdbusplus::vtable::property_::const_,
222 [this](const auto&) { return readingParametersPastVersion; });
223 dbusIface->register_property_r(
224 "ReadingParametersFutureVersion", readingParameters,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000225 sdbusplus::vtable::property_::const_,
226 [this](const auto&) { return readingParameters; });
227 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100228 "EmitsReadingsUpdate", bool{}, sdbusplus::vtable::property_::const_,
229 [this](const auto&) {
230 return utils::contains(reportActions,
231 ReportAction::emitsReadingsUpdate);
232 });
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100233 dbusIface->register_property_r("Name", std::string{},
234 sdbusplus::vtable::property_::const_,
235 [this](const auto&) { return name; });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000236 dbusIface->register_property_r(
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100237 "LogToMetricReportsCollection", bool{},
238 sdbusplus::vtable::property_::const_, [this](const auto&) {
239 return utils::contains(reportActions,
240 ReportAction::logToMetricReportsCollection);
241 });
242 dbusIface->register_property_r(
243 "ReportActions", std::vector<std::string>{},
244 sdbusplus::vtable::property_::const_, [this](const auto&) {
245 return utils::transform(reportActions, [](const auto reportAction) {
246 return utils::enumToString(reportAction);
247 });
248 });
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100249 dbusIface->register_property_r(
250 "AppendLimit", appendLimit.value_or(sensorCount),
251 sdbusplus::vtable::property_::emits_change,
252 [this](const auto&) { return appendLimit.value_or(sensorCount); });
Szymon Dompke3eb56862021-09-20 15:32:04 +0200253 dbusIface->register_property_rw(
254 "ReportUpdates", std::string(),
255 sdbusplus::vtable::property_::emits_change,
256 [this](auto newVal, auto& oldVal) {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100257 setReportUpdates(utils::toReportUpdates(newVal));
Szymon Dompke3eb56862021-09-20 15:32:04 +0200258 oldVal = newVal;
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100259 return 1;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200260 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100261 [this](const auto&) { return utils::enumToString(reportUpdates); });
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100262 dbusIface->register_property_r(
263 "TriggerIds", std::vector<std::string>{},
264 sdbusplus::vtable::property_::emits_change, [this](const auto&) {
265 return std::vector<std::string>(triggerIds.begin(),
266 triggerIds.end());
267 });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000268 dbusIface->register_method("Update", [this] {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100269 if (reportingType == ReportingType::onRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000270 {
271 updateReadings();
272 }
273 });
274 constexpr bool skipPropertiesChangedSignal = true;
275 dbusIface->initialize(skipPropertiesChangedSignal);
276 return dbusIface;
277}
278
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200279void Report::timerProc(boost::system::error_code ec, Report& self)
280{
281 if (ec)
282 {
283 return;
284 }
285
286 self.updateReadings();
287 self.scheduleTimer(self.interval);
288}
289
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000290void Report::scheduleTimer(Milliseconds timerInterval)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200291{
292 timer.expires_after(timerInterval);
293 timer.async_wait(
294 [this](boost::system::error_code ec) { timerProc(ec, *this); });
295}
296
297void Report::updateReadings()
298{
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200299 if (!enabled)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000300 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200301 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000302 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200303
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100304 if (reportUpdates == ReportUpdates::overwrite ||
305 reportingType == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200306 {
307 readingsBuffer.clear();
308 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000309
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200310 for (const auto& metric : metrics)
311 {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200312 for (const auto& [id, metadata, value, timestamp] :
313 metric->getReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200314 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100315 if (reportUpdates == ReportUpdates::appendStopsWhenFull &&
Szymon Dompke3eb56862021-09-20 15:32:04 +0200316 readingsBuffer.isFull())
317 {
318 enabled = false;
Krzysztof Grobelnyfbeb5bf2022-01-03 09:41:29 +0100319 for (auto& m : metrics)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200320 {
Krzysztof Grobelnyfbeb5bf2022-01-03 09:41:29 +0100321 m->deinitialize();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200322 }
323 break;
324 }
325 readingsBuffer.emplace(id, metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200326 }
327 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200328
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100329 readings = {
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100330 std::chrono::duration_cast<Milliseconds>(clock->systemTimestamp())
331 .count(),
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100332 std::vector<ReadingData>(readingsBuffer.begin(), readingsBuffer.end())};
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100333
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200334 reportIface->signal_property("Readings");
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200335}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100336
337bool Report::storeConfiguration() const
338{
339 try
340 {
341 nlohmann::json data;
342
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200343 data["Enabled"] = enabled;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100344 data["Version"] = reportVersion;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100345 data["Id"] = id;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100346 data["Name"] = name;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100347 data["ReportingType"] = utils::toUnderlying(reportingType);
348 data["ReportActions"] =
349 utils::transform(reportActions, [](const auto reportAction) {
350 return utils::toUnderlying(reportAction);
351 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100352 data["Interval"] = interval.count();
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100353 data["AppendLimit"] =
354 appendLimit.value_or(std::numeric_limits<uint64_t>::max());
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100355 data["ReportUpdates"] = utils::toUnderlying(reportUpdates);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000356 data["ReadingParameters"] =
357 utils::transform(metrics, [](const auto& metric) {
358 return metric->dumpConfiguration();
359 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100360
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100361 reportStorage.store(fileName(), data);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100362 }
363 catch (const std::exception& e)
364 {
365 phosphor::logging::log<phosphor::logging::level::ERR>(
366 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100367 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100368 return false;
369 }
370
371 return true;
372}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100373
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100374interfaces::JsonStorage::FilePath Report::fileName() const
375{
376 return interfaces::JsonStorage::FilePath{
377 std::to_string(std::hash<std::string>{}(id))};
378}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100379
380void Report::updateTriggerIds(const std::string& triggerId,
381 TriggerIdUpdate updateType)
382{
383 if (updateType == TriggerIdUpdate::Add)
384 {
385 if (triggerIds.insert(triggerId).second)
386 {
387 reportIface->signal_property("TriggerIds");
388 }
389 }
390 else if (updateType == TriggerIdUpdate::Remove)
391 {
392 if (triggerIds.erase(triggerId) > 0)
393 {
394 reportIface->signal_property("TriggerIds");
395 }
396 }
397}