blob: 4fc0ce9e48e215bc351bad0fbe9d3e534433c4de [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 Liu00ef5dc2022-10-05 16:27:52 +0800337 doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
338 const std::string& chassisId,
339 const std::string& powerSupplyId,
340 const std::optional<std::string>& validChassisPath)
341{
342 if (!validChassisPath)
343 {
344 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
345 return;
346 }
347
348 // Get the correct Path and Service that match the input parameters
349 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
George Liu34dfcb92022-10-05 16:47:44 +0800350 [asyncResp, chassisId, powerSupplyId](
351 const std::string& powerSupplyPath) {
George Liu00ef5dc2022-10-05 16:27:52 +0800352 asyncResp->res.addHeader(
353 boost::beast::http::field::link,
354 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
355 asyncResp->res.jsonValue["@odata.type"] =
356 "#PowerSupply.v1_5_0.PowerSupply";
357 asyncResp->res.jsonValue["Name"] = "Power Supply";
358 asyncResp->res.jsonValue["Id"] = powerSupplyId;
359 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
360 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId,
361 powerSupplyId);
George Liu34dfcb92022-10-05 16:47:44 +0800362
363 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
364 asyncResp->res.jsonValue["Status"]["Health"] = "OK";
365
366 constexpr std::array<std::string_view, 1> interfaces = {
367 "xyz.openbmc_project.Inventory.Item.PowerSupply"};
368 dbus::utility::getDbusObject(
369 powerSupplyPath, interfaces,
370 [asyncResp,
371 powerSupplyPath](const boost::system::error_code& ec,
372 const dbus::utility::MapperGetObject& object) {
373 if (ec || object.empty())
374 {
375 messages::internalError(asyncResp->res);
376 return;
377 }
378
379 getPowerSupplyState(asyncResp, object.begin()->first,
380 powerSupplyPath);
381 getPowerSupplyHealth(asyncResp, object.begin()->first,
382 powerSupplyPath);
George Liu2b45fb32022-10-05 17:00:00 +0800383 getPowerSupplyAsset(asyncResp, object.begin()->first,
384 powerSupplyPath);
George Liua0dba872022-10-05 17:03:20 +0800385 getPowerSupplyFirmwareVersion(asyncResp, object.begin()->first,
386 powerSupplyPath);
George Liu34dfcb92022-10-05 16:47:44 +0800387 });
George Liu00ef5dc2022-10-05 16:27:52 +0800388 });
389}
390
391inline void
392 handlePowerSupplyHead(App& app, const crow::Request& req,
393 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
394 const std::string& chassisId,
395 const std::string& powerSupplyId)
396{
397 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
398 {
399 return;
400 }
401
402 redfish::chassis_utils::getValidChassisPath(
403 asyncResp, chassisId,
404 [asyncResp, chassisId,
405 powerSupplyId](const std::optional<std::string>& validChassisPath) {
406 if (!validChassisPath)
407 {
408 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
409 return;
410 }
411
412 // Get the correct Path and Service that match the input parameters
413 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
George Liu34dfcb92022-10-05 16:47:44 +0800414 [asyncResp](const std::string&) {
George Liu00ef5dc2022-10-05 16:27:52 +0800415 asyncResp->res.addHeader(
416 boost::beast::http::field::link,
417 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
418 });
419 });
420}
421
422inline void
423 handlePowerSupplyGet(App& app, const crow::Request& req,
424 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
425 const std::string& chassisId,
426 const std::string& powerSupplyId)
427{
428 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
429 {
430 return;
431 }
432
433 redfish::chassis_utils::getValidChassisPath(
434 asyncResp, chassisId,
435 std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId));
436}
437
438inline void requestRoutesPowerSupply(App& app)
439{
440 BMCWEB_ROUTE(
441 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
442 .privileges(redfish::privileges::headPowerSupply)
443 .methods(boost::beast::http::verb::head)(
444 std::bind_front(handlePowerSupplyHead, std::ref(app)));
445
446 BMCWEB_ROUTE(
447 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
448 .privileges(redfish::privileges::getPowerSupply)
449 .methods(boost::beast::http::verb::get)(
450 std::bind_front(handlePowerSupplyGet, std::ref(app)));
451}
452
George Liua7210022022-10-05 15:44:11 +0800453} // namespace redfish