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 getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath); 195 getFabricAdapterAsset(asyncResp, serviceName, fabricAdapterPath); 196 getFabricAdapterState(asyncResp, serviceName, fabricAdapterPath); 197 getFabricAdapterHealth(asyncResp, serviceName, fabricAdapterPath); 198 getLocationIndicatorActive(asyncResp, fabricAdapterPath); 199 } 200 201 inline void afterGetValidFabricAdapterPath( 202 const std::string& adapterId, 203 std::function<void(const boost::system::error_code&, 204 const std::string& fabricAdapterPath, 205 const std::string& serviceName)>& callback, 206 const boost::system::error_code& ec, 207 const dbus::utility::MapperGetSubTreeResponse& subtree) 208 { 209 std::string fabricAdapterPath; 210 std::string serviceName; 211 if (ec) 212 { 213 callback(ec, fabricAdapterPath, serviceName); 214 return; 215 } 216 217 for (const auto& [adapterPath, serviceMap] : subtree) 218 { 219 std::string fabricAdapterName = 220 sdbusplus::message::object_path(adapterPath).filename(); 221 if (fabricAdapterName == adapterId) 222 { 223 fabricAdapterPath = adapterPath; 224 serviceName = serviceMap.begin()->first; 225 break; 226 } 227 } 228 callback(ec, fabricAdapterPath, serviceName); 229 } 230 231 inline void getValidFabricAdapterPath( 232 const std::string& adapterId, 233 std::function<void(const boost::system::error_code& ec, 234 const std::string& fabricAdapterPath, 235 const std::string& serviceName)>&& callback) 236 { 237 constexpr std::array<std::string_view, 1> interfaces{ 238 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 239 dbus::utility::getSubTree("/xyz/openbmc_project/inventory", 0, interfaces, 240 std::bind_front(afterGetValidFabricAdapterPath, 241 adapterId, std::move(callback))); 242 } 243 244 inline void afterHandleFabricAdapterGet( 245 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 246 const std::string& systemName, const std::string& adapterId, 247 const boost::system::error_code& ec, const std::string& fabricAdapterPath, 248 const std::string& serviceName) 249 { 250 if (ec) 251 { 252 if (ec.value() == boost::system::errc::io_error) 253 { 254 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 255 adapterId); 256 return; 257 } 258 259 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 260 messages::internalError(asyncResp->res); 261 return; 262 } 263 if (fabricAdapterPath.empty() || serviceName.empty()) 264 { 265 BMCWEB_LOG_WARNING("Adapter not found"); 266 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 267 return; 268 } 269 doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath, 270 serviceName); 271 } 272 273 inline void handleFabricAdapterGet( 274 App& app, const crow::Request& req, 275 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 276 const std::string& systemName, const std::string& adapterId) 277 { 278 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 279 { 280 return; 281 } 282 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 283 { 284 // Option currently returns no systems. TBD 285 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 286 systemName); 287 return; 288 } 289 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 290 { 291 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 292 systemName); 293 return; 294 } 295 getValidFabricAdapterPath( 296 adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp, 297 systemName, adapterId)); 298 } 299 300 inline void afterHandleFabricAdapterPatch( 301 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 302 const std::string& adapterId, std::optional<bool> locationIndicatorActive, 303 const boost::system::error_code& ec, const std::string& fabricAdapterPath, 304 const std::string& serviceName) 305 { 306 if (ec) 307 { 308 if (ec.value() == boost::system::errc::io_error) 309 { 310 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 311 adapterId); 312 return; 313 } 314 315 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 316 messages::internalError(asyncResp->res); 317 return; 318 } 319 if (fabricAdapterPath.empty() || serviceName.empty()) 320 { 321 BMCWEB_LOG_WARNING("Adapter {} not found", adapterId); 322 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 323 return; 324 } 325 326 if (locationIndicatorActive) 327 { 328 setLocationIndicatorActive(asyncResp, fabricAdapterPath, 329 *locationIndicatorActive); 330 } 331 } 332 333 inline void handleFabricAdapterPatch( 334 App& app, const crow::Request& req, 335 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 336 const std::string& systemName, const std::string& adapterId) 337 { 338 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 339 { 340 return; 341 } 342 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 343 { 344 // Option currently returns no systems. TBD 345 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 346 systemName); 347 return; 348 } 349 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 350 { 351 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 352 systemName); 353 return; 354 } 355 356 std::optional<bool> locationIndicatorActive; 357 358 if (!json_util::readJsonPatch(req, asyncResp->res, 359 "LocationIndicatorActive", 360 locationIndicatorActive)) 361 { 362 return; 363 } 364 365 getValidFabricAdapterPath( 366 adapterId, std::bind_front(afterHandleFabricAdapterPatch, asyncResp, 367 adapterId, locationIndicatorActive)); 368 } 369 370 inline void handleFabricAdapterCollectionGet( 371 crow::App& app, const crow::Request& req, 372 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 373 const std::string& systemName) 374 { 375 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 376 { 377 return; 378 } 379 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 380 { 381 // Option currently returns no systems. TBD 382 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 383 systemName); 384 return; 385 } 386 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 387 { 388 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 389 systemName); 390 return; 391 } 392 393 asyncResp->res.addHeader( 394 boost::beast::http::field::link, 395 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 396 asyncResp->res.jsonValue["@odata.type"] = 397 "#FabricAdapterCollection.FabricAdapterCollection"; 398 asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection"; 399 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 400 "/redfish/v1/Systems/{}/FabricAdapters", systemName); 401 402 constexpr std::array<std::string_view, 1> interfaces{ 403 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 404 collection_util::getCollectionMembers( 405 asyncResp, 406 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters", 407 BMCWEB_REDFISH_SYSTEM_URI_NAME), 408 interfaces, "/xyz/openbmc_project/inventory"); 409 } 410 411 inline void handleFabricAdapterCollectionHead( 412 crow::App& app, const crow::Request& req, 413 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 414 const std::string& systemName) 415 { 416 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 417 { 418 return; 419 } 420 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 421 { 422 // Option currently returns no systems. TBD 423 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 424 systemName); 425 return; 426 } 427 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 428 { 429 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 430 systemName); 431 return; 432 } 433 asyncResp->res.addHeader( 434 boost::beast::http::field::link, 435 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 436 } 437 438 inline void afterHandleFabricAdapterHead( 439 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 440 const std::string& adapterId, const boost::system::error_code& ec, 441 const std::string& fabricAdapterPath, const std::string& serviceName) 442 { 443 if (ec) 444 { 445 if (ec.value() == boost::system::errc::io_error) 446 { 447 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 448 adapterId); 449 return; 450 } 451 452 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 453 messages::internalError(asyncResp->res); 454 return; 455 } 456 if (fabricAdapterPath.empty() || serviceName.empty()) 457 { 458 BMCWEB_LOG_WARNING("Adapter not found"); 459 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 460 return; 461 } 462 asyncResp->res.addHeader( 463 boost::beast::http::field::link, 464 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 465 } 466 467 inline void handleFabricAdapterHead( 468 crow::App& app, const crow::Request& req, 469 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 470 const std::string& systemName, const std::string& adapterId) 471 { 472 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 473 { 474 return; 475 } 476 477 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 478 { 479 // Option currently returns no systems. TBD 480 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 481 systemName); 482 return; 483 } 484 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 485 { 486 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 487 systemName); 488 return; 489 } 490 getValidFabricAdapterPath( 491 adapterId, 492 std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId)); 493 } 494 495 inline void requestRoutesFabricAdapterCollection(App& app) 496 { 497 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 498 .privileges(redfish::privileges::getFabricAdapterCollection) 499 .methods(boost::beast::http::verb::get)( 500 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app))); 501 502 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 503 .privileges(redfish::privileges::headFabricAdapterCollection) 504 .methods(boost::beast::http::verb::head)( 505 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app))); 506 } 507 508 inline void requestRoutesFabricAdapters(App& app) 509 { 510 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 511 .privileges(redfish::privileges::getFabricAdapter) 512 .methods(boost::beast::http::verb::get)( 513 std::bind_front(handleFabricAdapterGet, std::ref(app))); 514 515 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 516 .privileges(redfish::privileges::headFabricAdapter) 517 .methods(boost::beast::http::verb::head)( 518 std::bind_front(handleFabricAdapterHead, std::ref(app))); 519 520 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 521 .privileges(redfish::privileges::patchFabricAdapter) 522 .methods(boost::beast::http::verb::patch)( 523 std::bind_front(handleFabricAdapterPatch, std::ref(app))); 524 } 525 } // namespace redfish 526