1 #pragma once 2 3 #include "app.hpp" 4 #include "dbus_utility.hpp" 5 #include "query.hpp" 6 #include "registries/privilege_registry.hpp" 7 #include "utils/chassis_utils.hpp" 8 9 #include <boost/system/error_code.hpp> 10 #include <boost/url/format.hpp> 11 12 #include <memory> 13 #include <optional> 14 #include <string> 15 16 namespace redfish 17 { 18 19 inline void 20 updatePowerSupplyList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 21 const std::string& chassisId, 22 const std::string& powerSupplyPath) 23 { 24 std::string powerSupplyName = 25 sdbusplus::message::object_path(powerSupplyPath).filename(); 26 if (powerSupplyName.empty()) 27 { 28 return; 29 } 30 31 nlohmann::json item = nlohmann::json::object(); 32 item["@odata.id"] = boost::urls::format( 33 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, 34 powerSupplyName); 35 36 nlohmann::json& powerSupplyList = asyncResp->res.jsonValue["Members"]; 37 powerSupplyList.emplace_back(std::move(item)); 38 asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size(); 39 } 40 41 inline void 42 doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 43 const std::string& chassisId, 44 const std::optional<std::string>& validChassisPath) 45 { 46 if (!validChassisPath) 47 { 48 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 49 return; 50 } 51 52 asyncResp->res.addHeader( 53 boost::beast::http::field::link, 54 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); 55 asyncResp->res.jsonValue["@odata.type"] = 56 "#PowerSupplyCollection.PowerSupplyCollection"; 57 asyncResp->res.jsonValue["Name"] = "Power Supply Collection"; 58 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 59 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId); 60 asyncResp->res.jsonValue["Description"] = 61 "The collection of PowerSupply resource instances."; 62 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 63 asyncResp->res.jsonValue["Members@odata.count"] = 0; 64 65 std::string powerPath = *validChassisPath + "/powered_by"; 66 dbus::utility::getAssociationEndPoints( 67 powerPath, [asyncResp, chassisId]( 68 const boost::system::error_code& ec, 69 const dbus::utility::MapperEndPoints& endpoints) { 70 if (ec) 71 { 72 if (ec.value() != EBADR) 73 { 74 BMCWEB_LOG_ERROR << "DBUS response error" << ec.value(); 75 messages::internalError(asyncResp->res); 76 } 77 return; 78 } 79 80 for (const auto& endpoint : endpoints) 81 { 82 updatePowerSupplyList(asyncResp, chassisId, endpoint); 83 } 84 }); 85 } 86 87 inline void handlePowerSupplyCollectionHead( 88 App& app, const crow::Request& req, 89 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 90 const std::string& chassisId) 91 { 92 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 93 { 94 return; 95 } 96 97 redfish::chassis_utils::getValidChassisPath( 98 asyncResp, chassisId, 99 [asyncResp, 100 chassisId](const std::optional<std::string>& validChassisPath) { 101 if (!validChassisPath) 102 { 103 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 104 return; 105 } 106 asyncResp->res.addHeader( 107 boost::beast::http::field::link, 108 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); 109 }); 110 } 111 112 inline void handlePowerSupplyCollectionGet( 113 App& app, const crow::Request& req, 114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 115 const std::string& chassisId) 116 { 117 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 118 { 119 return; 120 } 121 122 redfish::chassis_utils::getValidChassisPath( 123 asyncResp, chassisId, 124 std::bind_front(doPowerSupplyCollection, asyncResp, chassisId)); 125 } 126 127 inline void requestRoutesPowerSupplyCollection(App& app) 128 { 129 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/") 130 .privileges(redfish::privileges::headPowerSupplyCollection) 131 .methods(boost::beast::http::verb::head)( 132 std::bind_front(handlePowerSupplyCollectionHead, std::ref(app))); 133 134 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/") 135 .privileges(redfish::privileges::getPowerSupplyCollection) 136 .methods(boost::beast::http::verb::get)( 137 std::bind_front(handlePowerSupplyCollectionGet, std::ref(app))); 138 } 139 140 inline bool checkPowerSupplyId(const std::string& powerSupplyPath, 141 const std::string& powerSupplyId) 142 { 143 std::string powerSupplyName = 144 sdbusplus::message::object_path(powerSupplyPath).filename(); 145 146 return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId); 147 } 148 149 inline void getValidPowerSupplyPath( 150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 151 const std::string& validChassisPath, const std::string& powerSupplyId, 152 std::function<void(const std::string& powerSupplyPath)>&& callback) 153 { 154 std::string powerPath = validChassisPath + "/powered_by"; 155 dbus::utility::getAssociationEndPoints( 156 powerPath, [asyncResp, powerSupplyId, callback{std::move(callback)}]( 157 const boost::system::error_code& ec, 158 const dbus::utility::MapperEndPoints& endpoints) { 159 if (ec) 160 { 161 if (ec.value() != EBADR) 162 { 163 BMCWEB_LOG_ERROR 164 << "DBUS response error for getAssociationEndPoints" 165 << ec.value(); 166 messages::internalError(asyncResp->res); 167 return; 168 } 169 messages::resourceNotFound(asyncResp->res, "PowerSupplies", 170 powerSupplyId); 171 return; 172 } 173 174 for (const auto& endpoint : endpoints) 175 { 176 if (checkPowerSupplyId(endpoint, powerSupplyId)) 177 { 178 callback(endpoint); 179 return; 180 } 181 } 182 183 if (!endpoints.empty()) 184 { 185 BMCWEB_LOG_WARNING << "Power supply not found: " 186 << powerSupplyId; 187 messages::resourceNotFound(asyncResp->res, "PowerSupplies", 188 powerSupplyId); 189 return; 190 } 191 }); 192 } 193 194 inline void 195 getPowerSupplyState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 196 const std::string& service, const std::string& path) 197 { 198 sdbusplus::asio::getProperty<bool>( 199 *crow::connections::systemBus, service, path, 200 "xyz.openbmc_project.Inventory.Item", "Present", 201 [asyncResp](const boost::system::error_code& ec, const bool value) { 202 if (ec) 203 { 204 if (ec.value() != EBADR) 205 { 206 BMCWEB_LOG_ERROR << "DBUS response error for State " 207 << ec.value(); 208 messages::internalError(asyncResp->res); 209 } 210 return; 211 } 212 213 if (!value) 214 { 215 asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 216 } 217 }); 218 } 219 220 inline void 221 getPowerSupplyHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 222 const std::string& service, const std::string& path) 223 { 224 sdbusplus::asio::getProperty<bool>( 225 *crow::connections::systemBus, service, path, 226 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 227 [asyncResp](const boost::system::error_code& ec, const bool value) { 228 if (ec) 229 { 230 if (ec.value() != EBADR) 231 { 232 BMCWEB_LOG_ERROR << "DBUS response error for Health " 233 << ec.value(); 234 messages::internalError(asyncResp->res); 235 } 236 return; 237 } 238 239 if (!value) 240 { 241 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 242 } 243 }); 244 } 245 246 inline void 247 doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 248 const std::string& chassisId, 249 const std::string& powerSupplyId, 250 const std::optional<std::string>& validChassisPath) 251 { 252 if (!validChassisPath) 253 { 254 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 255 return; 256 } 257 258 // Get the correct Path and Service that match the input parameters 259 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 260 [asyncResp, chassisId, powerSupplyId]( 261 const std::string& powerSupplyPath) { 262 asyncResp->res.addHeader( 263 boost::beast::http::field::link, 264 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 265 asyncResp->res.jsonValue["@odata.type"] = 266 "#PowerSupply.v1_5_0.PowerSupply"; 267 asyncResp->res.jsonValue["Name"] = "Power Supply"; 268 asyncResp->res.jsonValue["Id"] = powerSupplyId; 269 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 270 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, 271 powerSupplyId); 272 273 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 274 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 275 276 constexpr std::array<std::string_view, 1> interfaces = { 277 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 278 dbus::utility::getDbusObject( 279 powerSupplyPath, interfaces, 280 [asyncResp, 281 powerSupplyPath](const boost::system::error_code& ec, 282 const dbus::utility::MapperGetObject& object) { 283 if (ec || object.empty()) 284 { 285 messages::internalError(asyncResp->res); 286 return; 287 } 288 289 getPowerSupplyState(asyncResp, object.begin()->first, 290 powerSupplyPath); 291 getPowerSupplyHealth(asyncResp, object.begin()->first, 292 powerSupplyPath); 293 }); 294 }); 295 } 296 297 inline void 298 handlePowerSupplyHead(App& app, const crow::Request& req, 299 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 300 const std::string& chassisId, 301 const std::string& powerSupplyId) 302 { 303 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 304 { 305 return; 306 } 307 308 redfish::chassis_utils::getValidChassisPath( 309 asyncResp, chassisId, 310 [asyncResp, chassisId, 311 powerSupplyId](const std::optional<std::string>& validChassisPath) { 312 if (!validChassisPath) 313 { 314 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 315 return; 316 } 317 318 // Get the correct Path and Service that match the input parameters 319 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 320 [asyncResp](const std::string&) { 321 asyncResp->res.addHeader( 322 boost::beast::http::field::link, 323 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 324 }); 325 }); 326 } 327 328 inline void 329 handlePowerSupplyGet(App& app, const crow::Request& req, 330 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 331 const std::string& chassisId, 332 const std::string& powerSupplyId) 333 { 334 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 335 { 336 return; 337 } 338 339 redfish::chassis_utils::getValidChassisPath( 340 asyncResp, chassisId, 341 std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId)); 342 } 343 344 inline void requestRoutesPowerSupply(App& app) 345 { 346 BMCWEB_ROUTE( 347 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 348 .privileges(redfish::privileges::headPowerSupply) 349 .methods(boost::beast::http::verb::head)( 350 std::bind_front(handlePowerSupplyHead, std::ref(app))); 351 352 BMCWEB_ROUTE( 353 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 354 .privileges(redfish::privileges::getPowerSupply) 355 .methods(boost::beast::http::verb::get)( 356 std::bind_front(handlePowerSupplyGet, std::ref(app))); 357 } 358 359 } // namespace redfish 360