xref: /openbmc/telemetry/src/report.cpp (revision a4e6761643f2ff306d6928ea5537eb151fae79a0)
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     persistency = storeConfiguration();
45     reportIface = makeReportInterface();
46 
47     if (reportingType == "Periodic")
48     {
49         scheduleTimer(interval);
50     }
51 
52     for (auto& metric : this->metrics)
53     {
54         metric->initialize();
55     }
56 }
57 
58 std::unique_ptr<sdbusplus::asio::dbus_interface> Report::makeReportInterface()
59 {
60     auto dbusIface = objServer->add_unique_interface(path, reportIfaceName);
61     dbusIface->register_property_rw(
62         "Interval", static_cast<uint64_t>(interval.count()),
63         sdbusplus::vtable::property_::emits_change,
64         [this](uint64_t newVal, auto&) {
65             std::chrono::milliseconds newValT(newVal);
66             if (newValT < ReportManager::minInterval)
67             {
68                 return false;
69             }
70             interval = newValT;
71             return true;
72         },
73         [this](const auto&) {
74             return static_cast<uint64_t>(interval.count());
75         });
76     dbusIface->register_property_rw(
77         "Persistency", persistency, sdbusplus::vtable::property_::emits_change,
78         [this](bool newVal, const auto&) {
79             if (newVal == persistency)
80             {
81                 return true;
82             }
83             if (newVal)
84             {
85                 persistency = storeConfiguration();
86             }
87             else
88             {
89                 reportStorage.remove(fileName);
90                 persistency = false;
91             }
92             return true;
93         },
94         [this](const auto&) { return persistency; });
95 
96     auto readingsFlag = sdbusplus::vtable::property_::none;
97     if (emitsReadingsUpdate)
98     {
99         readingsFlag = sdbusplus::vtable::property_::emits_change;
100     }
101     dbusIface->register_property_r("Readings", readings, readingsFlag,
102                                    [this](const auto&) { return readings; });
103     dbusIface->register_property_r(
104         "ReportingType", reportingType, sdbusplus::vtable::property_::const_,
105         [this](const auto&) { return reportingType; });
106     dbusIface->register_property_r(
107         "ReadingParameters", readingParameters,
108         sdbusplus::vtable::property_::const_,
109         [this](const auto&) { return readingParameters; });
110     dbusIface->register_property_r(
111         "EmitsReadingsUpdate", emitsReadingsUpdate,
112         sdbusplus::vtable::property_::const_,
113         [this](const auto&) { return emitsReadingsUpdate; });
114     dbusIface->register_property_r(
115         "LogToMetricReportsCollection", logToMetricReportsCollection,
116         sdbusplus::vtable::property_::const_,
117         [this](const auto&) { return logToMetricReportsCollection; });
118     dbusIface->register_method("Update", [this] {
119         if (reportingType == "OnRequest")
120         {
121             updateReadings();
122         }
123     });
124     constexpr bool skipPropertiesChangedSignal = true;
125     dbusIface->initialize(skipPropertiesChangedSignal);
126     return dbusIface;
127 }
128 
129 void Report::timerProc(boost::system::error_code ec, Report& self)
130 {
131     if (ec)
132     {
133         return;
134     }
135 
136     self.updateReadings();
137     self.scheduleTimer(self.interval);
138 }
139 
140 void Report::scheduleTimer(std::chrono::milliseconds timerInterval)
141 {
142     timer.expires_after(timerInterval);
143     timer.async_wait(
144         [this](boost::system::error_code ec) { timerProc(ec, *this); });
145 }
146 
147 void Report::updateReadings()
148 {
149     std::tuple_element_t<1, Readings> readingsCache(metrics.size());
150 
151     std::transform(std::begin(metrics), std::end(metrics),
152                    std::begin(readingsCache), [](const auto& metric) {
153                        const auto& reading = metric->getReading();
154                        return std::make_tuple(reading.id, reading.metadata,
155                                               reading.value, reading.timestamp);
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"] =
177             utils::transform(metrics, [](const auto& metric) {
178                 return metric->dumpConfiguration();
179             });
180 
181         reportStorage.store(fileName, data);
182     }
183     catch (const std::exception& e)
184     {
185         phosphor::logging::log<phosphor::logging::level::ERR>(
186             "Failed to store a report in storage",
187             phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
188         return false;
189     }
190 
191     return true;
192 }
193