summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch
diff options
context:
space:
mode:
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.patch496
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