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