xref: /openbmc/bmcweb/redfish-core/lib/fabric_adapters.hpp (revision f7e62c142ced153e9400b519bdd66062dd6bbf0e)
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 "generated/enums/resource.hpp"
12 #include "http_request.hpp"
13 #include "led.hpp"
14 #include "logging.hpp"
15 #include "query.hpp"
16 #include "registries/privilege_registry.hpp"
17 #include "utils/asset_utils.hpp"
18 #include "utils/collection.hpp"
19 #include "utils/dbus_utils.hpp"
20 #include "utils/json_utils.hpp"
21 
22 #include <asm-generic/errno.h>
23 
24 #include <boost/beast/http/field.hpp>
25 #include <boost/beast/http/verb.hpp>
26 #include <boost/system/error_code.hpp>
27 #include <boost/url/format.hpp>
28 #include <sdbusplus/unpack_properties.hpp>
29 
30 #include <array>
31 #include <functional>
32 #include <memory>
33 #include <optional>
34 #include <string>
35 #include <string_view>
36 #include <utility>
37 
38 namespace redfish
39 {
40 
41 inline void getFabricAdapterLocation(
42     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
43     const std::string& serviceName, const std::string& fabricAdapterPath)
44 {
45     dbus::utility::getProperty<std::string>(
46         serviceName, fabricAdapterPath,
47         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
48         [asyncResp](const boost::system::error_code& ec,
49                     const std::string& property) {
50             if (ec)
51             {
52                 if (ec.value() != EBADR)
53                 {
54                     BMCWEB_LOG_ERROR("DBUS response error for Location");
55                     messages::internalError(asyncResp->res);
56                 }
57                 return;
58             }
59 
60             asyncResp->res
61                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
62                 property;
63         });
64 }
65 
66 inline void getFabricAdapterState(
67     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
68     const std::string& serviceName, const std::string& fabricAdapterPath)
69 {
70     dbus::utility::getProperty<bool>(
71         serviceName, fabricAdapterPath, "xyz.openbmc_project.Inventory.Item",
72         "Present",
73         [asyncResp](const boost::system::error_code& ec, const bool present) {
74             if (ec)
75             {
76                 if (ec.value() != EBADR)
77                 {
78                     BMCWEB_LOG_ERROR("DBUS response error for State");
79                     messages::internalError(asyncResp->res);
80                 }
81                 return;
82             }
83 
84             if (!present)
85             {
86                 asyncResp->res.jsonValue["Status"]["State"] =
87                     resource::State::Absent;
88             }
89         });
90 }
91 
92 inline void getFabricAdapterHealth(
93     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
94     const std::string& serviceName, const std::string& fabricAdapterPath)
95 {
96     dbus::utility::getProperty<bool>(
97         serviceName, fabricAdapterPath,
98         "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
99         [asyncResp](const boost::system::error_code& ec,
100                     const bool functional) {
101             if (ec)
102             {
103                 if (ec.value() != EBADR)
104                 {
105                     BMCWEB_LOG_ERROR("DBUS response error for Health");
106                     messages::internalError(asyncResp->res);
107                 }
108                 return;
109             }
110 
111             if (!functional)
112             {
113                 asyncResp->res.jsonValue["Status"]["Health"] =
114                     resource::Health::Critical;
115             }
116         });
117 }
118 
119 inline void doAdapterGet(
120     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
121     const std::string& systemName, const std::string& adapterId,
122     const std::string& fabricAdapterPath, const std::string& serviceName)
123 {
124     asyncResp->res.addHeader(
125         boost::beast::http::field::link,
126         "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby");
127     asyncResp->res.jsonValue["@odata.type"] =
128         "#FabricAdapter.v1_4_0.FabricAdapter";
129     asyncResp->res.jsonValue["Name"] = "Fabric Adapter";
130     asyncResp->res.jsonValue["Id"] = adapterId;
131     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
132         "/redfish/v1/Systems/{}/FabricAdapters/{}", systemName, adapterId);
133 
134     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
135     asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK;
136 
137     asyncResp->res.jsonValue["Ports"]["@odata.id"] =
138         boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters/{}/Ports",
139                             systemName, adapterId);
140 
141     getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath);
142     asset_utils::getAssetInfo(asyncResp, serviceName, fabricAdapterPath,
143                               ""_json_pointer, true);
144     getFabricAdapterState(asyncResp, serviceName, fabricAdapterPath);
145     getFabricAdapterHealth(asyncResp, serviceName, fabricAdapterPath);
146     getLocationIndicatorActive(asyncResp, fabricAdapterPath);
147 }
148 
149 inline void afterGetValidFabricAdapterPath(
150     const std::string& adapterId,
151     std::function<void(const boost::system::error_code&,
152                        const std::string& fabricAdapterPath,
153                        const std::string& serviceName)>& callback,
154     const boost::system::error_code& ec,
155     const dbus::utility::MapperGetSubTreeResponse& subtree)
156 {
157     std::string fabricAdapterPath;
158     std::string serviceName;
159     if (ec)
160     {
161         callback(ec, fabricAdapterPath, serviceName);
162         return;
163     }
164 
165     for (const auto& [adapterPath, serviceMap] : subtree)
166     {
167         std::string fabricAdapterName =
168             sdbusplus::message::object_path(adapterPath).filename();
169         if (fabricAdapterName == adapterId)
170         {
171             fabricAdapterPath = adapterPath;
172             serviceName = serviceMap.begin()->first;
173             break;
174         }
175     }
176     callback(ec, fabricAdapterPath, serviceName);
177 }
178 
179 inline void getValidFabricAdapterPath(
180     const std::string& adapterId,
181     std::function<void(const boost::system::error_code& ec,
182                        const std::string& fabricAdapterPath,
183                        const std::string& serviceName)>&& callback)
184 {
185     constexpr std::array<std::string_view, 1> interfaces{
186         "xyz.openbmc_project.Inventory.Item.FabricAdapter"};
187     dbus::utility::getSubTree("/xyz/openbmc_project/inventory", 0, interfaces,
188                               std::bind_front(afterGetValidFabricAdapterPath,
189                                               adapterId, std::move(callback)));
190 }
191 
192 inline void afterHandleFabricAdapterGet(
193     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
194     const std::string& systemName, const std::string& adapterId,
195     const boost::system::error_code& ec, const std::string& fabricAdapterPath,
196     const std::string& serviceName)
197 {
198     if (ec)
199     {
200         if (ec.value() == boost::system::errc::io_error)
201         {
202             messages::resourceNotFound(asyncResp->res, "FabricAdapter",
203                                        adapterId);
204             return;
205         }
206 
207         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
208         messages::internalError(asyncResp->res);
209         return;
210     }
211     if (fabricAdapterPath.empty() || serviceName.empty())
212     {
213         BMCWEB_LOG_WARNING("Adapter not found");
214         messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
215         return;
216     }
217     doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath,
218                  serviceName);
219 }
220 
221 inline void handleFabricAdapterGet(
222     App& app, const crow::Request& req,
223     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
224     const std::string& systemName, const std::string& adapterId)
225 {
226     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
227     {
228         return;
229     }
230     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
231     {
232         // Option currently returns no systems.  TBD
233         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
234                                    systemName);
235         return;
236     }
237     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
238     {
239         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
240                                    systemName);
241         return;
242     }
243     getValidFabricAdapterPath(
244         adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp,
245                                    systemName, adapterId));
246 }
247 
248 inline void afterHandleFabricAdapterPatch(
249     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
250     const std::string& adapterId, std::optional<bool> locationIndicatorActive,
251     const boost::system::error_code& ec, const std::string& fabricAdapterPath,
252     const std::string& serviceName)
253 {
254     if (ec)
255     {
256         if (ec.value() == boost::system::errc::io_error)
257         {
258             messages::resourceNotFound(asyncResp->res, "FabricAdapter",
259                                        adapterId);
260             return;
261         }
262 
263         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
264         messages::internalError(asyncResp->res);
265         return;
266     }
267     if (fabricAdapterPath.empty() || serviceName.empty())
268     {
269         BMCWEB_LOG_WARNING("Adapter {} not found", adapterId);
270         messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
271         return;
272     }
273 
274     if (locationIndicatorActive)
275     {
276         setLocationIndicatorActive(asyncResp, fabricAdapterPath,
277                                    *locationIndicatorActive);
278     }
279 }
280 
281 inline void handleFabricAdapterPatch(
282     App& app, const crow::Request& req,
283     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
284     const std::string& systemName, const std::string& adapterId)
285 {
286     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
287     {
288         return;
289     }
290     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
291     {
292         // Option currently returns no systems.  TBD
293         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
294                                    systemName);
295         return;
296     }
297     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
298     {
299         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
300                                    systemName);
301         return;
302     }
303 
304     std::optional<bool> locationIndicatorActive;
305 
306     if (!json_util::readJsonPatch(req, asyncResp->res,
307                                   "LocationIndicatorActive",
308                                   locationIndicatorActive))
309     {
310         return;
311     }
312 
313     getValidFabricAdapterPath(
314         adapterId, std::bind_front(afterHandleFabricAdapterPatch, asyncResp,
315                                    adapterId, locationIndicatorActive));
316 }
317 
318 inline void handleFabricAdapterCollectionGet(
319     crow::App& app, const crow::Request& req,
320     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
321     const std::string& systemName)
322 {
323     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
324     {
325         return;
326     }
327     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
328     {
329         // Option currently returns no systems. TBD
330         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
331                                    systemName);
332         return;
333     }
334     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
335     {
336         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
337                                    systemName);
338         return;
339     }
340 
341     asyncResp->res.addHeader(
342         boost::beast::http::field::link,
343         "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
344     asyncResp->res.jsonValue["@odata.type"] =
345         "#FabricAdapterCollection.FabricAdapterCollection";
346     asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection";
347     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
348         "/redfish/v1/Systems/{}/FabricAdapters", systemName);
349 
350     constexpr std::array<std::string_view, 1> interfaces{
351         "xyz.openbmc_project.Inventory.Item.FabricAdapter"};
352     collection_util::getCollectionMembers(
353         asyncResp,
354         boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters",
355                             BMCWEB_REDFISH_SYSTEM_URI_NAME),
356         interfaces, "/xyz/openbmc_project/inventory");
357 }
358 
359 inline void handleFabricAdapterCollectionHead(
360     crow::App& app, const crow::Request& req,
361     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
362     const std::string& systemName)
363 {
364     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
365     {
366         return;
367     }
368     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
369     {
370         // Option currently returns no systems.  TBD
371         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
372                                    systemName);
373         return;
374     }
375     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
376     {
377         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
378                                    systemName);
379         return;
380     }
381     asyncResp->res.addHeader(
382         boost::beast::http::field::link,
383         "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
384 }
385 
386 inline void afterHandleFabricAdapterHead(
387     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
388     const std::string& adapterId, const boost::system::error_code& ec,
389     const std::string& fabricAdapterPath, const std::string& serviceName)
390 {
391     if (ec)
392     {
393         if (ec.value() == boost::system::errc::io_error)
394         {
395             messages::resourceNotFound(asyncResp->res, "FabricAdapter",
396                                        adapterId);
397             return;
398         }
399 
400         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
401         messages::internalError(asyncResp->res);
402         return;
403     }
404     if (fabricAdapterPath.empty() || serviceName.empty())
405     {
406         BMCWEB_LOG_WARNING("Adapter not found");
407         messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
408         return;
409     }
410     asyncResp->res.addHeader(
411         boost::beast::http::field::link,
412         "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby");
413 }
414 
415 inline void handleFabricAdapterHead(
416     crow::App& app, const crow::Request& req,
417     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
418     const std::string& systemName, const std::string& adapterId)
419 {
420     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
421     {
422         return;
423     }
424 
425     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
426     {
427         // Option currently returns no systems. TBD
428         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
429                                    systemName);
430         return;
431     }
432     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
433     {
434         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
435                                    systemName);
436         return;
437     }
438     getValidFabricAdapterPath(
439         adapterId,
440         std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId));
441 }
442 
443 inline void requestRoutesFabricAdapterCollection(App& app)
444 {
445     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
446         .privileges(redfish::privileges::getFabricAdapterCollection)
447         .methods(boost::beast::http::verb::get)(
448             std::bind_front(handleFabricAdapterCollectionGet, std::ref(app)));
449 
450     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
451         .privileges(redfish::privileges::headFabricAdapterCollection)
452         .methods(boost::beast::http::verb::head)(
453             std::bind_front(handleFabricAdapterCollectionHead, std::ref(app)));
454 }
455 
456 inline void requestRoutesFabricAdapters(App& app)
457 {
458     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
459         .privileges(redfish::privileges::getFabricAdapter)
460         .methods(boost::beast::http::verb::get)(
461             std::bind_front(handleFabricAdapterGet, std::ref(app)));
462 
463     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
464         .privileges(redfish::privileges::headFabricAdapter)
465         .methods(boost::beast::http::verb::head)(
466             std::bind_front(handleFabricAdapterHead, std::ref(app)));
467 
468     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
469         .privileges(redfish::privileges::patchFabricAdapter)
470         .methods(boost::beast::http::verb::patch)(
471             std::bind_front(handleFabricAdapterPatch, std::ref(app)));
472 }
473 } // namespace redfish
474