1 #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 
9 #include <boost/url/format.hpp>
10 
11 #include <memory>
12 #include <optional>
13 #include <string>
14 
15 namespace redfish
16 {
17 
18 inline void
19     updatePowerSupplyList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
20                           const std::string& chassisId,
21                           const std::string& powerSupplyPath)
22 {
23     std::string powerSupplyName =
24         sdbusplus::message::object_path(powerSupplyPath).filename();
25     if (powerSupplyName.empty())
26     {
27         return;
28     }
29 
30     nlohmann::json item = nlohmann::json::object();
31     item["@odata.id"] = boost::urls::format(
32         "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId,
33         powerSupplyName);
34 
35     nlohmann::json& powerSupplyList = asyncResp->res.jsonValue["Members"];
36     powerSupplyList.emplace_back(std::move(item));
37     asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size();
38 }
39 
40 inline void
41     doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
42                             const std::string& chassisId,
43                             const std::optional<std::string>& validChassisPath)
44 {
45     if (!validChassisPath)
46     {
47         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
48         return;
49     }
50 
51     asyncResp->res.addHeader(
52         boost::beast::http::field::link,
53         "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
54     asyncResp->res.jsonValue["@odata.type"] =
55         "#PowerSupplyCollection.PowerSupplyCollection";
56     asyncResp->res.jsonValue["Name"] = "Power Supply Collection";
57     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
58         "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId);
59     asyncResp->res.jsonValue["Description"] =
60         "The collection of PowerSupply resource instances.";
61     asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
62     asyncResp->res.jsonValue["Members@odata.count"] = 0;
63 
64     std::string powerPath = *validChassisPath + "/powered_by";
65     dbus::utility::getAssociationEndPoints(
66         powerPath, [asyncResp, chassisId](
67                        const boost::system::error_code& ec,
68                        const dbus::utility::MapperEndPoints& endpoints) {
69             if (ec)
70             {
71                 if (ec.value() != EBADR)
72                 {
73                     BMCWEB_LOG_ERROR << "DBUS response error" << ec.value();
74                     messages::internalError(asyncResp->res);
75                 }
76                 return;
77             }
78 
79             for (const auto& endpoint : endpoints)
80             {
81                 updatePowerSupplyList(asyncResp, chassisId, endpoint);
82             }
83         });
84 }
85 
86 inline void handlePowerSupplyCollectionHead(
87     App& app, const crow::Request& req,
88     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
89     const std::string& chassisId)
90 {
91     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
92     {
93         return;
94     }
95 
96     redfish::chassis_utils::getValidChassisPath(
97         asyncResp, chassisId,
98         [asyncResp,
99          chassisId](const std::optional<std::string>& validChassisPath) {
100         if (!validChassisPath)
101         {
102             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
103             return;
104         }
105         asyncResp->res.addHeader(
106             boost::beast::http::field::link,
107             "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby");
108         });
109 }
110 
111 inline void handlePowerSupplyCollectionGet(
112     App& app, const crow::Request& req,
113     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114     const std::string& chassisId)
115 {
116     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
117     {
118         return;
119     }
120 
121     redfish::chassis_utils::getValidChassisPath(
122         asyncResp, chassisId,
123         std::bind_front(doPowerSupplyCollection, asyncResp, chassisId));
124 }
125 
126 inline void requestRoutesPowerSupplyCollection(App& app)
127 {
128     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
129         .privileges(redfish::privileges::headPowerSupplyCollection)
130         .methods(boost::beast::http::verb::head)(
131             std::bind_front(handlePowerSupplyCollectionHead, std::ref(app)));
132 
133     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/")
134         .privileges(redfish::privileges::getPowerSupplyCollection)
135         .methods(boost::beast::http::verb::get)(
136             std::bind_front(handlePowerSupplyCollectionGet, std::ref(app)));
137 }
138 
139 inline bool checkPowerSupplyId(const std::string& powerSupplyPath,
140                                const std::string& powerSupplyId)
141 {
142     std::string powerSupplyName =
143         sdbusplus::message::object_path(powerSupplyPath).filename();
144 
145     return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId);
146 }
147 
148 inline void
149     getValidPowerSupplyPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
150                             const std::string& validChassisPath,
151                             const std::string& powerSupplyId,
152                             std::function<void()>&& callback)
153 {
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                 {
178                     callback();
179                     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 
194 inline void
195     doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
196                      const std::string& chassisId,
197                      const std::string& powerSupplyId,
198                      const std::optional<std::string>& validChassisPath)
199 {
200     if (!validChassisPath)
201     {
202         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
203         return;
204     }
205 
206     // Get the correct Path and Service that match the input parameters
207     getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
208                             [asyncResp, chassisId, powerSupplyId]() {
209         asyncResp->res.addHeader(
210             boost::beast::http::field::link,
211             "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
212         asyncResp->res.jsonValue["@odata.type"] =
213             "#PowerSupply.v1_5_0.PowerSupply";
214         asyncResp->res.jsonValue["Name"] = "Power Supply";
215         asyncResp->res.jsonValue["Id"] = powerSupplyId;
216         asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
217             "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId,
218             powerSupplyId);
219     });
220 }
221 
222 inline void
223     handlePowerSupplyHead(App& app, const crow::Request& req,
224                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
225                           const std::string& chassisId,
226                           const std::string& powerSupplyId)
227 {
228     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
229     {
230         return;
231     }
232 
233     redfish::chassis_utils::getValidChassisPath(
234         asyncResp, chassisId,
235         [asyncResp, chassisId,
236          powerSupplyId](const std::optional<std::string>& validChassisPath) {
237         if (!validChassisPath)
238         {
239             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
240             return;
241         }
242 
243         // Get the correct Path and Service that match the input parameters
244         getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId,
245                                 [asyncResp]() {
246             asyncResp->res.addHeader(
247                 boost::beast::http::field::link,
248                 "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby");
249         });
250         });
251 }
252 
253 inline void
254     handlePowerSupplyGet(App& app, const crow::Request& req,
255                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
256                          const std::string& chassisId,
257                          const std::string& powerSupplyId)
258 {
259     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
260     {
261         return;
262     }
263 
264     redfish::chassis_utils::getValidChassisPath(
265         asyncResp, chassisId,
266         std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId));
267 }
268 
269 inline void requestRoutesPowerSupply(App& app)
270 {
271     BMCWEB_ROUTE(
272         app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
273         .privileges(redfish::privileges::headPowerSupply)
274         .methods(boost::beast::http::verb::head)(
275             std::bind_front(handlePowerSupplyHead, std::ref(app)));
276 
277     BMCWEB_ROUTE(
278         app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
279         .privileges(redfish::privileges::getPowerSupply)
280         .methods(boost::beast::http::verb::get)(
281             std::bind_front(handlePowerSupplyGet, std::ref(app)));
282 }
283 
284 } // namespace redfish
285