xref: /openbmc/telemetry/src/report_manager.cpp (revision e28aa53dc1492f09a64dc9f1dbfd5b6dba06e31f)
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                     return addReport(yield, reportName, reportingType,
61                                      emitsReadingsUpdate,
62                                      logToMetricReportsCollection,
63                                      Milliseconds(interval),
64                                      convertToReadingParameters(
65                                          std::move(metricParams)),
66                                      enabledDefault)
67                         .getPath();
68                 });
69 
70             dbusIface.register_method(
71                 "AddReportFutureVersion",
72                 [this](boost::asio::yield_context& yield,
73                        const std::string& reportName,
74                        const std::string& reportingType,
75                        const bool emitsReadingsUpdate,
76                        const bool logToMetricReportsCollection,
77                        const uint64_t interval,
78                        ReadingParameters metricParams) {
79                     constexpr auto enabledDefault = true;
80                     return addReport(yield, reportName, reportingType,
81                                      emitsReadingsUpdate,
82                                      logToMetricReportsCollection,
83                                      Milliseconds(interval),
84                                      std::move(metricParams), enabledDefault)
85                         .getPath();
86                 });
87         });
88 }
89 
90 void ReportManager::removeReport(const interfaces::Report* report)
91 {
92     reports.erase(
93         std::remove_if(reports.begin(), reports.end(),
94                        [report](const auto& x) { return report == x.get(); }),
95         reports.end());
96 }
97 
98 void ReportManager::verifyReportNameLength(const std::string& reportName)
99 {
100     if (reportName.length() > maxReportNameLength)
101     {
102         throw sdbusplus::exception::SdBusError(
103             static_cast<int>(std::errc::invalid_argument),
104             "Report name exceeds maximum length");
105     }
106 }
107 
108 void ReportManager::verifyAddReport(
109     const std::string& reportName, const std::string& reportingType,
110     Milliseconds interval,
111     const std::vector<LabeledMetricParameters>& readingParams)
112 {
113     if (reports.size() >= maxReports)
114     {
115         throw sdbusplus::exception::SdBusError(
116             static_cast<int>(std::errc::too_many_files_open),
117             "Reached maximal report count");
118     }
119 
120     verifyReportNameLength(reportName);
121 
122     for (const auto& report : reports)
123     {
124         if (report->getName() == reportName)
125         {
126             throw sdbusplus::exception::SdBusError(
127                 static_cast<int>(std::errc::file_exists), "Duplicate report");
128         }
129     }
130 
131     auto found = std::find(supportedReportingType.begin(),
132                            supportedReportingType.end(), reportingType);
133     if (found == supportedReportingType.end())
134     {
135         throw sdbusplus::exception::SdBusError(
136             static_cast<int>(std::errc::invalid_argument),
137             "Invalid reportingType");
138     }
139 
140     if (reportingType == "Periodic" && interval < minInterval)
141     {
142         throw sdbusplus::exception::SdBusError(
143             static_cast<int>(std::errc::invalid_argument), "Invalid interval");
144     }
145 
146     if (readingParams.size() > maxReadingParams)
147 
148     {
149         throw sdbusplus::exception::SdBusError(
150             static_cast<int>(std::errc::argument_list_too_long),
151             "Too many reading parameters");
152     }
153 
154     try
155     {
156         namespace ts = utils::tstring;
157 
158         for (const LabeledMetricParameters& item : readingParams)
159         {
160             utils::toOperationType(
161                 utils::toUnderlying(item.at_label<ts::OperationType>()));
162         }
163     }
164     catch (const std::exception& e)
165     {
166         throw sdbusplus::exception::SdBusError(
167             static_cast<int>(std::errc::invalid_argument), e.what());
168     }
169 }
170 
171 interfaces::Report& ReportManager::addReport(
172     boost::asio::yield_context& yield, const std::string& reportName,
173     const std::string& reportingType, const bool emitsReadingsUpdate,
174     const bool logToMetricReportsCollection, Milliseconds interval,
175     ReadingParameters metricParams, const bool enabled)
176 {
177     auto labeledMetricParams =
178         reportFactory->convertMetricParams(yield, metricParams);
179 
180     return addReport(reportName, reportingType, emitsReadingsUpdate,
181                      logToMetricReportsCollection, interval,
182                      std::move(labeledMetricParams), enabled);
183 }
184 
185 interfaces::Report& ReportManager::addReport(
186     const std::string& reportName, const std::string& reportingType,
187     const bool emitsReadingsUpdate, const bool logToMetricReportsCollection,
188     Milliseconds interval,
189     std::vector<LabeledMetricParameters> labeledMetricParams,
190     const bool enabled)
191 {
192     verifyAddReport(reportName, reportingType, interval, labeledMetricParams);
193 
194     reports.emplace_back(
195         reportFactory->make(reportName, reportingType, emitsReadingsUpdate,
196                             logToMetricReportsCollection, interval, *this,
197                             *reportStorage, labeledMetricParams, enabled));
198     return *reports.back();
199 }
200 
201 void ReportManager::loadFromPersistent()
202 {
203     std::vector<interfaces::JsonStorage::FilePath> paths =
204         reportStorage->list();
205 
206     for (const auto& path : paths)
207     {
208         std::optional<nlohmann::json> data = reportStorage->load(path);
209         try
210         {
211             bool enabled = data->at("Enabled").get<bool>();
212             size_t version = data->at("Version").get<size_t>();
213             if (version != Report::reportVersion)
214             {
215                 throw std::logic_error("Invalid version");
216             }
217             std::string& name = data->at("Name").get_ref<std::string&>();
218             std::string& reportingType =
219                 data->at("ReportingType").get_ref<std::string&>();
220             bool emitsReadingsSignal =
221                 data->at("EmitsReadingsUpdate").get<bool>();
222             bool logToMetricReportsCollection =
223                 data->at("LogToMetricReportsCollection").get<bool>();
224             uint64_t interval = data->at("Interval").get<uint64_t>();
225             auto readingParameters =
226                 data->at("ReadingParameters")
227                     .get<std::vector<LabeledMetricParameters>>();
228 
229             addReport(name, reportingType, emitsReadingsSignal,
230                       logToMetricReportsCollection, Milliseconds(interval),
231                       std::move(readingParameters), enabled);
232         }
233         catch (const std::exception& e)
234         {
235             phosphor::logging::log<phosphor::logging::level::ERR>(
236                 "Failed to load report from storage",
237                 phosphor::logging::entry(
238                     "FILENAME=%s",
239                     static_cast<std::filesystem::path>(path).c_str()),
240                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
241             reportStorage->remove(path);
242         }
243     }
244 }
245 
246 void ReportManager::updateReport(const std::string& name)
247 {
248     for (auto& report : reports)
249     {
250         if (report->getName() == name)
251         {
252             report->updateReadings();
253             return;
254         }
255     }
256 }
257