xref: /openbmc/telemetry/src/report_manager.cpp (revision 51497a0c878d939c9ed2a6ed30d12b465af4d51c)
1 #include "report_manager.hpp"
2 
3 #include "report.hpp"
4 #include "types/report_types.hpp"
5 #include "utils/conversion.hpp"
6 #include "utils/transform.hpp"
7 
8 #include <phosphor-logging/log.hpp>
9 #include <sdbusplus/exception.hpp>
10 
11 #include <stdexcept>
12 #include <system_error>
13 
14 ReadingParameters
15     convertToReadingParameters(ReadingParametersPastVersion params)
16 {
17     return utils::transform(params, [](const auto& param) {
18         using namespace std::chrono_literals;
19 
20         return ReadingParameters::value_type(
21             std::vector{{std::get<0>(param)}}, std::get<1>(param),
22             std::get<2>(param), std::get<3>(param),
23             utils::enumToString(CollectionTimeScope::point), 0u);
24     });
25 }
26 
27 ReportManager::ReportManager(
28     std::unique_ptr<interfaces::ReportFactory> reportFactoryIn,
29     std::unique_ptr<interfaces::JsonStorage> reportStorageIn,
30     const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn) :
31     reportFactory(std::move(reportFactoryIn)),
32     reportStorage(std::move(reportStorageIn)), objServer(objServerIn)
33 {
34     reports.reserve(maxReports);
35 
36     loadFromPersistent();
37 
38     reportManagerIface = objServer->add_unique_interface(
39         reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) {
40             dbusIface.register_property_r(
41                 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_,
42                 [](const auto&) { return maxReports; });
43             dbusIface.register_property_r(
44                 "MaxReportNameLength", size_t{},
45                 sdbusplus::vtable::property_::const_,
46                 [](const auto&) { return maxReportNameLength; });
47             dbusIface.register_property_r(
48                 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_,
49                 [](const auto&) -> uint64_t { return minInterval.count(); });
50 
51             dbusIface.register_method(
52                 "AddReport", [this](boost::asio::yield_context& yield,
53                                     const std::string& reportName,
54                                     const std::string& reportingType,
55                                     const bool emitsReadingsUpdate,
56                                     const bool logToMetricReportsCollection,
57                                     const uint64_t interval,
58                                     ReadingParametersPastVersion metricParams) {
59                     constexpr auto enabledDefault = true;
60                     constexpr uint64_t appendLimitDefault = 0;
61                     constexpr ReportUpdates reportUpdatesDefault =
62                         ReportUpdates::overwrite;
63 
64                     std::vector<ReportAction> reportActions;
65 
66                     if (emitsReadingsUpdate)
67                     {
68                         reportActions.emplace_back(
69                             ReportAction::emitsReadingsUpdate);
70                     }
71                     if (logToMetricReportsCollection)
72                     {
73                         reportActions.emplace_back(
74                             ReportAction::logToMetricReportsCollection);
75                     }
76 
77                     return addReport(yield, reportName,
78                                      utils::toReportingType(reportingType),
79                                      reportActions, Milliseconds(interval),
80                                      appendLimitDefault, reportUpdatesDefault,
81                                      convertToReadingParameters(
82                                          std::move(metricParams)),
83                                      enabledDefault)
84                         .getPath();
85                 });
86 
87             dbusIface.register_method(
88                 "AddReportFutureVersion",
89                 [this](boost::asio::yield_context& yield,
90                        const std::string& reportName,
91                        const std::string& reportingType,
92                        const std::string& reportUpdates,
93                        const uint64_t appendLimit,
94                        const std::vector<std::string>& reportActions,
95                        const uint64_t interval,
96                        ReadingParameters metricParams) {
97                     constexpr auto enabledDefault = true;
98                     return addReport(yield, reportName,
99                                      utils::toReportingType(reportingType),
100                                      utils::transform(
101                                          reportActions,
102                                          [](const auto& reportAction) {
103                                              return utils::toReportAction(
104                                                  reportAction);
105                                          }),
106                                      Milliseconds(interval), appendLimit,
107                                      utils::toReportUpdates(reportUpdates),
108                                      std::move(metricParams), enabledDefault)
109                         .getPath();
110                 });
111         });
112 }
113 
114 void ReportManager::removeReport(const interfaces::Report* report)
115 {
116     reports.erase(
117         std::remove_if(reports.begin(), reports.end(),
118                        [report](const auto& x) { return report == x.get(); }),
119         reports.end());
120 }
121 
122 void ReportManager::verifyReportNameLength(const std::string& reportName)
123 {
124     if (reportName.length() > maxReportNameLength)
125     {
126         throw sdbusplus::exception::SdBusError(
127             static_cast<int>(std::errc::invalid_argument),
128             "Report name exceeds maximum length");
129     }
130 }
131 
132 void ReportManager::verifyAddReport(
133     const std::string& reportName, const ReportingType reportingType,
134     Milliseconds interval, const ReportUpdates reportUpdates,
135     const std::vector<LabeledMetricParameters>& readingParams)
136 {
137     if (reportingType == ReportingType::onChange)
138     {
139         throw sdbusplus::exception::SdBusError(
140             static_cast<int>(std::errc::invalid_argument),
141             "Invalid reportingType");
142     }
143 
144     if (reports.size() >= maxReports)
145     {
146         throw sdbusplus::exception::SdBusError(
147             static_cast<int>(std::errc::too_many_files_open),
148             "Reached maximal report count");
149     }
150 
151     verifyReportNameLength(reportName);
152 
153     for (const auto& report : reports)
154     {
155         if (report->getName() == reportName)
156         {
157             throw sdbusplus::exception::SdBusError(
158                 static_cast<int>(std::errc::file_exists), "Duplicate report");
159         }
160     }
161 
162     verifyReportUpdates(reportUpdates);
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     if (readingParams.size() > maxReadingParams)
171 
172     {
173         throw sdbusplus::exception::SdBusError(
174             static_cast<int>(std::errc::argument_list_too_long),
175             "Too many reading parameters");
176     }
177 
178     try
179     {
180         namespace ts = utils::tstring;
181 
182         for (const LabeledMetricParameters& item : readingParams)
183         {
184             utils::toOperationType(
185                 utils::toUnderlying(item.at_label<ts::OperationType>()));
186         }
187     }
188     catch (const std::exception& e)
189     {
190         throw sdbusplus::exception::SdBusError(
191             static_cast<int>(std::errc::invalid_argument), e.what());
192     }
193 }
194 
195 interfaces::Report& ReportManager::addReport(
196     boost::asio::yield_context& yield, const std::string& reportName,
197     const ReportingType reportingType,
198     const std::vector<ReportAction>& reportActions, Milliseconds interval,
199     const uint64_t appendLimit, const ReportUpdates reportUpdates,
200     ReadingParameters metricParams, const bool enabled)
201 {
202     auto labeledMetricParams =
203         reportFactory->convertMetricParams(yield, metricParams);
204 
205     return addReport(reportName, reportingType, reportActions, interval,
206                      appendLimit, reportUpdates, std::move(labeledMetricParams),
207                      enabled);
208 }
209 
210 interfaces::Report& ReportManager::addReport(
211     const std::string& reportName, const ReportingType reportingType,
212     const std::vector<ReportAction>& reportActions, Milliseconds interval,
213     const uint64_t appendLimit, const ReportUpdates reportUpdates,
214     std::vector<LabeledMetricParameters> labeledMetricParams,
215     const bool enabled)
216 {
217     verifyAddReport(reportName, reportingType, interval, reportUpdates,
218                     labeledMetricParams);
219 
220     reports.emplace_back(reportFactory->make(
221         reportName, reportingType, reportActions, interval, appendLimit,
222         reportUpdates, *this, *reportStorage, labeledMetricParams, enabled));
223     return *reports.back();
224 }
225 
226 void ReportManager::loadFromPersistent()
227 {
228     std::vector<interfaces::JsonStorage::FilePath> paths =
229         reportStorage->list();
230 
231     for (const auto& path : paths)
232     {
233         std::optional<nlohmann::json> data = reportStorage->load(path);
234         try
235         {
236             bool enabled = data->at("Enabled").get<bool>();
237             size_t version = data->at("Version").get<size_t>();
238             if (version != Report::reportVersion)
239             {
240                 throw std::logic_error("Invalid version");
241             }
242             std::string& name = data->at("Name").get_ref<std::string&>();
243 
244             uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
245             std::vector<ReportAction> reportActions = utils::transform(
246                 data->at("ReportActions").get<std::vector<uint32_t>>(),
247                 [](const auto reportAction) {
248                     return utils::toReportAction(reportAction);
249                 });
250             uint64_t interval = data->at("Interval").get<uint64_t>();
251             uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
252             uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
253             auto readingParameters =
254                 data->at("ReadingParameters")
255                     .get<std::vector<LabeledMetricParameters>>();
256 
257             addReport(name, utils::toReportingType(reportingType),
258                       reportActions, Milliseconds(interval), appendLimit,
259                       utils::toReportUpdates(reportUpdates),
260                       std::move(readingParameters), enabled);
261         }
262         catch (const std::exception& e)
263         {
264             phosphor::logging::log<phosphor::logging::level::ERR>(
265                 "Failed to load report from storage",
266                 phosphor::logging::entry(
267                     "FILENAME=%s",
268                     static_cast<std::filesystem::path>(path).c_str()),
269                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
270             reportStorage->remove(path);
271         }
272     }
273 }
274 
275 void ReportManager::updateReport(const std::string& name)
276 {
277     for (auto& report : reports)
278     {
279         if (report->getName() == name)
280         {
281             report->updateReadings();
282             return;
283         }
284     }
285 }
286 
287 void ReportManager::verifyReportUpdates(const ReportUpdates reportUpdates)
288 {
289     if (std::find(supportedReportUpdates.begin(), supportedReportUpdates.end(),
290                   reportUpdates) == supportedReportUpdates.end())
291     {
292         throw sdbusplus::exception::SdBusError(
293             static_cast<int>(std::errc::invalid_argument),
294             "Invalid ReportUpdates");
295     }
296 }
297