blob: 69a110c9156694503c183d97dbac079af676fcb9 [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(
Patrick Williams3a1c2972023-05-10 07:51:04 -050047 reportManagerPath, reportManagerIfaceName,
48 [this](auto& dbusIface) {
49 dbusIface.register_property_r("MaxReports", size_t{},
50 sdbusplus::vtable::property_::const_,
51 [](const auto&) { return maxReports; });
52 dbusIface.register_property_r(
53 "MinInterval", uint64_t{}, sdbusplus::vtable::property_::const_,
54 [](const auto&) -> uint64_t { return minInterval.count(); });
55 dbusIface.register_property_r(
56 "SupportedOperationTypes", std::vector<std::string>{},
57 sdbusplus::vtable::property_::const_,
58 [](const auto&) -> std::vector<std::string> {
59 return utils::transform<std::vector>(
60 utils::convDataOperationType,
61 [](const auto& item) { return std::string(item.first); });
62 });
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +020063
Patrick Williams3a1c2972023-05-10 07:51:04 -050064 dbusIface.register_method(
65 "AddReport",
66 [this](boost::asio::yield_context& yield,
67 const std::string& reportId,
68 const std::string& reportingType,
69 const bool emitsReadingsUpdate,
70 const bool logToMetricReportsCollection,
71 const uint64_t interval,
72 ReadingParametersPastVersion metricParams) {
73 constexpr auto enabledDefault = true;
74 constexpr ReportUpdates reportUpdatesDefault =
75 ReportUpdates::overwrite;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010076
Patrick Williams3a1c2972023-05-10 07:51:04 -050077 std::vector<ReportAction> reportActions;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010078
Patrick Williams3a1c2972023-05-10 07:51:04 -050079 if (emitsReadingsUpdate)
80 {
81 reportActions.emplace_back(ReportAction::emitsReadingsUpdate);
82 }
83 if (logToMetricReportsCollection)
84 {
85 reportActions.emplace_back(
86 ReportAction::logToMetricReportsCollection);
87 }
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010088
Patrick Williams3a1c2972023-05-10 07:51:04 -050089 return addReport(
90 yield, reportId, reportId,
91 utils::toReportingType(reportingType), reportActions,
92 Milliseconds(interval), maxAppendLimit,
93 reportUpdatesDefault,
94 convertToReadingParameters(std::move(metricParams)),
95 enabledDefault)
96 .getPath();
97 });
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000098
Patrick Williams3a1c2972023-05-10 07:51:04 -050099 dbusIface.register_method(
100 "AddReportFutureVersion",
101 [this](boost::asio::yield_context& yield,
102 const std::vector<
103 std::pair<std::string, AddReportFutureVersionVariant>>&
104 properties) {
105 std::optional<std::string> reportId;
106 std::optional<std::string> reportName;
107 std::optional<std::string> reportingType;
108 std::optional<std::vector<std::string>> reportActions;
109 std::optional<uint64_t> interval;
110 std::optional<uint64_t> appendLimit;
111 std::optional<std::string> reportUpdates;
112 std::optional<ReadingParameters> metricParams;
113 std::optional<ReadingParameters> readingParameters;
114 std::optional<bool> enabled;
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200115
Patrick Williams3a1c2972023-05-10 07:51:04 -0500116 try
117 {
118 sdbusplus::unpackProperties(
119 properties, "Id", reportId, "Name", reportName,
120 "ReportingType", reportingType, "ReportActions",
121 reportActions, "Interval", interval, "AppendLimit",
122 appendLimit, "ReportUpdates", reportUpdates, "MetricParams",
123 metricParams, "Enabled", enabled, "ReadingParameters",
124 readingParameters);
125 }
126 catch (const sdbusplus::exception::UnpackPropertyError& e)
127 {
128 throw errors::InvalidArgument(e.propertyName);
129 }
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200130
Patrick Williams3a1c2972023-05-10 07:51:04 -0500131 if (readingParameters == std::nullopt)
132 {
133 readingParameters = metricParams;
134 }
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200135
Patrick Williams3a1c2972023-05-10 07:51:04 -0500136 return addReport(
137 yield, reportId.value_or(""), reportName.value_or(""),
138 utils::toReportingType(reportingType.value_or(
139 utils::enumToString(ReportingType::onRequest))),
140 utils::transform(
141 reportActions.value_or(std::vector<std::string>{}),
142 [](const auto& reportAction) {
143 return utils::toReportAction(reportAction);
144 }),
145 Milliseconds(interval.value_or(0)),
146 appendLimit.value_or(maxAppendLimit),
147 utils::toReportUpdates(reportUpdates.value_or(
148 utils::enumToString(ReportUpdates::overwrite))),
149 readingParameters.value_or(ReadingParameters{}),
150 enabled.value_or(true))
151 .getPath();
152 });
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200153 });
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +0200154}
155
Wludzik, Jozef2f9f9b82020-10-13 09:07:45 +0200156void ReportManager::removeReport(const interfaces::Report* report)
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +0200157{
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200158 reports.erase(
159 std::remove_if(reports.begin(), reports.end(),
160 [report](const auto& x) { return report == x.get(); }),
161 reports.end());
Krzysztof Grobelny64b75a52020-09-18 10:17:16 +0200162}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100163
Szymon Dompke32305f12022-07-05 15:37:21 +0200164void ReportManager::verifyMetricParameters(
165 const std::vector<LabeledMetricParameters>& readingParams)
166{
167 namespace ts = utils::tstring;
168
169 for (auto readingParam : readingParams)
170 {
171 if (readingParam.at_label<ts::Id>().length() >
172 utils::constants::maxIdNameLength)
173 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200174 throw errors::InvalidArgument("ReadingParameters.Id", "Too long.");
Szymon Dompke32305f12022-07-05 15:37:21 +0200175 }
176 }
177}
178
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000179void ReportManager::verifyAddReport(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100180 const std::string& reportId, const std::string& reportName,
181 const ReportingType reportingType, Milliseconds interval,
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000182 const ReportUpdates reportUpdates, const uint64_t appendLimit,
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000183 const std::vector<LabeledMetricParameters>& readingParams)
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100184{
Szymon Dompke32305f12022-07-05 15:37:21 +0200185 namespace ts = utils::tstring;
186
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100187 if (reports.size() >= maxReports)
188 {
189 throw sdbusplus::exception::SdBusError(
190 static_cast<int>(std::errc::too_many_files_open),
191 "Reached maximal report count");
192 }
193
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100194 if (appendLimit > maxAppendLimit &&
195 appendLimit != std::numeric_limits<uint64_t>::max())
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000196 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200197 throw errors::InvalidArgument("AppendLimit", "Out of range.");
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000198 }
199
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200200 if ((reportingType == ReportingType::periodic && interval < minInterval) ||
201 (reportingType != ReportingType::periodic &&
202 interval != Milliseconds{0}))
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100203 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200204 throw errors::InvalidArgument("Interval");
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100205 }
Wludzik, Jozefbc766b42020-12-08 16:06:22 +0100206
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000207 size_t metricCount = 0;
208 for (auto metricParam : readingParams)
209 {
210 auto metricParamsVec =
211 metricParam.at_label<utils::tstring::SensorPath>();
212 metricCount += metricParamsVec.size();
213 }
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000214
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000215 if (readingParams.size() > maxNumberMetrics ||
216 metricCount > maxNumberMetrics)
Wludzik, Jozefbc766b42020-12-08 16:06:22 +0100217 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200218 throw errors::InvalidArgument("MetricParams", "Too many.");
Wludzik, Jozefbc766b42020-12-08 16:06:22 +0100219 }
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000220
Szymon Dompke32305f12022-07-05 15:37:21 +0200221 verifyMetricParameters(readingParams);
222
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200223 for (const LabeledMetricParameters& item : readingParams)
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000224 {
Krzysztof Grobelny62c08e92022-09-16 10:28:53 +0200225 utils::toOperationType(
226 utils::toUnderlying(item.at_label<ts::OperationType>()));
Krzysztof Grobelnye8fc5752021-02-05 14:30:45 +0000227 }
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000228}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100229
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000230interfaces::Report& ReportManager::addReport(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100231 boost::asio::yield_context& yield, const std::string& reportId,
232 const std::string& reportName, const ReportingType reportingType,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100233 const std::vector<ReportAction>& reportActions, Milliseconds interval,
234 const uint64_t appendLimit, const ReportUpdates reportUpdates,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200235 ReadingParameters metricParams, const bool enabled)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000236{
Patrick Williams3a1c2972023-05-10 07:51:04 -0500237 auto labeledMetricParams = reportFactory->convertMetricParams(yield,
238 metricParams);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000239
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100240 return addReport(reportId, reportName, reportingType, reportActions,
241 interval, appendLimit, reportUpdates,
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100242 std::move(labeledMetricParams), enabled, Readings{});
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000243}
244
245interfaces::Report& ReportManager::addReport(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100246 const std::string& reportId, const std::string& reportName,
247 const ReportingType reportingType,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100248 const std::vector<ReportAction>& reportActions, Milliseconds interval,
249 const uint64_t appendLimit, const ReportUpdates reportUpdates,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200250 std::vector<LabeledMetricParameters> labeledMetricParams,
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100251 const bool enabled, Readings readings)
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000252{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100253 const auto existingReportIds = utils::transform(
254 reports, [](const auto& report) { return report->getId(); });
255
Szymon Dompke32305f12022-07-05 15:37:21 +0200256 auto [id, name] = utils::makeIdName(reportId, reportName, reportNameDefault,
257 existingReportIds);
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100258
259 verifyAddReport(id, name, reportingType, interval, reportUpdates,
Ankita Vilas Gawadecd5b0b72022-01-20 20:55:04 +0000260 appendLimit, labeledMetricParams);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000261
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100262 reports.emplace_back(
263 reportFactory->make(id, name, reportingType, reportActions, interval,
264 appendLimit, reportUpdates, *this, *reportStorage,
265 labeledMetricParams, enabled, std::move(readings)));
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000266 return *reports.back();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100267}
268
269void ReportManager::loadFromPersistent()
270{
271 std::vector<interfaces::JsonStorage::FilePath> paths =
272 reportStorage->list();
273
274 for (const auto& path : paths)
275 {
276 std::optional<nlohmann::json> data = reportStorage->load(path);
277 try
278 {
279 size_t version = data->at("Version").get<size_t>();
280 if (version != Report::reportVersion)
281 {
282 throw std::logic_error("Invalid version");
283 }
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100284 bool enabled = data->at("Enabled").get<bool>();
285 std::string& id = data->at("Id").get_ref<std::string&>();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100286 std::string& name = data->at("Name").get_ref<std::string&>();
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100287
288 uint32_t reportingType = data->at("ReportingType").get<uint32_t>();
289 std::vector<ReportAction> reportActions = utils::transform(
290 data->at("ReportActions").get<std::vector<uint32_t>>(),
291 [](const auto reportAction) {
Patrick Williams3a1c2972023-05-10 07:51:04 -0500292 return utils::toReportAction(reportAction);
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100293 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100294 uint64_t interval = data->at("Interval").get<uint64_t>();
Szymon Dompke3eb56862021-09-20 15:32:04 +0200295 uint64_t appendLimit = data->at("AppendLimit").get<uint64_t>();
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100296 uint32_t reportUpdates = data->at("ReportUpdates").get<uint32_t>();
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000297 auto readingParameters =
298 data->at("ReadingParameters")
299 .get<std::vector<LabeledMetricParameters>>();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100300
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100301 Readings readings = {};
302
303 if (auto it = data->find("MetricValues"); it != data->end())
304 {
305 const auto labeledReadings = it->get<LabeledReadings>();
306 readings = utils::toReadings(labeledReadings);
307 }
308
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100309 addReport(id, name, utils::toReportingType(reportingType),
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100310 reportActions, Milliseconds(interval), appendLimit,
311 utils::toReportUpdates(reportUpdates),
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100312 std::move(readingParameters), enabled,
313 std::move(readings));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100314 }
315 catch (const std::exception& e)
316 {
317 phosphor::logging::log<phosphor::logging::level::ERR>(
318 "Failed to load report from storage",
319 phosphor::logging::entry(
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100320 "FILENAME=%s",
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100321 static_cast<std::filesystem::path>(path).c_str()),
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100322 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100323 reportStorage->remove(path);
324 }
325 }
326}