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