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.patch619
1 files changed, 619 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..f5226fe6e
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-phosphor/interfaces/bmcweb/telemetry/0001-Add-support-for-MetricDefinition-scheme.patch
@@ -0,0 +1,619 @@
+From 32e557279450226ed9c06312649d90b802f3d4c5 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 BMCWEB_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM will be enabled by
+default (meson option redfish-new-powersubsystem-thermalsubsystem) 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/Fan_Pwm"
+ },
+ {
+ "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Fan_Tach"
+ },
+ {
+ "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/HostCpuUtilization"
+ },
+ {
+ "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/HostMemoryBandwidthUtilization"
+ },
+ {
+ "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/HostPciBandwidthUtilization"
+ },
+ {
+ "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Inlet_BRD_Temp"
+ },
+ {
+ "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Left_Rear_Board_Temp"
+ }
+ ],
+ "Members@odata.count": 7,
+ "Name": "Metric Definition Collection"
+}
+
+GET /redfish/v1/TelemetryService/MetricDefinitions/Fan_Tach
+{
+ "@odata.id": "/redfish/v1/TelemetryService/MetricDefinitions/Fan_Tach",
+ "@odata.type": "#MetricDefinition.v1_0_3.MetricDefinition",
+ "Id": "Fan_Tach",
+ "IsLinear": true,
+ "MaxReadingRange": 25000.0,
+ "MetricDataType": "Decimal",
+ "MetricProperties": [
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/0/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/1/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/2/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/3/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/4/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/5/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/6/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/7/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/8/Reading",
+ "/redfish/v1/Chassis/AC_Baseboard/Thermal#/Fans/9/Reading"
+ ],
+ "MetricType": "Gauge",
+ "MinReadingRange": 0.0,
+ "Name": "Fan_Tach",
+ "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 | 368 ++++++++++++++++++
+ redfish-core/lib/telemetry_service.hpp | 3 +-
+ 5 files changed, 433 insertions(+), 1 deletion(-)
+ 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 0a97150..67c5af2 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"
+@@ -200,6 +201,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..347c297
+--- /dev/null
++++ b/redfish-core/lib/metric_definition.hpp
+@@ -0,0 +1,368 @@
++#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
++{
++
++struct ValueVisitor
++{
++ ValueVisitor(boost::system::error_code& ec) : ec(ec)
++ {}
++
++ template <class T>
++ double operator()(T value) const
++ {
++ return static_cast<double>(value);
++ }
++
++ double operator()(std::monostate) const
++ {
++ ec = boost::system::errc::make_error_code(
++ boost::system::errc::invalid_argument);
++ return double{};
++ }
++
++ boost::system::error_code& ec;
++};
++
++inline void getReadingRange(
++ const std::string& service, const std::string& path,
++ const std::string& property,
++ std::function<void(boost::system::error_code, double)> callback)
++{
++ crow::connections::systemBus->async_method_call(
++ [callback = std::move(callback)](
++ boost::system::error_code ec,
++ const std::variant<std::monostate, double, uint64_t, int64_t,
++ uint32_t, int32_t, uint16_t, int16_t>&
++ valueVariant) {
++ if (ec)
++ {
++ callback(ec, double{});
++ return;
++ }
++
++ const double value = std::visit(ValueVisitor(ec), valueVariant);
++
++ callback(ec, value);
++ },
++ service, path, "org.freedesktop.DBus.Properties", "Get",
++ "xyz.openbmc_project.Sensor.Value", property);
++}
++
++inline void
++ fillMinMaxReadingRange(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& serviceName,
++ const std::string& sensorPath)
++{
++ asyncResp->res.jsonValue["MetricType"] = "Numeric";
++
++ telemetry::getReadingRange(
++ serviceName, sensorPath, "MinValue",
++ [asyncResp](boost::system::error_code ec, double readingRange) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ if (std::isfinite(readingRange))
++ {
++ asyncResp->res.jsonValue["MetricType"] = "Gauge";
++
++ asyncResp->res.jsonValue["MinReadingRange"] = readingRange;
++ }
++ });
++
++ telemetry::getReadingRange(
++ serviceName, sensorPath, "MaxValue",
++ [asyncResp](boost::system::error_code ec, double readingRange) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++
++ if (std::isfinite(readingRange))
++ {
++ asyncResp->res.jsonValue["MetricType"] = "Gauge";
++
++ asyncResp->res.jsonValue["MaxReadingRange"] = readingRange;
++ }
++ });
++}
++
++inline void getSensorService(
++ const std::string& sensorPath,
++ std::function<void(boost::system::error_code, const std::string&)> callback)
++{
++ using ResultType = std::pair<
++ std::string,
++ std::vector<std::pair<std::string, std::vector<std::string>>>>;
++
++ crow::connections::systemBus->async_method_call(
++ [sensorPath, callback = std::move(callback)](
++ boost::system::error_code ec,
++ const std::vector<ResultType>& result) {
++ if (ec)
++ {
++ callback(ec, std::string{});
++ return;
++ }
++
++ for (const auto& [path, serviceToInterfaces] : result)
++ {
++ if (path == sensorPath)
++ {
++ for (const auto& [service, interfaces] :
++ serviceToInterfaces)
++ {
++ callback(boost::system::errc::make_error_code(
++ boost::system::errc::success),
++ service);
++ return;
++ }
++ }
++ }
++
++ callback(boost::system::errc::make_error_code(
++ boost::system::errc::no_such_file_or_directory),
++ std::string{});
++ },
++ "xyz.openbmc_project.ObjectMapper",
++ "/xyz/openbmc_project/object_mapper",
++ "xyz.openbmc_project.ObjectMapper", "GetSubTree",
++ "/xyz/openbmc_project/sensors", 2,
++ std::array{"xyz.openbmc_project.Sensor.Value"});
++}
++
++constexpr auto metricDefinitionMapping = std::array{
++ std::pair{"fan_pwm", "Fan_Pwm"}, std::pair{"fan_tach", "Fan_Tach"}};
++
++std::string mapSensorToMetricDefinition(const std::string& sensorPath)
++{
++ sdbusplus::message::object_path sensorObjectPath{sensorPath};
++
++ const auto it = std::find_if(
++ metricDefinitionMapping.begin(), metricDefinitionMapping.end(),
++ [&sensorObjectPath](const auto& item) {
++ return item.first == sensorObjectPath.parent_path().filename();
++ });
++
++ const char* metricDefinitionPath =
++ "/redfish/v1/TelemetryService/MetricDefinitions/";
++
++ if (it != metricDefinitionMapping.end())
++ {
++ return std::string{metricDefinitionPath} + it->second;
++ }
++
++ return metricDefinitionPath + sensorObjectPath.filename();
++}
++
++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 counter = std::make_shared<std::pair<
++ boost::container::flat_map<std::string, std::string>, size_t>>();
++
++ auto handleRetrieveUriToDbusMap =
++ [counter, 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);
++ counter->second = 0u;
++ callback(boost::system::errc::make_error_code(
++ boost::system::errc::io_error),
++ {});
++ return;
++ }
++
++ for (const auto& [key, value] : uriToDbus)
++ {
++ counter->first[key] = value;
++ }
++
++ if (--counter->second == 0u)
++ {
++ callback(boost::system::errc::make_error_code(
++ boost::system::errc::success),
++ counter->first);
++ }
++ };
++
++ for (const std::string& chassisName : chassisNames)
++ {
++ for (const auto& [sensorNode, dbusPaths] : sensors::dbus::paths)
++ {
++ ++counter->second;
++ 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;
++ }
++
++ std::set<std::string> members;
++
++ for (const auto& [uri, dbusPath] : uriToDbus)
++ {
++ members.insert(
++ telemetry::mapSensorToMetricDefinition(
++ dbusPath));
++ }
++
++ for (const std::string& odataId : members)
++ {
++ asyncResp->res.jsonValue["Members"].push_back(
++ {{"@odata.id", odataId}});
++ }
++
++ asyncResp->res.jsonValue["Members@odata.count"] =
++ asyncResp->res.jsonValue["Members"].size();
++ });
++
++ 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;
++ });
++}
++
++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& name) {
++ telemetry::mapRedfishUriToDbusPath(
++ [asyncResp, name](
++ 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;
++ }
++
++ std::string odataId = telemetry::metricDefinitionUri + name;
++ boost::container::flat_map<std::string, std::string>
++ matchingUris;
++
++ for (const auto& [uri, dbusPath] : uriToDbus)
++ {
++ if (telemetry::mapSensorToMetricDefinition(dbusPath) ==
++ odataId)
++ {
++ matchingUris.emplace(uri, dbusPath);
++ }
++ }
++
++ if (matchingUris.empty())
++ {
++ messages::resourceNotFound(asyncResp->res,
++ "MetricDefinition", name);
++ return;
++ }
++
++ std::string sensorPath = matchingUris.begin()->second;
++
++ telemetry::getSensorService(
++ sensorPath,
++ [asyncResp, name, odataId = std::move(odataId),
++ sensorPath, matchingUris = std::move(matchingUris)](
++ boost::system::error_code ec,
++ const std::string& serviceName) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ BMCWEB_LOG_ERROR << "getServiceSensorFailed: "
++ << ec.value();
++ return;
++ }
++
++ asyncResp->res.jsonValue["Id"] = name;
++ asyncResp->res.jsonValue["Name"] = name;
++ asyncResp->res.jsonValue["@odata.id"] = odataId;
++ asyncResp->res.jsonValue["@odata.type"] =
++ "#MetricDefinition.v1_0_3.MetricDefinition";
++ asyncResp->res.jsonValue["MetricDataType"] =
++ "Decimal";
++ asyncResp->res.jsonValue["IsLinear"] = true;
++ asyncResp->res.jsonValue["Units"] =
++ sensors::toReadingUnits(
++ sdbusplus::message::object_path{sensorPath}
++ .parent_path()
++ .filename());
++
++ for (const auto& [uri, dbusPath] : matchingUris)
++ {
++ asyncResp->res.jsonValue["MetricProperties"]
++ .push_back(uri);
++ }
++
++ telemetry::fillMinMaxReadingRange(
++ asyncResp, serviceName, sensorPath);
++ });
++ });
++ });
++}
++
++} // namespace redfish
+diff --git a/redfish-core/lib/telemetry_service.hpp b/redfish-core/lib/telemetry_service.hpp
+index 8ecc591..027b51b 100644
+--- a/redfish-core/lib/telemetry_service.hpp
++++ b/redfish-core/lib/telemetry_service.hpp
+@@ -18,11 +18,12 @@ inline void handleTelemetryServiceGet(
+ asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/TelemetryService";
+ asyncResp->res.jsonValue["Id"] = "TelemetryService";
+ asyncResp->res.jsonValue["Name"] = "Telemetry Service";
+-
+ asyncResp->res.jsonValue["MetricReportDefinitions"]["@odata.id"] =
+ "/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](const boost::system::error_code ec,
+--
+2.25.1