xref: /openbmc/telemetry/src/report_manager.cpp (revision b8cc78ddf9cc87c83176c7bda575ceef2678d00f)
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 
56             dbusIface.register_method(
57                 "AddReport", [this](boost::asio::yield_context& yield,
58                                     const std::string& reportId,
59                                     const std::string& reportingType,
60                                     const bool emitsReadingsUpdate,
61                                     const bool logToMetricReportsCollection,
62                                     const uint64_t interval,
63                                     ReadingParametersPastVersion metricParams) {
64                     constexpr auto enabledDefault = true;
65                     constexpr uint64_t appendLimitDefault = 0;
66                     constexpr ReportUpdates reportUpdatesDefault =
67                         ReportUpdates::overwrite;
68 
69                     std::vector<ReportAction> reportActions;
70 
71                     if (emitsReadingsUpdate)
72                     {
73                         reportActions.emplace_back(
74                             ReportAction::emitsReadingsUpdate);
75                     }
76                     if (logToMetricReportsCollection)
77                     {
78                         reportActions.emplace_back(
79                             ReportAction::logToMetricReportsCollection);
80                     }
81 
82                     return addReport(yield, reportId, reportId,
83                                      utils::toReportingType(reportingType),
84                                      reportActions, Milliseconds(interval),
85                                      appendLimitDefault, reportUpdatesDefault,
86                                      convertToReadingParameters(
87                                          std::move(metricParams)),
88                                      enabledDefault)
89                         .getPath();
90                 });
91 
92             dbusIface.register_method(
93                 "AddReportFutureVersion",
94                 [this](
95                     boost::asio::yield_context& yield,
96                     const std::string& reportId, const std::string& reportName,
97                     const std::string& reportingType,
98                     const std::string& reportUpdates,
99                     const uint64_t appendLimit,
100                     const std::vector<std::string>& reportActions,
101                     const uint64_t interval, ReadingParameters metricParams) {
102                     constexpr auto enabledDefault = true;
103                     return addReport(yield, reportId, reportName,
104                                      utils::toReportingType(reportingType),
105                                      utils::transform(
106                                          reportActions,
107                                          [](const auto& reportAction) {
108                                              return utils::toReportAction(
109                                                  reportAction);
110                                          }),
111                                      Milliseconds(interval), appendLimit,
112                                      utils::toReportUpdates(reportUpdates),
113                                      std::move(metricParams), enabledDefault)
114                         .getPath();
115                 });
116         });
117 }
118 
119 void ReportManager::removeReport(const interfaces::Report* report)
120 {
121     reports.erase(
122         std::remove_if(reports.begin(), reports.end(),
123                        [report](const auto& x) { return report == x.get(); }),
124         reports.end());
125 }
126 
127 void ReportManager::verifyReportIdLength(const std::string& reportId)
128 {
129     if (reportId.length() > maxReportIdLength)
130     {
131         throw sdbusplus::exception::SdBusError(
132             static_cast<int>(std::errc::invalid_argument),
133             "Report id exceeds maximum length");
134     }
135 }
136 
137 void ReportManager::verifyAddReport(
138     const std::string& reportId, const std::string& reportName,
139     const ReportingType reportingType, Milliseconds interval,
140     const ReportUpdates reportUpdates,
141     const std::vector<LabeledMetricParameters>& readingParams)
142 {
143     if (reportingType == ReportingType::onChange)
144     {
145         throw sdbusplus::exception::SdBusError(
146             static_cast<int>(std::errc::invalid_argument),
147             "Invalid reportingType");
148     }
149 
150     if (reports.size() >= maxReports)
151     {
152         throw sdbusplus::exception::SdBusError(
153             static_cast<int>(std::errc::too_many_files_open),
154             "Reached maximal report count");
155     }
156 
157     verifyReportIdLength(reportId);
158     utils::verifyIdCharacters(reportId);
159 
160     for (const auto& report : reports)
161     {
162         if (report->getId() == reportId)
163         {
164             throw sdbusplus::exception::SdBusError(
165                 static_cast<int>(std::errc::file_exists), "Duplicate report");
166         }
167     }
168 
169     verifyReportUpdates(reportUpdates);
170 
171     if (reportingType == ReportingType::periodic && interval < minInterval)
172     {
173         throw sdbusplus::exception::SdBusError(
174             static_cast<int>(std::errc::invalid_argument), "Invalid interval");
175     }
176 
177     if (readingParams.size() > maxReadingParams)
178 
179     {
180         throw sdbusplus::exception::SdBusError(
181             static_cast<int>(std::errc::argument_list_too_long),
182             "Too many reading parameters");
183     }
184 
185     try
186     {
187         namespace ts = utils::tstring;
188 
189         for (const LabeledMetricParameters& item : readingParams)
190         {
191             utils::toOperationType(
192                 utils::toUnderlying(item.at_label<ts::OperationType>()));
193         }
194     }
195     catch (const std::exception& e)
196     {
197         throw sdbusplus::exception::SdBusError(
198             static_cast<int>(std::errc::invalid_argument), e.what());
199     }
200 }
201 
202 interfaces::Report& ReportManager::addReport(
203     boost::asio::yield_context& yield, const std::string& reportId,
204     const std::string& reportName, const ReportingType reportingType,
205     const std::vector<ReportAction>& reportActions, Milliseconds interval,
206     const uint64_t appendLimit, const ReportUpdates reportUpdates,
207     ReadingParameters metricParams, const bool enabled)
208 {
209     auto labeledMetricParams =
210         reportFactory->convertMetricParams(yield, metricParams);
211 
212     return addReport(reportId, reportName, reportingType, reportActions,
213                      interval, appendLimit, reportUpdates,
214                      std::move(labeledMetricParams), enabled);
215 }
216 
217 interfaces::Report& ReportManager::addReport(
218     const std::string& reportId, const std::string& reportName,
219     const ReportingType reportingType,
220     const std::vector<ReportAction>& reportActions, Milliseconds interval,
221     const uint64_t appendLimit, const ReportUpdates reportUpdates,
222     std::vector<LabeledMetricParameters> labeledMetricParams,
223     const bool enabled)
224 {
225     std::string name = reportName;
226     if (name.empty())
227     {
228         name = "Report";
229     }
230 
231     const auto existingReportIds = utils::transform(
232         reports, [](const auto& report) { return report->getId(); });
233 
234     std::string id =
235         utils::generateId(reportId, name, existingReportIds, maxReportIdLength);
236 
237     verifyAddReport(id, name, reportingType, interval, reportUpdates,
238                     labeledMetricParams);
239 
240     reports.emplace_back(reportFactory->make(
241         id, name, reportingType, reportActions, interval, appendLimit,
242         reportUpdates, *this, *reportStorage, labeledMetricParams, enabled));
243     return *reports.back();
244 }
245 
246 void ReportManager::loadFromPersistent()
247 {
248     std::vector<interfaces::JsonStorage::FilePath> paths =
249         reportStorage->list();
250 
251     for (const auto& path : paths)
252     {
253         std::optional<nlohmann::json> data = reportStorage->load(path);
254         try
255         {
256             size_t version = data->at("Version").get<size_t>();
257             if (version != Report::reportVersion)
258             {
259                 throw std::logic_error("Invalid version");
260             }
261             bool enabled = data->at("Enabled").get<bool>();
262             std::string& id = data->at("Id").get_ref<std::string&>();
263             std::string& name = data->at("Name").get_ref<std::string&>();
264 
265             uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
266             std::vector<ReportAction> reportActions = utils::transform(
267                 data->at("ReportActions").get<std::vector<uint32_t>>(),
268                 [](const auto reportAction) {
269                     return utils::toReportAction(reportAction);
270                 });
271             uint64_t interval = data->at("Interval").get<uint64_t>();
272             uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
273             uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
274             auto readingParameters =
275                 data->at("ReadingParameters")
276                     .get<std::vector<LabeledMetricParameters>>();
277 
278             addReport(id, name, utils::toReportingType(reportingType),
279                       reportActions, Milliseconds(interval), appendLimit,
280                       utils::toReportUpdates(reportUpdates),
281                       std::move(readingParameters), enabled);
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 
296 void ReportManager::updateReport(const std::string& id)
297 {
298     for (auto& report : reports)
299     {
300         if (report->getId() == id)
301         {
302             report->updateReadings();
303             return;
304         }
305     }
306 }
307 
308 void ReportManager::verifyReportUpdates(const ReportUpdates reportUpdates)
309 {
310     if (std::find(supportedReportUpdates.begin(), supportedReportUpdates.end(),
311                   reportUpdates) == supportedReportUpdates.end())
312     {
313         throw sdbusplus::exception::SdBusError(
314             static_cast<int>(std::errc::invalid_argument),
315             "Invalid ReportUpdates");
316     }
317 }
318