blob: 8360fb197ccaae96145757c4627505133408ba76 [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"
8
George Liu34dfcb92022-10-05 16:47:44 +08009#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070010#include <boost/url/format.hpp>
11
George Liua7210022022-10-05 15:44:11 +080012#include <memory>
13#include <optional>
14#include <string>
15
16namespace redfish
17{
18
George Liu00ef5dc2022-10-05 16:27:52 +080019inline void
20 updatePowerSupplyList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
21 const std::string& chassisId,
22 const std::string& powerSupplyPath)
George Liua7210022022-10-05 15:44:11 +080023{
George Liu00ef5dc2022-10-05 16:27:52 +080024 std::string powerSupplyName =
25 sdbusplus::message::object_path(powerSupplyPath).filename();
26 if (powerSupplyName.empty())
27 {
28 return;
29 }
30
31 nlohmann::json item = nlohmann::json::object();
32 item["@odata.id"] = boost::urls::format(
33 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId,
34 powerSupplyName);
35
36 nlohmann::json& powerSupplyList = asyncResp->res.jsonValue["Members"];
37 powerSupplyList.emplace_back(std::move(item));
38 asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size();
George Liua7210022022-10-05 15:44:11 +080039}
40
41inline void
42 doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
43 const std::string& chassisId,
44 const std::optional<std::string>& validChassisPath)
45{
46 if (!validChassisPath)
47 {
48 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
49 return;
50 }
51
52 asyncResp->res.addHeader(
53 boost::beast::http::field::link,
54 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
55 asyncResp->res.jsonValue["@odata.type"] =
56 "#PowerSupplyCollection.PowerSupplyCollection";
57 asyncResp->res.jsonValue["Name"] = "Power Supply Collection";
Ed Tanousef4c65b2023-04-24 15:28:50 -070058 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
59 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId);
George Liua7210022022-10-05 15:44:11 +080060 asyncResp->res.jsonValue["Description"] =
61 "The collection of PowerSupply resource instances.";
George Liu7a2bb2c2023-04-11 10:44:33 +080062 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
63 asyncResp->res.jsonValue["Members@odata.count"] = 0;
George Liua7210022022-10-05 15:44:11 +080064
65 std::string powerPath = *validChassisPath + "/powered_by";
66 dbus::utility::getAssociationEndPoints(
67 powerPath, [asyncResp, chassisId](
68 const boost::system::error_code& ec,
69 const dbus::utility::MapperEndPoints& endpoints) {
70 if (ec)
71 {
72 if (ec.value() != EBADR)
73 {
74 BMCWEB_LOG_ERROR << "DBUS response error" << ec.value();
75 messages::internalError(asyncResp->res);
76 }
77 return;
78 }
79
80 for (const auto& endpoint : endpoints)
81 {
82 updatePowerSupplyList(asyncResp, chassisId, endpoint);
83 }
84 });
85}
86
87inline void handlePowerSupplyCollectionHead(
88 App& app, const crow::Request& req,
89 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
90 const std::string& chassisId)
91{
92 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
93 {
94 return;
95 }
96
97 redfish::chassis_utils::getValidChassisPath(
98 asyncResp, chassisId,
99 [asyncResp,
100 chassisId](const std::optional<std::string>& validChassisPath) {
101 if (!validChassisPath)
102 {
103 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
104 return;
105 }
106 asyncResp->res.addHeader(
107 boost::beast::http::field::link,
108 "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
109 });
110}
111
112inline void handlePowerSupplyCollectionGet(
113 App& app, const crow::Request& req,
114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115 const std::string& chassisId)
116{
117 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
118 {
119 return;
120 }
121
122 redfish::chassis_utils::getValidChassisPath(
123 asyncResp, chassisId,
124 std::bind_front(doPowerSupplyCollection, asyncResp, chassisId));
125}
126
127inline void requestRoutesPowerSupplyCollection(App& app)
128{
129 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
130 .privileges(redfish::privileges::headPowerSupplyCollection)
131 .methods(boost::beast::http::verb::head)(
132 std::bind_front(handlePowerSupplyCollectionHead, std::ref(app)));
133
134 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
135 .privileges(redfish::privileges::getPowerSupplyCollection)
136 .methods(boost::beast::http::verb::get)(
137 std::bind_front(handlePowerSupplyCollectionGet, std::ref(app)));
138}
139
George Liu00ef5dc2022-10-05 16:27:52 +0800140inline bool checkPowerSupplyId(const std::string& powerSupplyPath,
141 const std::string& powerSupplyId)
142{
143 std::string powerSupplyName =
144 sdbusplus::message::object_path(powerSupplyPath).filename();
145
146 return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId);
147}
148
George Liu34dfcb92022-10-05 16:47:44 +0800149inline void getValidPowerSupplyPath(
150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
151 const std::string& validChassisPath, const std::string& powerSupplyId,
152 std::function<void(const std::string& powerSupplyPath)>&& callback)
George Liu00ef5dc2022-10-05 16:27:52 +0800153{
154 std::string powerPath = validChassisPath + "/powered_by";
155 dbus::utility::getAssociationEndPoints(
156 powerPath, [asyncResp, powerSupplyId, callback{std::move(callback)}](
157 const boost::system::error_code& ec,
158 const dbus::utility::MapperEndPoints& endpoints) {
159 if (ec)
160 {
161 if (ec.value() != EBADR)
162 {
163 BMCWEB_LOG_ERROR
164 << "DBUS response error for getAssociationEndPoints"
165 << ec.value();
166 messages::internalError(asyncResp->res);
167 return;
168 }
169 messages::resourceNotFound(asyncResp->res, "PowerSupplies",
170 powerSupplyId);
171 return;
172 }
173
174 for (const auto& endpoint : endpoints)
175 {
176 if (checkPowerSupplyId(endpoint, powerSupplyId))
177 {
George Liu34dfcb92022-10-05 16:47:44 +0800178 callback(endpoint);
George Liu00ef5dc2022-10-05 16:27:52 +0800179 return;
180 }
181 }
182
183 if (!endpoints.empty())
184 {
185 BMCWEB_LOG_WARNING << "Power supply not found: "
186 << powerSupplyId;
187 messages::resourceNotFound(asyncResp->res, "PowerSupplies",
188 powerSupplyId);
189 return;
190 }
191 });
192}
193
194inline void
George Liu34dfcb92022-10-05 16:47:44 +0800195 getPowerSupplyState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
196 const std::string& service, const std::string& path)
197{
198 sdbusplus::asio::getProperty<bool>(
199 *crow::connections::systemBus, service, path,
200 "xyz.openbmc_project.Inventory.Item", "Present",
201 [asyncResp](const boost::system::error_code& ec, const bool value) {
202 if (ec)
203 {
204 if (ec.value() != EBADR)
205 {
206 BMCWEB_LOG_ERROR << "DBUS response error for State "
207 << ec.value();
208 messages::internalError(asyncResp->res);
209 }
210 return;
211 }
212
213 if (!value)
214 {
215 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
216 }
217 });
218}
219
220inline void
221 getPowerSupplyHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
222 const std::string& service, const std::string& path)
223{
224 sdbusplus::asio::getProperty<bool>(
225 *crow::connections::systemBus, service, path,
226 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
227 [asyncResp](const boost::system::error_code& ec, const bool value) {
228 if (ec)
229 {
230 if (ec.value() != EBADR)
231 {
232 BMCWEB_LOG_ERROR << "DBUS response error for Health "
233 << ec.value();
234 messages::internalError(asyncResp->res);
235 }
236 return;
237 }
238
239 if (!value)
240 {
241 asyncResp->res.jsonValue["Status"]["Health"] = "Critical";
242 }
243 });
244}
245
246inline void
George Liu00ef5dc2022-10-05 16:27:52 +0800247 doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
248 const std::string& chassisId,
249 const std::string& powerSupplyId,
250 const std::optional<std::string>& validChassisPath)
251{
252 if (!validChassisPath)
253 {
254 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
255 return;
256 }
257
258 // Get the correct Path and Service that match the input parameters
259 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
George Liu34dfcb92022-10-05 16:47:44 +0800260 [asyncResp, chassisId, powerSupplyId](
261 const std::string& powerSupplyPath) {
George Liu00ef5dc2022-10-05 16:27:52 +0800262 asyncResp->res.addHeader(
263 boost::beast::http::field::link,
264 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
265 asyncResp->res.jsonValue["@odata.type"] =
266 "#PowerSupply.v1_5_0.PowerSupply";
267 asyncResp->res.jsonValue["Name"] = "Power Supply";
268 asyncResp->res.jsonValue["Id"] = powerSupplyId;
269 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
270 "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId,
271 powerSupplyId);
George Liu34dfcb92022-10-05 16:47:44 +0800272
273 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
274 asyncResp->res.jsonValue["Status"]["Health"] = "OK";
275
276 constexpr std::array<std::string_view, 1> interfaces = {
277 "xyz.openbmc_project.Inventory.Item.PowerSupply"};
278 dbus::utility::getDbusObject(
279 powerSupplyPath, interfaces,
280 [asyncResp,
281 powerSupplyPath](const boost::system::error_code& ec,
282 const dbus::utility::MapperGetObject& object) {
283 if (ec || object.empty())
284 {
285 messages::internalError(asyncResp->res);
286 return;
287 }
288
289 getPowerSupplyState(asyncResp, object.begin()->first,
290 powerSupplyPath);
291 getPowerSupplyHealth(asyncResp, object.begin()->first,
292 powerSupplyPath);
293 });
George Liu00ef5dc2022-10-05 16:27:52 +0800294 });
295}
296
297inline void
298 handlePowerSupplyHead(App& app, const crow::Request& req,
299 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
300 const std::string& chassisId,
301 const std::string& powerSupplyId)
302{
303 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
304 {
305 return;
306 }
307
308 redfish::chassis_utils::getValidChassisPath(
309 asyncResp, chassisId,
310 [asyncResp, chassisId,
311 powerSupplyId](const std::optional<std::string>& validChassisPath) {
312 if (!validChassisPath)
313 {
314 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
315 return;
316 }
317
318 // Get the correct Path and Service that match the input parameters
319 getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
George Liu34dfcb92022-10-05 16:47:44 +0800320 [asyncResp](const std::string&) {
George Liu00ef5dc2022-10-05 16:27:52 +0800321 asyncResp->res.addHeader(
322 boost::beast::http::field::link,
323 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
324 });
325 });
326}
327
328inline void
329 handlePowerSupplyGet(App& app, const crow::Request& req,
330 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
331 const std::string& chassisId,
332 const std::string& powerSupplyId)
333{
334 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
335 {
336 return;
337 }
338
339 redfish::chassis_utils::getValidChassisPath(
340 asyncResp, chassisId,
341 std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId));
342}
343
344inline void requestRoutesPowerSupply(App& app)
345{
346 BMCWEB_ROUTE(
347 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
348 .privileges(redfish::privileges::headPowerSupply)
349 .methods(boost::beast::http::verb::head)(
350 std::bind_front(handlePowerSupplyHead, std::ref(app)));
351
352 BMCWEB_ROUTE(
353 app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
354 .privileges(redfish::privileges::getPowerSupply)
355 .methods(boost::beast::http::verb::get)(
356 std::bind_front(handlePowerSupplyGet, std::ref(app)));
357}
358
George Liua7210022022-10-05 15:44:11 +0800359} // namespace redfish