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