xref: /openbmc/telemetry/src/report_manager.cpp (revision 1cdd7e4f)
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         (reportingType != ReportingType::periodic &&
159          interval != Milliseconds{0}))
160     {
161         throw sdbusplus::exception::SdBusError(
162             static_cast<int>(std::errc::invalid_argument), "Invalid interval");
163     }
164 
165     size_t metricCount = 0;
166     for (auto metricParam : readingParams)
167     {
168         auto metricParamsVec =
169             metricParam.at_label<utils::tstring::SensorPath>();
170         metricCount += metricParamsVec.size();
171     }
172 
173     if (readingParams.size() > maxNumberMetrics ||
174         metricCount > maxNumberMetrics)
175     {
176         throw sdbusplus::exception::SdBusError(
177             static_cast<int>(std::errc::argument_list_too_long),
178             "Too many reading parameters");
179     }
180 
181     try
182     {
183         namespace ts = utils::tstring;
184 
185         for (const LabeledMetricParameters& item : readingParams)
186         {
187             utils::toOperationType(
188                 utils::toUnderlying(item.at_label<ts::OperationType>()));
189         }
190     }
191     catch (const std::exception& e)
192     {
193         throw sdbusplus::exception::SdBusError(
194             static_cast<int>(std::errc::invalid_argument), e.what());
195     }
196 }
197 
198 interfaces::Report& ReportManager::addReport(
199     boost::asio::yield_context& yield, const std::string& reportId,
200     const std::string& reportName, const ReportingType reportingType,
201     const std::vector<ReportAction>& reportActions, Milliseconds interval,
202     const uint64_t appendLimit, const ReportUpdates reportUpdates,
203     ReadingParameters metricParams, const bool enabled)
204 {
205     auto labeledMetricParams =
206         reportFactory->convertMetricParams(yield, metricParams);
207 
208     return addReport(reportId, reportName, reportingType, reportActions,
209                      interval, appendLimit, reportUpdates,
210                      std::move(labeledMetricParams), enabled, Readings{});
211 }
212 
213 interfaces::Report& ReportManager::addReport(
214     const std::string& reportId, const std::string& reportName,
215     const ReportingType reportingType,
216     const std::vector<ReportAction>& reportActions, Milliseconds interval,
217     const uint64_t appendLimit, const ReportUpdates reportUpdates,
218     std::vector<LabeledMetricParameters> labeledMetricParams,
219     const bool enabled, Readings readings)
220 {
221     const auto existingReportIds = utils::transform(
222         reports, [](const auto& report) { return report->getId(); });
223 
224     auto [id, name] = utils::generateId(reportId, reportName, reportNameDefault,
225                                         existingReportIds, maxReportIdLength);
226 
227     verifyAddReport(id, name, reportingType, interval, reportUpdates,
228                     appendLimit, labeledMetricParams);
229 
230     reports.emplace_back(
231         reportFactory->make(id, name, reportingType, reportActions, interval,
232                             appendLimit, reportUpdates, *this, *reportStorage,
233                             labeledMetricParams, enabled, std::move(readings)));
234     return *reports.back();
235 }
236 
237 void ReportManager::loadFromPersistent()
238 {
239     std::vector<interfaces::JsonStorage::FilePath> paths =
240         reportStorage->list();
241 
242     for (const auto& path : paths)
243     {
244         std::optional<nlohmann::json> data = reportStorage->load(path);
245         try
246         {
247             size_t version = data->at("Version").get<size_t>();
248             if (version != Report::reportVersion)
249             {
250                 throw std::logic_error("Invalid version");
251             }
252             bool enabled = data->at("Enabled").get<bool>();
253             std::string& id = data->at("Id").get_ref<std::string&>();
254             std::string& name = data->at("Name").get_ref<std::string&>();
255 
256             uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
257             std::vector<ReportAction> reportActions = utils::transform(
258                 data->at("ReportActions").get<std::vector<uint32_t>>(),
259                 [](const auto reportAction) {
260                     return utils::toReportAction(reportAction);
261                 });
262             uint64_t interval = data->at("Interval").get<uint64_t>();
263             uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
264             uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
265             auto readingParameters =
266                 data->at("ReadingParameters")
267                     .get<std::vector<LabeledMetricParameters>>();
268 
269             Readings readings = {};
270 
271             if (auto it = data->find("MetricValues"); it != data->end())
272             {
273                 const auto labeledReadings = it->get<LabeledReadings>();
274                 readings = utils::toReadings(labeledReadings);
275             }
276 
277             addReport(id, name, utils::toReportingType(reportingType),
278                       reportActions, Milliseconds(interval), appendLimit,
279                       utils::toReportUpdates(reportUpdates),
280                       std::move(readingParameters), enabled,
281                       std::move(readings));
282         }
283         catch (const std::exception& e)
284         {
285             phosphor::logging::log<phosphor::logging::level::ERR>(
286                 "Failed to load report from storage",
287                 phosphor::logging::entry(
288                     "FILENAME=%s",
289                     static_cast<std::filesystem::path>(path).c_str()),
290                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
291             reportStorage->remove(path);
292         }
293     }
294 }
295