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