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