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