1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "app.hpp" 6 #include "async_resp.hpp" 7 #include "dbus_singleton.hpp" 8 #include "dbus_utility.hpp" 9 #include "error_messages.hpp" 10 #include "http_request.hpp" 11 #include "logging.hpp" 12 #include "query.hpp" 13 #include "registries/privilege_registry.hpp" 14 #include "utils/chassis_utils.hpp" 15 #include "utils/json_utils.hpp" 16 #include "utils/sensor_utils.hpp" 17 18 #include <asm-generic/errno.h> 19 20 #include <boost/beast/http/field.hpp> 21 #include <boost/beast/http/verb.hpp> 22 #include <boost/system/error_code.hpp> 23 #include <boost/url/format.hpp> 24 #include <nlohmann/json.hpp> 25 26 #include <array> 27 #include <functional> 28 #include <memory> 29 #include <optional> 30 #include <string> 31 #include <string_view> 32 #include <utility> 33 34 namespace redfish 35 { 36 inline void afterGetTemperatureValue( 37 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 38 const std::string& chassisId, const std::string& path, 39 const boost::system::error_code& ec, 40 const dbus::utility::DBusPropertiesMap& valuesDict) 41 { 42 if (ec) 43 { 44 if (ec.value() != EBADR) 45 { 46 BMCWEB_LOG_ERROR("DBUS response error for getAllProperties {}", 47 ec.value()); 48 messages::internalError(asyncResp->res); 49 } 50 return; 51 } 52 53 nlohmann::json item = nlohmann::json::object(); 54 55 /* Don't return an error for a failure to fill in properties from any of 56 * the sensors in the list. Just skip it. 57 */ 58 if (sensor_utils::objectExcerptToJson( 59 path, chassisId, sensor_utils::ChassisSubNode::thermalMetricsNode, 60 "temperature", valuesDict, item)) 61 { 62 nlohmann::json& temperatureReadings = 63 asyncResp->res.jsonValue["TemperatureReadingsCelsius"]; 64 nlohmann::json::array_t* temperatureArray = 65 temperatureReadings.get_ptr<nlohmann::json::array_t*>(); 66 if (temperatureArray == nullptr) 67 { 68 BMCWEB_LOG_ERROR("Missing TemperatureReadingsCelsius Json array"); 69 messages::internalError(asyncResp->res); 70 return; 71 } 72 73 temperatureArray->emplace_back(std::move(item)); 74 asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] = 75 temperatureArray->size(); 76 77 json_util::sortJsonArrayByKey(*temperatureArray, "DataSourceUri"); 78 } 79 } 80 81 inline void handleTemperatureReadingsCelsius( 82 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 83 const std::string& chassisId, const boost::system::error_code& ec, 84 const sensor_utils::SensorServicePathList& sensorsServiceAndPath) 85 { 86 if (ec) 87 { 88 if (ec.value() != EBADR) 89 { 90 BMCWEB_LOG_ERROR("DBUS response error for getAssociatedSubTree {}", 91 ec.value()); 92 messages::internalError(asyncResp->res); 93 } 94 return; 95 } 96 97 asyncResp->res.jsonValue["TemperatureReadingsCelsius"] = 98 nlohmann::json::array_t(); 99 asyncResp->res.jsonValue["TemperatureReadingsCelsius@odata.count"] = 0; 100 101 for (const auto& [service, sensorPath] : sensorsServiceAndPath) 102 { 103 dbus::utility::getAllProperties( 104 *crow::connections::systemBus, service, sensorPath, 105 "xyz.openbmc_project.Sensor.Value", 106 [asyncResp, chassisId, 107 sensorPath](const boost::system::error_code& ec1, 108 const dbus::utility::DBusPropertiesMap& properties) { 109 afterGetTemperatureValue(asyncResp, chassisId, sensorPath, ec1, 110 properties); 111 }); 112 } 113 } 114 115 inline void getTemperatureReadingsCelsius( 116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 117 const std::string& validChassisPath, const std::string& chassisId) 118 { 119 constexpr std::array<std::string_view, 1> interfaces = { 120 "xyz.openbmc_project.Sensor.Value"}; 121 122 sensor_utils::getAllSensorObjects( 123 validChassisPath, "/xyz/openbmc_project/sensors/temperature", 124 interfaces, 1, 125 std::bind_front(handleTemperatureReadingsCelsius, asyncResp, 126 chassisId)); 127 } 128 129 inline void 130 doThermalMetrics(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 131 const std::string& chassisId, 132 const std::optional<std::string>& validChassisPath) 133 { 134 if (!validChassisPath) 135 { 136 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 137 return; 138 } 139 140 asyncResp->res.addHeader( 141 boost::beast::http::field::link, 142 "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby"); 143 asyncResp->res.jsonValue["@odata.type"] = 144 "#ThermalMetrics.v1_0_1.ThermalMetrics"; 145 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 146 "/redfish/v1/Chassis/{}/ThermalSubsystem/ThermalMetrics", chassisId); 147 asyncResp->res.jsonValue["Id"] = "ThermalMetrics"; 148 asyncResp->res.jsonValue["Name"] = "Thermal Metrics"; 149 150 getTemperatureReadingsCelsius(asyncResp, *validChassisPath, chassisId); 151 } 152 153 inline void handleThermalMetricsHead( 154 App& app, const crow::Request& req, 155 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 156 const std::string& chassisId) 157 { 158 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 159 { 160 return; 161 } 162 163 redfish::chassis_utils::getValidChassisPath( 164 asyncResp, chassisId, 165 [asyncResp, 166 chassisId](const std::optional<std::string>& validChassisPath) { 167 if (!validChassisPath) 168 { 169 messages::resourceNotFound(asyncResp->res, "Chassis", 170 chassisId); 171 return; 172 } 173 asyncResp->res.addHeader( 174 boost::beast::http::field::link, 175 "</redfish/v1/JsonSchemas/ThermalMetrics/ThermalMetrics.json>; rel=describedby"); 176 }); 177 } 178 179 inline void 180 handleThermalMetricsGet(App& app, const crow::Request& req, 181 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 182 const std::string& chassisId) 183 { 184 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 185 { 186 return; 187 } 188 189 redfish::chassis_utils::getValidChassisPath( 190 asyncResp, chassisId, 191 std::bind_front(doThermalMetrics, asyncResp, chassisId)); 192 } 193 194 inline void requestRoutesThermalMetrics(App& app) 195 { 196 BMCWEB_ROUTE(app, 197 "/redfish/v1/Chassis/<str>/ThermalSubsystem/ThermalMetrics/") 198 .privileges(redfish::privileges::headThermalMetrics) 199 .methods(boost::beast::http::verb::head)( 200 std::bind_front(handleThermalMetricsHead, std::ref(app))); 201 202 BMCWEB_ROUTE(app, 203 "/redfish/v1/Chassis/<str>/ThermalSubsystem/ThermalMetrics/") 204 .privileges(redfish::privileges::getThermalMetrics) 205 .methods(boost::beast::http::verb::get)( 206 std::bind_front(handleThermalMetricsGet, std::ref(app))); 207 } 208 } // namespace redfish 209