blob: 190352a64ade452e18529a1a5d8a038e2752326f [file] [log] [blame]
George Liua7210022022-10-05 15:44:11 +08001#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/chassis_utils.hpp"
George Liu2b45fb32022-10-05 17:00:00 +08008#include "utils/dbus_utils.hpp"
9#include "utils/json_utils.hpp"
George Liua7210022022-10-05 15:44:11 +080010
George Liu34dfcb92022-10-05 16:47:44 +080011#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070012#include <boost/url/format.hpp>
13
George Liua7210022022-10-05 15:44:11 +080014#include <memory>
15#include <optional>
16#include <string>
17
18namespace redfish
19{
20
George Liu00ef5dc2022-10-05 16:27:52 +080021inline void
22 updatePowerSupplyList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
23 const std::string& chassisId,
24 const std::string& powerSupplyPath)
George Liua7210022022-10-05 15:44:11 +080025{
George Liu00ef5dc2022-10-05 16:27:52 +080026 std::string powerSupplyName =
27 sdbusplus::message::object_path(powerSupplyPath).filename();
28 if (powerSupplyName.empty())
29 {
30 return;
31 }
32
33 nlohmann::json item = nlohmann::json::object();
34 item["@odata.id"] = boost::urls::format(
35 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId,
36 powerSupplyName);
37
38 nlohmann::json& powerSupplyList = asyncResp->res.jsonValue["Members"];
39 powerSupplyList.emplace_back(std::move(item));
40 asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size();
George Liua7210022022-10-05 15:44:11 +080041}
42
43inline void
44 doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
45 const std::string& chassisId,
46 const std::optional<std::string>& validChassisPath)
47{
48 if (!validChassisPath)
49 {
50 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
51 return;
52 }
53
54 asyncResp->res.addHeader(
55 boost::beast::http::field::link,
56 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
57 asyncResp->res.jsonValue["@odata.type"] =
58 "#PowerSupplyCollection.PowerSupplyCollection";
59 asyncResp->res.jsonValue["Name"] = "Power Supply Collection";
Ed Tanousef4c65b2023-04-24 15:28:50 -070060 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
61 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId);
George Liua7210022022-10-05 15:44:11 +080062 asyncResp->res.jsonValue["Description"] =
63 "The collection of PowerSupply resource instances.";
George Liu7a2bb2c2023-04-11 10:44:33 +080064 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
65 asyncResp->res.jsonValue["Members@odata.count"] = 0;
George Liua7210022022-10-05 15:44:11 +080066
67 std::string powerPath = *validChassisPath + "/powered_by";
68 dbus::utility::getAssociationEndPoints(
69 powerPath, [asyncResp, chassisId](
70 const boost::system::error_code& ec,
71 const dbus::utility::MapperEndPoints& endpoints) {
72 if (ec)
73 {
74 if (ec.value() != EBADR)
75 {
76 BMCWEB_LOG_ERROR << "DBUS response error" << ec.value();
77 messages::internalError(asyncResp->res);
78 }
79 return;
80 }
81
82 for (const auto& endpoint : endpoints)
83 {
84 updatePowerSupplyList(asyncResp, chassisId, endpoint);
85 }
86 });
87}
88
89inline void handlePowerSupplyCollectionHead(
90 App& app, const crow::Request& req,
91 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
92 const std::string& chassisId)
93{
94 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
95 {
96 return;
97 }
98
99 redfish::chassis_utils::getValidChassisPath(
100 asyncResp, chassisId,
101 [asyncResp,
102 chassisId](const std::optional<std::string>& validChassisPath) {
103 if (!validChassisPath)
104 {
105 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
106 return;
107 }
108 asyncResp->res.addHeader(
109 boost::beast::http::field::link,
110 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
111 });
112}
113
114inline void handlePowerSupplyCollectionGet(
115 App& app, const crow::Request& req,
116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
117 const std::string& chassisId)
118{
119 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
120 {
121 return;
122 }
123
124 redfish::chassis_utils::getValidChassisPath(
125 asyncResp, chassisId,
126 std::bind_front(doPowerSupplyCollection, asyncResp, chassisId));
127}
128
129inline void requestRoutesPowerSupplyCollection(App& app)
130{
131 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
132 .privileges(redfish::privileges::headPowerSupplyCollection)
133 .methods(boost::beast::http::verb::head)(
134 std::bind_front(handlePowerSupplyCollectionHead, std::ref(app)));
135
136 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
137 .privileges(redfish::privileges::getPowerSupplyCollection)
138 .methods(boost::beast::http::verb::get)(
139 std::bind_front(handlePowerSupplyCollectionGet, std::ref(app)));
140}
141
George Liu00ef5dc2022-10-05 16:27:52 +0800142inline bool checkPowerSupplyId(const std::string& powerSupplyPath,
143 const std::string& powerSupplyId)
144{
145 std::string powerSupplyName =
146 sdbusplus::message::object_path(powerSupplyPath).filename();
147
148 return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId);
149}
150
George Liu34dfcb92022-10-05 16:47:44 +0800151inline void getValidPowerSupplyPath(
152 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
153 const std::string& validChassisPath, const std::string& powerSupplyId,
154 std::function<void(const std::string& powerSupplyPath)>&& callback)
George Liu00ef5dc2022-10-05 16:27:52 +0800155{
156 std::string powerPath = validChassisPath + "/powered_by";
157 dbus::utility::getAssociationEndPoints(
158 powerPath, [asyncResp, powerSupplyId, callback{std::move(callback)}](
159 const boost::system::error_code& ec,
160 const dbus::utility::MapperEndPoints& endpoints) {
161 if (ec)
162 {
163 if (ec.value() != EBADR)
164 {
165 BMCWEB_LOG_ERROR
166 << "DBUS response error for getAssociationEndPoints"
167 << ec.value();
168 messages::internalError(asyncResp->res);
169 return;
170 }
171 messages::resourceNotFound(asyncResp->res, "PowerSupplies",
172 powerSupplyId);
173 return;
174 }
175
176 for (const auto& endpoint : endpoints)
177 {
178 if (checkPowerSupplyId(endpoint, powerSupplyId))
179 {
George Liu34dfcb92022-10-05 16:47:44 +0800180 callback(endpoint);
George Liu00ef5dc2022-10-05 16:27:52 +0800181 return;
182 }
183 }
184
185 if (!endpoints.empty())
186 {
187 BMCWEB_LOG_WARNING << "Power supply not found: "
188 << powerSupplyId;
189 messages::resourceNotFound(asyncResp->res, "PowerSupplies",
190 powerSupplyId);
191 return;
192 }
193 });
194}
195
196inline void
George Liu34dfcb92022-10-05 16:47:44 +0800197 getPowerSupplyState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
198 const std::string& service, const std::string& path)
199{
200 sdbusplus::asio::getProperty<bool>(
201 *crow::connections::systemBus, service, path,
202 "xyz.openbmc_project.Inventory.Item", "Present",
203 [asyncResp](const boost::system::error_code& ec, const bool value) {
204 if (ec)
205 {
206 if (ec.value() != EBADR)
207 {
208 BMCWEB_LOG_ERROR << "DBUS response error for State "
209 << ec.value();
210 messages::internalError(asyncResp->res);
211 }
212 return;
213 }
214
215 if (!value)
216 {
217 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
218 }
219 });
220}
221
222inline void
223 getPowerSupplyHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
224 const std::string& service, const std::string& path)
225{
226 sdbusplus::asio::getProperty<bool>(
227 *crow::connections::systemBus, service, path,
228 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
229 [asyncResp](const boost::system::error_code& ec, const bool value) {
230 if (ec)
231 {
232 if (ec.value() != EBADR)
233 {
234 BMCWEB_LOG_ERROR << "DBUS response error for Health "
235 << ec.value();
236 messages::internalError(asyncResp->res);
237 }
238 return;
239 }
240
241 if (!value)
242 {
243 asyncResp->res.jsonValue["Status"]["Health"] = "Critical";
244 }
245 });
246}
247
248inline void
George Liu2b45fb32022-10-05 17:00:00 +0800249 getPowerSupplyAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
250 const std::string& service, const std::string& path)
251{
252 sdbusplus::asio::getAllProperties(
253 *crow::connections::systemBus, service, path,
254 "xyz.openbmc_project.Inventory.Decorator.Asset",
255 [asyncResp](const boost::system::error_code& ec,
256 const dbus::utility::DBusPropertiesMap& propertiesList) {
257 if (ec)
258 {
259 if (ec.value() != EBADR)
260 {
261 BMCWEB_LOG_ERROR << "DBUS response error for Asset "
262 << ec.value();
263 messages::internalError(asyncResp->res);
264 }
265 return;
266 }
267
268 const std::string* partNumber = nullptr;
269 const std::string* serialNumber = nullptr;
270 const std::string* manufacturer = nullptr;
271 const std::string* model = nullptr;
272 const std::string* sparePartNumber = nullptr;
273
274 const bool success = sdbusplus::unpackPropertiesNoThrow(
275 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
276 partNumber, "SerialNumber", serialNumber, "Manufacturer",
277 manufacturer, "Model", model, "SparePartNumber", sparePartNumber);
278
279 if (!success)
280 {
281 messages::internalError(asyncResp->res);
282 return;
283 }
284
285 if (partNumber != nullptr)
286 {
287 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
288 }
289
290 if (serialNumber != nullptr)
291 {
292 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
293 }
294
295 if (manufacturer != nullptr)
296 {
297 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
298 }
299
300 if (model != nullptr)
301 {
302 asyncResp->res.jsonValue["Model"] = *model;
303 }
304
305 // SparePartNumber is optional on D-Bus so skip if it is empty
306 if (sparePartNumber != nullptr && !sparePartNumber->empty())
307 {
308 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
309 }
310 });
311}
312
George Liua0dba872022-10-05 17:03:20 +0800313inline void getPowerSupplyFirmwareVersion(
314 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
315 const std::string& service, const std::string& path)
316{
317 sdbusplus::asio::getProperty<std::string>(
318 *crow::connections::systemBus, service, path,
319 "xyz.openbmc_project.Software.Version", "Version",
320 [asyncResp](const boost::system::error_code& ec,
321 const std::string& value) {
322 if (ec)
323 {
324 if (ec.value() != EBADR)
325 {
326 BMCWEB_LOG_ERROR << "DBUS response error for FirmwareVersion "
327 << ec.value();
328 messages::internalError(asyncResp->res);
329 }
330 return;
331 }
332 asyncResp->res.jsonValue["FirmwareVersion"] = value;
333 });
334}
335
George Liu2b45fb32022-10-05 17:00:00 +0800336inline void
George Liu44845e52022-10-05 17:09:21 +0800337 getPowerSupplyLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
338 const std::string& service, const std::string& path)
339{
340 sdbusplus::asio::getProperty<std::string>(
341 *crow::connections::systemBus, service, path,
342 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
343 [asyncResp](const boost::system::error_code& ec,
344 const std::string& value) {
345 if (ec)
346 {
347 if (ec.value() != EBADR)
348 {
349 BMCWEB_LOG_ERROR << "DBUS response error for Location "
350 << ec.value();
351 messages::internalError(asyncResp->res);
352 }
353 return;
354 }
355 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
356 value;
357 });
358}
359
360inline void
George Liu00ef5dc2022-10-05 16:27:52 +0800361 doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
362 const std::string& chassisId,
363 const std::string& powerSupplyId,
364 const std::optional<std::string>& validChassisPath)
365{
366 if (!validChassisPath)
367 {
368 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
369 return;
370 }
371
372 // Get the correct Path and Service that match the input parameters
373 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
George Liu34dfcb92022-10-05 16:47:44 +0800374 [asyncResp, chassisId, powerSupplyId](
375 const std::string& powerSupplyPath) {
George Liu00ef5dc2022-10-05 16:27:52 +0800376 asyncResp->res.addHeader(
377 boost::beast::http::field::link,
378 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
379 asyncResp->res.jsonValue["@odata.type"] =
380 "#PowerSupply.v1_5_0.PowerSupply";
381 asyncResp->res.jsonValue["Name"] = "Power Supply";
382 asyncResp->res.jsonValue["Id"] = powerSupplyId;
383 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
384 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId,
385 powerSupplyId);
George Liu34dfcb92022-10-05 16:47:44 +0800386
387 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
388 asyncResp->res.jsonValue["Status"]["Health"] = "OK";
389
390 constexpr std::array<std::string_view, 1> interfaces = {
391 "xyz.openbmc_project.Inventory.Item.PowerSupply"};
392 dbus::utility::getDbusObject(
393 powerSupplyPath, interfaces,
394 [asyncResp,
395 powerSupplyPath](const boost::system::error_code& ec,
396 const dbus::utility::MapperGetObject& object) {
397 if (ec || object.empty())
398 {
399 messages::internalError(asyncResp->res);
400 return;
401 }
402
403 getPowerSupplyState(asyncResp, object.begin()->first,
404 powerSupplyPath);
405 getPowerSupplyHealth(asyncResp, object.begin()->first,
406 powerSupplyPath);
George Liu2b45fb32022-10-05 17:00:00 +0800407 getPowerSupplyAsset(asyncResp, object.begin()->first,
408 powerSupplyPath);
George Liua0dba872022-10-05 17:03:20 +0800409 getPowerSupplyFirmwareVersion(asyncResp, object.begin()->first,
410 powerSupplyPath);
George Liu44845e52022-10-05 17:09:21 +0800411 getPowerSupplyLocation(asyncResp, object.begin()->first,
412 powerSupplyPath);
George Liu34dfcb92022-10-05 16:47:44 +0800413 });
George Liu00ef5dc2022-10-05 16:27:52 +0800414 });
415}
416
417inline void
418 handlePowerSupplyHead(App& app, const crow::Request& req,
419 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
420 const std::string& chassisId,
421 const std::string& powerSupplyId)
422{
423 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
424 {
425 return;
426 }
427
428 redfish::chassis_utils::getValidChassisPath(
429 asyncResp, chassisId,
430 [asyncResp, chassisId,
431 powerSupplyId](const std::optional<std::string>& validChassisPath) {
432 if (!validChassisPath)
433 {
434 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
435 return;
436 }
437
438 // Get the correct Path and Service that match the input parameters
439 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
George Liu34dfcb92022-10-05 16:47:44 +0800440 [asyncResp](const std::string&) {
George Liu00ef5dc2022-10-05 16:27:52 +0800441 asyncResp->res.addHeader(
442 boost::beast::http::field::link,
443 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
444 });
445 });
446}
447
448inline void
449 handlePowerSupplyGet(App& app, const crow::Request& req,
450 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
451 const std::string& chassisId,
452 const std::string& powerSupplyId)
453{
454 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
455 {
456 return;
457 }
458
459 redfish::chassis_utils::getValidChassisPath(
460 asyncResp, chassisId,
461 std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId));
462}
463
464inline void requestRoutesPowerSupply(App& app)
465{
466 BMCWEB_ROUTE(
467 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
468 .privileges(redfish::privileges::headPowerSupply)
469 .methods(boost::beast::http::verb::head)(
470 std::bind_front(handlePowerSupplyHead, std::ref(app)));
471
472 BMCWEB_ROUTE(
473 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
474 .privileges(redfish::privileges::getPowerSupply)
475 .methods(boost::beast::http::verb::get)(
476 std::bind_front(handlePowerSupplyGet, std::ref(app)));
477}
478
George Liua7210022022-10-05 15:44:11 +0800479} // namespace redfish