xref: /openbmc/telemetry/src/report_manager.cpp (revision b4ef22e4d365bde35a7fce7950033c9271c68ce7)
1 #include "report_manager.hpp"
2 
3 #include "report.hpp"
4 #include "types/report_types.hpp"
5 #include "utils/conversion.hpp"
6 #include "utils/generate_id.hpp"
7 #include "utils/transform.hpp"
8 
9 #include <phosphor-logging/log.hpp>
10 #include <sdbusplus/exception.hpp>
11 
12 #include <stdexcept>
13 #include <system_error>
14 
15 ReadingParameters
16     convertToReadingParameters(ReadingParametersPastVersion params)
17 {
18     return utils::transform(params, [](const auto& param) {
19         using namespace std::chrono_literals;
20 
21         const auto& [sensorPath, operationType, id, metadata] = param;
22 
23         return ReadingParameters::value_type(
24             std::vector<
25                 std::tuple<sdbusplus::message::object_path, std::string>>{
26                 {sensorPath, metadata}},
27             operationType, id, utils::enumToString(CollectionTimeScope::point),
28             0u);
29     });
30 }
31 
32 ReportManager::ReportManager(
33     std::unique_ptr<interfaces::ReportFactory> reportFactoryIn,
34     std::unique_ptr<interfaces::JsonStorage> reportStorageIn,
35     const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn,
36     std::unique_ptr<interfaces::TriggerManager>& triggerManagerIn) :
37     reportFactory(std::move(reportFactoryIn)),
38     reportStorage(std::move(reportStorageIn)), objServer(objServerIn),
39     triggerManager(triggerManagerIn)
40 {
41     reports.reserve(maxReports);
42 
43     loadFromPersistent();
44 
45     reportManagerIface = objServer->add_unique_interface(
46         reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) {
47             dbusIface.register_property_r(
48                 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_,
49                 [](const auto&) { return maxReports; });
50             dbusIface.register_property_r(
51                 "MaxReportIdLength", size_t{},
52                 sdbusplus::vtable::property_::const_,
53                 [](const auto&) { return maxReportIdLength; });
54             dbusIface.register_property_r(
55                 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_,
56                 [](const auto&) -> uint64_t { return minInterval.count(); });
57 
58             dbusIface.register_method(
59                 "AddReport", [this](boost::asio::yield_context& yield,
60                                     const std::string& reportId,
61                                     const std::string& reportingType,
62                                     const bool emitsReadingsUpdate,
63                                     const bool logToMetricReportsCollection,
64                                     const uint64_t interval,
65                                     ReadingParametersPastVersion metricParams) {
66                     constexpr auto enabledDefault = true;
67                     constexpr uint64_t appendLimitDefault = 0;
68                     constexpr ReportUpdates reportUpdatesDefault =
69                         ReportUpdates::overwrite;
70 
71                     std::vector<ReportAction> reportActions;
72 
73                     if (emitsReadingsUpdate)
74                     {
75                         reportActions.emplace_back(
76                             ReportAction::emitsReadingsUpdate);
77                     }
78                     if (logToMetricReportsCollection)
79                     {
80                         reportActions.emplace_back(
81                             ReportAction::logToMetricReportsCollection);
82                     }
83 
84                     return addReport(yield, reportId, reportId,
85                                      utils::toReportingType(reportingType),
86                                      reportActions, Milliseconds(interval),
87                                      appendLimitDefault, reportUpdatesDefault,
88                                      convertToReadingParameters(
89                                          std::move(metricParams)),
90                                      enabledDefault)
91                         .getPath();
92                 });
93 
94             dbusIface.register_method(
95                 "AddReportFutureVersion",
96                 [this](
97                     boost::asio::yield_context& yield,
98                     const std::string& reportId, const std::string& reportName,
99                     const std::string& reportingType,
100                     const std::string& reportUpdates,
101                     const uint64_t appendLimit,
102                     const std::vector<std::string>& reportActions,
103                     const uint64_t interval, ReadingParameters metricParams) {
104                     constexpr auto enabledDefault = true;
105                     return addReport(yield, reportId, reportName,
106                                      utils::toReportingType(reportingType),
107                                      utils::transform(
108                                          reportActions,
109                                          [](const auto& reportAction) {
110                                              return utils::toReportAction(
111                                                  reportAction);
112                                          }),
113                                      Milliseconds(interval), appendLimit,
114                                      utils::toReportUpdates(reportUpdates),
115                                      std::move(metricParams), enabledDefault)
116                         .getPath();
117                 });
118         });
119 }
120 
121 void ReportManager::removeReport(const interfaces::Report* report)
122 {
123     reports.erase(
124         std::remove_if(reports.begin(), reports.end(),
125                        [report](const auto& x) { return report == x.get(); }),
126         reports.end());
127 }
128 
129 void ReportManager::verifyAddReport(
130     const std::string& reportId, const std::string& reportName,
131     const ReportingType reportingType, Milliseconds interval,
132     const ReportUpdates reportUpdates, const uint64_t appendLimit,
133     const std::vector<LabeledMetricParameters>& readingParams)
134 {
135     if (reportingType == ReportingType::onChange)
136     {
137         throw sdbusplus::exception::SdBusError(
138             static_cast<int>(std::errc::invalid_argument),
139             "Invalid reportingType");
140     }
141 
142     if (reports.size() >= maxReports)
143     {
144         throw sdbusplus::exception::SdBusError(
145             static_cast<int>(std::errc::too_many_files_open),
146             "Reached maximal report count");
147     }
148 
149     if (appendLimit > maxAppendLimit &&
150         appendLimit != std::numeric_limits<uint64_t>::max())
151     {
152         throw sdbusplus::exception::SdBusError(
153             static_cast<int>(std::errc::invalid_argument),
154             "Append limit out of range");
155     }
156 
157     if (reportingType == ReportingType::periodic && interval < minInterval)
158     {
159         throw sdbusplus::exception::SdBusError(
160             static_cast<int>(std::errc::invalid_argument), "Invalid interval");
161     }
162 
163     size_t metricCount = 0;
164     for (auto metricParam : readingParams)
165     {
166         auto metricParamsVec =
167             metricParam.at_label<utils::tstring::SensorPath>();
168         metricCount += metricParamsVec.size();
169     }
170 
171     if (readingParams.size() > maxNumberMetrics ||
172         metricCount > maxNumberMetrics)
173     {
174         throw sdbusplus::exception::SdBusError(
175             static_cast<int>(std::errc::argument_list_too_long),
176             "Too many reading parameters");
177     }
178 
179     try
180     {
181         namespace ts = utils::tstring;
182 
183         for (const LabeledMetricParameters& item : readingParams)
184         {
185             utils::toOperationType(
186                 utils::toUnderlying(item.at_label<ts::OperationType>()));
187         }
188     }
189     catch (const std::exception& e)
190     {
191         throw sdbusplus::exception::SdBusError(
192             static_cast<int>(std::errc::invalid_argument), e.what());
193     }
194 }
195 
196 interfaces::Report& ReportManager::addReport(
197     boost::asio::yield_context& yield, const std::string& reportId,
198     const std::string& reportName, const ReportingType reportingType,
199     const std::vector<ReportAction>& reportActions, Milliseconds interval,
200     const uint64_t appendLimit, const ReportUpdates reportUpdates,
201     ReadingParameters metricParams, const bool enabled)
202 {
203     auto labeledMetricParams =
204         reportFactory->convertMetricParams(yield, metricParams);
205 
206     return addReport(reportId, reportName, reportingType, reportActions,
207                      interval, appendLimit, reportUpdates,
208                      std::move(labeledMetricParams), enabled);
209 }
210 
211 interfaces::Report& ReportManager::addReport(
212     const std::string& reportId, const std::string& reportName,
213     const ReportingType reportingType,
214     const std::vector<ReportAction>& reportActions, Milliseconds interval,
215     const uint64_t appendLimit, const ReportUpdates reportUpdates,
216     std::vector<LabeledMetricParameters> labeledMetricParams,
217     const bool enabled)
218 {
219     const auto existingReportIds = utils::transform(
220         reports, [](const auto& report) { return report->getId(); });
221 
222     auto [id, name] = utils::generateId(reportId, reportName, reportNameDefault,
223                                         existingReportIds, maxReportIdLength);
224 
225     verifyAddReport(id, name, reportingType, interval, reportUpdates,
226                     appendLimit, labeledMetricParams);
227 
228     std::vector<std::string> triggerIds;
229     if (triggerManager)
230     {
231         triggerIds = triggerManager->getTriggerIdsForReport(id);
232     }
233 
234     reports.emplace_back(
235         reportFactory->make(id, name, reportingType, reportActions, interval,
236                             appendLimit, reportUpdates, *this, *reportStorage,
237                             labeledMetricParams, enabled, triggerIds));
238     return *reports.back();
239 }
240 
241 void ReportManager::loadFromPersistent()
242 {
243     std::vector<interfaces::JsonStorage::FilePath> paths =
244         reportStorage->list();
245 
246     for (const auto& path : paths)
247     {
248         std::optional<nlohmann::json> data = reportStorage->load(path);
249         try
250         {
251             size_t version = data->at("Version").get<size_t>();
252             if (version != Report::reportVersion)
253             {
254                 throw std::logic_error("Invalid version");
255             }
256             bool enabled = data->at("Enabled").get<bool>();
257             std::string& id = data->at("Id").get_ref<std::string&>();
258             std::string& name = data->at("Name").get_ref<std::string&>();
259 
260             uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
261             std::vector<ReportAction> reportActions = utils::transform(
262                 data->at("ReportActions").get<std::vector<uint32_t>>(),
263                 [](const auto reportAction) {
264                     return utils::toReportAction(reportAction);
265                 });
266             uint64_t interval = data->at("Interval").get<uint64_t>();
267             uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
268             uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
269             auto readingParameters =
270                 data->at("ReadingParameters")
271                     .get<std::vector<LabeledMetricParameters>>();
272 
273             addReport(id, name, utils::toReportingType(reportingType),
274                       reportActions, Milliseconds(interval), appendLimit,
275                       utils::toReportUpdates(reportUpdates),
276                       std::move(readingParameters), enabled);
277         }
278         catch (const std::exception& e)
279         {
280             phosphor::logging::log<phosphor::logging::level::ERR>(
281                 "Failed to load report from storage",
282                 phosphor::logging::entry(
283                     "FILENAME=%s",
284                     static_cast<std::filesystem::path>(path).c_str()),
285                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
286             reportStorage->remove(path);
287         }
288     }
289 }
290 
291 void ReportManager::updateReport(const std::string& id)
292 {
293     for (auto& report : reports)
294     {
295         if (report->getId() == id)
296         {
297             report->updateReadings();
298             return;
299         }
300     }
301 }
302 
303 void ReportManager::updateTriggerIds(const std::string& reportId,
304                                      const std::string& triggerId,
305                                      TriggerIdUpdate updateType)
306 {
307     if (auto res = std::find_if(reports.begin(), reports.end(),
308                                 [&reportId](const auto& report) {
309                                     return report->getId() == reportId;
310                                 });
311         res != reports.end())
312     {
313         (*res)->updateTriggerIds(triggerId, updateType);
314     }
315 }
316