xref: /openbmc/telemetry/src/report.cpp (revision e2362796befa500153beb22d3eae0a959bf825f4)
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         });
102 
103     if (reportingType == "Periodic")
104     {
105         scheduleTimer(interval);
106     }
107 }
108 
109 void Report::timerProc(boost::system::error_code ec, Report& self)
110 {
111     if (ec)
112     {
113         return;
114     }
115 
116     self.updateReadings();
117     self.scheduleTimer(self.interval);
118 }
119 
120 void Report::scheduleTimer(std::chrono::milliseconds timerInterval)
121 {
122     timer.expires_after(timerInterval);
123     timer.async_wait(
124         [this](boost::system::error_code ec) { timerProc(ec, *this); });
125 }
126 
127 void Report::updateReadings()
128 {
129     auto numElements = std::accumulate(
130         metrics.begin(), metrics.end(), 0u, [](auto sum, const auto& metric) {
131             return sum + metric->getReadings().size();
132         });
133 
134     readingsCache.resize(numElements);
135 
136     auto it = readingsCache.begin();
137 
138     for (const auto& metric : metrics)
139     {
140         for (const auto& reading : metric->getReadings())
141         {
142             *(it++) = std::make_tuple(reading.id, reading.metadata,
143                                       reading.value, reading.timestamp);
144         }
145     }
146 
147     std::get<0>(readings) = std::time(0);
148     std::get<1>(readings) = readingsCache;
149 
150     reportIface->signal_property("Readings");
151 }
152 
153 bool Report::storeConfiguration() const
154 {
155     try
156     {
157         nlohmann::json data;
158 
159         data["Version"] = reportVersion;
160         data["Name"] = name;
161         data["ReportingType"] = reportingType;
162         data["EmitsReadingsUpdate"] = emitsReadingsUpdate;
163         data["LogToMetricReportsCollection"] = logToMetricReportsCollection;
164         data["Interval"] = interval.count();
165         data["ReadingParameters"] = utils::transform(
166             metrics, [](const auto& metric) { return metric->to_json(); });
167 
168         reportStorage.store(fileName, data);
169     }
170     catch (const std::exception& e)
171     {
172         phosphor::logging::log<phosphor::logging::level::ERR>(
173             "Failed to store a report in storage",
174             phosphor::logging::entry("msg=", e.what()));
175         return false;
176     }
177 
178     return true;
179 }
180