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