xref: /openbmc/telemetry/src/report_manager.cpp (revision 493e62eb2f2ff6cbf3282b9cb98ebe6e520b88fc)
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     reportFactory(std::move(reportFactoryIn)),
37     reportStorage(std::move(reportStorageIn)), objServer(objServerIn)
38 {
39     reports.reserve(maxReports);
40 
41     loadFromPersistent();
42 
43     reportManagerIface = objServer->add_unique_interface(
44         reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) {
45             dbusIface.register_property_r(
46                 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_,
47                 [](const auto&) { return maxReports; });
48             dbusIface.register_property_r(
49                 "MaxReportIdLength", size_t{},
50                 sdbusplus::vtable::property_::const_,
51                 [](const auto&) { return maxReportIdLength; });
52             dbusIface.register_property_r(
53                 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_,
54                 [](const auto&) -> uint64_t { return minInterval.count(); });
55             dbusIface.register_property_r(
56                 "SupportedOperationTypes", std::vector<std::string>{},
57                 sdbusplus::vtable::property_::const_,
58                 [](const auto&) -> std::vector<std::string> {
59                     return utils::transform<std::vector>(
60                         utils::convDataOperationType, [](const auto& item) {
61                             return std::string(item.first);
62                         });
63                 });
64 
65             dbusIface.register_method(
66                 "AddReport", [this](boost::asio::yield_context& yield,
67                                     const std::string& reportId,
68                                     const std::string& reportingType,
69                                     const bool emitsReadingsUpdate,
70                                     const bool logToMetricReportsCollection,
71                                     const uint64_t interval,
72                                     ReadingParametersPastVersion metricParams) {
73                     constexpr auto enabledDefault = true;
74                     constexpr uint64_t appendLimitDefault = 0;
75                     constexpr ReportUpdates reportUpdatesDefault =
76                         ReportUpdates::overwrite;
77 
78                     std::vector<ReportAction> reportActions;
79 
80                     if (emitsReadingsUpdate)
81                     {
82                         reportActions.emplace_back(
83                             ReportAction::emitsReadingsUpdate);
84                     }
85                     if (logToMetricReportsCollection)
86                     {
87                         reportActions.emplace_back(
88                             ReportAction::logToMetricReportsCollection);
89                     }
90 
91                     return addReport(yield, reportId, reportId,
92                                      utils::toReportingType(reportingType),
93                                      reportActions, Milliseconds(interval),
94                                      appendLimitDefault, reportUpdatesDefault,
95                                      convertToReadingParameters(
96                                          std::move(metricParams)),
97                                      enabledDefault)
98                         .getPath();
99                 });
100 
101             dbusIface.register_method(
102                 "AddReportFutureVersion",
103                 [this](
104                     boost::asio::yield_context& yield,
105                     const std::string& reportId, const std::string& reportName,
106                     const std::string& reportingType,
107                     const std::string& reportUpdates,
108                     const uint64_t appendLimit,
109                     const std::vector<std::string>& reportActions,
110                     const uint64_t interval, ReadingParameters metricParams) {
111                     constexpr auto enabledDefault = true;
112                     return addReport(yield, reportId, reportName,
113                                      utils::toReportingType(reportingType),
114                                      utils::transform(
115                                          reportActions,
116                                          [](const auto& reportAction) {
117                                              return utils::toReportAction(
118                                                  reportAction);
119                                          }),
120                                      Milliseconds(interval), appendLimit,
121                                      utils::toReportUpdates(reportUpdates),
122                                      std::move(metricParams), enabledDefault)
123                         .getPath();
124                 });
125         });
126 }
127 
128 void ReportManager::removeReport(const interfaces::Report* report)
129 {
130     reports.erase(
131         std::remove_if(reports.begin(), reports.end(),
132                        [report](const auto& x) { return report == x.get(); }),
133         reports.end());
134 }
135 
136 void ReportManager::verifyAddReport(
137     const std::string& reportId, const std::string& reportName,
138     const ReportingType reportingType, Milliseconds interval,
139     const ReportUpdates reportUpdates, const uint64_t appendLimit,
140     const std::vector<LabeledMetricParameters>& readingParams)
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, Readings{});
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, Readings readings)
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     reports.emplace_back(
229         reportFactory->make(id, name, reportingType, reportActions, interval,
230                             appendLimit, reportUpdates, *this, *reportStorage,
231                             labeledMetricParams, enabled, std::move(readings)));
232     return *reports.back();
233 }
234 
235 void ReportManager::loadFromPersistent()
236 {
237     std::vector<interfaces::JsonStorage::FilePath> paths =
238         reportStorage->list();
239 
240     for (const auto& path : paths)
241     {
242         std::optional<nlohmann::json> data = reportStorage->load(path);
243         try
244         {
245             size_t version = data->at("Version").get<size_t>();
246             if (version != Report::reportVersion)
247             {
248                 throw std::logic_error("Invalid version");
249             }
250             bool enabled = data->at("Enabled").get<bool>();
251             std::string& id = data->at("Id").get_ref<std::string&>();
252             std::string& name = data->at("Name").get_ref<std::string&>();
253 
254             uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
255             std::vector<ReportAction> reportActions = utils::transform(
256                 data->at("ReportActions").get<std::vector<uint32_t>>(),
257                 [](const auto reportAction) {
258                     return utils::toReportAction(reportAction);
259                 });
260             uint64_t interval = data->at("Interval").get<uint64_t>();
261             uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
262             uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
263             auto readingParameters =
264                 data->at("ReadingParameters")
265                     .get<std::vector<LabeledMetricParameters>>();
266 
267             Readings readings = {};
268 
269             if (auto it = data->find("MetricValues"); it != data->end())
270             {
271                 const auto labeledReadings = it->get<LabeledReadings>();
272                 readings = utils::toReadings(labeledReadings);
273             }
274 
275             addReport(id, name, utils::toReportingType(reportingType),
276                       reportActions, Milliseconds(interval), appendLimit,
277                       utils::toReportUpdates(reportUpdates),
278                       std::move(readingParameters), enabled,
279                       std::move(readings));
280         }
281         catch (const std::exception& e)
282         {
283             phosphor::logging::log<phosphor::logging::level::ERR>(
284                 "Failed to load report from storage",
285                 phosphor::logging::entry(
286                     "FILENAME=%s",
287                     static_cast<std::filesystem::path>(path).c_str()),
288                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
289             reportStorage->remove(path);
290         }
291     }
292 }
293