diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch new file mode 100644 index 000000000..0ce5a3ea6 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0005-Switched-bmcweb-to-use-new-telemetry-service-API.patch @@ -0,0 +1,559 @@ +From 4bd7f53cc01315774eedef637f5d1824cd7f662a Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Thu, 17 Jun 2021 13:37:57 +0000 +Subject: [PATCH] Switched bmcweb to use new telemetry service API + +Added support for multiple MetricProperties. Added support for new +parameters: CollectionTimeScope, CollectionDuration. + +Tested: + - It is possible to create MetricReportDefinitions with multiple + MetricProperties. + - Stub values for new parameters are correctly passed to telemetry + service. + - All existing telemetry service functionalities remain unchanged. + +Change-Id: I2cd17069e3ea015c8f5571c29278f1d50536272a +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Signed-off-by: Lukasz Kazmierczak <lukasz.kazmierczak@intel.com> +--- + include/dbus_utility.hpp | 5 +- + redfish-core/lib/metric_report_definition.hpp | 330 ++++++++++++------ + redfish-core/lib/telemetry_service.hpp | 13 + + 3 files changed, 240 insertions(+), 108 deletions(-) + +diff --git a/include/dbus_utility.hpp b/include/dbus_utility.hpp +index 481b33d..32153f8 100644 +--- a/include/dbus_utility.hpp ++++ b/include/dbus_utility.hpp +@@ -50,8 +50,9 @@ using DbusVariantType = std::variant< + std::vector<std::tuple<std::string, std::string>>, + std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>, + std::vector<std::tuple<uint32_t, size_t>>, +- std::vector<std::tuple<sdbusplus::message::object_path, std::string, +- std::string, std::string>> ++ std::vector<std::tuple< ++ std::vector<std::tuple<sdbusplus::message::object_path, std::string>>, ++ std::string, std::string, std::string, uint64_t>> + >; + + // clang-format on +diff --git a/redfish-core/lib/metric_report_definition.hpp b/redfish-core/lib/metric_report_definition.hpp +index 1a520f3..ac980aa 100644 +--- a/redfish-core/lib/metric_report_definition.hpp ++++ b/redfish-core/lib/metric_report_definition.hpp +@@ -17,50 +17,68 @@ namespace redfish + + namespace telemetry + { +- + constexpr const char* metricReportDefinitionUri = + "/redfish/v1/TelemetryService/MetricReportDefinitions"; + +-using ReadingParameters = +- std::vector<std::tuple<sdbusplus::message::object_path, std::string, +- std::string, std::string>>; ++using ReadingParameters = std::vector<std::tuple< ++ std::vector<std::tuple<sdbusplus::message::object_path, std::string>>, ++ std::string, std::string, std::string, uint64_t>>; ++ ++std::string toReadfishReportAction(std::string_view action) ++{ ++ if (action == "EmitsReadingsUpdate") ++ { ++ return "RedfishEvent"; ++ } ++ if (action == "LogToMetricReportsCollection") ++ { ++ return "LogToMetricReportsCollection"; ++ } ++ return ""; ++} ++ ++std::string toDbusReportAction(std::string_view action) ++{ ++ if (action == "RedfishEvent") ++ { ++ return "EmitsReadingsUpdate"; ++ } ++ if (action == "LogToMetricReportsCollection") ++ { ++ return "LogToMetricReportsCollection"; ++ } ++ return ""; ++} + + inline void fillReportDefinition( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, + const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>& +- ret) ++ properties) + { +- asyncResp->res.jsonValue["@odata.type"] = +- "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; +- asyncResp->res.jsonValue["@odata.id"] = +- crow::utility::urlFromPieces("redfish", "v1", "TelemetryService", +- "MetricReportDefinitions", id) +- .string(); +- asyncResp->res.jsonValue["Id"] = id; +- asyncResp->res.jsonValue["Name"] = id; +- asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = +- crow::utility::urlFromPieces("redfish", "v1", "TelemetryService", +- "MetricReports", id) +- .string(); +- asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; +- asyncResp->res.jsonValue["ReportUpdates"] = "Overwrite"; +- +- const bool* emitsReadingsUpdate = nullptr; +- const bool* logToMetricReportsCollection = nullptr; ++ const std::vector<std::string>* reportActions = nullptr; + const ReadingParameters* readingParams = nullptr; + const std::string* reportingType = nullptr; ++ const std::string* reportUpdates = nullptr; ++ const std::string* name = nullptr; ++ const uint64_t* appendLimit = nullptr; + const uint64_t* interval = nullptr; +- for (const auto& [key, var] : ret) ++ const bool* enabled = nullptr; ++ ++ for (const auto& [key, var] : properties) + { +- if (key == "EmitsReadingsUpdate") ++ if (key == "ReportActions") ++ { ++ reportActions = std::get_if<std::vector<std::string>>(&var); ++ } ++ else if (key == "ReportUpdates") + { +- emitsReadingsUpdate = std::get_if<bool>(&var); ++ reportUpdates = std::get_if<std::string>(&var); + } +- else if (key == "LogToMetricReportsCollection") ++ else if (key == "AppendLimit") + { +- logToMetricReportsCollection = std::get_if<bool>(&var); ++ appendLimit = std::get_if<uint64_t>(&var); + } +- else if (key == "ReadingParameters") ++ else if (key == "ReadingParametersFutureVersion") + { + readingParams = std::get_if<ReadingParameters>(&var); + } +@@ -72,73 +90,149 @@ inline void fillReportDefinition( + { + interval = std::get_if<uint64_t>(&var); + } ++ else if (key == "Name") ++ { ++ name = std::get_if<std::string>(&var); ++ } ++ else if (key == "Enabled") ++ { ++ enabled = std::get_if<bool>(&var); ++ } + } +- if (emitsReadingsUpdate == nullptr || +- logToMetricReportsCollection == nullptr || readingParams == nullptr || +- reportingType == nullptr || interval == nullptr) ++ ++ std::vector<std::string> redfishReportActions; ++ if (reportActions != nullptr) + { +- BMCWEB_LOG_ERROR << "Property type mismatch or property is missing"; +- messages::internalError(asyncResp->res); +- return; ++ for (const std::string& action : *reportActions) ++ { ++ std::string redfishAction = toReadfishReportAction(action); ++ ++ if (redfishAction.empty()) ++ { ++ BMCWEB_LOG_ERROR << "Unknown ReportActions element: " << action; ++ messages::internalError(asyncResp->res); ++ return; ++ } ++ ++ redfishReportActions.emplace_back(std::move(redfishAction)); ++ } + } + +- std::vector<std::string> redfishReportActions; +- redfishReportActions.reserve(2); +- if (*emitsReadingsUpdate) ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; ++ asyncResp->res.jsonValue["@odata.id"] = ++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService", ++ "MetricReportDefinitions", id) ++ .string(); ++ asyncResp->res.jsonValue["Id"] = id; ++ asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = ++ crow::utility::urlFromPieces("redfish", "v1", "TelemetryService", ++ "MetricReports", id) ++ .string(); ++ ++ if (enabled != nullptr) + { +- redfishReportActions.emplace_back("RedfishEvent"); ++ asyncResp->res.jsonValue["Status"]["State"] = ++ *enabled ? "Enabled" : "Disabled"; ++ asyncResp->res.jsonValue["MetricReportDefinitionEnabled"] = *enabled; + } +- if (*logToMetricReportsCollection) ++ ++ if (appendLimit != nullptr) + { +- redfishReportActions.emplace_back("LogToMetricReportsCollection"); ++ asyncResp->res.jsonValue["AppendLimit"] = *appendLimit; + } + +- nlohmann::json metrics = nlohmann::json::array(); +- for (const auto& [sensorPath, operationType, id, metadata] : *readingParams) ++ if (reportUpdates != nullptr) + { +- metrics.push_back({ +- {"MetricId", id}, +- {"MetricProperties", {metadata}}, +- }); ++ asyncResp->res.jsonValue["ReportUpdates"] = *reportUpdates; ++ } ++ ++ if (name != nullptr) ++ { ++ asyncResp->res.jsonValue["Name"] = *name; ++ } ++ ++ if (reportActions != nullptr) ++ { ++ asyncResp->res.jsonValue["ReportActions"] = ++ std::move(redfishReportActions); ++ } ++ ++ if (reportingType != nullptr) ++ { ++ asyncResp->res.jsonValue["MetricReportDefinitionType"] = *reportingType; ++ } ++ ++ if (interval != nullptr) ++ { ++ asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] = ++ time_utils::toDurationString(std::chrono::milliseconds(*interval)); ++ } ++ ++ if (readingParams != nullptr) ++ { ++ nlohmann::json& metrics = asyncResp->res.jsonValue["Metrics"]; ++ metrics = nlohmann::json::array(); ++ for (auto& [sensorData, collectionFunction, id, collectionTimeScope, ++ collectionDuration] : *readingParams) ++ { ++ std::vector<std::string> metricProperties; ++ ++ for (const auto& [sensorPath, sensorMetadata] : sensorData) ++ { ++ metricProperties.emplace_back(sensorMetadata); ++ } ++ ++ metrics.push_back( ++ {{"MetricId", id}, ++ {"MetricProperties", std::move(metricProperties)}, ++ {"CollectionFunction", collectionFunction}, ++ {"CollectionDuration", ++ time_utils::toDurationString( ++ std::chrono::milliseconds(collectionDuration))}, ++ {"CollectionTimeScope", collectionTimeScope}}); ++ } + } +- asyncResp->res.jsonValue["Metrics"] = metrics; +- asyncResp->res.jsonValue["MetricReportDefinitionType"] = *reportingType; +- asyncResp->res.jsonValue["ReportActions"] = redfishReportActions; +- asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] = +- time_utils::toDurationString(std::chrono::milliseconds(*interval)); + } + + struct AddReportArgs + { +- std::string name; ++ struct MetricArgs ++ { ++ std::string id; ++ std::vector<std::string> uris; ++ std::optional<std::string> collectionFunction; ++ std::optional<std::string> collectionTimeScope; ++ std::optional<uint64_t> collectionDuration; ++ }; ++ ++ std::optional<std::string> id; ++ std::optional<std::string> name; + std::string reportingType; +- bool emitsReadingsUpdate = false; +- bool logToMetricReportsCollection = false; ++ std::optional<std::string> reportUpdates; ++ std::optional<uint64_t> appendLimit; ++ std::vector<std::string> reportActions; + uint64_t interval = 0; +- std::vector<std::pair<std::string, std::vector<std::string>>> metrics; ++ std::vector<MetricArgs> metrics; + }; + + inline bool toDbusReportActions(crow::Response& res, +- std::vector<std::string>& actions, ++ const std::vector<std::string>& actions, + AddReportArgs& args) + { + size_t index = 0; +- for (auto& action : actions) ++ for (const auto& action : actions) + { +- if (action == "RedfishEvent") +- { +- args.emitsReadingsUpdate = true; +- } +- else if (action == "LogToMetricReportsCollection") +- { +- args.logToMetricReportsCollection = true; +- } +- else ++ std::string dbusReportAction = toDbusReportAction(action); ++ ++ if (dbusReportAction.empty()) + { + messages::propertyValueNotInList( + res, action, "ReportActions/" + std::to_string(index)); + return false; + } ++ ++ args.reportActions.emplace_back(std::move(dbusReportAction)); + index++; + } + return true; +@@ -150,27 +244,17 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req, + std::vector<nlohmann::json> metrics; + std::vector<std::string> reportActions; + std::optional<nlohmann::json> schedule; +- if (!json_util::readJsonPatch(req, res, "Id", args.name, "Metrics", metrics, +- "MetricReportDefinitionType", +- args.reportingType, "ReportActions", +- reportActions, "Schedule", schedule)) +- { +- return false; +- } +- +- constexpr const char* allowedCharactersInName = +- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; +- if (args.name.empty() || args.name.find_first_not_of( +- allowedCharactersInName) != std::string::npos) ++ if (!json_util::readJsonPatch( ++ req, res, "Id", args.id, "Name", args.name, "Metrics", metrics, ++ "MetricReportDefinitionType", args.reportingType, "ReportUpdates", ++ args.reportUpdates, "AppendLimit", args.appendLimit, ++ "ReportActions", reportActions, "Schedule", schedule)) + { +- BMCWEB_LOG_ERROR << "Failed to match " << args.name +- << " with allowed character " +- << allowedCharactersInName; +- messages::propertyValueIncorrect(res, "Id", args.name); + return false; + } + +- if (args.reportingType != "Periodic" && args.reportingType != "OnRequest") ++ if (args.reportingType != "Periodic" && args.reportingType != "OnRequest" && ++ args.reportingType != "OnChange") + { + messages::propertyValueNotInList(res, args.reportingType, + "MetricReportDefinitionType"); +@@ -211,15 +295,35 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req, + args.metrics.reserve(metrics.size()); + for (auto& m : metrics) + { +- std::string id; +- std::vector<std::string> uris; +- if (!json_util::readJson(m, res, "MetricId", id, "MetricProperties", +- uris)) ++ std::optional<std::string> collectionDurationStr; ++ AddReportArgs::MetricArgs metricArgs; ++ if (!json_util::readJson( ++ m, res, "MetricId", metricArgs.id, "MetricProperties", ++ metricArgs.uris, "CollectionFunction", ++ metricArgs.collectionFunction, "CollectionTimeScope", ++ metricArgs.collectionTimeScope, "CollectionDuration", ++ collectionDurationStr)) + { + return false; + } + +- args.metrics.emplace_back(std::move(id), std::move(uris)); ++ if (collectionDurationStr) ++ { ++ std::optional<std::chrono::milliseconds> duration = ++ time_utils::fromDurationString(*collectionDurationStr); ++ ++ if (!duration || duration->count() < 0) ++ { ++ messages::propertyValueIncorrect(res, "CollectionDuration", ++ *collectionDurationStr); ++ return false; ++ } ++ ++ metricArgs.collectionDuration = ++ static_cast<uint64_t>(duration->count()); ++ } ++ ++ args.metrics.emplace_back(std::move(metricArgs)); + } + + return true; +@@ -227,15 +331,14 @@ inline bool getUserParameters(crow::Response& res, const crow::Request& req, + + inline bool getChassisSensorNode( + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, +- const std::vector<std::pair<std::string, std::vector<std::string>>>& +- metrics, ++ const std::vector<AddReportArgs::MetricArgs>& metrics, + boost::container::flat_set<std::pair<std::string, std::string>>& matched) + { +- for (const auto& [id, uris] : metrics) ++ for (const auto& metric : metrics) + { +- for (size_t i = 0; i < uris.size(); i++) ++ for (size_t i = 0; i < metric.uris.size(); i++) + { +- const std::string& uri = uris[i]; ++ const std::string& uri = metric.uris[i]; + std::string chassis; + std::string node; + +@@ -280,11 +383,16 @@ class AddReport + telemetry::ReadingParameters readingParams; + readingParams.reserve(args.metrics.size()); + +- for (const auto& [id, uris] : args.metrics) ++ for (auto& metric : args.metrics) + { +- for (size_t i = 0; i < uris.size(); i++) ++ std::vector< ++ std::tuple<sdbusplus::message::object_path, std::string>> ++ sensorParams; ++ sensorParams.reserve(metric.uris.size()); ++ ++ for (size_t i = 0; i < metric.uris.size(); i++) + { +- const std::string& uri = uris[i]; ++ const std::string& uri = metric.uris[i]; + auto el = uriToDbus.find(uri); + if (el == uriToDbus.end()) + { +@@ -298,17 +406,23 @@ class AddReport + } + + const std::string& dbusPath = el->second; +- readingParams.emplace_back(dbusPath, "SINGLE", id, uri); ++ sensorParams.emplace_back(dbusPath, uri); + } ++ ++ readingParams.emplace_back( ++ std::move(sensorParams), metric.collectionFunction.value_or(""), ++ std::move(metric.id), metric.collectionTimeScope.value_or(""), ++ metric.collectionDuration.value_or(0U)); + } + const std::shared_ptr<bmcweb::AsyncResp> aResp = asyncResp; + crow::connections::systemBus->async_method_call( +- [aResp, name = args.name, uriToDbus = std::move(uriToDbus)]( ++ [aResp, id = args.id.value_or(""), ++ uriToDbus = std::move(uriToDbus)]( + const boost::system::error_code ec, const std::string&) { + if (ec == boost::system::errc::file_exists) + { + messages::resourceAlreadyExists( +- aResp->res, "MetricReportDefinition", "Id", name); ++ aResp->res, "MetricReportDefinition", "Id", id); + return; + } + if (ec == boost::system::errc::too_many_files_open) +@@ -338,10 +452,12 @@ class AddReport + messages::created(aResp->res); + }, + telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", +- "xyz.openbmc_project.Telemetry.ReportManager", "AddReport", +- "TelemetryService/" + args.name, args.reportingType, +- args.emitsReadingsUpdate, args.logToMetricReportsCollection, +- args.interval, readingParams); ++ "xyz.openbmc_project.Telemetry.ReportManager", ++ "AddReportFutureVersion", ++ "TelemetryService/" + args.id.value_or(""), args.name.value_or(""), ++ args.reportingType, args.reportUpdates.value_or("Overwrite"), ++ args.appendLimit.value_or(0), args.reportActions, args.interval, ++ readingParams); + } + + AddReport(const AddReport&) = delete; +@@ -436,10 +552,10 @@ inline void requestRoutesMetricReportDefinition(App& app) + const std::string& id) { + crow::connections::systemBus->async_method_call( + [asyncResp, +- id](const boost::system::error_code ec, ++ id](boost::system::error_code ec, + const std::vector<std::pair< + std::string, dbus::utility::DbusVariantType>>& +- ret) { ++ properties) { + if (ec.value() == EBADR || + ec == boost::system::errc::host_unreachable) + { +@@ -454,12 +570,14 @@ inline void requestRoutesMetricReportDefinition(App& app) + return; + } + +- telemetry::fillReportDefinition(asyncResp, id, ret); ++ telemetry::fillReportDefinition(asyncResp, id, ++ properties); + }, + telemetry::service, telemetry::getDbusReportPath(id), + "org.freedesktop.DBus.Properties", "GetAll", + telemetry::reportInterface); + }); ++ + BMCWEB_ROUTE(app, + "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") + .privileges(redfish::privileges::deleteMetricReportDefinitionCollection) +diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp +index c1fe7d0..64d712b 100644 +--- a/redfish-core/lib/telemetry_service.hpp ++++ b/redfish-core/lib/telemetry_service.hpp +@@ -49,6 +49,8 @@ inline void handleTelemetryServiceGet( + + const size_t* maxReports = nullptr; + const uint64_t* minInterval = nullptr; ++ const std::vector<std::string>* supportedCollectionFunction = ++ nullptr; + for (const auto& [key, var] : ret) + { + if (key == "MaxReports") +@@ -59,6 +61,11 @@ inline void handleTelemetryServiceGet( + { + minInterval = std::get_if<uint64_t>(&var); + } ++ else if (key == "SupportedOperationTypes") ++ { ++ supportedCollectionFunction = ++ std::get_if<std::vector<std::string>>(&var); ++ } + } + if (maxReports == nullptr || minInterval == nullptr) + { +@@ -72,6 +79,12 @@ inline void handleTelemetryServiceGet( + asyncResp->res.jsonValue["MinCollectionInterval"] = + time_utils::toDurationString(std::chrono::milliseconds( + static_cast<time_t>(*minInterval))); ++ ++ if (supportedCollectionFunction != nullptr) ++ { ++ asyncResp->res.jsonValue["SupportedCollectionFunction"] = ++ *supportedCollectionFunction; ++ } + }, + telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", + "org.freedesktop.DBus.Properties", "GetAll", +-- +2.25.1 + |