xref: /openbmc/telemetry/src/report_manager.cpp (revision 89cf7d7dafe2c073d781a40d59fa9c46cb344be6)
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 ReportUpdates reportUpdatesDefault =
74                         ReportUpdates::overwrite;
75 
76                     std::vector<ReportAction> reportActions;
77 
78                     if (emitsReadingsUpdate)
79                     {
80                         reportActions.emplace_back(
81                             ReportAction::emitsReadingsUpdate);
82                     }
83                     if (logToMetricReportsCollection)
84                     {
85                         reportActions.emplace_back(
86                             ReportAction::logToMetricReportsCollection);
87                     }
88 
89                     return addReport(yield, reportId, reportId,
90                                      utils::toReportingType(reportingType),
91                                      reportActions, Milliseconds(interval),
92                                      maxAppendLimit, reportUpdatesDefault,
93                                      convertToReadingParameters(
94                                          std::move(metricParams)),
95                                      enabledDefault)
96                         .getPath();
97                 });
98 
99             dbusIface.register_method(
100                 "AddReportFutureVersion",
101                 [this](
102                     boost::asio::yield_context& yield,
103                     const std::vector<
104                         std::pair<std::string, AddReportFutureVersionVariant>>&
105                         properties) {
106                     std::optional<std::string> reportId;
107                     std::optional<std::string> reportName;
108                     std::optional<std::string> reportingType;
109                     std::optional<std::vector<std::string>> reportActions;
110                     std::optional<uint64_t> interval;
111                     std::optional<uint64_t> appendLimit;
112                     std::optional<std::string> reportUpdates;
113                     std::optional<ReadingParameters> metricParams;
114                     std::optional<ReadingParameters> readingParameters;
115                     std::optional<bool> enabled;
116 
117                     try
118                     {
119                         sdbusplus::unpackProperties(
120                             properties, "Id", reportId, "Name", reportName,
121                             "ReportingType", reportingType, "ReportActions",
122                             reportActions, "Interval", interval, "AppendLimit",
123                             appendLimit, "ReportUpdates", reportUpdates,
124                             "MetricParams", metricParams, "Enabled", enabled,
125                             "ReadingParameters", readingParameters);
126                     }
127                     catch (const sdbusplus::exception::UnpackPropertyError& e)
128                     {
129                         throw errors::InvalidArgument(e.propertyName);
130                     }
131 
132                     if (readingParameters == std::nullopt)
133                     {
134                         readingParameters = metricParams;
135                     }
136 
137                     return addReport(
138                                yield, reportId.value_or(""),
139                                reportName.value_or(""),
140                                utils::toReportingType(
141                                    reportingType.value_or(utils::enumToString(
142                                        ReportingType::onRequest))),
143                                utils::transform(
144                                    reportActions.value_or(
145                                        std::vector<std::string>{}),
146                                    [](const auto& reportAction) {
147                                        return utils::toReportAction(
148                                            reportAction);
149                                    }),
150                                Milliseconds(interval.value_or(0)),
151                                appendLimit.value_or(maxAppendLimit),
152                                utils::toReportUpdates(
153                                    reportUpdates.value_or(utils::enumToString(
154                                        ReportUpdates::overwrite))),
155                                readingParameters.value_or(ReadingParameters{}),
156                                enabled.value_or(true))
157                         .getPath();
158                 });
159         });
160 }
161 
162 void ReportManager::removeReport(const interfaces::Report* report)
163 {
164     reports.erase(
165         std::remove_if(reports.begin(), reports.end(),
166                        [report](const auto& x) { return report == x.get(); }),
167         reports.end());
168 }
169 
170 void ReportManager::verifyMetricParameters(
171     const std::vector<LabeledMetricParameters>& readingParams)
172 {
173     namespace ts = utils::tstring;
174 
175     for (auto readingParam : readingParams)
176     {
177         if (readingParam.at_label<ts::Id>().length() >
178             utils::constants::maxIdNameLength)
179         {
180             throw errors::InvalidArgument("ReadingParameters.Id", "Too long.");
181         }
182     }
183 }
184 
185 void ReportManager::verifyAddReport(
186     const std::string& reportId, const std::string& reportName,
187     const ReportingType reportingType, Milliseconds interval,
188     const ReportUpdates reportUpdates, const uint64_t appendLimit,
189     const std::vector<LabeledMetricParameters>& readingParams)
190 {
191     namespace ts = utils::tstring;
192 
193     if (reports.size() >= maxReports)
194     {
195         throw sdbusplus::exception::SdBusError(
196             static_cast<int>(std::errc::too_many_files_open),
197             "Reached maximal report count");
198     }
199 
200     if (appendLimit > maxAppendLimit &&
201         appendLimit != std::numeric_limits<uint64_t>::max())
202     {
203         throw errors::InvalidArgument("AppendLimit", "Out of range.");
204     }
205 
206     if ((reportingType == ReportingType::periodic && interval < minInterval) ||
207         (reportingType != ReportingType::periodic &&
208          interval != Milliseconds{0}))
209     {
210         throw errors::InvalidArgument("Interval");
211     }
212 
213     size_t metricCount = 0;
214     for (auto metricParam : readingParams)
215     {
216         auto metricParamsVec =
217             metricParam.at_label<utils::tstring::SensorPath>();
218         metricCount += metricParamsVec.size();
219     }
220 
221     if (readingParams.size() > maxNumberMetrics ||
222         metricCount > maxNumberMetrics)
223     {
224         throw errors::InvalidArgument("MetricParams", "Too many.");
225     }
226 
227     verifyMetricParameters(readingParams);
228 
229     for (const LabeledMetricParameters& item : readingParams)
230     {
231         utils::toOperationType(
232             utils::toUnderlying(item.at_label<ts::OperationType>()));
233     }
234 }
235 
236 interfaces::Report& ReportManager::addReport(
237     boost::asio::yield_context& yield, const std::string& reportId,
238     const std::string& reportName, const ReportingType reportingType,
239     const std::vector<ReportAction>& reportActions, Milliseconds interval,
240     const uint64_t appendLimit, const ReportUpdates reportUpdates,
241     ReadingParameters metricParams, const bool enabled)
242 {
243     auto labeledMetricParams =
244         reportFactory->convertMetricParams(yield, metricParams);
245 
246     return addReport(reportId, reportName, reportingType, reportActions,
247                      interval, appendLimit, reportUpdates,
248                      std::move(labeledMetricParams), enabled, Readings{});
249 }
250 
251 interfaces::Report& ReportManager::addReport(
252     const std::string& reportId, const std::string& reportName,
253     const ReportingType reportingType,
254     const std::vector<ReportAction>& reportActions, Milliseconds interval,
255     const uint64_t appendLimit, const ReportUpdates reportUpdates,
256     std::vector<LabeledMetricParameters> labeledMetricParams,
257     const bool enabled, Readings readings)
258 {
259     const auto existingReportIds = utils::transform(
260         reports, [](const auto& report) { return report->getId(); });
261 
262     auto [id, name] = utils::makeIdName(reportId, reportName, reportNameDefault,
263                                         existingReportIds);
264 
265     verifyAddReport(id, name, reportingType, interval, reportUpdates,
266                     appendLimit, labeledMetricParams);
267 
268     reports.emplace_back(
269         reportFactory->make(id, name, reportingType, reportActions, interval,
270                             appendLimit, reportUpdates, *this, *reportStorage,
271                             labeledMetricParams, enabled, std::move(readings)));
272     return *reports.back();
273 }
274 
275 void ReportManager::loadFromPersistent()
276 {
277     std::vector<interfaces::JsonStorage::FilePath> paths =
278         reportStorage->list();
279 
280     for (const auto& path : paths)
281     {
282         std::optional<nlohmann::json> data = reportStorage->load(path);
283         try
284         {
285             size_t version = data->at("Version").get<size_t>();
286             if (version != Report::reportVersion)
287             {
288                 throw std::logic_error("Invalid version");
289             }
290             bool enabled = data->at("Enabled").get<bool>();
291             std::string& id = data->at("Id").get_ref<std::string&>();
292             std::string& name = data->at("Name").get_ref<std::string&>();
293 
294             uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
295             std::vector<ReportAction> reportActions = utils::transform(
296                 data->at("ReportActions").get<std::vector<uint32_t>>(),
297                 [](const auto reportAction) {
298                     return utils::toReportAction(reportAction);
299                 });
300             uint64_t interval = data->at("Interval").get<uint64_t>();
301             uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
302             uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
303             auto readingParameters =
304                 data->at("ReadingParameters")
305                     .get<std::vector<LabeledMetricParameters>>();
306 
307             Readings readings = {};
308 
309             if (auto it = data->find("MetricValues"); it != data->end())
310             {
311                 const auto labeledReadings = it->get<LabeledReadings>();
312                 readings = utils::toReadings(labeledReadings);
313             }
314 
315             addReport(id, name, utils::toReportingType(reportingType),
316                       reportActions, Milliseconds(interval), appendLimit,
317                       utils::toReportUpdates(reportUpdates),
318                       std::move(readingParameters), enabled,
319                       std::move(readings));
320         }
321         catch (const std::exception& e)
322         {
323             phosphor::logging::log<phosphor::logging::level::ERR>(
324                 "Failed to load report from storage",
325                 phosphor::logging::entry(
326                     "FILENAME=%s",
327                     static_cast<std::filesystem::path>(path).c_str()),
328                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
329             reportStorage->remove(path);
330         }
331     }
332 }
333