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