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