1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "bmcweb_config.h" 6 7 #include "app.hpp" 8 #include "async_resp.hpp" 9 #include "dbus_utility.hpp" 10 #include "error_messages.hpp" 11 #include "generated/enums/resource.hpp" 12 #include "http_request.hpp" 13 #include "led.hpp" 14 #include "logging.hpp" 15 #include "query.hpp" 16 #include "registries/privilege_registry.hpp" 17 #include "utils/collection.hpp" 18 #include "utils/dbus_utils.hpp" 19 #include "utils/json_utils.hpp" 20 21 #include <asm-generic/errno.h> 22 23 #include <boost/beast/http/field.hpp> 24 #include <boost/beast/http/verb.hpp> 25 #include <boost/system/error_code.hpp> 26 #include <boost/url/format.hpp> 27 #include <sdbusplus/unpack_properties.hpp> 28 29 #include <array> 30 #include <functional> 31 #include <memory> 32 #include <optional> 33 #include <string> 34 #include <string_view> 35 #include <utility> 36 37 namespace redfish 38 { 39 40 inline void getFabricAdapterLocation( 41 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 42 const std::string& serviceName, const std::string& fabricAdapterPath) 43 { 44 dbus::utility::getProperty<std::string>( 45 serviceName, fabricAdapterPath, 46 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 47 [asyncResp](const boost::system::error_code& ec, 48 const std::string& property) { 49 if (ec) 50 { 51 if (ec.value() != EBADR) 52 { 53 BMCWEB_LOG_ERROR("DBUS response error for Location"); 54 messages::internalError(asyncResp->res); 55 } 56 return; 57 } 58 59 asyncResp->res 60 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 61 property; 62 }); 63 } 64 65 inline void getFabricAdapterAsset( 66 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 67 const std::string& serviceName, const std::string& fabricAdapterPath) 68 { 69 dbus::utility::getAllProperties( 70 serviceName, fabricAdapterPath, 71 "xyz.openbmc_project.Inventory.Decorator.Asset", 72 [fabricAdapterPath, asyncResp{asyncResp}]( 73 const boost::system::error_code& ec, 74 const dbus::utility::DBusPropertiesMap& propertiesList) { 75 if (ec) 76 { 77 if (ec.value() != EBADR) 78 { 79 BMCWEB_LOG_ERROR("DBUS response error for Properties"); 80 messages::internalError(asyncResp->res); 81 } 82 return; 83 } 84 85 const std::string* serialNumber = nullptr; 86 const std::string* model = nullptr; 87 const std::string* partNumber = nullptr; 88 const std::string* sparePartNumber = nullptr; 89 90 const bool success = sdbusplus::unpackPropertiesNoThrow( 91 dbus_utils::UnpackErrorPrinter(), propertiesList, 92 "SerialNumber", serialNumber, "Model", model, "PartNumber", 93 partNumber, "SparePartNumber", sparePartNumber); 94 95 if (!success) 96 { 97 messages::internalError(asyncResp->res); 98 return; 99 } 100 101 if (serialNumber != nullptr) 102 { 103 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 104 } 105 106 if (model != nullptr) 107 { 108 asyncResp->res.jsonValue["Model"] = *model; 109 } 110 111 if (partNumber != nullptr) 112 { 113 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 114 } 115 116 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 117 { 118 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 119 } 120 }); 121 } 122 123 inline void getFabricAdapterState( 124 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 125 const std::string& serviceName, const std::string& fabricAdapterPath) 126 { 127 dbus::utility::getProperty<bool>( 128 serviceName, fabricAdapterPath, "xyz.openbmc_project.Inventory.Item", 129 "Present", 130 [asyncResp](const boost::system::error_code& ec, const bool present) { 131 if (ec) 132 { 133 if (ec.value() != EBADR) 134 { 135 BMCWEB_LOG_ERROR("DBUS response error for State"); 136 messages::internalError(asyncResp->res); 137 } 138 return; 139 } 140 141 if (!present) 142 { 143 asyncResp->res.jsonValue["Status"]["State"] = 144 resource::State::Absent; 145 } 146 }); 147 } 148 149 inline void getFabricAdapterHealth( 150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 151 const std::string& serviceName, const std::string& fabricAdapterPath) 152 { 153 dbus::utility::getProperty<bool>( 154 serviceName, fabricAdapterPath, 155 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 156 [asyncResp](const boost::system::error_code& ec, 157 const bool functional) { 158 if (ec) 159 { 160 if (ec.value() != EBADR) 161 { 162 BMCWEB_LOG_ERROR("DBUS response error for Health"); 163 messages::internalError(asyncResp->res); 164 } 165 return; 166 } 167 168 if (!functional) 169 { 170 asyncResp->res.jsonValue["Status"]["Health"] = 171 resource::Health::Critical; 172 } 173 }); 174 } 175 176 inline void doAdapterGet( 177 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 178 const std::string& systemName, const std::string& adapterId, 179 const std::string& fabricAdapterPath, const std::string& serviceName) 180 { 181 asyncResp->res.addHeader( 182 boost::beast::http::field::link, 183 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 184 asyncResp->res.jsonValue["@odata.type"] = 185 "#FabricAdapter.v1_4_0.FabricAdapter"; 186 asyncResp->res.jsonValue["Name"] = "Fabric Adapter"; 187 asyncResp->res.jsonValue["Id"] = adapterId; 188 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 189 "/redfish/v1/Systems/{}/FabricAdapters/{}", systemName, adapterId); 190 191 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 192 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 193 194 asyncResp->res.jsonValue["Ports"]["@odata.id"] = 195 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters/{}/Ports", 196 systemName, adapterId); 197 198 getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath); 199 getFabricAdapterAsset(asyncResp, serviceName, fabricAdapterPath); 200 getFabricAdapterState(asyncResp, serviceName, fabricAdapterPath); 201 getFabricAdapterHealth(asyncResp, serviceName, fabricAdapterPath); 202 getLocationIndicatorActive(asyncResp, fabricAdapterPath); 203 } 204 205 inline void afterGetValidFabricAdapterPath( 206 const std::string& adapterId, 207 std::function<void(const boost::system::error_code&, 208 const std::string& fabricAdapterPath, 209 const std::string& serviceName)>& callback, 210 const boost::system::error_code& ec, 211 const dbus::utility::MapperGetSubTreeResponse& subtree) 212 { 213 std::string fabricAdapterPath; 214 std::string serviceName; 215 if (ec) 216 { 217 callback(ec, fabricAdapterPath, serviceName); 218 return; 219 } 220 221 for (const auto& [adapterPath, serviceMap] : subtree) 222 { 223 std::string fabricAdapterName = 224 sdbusplus::message::object_path(adapterPath).filename(); 225 if (fabricAdapterName == adapterId) 226 { 227 fabricAdapterPath = adapterPath; 228 serviceName = serviceMap.begin()->first; 229 break; 230 } 231 } 232 callback(ec, fabricAdapterPath, serviceName); 233 } 234 235 inline void getValidFabricAdapterPath( 236 const std::string& adapterId, 237 std::function<void(const boost::system::error_code& ec, 238 const std::string& fabricAdapterPath, 239 const std::string& serviceName)>&& callback) 240 { 241 constexpr std::array<std::string_view, 1> interfaces{ 242 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 243 dbus::utility::getSubTree("/xyz/openbmc_project/inventory", 0, interfaces, 244 std::bind_front(afterGetValidFabricAdapterPath, 245 adapterId, std::move(callback))); 246 } 247 248 inline void afterHandleFabricAdapterGet( 249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 250 const std::string& systemName, const std::string& adapterId, 251 const boost::system::error_code& ec, const std::string& fabricAdapterPath, 252 const std::string& serviceName) 253 { 254 if (ec) 255 { 256 if (ec.value() == boost::system::errc::io_error) 257 { 258 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 259 adapterId); 260 return; 261 } 262 263 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 264 messages::internalError(asyncResp->res); 265 return; 266 } 267 if (fabricAdapterPath.empty() || serviceName.empty()) 268 { 269 BMCWEB_LOG_WARNING("Adapter not found"); 270 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 271 return; 272 } 273 doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath, 274 serviceName); 275 } 276 277 inline void handleFabricAdapterGet( 278 App& app, const crow::Request& req, 279 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 280 const std::string& systemName, const std::string& adapterId) 281 { 282 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 283 { 284 return; 285 } 286 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 287 { 288 // Option currently returns no systems. TBD 289 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 290 systemName); 291 return; 292 } 293 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 294 { 295 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 296 systemName); 297 return; 298 } 299 getValidFabricAdapterPath( 300 adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp, 301 systemName, adapterId)); 302 } 303 304 inline void afterHandleFabricAdapterPatch( 305 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 306 const std::string& adapterId, std::optional<bool> locationIndicatorActive, 307 const boost::system::error_code& ec, const std::string& fabricAdapterPath, 308 const std::string& serviceName) 309 { 310 if (ec) 311 { 312 if (ec.value() == boost::system::errc::io_error) 313 { 314 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 315 adapterId); 316 return; 317 } 318 319 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 320 messages::internalError(asyncResp->res); 321 return; 322 } 323 if (fabricAdapterPath.empty() || serviceName.empty()) 324 { 325 BMCWEB_LOG_WARNING("Adapter {} not found", adapterId); 326 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 327 return; 328 } 329 330 if (locationIndicatorActive) 331 { 332 setLocationIndicatorActive(asyncResp, fabricAdapterPath, 333 *locationIndicatorActive); 334 } 335 } 336 337 inline void handleFabricAdapterPatch( 338 App& app, const crow::Request& req, 339 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 340 const std::string& systemName, const std::string& adapterId) 341 { 342 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 343 { 344 return; 345 } 346 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 347 { 348 // Option currently returns no systems. TBD 349 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 350 systemName); 351 return; 352 } 353 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 354 { 355 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 356 systemName); 357 return; 358 } 359 360 std::optional<bool> locationIndicatorActive; 361 362 if (!json_util::readJsonPatch(req, asyncResp->res, 363 "LocationIndicatorActive", 364 locationIndicatorActive)) 365 { 366 return; 367 } 368 369 getValidFabricAdapterPath( 370 adapterId, std::bind_front(afterHandleFabricAdapterPatch, asyncResp, 371 adapterId, locationIndicatorActive)); 372 } 373 374 inline void handleFabricAdapterCollectionGet( 375 crow::App& app, const crow::Request& req, 376 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 377 const std::string& systemName) 378 { 379 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 380 { 381 return; 382 } 383 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 384 { 385 // Option currently returns no systems. TBD 386 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 387 systemName); 388 return; 389 } 390 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 391 { 392 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 393 systemName); 394 return; 395 } 396 397 asyncResp->res.addHeader( 398 boost::beast::http::field::link, 399 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 400 asyncResp->res.jsonValue["@odata.type"] = 401 "#FabricAdapterCollection.FabricAdapterCollection"; 402 asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection"; 403 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 404 "/redfish/v1/Systems/{}/FabricAdapters", systemName); 405 406 constexpr std::array<std::string_view, 1> interfaces{ 407 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 408 collection_util::getCollectionMembers( 409 asyncResp, 410 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters", 411 BMCWEB_REDFISH_SYSTEM_URI_NAME), 412 interfaces, "/xyz/openbmc_project/inventory"); 413 } 414 415 inline void handleFabricAdapterCollectionHead( 416 crow::App& app, const crow::Request& req, 417 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 418 const std::string& systemName) 419 { 420 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 421 { 422 return; 423 } 424 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 425 { 426 // Option currently returns no systems. TBD 427 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 428 systemName); 429 return; 430 } 431 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 432 { 433 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 434 systemName); 435 return; 436 } 437 asyncResp->res.addHeader( 438 boost::beast::http::field::link, 439 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 440 } 441 442 inline void afterHandleFabricAdapterHead( 443 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 444 const std::string& adapterId, const boost::system::error_code& ec, 445 const std::string& fabricAdapterPath, const std::string& serviceName) 446 { 447 if (ec) 448 { 449 if (ec.value() == boost::system::errc::io_error) 450 { 451 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 452 adapterId); 453 return; 454 } 455 456 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 457 messages::internalError(asyncResp->res); 458 return; 459 } 460 if (fabricAdapterPath.empty() || serviceName.empty()) 461 { 462 BMCWEB_LOG_WARNING("Adapter not found"); 463 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 464 return; 465 } 466 asyncResp->res.addHeader( 467 boost::beast::http::field::link, 468 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 469 } 470 471 inline void handleFabricAdapterHead( 472 crow::App& app, const crow::Request& req, 473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 474 const std::string& systemName, const std::string& adapterId) 475 { 476 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 477 { 478 return; 479 } 480 481 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 482 { 483 // Option currently returns no systems. TBD 484 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 485 systemName); 486 return; 487 } 488 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 489 { 490 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 491 systemName); 492 return; 493 } 494 getValidFabricAdapterPath( 495 adapterId, 496 std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId)); 497 } 498 499 inline void requestRoutesFabricAdapterCollection(App& app) 500 { 501 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 502 .privileges(redfish::privileges::getFabricAdapterCollection) 503 .methods(boost::beast::http::verb::get)( 504 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app))); 505 506 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 507 .privileges(redfish::privileges::headFabricAdapterCollection) 508 .methods(boost::beast::http::verb::head)( 509 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app))); 510 } 511 512 inline void requestRoutesFabricAdapters(App& app) 513 { 514 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 515 .privileges(redfish::privileges::getFabricAdapter) 516 .methods(boost::beast::http::verb::get)( 517 std::bind_front(handleFabricAdapterGet, std::ref(app))); 518 519 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 520 .privileges(redfish::privileges::headFabricAdapter) 521 .methods(boost::beast::http::verb::head)( 522 std::bind_front(handleFabricAdapterHead, std::ref(app))); 523 524 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 525 .privileges(redfish::privileges::patchFabricAdapter) 526 .methods(boost::beast::http::verb::patch)( 527 std::bind_front(handleFabricAdapterPatch, std::ref(app))); 528 } 529 } // namespace redfish 530