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