xref: /openbmc/telemetry/src/report_manager.cpp (revision 583ba441654657bb4ba9d051b747144a7258c159)
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 
ReportManager(std::unique_ptr<interfaces::ReportFactory> reportFactoryIn,std::unique_ptr<interfaces::JsonStorage> reportStorageIn,const std::shared_ptr<sdbusplus::asio::object_server> & objServerIn)18 ReportManager::ReportManager(
19     std::unique_ptr<interfaces::ReportFactory> reportFactoryIn,
20     std::unique_ptr<interfaces::JsonStorage> reportStorageIn,
21     const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn) :
22     reportFactory(std::move(reportFactoryIn)),
23     reportStorage(std::move(reportStorageIn)), objServer(objServerIn)
24 {
25     reports.reserve(maxReports);
26 
27     loadFromPersistent();
28 
29     reportManagerIface = objServer->add_unique_interface(
30         reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) {
31             dbusIface.register_property_r(
32                 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_,
33                 [](const auto&) { return maxReports; });
34             dbusIface.register_property_r(
35                 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_,
36                 [](const auto&) -> uint64_t { return minInterval.count(); });
37             dbusIface.register_property_r(
38                 "SupportedOperationTypes", std::vector<std::string>{},
39                 sdbusplus::vtable::property_::const_,
40                 [](const auto&) -> std::vector<std::string> {
41                     return utils::transform<std::vector>(
42                         utils::convDataOperationType, [](const auto& item) {
43                             return std::string(item.first);
44                         });
45                 });
46             dbusIface.register_method(
47                 "AddReport",
48                 [this](boost::asio::yield_context& yield, std::string reportId,
49                        std::string reportName, std::string reportingType,
50                        std::string reportUpdates, uint64_t appendLimit,
51                        std::vector<std::string> reportActions,
52                        uint64_t interval, ReadingParameters readingParameters,
53                        bool enabled) {
54                     if (reportingType.empty())
55                     {
56                         reportingType =
57                             utils::enumToString(ReportingType::onRequest);
58                     }
59 
60                     if (reportUpdates.empty())
61                     {
62                         reportUpdates =
63                             utils::enumToString(ReportUpdates::overwrite);
64                     }
65 
66                     if (appendLimit == std::numeric_limits<uint64_t>::max())
67                     {
68                         appendLimit = maxAppendLimit;
69                     }
70 
71                     if (interval == std::numeric_limits<uint64_t>::max())
72                     {
73                         interval = 0;
74                     }
75 
76                     return addReport(yield, reportId, reportName,
77                                      utils::toReportingType(reportingType),
78                                      utils::transform(
79                                          reportActions,
80                                          [](const auto& reportAction) {
81                                              return utils::toReportAction(
82                                                  reportAction);
83                                          }),
84                                      Milliseconds(interval), appendLimit,
85                                      utils::toReportUpdates(reportUpdates),
86                                      readingParameters, enabled)
87                         .getPath();
88                 });
89         });
90 }
91 
removeReport(const interfaces::Report * report)92 void ReportManager::removeReport(const interfaces::Report* report)
93 {
94     reports.erase(
95         std::remove_if(reports.begin(), reports.end(),
96                        [report](const auto& x) { return report == x.get(); }),
97         reports.end());
98 }
99 
verifyAddReport(const std::string & reportId,const std::string & reportName,const ReportingType reportingType,Milliseconds interval,const ReportUpdates reportUpdates,const uint64_t appendLimit,const std::vector<LabeledMetricParameters> & readingParams)100 void ReportManager::verifyAddReport(
101     const std::string& reportId, const std::string& reportName,
102     const ReportingType reportingType, Milliseconds interval,
103     const ReportUpdates reportUpdates, const uint64_t appendLimit,
104     const std::vector<LabeledMetricParameters>& readingParams)
105 {
106     namespace ts = utils::tstring;
107 
108     if (reports.size() >= maxReports)
109     {
110         throw sdbusplus::exception::SdBusError(
111             static_cast<int>(std::errc::too_many_files_open),
112             "Reached maximal report count");
113     }
114 
115     if (appendLimit > maxAppendLimit &&
116         appendLimit != std::numeric_limits<uint64_t>::max())
117     {
118         throw errors::InvalidArgument("AppendLimit", "Out of range.");
119     }
120 
121     if ((reportingType == ReportingType::periodic && interval < minInterval) ||
122         (reportingType != ReportingType::periodic &&
123          interval != Milliseconds{0}))
124     {
125         throw errors::InvalidArgument("Interval");
126     }
127 
128     verifyMetricParams(readingParams);
129 
130     for (const LabeledMetricParameters& item : readingParams)
131     {
132         utils::toOperationType(
133             utils::toUnderlying(item.at_label<ts::OperationType>()));
134     }
135 }
136 
addReport(boost::asio::yield_context & yield,const std::string & reportId,const std::string & reportName,const ReportingType reportingType,const std::vector<ReportAction> & reportActions,Milliseconds interval,const uint64_t appendLimit,const ReportUpdates reportUpdates,ReadingParameters metricParams,const bool enabled)137 interfaces::Report& ReportManager::addReport(
138     boost::asio::yield_context& yield, const std::string& reportId,
139     const std::string& reportName, const ReportingType reportingType,
140     const std::vector<ReportAction>& reportActions, Milliseconds interval,
141     const uint64_t appendLimit, const ReportUpdates reportUpdates,
142     ReadingParameters metricParams, const bool enabled)
143 {
144     auto labeledMetricParams =
145         reportFactory->convertMetricParams(yield, metricParams);
146 
147     return addReport(reportId, reportName, reportingType, reportActions,
148                      interval, appendLimit, reportUpdates,
149                      std::move(labeledMetricParams), enabled, Readings{});
150 }
151 
addReport(const std::string & reportId,const std::string & reportName,const ReportingType reportingType,const std::vector<ReportAction> & reportActions,Milliseconds interval,const uint64_t appendLimit,const ReportUpdates reportUpdates,std::vector<LabeledMetricParameters> labeledMetricParams,const bool enabled,Readings readings)152 interfaces::Report& ReportManager::addReport(
153     const std::string& reportId, const std::string& reportName,
154     const ReportingType reportingType,
155     const std::vector<ReportAction>& reportActions, Milliseconds interval,
156     const uint64_t appendLimit, const ReportUpdates reportUpdates,
157     std::vector<LabeledMetricParameters> labeledMetricParams,
158     const bool enabled, Readings readings)
159 {
160     const auto existingReportIds = utils::transform(
161         reports, [](const auto& report) { return report->getId(); });
162 
163     auto [id, name] = utils::makeIdName(reportId, reportName, reportNameDefault,
164                                         existingReportIds);
165 
166     verifyAddReport(id, name, reportingType, interval, reportUpdates,
167                     appendLimit, labeledMetricParams);
168 
169     reports.emplace_back(
170         reportFactory->make(id, name, reportingType, reportActions, interval,
171                             appendLimit, reportUpdates, *this, *reportStorage,
172                             labeledMetricParams, enabled, std::move(readings)));
173     return *reports.back();
174 }
175 
loadFromPersistent()176 void ReportManager::loadFromPersistent()
177 {
178     std::vector<interfaces::JsonStorage::FilePath> paths =
179         reportStorage->list();
180 
181     for (const auto& path : paths)
182     {
183         std::optional<nlohmann::json> data = reportStorage->load(path);
184         try
185         {
186             size_t version = data->at("Version").get<size_t>();
187             if (version != Report::reportVersion)
188             {
189                 throw std::logic_error("Invalid version");
190             }
191             bool enabled = data->at("Enabled").get<bool>();
192             std::string& id = data->at("Id").get_ref<std::string&>();
193             std::string& name = data->at("Name").get_ref<std::string&>();
194 
195             uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
196             std::vector<ReportAction> reportActions = utils::transform(
197                 data->at("ReportActions").get<std::vector<uint32_t>>(),
198                 [](const auto reportAction) {
199                     return utils::toReportAction(reportAction);
200                 });
201             uint64_t interval = data->at("Interval").get<uint64_t>();
202             uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
203             uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
204             auto readingParameters =
205                 data->at("ReadingParameters")
206                     .get<std::vector<LabeledMetricParameters>>();
207 
208             Readings readings = {};
209 
210             if (auto it = data->find("MetricValues"); it != data->end())
211             {
212                 const auto labeledReadings = it->get<LabeledReadings>();
213                 readings = utils::toReadings(labeledReadings);
214             }
215 
216             addReport(id, name, utils::toReportingType(reportingType),
217                       reportActions, Milliseconds(interval), appendLimit,
218                       utils::toReportUpdates(reportUpdates),
219                       std::move(readingParameters), enabled,
220                       std::move(readings));
221         }
222         catch (const std::exception& e)
223         {
224             phosphor::logging::log<phosphor::logging::level::ERR>(
225                 "Failed to load report from storage",
226                 phosphor::logging::entry(
227                     "FILENAME=%s",
228                     static_cast<std::filesystem::path>(path).c_str()),
229                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
230             reportStorage->remove(path);
231         }
232     }
233 }
234 
verifyMetricParams(const std::vector<LabeledMetricParameters> & metricParams)235 void ReportManager::verifyMetricParams(
236     const std::vector<LabeledMetricParameters>& metricParams)
237 {
238     size_t metricCount = 0;
239     for (const auto& metricParam : metricParams)
240     {
241         auto metricParamsVec =
242             metricParam.at_label<utils::tstring::SensorPath>();
243         metricCount += metricParamsVec.size();
244     }
245 
246     if (metricParams.size() > ReportManager::maxNumberMetrics ||
247         metricCount > ReportManager::maxNumberMetrics)
248     {
249         throw errors::InvalidArgument("ReadingParameters", "Too many");
250     }
251 }
252