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