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