Implement TemperatureReadingsCelsius property for ThermalMetrics
The ThermalMetrics schema[1] provides for efficient thermal metric
gathering for thermal sensors. The schema allows retrieving just the
thermal metrics with one Redfish URI. This prevents the additional work
required when returning all the sensor data, or multiple Redfish URI
calls to retrieve the properties for all of the thermal sensors.
This commit implements the TemperatureReadingsCelsius property of
ThermalMetrics[1]. ThermalMetrics is a property of ThermalSubsystem[2].
TemperatureReadingsCelsius is a SensorArrayExcerpt[3].
[1] https://redfish.dmtf.org/schemas/v1/ThermalMetrics.v1_0_1.json
[2] https://redfish.dmtf.org/schemas/v1/ThermalSubsystem.v1_3_2.json
[3] http://redfish.dmtf.org/schemas/v1/Sensor.v1_9_0.json#/definitions/SensorArrayExcerpt
The temperature sensors are found by finding 'all_sensors' endpoints for
specific chassis of D-Bus service
/xyz/openbmc_project/sensors/temperature. An entry of SensorArrayExcerpt
is built for each temperature sensor retrieved.
Implementation Notes:
- Common function sensor_utils::objectPropertiesToJson() is used to
fill in sensor excerpt properties. Currently the only excerpt
ChassisSubNode is ThermalMetrics. However there are others excerpts
defined by Redfish. Right now mostly this is just skipping things,
but I'm expecting when other sensor excerpts are implemented that
some of the other properties may be added for excerpts as well. I'm
expecting the combination of the chassisSubNode and the sensorType
will be used to determine which properties are included for a
particular call to build a sensor Json representation.
- New sensor_utils::objectExcerptToJson() function created. This wraps
sensor_utils::objectPropertiesToJson() and builds DataSourceUri for a
sensor excerpt.
- New sensor_utils::getAllSensorObjects() function created. This builds
list of 'all_sensors' association endpoints for specified D-Bus path
with specified D-Bus interfaces. Callback function is called with
list for handling sensors.
Tested:
1. Redfish Service Validator passed.
2. doGet method:
```
curl -k -H "X-Auth-Token: ${token}" -X GET https://${bmc}/redfish/v1/Chassis/chassis/ThermalSubsystem/ThermalMetrics
{
"@odata.id": "/redfish/v1/Chassis/chassis/ThermalSubsystem/ThermalMetrics",
"@odata.type": "#ThermalMetrics.v1_0_1.ThermalMetrics",
"Id": "ThermalMetrics",
"Name": "Thermal Metrics",
"TemperatureReadingsCelsius": [
{
"DataSourceUri": "/redfish/v1/Chassis/chassis/Sensors/temperature_ps0_temp0",
"Reading": -131072000.0
},
{
"DataSourceUri": "/redfish/v1/Chassis/chassis/Sensors/temperature_ps0_temp1",
"Reading": -131072000.0
},
{
"DataSourceUri": "/redfish/v1/Chassis/chassis/Sensors/temperature_ps0_temp2",
"Reading": -131072000.0
},
{
"DataSourceUri": "/redfish/v1/Chassis/chassis/Sensors/temperature_ps1_temp0",
"Reading": -131072000.0
},
{
"DataSourceUri": "/redfish/v1/Chassis/chassis/Sensors/temperature_ps1_temp1",
"Reading": -131072000.0
},
{
"DataSourceUri": "/redfish/v1/Chassis/chassis/Sensors/temperature_ps1_temp2",
"Reading": -131072000.0
}
],
"TemperatureReadingsCelsius@odata.count": 6
}
```
3. Verification of DataSourceUri:
```
curl -k -H "X-Auth-Token: ${token}" -X GET https://${bmc}/redfish/v1/Chassis/chassis/Sensors/temperature_ps1_temp0
{
"@odata.id": "/redfish/v1/Chassis/chassis/Sensors/temperature_ps1_temp0",
"@odata.type": "#Sensor.v1_2_0.Sensor",
"Id": "temperature_ps1_temp0",
"Name": "ps1 temp0",
"Reading": -131072000.0,
"ReadingType": "Temperature",
"ReadingUnits": "Cel",
"Status": {
"Health": "OK",
"State": "Enabled"
}
}
```
4. A bad chassis ID:
```
curl -k -H "X-Auth-Token: ${token}" -X GET https://${bmc}/redfish/v1/Chassis/chassisBAD/ThermalSubsystem/ThermalMetrics
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The requested resource of type Chassis named 'chassisBAD' was not found.",
"MessageArgs": [
"Chassis",
"chassisBAD"
],
"MessageId": "Base.1.18.1.ResourceNotFound",
"MessageSeverity": "Critical",
"Resolution": "Provide a valid resource identifier and resubmit the request."
}
],
"code": "Base.1.18.1.ResourceNotFound",
"message": "The requested resource of type Chassis named 'chassisBAD' was not found."
}
}
```
Signed-off-by: George Liu <liuxiwei@ieisystem.com>
Change-Id: I6e4ed1f281fd5371c978983b6cc5666badd3752c
Signed-off-by: Janet Adkins <janeta@us.ibm.com>
diff --git a/redfish-core/lib/thermal_metrics.hpp b/redfish-core/lib/thermal_metrics.hpp
index da4da01..abf296c 100644
--- a/redfish-core/lib/thermal_metrics.hpp
+++ b/redfish-core/lib/thermal_metrics.hpp
@@ -1,17 +1,117 @@
#pragma once
#include "app.hpp"
+#include "dbus_utility.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
#include "utils/chassis_utils.hpp"
+#include "utils/json_utils.hpp"
+#include "utils/sensor_utils.hpp"
+#include <boost/system/error_code.hpp>
+
+#include <array>
#include <functional>
#include <memory>
#include <optional>
#include <string>
+#include <string_view>
namespace redfish
{
+inline void afterGetTemperatureValue(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& path,
+ const boost::system::error_code& ec,
+ const dbus::utility::DBusPropertiesMap& valuesDict)
+{
+ if (ec)
+ {
+ if (ec.value() != EBADR)
+ {
+ BMCWEB_LOG_ERROR("DBUS response error for getAllProperties {}",
+ ec.value());
+ messages::internalError(asyncResp->res);
+ }
+ return;
+ }
+
+ nlohmann::json item = nlohmann::json::object();
+
+ /* Don't return an error for a failure to fill in properties from any of
+ * the sensors in the list. Just skip it.
+ */
+ if (sensor_utils::objectExcerptToJson(
+ path, chassisId, sensor_utils::ChassisSubNode::thermalMetricsNode,
+ "temperature", valuesDict, item))
+ {
+ nlohmann::json& temperatureReadings =
+ asyncResp->res.jsonValue["TemperatureReadingsCelsius"];
+ nlohmann::json::array_t* temperatureArray =
+ temperatureReadings.get_ptr<nlohmann::json::array_t*>();
+ if (temperatureArray == nullptr)
+ {
+ BMCWEB_LOG_ERROR("Missing TemperatureReadingsCelsius Json array");
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ temperatureArray->emplace_back(std::move(item));
+ asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] =
+ temperatureArray->size();
+
+ json_util::sortJsonArrayByKey(*temperatureArray, "DataSourceUri");
+ }
+}
+
+inline void handleTemperatureReadingsCelsius(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const boost::system::error_code& ec,
+ const sensor_utils::SensorServicePathList& sensorsServiceAndPath)
+{
+ if (ec)
+ {
+ if (ec.value() != EBADR)
+ {
+ BMCWEB_LOG_ERROR("DBUS response error for getAssociatedSubTree {}",
+ ec.value());
+ messages::internalError(asyncResp->res);
+ }
+ return;
+ }
+
+ asyncResp->res.jsonValue["TemperatureReadingsCelsius"] =
+ nlohmann::json::array_t();
+ asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] = 0;
+
+ for (const auto& [service, sensorPath] : sensorsServiceAndPath)
+ {
+ sdbusplus::asio::getAllProperties(
+ *crow::connections::systemBus, service, sensorPath,
+ "xyz.openbmc_project.Sensor.Value",
+ [asyncResp, chassisId,
+ sensorPath](const boost::system::error_code& ec1,
+ const dbus::utility::DBusPropertiesMap& properties) {
+ afterGetTemperatureValue(asyncResp, chassisId, sensorPath, ec1,
+ properties);
+ });
+ }
+}
+
+inline void getTemperatureReadingsCelsius(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& validChassisPath, const std::string& chassisId)
+{
+ constexpr std::array<std::string_view, 1> interfaces = {
+ "xyz.openbmc_project.Sensor.Value"};
+
+ sensor_utils::getAllSensorObjects(
+ validChassisPath, "/xyz/openbmc_project/sensors/temperature",
+ interfaces, 1,
+ std::bind_front(handleTemperatureReadingsCelsius, asyncResp,
+ chassisId));
+}
+
inline void
doThermalMetrics(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId,
@@ -32,6 +132,8 @@
"/redfish/v1/Chassis/{}/ThermalSubsystem/ThermalMetrics", chassisId);
asyncResp->res.jsonValue["Id"] = "ThermalMetrics";
asyncResp->res.jsonValue["Name"] = "Thermal Metrics";
+
+ getTemperatureReadingsCelsius(asyncResp, *validChassisPath, chassisId);
}
inline void handleThermalMetricsHead(