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