xref: /openbmc/telemetry/src/report.cpp (revision f32f6fef2503c1eba778a1446d95f05dca077157)
1 #include "report.hpp"
2 
3 #include "report_manager.hpp"
4 #include "utils/transform.hpp"
5 
6 #include <phosphor-logging/log.hpp>
7 
8 #include <numeric>
9 
10 Report::Report(boost::asio::io_context& ioc,
11                const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
12                const std::string& reportName,
13                const std::string& reportingTypeIn,
14                const bool emitsReadingsUpdateIn,
15                const bool logToMetricReportsCollectionIn,
16                const std::chrono::milliseconds intervalIn,
17                const ReadingParameters& readingParametersIn,
18                interfaces::ReportManager& reportManager,
19                interfaces::JsonStorage& reportStorageIn,
20                std::vector<std::shared_ptr<interfaces::Metric>> metrics) :
21     name(reportName),
22     path(reportDir + name), reportingType(reportingTypeIn),
23     interval(intervalIn), emitsReadingsUpdate(emitsReadingsUpdateIn),
24     logToMetricReportsCollection(logToMetricReportsCollectionIn),
25     readingParameters(readingParametersIn), objServer(objServer),
26     metrics(std::move(metrics)), timer(ioc),
27     fileName(std::to_string(std::hash<std::string>{}(name))),
28     reportStorage(reportStorageIn)
29 {
30     deleteIface = objServer->add_unique_interface(
31         path, deleteIfaceName, [this, &ioc, &reportManager](auto& dbusIface) {
32             dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
33                 if (persistency)
34                 {
35                     reportStorage.remove(fileName);
36                 }
37                 boost::asio::post(ioc, [this, &reportManager] {
38                     reportManager.removeReport(this);
39                 });
40             });
41         });
42 
43     reportIface = objServer->add_unique_interface(
44         path, reportIfaceName, [this](auto& dbusIface) {
45             dbusIface.register_property_rw(
46                 "Interval", static_cast<uint64_t>(interval.count()),
47                 sdbusplus::vtable::property_::emits_change,
48                 [this](uint64_t newVal, auto&) {
49                     std::chrono::milliseconds newValT(newVal);
50                     if (newValT < ReportManager::minInterval)
51                     {
52                         return false;
53                     }
54                     interval = newValT;
55                     return true;
56                 },
57                 [this](const auto&) {
58                     return static_cast<uint64_t>(interval.count());
59                 });
60             persistency = storeConfiguration();
61             dbusIface.register_property_rw(
62                 "Persistency", persistency,
63                 sdbusplus::vtable::property_::emits_change,
64                 [this](bool newVal, const auto&) {
65                     if (newVal == persistency)
66                     {
67                         return true;
68                     }
69                     if (newVal)
70                     {
71                         persistency = storeConfiguration();
72                     }
73                     else
74                     {
75                         reportStorage.remove(fileName);
76                         persistency = false;
77                     }
78                     return true;
79                 },
80                 [this](const auto&) { return persistency; });
81             dbusIface.register_property_r(
82                 "Readings", readings,
83                 sdbusplus::vtable::property_::emits_change,
84                 [this](const auto&) { return readings; });
85             dbusIface.register_property_r(
86                 "ReportingType", reportingType,
87                 sdbusplus::vtable::property_::const_,
88                 [this](const auto&) { return reportingType; });
89             dbusIface.register_property_r(
90                 "ReadingParameters", readingParameters,
91                 sdbusplus::vtable::property_::const_,
92                 [this](const auto&) { return readingParameters; });
93             dbusIface.register_property_r(
94                 "EmitsReadingsUpdate", emitsReadingsUpdate,
95                 sdbusplus::vtable::property_::const_,
96                 [this](const auto&) { return emitsReadingsUpdate; });
97             dbusIface.register_property_r(
98                 "LogToMetricReportsCollection", logToMetricReportsCollection,
99                 sdbusplus::vtable::property_::const_,
100                 [this](const auto&) { return logToMetricReportsCollection; });
101             dbusIface.register_method("Update", [this] {
102                 if (reportingType == "OnRequest")
103                 {
104                     updateReadings();
105                 }
106             });
107         });
108 
109     if (reportingType == "Periodic")
110     {
111         scheduleTimer(interval);
112     }
113 }
114 
115 void Report::timerProc(boost::system::error_code ec, Report& self)
116 {
117     if (ec)
118     {
119         return;
120     }
121 
122     self.updateReadings();
123     self.scheduleTimer(self.interval);
124 }
125 
126 void Report::scheduleTimer(std::chrono::milliseconds timerInterval)
127 {
128     timer.expires_after(timerInterval);
129     timer.async_wait(
130         [this](boost::system::error_code ec) { timerProc(ec, *this); });
131 }
132 
133 void Report::updateReadings()
134 {
135     auto numElements = std::accumulate(
136         metrics.begin(), metrics.end(), 0u, [](auto sum, const auto& metric) {
137             return sum + metric->getReadings().size();
138         });
139 
140     readingsCache.resize(numElements);
141 
142     auto it = readingsCache.begin();
143 
144     for (const auto& metric : metrics)
145     {
146         for (const auto& reading : metric->getReadings())
147         {
148             *(it++) = std::make_tuple(reading.id, reading.metadata,
149                                       reading.value, reading.timestamp);
150         }
151     }
152 
153     std::get<0>(readings) = std::time(0);
154     std::get<1>(readings) = readingsCache;
155 
156     reportIface->signal_property("Readings");
157 }
158 
159 bool Report::storeConfiguration() const
160 {
161     try
162     {
163         nlohmann::json data;
164 
165         data["Version"] = reportVersion;
166         data["Name"] = name;
167         data["ReportingType"] = reportingType;
168         data["EmitsReadingsUpdate"] = emitsReadingsUpdate;
169         data["LogToMetricReportsCollection"] = logToMetricReportsCollection;
170         data["Interval"] = interval.count();
171         data["ReadingParameters"] = utils::transform(
172             metrics, [](const auto& metric) { return metric->to_json(); });
173 
174         reportStorage.store(fileName, data);
175     }
176     catch (const std::exception& e)
177     {
178         phosphor::logging::log<phosphor::logging::level::ERR>(
179             "Failed to store a report in storage",
180             phosphor::logging::entry("msg=", e.what()));
181         return false;
182     }
183 
184     return true;
185 }
186