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