xref: /openbmc/telemetry/src/report.cpp (revision 7e098e93)
1 #include "report.hpp"
2 
3 #include "report_manager.hpp"
4 #include "utils/transform.hpp"
5 
6 #include <phosphor-logging/log.hpp>
7 #include <sdbusplus/vtable.hpp>
8 
9 #include <numeric>
10 
11 Report::Report(boost::asio::io_context& ioc,
12                const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
13                const std::string& reportName,
14                const std::string& reportingTypeIn,
15                const bool emitsReadingsUpdateIn,
16                const bool logToMetricReportsCollectionIn,
17                const Milliseconds intervalIn,
18                interfaces::ReportManager& reportManager,
19                interfaces::JsonStorage& reportStorageIn,
20                std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
21                const bool enabledIn) :
22     name(reportName),
23     path(reportDir + name), reportingType(reportingTypeIn),
24     interval(intervalIn), emitsReadingsUpdate(emitsReadingsUpdateIn),
25     logToMetricReportsCollection(logToMetricReportsCollectionIn),
26     objServer(objServer), metrics(std::move(metricsIn)), timer(ioc),
27     fileName(std::to_string(std::hash<std::string>{}(name))),
28     reportStorage(reportStorageIn), enabled(enabledIn)
29 {
30     readingParameters =
31         toReadingParameters(utils::transform(metrics, [](const auto& metric) {
32             return metric->dumpConfiguration();
33         }));
34 
35     readingParametersPastVersion =
36         utils::transform(readingParameters, [](const auto& item) {
37             return ReadingParametersPastVersion::value_type(
38                 std::get<0>(item).front(), std::get<1>(item), std::get<2>(item),
39                 std::get<3>(item));
40         });
41 
42     deleteIface = objServer->add_unique_interface(
43         path, deleteIfaceName, [this, &ioc, &reportManager](auto& dbusIface) {
44             dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
45                 if (persistency)
46                 {
47                     reportStorage.remove(fileName);
48                 }
49                 boost::asio::post(ioc, [this, &reportManager] {
50                     reportManager.removeReport(this);
51                 });
52             });
53         });
54 
55     persistency = storeConfiguration();
56     reportIface = makeReportInterface();
57 
58     if (reportingType == "Periodic")
59     {
60         scheduleTimer(interval);
61     }
62 
63     if (enabled)
64     {
65         for (auto& metric : this->metrics)
66         {
67             metric->initialize();
68         }
69     }
70 }
71 
72 std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
73 {
74     auto dbusIface = objServer->add_unique_interface(path, reportIfaceName);
75     dbusIface->register_property_rw(
76         "Enabled", enabled, sdbusplus::vtable::property_::emits_change,
77         [this](bool newVal, const auto&) {
78             if (newVal != enabled)
79             {
80                 if (true == newVal && "Periodic" == reportingType)
81                 {
82                     scheduleTimer(interval);
83                 }
84                 if (newVal)
85                 {
86                     for (auto& metric : metrics)
87                     {
88                         metric->initialize();
89                     }
90                 }
91                 else
92                 {
93                     for (auto& metric : metrics)
94                     {
95                         metric->deinitialize();
96                     }
97                 }
98 
99                 enabled = newVal;
100                 persistency = storeConfiguration();
101             }
102             return true;
103         },
104         [this](const auto&) { return enabled; });
105     dbusIface->register_property_rw(
106         "Interval", interval.count(),
107         sdbusplus::vtable::property_::emits_change,
108         [this](uint64_t newVal, auto&) {
109             if (Milliseconds newValT{newVal};
110                 newValT >= ReportManager::minInterval)
111             {
112                 if (newValT != interval)
113                 {
114                     interval = newValT;
115                     persistency = storeConfiguration();
116                 }
117                 return true;
118             }
119             return false;
120         },
121         [this](const auto&) { return interval.count(); });
122     dbusIface->register_property_rw(
123         "Persistency", persistency, sdbusplus::vtable::property_::emits_change,
124         [this](bool newVal, const auto&) {
125             if (newVal == persistency)
126             {
127                 return true;
128             }
129             if (newVal)
130             {
131                 persistency = storeConfiguration();
132             }
133             else
134             {
135                 reportStorage.remove(fileName);
136                 persistency = false;
137             }
138             return true;
139         },
140         [this](const auto&) { return persistency; });
141 
142     auto readingsFlag = sdbusplus::vtable::property_::none;
143     if (emitsReadingsUpdate)
144     {
145         readingsFlag = sdbusplus::vtable::property_::emits_change;
146     }
147     dbusIface->register_property_r("Readings", readings, readingsFlag,
148                                    [this](const auto&) { return readings; });
149     dbusIface->register_property_r(
150         "ReportingType", reportingType, sdbusplus::vtable::property_::const_,
151         [this](const auto&) { return reportingType; });
152     dbusIface->register_property_r(
153         "ReadingParameters", readingParametersPastVersion,
154         sdbusplus::vtable::property_::const_,
155         [this](const auto&) { return readingParametersPastVersion; });
156     dbusIface->register_property_r(
157         "ReadingParametersFutureVersion", readingParameters,
158         sdbusplus::vtable::property_::const_,
159         [this](const auto&) { return readingParameters; });
160     dbusIface->register_property_r(
161         "EmitsReadingsUpdate", emitsReadingsUpdate,
162         sdbusplus::vtable::property_::const_,
163         [this](const auto&) { return emitsReadingsUpdate; });
164     dbusIface->register_property_r(
165         "LogToMetricReportsCollection", logToMetricReportsCollection,
166         sdbusplus::vtable::property_::const_,
167         [this](const auto&) { return logToMetricReportsCollection; });
168     dbusIface->register_method("Update", [this] {
169         if (reportingType == "OnRequest")
170         {
171             updateReadings();
172         }
173     });
174     constexpr bool skipPropertiesChangedSignal = true;
175     dbusIface->initialize(skipPropertiesChangedSignal);
176     return dbusIface;
177 }
178 
179 void Report::timerProc(boost::system::error_code ec, Report& self)
180 {
181     if (ec)
182     {
183         return;
184     }
185 
186     self.updateReadings();
187     self.scheduleTimer(self.interval);
188 }
189 
190 void Report::scheduleTimer(Milliseconds timerInterval)
191 {
192     timer.expires_after(timerInterval);
193     timer.async_wait(
194         [this](boost::system::error_code ec) { timerProc(ec, *this); });
195 }
196 
197 void Report::updateReadings()
198 {
199     if (!enabled)
200     {
201         return;
202     }
203 
204     using ReadingsTimestamp = std::tuple_element_t<0, Readings>;
205     using ReadingsValue = std::tuple_element_t<1, Readings>;
206 
207     std::get<ReadingsValue>(cachedReadings).clear();
208     for (const auto& metric : metrics)
209     {
210         for (const auto& [id, meta, val, timestamp] : metric->getReadings())
211         {
212             std::get<ReadingsValue>(cachedReadings)
213                 .emplace_back(id, meta, val, timestamp);
214         }
215     }
216     std::get<ReadingsTimestamp>(cachedReadings) = std::time(0);
217     std::swap(readings, cachedReadings);
218 
219     reportIface->signal_property("Readings");
220 }
221 
222 bool Report::storeConfiguration() const
223 {
224     try
225     {
226         nlohmann::json data;
227 
228         data["Enabled"] = enabled;
229         data["Version"] = reportVersion;
230         data["Name"] = name;
231         data["ReportingType"] = reportingType;
232         data["EmitsReadingsUpdate"] = emitsReadingsUpdate;
233         data["LogToMetricReportsCollection"] = logToMetricReportsCollection;
234         data["Interval"] = interval.count();
235         data["ReadingParameters"] =
236             utils::transform(metrics, [](const auto& metric) {
237                 return metric->dumpConfiguration();
238             });
239 
240         reportStorage.store(fileName, data);
241     }
242     catch (const std::exception& e)
243     {
244         phosphor::logging::log<phosphor::logging::level::ERR>(
245             "Failed to store a report in storage",
246             phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
247         return false;
248     }
249 
250     return true;
251 }
252