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