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