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