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