137937d51SGeorge Liu // SPDX-License-Identifier: Apache-2.0 237937d51SGeorge Liu // SPDX-FileCopyrightText: Copyright OpenBMC Authors 337937d51SGeorge Liu #pragma once 437937d51SGeorge Liu 537937d51SGeorge Liu #include "bmcweb_config.h" 637937d51SGeorge Liu 737937d51SGeorge Liu #include "app.hpp" 837937d51SGeorge Liu #include "async_resp.hpp" 9*7842c99fSMyung Bae #include "dbus_singleton.hpp" 1037937d51SGeorge Liu #include "dbus_utility.hpp" 1137937d51SGeorge Liu #include "error_messages.hpp" 1237937d51SGeorge Liu #include "http_request.hpp" 1337937d51SGeorge Liu #include "human_sort.hpp" 1437937d51SGeorge Liu #include "logging.hpp" 1537937d51SGeorge Liu #include "query.hpp" 1637937d51SGeorge Liu #include "registries/privilege_registry.hpp" 1737937d51SGeorge Liu 18*7842c99fSMyung Bae #include <asm-generic/errno.h> 19*7842c99fSMyung Bae 2037937d51SGeorge Liu #include <boost/beast/http/field.hpp> 2137937d51SGeorge Liu #include <boost/beast/http/verb.hpp> 2237937d51SGeorge Liu #include <boost/system/error_code.hpp> 2337937d51SGeorge Liu #include <boost/url/format.hpp> 24*7842c99fSMyung Bae #include <sdbusplus/asio/property.hpp> 2537937d51SGeorge Liu 2637937d51SGeorge Liu #include <algorithm> 2737937d51SGeorge Liu #include <array> 2837937d51SGeorge Liu #include <functional> 2937937d51SGeorge Liu #include <memory> 3037937d51SGeorge Liu #include <ranges> 3137937d51SGeorge Liu #include <string> 3237937d51SGeorge Liu #include <string_view> 3337937d51SGeorge Liu #include <utility> 3437937d51SGeorge Liu #include <vector> 3537937d51SGeorge Liu 3637937d51SGeorge Liu namespace redfish 3737937d51SGeorge Liu { 3837937d51SGeorge Liu static constexpr std::array<std::string_view, 1> fabricInterfaces{ 3937937d51SGeorge Liu "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 4037937d51SGeorge Liu static constexpr std::array<std::string_view, 1> portInterfaces{ 4137937d51SGeorge Liu "xyz.openbmc_project.Inventory.Connector.Port"}; 4237937d51SGeorge Liu 43*7842c99fSMyung Bae inline void afterGetFabricPortLocation( 44*7842c99fSMyung Bae const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 45*7842c99fSMyung Bae const boost::system::error_code& ec, const std::string& value) 46*7842c99fSMyung Bae { 47*7842c99fSMyung Bae if (ec) 48*7842c99fSMyung Bae { 49*7842c99fSMyung Bae if (ec.value() != EBADR) 50*7842c99fSMyung Bae { 51*7842c99fSMyung Bae BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 52*7842c99fSMyung Bae messages::internalError(asyncResp->res); 53*7842c99fSMyung Bae } 54*7842c99fSMyung Bae return; 55*7842c99fSMyung Bae } 56*7842c99fSMyung Bae asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 57*7842c99fSMyung Bae value; 58*7842c99fSMyung Bae } 59*7842c99fSMyung Bae 60*7842c99fSMyung Bae inline void getFabricPortLocation( 61*7842c99fSMyung Bae const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 62*7842c99fSMyung Bae const std::string& portPath, const std::string& serviceName) 63*7842c99fSMyung Bae { 64*7842c99fSMyung Bae dbus::utility::getProperty<std::string>( 65*7842c99fSMyung Bae serviceName, portPath, 66*7842c99fSMyung Bae "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 67*7842c99fSMyung Bae std::bind_front(afterGetFabricPortLocation, asyncResp)); 68*7842c99fSMyung Bae } 69*7842c99fSMyung Bae 7037937d51SGeorge Liu inline void getFabricPortProperties( 7137937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 7237937d51SGeorge Liu const std::string& systemName, const std::string& adapterId, 73*7842c99fSMyung Bae const std::string& portId, const std::string& portPath, 74*7842c99fSMyung Bae const std::string& serviceName) 7537937d51SGeorge Liu { 7637937d51SGeorge Liu if (portPath.empty()) 7737937d51SGeorge Liu { 7837937d51SGeorge Liu BMCWEB_LOG_WARNING("Port not found"); 7937937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "Port", portId); 8037937d51SGeorge Liu return; 8137937d51SGeorge Liu } 8237937d51SGeorge Liu 8337937d51SGeorge Liu asyncResp->res.addHeader( 8437937d51SGeorge Liu boost::beast::http::field::link, 8537937d51SGeorge Liu "</redfish/v1/JsonSchemas/Port/Port.json>; rel=describedby"); 8637937d51SGeorge Liu 8737937d51SGeorge Liu asyncResp->res.jsonValue["@odata.type"] = "#Port.v1_11_0.Port"; 8837937d51SGeorge Liu asyncResp->res.jsonValue["@odata.id"] = 8937937d51SGeorge Liu boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters/{}/Ports/{}", 9037937d51SGeorge Liu systemName, adapterId, portId); 9137937d51SGeorge Liu asyncResp->res.jsonValue["Id"] = portId; 9237937d51SGeorge Liu asyncResp->res.jsonValue["Name"] = "Fabric Port"; 93*7842c99fSMyung Bae getFabricPortLocation(asyncResp, portPath, serviceName); 9437937d51SGeorge Liu } 9537937d51SGeorge Liu 9637937d51SGeorge Liu inline void afterGetValidFabricPortPath( 9737937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 9837937d51SGeorge Liu const std::string& portId, 99*7842c99fSMyung Bae std::function<void(const std::string& portPath, 100*7842c99fSMyung Bae const std::string& portServiceName)>& callback, 10137937d51SGeorge Liu const boost::system::error_code& ec, 10237937d51SGeorge Liu const dbus::utility::MapperGetSubTreePathsResponse& portSubTreePaths) 10337937d51SGeorge Liu { 10437937d51SGeorge Liu if (ec) 10537937d51SGeorge Liu { 10637937d51SGeorge Liu if (ec.value() != boost::system::errc::io_error) 10737937d51SGeorge Liu { 10837937d51SGeorge Liu BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 10937937d51SGeorge Liu messages::internalError(asyncResp->res); 11037937d51SGeorge Liu return; 11137937d51SGeorge Liu } 11237937d51SGeorge Liu // Port not found 113*7842c99fSMyung Bae callback(std::string(), std::string()); 11437937d51SGeorge Liu return; 11537937d51SGeorge Liu } 11637937d51SGeorge Liu const auto& it = 11737937d51SGeorge Liu std::ranges::find_if(portSubTreePaths, [portId](const auto& portPath) { 11837937d51SGeorge Liu return portId == 11937937d51SGeorge Liu sdbusplus::message::object_path(portPath).filename(); 12037937d51SGeorge Liu }); 12137937d51SGeorge Liu if (it == portSubTreePaths.end()) 12237937d51SGeorge Liu { 12337937d51SGeorge Liu // Port not found 124*7842c99fSMyung Bae callback(std::string(), std::string()); 12537937d51SGeorge Liu return; 12637937d51SGeorge Liu } 12737937d51SGeorge Liu 12837937d51SGeorge Liu const std::string& portPath = *it; 12937937d51SGeorge Liu dbus::utility::getDbusObject( 13037937d51SGeorge Liu portPath, portInterfaces, 13137937d51SGeorge Liu [asyncResp, portPath, callback{std::move(callback)}]( 13237937d51SGeorge Liu const boost::system::error_code& ec1, 13337937d51SGeorge Liu const dbus::utility::MapperGetObject& object) { 13437937d51SGeorge Liu if (ec1 || object.empty()) 13537937d51SGeorge Liu { 13637937d51SGeorge Liu BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}", 13737937d51SGeorge Liu ec1.value()); 13837937d51SGeorge Liu messages::internalError(asyncResp->res); 13937937d51SGeorge Liu return; 14037937d51SGeorge Liu } 141*7842c99fSMyung Bae callback(portPath, object.begin()->first); 14237937d51SGeorge Liu }); 14337937d51SGeorge Liu } 14437937d51SGeorge Liu 14537937d51SGeorge Liu inline void getValidFabricPortPath( 14637937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 14737937d51SGeorge Liu const std::string& adapterId, const std::string& portId, 148*7842c99fSMyung Bae std::function<void(const std::string& portPath, 149*7842c99fSMyung Bae const std::string& portServiceName)>&& callback) 15037937d51SGeorge Liu { 15137937d51SGeorge Liu dbus::utility::getAssociatedSubTreePathsById( 15237937d51SGeorge Liu adapterId, "/xyz/openbmc_project/inventory", fabricInterfaces, 15337937d51SGeorge Liu "connecting", portInterfaces, 15437937d51SGeorge Liu std::bind_front(afterGetValidFabricPortPath, asyncResp, portId, 15537937d51SGeorge Liu std::move(callback))); 15637937d51SGeorge Liu } 15737937d51SGeorge Liu 15837937d51SGeorge Liu inline void handleFabricPortHead( 15937937d51SGeorge Liu crow::App& app, const crow::Request& req, 16037937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 16137937d51SGeorge Liu const std::string& systemName, const std::string& adapterId, 16237937d51SGeorge Liu const std::string& portId) 16337937d51SGeorge Liu { 16437937d51SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 16537937d51SGeorge Liu { 16637937d51SGeorge Liu return; 16737937d51SGeorge Liu } 16837937d51SGeorge Liu if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 16937937d51SGeorge Liu { 17037937d51SGeorge Liu // Option currently returns no systems. TBD 17137937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 17237937d51SGeorge Liu systemName); 17337937d51SGeorge Liu return; 17437937d51SGeorge Liu } 17537937d51SGeorge Liu if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 17637937d51SGeorge Liu { 17737937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 17837937d51SGeorge Liu systemName); 17937937d51SGeorge Liu return; 18037937d51SGeorge Liu } 18137937d51SGeorge Liu 18237937d51SGeorge Liu getValidFabricPortPath( 18337937d51SGeorge Liu asyncResp, adapterId, portId, 184*7842c99fSMyung Bae [asyncResp, portId](const std::string& portPath, const std::string&) { 18537937d51SGeorge Liu if (portPath.empty()) 18637937d51SGeorge Liu { 18737937d51SGeorge Liu BMCWEB_LOG_WARNING("Port not found"); 18837937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "Port", portId); 18937937d51SGeorge Liu return; 19037937d51SGeorge Liu } 19137937d51SGeorge Liu asyncResp->res.addHeader( 19237937d51SGeorge Liu boost::beast::http::field::link, 19337937d51SGeorge Liu "</redfish/v1/JsonSchemas/Port/Port.json>; rel=describedby"); 19437937d51SGeorge Liu }); 19537937d51SGeorge Liu } 19637937d51SGeorge Liu 19737937d51SGeorge Liu inline void handleFabricPortGet( 19837937d51SGeorge Liu App& app, const crow::Request& req, 19937937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 20037937d51SGeorge Liu const std::string& systemName, const std::string& adapterId, 20137937d51SGeorge Liu const std::string& portId) 20237937d51SGeorge Liu { 20337937d51SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 20437937d51SGeorge Liu { 20537937d51SGeorge Liu return; 20637937d51SGeorge Liu } 20737937d51SGeorge Liu if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 20837937d51SGeorge Liu { 20937937d51SGeorge Liu // Option currently returns no systems. TBD 21037937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 21137937d51SGeorge Liu systemName); 21237937d51SGeorge Liu return; 21337937d51SGeorge Liu } 21437937d51SGeorge Liu if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 21537937d51SGeorge Liu { 21637937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 21737937d51SGeorge Liu systemName); 21837937d51SGeorge Liu return; 21937937d51SGeorge Liu } 22037937d51SGeorge Liu getValidFabricPortPath(asyncResp, adapterId, portId, 22137937d51SGeorge Liu std::bind_front(getFabricPortProperties, asyncResp, 22237937d51SGeorge Liu systemName, adapterId, portId)); 22337937d51SGeorge Liu } 22437937d51SGeorge Liu 22537937d51SGeorge Liu inline void afterHandleFabricPortCollectionHead( 22637937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 22737937d51SGeorge Liu const std::string& adapterId, const boost::system::error_code& ec, 22837937d51SGeorge Liu const dbus::utility::MapperGetSubTreePathsResponse& /* portSubTreePaths */) 22937937d51SGeorge Liu { 23037937d51SGeorge Liu if (ec) 23137937d51SGeorge Liu { 23237937d51SGeorge Liu if (ec.value() != boost::system::errc::io_error) 23337937d51SGeorge Liu { 23437937d51SGeorge Liu BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 23537937d51SGeorge Liu messages::internalError(asyncResp->res); 23637937d51SGeorge Liu return; 23737937d51SGeorge Liu } 23837937d51SGeorge Liu BMCWEB_LOG_WARNING("Adapter not found"); 23937937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "Adapter", adapterId); 24037937d51SGeorge Liu return; 24137937d51SGeorge Liu } 24237937d51SGeorge Liu asyncResp->res.addHeader( 24337937d51SGeorge Liu boost::beast::http::field::link, 24437937d51SGeorge Liu "</redfish/v1/JsonSchemas/PortCollection/PortCollection.json>; rel=describedby"); 24537937d51SGeorge Liu } 24637937d51SGeorge Liu 24737937d51SGeorge Liu inline void handleFabricPortCollectionHead( 24837937d51SGeorge Liu crow::App& app, const crow::Request& req, 24937937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 25037937d51SGeorge Liu const std::string& systemName, const std::string& adapterId) 25137937d51SGeorge Liu { 25237937d51SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 25337937d51SGeorge Liu { 25437937d51SGeorge Liu return; 25537937d51SGeorge Liu } 25637937d51SGeorge Liu if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 25737937d51SGeorge Liu { 25837937d51SGeorge Liu // Option currently returns no systems. TBD 25937937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 26037937d51SGeorge Liu systemName); 26137937d51SGeorge Liu return; 26237937d51SGeorge Liu } 26337937d51SGeorge Liu if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 26437937d51SGeorge Liu { 26537937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 26637937d51SGeorge Liu systemName); 26737937d51SGeorge Liu return; 26837937d51SGeorge Liu } 26937937d51SGeorge Liu 27037937d51SGeorge Liu dbus::utility::getAssociatedSubTreePathsById( 27137937d51SGeorge Liu adapterId, "/xyz/openbmc_project/inventory", fabricInterfaces, 27237937d51SGeorge Liu "connecting", portInterfaces, 27337937d51SGeorge Liu std::bind_front(afterHandleFabricPortCollectionHead, asyncResp, 27437937d51SGeorge Liu adapterId)); 27537937d51SGeorge Liu } 27637937d51SGeorge Liu 27737937d51SGeorge Liu inline void doHandleFabricPortCollectionGet( 27837937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 27937937d51SGeorge Liu const std::string& systemName, const std::string& adapterId, 28037937d51SGeorge Liu const boost::system::error_code& ec, 28137937d51SGeorge Liu const dbus::utility::MapperGetSubTreePathsResponse& portSubTreePaths) 28237937d51SGeorge Liu { 28337937d51SGeorge Liu if (ec) 28437937d51SGeorge Liu { 28537937d51SGeorge Liu if (ec.value() != boost::system::errc::io_error) 28637937d51SGeorge Liu { 28737937d51SGeorge Liu BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); 28837937d51SGeorge Liu messages::internalError(asyncResp->res); 28937937d51SGeorge Liu return; 29037937d51SGeorge Liu } 29137937d51SGeorge Liu BMCWEB_LOG_WARNING("Adapter not found"); 29237937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "Adapter", adapterId); 29337937d51SGeorge Liu return; 29437937d51SGeorge Liu } 29537937d51SGeorge Liu asyncResp->res.addHeader( 29637937d51SGeorge Liu boost::beast::http::field::link, 29737937d51SGeorge Liu "</redfish/v1/JsonSchemas/PortCollection/PortCollection.json>; rel=describedby"); 29837937d51SGeorge Liu 29937937d51SGeorge Liu asyncResp->res.jsonValue["@odata.type"] = "#PortCollection.PortCollection"; 30037937d51SGeorge Liu asyncResp->res.jsonValue["Name"] = "Port Collection"; 30137937d51SGeorge Liu asyncResp->res.jsonValue["@odata.id"] = 30237937d51SGeorge Liu boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters/{}/Ports", 30337937d51SGeorge Liu systemName, adapterId); 30437937d51SGeorge Liu asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 30537937d51SGeorge Liu 30637937d51SGeorge Liu std::vector<std::string> portIdNames; 30737937d51SGeorge Liu for (const std::string& portPath : portSubTreePaths) 30837937d51SGeorge Liu { 30937937d51SGeorge Liu std::string portId = 31037937d51SGeorge Liu sdbusplus::message::object_path(portPath).filename(); 31137937d51SGeorge Liu if (!portId.empty()) 31237937d51SGeorge Liu { 31337937d51SGeorge Liu portIdNames.emplace_back(std::move(portId)); 31437937d51SGeorge Liu } 31537937d51SGeorge Liu } 31637937d51SGeorge Liu 31737937d51SGeorge Liu std::ranges::sort(portIdNames, AlphanumLess<std::string>()); 31837937d51SGeorge Liu 31937937d51SGeorge Liu nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 32037937d51SGeorge Liu for (const std::string& portId : portIdNames) 32137937d51SGeorge Liu { 32237937d51SGeorge Liu nlohmann::json item; 32337937d51SGeorge Liu item["@odata.id"] = boost::urls::format( 32437937d51SGeorge Liu "/redfish/v1/Systems/{}/FabricAdapters/{}/Ports/{}", systemName, 32537937d51SGeorge Liu adapterId, portId); 32637937d51SGeorge Liu members.emplace_back(std::move(item)); 32737937d51SGeorge Liu } 32837937d51SGeorge Liu asyncResp->res.jsonValue["Members@odata.count"] = members.size(); 32937937d51SGeorge Liu } 33037937d51SGeorge Liu 33137937d51SGeorge Liu inline void handleFabricPortCollectionGet( 33237937d51SGeorge Liu App& app, const crow::Request& req, 33337937d51SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 33437937d51SGeorge Liu const std::string& systemName, const std::string& adapterId) 33537937d51SGeorge Liu { 33637937d51SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 33737937d51SGeorge Liu { 33837937d51SGeorge Liu return; 33937937d51SGeorge Liu } 34037937d51SGeorge Liu if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 34137937d51SGeorge Liu { 34237937d51SGeorge Liu // Option currently returns no systems. TBD 34337937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 34437937d51SGeorge Liu systemName); 34537937d51SGeorge Liu return; 34637937d51SGeorge Liu } 34737937d51SGeorge Liu if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 34837937d51SGeorge Liu { 34937937d51SGeorge Liu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 35037937d51SGeorge Liu systemName); 35137937d51SGeorge Liu return; 35237937d51SGeorge Liu } 35337937d51SGeorge Liu 35437937d51SGeorge Liu dbus::utility::getAssociatedSubTreePathsById( 35537937d51SGeorge Liu adapterId, "/xyz/openbmc_project/inventory", fabricInterfaces, 35637937d51SGeorge Liu "connecting", portInterfaces, 35737937d51SGeorge Liu std::bind_front(doHandleFabricPortCollectionGet, asyncResp, systemName, 35837937d51SGeorge Liu adapterId)); 35937937d51SGeorge Liu } 36037937d51SGeorge Liu inline void requestRoutesFabricPort(App& app) 36137937d51SGeorge Liu { 36237937d51SGeorge Liu BMCWEB_ROUTE(app, 36337937d51SGeorge Liu "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/<str>/") 36437937d51SGeorge Liu .privileges(redfish::privileges::headPort) 36537937d51SGeorge Liu .methods(boost::beast::http::verb::head)( 36637937d51SGeorge Liu std::bind_front(handleFabricPortHead, std::ref(app))); 36737937d51SGeorge Liu 36837937d51SGeorge Liu BMCWEB_ROUTE(app, 36937937d51SGeorge Liu "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/<str>/") 37037937d51SGeorge Liu .privileges(redfish::privileges::getPort) 37137937d51SGeorge Liu .methods(boost::beast::http::verb::get)( 37237937d51SGeorge Liu std::bind_front(handleFabricPortGet, std::ref(app))); 37337937d51SGeorge Liu 37437937d51SGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/") 37537937d51SGeorge Liu .privileges(redfish::privileges::headPortCollection) 37637937d51SGeorge Liu .methods(boost::beast::http::verb::head)( 37737937d51SGeorge Liu std::bind_front(handleFabricPortCollectionHead, std::ref(app))); 37837937d51SGeorge Liu 37937937d51SGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/Ports/") 38037937d51SGeorge Liu .privileges(redfish::privileges::getPortCollection) 38137937d51SGeorge Liu .methods(boost::beast::http::verb::get)( 38237937d51SGeorge Liu std::bind_front(handleFabricPortCollectionGet, std::ref(app))); 38337937d51SGeorge Liu } 38437937d51SGeorge Liu 38537937d51SGeorge Liu } // namespace redfish 386