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