blob: d12e6aaa20a8a7a5e420a14187792b59103398f2 [file] [log] [blame]
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +02001#include "report_manager.hpp"
2
Wludzik, Jozefe2362792020-10-27 17:23:55 +01003#include "report.hpp"
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +00004#include "types/report_types.hpp"
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +00005#include "utils/conversion.hpp"
Szymon Dompke32305f12022-07-05 15:37:21 +02006#include "utils/dbus_path_utils.hpp"
7#include "utils/make_id_name.hpp"
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +00008#include "utils/transform.hpp"
Wludzik, Jozefe2362792020-10-27 17:23:55 +01009
10#include <phosphor-logging/log.hpp>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020011#include <sdbusplus/exception.hpp>
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +020012#include <sdbusplus/unpack_properties.hpp>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020013
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +020014#include <optional>
Wludzik, Jozefe2362792020-10-27 17:23:55 +010015#include <stdexcept>
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +020016#include <system_error>
17
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000018ReadingParameters
19 convertToReadingParameters(ReadingParametersPastVersion params)
20{
21 return utils::transform(params, [](const auto& param) {
22 using namespace std::chrono_literals;
23
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010024 const auto& [sensorPath, operationType, id, metadata] = param;
25
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000026 return ReadingParameters::value_type(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010027 std::vector<
28 std::tuple<sdbusplus::message::object_path, std::string>>{
29 {sensorPath, metadata}},
30 operationType, id, utils::enumToString(CollectionTimeScope::point),
31 0u);
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000032 });
33}
34
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +020035ReportManager::ReportManager(
Wludzik, Jozef2f9f9b82020-10-13 09:07:45 +020036 std::unique_ptr<interfaces::ReportFactory> reportFactoryIn,
Wludzik, Jozefe2362792020-10-27 17:23:55 +010037 std::unique_ptr<interfaces::JsonStorage> reportStorageIn,
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010038 const std::shared_ptr<sdbusplus::asio::object_server>& objServerIn) :
Wludzik, Jozef2f9f9b82020-10-13 09:07:45 +020039 reportFactory(std::move(reportFactoryIn)),
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010040 reportStorage(std::move(reportStorageIn)), objServer(objServerIn)
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +020041{
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020042 reports.reserve(maxReports);
Krzysztof Grobelny6ccfcbf2020-11-04 09:31:36 +010043
Wludzik, Jozefe2362792020-10-27 17:23:55 +010044 loadFromPersistent();
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +020045
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020046 reportManagerIface = objServer->add_unique_interface(
Wludzik, Jozef2f9f9b82020-10-13 09:07:45 +020047 reportManagerPath, reportManagerIfaceName, [this](auto& dbusIface) {
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020048 dbusIface.register_property_r(
Wludzik, Jozef503c1582020-12-11 14:48:01 +010049 "MaxReports", size_t{}, sdbusplus::vtable::property_::const_,
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020050 [](const auto&) { return maxReports; });
51 dbusIface.register_property_r(
52 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_,
53 [](const auto&) -> uint64_t { return minInterval.count(); });
Krzysztof Grobelny60fee072022-01-13 16:25:04 +010054 dbusIface.register_property_r(
55 "SupportedOperationTypes", std::vector<std::string>{},
56 sdbusplus::vtable::property_::const_,
57 [](const auto&) -> std::vector<std::string> {
Szymon Dompkefdb06a12022-02-11 11:04:44 +010058 return utils::transform<std::vector>(
Krzysztof Grobelny60fee072022-01-13 16:25:04 +010059 utils::convDataOperationType, [](const auto& item) {
60 return std::string(item.first);
61 });
62 });
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +020063
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020064 dbusIface.register_method(
Wludzik, Jozefe2362792020-10-27 17:23:55 +010065 "AddReport", [this](boost::asio::yield_context& yield,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010066 const std::string& reportId,
Wludzik, Jozef2f9f9b82020-10-13 09:07:45 +020067 const std::string& reportingType,
68 const bool emitsReadingsUpdate,
69 const bool logToMetricReportsCollection,
70 const uint64_t interval,
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000071 ReadingParametersPastVersion metricParams) {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020072 constexpr auto enabledDefault = true;
Szymon Dompke3eb56862021-09-20 15:32:04 +020073 constexpr ReportUpdates reportUpdatesDefault =
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010074 ReportUpdates::overwrite;
75
76 std::vector<ReportAction> reportActions;
77
78 if (emitsReadingsUpdate)
79 {
80 reportActions.emplace_back(
81 ReportAction::emitsReadingsUpdate);
82 }
83 if (logToMetricReportsCollection)
84 {
85 reportActions.emplace_back(
86 ReportAction::logToMetricReportsCollection);
87 }
88
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010089 return addReport(yield, reportId, reportId,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010090 utils::toReportingType(reportingType),
91 reportActions, Milliseconds(interval),
Krzysztof Grobelny18e71012022-11-02 13:17:01 +000092 maxAppendLimit, reportUpdatesDefault,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010093 convertToReadingParameters(
94 std::move(metricParams)),
95 enabledDefault)
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000096 .getPath();
97 });
98
99 dbusIface.register_method(
100 "AddReportFutureVersion",
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100101 [this](
102 boost::asio::yield_context& yield,
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200103 const std::vector<
104 std::pair<std::string, AddReportFutureVersionVariant>>&
105 properties) {
106 std::optional<std::string> reportId;
107 std::optional<std::string> reportName;
108 std::optional<std::string> reportingType;
109 std::optional<std::vector<std::string>> reportActions;
110 std::optional<uint64_t> interval;
111 std::optional<uint64_t> appendLimit;
112 std::optional<std::string> reportUpdates;
113 std::optional<ReadingParameters> metricParams;
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200114 std::optional<ReadingParameters> readingParameters;
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200115 std::optional<bool> enabled;
116
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200117 try
118 {
119 sdbusplus::unpackProperties(
120 properties, "Id", reportId, "Name", reportName,
121 "ReportingType", reportingType, "ReportActions",
122 reportActions, "Interval", interval, "AppendLimit",
123 appendLimit, "ReportUpdates", reportUpdates,
124 "MetricParams", metricParams, "Enabled", enabled,
125 "ReadingParameters", readingParameters);
126 }
127 catch (const sdbusplus::exception::UnpackPropertyError& e)
128 {
129 throw errors::InvalidArgument(e.propertyName);
130 }
131
132 if (readingParameters == std::nullopt)
133 {
134 readingParameters = metricParams;
135 }
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200136
137 return addReport(
138 yield, reportId.value_or(""),
139 reportName.value_or(""),
140 utils::toReportingType(
141 reportingType.value_or(utils::enumToString(
142 ReportingType::onRequest))),
143 utils::transform(
144 reportActions.value_or(
145 std::vector<std::string>{}),
146 [](const auto& reportAction) {
147 return utils::toReportAction(
148 reportAction);
149 }),
150 Milliseconds(interval.value_or(0)),
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200151 appendLimit.value_or(maxAppendLimit),
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200152 utils::toReportUpdates(
153 reportUpdates.value_or(utils::enumToString(
154 ReportUpdates::overwrite))),
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200155 readingParameters.value_or(ReadingParameters{}),
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200156 enabled.value_or(true))
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000157 .getPath();
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200158 });
159 });
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +0200160}
161
Wludzik, Jozef2f9f9b82020-10-13 09:07:45 +0200162void ReportManager::removeReport(const interfaces::Report* report)
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +0200163{
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200164 reports.erase(
165 std::remove_if(reports.begin(), reports.end(),
166 [report](const auto& x) { return report == x.get(); }),
167 reports.end());
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +0200168}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100169
Szymon Dompke32305f12022-07-05 15:37:21 +0200170void ReportManager::verifyMetricParameters(
171 const std::vector<LabeledMetricParameters>& readingParams)
172{
173 namespace ts = utils::tstring;
174
175 for (auto readingParam : readingParams)
176 {
177 if (readingParam.at_label<ts::Id>().length() >
178 utils::constants::maxIdNameLength)
179 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200180 throw errors::InvalidArgument("ReadingParameters.Id", "Too long.");
Szymon Dompke32305f12022-07-05 15:37:21 +0200181 }
182 }
183}
184
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000185void ReportManager::verifyAddReport(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100186 const std::string& reportId, const std::string& reportName,
187 const ReportingType reportingType, Milliseconds interval,
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000188 const ReportUpdates reportUpdates, const uint64_t appendLimit,
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000189 const std::vector<LabeledMetricParameters>& readingParams)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100190{
Szymon Dompke32305f12022-07-05 15:37:21 +0200191 namespace ts = utils::tstring;
192
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100193 if (reports.size() >= maxReports)
194 {
195 throw sdbusplus::exception::SdBusError(
196 static_cast<int>(std::errc::too_many_files_open),
197 "Reached maximal report count");
198 }
199
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100200 if (appendLimit > maxAppendLimit &&
201 appendLimit != std::numeric_limits<uint64_t>::max())
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000202 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200203 throw errors::InvalidArgument("AppendLimit", "Out of range.");
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000204 }
205
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200206 if ((reportingType == ReportingType::periodic && interval < minInterval) ||
207 (reportingType != ReportingType::periodic &&
208 interval != Milliseconds{0}))
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100209 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200210 throw errors::InvalidArgument("Interval");
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100211 }
Wludzik, Jozefbc766b42020-12-08 16:06:22 +0100212
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000213 size_t metricCount = 0;
214 for (auto metricParam : readingParams)
215 {
216 auto metricParamsVec =
217 metricParam.at_label<utils::tstring::SensorPath>();
218 metricCount += metricParamsVec.size();
219 }
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000220
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000221 if (readingParams.size() > maxNumberMetrics ||
222 metricCount > maxNumberMetrics)
Wludzik, Jozefbc766b42020-12-08 16:06:22 +0100223 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200224 throw errors::InvalidArgument("MetricParams", "Too many.");
Wludzik, Jozefbc766b42020-12-08 16:06:22 +0100225 }
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000226
Szymon Dompke32305f12022-07-05 15:37:21 +0200227 verifyMetricParameters(readingParams);
228
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200229 for (const LabeledMetricParameters& item : readingParams)
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000230 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200231 utils::toOperationType(
232 utils::toUnderlying(item.at_label<ts::OperationType>()));
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000233 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000234}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100235
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000236interfaces::Report& ReportManager::addReport(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100237 boost::asio::yield_context& yield, const std::string& reportId,
238 const std::string& reportName, const ReportingType reportingType,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100239 const std::vector<ReportAction>& reportActions, Milliseconds interval,
240 const uint64_t appendLimit, const ReportUpdates reportUpdates,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200241 ReadingParameters metricParams, const bool enabled)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000242{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000243 auto labeledMetricParams =
244 reportFactory->convertMetricParams(yield, metricParams);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000245
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100246 return addReport(reportId, reportName, reportingType, reportActions,
247 interval, appendLimit, reportUpdates,
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100248 std::move(labeledMetricParams), enabled, Readings{});
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000249}
250
251interfaces::Report& ReportManager::addReport(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100252 const std::string& reportId, const std::string& reportName,
253 const ReportingType reportingType,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100254 const std::vector<ReportAction>& reportActions, Milliseconds interval,
255 const uint64_t appendLimit, const ReportUpdates reportUpdates,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200256 std::vector<LabeledMetricParameters> labeledMetricParams,
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100257 const bool enabled, Readings readings)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000258{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100259 const auto existingReportIds = utils::transform(
260 reports, [](const auto& report) { return report->getId(); });
261
Szymon Dompke32305f12022-07-05 15:37:21 +0200262 auto [id, name] = utils::makeIdName(reportId, reportName, reportNameDefault,
263 existingReportIds);
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100264
265 verifyAddReport(id, name, reportingType, interval, reportUpdates,
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000266 appendLimit, labeledMetricParams);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000267
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100268 reports.emplace_back(
269 reportFactory->make(id, name, reportingType, reportActions, interval,
270 appendLimit, reportUpdates, *this, *reportStorage,
271 labeledMetricParams, enabled, std::move(readings)));
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000272 return *reports.back();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100273}
274
275void ReportManager::loadFromPersistent()
276{
277 std::vector<interfaces::JsonStorage::FilePath> paths =
278 reportStorage->list();
279
280 for (const auto& path : paths)
281 {
282 std::optional<nlohmann::json> data = reportStorage->load(path);
283 try
284 {
285 size_t version = data->at("Version").get<size_t>();
286 if (version != Report::reportVersion)
287 {
288 throw std::logic_error("Invalid version");
289 }
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100290 bool enabled = data->at("Enabled").get<bool>();
291 std::string& id = data->at("Id").get_ref<std::string&>();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100292 std::string& name = data->at("Name").get_ref<std::string&>();
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100293
294 uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
295 std::vector<ReportAction> reportActions = utils::transform(
296 data->at("ReportActions").get<std::vector<uint32_t>>(),
297 [](const auto reportAction) {
298 return utils::toReportAction(reportAction);
299 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100300 uint64_t interval = data->at("Interval").get<uint64_t>();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200301 uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100302 uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000303 auto readingParameters =
304 data->at("ReadingParameters")
305 .get<std::vector<LabeledMetricParameters>>();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100306
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100307 Readings readings = {};
308
309 if (auto it = data->find("MetricValues"); it != data->end())
310 {
311 const auto labeledReadings = it->get<LabeledReadings>();
312 readings = utils::toReadings(labeledReadings);
313 }
314
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100315 addReport(id, name, utils::toReportingType(reportingType),
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100316 reportActions, Milliseconds(interval), appendLimit,
317 utils::toReportUpdates(reportUpdates),
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100318 std::move(readingParameters), enabled,
319 std::move(readings));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100320 }
321 catch (const std::exception& e)
322 {
323 phosphor::logging::log<phosphor::logging::level::ERR>(
324 "Failed to load report from storage",
325 phosphor::logging::entry(
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100326 "FILENAME=%s",
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100327 static_cast<std::filesystem::path>(path).c_str()),
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100328 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100329 reportStorage->remove(path);
330 }
331 }
332}