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
47                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
48                 property;
49         });
50 }
51 
52 inline void getFabricAdapterAsset(
53     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
54     const std::string& serviceName, 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,
79                 "SerialNumber", serialNumber, "Model", model, "PartNumber",
80                 partNumber, "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 getFabricAdapterState(
111     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
112     const std::string& serviceName, 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"] =
131                     resource::State::Absent;
132             }
133         });
134 }
135 
136 inline void getFabricAdapterHealth(
137     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
138     const std::string& serviceName, 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"] =
158                     resource::Health::Critical;
159             }
160         });
161 }
162 
163 inline void doAdapterGet(
164     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
165     const std::string& systemName, const std::string& adapterId,
166     const std::string& fabricAdapterPath, 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"] = resource::State::Enabled;
179     asyncResp->res.jsonValue["Status"]["Health"] = resource::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 handleFabricAdapterGet(
260     App& app, const crow::Request& req,
261     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262     const std::string& systemName, const std::string& adapterId)
263 {
264     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
265     {
266         return;
267     }
268     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
269     {
270         // Option currently returns no systems.  TBD
271         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
272                                    systemName);
273         return;
274     }
275     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
276     {
277         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
278                                    systemName);
279         return;
280     }
281     getValidFabricAdapterPath(
282         adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp,
283                                    systemName, adapterId));
284 }
285 
286 inline void handleFabricAdapterCollectionGet(
287     crow::App& app, const crow::Request& req,
288     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
289     const std::string& systemName)
290 {
291     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
292     {
293         return;
294     }
295     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
296     {
297         // Option currently returns no systems. TBD
298         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
299                                    systemName);
300         return;
301     }
302     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
303     {
304         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
305                                    systemName);
306         return;
307     }
308 
309     asyncResp->res.addHeader(
310         boost::beast::http::field::link,
311         "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
312     asyncResp->res.jsonValue["@odata.type"] =
313         "#FabricAdapterCollection.FabricAdapterCollection";
314     asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection";
315     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
316         "/redfish/v1/Systems/{}/FabricAdapters", systemName);
317 
318     constexpr std::array<std::string_view, 1> interfaces{
319         "xyz.openbmc_project.Inventory.Item.FabricAdapter"};
320     collection_util::getCollectionMembers(
321         asyncResp,
322         boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters",
323                             BMCWEB_REDFISH_SYSTEM_URI_NAME),
324         interfaces, "/xyz/openbmc_project/inventory");
325 }
326 
327 inline void handleFabricAdapterCollectionHead(
328     crow::App& app, const crow::Request& req,
329     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
330     const std::string& systemName)
331 {
332     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
333     {
334         return;
335     }
336     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
337     {
338         // Option currently returns no systems.  TBD
339         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
340                                    systemName);
341         return;
342     }
343     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
344     {
345         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
346                                    systemName);
347         return;
348     }
349     asyncResp->res.addHeader(
350         boost::beast::http::field::link,
351         "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
352 }
353 
354 inline void afterHandleFabricAdapterHead(
355     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
356     const std::string& adapterId, const boost::system::error_code& ec,
357     const std::string& fabricAdapterPath, const std::string& serviceName)
358 {
359     if (ec)
360     {
361         if (ec.value() == boost::system::errc::io_error)
362         {
363             messages::resourceNotFound(asyncResp->res, "FabricAdapter",
364                                        adapterId);
365             return;
366         }
367 
368         BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
369         messages::internalError(asyncResp->res);
370         return;
371     }
372     if (fabricAdapterPath.empty() || serviceName.empty())
373     {
374         BMCWEB_LOG_WARNING("Adapter not found");
375         messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
376         return;
377     }
378     asyncResp->res.addHeader(
379         boost::beast::http::field::link,
380         "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby");
381 }
382 
383 inline void handleFabricAdapterHead(
384     crow::App& app, const crow::Request& req,
385     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
386     const std::string& systemName, const std::string& adapterId)
387 {
388     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
389     {
390         return;
391     }
392 
393     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
394     {
395         // Option currently returns no systems. TBD
396         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
397                                    systemName);
398         return;
399     }
400     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
401     {
402         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
403                                    systemName);
404         return;
405     }
406     getValidFabricAdapterPath(
407         adapterId,
408         std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId));
409 }
410 
411 inline void requestRoutesFabricAdapterCollection(App& app)
412 {
413     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
414         .privileges(redfish::privileges::getFabricAdapterCollection)
415         .methods(boost::beast::http::verb::get)(
416             std::bind_front(handleFabricAdapterCollectionGet, std::ref(app)));
417 
418     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
419         .privileges(redfish::privileges::headFabricAdapterCollection)
420         .methods(boost::beast::http::verb::head)(
421             std::bind_front(handleFabricAdapterCollectionHead, std::ref(app)));
422 }
423 
424 inline void requestRoutesFabricAdapters(App& app)
425 {
426     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
427         .privileges(redfish::privileges::getFabricAdapter)
428         .methods(boost::beast::http::verb::get)(
429             std::bind_front(handleFabricAdapterGet, std::ref(app)));
430 
431     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
432         .privileges(redfish::privileges::headFabricAdapter)
433         .methods(boost::beast::http::verb::head)(
434             std::bind_front(handleFabricAdapterHead, std::ref(app)));
435 }
436 } // namespace redfish
437