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