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