xref: /openbmc/bmcweb/features/redfish/lib/fabric_ports.hpp (revision 37937d515e98beff1807438eb3c95329dfb035ab)
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