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