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