blob: 47d65ee23b50bd466dcd1f15aa6def9c3ea3ffaa [file] [log] [blame]
George Liu9516f412022-09-29 17:21:41 +08001#pragma once
2
3#include "app.hpp"
4#include "dbus_utility.hpp"
5#include "error_messages.hpp"
6#include "query.hpp"
7#include "registries/privilege_registry.hpp"
8#include "utils/chassis_utils.hpp"
9
10#include <boost/url/format.hpp>
11#include <sdbusplus/message/types.hpp>
12
13#include <functional>
14#include <memory>
15#include <optional>
16#include <string>
17#include <string_view>
18
19namespace redfish
20{
21constexpr std::array<std::string_view, 1> fanInterface = {
22 "xyz.openbmc_project.Inventory.Item.Fan"};
23
24inline void
25 updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
26 const std::string& chassisId,
27 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
28{
29 nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
30 for (const std::string& fanPath : fanPaths)
31 {
32 std::string fanName =
33 sdbusplus::message::object_path(fanPath).filename();
34 if (fanName.empty())
35 {
36 continue;
37 }
38
39 nlohmann::json item = nlohmann::json::object();
40 item["@odata.id"] = boost::urls::format(
41 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
42 fanName);
43
44 fanList.emplace_back(std::move(item));
45 }
46 asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
47}
48
49inline void getFanPaths(
50 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
51 const std::optional<std::string>& validChassisPath,
52 const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
53 fanPaths)>& callback)
54{
55 sdbusplus::message::object_path endpointPath{*validChassisPath};
56 endpointPath /= "cooled_by";
57
58 dbus::utility::getAssociatedSubTreePaths(
59 endpointPath,
60 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
61 fanInterface,
62 [asyncResp, callback](
63 const boost::system::error_code& ec,
64 const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
65 if (ec)
66 {
67 if (ec.value() != EBADR)
68 {
69 BMCWEB_LOG_ERROR
70 << "DBUS response error for getAssociatedSubTreePaths "
71 << ec.value();
72 messages::internalError(asyncResp->res);
73 }
74 return;
75 }
76 callback(subtreePaths);
77 });
78}
79
80inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
81 const std::string& chassisId,
82 const std::optional<std::string>& validChassisPath)
83{
84 if (!validChassisPath)
85 {
86 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
87 return;
88 }
89
90 asyncResp->res.addHeader(
91 boost::beast::http::field::link,
92 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
93 asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
94 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
95 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
96 asyncResp->res.jsonValue["Name"] = "Fan Collection";
97 asyncResp->res.jsonValue["Description"] =
98 "The collection of Fan resource instances " + chassisId;
99 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
100 asyncResp->res.jsonValue["Members@odata.count"] = 0;
101
102 getFanPaths(asyncResp, validChassisPath,
103 std::bind_front(updateFanList, asyncResp, chassisId));
104}
105
106inline void
107 handleFanCollectionHead(App& app, const crow::Request& req,
108 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
109 const std::string& chassisId)
110{
111 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
112 {
113 return;
114 }
115
116 redfish::chassis_utils::getValidChassisPath(
117 asyncResp, chassisId,
118 [asyncResp,
119 chassisId](const std::optional<std::string>& validChassisPath) {
120 if (!validChassisPath)
121 {
122 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
123 return;
124 }
125 asyncResp->res.addHeader(
126 boost::beast::http::field::link,
127 "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
128 });
129}
130
131inline void
132 handleFanCollectionGet(App& app, const crow::Request& req,
133 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
134 const std::string& chassisId)
135{
136 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
137 {
138 return;
139 }
140
141 redfish::chassis_utils::getValidChassisPath(
142 asyncResp, chassisId,
143 std::bind_front(doFanCollection, asyncResp, chassisId));
144}
145
146inline void requestRoutesFanCollection(App& app)
147{
148 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
149 .privileges(redfish::privileges::headFanCollection)
150 .methods(boost::beast::http::verb::head)(
151 std::bind_front(handleFanCollectionHead, std::ref(app)));
152
153 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
154 .privileges(redfish::privileges::getFanCollection)
155 .methods(boost::beast::http::verb::get)(
156 std::bind_front(handleFanCollectionGet, std::ref(app)));
157}
158
George Liue4e54232022-09-30 09:08:11 +0800159inline bool checkFanId(const std::string& fanPath, const std::string& fanId)
160{
161 std::string fanName = sdbusplus::message::object_path(fanPath).filename();
162
163 return !(fanName.empty() || fanName != fanId);
164}
165
166static inline void handleFanPath(
167 const std::string& fanId,
168 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
169 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths,
170 const std::function<void(const std::string& fanPath,
171 const std::string& service)>& callback)
172{
173 for (const auto& fanPath : fanPaths)
174 {
175 if (!checkFanId(fanPath, fanId))
176 {
177 continue;
178 }
179 dbus::utility::getDbusObject(
180 fanPath, fanInterface,
181 [fanPath, asyncResp,
182 callback](const boost::system::error_code& ec,
183 const dbus::utility::MapperGetObject& object) {
184 if (ec || object.empty())
185 {
186 BMCWEB_LOG_ERROR << "DBUS response error on getDbusObject "
187 << ec.value();
188 messages::internalError(asyncResp->res);
189 return;
190 }
191 callback(fanPath, object.begin()->first);
192 });
193
194 return;
195 }
196 BMCWEB_LOG_WARNING << "Fan not found " << fanId;
197 messages::resourceNotFound(asyncResp->res, "Fan", fanId);
198}
199
200inline void getValidFanPath(
201 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
202 const std::string& validChassisPath, const std::string& fanId,
203 const std::function<void(const std::string& fanPath,
204 const std::string& service)>& callback)
205{
206 getFanPaths(
207 asyncResp, validChassisPath,
208 [fanId, asyncResp, callback](
209 const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) {
210 handleFanPath(fanId, asyncResp, fanPaths, callback);
211 });
212}
213
214inline void addFanCommonProperties(crow::Response& resp,
215 const std::string& chassisId,
216 const std::string& fanId)
217{
218 resp.addHeader(boost::beast::http::field::link,
219 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
220 resp.jsonValue["@odata.type"] = "#Fan.v1_3_0.Fan";
221 resp.jsonValue["Name"] = "Fan";
222 resp.jsonValue["Id"] = fanId;
223 resp.jsonValue["@odata.id"] = boost::urls::format(
224 "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId, fanId);
225}
226
227inline void
228 afterGetValidFanPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
229 const std::string& chassisId, const std::string& fanId,
230 const std::string& fanPath, const std::string& service)
231{
232 BMCWEB_LOG_DEBUG << "fanPath = " << fanPath << " service = " << service;
233 addFanCommonProperties(asyncResp->res, chassisId, fanId);
234}
235
236inline void doFanGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
237 const std::string& chassisId, const std::string& fanId,
238 const std::optional<std::string>& validChassisPath)
239{
240 if (!validChassisPath)
241 {
242 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
243 return;
244 }
245
246 getValidFanPath(
247 asyncResp, *validChassisPath, fanId,
248 std::bind_front(afterGetValidFanPath, asyncResp, chassisId, fanId));
249}
250
251inline void handleFanHead(App& app, const crow::Request& req,
252 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
253 const std::string& chassisId,
254 const std::string& fanId)
255{
256 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
257 {
258 return;
259 }
260
261 redfish::chassis_utils::getValidChassisPath(
262 asyncResp, chassisId,
263 [asyncResp, chassisId,
264 fanId](const std::optional<std::string>& validChassisPath) {
265 if (!validChassisPath)
266 {
267 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
268 return;
269 }
270 getValidFanPath(asyncResp, *validChassisPath, fanId,
271 [asyncResp](const std::string&, const std::string&) {
272 asyncResp->res.addHeader(
273 boost::beast::http::field::link,
274 "</redfish/v1/JsonSchemas/Fan/Fan.json>; rel=describedby");
275 });
276 });
277}
278
279inline void handleFanGet(App& app, const crow::Request& req,
280 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
281 const std::string& chassisId, const std::string& fanId)
282{
283 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
284 {
285 return;
286 }
287
288 redfish::chassis_utils::getValidChassisPath(
289 asyncResp, chassisId,
290 std::bind_front(doFanGet, asyncResp, chassisId, fanId));
291}
292
293inline void requestRoutesFan(App& app)
294{
295 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
296 .privileges(redfish::privileges::headFan)
297 .methods(boost::beast::http::verb::head)(
298 std::bind_front(handleFanHead, std::ref(app)));
299
300 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/<str>/")
301 .privileges(redfish::privileges::getFan)
302 .methods(boost::beast::http::verb::get)(
303 std::bind_front(handleFanGet, std::ref(app)));
304}
305
George Liu9516f412022-09-29 17:21:41 +0800306} // namespace redfish