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