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