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