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