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_singleton.hpp" 10 #include "dbus_utility.hpp" 11 #include "error_messages.hpp" 12 #include "generated/enums/resource.hpp" 13 #include "http_request.hpp" 14 #include "human_sort.hpp" 15 #include "led.hpp" 16 #include "logging.hpp" 17 #include "query.hpp" 18 #include "registries/privilege_registry.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/asio/property.hpp> 28 29 #include <algorithm> 30 #include <array> 31 #include <functional> 32 #include <memory> 33 #include <optional> 34 #include <ranges> 35 #include <string> 36 #include <string_view> 37 #include <utility> 38 #include <vector> 39 40 namespace redfish 41 { 42 static constexpr std::array<std::string_view, 1> fabricInterfaces{ 43 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 44 static constexpr std::array<std::string_view, 1> portInterfaces{ 45 "xyz.openbmc_project.Inventory.Connector.Port"}; 46 47 inline void afterGetFabricPortLocation( 48 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 49 const boost::system::error_code& ec, const std::string& value) 50 { 51 if (ec) 52 { 53 if (ec.value() != EBADR) 54 { 55 BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 56 messages::internalError(asyncResp->res); 57 } 58 return; 59 } 60 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 61 value; 62 } 63 64 inline void getFabricPortLocation( 65 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 66 const std::string& portPath, const std::string& serviceName) 67 { 68 dbus::utility::getProperty<std::string>( 69 serviceName, portPath, 70 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 71 std::bind_front(afterGetFabricPortLocation, asyncResp)); 72 } 73 74 inline void afterGetFabricPortState( 75 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 76 const boost::system::error_code& ec, bool present) 77 { 78 if (ec) 79 { 80 if (ec.value() != EBADR) 81 { 82 BMCWEB_LOG_ERROR("DBUS response error for State, ec {}", 83 ec.value()); 84 messages::internalError(asyncResp->res); 85 } 86 return; 87 } 88 if (!present) 89 { 90 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Absent; 91 } 92 } 93 94 inline void getFabricPortState( 95 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 96 const std::string& portPath, const std::string& serviceName) 97 { 98 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 99 dbus::utility::getProperty<bool>( 100 serviceName, portPath, "xyz.openbmc_project.Inventory.Item", "Present", 101 std::bind_front(afterGetFabricPortState, asyncResp)); 102 } 103 104 inline void afterGetFabricPortHealth( 105 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 106 const boost::system::error_code& ec, bool functional) 107 { 108 if (ec) 109 { 110 if (ec.value() != EBADR) 111 { 112 BMCWEB_LOG_ERROR("DBUS response error for Health, ec {}", 113 ec.value()); 114 messages::internalError(asyncResp->res); 115 } 116 return; 117 } 118 119 if (!functional) 120 { 121 asyncResp->res.jsonValue["Status"]["Health"] = 122 resource::Health::Critical; 123 } 124 } 125 126 inline void getFabricPortHealth( 127 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 128 const std::string& portPath, const std::string& serviceName) 129 { 130 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 131 dbus::utility::getProperty<bool>( 132 serviceName, portPath, 133 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 134 std::bind_front(afterGetFabricPortHealth, asyncResp)); 135 } 136 137 inline void getFabricPortProperties( 138 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 139 const std::string& systemName, const std::string& adapterId, 140 const std::string& portId, const std::string& portPath, 141 const std::string& serviceName) 142 { 143 if (portPath.empty()) 144 { 145 BMCWEB_LOG_WARNING("Port not found"); 146 messages::resourceNotFound(asyncResp->res, "Port", portId); 147 return; 148 } 149 150 asyncResp->res.addHeader( 151 boost::beast::http::field::link, 152 "</redfish/v1/JsonSchemas/Port/Port.json>; rel=describedby"); 153 154 asyncResp->res.jsonValue["@odata.type"] = "#Port.v1_11_0.Port"; 155 asyncResp->res.jsonValue["@odata.id"] = 156 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters/{}/Ports/{}", 157 systemName, adapterId, portId); 158 asyncResp->res.jsonValue["Id"] = portId; 159 asyncResp->res.jsonValue["Name"] = "Fabric Port"; 160 161 getFabricPortLocation(asyncResp, portPath, serviceName); 162 getFabricPortState(asyncResp, portPath, serviceName); 163 getFabricPortHealth(asyncResp, portPath, serviceName); 164 getLocationIndicatorActive(asyncResp, portPath); 165 } 166 167 inline void afterGetValidFabricPortPath( 168 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 169 const std::string& portId, 170 std::function<void(const std::string& portPath, 171 const std::string& portServiceName)>& callback, 172 const boost::system::error_code& ec, 173 const dbus::utility::MapperGetSubTreePathsResponse& portSubTreePaths) 174 { 175 if (ec) 176 { 177 if (ec.value() != boost::system::errc::io_error) 178 { 179 BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 180 messages::internalError(asyncResp->res); 181 return; 182 } 183 // Port not found 184 callback(std::string(), std::string()); 185 return; 186 } 187 const auto& it = 188 std::ranges::find_if(portSubTreePaths, [portId](const auto& portPath) { 189 return portId == 190 sdbusplus::message::object_path(portPath).filename(); 191 }); 192 if (it == portSubTreePaths.end()) 193 { 194 // Port not found 195 callback(std::string(), std::string()); 196 return; 197 } 198 199 const std::string& portPath = *it; 200 dbus::utility::getDbusObject( 201 portPath, portInterfaces, 202 [asyncResp, portPath, callback{std::move(callback)}]( 203 const boost::system::error_code& ec1, 204 const dbus::utility::MapperGetObject& object) { 205 if (ec1 || object.empty()) 206 { 207 BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}", 208 ec1.value()); 209 messages::internalError(asyncResp->res); 210 return; 211 } 212 callback(portPath, object.begin()->first); 213 }); 214 } 215 216 inline void getValidFabricPortPath( 217 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 218 const std::string& adapterId, const std::string& portId, 219 std::function<void(const std::string& portPath, 220 const std::string& portServiceName)>&& callback) 221 { 222 dbus::utility::getAssociatedSubTreePathsById( 223 adapterId, "/xyz/openbmc_project/inventory", fabricInterfaces, 224 "connecting", portInterfaces, 225 std::bind_front(afterGetValidFabricPortPath, asyncResp, portId, 226 std::move(callback))); 227 } 228 229 inline void handleFabricPortHead( 230 crow::App& app, const crow::Request& req, 231 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 232 const std::string& systemName, const std::string& adapterId, 233 const std::string& portId) 234 { 235 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 236 { 237 return; 238 } 239 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 240 { 241 // Option currently returns no systems. TBD 242 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 243 systemName); 244 return; 245 } 246 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 247 { 248 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 249 systemName); 250 return; 251 } 252 253 getValidFabricPortPath( 254 asyncResp, adapterId, portId, 255 [asyncResp, portId](const std::string& portPath, const std::string&) { 256 if (portPath.empty()) 257 { 258 BMCWEB_LOG_WARNING("Port not found"); 259 messages::resourceNotFound(asyncResp->res, "Port", portId); 260 return; 261 } 262 asyncResp->res.addHeader( 263 boost::beast::http::field::link, 264 "</redfish/v1/JsonSchemas/Port/Port.json>; rel=describedby"); 265 }); 266 } 267 268 inline void handleFabricPortGet( 269 App& app, const crow::Request& req, 270 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 271 const std::string& systemName, const std::string& adapterId, 272 const std::string& portId) 273 { 274 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 275 { 276 return; 277 } 278 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 279 { 280 // Option currently returns no systems. TBD 281 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 282 systemName); 283 return; 284 } 285 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 286 { 287 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 288 systemName); 289 return; 290 } 291 getValidFabricPortPath(asyncResp, adapterId, portId, 292 std::bind_front(getFabricPortProperties, asyncResp, 293 systemName, adapterId, portId)); 294 } 295 296 inline void afterHandleFabricPortCollectionHead( 297 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 298 const std::string& adapterId, const boost::system::error_code& ec, 299 const dbus::utility::MapperGetSubTreePathsResponse& /* portSubTreePaths */) 300 { 301 if (ec) 302 { 303 if (ec.value() != boost::system::errc::io_error) 304 { 305 BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 306 messages::internalError(asyncResp->res); 307 return; 308 } 309 BMCWEB_LOG_WARNING("Adapter not found"); 310 messages::resourceNotFound(asyncResp->res, "Adapter", adapterId); 311 return; 312 } 313 asyncResp->res.addHeader( 314 boost::beast::http::field::link, 315 "</redfish/v1/JsonSchemas/PortCollection/PortCollection.json>; rel=describedby"); 316 } 317 318 inline void handleFabricPortCollectionHead( 319 crow::App& app, const crow::Request& req, 320 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 321 const std::string& systemName, const std::string& adapterId) 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 dbus::utility::getAssociatedSubTreePathsById( 342 adapterId, "/xyz/openbmc_project/inventory", fabricInterfaces, 343 "connecting", portInterfaces, 344 std::bind_front(afterHandleFabricPortCollectionHead, asyncResp, 345 adapterId)); 346 } 347 348 inline void doHandleFabricPortCollectionGet( 349 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 350 const std::string& systemName, const std::string& adapterId, 351 const boost::system::error_code& ec, 352 const dbus::utility::MapperGetSubTreePathsResponse& portSubTreePaths) 353 { 354 if (ec) 355 { 356 if (ec.value() != boost::system::errc::io_error) 357 { 358 BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 359 messages::internalError(asyncResp->res); 360 return; 361 } 362 BMCWEB_LOG_WARNING("Adapter not found"); 363 messages::resourceNotFound(asyncResp->res, "Adapter", adapterId); 364 return; 365 } 366 asyncResp->res.addHeader( 367 boost::beast::http::field::link, 368 "</redfish/v1/JsonSchemas/PortCollection/PortCollection.json>; rel=describedby"); 369 370 asyncResp->res.jsonValue["@odata.type"] = "#PortCollection.PortCollection"; 371 asyncResp->res.jsonValue["Name"] = "Port Collection"; 372 asyncResp->res.jsonValue["@odata.id"] = 373 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters/{}/Ports", 374 systemName, adapterId); 375 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 376 377 std::vector<std::string> portIdNames; 378 for (const std::string& portPath : portSubTreePaths) 379 { 380 std::string portId = 381 sdbusplus::message::object_path(portPath).filename(); 382 if (!portId.empty()) 383 { 384 portIdNames.emplace_back(std::move(portId)); 385 } 386 } 387 388 std::ranges::sort(portIdNames, AlphanumLess<std::string>()); 389 390 nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 391 for (const std::string& portId : portIdNames) 392 { 393 nlohmann::json item; 394 item["@odata.id"] = boost::urls::format( 395 "/redfish/v1/Systems/{}/FabricAdapters/{}/Ports/{}", systemName, 396 adapterId, portId); 397 members.emplace_back(std::move(item)); 398 } 399 asyncResp->res.jsonValue["Members@odata.count"] = members.size(); 400 } 401 402 inline void handleFabricPortCollectionGet( 403 App& app, const crow::Request& req, 404 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 405 const std::string& systemName, const std::string& adapterId) 406 { 407 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 408 { 409 return; 410 } 411 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 412 { 413 // Option currently returns no systems. TBD 414 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 415 systemName); 416 return; 417 } 418 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 419 { 420 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 421 systemName); 422 return; 423 } 424 425 dbus::utility::getAssociatedSubTreePathsById( 426 adapterId, "/xyz/openbmc_project/inventory", fabricInterfaces, 427 "connecting", portInterfaces, 428 std::bind_front(doHandleFabricPortCollectionGet, asyncResp, systemName, 429 adapterId)); 430 } 431 432 inline void afterHandlePortPatch( 433 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 434 const std::string& portId, const bool locationIndicatorActive, 435 const std::string& portPath, const std::string& /*serviceName*/) 436 { 437 if (portPath.empty()) 438 { 439 BMCWEB_LOG_WARNING("Port not found"); 440 messages::resourceNotFound(asyncResp->res, "Port", portId); 441 return; 442 } 443 444 setLocationIndicatorActive(asyncResp, portPath, locationIndicatorActive); 445 } 446 447 inline void handlePortPatch(App& app, const crow::Request& req, 448 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 449 const std::string& systemName, 450 const std::string& adapterId, 451 const std::string& portId) 452 { 453 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 454 { 455 return; 456 } 457 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 458 { 459 // Option currently returns no systems. TBD 460 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 461 systemName); 462 return; 463 } 464 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 465 { 466 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 467 systemName); 468 return; 469 } 470 471 std::optional<bool> locationIndicatorActive; 472 if (!json_util::readJsonPatch(req, asyncResp->res, 473 "LocationIndicatorActive", 474 locationIndicatorActive)) 475 { 476 return; 477 } 478 if (locationIndicatorActive) 479 { 480 getValidFabricPortPath( 481 asyncResp, adapterId, portId, 482 std::bind_front(afterHandlePortPatch, asyncResp, portId, 483 *locationIndicatorActive)); 484 } 485 } 486 487 inline void requestRoutesFabricPort(App& app) 488 { 489 BMCWEB_ROUTE(app, 490 "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/<str>/") 491 .privileges(redfish::privileges::headPort) 492 .methods(boost::beast::http::verb::head)( 493 std::bind_front(handleFabricPortHead, std::ref(app))); 494 495 BMCWEB_ROUTE(app, 496 "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/<str>/") 497 .privileges(redfish::privileges::getPort) 498 .methods(boost::beast::http::verb::get)( 499 std::bind_front(handleFabricPortGet, std::ref(app))); 500 501 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/") 502 .privileges(redfish::privileges::headPortCollection) 503 .methods(boost::beast::http::verb::head)( 504 std::bind_front(handleFabricPortCollectionHead, std::ref(app))); 505 506 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/") 507 .privileges(redfish::privileges::getPortCollection) 508 .methods(boost::beast::http::verb::get)( 509 std::bind_front(handleFabricPortCollectionGet, std::ref(app))); 510 511 BMCWEB_ROUTE(app, 512 "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/<str>/") 513 .privileges(redfish::privileges::patchPort) 514 .methods(boost::beast::http::verb::patch)( 515 std::bind_front(handlePortPatch, std::ref(app))); 516 } 517 518 } // namespace redfish 519