blob: 8c01cf21b2b545f2699458dbbf381a764b942c44 [file] [log] [blame]
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02001#include "report.hpp"
2
3#include "report_manager.hpp"
Wludzik, Jozefe2362792020-10-27 17:23:55 +01004#include "utils/transform.hpp"
5
6#include <phosphor-logging/log.hpp>
Wludzik, Jozefb1ff1f62020-10-23 13:20:52 +02007#include <sdbusplus/vtable.hpp>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02008
Szymon Dompke3eb56862021-09-20 15:32:04 +02009#include <limits>
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020010#include <numeric>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020011
12Report::Report(boost::asio::io_context& ioc,
13 const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
Wludzik, Jozefe2362792020-10-27 17:23:55 +010014 const std::string& reportName,
Szymon Dompke3eb56862021-09-20 15:32:04 +020015 const ReportingType reportingTypeIn,
Wludzik, Jozefe2362792020-10-27 17:23:55 +010016 const bool emitsReadingsUpdateIn,
17 const bool logToMetricReportsCollectionIn,
Szymon Dompke3eb56862021-09-20 15:32:04 +020018 const Milliseconds intervalIn, const uint64_t appendLimitIn,
19 const ReportUpdates reportUpdatesIn,
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020020 interfaces::ReportManager& reportManager,
Wludzik, Jozefe2362792020-10-27 17:23:55 +010021 interfaces::JsonStorage& reportStorageIn,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020022 std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
23 const bool enabledIn) :
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020024 name(reportName),
Wludzik, Jozefe2362792020-10-27 17:23:55 +010025 path(reportDir + name), reportingType(reportingTypeIn),
26 interval(intervalIn), emitsReadingsUpdate(emitsReadingsUpdateIn),
27 logToMetricReportsCollection(logToMetricReportsCollectionIn),
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),
Wludzik, Jozefe2362792020-10-27 17:23:55 +010033 fileName(std::to_string(std::hash<std::string>{}(name))),
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020034 reportStorage(reportStorageIn), enabled(enabledIn)
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020035{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000036 readingParameters =
37 toReadingParameters(utils::transform(metrics, [](const auto& metric) {
38 return metric->dumpConfiguration();
39 }));
40
41 readingParametersPastVersion =
42 utils::transform(readingParameters, [](const auto& item) {
43 return ReadingParametersPastVersion::value_type(
44 std::get<0>(item).front(), std::get<1>(item), std::get<2>(item),
45 std::get<3>(item));
46 });
47
Wludzik, Jozefe2362792020-10-27 17:23:55 +010048 deleteIface = objServer->add_unique_interface(
49 path, deleteIfaceName, [this, &ioc, &reportManager](auto& dbusIface) {
50 dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
51 if (persistency)
52 {
53 reportStorage.remove(fileName);
54 }
55 boost::asio::post(ioc, [this, &reportManager] {
56 reportManager.removeReport(this);
57 });
58 });
59 });
60
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +000061 persistency = storeConfiguration();
62 reportIface = makeReportInterface();
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020063
Szymon Dompke3eb56862021-09-20 15:32:04 +020064 if (reportingType == ReportingType::Periodic)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020065 {
66 scheduleTimer(interval);
67 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000068
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020069 if (enabled)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000070 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020071 for (auto& metric : this->metrics)
72 {
73 metric->initialize();
74 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +000075 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020076}
77
Szymon Dompke3eb56862021-09-20 15:32:04 +020078uint64_t Report::getSensorCount(
79 std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
80{
81 uint64_t sensorCount = 0;
82 for (auto& metric : metrics)
83 {
84 sensorCount += metric->sensorCount();
85 }
86 return sensorCount;
87}
88
89uint64_t Report::deduceAppendLimit(const uint64_t appendLimitIn) const
90{
91 if (appendLimitIn == std::numeric_limits<uint64_t>::max())
92 {
93 return sensorCount;
94 }
95 else
96 {
97 return appendLimitIn;
98 }
99}
100
101uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
102 const ReportingType reportingTypeIn) const
103{
104 if (reportUpdatesIn == ReportUpdates::Overwrite ||
105 reportingTypeIn == ReportingType::OnRequest)
106 {
107 return sensorCount;
108 }
109 else
110 {
111 return appendLimit;
112 }
113}
114
115void Report::setReportUpdates(const ReportUpdates newReportUpdates)
116{
117 if (reportUpdates != newReportUpdates)
118 {
119 if (reportingType != ReportingType::OnRequest &&
120 (reportUpdates == ReportUpdates::Overwrite ||
121 newReportUpdates == ReportUpdates::Overwrite))
122 {
123 readingsBuffer.clearAndResize(
124 deduceBufferSize(newReportUpdates, reportingType));
125 }
126 reportUpdates = newReportUpdates;
127 }
128}
129
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000130std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
131{
132 auto dbusIface = objServer->add_unique_interface(path, reportIfaceName);
133 dbusIface->register_property_rw(
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200134 "Enabled", enabled, sdbusplus::vtable::property_::emits_change,
135 [this](bool newVal, const auto&) {
136 if (newVal != enabled)
137 {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200138 if (true == newVal && ReportingType::Periodic == reportingType)
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200139 {
140 scheduleTimer(interval);
141 }
142 if (newVal)
143 {
144 for (auto& metric : metrics)
145 {
146 metric->initialize();
147 }
148 }
149 else
150 {
151 for (auto& metric : metrics)
152 {
153 metric->deinitialize();
154 }
155 }
156
157 enabled = newVal;
158 persistency = storeConfiguration();
159 }
160 return true;
161 },
162 [this](const auto&) { return enabled; });
163 dbusIface->register_property_rw(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000164 "Interval", interval.count(),
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000165 sdbusplus::vtable::property_::emits_change,
166 [this](uint64_t newVal, auto&) {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200167 if (Milliseconds newValT{newVal};
168 newValT >= ReportManager::minInterval)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000169 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200170 if (newValT != interval)
171 {
172 interval = newValT;
173 persistency = storeConfiguration();
174 }
175 return true;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000176 }
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200177 return false;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000178 },
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000179 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000180 dbusIface->register_property_rw(
181 "Persistency", persistency, sdbusplus::vtable::property_::emits_change,
182 [this](bool newVal, const auto&) {
183 if (newVal == persistency)
184 {
185 return true;
186 }
187 if (newVal)
188 {
189 persistency = storeConfiguration();
190 }
191 else
192 {
193 reportStorage.remove(fileName);
194 persistency = false;
195 }
196 return true;
197 },
198 [this](const auto&) { return persistency; });
199
200 auto readingsFlag = sdbusplus::vtable::property_::none;
201 if (emitsReadingsUpdate)
202 {
203 readingsFlag = sdbusplus::vtable::property_::emits_change;
204 }
205 dbusIface->register_property_r("Readings", readings, readingsFlag,
206 [this](const auto&) { return readings; });
207 dbusIface->register_property_r(
Szymon Dompke3eb56862021-09-20 15:32:04 +0200208 "ReportingType", std::string(), sdbusplus::vtable::property_::const_,
209 [this](const auto&) { return reportingTypeToString(reportingType); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000210 dbusIface->register_property_r(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000211 "ReadingParameters", readingParametersPastVersion,
212 sdbusplus::vtable::property_::const_,
213 [this](const auto&) { return readingParametersPastVersion; });
214 dbusIface->register_property_r(
215 "ReadingParametersFutureVersion", readingParameters,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000216 sdbusplus::vtable::property_::const_,
217 [this](const auto&) { return readingParameters; });
218 dbusIface->register_property_r(
219 "EmitsReadingsUpdate", emitsReadingsUpdate,
220 sdbusplus::vtable::property_::const_,
221 [this](const auto&) { return emitsReadingsUpdate; });
222 dbusIface->register_property_r(
223 "LogToMetricReportsCollection", logToMetricReportsCollection,
224 sdbusplus::vtable::property_::const_,
225 [this](const auto&) { return logToMetricReportsCollection; });
Szymon Dompke3eb56862021-09-20 15:32:04 +0200226 dbusIface->register_property_r("AppendLimit", appendLimit,
227 sdbusplus::vtable::property_::emits_change,
228 [this](const auto&) { return appendLimit; });
229 dbusIface->register_property_rw(
230 "ReportUpdates", std::string(),
231 sdbusplus::vtable::property_::emits_change,
232 [this](auto newVal, auto& oldVal) {
233 ReportManager::verifyReportUpdates(newVal);
234 setReportUpdates(stringToReportUpdates(newVal));
235 oldVal = newVal;
236 return true;
237 },
238 [this](const auto&) { return reportUpdatesToString(reportUpdates); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000239 dbusIface->register_method("Update", [this] {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200240 if (reportingType == ReportingType::OnRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000241 {
242 updateReadings();
243 }
244 });
245 constexpr bool skipPropertiesChangedSignal = true;
246 dbusIface->initialize(skipPropertiesChangedSignal);
247 return dbusIface;
248}
249
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200250void Report::timerProc(boost::system::error_code ec, Report& self)
251{
252 if (ec)
253 {
254 return;
255 }
256
257 self.updateReadings();
258 self.scheduleTimer(self.interval);
259}
260
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000261void Report::scheduleTimer(Milliseconds timerInterval)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200262{
263 timer.expires_after(timerInterval);
264 timer.async_wait(
265 [this](boost::system::error_code ec) { timerProc(ec, *this); });
266}
267
268void Report::updateReadings()
269{
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200270 if (!enabled)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000271 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200272 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000273 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200274
Szymon Dompke3eb56862021-09-20 15:32:04 +0200275 if (reportUpdates == ReportUpdates::Overwrite ||
276 reportingType == ReportingType::OnRequest)
277 {
278 readingsBuffer.clear();
279 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000280
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200281 for (const auto& metric : metrics)
282 {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200283 for (const auto& [id, metadata, value, timestamp] :
284 metric->getReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200285 {
Szymon Dompke3eb56862021-09-20 15:32:04 +0200286 if (reportUpdates == ReportUpdates::AppendStopsWhenFull &&
287 readingsBuffer.isFull())
288 {
289 enabled = false;
290 for (auto& metric : metrics)
291 {
292 metric->deinitialize();
293 }
294 break;
295 }
296 readingsBuffer.emplace(id, metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200297 }
298 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200299
300 readings = {std::time(0), std::vector<ReadingData>(readingsBuffer.begin(),
301 readingsBuffer.end())};
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100302
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200303 reportIface->signal_property("Readings");
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200304}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100305
306bool Report::storeConfiguration() const
307{
308 try
309 {
310 nlohmann::json data;
311
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200312 data["Enabled"] = enabled;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100313 data["Version"] = reportVersion;
314 data["Name"] = name;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200315 data["ReportingType"] = reportingTypeToString(reportingType);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100316 data["EmitsReadingsUpdate"] = emitsReadingsUpdate;
317 data["LogToMetricReportsCollection"] = logToMetricReportsCollection;
318 data["Interval"] = interval.count();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200319 data["AppendLimit"] = appendLimit;
320 data["ReportUpdates"] = reportUpdatesToString(reportUpdates);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000321 data["ReadingParameters"] =
322 utils::transform(metrics, [](const auto& metric) {
323 return metric->dumpConfiguration();
324 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100325
326 reportStorage.store(fileName, data);
327 }
328 catch (const std::exception& e)
329 {
330 phosphor::logging::log<phosphor::logging::level::ERR>(
331 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100332 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100333 return false;
334 }
335
336 return true;
337}