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 #include "utils/dbus_utils.hpp" 9 #include "utils/json_utils.hpp" 10 11 #include <boost/system/error_code.hpp> 12 #include <boost/url/format.hpp> 13 14 #include <memory> 15 #include <optional> 16 #include <string> 17 18 namespace redfish 19 { 20 21 static constexpr std::array<std::string_view, 1> powerSupplyInterface = { 22 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 23 24 inline void updatePowerSupplyList( 25 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 26 const std::string& chassisId, 27 const dbus::utility::MapperGetSubTreePathsResponse& powerSupplyPaths) 28 { 29 nlohmann::json& powerSupplyList = asyncResp->res.jsonValue["Members"]; 30 for (const std::string& powerSupplyPath : powerSupplyPaths) 31 { 32 std::string powerSupplyName = 33 sdbusplus::message::object_path(powerSupplyPath).filename(); 34 if (powerSupplyName.empty()) 35 { 36 continue; 37 } 38 39 nlohmann::json item = nlohmann::json::object(); 40 item["@odata.id"] = boost::urls::format( 41 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, 42 powerSupplyName); 43 44 powerSupplyList.emplace_back(std::move(item)); 45 } 46 asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size(); 47 } 48 49 inline void 50 doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 51 const std::string& chassisId, 52 const std::optional<std::string>& validChassisPath) 53 { 54 if (!validChassisPath) 55 { 56 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 57 return; 58 } 59 60 asyncResp->res.addHeader( 61 boost::beast::http::field::link, 62 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); 63 asyncResp->res.jsonValue["@odata.type"] = 64 "#PowerSupplyCollection.PowerSupplyCollection"; 65 asyncResp->res.jsonValue["Name"] = "Power Supply Collection"; 66 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 67 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId); 68 asyncResp->res.jsonValue["Description"] = 69 "The collection of PowerSupply resource instances."; 70 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 71 asyncResp->res.jsonValue["Members@odata.count"] = 0; 72 73 std::string powerPath = *validChassisPath + "/powered_by"; 74 dbus::utility::getAssociatedSubTreePaths( 75 powerPath, 76 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, 77 powerSupplyInterface, 78 [asyncResp, chassisId]( 79 const boost::system::error_code& ec, 80 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { 81 if (ec) 82 { 83 if (ec.value() != EBADR) 84 { 85 BMCWEB_LOG_ERROR << "DBUS response error" << ec.value(); 86 messages::internalError(asyncResp->res); 87 } 88 return; 89 } 90 91 updatePowerSupplyList(asyncResp, chassisId, subtreePaths); 92 }); 93 } 94 95 inline void handlePowerSupplyCollectionHead( 96 App& app, const crow::Request& req, 97 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 98 const std::string& chassisId) 99 { 100 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 101 { 102 return; 103 } 104 105 redfish::chassis_utils::getValidChassisPath( 106 asyncResp, chassisId, 107 [asyncResp, 108 chassisId](const std::optional<std::string>& validChassisPath) { 109 if (!validChassisPath) 110 { 111 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 112 return; 113 } 114 asyncResp->res.addHeader( 115 boost::beast::http::field::link, 116 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); 117 }); 118 } 119 120 inline void handlePowerSupplyCollectionGet( 121 App& app, const crow::Request& req, 122 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 123 const std::string& chassisId) 124 { 125 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 126 { 127 return; 128 } 129 130 redfish::chassis_utils::getValidChassisPath( 131 asyncResp, chassisId, 132 std::bind_front(doPowerSupplyCollection, asyncResp, chassisId)); 133 } 134 135 inline void requestRoutesPowerSupplyCollection(App& app) 136 { 137 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/") 138 .privileges(redfish::privileges::headPowerSupplyCollection) 139 .methods(boost::beast::http::verb::head)( 140 std::bind_front(handlePowerSupplyCollectionHead, std::ref(app))); 141 142 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/") 143 .privileges(redfish::privileges::getPowerSupplyCollection) 144 .methods(boost::beast::http::verb::get)( 145 std::bind_front(handlePowerSupplyCollectionGet, std::ref(app))); 146 } 147 148 inline bool checkPowerSupplyId(const std::string& powerSupplyPath, 149 const std::string& powerSupplyId) 150 { 151 std::string powerSupplyName = 152 sdbusplus::message::object_path(powerSupplyPath).filename(); 153 154 return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId); 155 } 156 157 inline void getValidPowerSupplyPath( 158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 159 const std::string& validChassisPath, const std::string& powerSupplyId, 160 std::function<void(const std::string& powerSupplyPath)>&& callback) 161 { 162 std::string powerPath = validChassisPath + "/powered_by"; 163 dbus::utility::getAssociatedSubTreePaths( 164 powerPath, 165 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, 166 powerSupplyInterface, 167 [asyncResp, powerSupplyId, callback{std::move(callback)}]( 168 const boost::system::error_code& ec, 169 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { 170 if (ec) 171 { 172 if (ec.value() != EBADR) 173 { 174 BMCWEB_LOG_ERROR 175 << "DBUS response error for getAssociatedSubTreePaths" 176 << ec.value(); 177 messages::internalError(asyncResp->res); 178 return; 179 } 180 messages::resourceNotFound(asyncResp->res, "PowerSupplies", 181 powerSupplyId); 182 return; 183 } 184 185 for (const std::string& path : subtreePaths) 186 { 187 if (checkPowerSupplyId(path, powerSupplyId)) 188 { 189 callback(path); 190 return; 191 } 192 } 193 194 if (!subtreePaths.empty()) 195 { 196 BMCWEB_LOG_WARNING << "Power supply not found: " << powerSupplyId; 197 messages::resourceNotFound(asyncResp->res, "PowerSupplies", 198 powerSupplyId); 199 return; 200 } 201 }); 202 } 203 204 inline void 205 getPowerSupplyState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 206 const std::string& service, const std::string& path) 207 { 208 sdbusplus::asio::getProperty<bool>( 209 *crow::connections::systemBus, service, path, 210 "xyz.openbmc_project.Inventory.Item", "Present", 211 [asyncResp](const boost::system::error_code& ec, const bool value) { 212 if (ec) 213 { 214 if (ec.value() != EBADR) 215 { 216 BMCWEB_LOG_ERROR << "DBUS response error for State " 217 << ec.value(); 218 messages::internalError(asyncResp->res); 219 } 220 return; 221 } 222 223 if (!value) 224 { 225 asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 226 } 227 }); 228 } 229 230 inline void 231 getPowerSupplyHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 232 const std::string& service, const std::string& path) 233 { 234 sdbusplus::asio::getProperty<bool>( 235 *crow::connections::systemBus, service, path, 236 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 237 [asyncResp](const boost::system::error_code& ec, const bool value) { 238 if (ec) 239 { 240 if (ec.value() != EBADR) 241 { 242 BMCWEB_LOG_ERROR << "DBUS response error for Health " 243 << ec.value(); 244 messages::internalError(asyncResp->res); 245 } 246 return; 247 } 248 249 if (!value) 250 { 251 asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 252 } 253 }); 254 } 255 256 inline void 257 getPowerSupplyAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 258 const std::string& service, const std::string& path) 259 { 260 sdbusplus::asio::getAllProperties( 261 *crow::connections::systemBus, service, path, 262 "xyz.openbmc_project.Inventory.Decorator.Asset", 263 [asyncResp](const boost::system::error_code& ec, 264 const dbus::utility::DBusPropertiesMap& propertiesList) { 265 if (ec) 266 { 267 if (ec.value() != EBADR) 268 { 269 BMCWEB_LOG_ERROR << "DBUS response error for Asset " 270 << ec.value(); 271 messages::internalError(asyncResp->res); 272 } 273 return; 274 } 275 276 const std::string* partNumber = nullptr; 277 const std::string* serialNumber = nullptr; 278 const std::string* manufacturer = nullptr; 279 const std::string* model = nullptr; 280 const std::string* sparePartNumber = nullptr; 281 282 const bool success = sdbusplus::unpackPropertiesNoThrow( 283 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", 284 partNumber, "SerialNumber", serialNumber, "Manufacturer", 285 manufacturer, "Model", model, "SparePartNumber", sparePartNumber); 286 287 if (!success) 288 { 289 messages::internalError(asyncResp->res); 290 return; 291 } 292 293 if (partNumber != nullptr) 294 { 295 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 296 } 297 298 if (serialNumber != nullptr) 299 { 300 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 301 } 302 303 if (manufacturer != nullptr) 304 { 305 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 306 } 307 308 if (model != nullptr) 309 { 310 asyncResp->res.jsonValue["Model"] = *model; 311 } 312 313 // SparePartNumber is optional on D-Bus so skip if it is empty 314 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 315 { 316 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 317 } 318 }); 319 } 320 321 inline void getPowerSupplyFirmwareVersion( 322 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 323 const std::string& service, const std::string& path) 324 { 325 sdbusplus::asio::getProperty<std::string>( 326 *crow::connections::systemBus, service, path, 327 "xyz.openbmc_project.Software.Version", "Version", 328 [asyncResp](const boost::system::error_code& ec, 329 const std::string& value) { 330 if (ec) 331 { 332 if (ec.value() != EBADR) 333 { 334 BMCWEB_LOG_ERROR << "DBUS response error for FirmwareVersion " 335 << ec.value(); 336 messages::internalError(asyncResp->res); 337 } 338 return; 339 } 340 asyncResp->res.jsonValue["FirmwareVersion"] = value; 341 }); 342 } 343 344 inline void 345 getPowerSupplyLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 346 const std::string& service, const std::string& path) 347 { 348 sdbusplus::asio::getProperty<std::string>( 349 *crow::connections::systemBus, service, path, 350 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 351 [asyncResp](const boost::system::error_code& ec, 352 const std::string& value) { 353 if (ec) 354 { 355 if (ec.value() != EBADR) 356 { 357 BMCWEB_LOG_ERROR << "DBUS response error for Location " 358 << ec.value(); 359 messages::internalError(asyncResp->res); 360 } 361 return; 362 } 363 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 364 value; 365 }); 366 } 367 368 inline void 369 doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 370 const std::string& chassisId, 371 const std::string& powerSupplyId, 372 const std::optional<std::string>& validChassisPath) 373 { 374 if (!validChassisPath) 375 { 376 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 377 return; 378 } 379 380 // Get the correct Path and Service that match the input parameters 381 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 382 [asyncResp, chassisId, powerSupplyId]( 383 const std::string& powerSupplyPath) { 384 asyncResp->res.addHeader( 385 boost::beast::http::field::link, 386 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 387 asyncResp->res.jsonValue["@odata.type"] = 388 "#PowerSupply.v1_5_0.PowerSupply"; 389 asyncResp->res.jsonValue["Name"] = "Power Supply"; 390 asyncResp->res.jsonValue["Id"] = powerSupplyId; 391 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 392 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, 393 powerSupplyId); 394 395 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 396 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 397 398 dbus::utility::getDbusObject( 399 powerSupplyPath, powerSupplyInterface, 400 [asyncResp, 401 powerSupplyPath](const boost::system::error_code& ec, 402 const dbus::utility::MapperGetObject& object) { 403 if (ec || object.empty()) 404 { 405 messages::internalError(asyncResp->res); 406 return; 407 } 408 409 getPowerSupplyState(asyncResp, object.begin()->first, 410 powerSupplyPath); 411 getPowerSupplyHealth(asyncResp, object.begin()->first, 412 powerSupplyPath); 413 getPowerSupplyAsset(asyncResp, object.begin()->first, 414 powerSupplyPath); 415 getPowerSupplyFirmwareVersion(asyncResp, object.begin()->first, 416 powerSupplyPath); 417 getPowerSupplyLocation(asyncResp, object.begin()->first, 418 powerSupplyPath); 419 }); 420 }); 421 } 422 423 inline void 424 handlePowerSupplyHead(App& app, const crow::Request& req, 425 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 426 const std::string& chassisId, 427 const std::string& powerSupplyId) 428 { 429 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 430 { 431 return; 432 } 433 434 redfish::chassis_utils::getValidChassisPath( 435 asyncResp, chassisId, 436 [asyncResp, chassisId, 437 powerSupplyId](const std::optional<std::string>& validChassisPath) { 438 if (!validChassisPath) 439 { 440 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 441 return; 442 } 443 444 // Get the correct Path and Service that match the input parameters 445 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 446 [asyncResp](const std::string&) { 447 asyncResp->res.addHeader( 448 boost::beast::http::field::link, 449 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 450 }); 451 }); 452 } 453 454 inline void 455 handlePowerSupplyGet(App& app, const crow::Request& req, 456 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 457 const std::string& chassisId, 458 const std::string& powerSupplyId) 459 { 460 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 461 { 462 return; 463 } 464 465 redfish::chassis_utils::getValidChassisPath( 466 asyncResp, chassisId, 467 std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId)); 468 } 469 470 inline void requestRoutesPowerSupply(App& app) 471 { 472 BMCWEB_ROUTE( 473 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 474 .privileges(redfish::privileges::headPowerSupply) 475 .methods(boost::beast::http::verb::head)( 476 std::bind_front(handlePowerSupplyHead, std::ref(app))); 477 478 BMCWEB_ROUTE( 479 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 480 .privileges(redfish::privileges::getPowerSupply) 481 .methods(boost::beast::http::verb::get)( 482 std::bind_front(handlePowerSupplyGet, std::ref(app))); 483 } 484 485 } // namespace redfish 486