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