diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch new file mode 100644 index 000000000..c19691cdc --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch @@ -0,0 +1,496 @@ +From 80608f0d72da62426bb00e03a42fbf5daed931c9 Mon Sep 17 00:00:00 2001 +From: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Date: Tue, 13 Apr 2021 13:00:18 +0000 +Subject: [PATCH] Add support for MetricDefinition scheme + +Added MetricDefinition node to Redfish code. Now user is able to list +all available metrics in OpenBMC that are supported by Telemetry +service. Metrics are grouped by reading type. + +MetricDefinitions contains all physical sensors supported by redfish, +algorithm iterates through all chassis and collects results for each +node available in that chassis (Power, Thermal, Sensors). + +When https://gerrit.openbmc-project.xyz/c/openbmc/bmcweb/+/40169 will +be merge it will be possible to optimize this algorithm to only get +sensors from Sensors node. Currently Sensors node doesn't contain all +available sensors. + +Tested: + - MetricDefinitions response is filled with existing sensors, it works + with and without Telemetry service + - Validated a presence of MetricDefinition members and its attributes + - Successfully passed RedfishServiceValidator.py using witherspoon + image on QEMU + - Tested using following GET,POST requests + +GET /redfish/v1/TelemetryService/MetricDefinitions +{ + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions", + "@odata.type": "#MetricDefinitionCollection.MetricDefinitionCollection", + "Members": [ + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Rotational" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Percent" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Temperature" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Power" + }, + { + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/AirFlow" + } + ], + "Members@odata.count": 5, + "Name": "Metric Definition Collection" +} + +GET /redfish/v1/TelemetryService/MetricDefinitions/Rotational +{ + "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Rotational", + "@odata.type": "#MetricDefinition.v1_0_3.MetricDefinition", + "Id": "Rotational", + "Implementation": "PhysicalSensor", + "IsLinear": true, + "MetricDataType": "Decimal", + "MetricProperties": [ + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/0/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/1/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/2/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/3/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/4/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/5/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/6/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/7/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/8/Reading", + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/9/Reading" + ], + "MetricType": "Numeric", + "Name": "Rotational", + "Units": "RPM" +} + +POST redfish/v1/TelemetryService/MetricReportDefinitions, body: +{ + "Id": "TestReport", + "Metrics": [ + { + "MetricId": "TestMetric", + "MetricProperties": [ + "/redfish/v1/Chassis/Chassis0/Thermal#/Fans/3/Reading", + ] + } + ], + "MetricReportDefinitionType": "OnRequest", + "ReportActions": [ + "RedfishEvent", + "LogToMetricReportsCollection" + ] +} +{ + "@Message.ExtendedInfo": [ + { + "@odata.type": "#Message.v1_1_1.Message", + "Message": "The resource has been created successfully", + "MessageArgs": [], + "MessageId": "Base.1.8.1.Created", + "MessageSeverity": "OK", + "Resolution": "None" + } + ] +} + +Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com> +Signed-off-by: Krzysztof Grobelny <krzysztof.grobelny@intel.com> +Change-Id: I3086e1302e1ba2e5442d1367939fd5507a0cbc00 +--- + redfish-core/include/redfish.hpp | 3 + + .../include/utils/get_chassis_names.hpp | 58 ++++ + .../include/utils/telemetry_utils.hpp | 2 + + redfish-core/lib/metric_definition.hpp | 258 ++++++++++++++++++ + redfish-core/lib/telemetry_service.hpp | 2 + + 5 files changed, 323 insertions(+) + create mode 100644 redfish-core/include/utils/get_chassis_names.hpp + create mode 100644 redfish-core/lib/metric_definition.hpp + +diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp +index 1c7b695..9983b88 100644 +--- a/redfish-core/include/redfish.hpp ++++ b/redfish-core/include/redfish.hpp +@@ -26,6 +26,7 @@ + #include "../lib/managers.hpp" + #include "../lib/memory.hpp" + #include "../lib/message_registries.hpp" ++#include "../lib/metric_definition.hpp" + #include "../lib/metric_report.hpp" + #include "../lib/metric_report_definition.hpp" + #include "../lib/network_protocol.hpp" +@@ -199,6 +200,8 @@ class RedfishService + requestRoutesMetricReportDefinition(app); + requestRoutesMetricReportCollection(app); + requestRoutesMetricReport(app); ++ requestRoutesMetricDefinitionCollection(app); ++ requestRoutesMetricDefinition(app); + } + }; + +diff --git a/redfish-core/include/utils/get_chassis_names.hpp b/redfish-core/include/utils/get_chassis_names.hpp +new file mode 100644 +index 0000000..0276b6f +--- /dev/null ++++ b/redfish-core/include/utils/get_chassis_names.hpp +@@ -0,0 +1,58 @@ ++#pragma once ++ ++#include <include/dbus_singleton.hpp> ++ ++#include <array> ++#include <string> ++#include <vector> ++ ++namespace redfish ++{ ++ ++namespace utils ++{ ++ ++template <typename F> ++inline void getChassisNames(F&& cb) ++{ ++ const std::array<const char*, 2> interfaces = { ++ "xyz.openbmc_project.Inventory.Item.Board", ++ "xyz.openbmc_project.Inventory.Item.Chassis"}; ++ ++ crow::connections::systemBus->async_method_call( ++ [callback = std::move(cb)](const boost::system::error_code ec, ++ const std::vector<std::string>& chassis) { ++ std::vector<std::string> chassisNames; ++ ++ if (ec) ++ { ++ callback(ec, chassisNames); ++ return; ++ } ++ ++ chassisNames.reserve(chassis.size()); ++ for (const std::string& path : chassis) ++ { ++ sdbusplus::message::object_path dbusPath = path; ++ std::string name = dbusPath.filename(); ++ if (name.empty()) ++ { ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::invalid_argument), ++ chassisNames); ++ return; ++ } ++ chassisNames.emplace_back(std::move(name)); ++ } ++ ++ callback(ec, chassisNames); ++ }, ++ "xyz.openbmc_project.ObjectMapper", ++ "/xyz/openbmc_project/object_mapper", ++ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", ++ "/xyz/openbmc_project/inventory", 0, interfaces); ++} ++ ++} // namespace utils ++ ++} // namespace redfish +diff --git a/redfish-core/include/utils/telemetry_utils.hpp b/redfish-core/include/utils/telemetry_utils.hpp +index 5872350..1b4f75d 100644 +--- a/redfish-core/include/utils/telemetry_utils.hpp ++++ b/redfish-core/include/utils/telemetry_utils.hpp +@@ -10,6 +10,8 @@ namespace telemetry + + constexpr const char* service = "xyz.openbmc_project.Telemetry"; + constexpr const char* reportInterface = "xyz.openbmc_project.Telemetry.Report"; ++constexpr const char* metricDefinitionUri = ++ "/redfish/v1/TelemetryService/MetricDefinitions/"; + constexpr const char* metricReportDefinitionUri = + "/redfish/v1/TelemetryService/MetricReportDefinitions/"; + constexpr const char* metricReportUri = +diff --git a/redfish-core/lib/metric_definition.hpp b/redfish-core/lib/metric_definition.hpp +new file mode 100644 +index 0000000..019168b +--- /dev/null ++++ b/redfish-core/lib/metric_definition.hpp +@@ -0,0 +1,258 @@ ++#pragma once ++ ++#include "async_resp.hpp" ++#include "sensors.hpp" ++#include "utils/get_chassis_names.hpp" ++#include "utils/telemetry_utils.hpp" ++ ++#include <registries/privilege_registry.hpp> ++ ++namespace redfish ++{ ++ ++namespace telemetry ++{ ++ ++bool containsOdata(const nlohmann::json& json, const std::string& odataId) ++{ ++ const auto it = std::find_if( ++ json.begin(), json.end(), [&odataId](const nlohmann::json& item) { ++ auto kt = item.find("@odata.id"); ++ if (kt == item.end()) ++ { ++ return false; ++ } ++ const std::string* value = kt->get_ptr<const std::string*>(); ++ if (!value) ++ { ++ return false; ++ } ++ return *value == odataId; ++ }); ++ ++ return it != json.end(); ++} ++ ++void addMembers(crow::Response& res, ++ const boost::container::flat_map<std::string, std::string>& el) ++{ ++ for (const auto& [_, dbusSensor] : el) ++ { ++ sdbusplus::message::object_path path(dbusSensor); ++ sdbusplus::message::object_path parentPath = path.parent_path(); ++ const std::string type = parentPath.filename(); ++ ++ if (type.empty()) ++ { ++ BMCWEB_LOG_ERROR << "Received invalid DBus Sensor Path = " ++ << dbusSensor; ++ continue; ++ } ++ ++ nlohmann::json& members = res.jsonValue["Members"]; ++ ++ const std::string odataId = ++ std::string(telemetry::metricDefinitionUri) + ++ sensors::toReadingType(type); ++ ++ if (!containsOdata(members, odataId)) ++ { ++ members.push_back({{"@odata.id", odataId}}); ++ } ++ ++ res.jsonValue["Members@odata.count"] = members.size(); ++ } ++} ++ ++template <class Callback> ++inline void mapRedfishUriToDbusPath(Callback&& callback) ++{ ++ utils::getChassisNames([callback = std::move(callback)]( ++ boost::system::error_code ec, ++ const std::vector<std::string>& chassisNames) { ++ if (ec) ++ { ++ BMCWEB_LOG_ERROR << "getChassisNames error: " << ec.value(); ++ callback(ec, {}); ++ return; ++ } ++ ++ auto handleRetrieveUriToDbusMap = ++ [callback = std::move(callback)]( ++ const boost::beast::http::status status, ++ const boost::container::flat_map<std::string, std::string>& ++ uriToDbus) { ++ if (status != boost::beast::http::status::ok) ++ { ++ BMCWEB_LOG_ERROR << "Failed to retrieve URI to dbus " ++ "sensors map with err " ++ << static_cast<unsigned>(status); ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::io_error), ++ {}); ++ return; ++ } ++ ++ callback(boost::system::errc::make_error_code( ++ boost::system::errc::success), ++ uriToDbus); ++ }; ++ ++ for (const std::string& chassisName : chassisNames) ++ { ++ for (const auto& [sensorNode, dbusPaths] : sensors::dbus::paths) ++ { ++ retrieveUriToDbusMap(chassisName, sensorNode.data(), ++ handleRetrieveUriToDbusMap); ++ } ++ } ++ }); ++} ++ ++} // namespace telemetry ++ ++inline void requestRoutesMetricDefinitionCollection(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricDefinitions/") ++ .privileges(privileges::getTelemetryService) ++ .methods(boost::beast::http::verb::get)( ++ [](const crow::Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { ++ telemetry::mapRedfishUriToDbusPath( ++ [asyncResp](boost::system::error_code ec, ++ const boost::container::flat_map< ++ std::string, std::string>& uriToDbus) { ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_ERROR ++ << "mapRedfishUriToDbusPath error: " ++ << ec.value(); ++ return; ++ } ++ ++ telemetry::addMembers(asyncResp->res, uriToDbus); ++ }); ++ ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#MetricDefinitionCollection." ++ "MetricDefinitionCollection"; ++ asyncResp->res.jsonValue["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricDefinitions"; ++ asyncResp->res.jsonValue["Name"] = ++ "Metric Definition Collection"; ++ asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); ++ asyncResp->res.jsonValue["Members@odata.count"] = 0; ++ }); ++} ++ ++namespace telemetry ++{ ++ ++bool isSensorIdSupported(std::string_view readingType) ++{ ++ for (const std::pair<std::string_view, std::vector<const char*>>& ++ typeToPaths : sensors::dbus::paths) ++ { ++ for (const char* supportedPath : typeToPaths.second) ++ { ++ if (readingType == ++ sensors::toReadingType( ++ sdbusplus::message::object_path(supportedPath).filename())) ++ { ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++void addMetricProperty( ++ bmcweb::AsyncResp& asyncResp, const std::string& readingType, ++ const boost::container::flat_map<std::string, std::string>& el) ++{ ++ nlohmann::json& metricProperties = ++ asyncResp.res.jsonValue["MetricProperties"]; ++ ++ for (const auto& [redfishSensor, dbusSensor] : el) ++ { ++ std::string sensorId; ++ if (dbus::utility::getNthStringFromPath(dbusSensor, 3, sensorId)) ++ { ++ if (sensors::toReadingType(sensorId) == readingType) ++ { ++ metricProperties.push_back(redfishSensor); ++ } ++ } ++ } ++} ++ ++inline const char* readingTypeToReadingUnits(const std::string& readingType) ++{ ++ for (const auto& [node, paths] : sensors::dbus::paths) ++ { ++ for (const char* path : paths) ++ { ++ const sdbusplus::message::object_path sensorPath = ++ sdbusplus::message::object_path(path); ++ if (sensors::toReadingType(sensorPath.filename()) == readingType) ++ { ++ return sensors::toReadingUnits(sensorPath.filename()); ++ } ++ } ++ } ++ return ""; ++} ++ ++} // namespace telemetry ++ ++inline void requestRoutesMetricDefinition(App& app) ++{ ++ BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricDefinitions/<str>/") ++ .privileges(privileges::getTelemetryService) ++ .methods(boost::beast::http::verb::get)( ++ [](const crow::Request&, ++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, ++ const std::string& readingType) { ++ if (!telemetry::isSensorIdSupported(readingType)) ++ { ++ messages::resourceNotFound(asyncResp->res, ++ "MetricDefinition", readingType); ++ return; ++ } ++ ++ telemetry::mapRedfishUriToDbusPath( ++ [asyncResp, ++ readingType](boost::system::error_code ec, ++ const boost::container::flat_map< ++ std::string, std::string>& uriToDbus) { ++ if (ec) ++ { ++ messages::internalError(asyncResp->res); ++ BMCWEB_LOG_ERROR ++ << "mapRedfishUriToDbusPath error: " ++ << ec.value(); ++ return; ++ } ++ ++ asyncResp->res.jsonValue["Id"] = readingType; ++ asyncResp->res.jsonValue["Name"] = readingType; ++ asyncResp->res.jsonValue["@odata.id"] = ++ telemetry::metricDefinitionUri + readingType; ++ asyncResp->res.jsonValue["@odata.type"] = ++ "#MetricDefinition.v1_0_3.MetricDefinition"; ++ asyncResp->res.jsonValue["MetricDataType"] = "Decimal"; ++ asyncResp->res.jsonValue["MetricType"] = "Numeric"; ++ asyncResp->res.jsonValue["IsLinear"] = true; ++ asyncResp->res.jsonValue["Implementation"] = ++ "PhysicalSensor"; ++ asyncResp->res.jsonValue["Units"] = ++ telemetry::readingTypeToReadingUnits(readingType); ++ ++ telemetry::addMetricProperty(*asyncResp, readingType, ++ uriToDbus); ++ }); ++ }); ++} ++ ++} // namespace redfish +diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp +index ad86d5c..c4962e9 100644 +--- a/redfish-core/lib/telemetry_service.hpp ++++ b/redfish-core/lib/telemetry_service.hpp +@@ -29,6 +29,8 @@ inline void requestRoutesTelemetryService(App& app) + "/redfish/v1/TelemetryService/MetricReportDefinitions"; + asyncResp->res.jsonValue["MetricReports"]["@odata.id"] = + "/redfish/v1/TelemetryService/MetricReports"; ++ asyncResp->res.jsonValue["MetricDefinitions"]["@odata.id"] = ++ "/redfish/v1/TelemetryService/MetricDefinitions"; + + crow::connections::systemBus->async_method_call( + [asyncResp]( +-- +2.25.1 |