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