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 handleGetEfficiencyResponse( 369 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 370 const boost::system::error_code& ec, uint32_t value) 371 { 372 if (ec) 373 { 374 if (ec.value() != EBADR) 375 { 376 BMCWEB_LOG_ERROR << "DBUS response error for DeratingFactor " 377 << ec.value(); 378 messages::internalError(asyncResp->res); 379 } 380 return; 381 } 382 // The PDI default value is 0, if it hasn't been set leave off 383 if (value == 0) 384 { 385 return; 386 } 387 388 nlohmann::json::array_t efficiencyRatings; 389 nlohmann::json::object_t efficiencyPercent; 390 efficiencyPercent["EfficiencyPercent"] = value; 391 efficiencyRatings.emplace_back(std::move(efficiencyPercent)); 392 asyncResp->res.jsonValue["EfficiencyRatings"] = 393 std::move(efficiencyRatings); 394 } 395 396 inline void handlePowerSupplyAttributesSubTreeResponse( 397 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 398 const boost::system::error_code& ec, 399 const dbus::utility::MapperGetSubTreeResponse& subtree) 400 { 401 if (ec) 402 { 403 if (ec.value() != EBADR) 404 { 405 BMCWEB_LOG_ERROR << "DBUS response error for EfficiencyPercent " 406 << ec.value(); 407 messages::internalError(asyncResp->res); 408 } 409 return; 410 } 411 412 if (subtree.empty()) 413 { 414 BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!"; 415 return; 416 } 417 418 if (subtree.size() != 1) 419 { 420 BMCWEB_LOG_ERROR 421 << "Unexpected number of paths returned by getSubTree: " 422 << subtree.size(); 423 messages::internalError(asyncResp->res); 424 return; 425 } 426 427 const auto& [path, serviceMap] = *subtree.begin(); 428 const auto& [service, interfaces] = *serviceMap.begin(); 429 sdbusplus::asio::getProperty<uint32_t>( 430 *crow::connections::systemBus, service, path, 431 "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor", 432 [asyncResp](const boost::system::error_code& ec1, uint32_t value) { 433 handleGetEfficiencyResponse(asyncResp, ec1, value); 434 }); 435 } 436 437 inline void 438 getEfficiencyPercent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 439 { 440 constexpr std::array<std::string_view, 1> efficiencyIntf = { 441 "xyz.openbmc_project.Control.PowerSupplyAttributes"}; 442 443 dbus::utility::getSubTree( 444 "/xyz/openbmc_project", 0, efficiencyIntf, 445 [asyncResp](const boost::system::error_code& ec, 446 const dbus::utility::MapperGetSubTreeResponse& subtree) { 447 handlePowerSupplyAttributesSubTreeResponse(asyncResp, ec, subtree); 448 }); 449 } 450 451 inline void 452 doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 453 const std::string& chassisId, 454 const std::string& powerSupplyId, 455 const std::optional<std::string>& validChassisPath) 456 { 457 if (!validChassisPath) 458 { 459 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 460 return; 461 } 462 463 // Get the correct Path and Service that match the input parameters 464 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 465 [asyncResp, chassisId, powerSupplyId]( 466 const std::string& powerSupplyPath) { 467 asyncResp->res.addHeader( 468 boost::beast::http::field::link, 469 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 470 asyncResp->res.jsonValue["@odata.type"] = 471 "#PowerSupply.v1_5_0.PowerSupply"; 472 asyncResp->res.jsonValue["Name"] = "Power Supply"; 473 asyncResp->res.jsonValue["Id"] = powerSupplyId; 474 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 475 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, 476 powerSupplyId); 477 478 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 479 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 480 481 dbus::utility::getDbusObject( 482 powerSupplyPath, powerSupplyInterface, 483 [asyncResp, 484 powerSupplyPath](const boost::system::error_code& ec, 485 const dbus::utility::MapperGetObject& object) { 486 if (ec || object.empty()) 487 { 488 messages::internalError(asyncResp->res); 489 return; 490 } 491 492 getPowerSupplyState(asyncResp, object.begin()->first, 493 powerSupplyPath); 494 getPowerSupplyHealth(asyncResp, object.begin()->first, 495 powerSupplyPath); 496 getPowerSupplyAsset(asyncResp, object.begin()->first, 497 powerSupplyPath); 498 getPowerSupplyFirmwareVersion(asyncResp, object.begin()->first, 499 powerSupplyPath); 500 getPowerSupplyLocation(asyncResp, object.begin()->first, 501 powerSupplyPath); 502 }); 503 504 getEfficiencyPercent(asyncResp); 505 }); 506 } 507 508 inline void 509 handlePowerSupplyHead(App& app, const crow::Request& req, 510 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 511 const std::string& chassisId, 512 const std::string& powerSupplyId) 513 { 514 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 515 { 516 return; 517 } 518 519 redfish::chassis_utils::getValidChassisPath( 520 asyncResp, chassisId, 521 [asyncResp, chassisId, 522 powerSupplyId](const std::optional<std::string>& validChassisPath) { 523 if (!validChassisPath) 524 { 525 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 526 return; 527 } 528 529 // Get the correct Path and Service that match the input parameters 530 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 531 [asyncResp](const std::string&) { 532 asyncResp->res.addHeader( 533 boost::beast::http::field::link, 534 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 535 }); 536 }); 537 } 538 539 inline void 540 handlePowerSupplyGet(App& app, const crow::Request& req, 541 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 542 const std::string& chassisId, 543 const std::string& powerSupplyId) 544 { 545 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 546 { 547 return; 548 } 549 550 redfish::chassis_utils::getValidChassisPath( 551 asyncResp, chassisId, 552 std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId)); 553 } 554 555 inline void requestRoutesPowerSupply(App& app) 556 { 557 BMCWEB_ROUTE( 558 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 559 .privileges(redfish::privileges::headPowerSupply) 560 .methods(boost::beast::http::verb::head)( 561 std::bind_front(handlePowerSupplyHead, std::ref(app))); 562 563 BMCWEB_ROUTE( 564 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 565 .privileges(redfish::privileges::getPowerSupply) 566 .methods(boost::beast::http::verb::get)( 567 std::bind_front(handlePowerSupplyGet, std::ref(app))); 568 } 569 570 } // namespace redfish 571