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