xref: /openbmc/bmcweb/redfish-core/lib/fabric_adapters.hpp (revision be1384e3add6a57e1908443e28f49247403a947b)
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 
getFabricAdapterLocation(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)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 
getFabricAdapterAsset(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)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 
getFabricAdapterState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)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 
getFabricAdapterHealth(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)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 
doAdapterGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId,const std::string & fabricAdapterPath,const std::string & serviceName)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     getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath);
195     getFabricAdapterAsset(asyncResp, serviceName, fabricAdapterPath);
196     getFabricAdapterState(asyncResp, serviceName, fabricAdapterPath);
197     getFabricAdapterHealth(asyncResp, serviceName, fabricAdapterPath);
198     getLocationIndicatorActive(asyncResp, fabricAdapterPath);
199 }
200 
afterGetValidFabricAdapterPath(const std::string & adapterId,std::function<void (const boost::system::error_code &,const std::string & fabricAdapterPath,const std::string & serviceName)> & callback,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)201 inline void afterGetValidFabricAdapterPath(
202     const std::string& adapterId,
203     std::function<void(const boost::system::error_code&,
204                        const std::string& fabricAdapterPath,
205                        const std::string& serviceName)>& callback,
206     const boost::system::error_code& ec,
207     const dbus::utility::MapperGetSubTreeResponse& subtree)
208 {
209     std::string fabricAdapterPath;
210     std::string serviceName;
211     if (ec)
212     {
213         callback(ec, fabricAdapterPath, serviceName);
214         return;
215     }
216 
217     for (const auto& [adapterPath, serviceMap] : subtree)
218     {
219         std::string fabricAdapterName =
220             sdbusplus::message::object_path(adapterPath).filename();
221         if (fabricAdapterName == adapterId)
222         {
223             fabricAdapterPath = adapterPath;
224             serviceName = serviceMap.begin()->first;
225             break;
226         }
227     }
228     callback(ec, fabricAdapterPath, serviceName);
229 }
230 
getValidFabricAdapterPath(const std::string & adapterId,std::function<void (const boost::system::error_code & ec,const std::string & fabricAdapterPath,const std::string & serviceName)> && callback)231 inline void getValidFabricAdapterPath(
232     const std::string& adapterId,
233     std::function<void(const boost::system::error_code& ec,
234                        const std::string& fabricAdapterPath,
235                        const std::string& serviceName)>&& callback)
236 {
237     constexpr std::array<std::string_view, 1> interfaces{
238         "xyz.openbmc_project.Inventory.Item.FabricAdapter"};
239     dbus::utility::getSubTree("/xyz/openbmc_project/inventory", 0, interfaces,
240                               std::bind_front(afterGetValidFabricAdapterPath,
241                                               adapterId, std::move(callback)));
242 }
243 
afterHandleFabricAdapterGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId,const boost::system::error_code & ec,const std::string & fabricAdapterPath,const std::string & serviceName)244 inline void afterHandleFabricAdapterGet(
245     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
246     const std::string& systemName, const std::string& adapterId,
247     const boost::system::error_code& ec, const std::string& fabricAdapterPath,
248     const std::string& serviceName)
249 {
250     if (ec)
251     {
252         if (ec.value() == boost::system::errc::io_error)
253         {
254             messages::resourceNotFound(asyncResp->res, "FabricAdapter",
255                                        adapterId);
256             return;
257         }
258 
259         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
260         messages::internalError(asyncResp->res);
261         return;
262     }
263     if (fabricAdapterPath.empty() || serviceName.empty())
264     {
265         BMCWEB_LOG_WARNING("Adapter not found");
266         messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
267         return;
268     }
269     doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath,
270                  serviceName);
271 }
272 
handleFabricAdapterGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId)273 inline void handleFabricAdapterGet(
274     App& app, const crow::Request& req,
275     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
276     const std::string& systemName, const std::string& adapterId)
277 {
278     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
279     {
280         return;
281     }
282     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
283     {
284         // Option currently returns no systems.  TBD
285         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
286                                    systemName);
287         return;
288     }
289     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
290     {
291         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
292                                    systemName);
293         return;
294     }
295     getValidFabricAdapterPath(
296         adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp,
297                                    systemName, adapterId));
298 }
299 
afterHandleFabricAdapterPatch(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & adapterId,std::optional<bool> locationIndicatorActive,const boost::system::error_code & ec,const std::string & fabricAdapterPath,const std::string & serviceName)300 inline void afterHandleFabricAdapterPatch(
301     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
302     const std::string& adapterId, std::optional<bool> locationIndicatorActive,
303     const boost::system::error_code& ec, const std::string& fabricAdapterPath,
304     const std::string& serviceName)
305 {
306     if (ec)
307     {
308         if (ec.value() == boost::system::errc::io_error)
309         {
310             messages::resourceNotFound(asyncResp->res, "FabricAdapter",
311                                        adapterId);
312             return;
313         }
314 
315         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
316         messages::internalError(asyncResp->res);
317         return;
318     }
319     if (fabricAdapterPath.empty() || serviceName.empty())
320     {
321         BMCWEB_LOG_WARNING("Adapter {} not found", adapterId);
322         messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
323         return;
324     }
325 
326     if (locationIndicatorActive)
327     {
328         setLocationIndicatorActive(asyncResp, fabricAdapterPath,
329                                    *locationIndicatorActive);
330     }
331 }
332 
handleFabricAdapterPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId)333 inline void handleFabricAdapterPatch(
334     App& app, const crow::Request& req,
335     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
336     const std::string& systemName, const std::string& adapterId)
337 {
338     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
339     {
340         return;
341     }
342     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
343     {
344         // Option currently returns no systems.  TBD
345         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
346                                    systemName);
347         return;
348     }
349     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
350     {
351         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
352                                    systemName);
353         return;
354     }
355 
356     std::optional<bool> locationIndicatorActive;
357 
358     if (!json_util::readJsonPatch(req, asyncResp->res,
359                                   "LocationIndicatorActive",
360                                   locationIndicatorActive))
361     {
362         return;
363     }
364 
365     getValidFabricAdapterPath(
366         adapterId, std::bind_front(afterHandleFabricAdapterPatch, asyncResp,
367                                    adapterId, locationIndicatorActive));
368 }
369 
handleFabricAdapterCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)370 inline void handleFabricAdapterCollectionGet(
371     crow::App& app, const crow::Request& req,
372     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
373     const std::string& systemName)
374 {
375     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
376     {
377         return;
378     }
379     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
380     {
381         // Option currently returns no systems. TBD
382         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
383                                    systemName);
384         return;
385     }
386     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
387     {
388         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
389                                    systemName);
390         return;
391     }
392 
393     asyncResp->res.addHeader(
394         boost::beast::http::field::link,
395         "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
396     asyncResp->res.jsonValue["@odata.type"] =
397         "#FabricAdapterCollection.FabricAdapterCollection";
398     asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection";
399     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
400         "/redfish/v1/Systems/{}/FabricAdapters", systemName);
401 
402     constexpr std::array<std::string_view, 1> interfaces{
403         "xyz.openbmc_project.Inventory.Item.FabricAdapter"};
404     collection_util::getCollectionMembers(
405         asyncResp,
406         boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters",
407                             BMCWEB_REDFISH_SYSTEM_URI_NAME),
408         interfaces, "/xyz/openbmc_project/inventory");
409 }
410 
handleFabricAdapterCollectionHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)411 inline void handleFabricAdapterCollectionHead(
412     crow::App& app, const crow::Request& req,
413     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
414     const std::string& systemName)
415 {
416     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
417     {
418         return;
419     }
420     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
421     {
422         // Option currently returns no systems.  TBD
423         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
424                                    systemName);
425         return;
426     }
427     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
428     {
429         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
430                                    systemName);
431         return;
432     }
433     asyncResp->res.addHeader(
434         boost::beast::http::field::link,
435         "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
436 }
437 
afterHandleFabricAdapterHead(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & adapterId,const boost::system::error_code & ec,const std::string & fabricAdapterPath,const std::string & serviceName)438 inline void afterHandleFabricAdapterHead(
439     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
440     const std::string& adapterId, const boost::system::error_code& ec,
441     const std::string& fabricAdapterPath, const std::string& serviceName)
442 {
443     if (ec)
444     {
445         if (ec.value() == boost::system::errc::io_error)
446         {
447             messages::resourceNotFound(asyncResp->res, "FabricAdapter",
448                                        adapterId);
449             return;
450         }
451 
452         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
453         messages::internalError(asyncResp->res);
454         return;
455     }
456     if (fabricAdapterPath.empty() || serviceName.empty())
457     {
458         BMCWEB_LOG_WARNING("Adapter not found");
459         messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
460         return;
461     }
462     asyncResp->res.addHeader(
463         boost::beast::http::field::link,
464         "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby");
465 }
466 
handleFabricAdapterHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId)467 inline void handleFabricAdapterHead(
468     crow::App& app, const crow::Request& req,
469     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
470     const std::string& systemName, const std::string& adapterId)
471 {
472     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
473     {
474         return;
475     }
476 
477     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
478     {
479         // Option currently returns no systems. TBD
480         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
481                                    systemName);
482         return;
483     }
484     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
485     {
486         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
487                                    systemName);
488         return;
489     }
490     getValidFabricAdapterPath(
491         adapterId,
492         std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId));
493 }
494 
requestRoutesFabricAdapterCollection(App & app)495 inline void requestRoutesFabricAdapterCollection(App& app)
496 {
497     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
498         .privileges(redfish::privileges::getFabricAdapterCollection)
499         .methods(boost::beast::http::verb::get)(
500             std::bind_front(handleFabricAdapterCollectionGet, std::ref(app)));
501 
502     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
503         .privileges(redfish::privileges::headFabricAdapterCollection)
504         .methods(boost::beast::http::verb::head)(
505             std::bind_front(handleFabricAdapterCollectionHead, std::ref(app)));
506 }
507 
requestRoutesFabricAdapters(App & app)508 inline void requestRoutesFabricAdapters(App& app)
509 {
510     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
511         .privileges(redfish::privileges::getFabricAdapter)
512         .methods(boost::beast::http::verb::get)(
513             std::bind_front(handleFabricAdapterGet, std::ref(app)));
514 
515     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
516         .privileges(redfish::privileges::headFabricAdapter)
517         .methods(boost::beast::http::verb::head)(
518             std::bind_front(handleFabricAdapterHead, std::ref(app)));
519 
520     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
521         .privileges(redfish::privileges::patchFabricAdapter)
522         .methods(boost::beast::http::verb::patch)(
523             std::bind_front(handleFabricAdapterPatch, std::ref(app)));
524 }
525 } // namespace redfish
526