1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 #pragma once
4
5 #include "bmcweb_config.h"
6
7 #include "app.hpp"
8 #include "async_resp.hpp"
9 #include "dbus_utility.hpp"
10 #include "error_messages.hpp"
11 #include "generated/enums/resource.hpp"
12 #include "http_request.hpp"
13 #include "led.hpp"
14 #include "logging.hpp"
15 #include "query.hpp"
16 #include "registries/privilege_registry.hpp"
17 #include "utils/collection.hpp"
18 #include "utils/dbus_utils.hpp"
19 #include "utils/json_utils.hpp"
20
21 #include <asm-generic/errno.h>
22
23 #include <boost/beast/http/field.hpp>
24 #include <boost/beast/http/verb.hpp>
25 #include <boost/system/error_code.hpp>
26 #include <boost/url/format.hpp>
27 #include <sdbusplus/unpack_properties.hpp>
28
29 #include <array>
30 #include <functional>
31 #include <memory>
32 #include <optional>
33 #include <string>
34 #include <string_view>
35 #include <utility>
36
37 namespace redfish
38 {
39
getFabricAdapterLocation(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)40 inline void getFabricAdapterLocation(
41 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
42 const std::string& serviceName, const std::string& fabricAdapterPath)
43 {
44 dbus::utility::getProperty<std::string>(
45 serviceName, fabricAdapterPath,
46 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
47 [asyncResp](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(asyncResp->res);
55 }
56 return;
57 }
58
59 asyncResp->res
60 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
61 property;
62 });
63 }
64
getFabricAdapterAsset(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)65 inline void getFabricAdapterAsset(
66 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
67 const std::string& serviceName, const std::string& fabricAdapterPath)
68 {
69 dbus::utility::getAllProperties(
70 serviceName, fabricAdapterPath,
71 "xyz.openbmc_project.Inventory.Decorator.Asset",
72 [fabricAdapterPath, asyncResp{asyncResp}](
73 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(asyncResp->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,
92 "SerialNumber", serialNumber, "Model", model, "PartNumber",
93 partNumber, "SparePartNumber", sparePartNumber);
94
95 if (!success)
96 {
97 messages::internalError(asyncResp->res);
98 return;
99 }
100
101 if (serialNumber != nullptr)
102 {
103 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
104 }
105
106 if (model != nullptr)
107 {
108 asyncResp->res.jsonValue["Model"] = *model;
109 }
110
111 if (partNumber != nullptr)
112 {
113 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
114 }
115
116 if (sparePartNumber != nullptr && !sparePartNumber->empty())
117 {
118 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
119 }
120 });
121 }
122
getFabricAdapterState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)123 inline void getFabricAdapterState(
124 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
125 const std::string& serviceName, const std::string& fabricAdapterPath)
126 {
127 dbus::utility::getProperty<bool>(
128 serviceName, fabricAdapterPath, "xyz.openbmc_project.Inventory.Item",
129 "Present",
130 [asyncResp](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(asyncResp->res);
137 }
138 return;
139 }
140
141 if (!present)
142 {
143 asyncResp->res.jsonValue["Status"]["State"] =
144 resource::State::Absent;
145 }
146 });
147 }
148
getFabricAdapterHealth(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & fabricAdapterPath)149 inline void getFabricAdapterHealth(
150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
151 const std::string& serviceName, const std::string& fabricAdapterPath)
152 {
153 dbus::utility::getProperty<bool>(
154 serviceName, fabricAdapterPath,
155 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
156 [asyncResp](const boost::system::error_code& ec,
157 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(asyncResp->res);
164 }
165 return;
166 }
167
168 if (!functional)
169 {
170 asyncResp->res.jsonValue["Status"]["Health"] =
171 resource::Health::Critical;
172 }
173 });
174 }
175
doAdapterGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId,const std::string & fabricAdapterPath,const std::string & serviceName)176 inline void doAdapterGet(
177 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
178 const std::string& systemName, const std::string& adapterId,
179 const std::string& fabricAdapterPath, const std::string& serviceName)
180 {
181 asyncResp->res.addHeader(
182 boost::beast::http::field::link,
183 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby");
184 asyncResp->res.jsonValue["@odata.type"] =
185 "#FabricAdapter.v1_4_0.FabricAdapter";
186 asyncResp->res.jsonValue["Name"] = "Fabric Adapter";
187 asyncResp->res.jsonValue["Id"] = adapterId;
188 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
189 "/redfish/v1/Systems/{}/FabricAdapters/{}", systemName, adapterId);
190
191 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
192 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK;
193
194 getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath);
195 getFabricAdapterAsset(asyncResp, serviceName, fabricAdapterPath);
196 getFabricAdapterState(asyncResp, serviceName, fabricAdapterPath);
197 getFabricAdapterHealth(asyncResp, serviceName, fabricAdapterPath);
198 getLocationIndicatorActive(asyncResp, fabricAdapterPath);
199 }
200
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)201 inline void afterGetValidFabricAdapterPath(
202 const std::string& adapterId,
203 std::function<void(const boost::system::error_code&,
204 const std::string& fabricAdapterPath,
205 const std::string& serviceName)>& callback,
206 const boost::system::error_code& ec,
207 const dbus::utility::MapperGetSubTreeResponse& subtree)
208 {
209 std::string fabricAdapterPath;
210 std::string serviceName;
211 if (ec)
212 {
213 callback(ec, fabricAdapterPath, serviceName);
214 return;
215 }
216
217 for (const auto& [adapterPath, serviceMap] : subtree)
218 {
219 std::string fabricAdapterName =
220 sdbusplus::message::object_path(adapterPath).filename();
221 if (fabricAdapterName == adapterId)
222 {
223 fabricAdapterPath = adapterPath;
224 serviceName = serviceMap.begin()->first;
225 break;
226 }
227 }
228 callback(ec, fabricAdapterPath, serviceName);
229 }
230
getValidFabricAdapterPath(const std::string & adapterId,std::function<void (const boost::system::error_code & ec,const std::string & fabricAdapterPath,const std::string & serviceName)> && callback)231 inline void getValidFabricAdapterPath(
232 const std::string& adapterId,
233 std::function<void(const boost::system::error_code& ec,
234 const std::string& fabricAdapterPath,
235 const std::string& serviceName)>&& callback)
236 {
237 constexpr std::array<std::string_view, 1> interfaces{
238 "xyz.openbmc_project.Inventory.Item.FabricAdapter"};
239 dbus::utility::getSubTree("/xyz/openbmc_project/inventory", 0, interfaces,
240 std::bind_front(afterGetValidFabricAdapterPath,
241 adapterId, std::move(callback)));
242 }
243
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)244 inline void afterHandleFabricAdapterGet(
245 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
246 const std::string& systemName, const std::string& adapterId,
247 const boost::system::error_code& ec, const std::string& fabricAdapterPath,
248 const std::string& serviceName)
249 {
250 if (ec)
251 {
252 if (ec.value() == boost::system::errc::io_error)
253 {
254 messages::resourceNotFound(asyncResp->res, "FabricAdapter",
255 adapterId);
256 return;
257 }
258
259 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
260 messages::internalError(asyncResp->res);
261 return;
262 }
263 if (fabricAdapterPath.empty() || serviceName.empty())
264 {
265 BMCWEB_LOG_WARNING("Adapter not found");
266 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
267 return;
268 }
269 doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath,
270 serviceName);
271 }
272
handleFabricAdapterGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId)273 inline void handleFabricAdapterGet(
274 App& app, const crow::Request& req,
275 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
276 const std::string& systemName, const std::string& adapterId)
277 {
278 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
279 {
280 return;
281 }
282 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
283 {
284 // Option currently returns no systems. TBD
285 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
286 systemName);
287 return;
288 }
289 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
290 {
291 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
292 systemName);
293 return;
294 }
295 getValidFabricAdapterPath(
296 adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp,
297 systemName, adapterId));
298 }
299
afterHandleFabricAdapterPatch(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & adapterId,std::optional<bool> locationIndicatorActive,const boost::system::error_code & ec,const std::string & fabricAdapterPath,const std::string & serviceName)300 inline void afterHandleFabricAdapterPatch(
301 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
302 const std::string& adapterId, std::optional<bool> locationIndicatorActive,
303 const boost::system::error_code& ec, const std::string& fabricAdapterPath,
304 const std::string& serviceName)
305 {
306 if (ec)
307 {
308 if (ec.value() == boost::system::errc::io_error)
309 {
310 messages::resourceNotFound(asyncResp->res, "FabricAdapter",
311 adapterId);
312 return;
313 }
314
315 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
316 messages::internalError(asyncResp->res);
317 return;
318 }
319 if (fabricAdapterPath.empty() || serviceName.empty())
320 {
321 BMCWEB_LOG_WARNING("Adapter {} not found", adapterId);
322 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
323 return;
324 }
325
326 if (locationIndicatorActive)
327 {
328 setLocationIndicatorActive(asyncResp, fabricAdapterPath,
329 *locationIndicatorActive);
330 }
331 }
332
handleFabricAdapterPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId)333 inline void handleFabricAdapterPatch(
334 App& app, const crow::Request& req,
335 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
336 const std::string& systemName, const std::string& adapterId)
337 {
338 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
339 {
340 return;
341 }
342 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
343 {
344 // Option currently returns no systems. TBD
345 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
346 systemName);
347 return;
348 }
349 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
350 {
351 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
352 systemName);
353 return;
354 }
355
356 std::optional<bool> locationIndicatorActive;
357
358 if (!json_util::readJsonPatch(req, asyncResp->res,
359 "LocationIndicatorActive",
360 locationIndicatorActive))
361 {
362 return;
363 }
364
365 getValidFabricAdapterPath(
366 adapterId, std::bind_front(afterHandleFabricAdapterPatch, asyncResp,
367 adapterId, locationIndicatorActive));
368 }
369
handleFabricAdapterCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)370 inline void handleFabricAdapterCollectionGet(
371 crow::App& app, const crow::Request& req,
372 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
373 const std::string& systemName)
374 {
375 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
376 {
377 return;
378 }
379 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
380 {
381 // Option currently returns no systems. TBD
382 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
383 systemName);
384 return;
385 }
386 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
387 {
388 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
389 systemName);
390 return;
391 }
392
393 asyncResp->res.addHeader(
394 boost::beast::http::field::link,
395 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
396 asyncResp->res.jsonValue["@odata.type"] =
397 "#FabricAdapterCollection.FabricAdapterCollection";
398 asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection";
399 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
400 "/redfish/v1/Systems/{}/FabricAdapters", systemName);
401
402 constexpr std::array<std::string_view, 1> interfaces{
403 "xyz.openbmc_project.Inventory.Item.FabricAdapter"};
404 collection_util::getCollectionMembers(
405 asyncResp,
406 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters",
407 BMCWEB_REDFISH_SYSTEM_URI_NAME),
408 interfaces, "/xyz/openbmc_project/inventory");
409 }
410
handleFabricAdapterCollectionHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)411 inline void handleFabricAdapterCollectionHead(
412 crow::App& app, const crow::Request& req,
413 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
414 const std::string& systemName)
415 {
416 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
417 {
418 return;
419 }
420 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
421 {
422 // Option currently returns no systems. TBD
423 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
424 systemName);
425 return;
426 }
427 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
428 {
429 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
430 systemName);
431 return;
432 }
433 asyncResp->res.addHeader(
434 boost::beast::http::field::link,
435 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby");
436 }
437
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)438 inline void afterHandleFabricAdapterHead(
439 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
440 const std::string& adapterId, const boost::system::error_code& ec,
441 const std::string& fabricAdapterPath, const std::string& serviceName)
442 {
443 if (ec)
444 {
445 if (ec.value() == boost::system::errc::io_error)
446 {
447 messages::resourceNotFound(asyncResp->res, "FabricAdapter",
448 adapterId);
449 return;
450 }
451
452 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
453 messages::internalError(asyncResp->res);
454 return;
455 }
456 if (fabricAdapterPath.empty() || serviceName.empty())
457 {
458 BMCWEB_LOG_WARNING("Adapter not found");
459 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId);
460 return;
461 }
462 asyncResp->res.addHeader(
463 boost::beast::http::field::link,
464 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby");
465 }
466
handleFabricAdapterHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & adapterId)467 inline void handleFabricAdapterHead(
468 crow::App& app, const crow::Request& req,
469 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
470 const std::string& systemName, const std::string& adapterId)
471 {
472 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
473 {
474 return;
475 }
476
477 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
478 {
479 // Option currently returns no systems. TBD
480 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
481 systemName);
482 return;
483 }
484 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
485 {
486 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
487 systemName);
488 return;
489 }
490 getValidFabricAdapterPath(
491 adapterId,
492 std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId));
493 }
494
requestRoutesFabricAdapterCollection(App & app)495 inline void requestRoutesFabricAdapterCollection(App& app)
496 {
497 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
498 .privileges(redfish::privileges::getFabricAdapterCollection)
499 .methods(boost::beast::http::verb::get)(
500 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app)));
501
502 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/")
503 .privileges(redfish::privileges::headFabricAdapterCollection)
504 .methods(boost::beast::http::verb::head)(
505 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app)));
506 }
507
requestRoutesFabricAdapters(App & app)508 inline void requestRoutesFabricAdapters(App& app)
509 {
510 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
511 .privileges(redfish::privileges::getFabricAdapter)
512 .methods(boost::beast::http::verb::get)(
513 std::bind_front(handleFabricAdapterGet, std::ref(app)));
514
515 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
516 .privileges(redfish::privileges::headFabricAdapter)
517 .methods(boost::beast::http::verb::head)(
518 std::bind_front(handleFabricAdapterHead, std::ref(app)));
519
520 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/")
521 .privileges(redfish::privileges::patchFabricAdapter)
522 .methods(boost::beast::http::verb::patch)(
523 std::bind_front(handleFabricAdapterPatch, std::ref(app)));
524 }
525 } // namespace redfish
526