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