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