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