xref: /openbmc/bmcweb/features/redfish/lib/fan.hpp (revision 9516f41f9295170ad5d59ded36584d22776417d5)
1*9516f41fSGeorge Liu #pragma once
2*9516f41fSGeorge Liu 
3*9516f41fSGeorge Liu #include "app.hpp"
4*9516f41fSGeorge Liu #include "dbus_utility.hpp"
5*9516f41fSGeorge Liu #include "error_messages.hpp"
6*9516f41fSGeorge Liu #include "query.hpp"
7*9516f41fSGeorge Liu #include "registries/privilege_registry.hpp"
8*9516f41fSGeorge Liu #include "utils/chassis_utils.hpp"
9*9516f41fSGeorge Liu 
10*9516f41fSGeorge Liu #include <boost/url/format.hpp>
11*9516f41fSGeorge Liu #include <sdbusplus/message/types.hpp>
12*9516f41fSGeorge Liu 
13*9516f41fSGeorge Liu #include <functional>
14*9516f41fSGeorge Liu #include <memory>
15*9516f41fSGeorge Liu #include <optional>
16*9516f41fSGeorge Liu #include <string>
17*9516f41fSGeorge Liu #include <string_view>
18*9516f41fSGeorge Liu 
19*9516f41fSGeorge Liu namespace redfish
20*9516f41fSGeorge Liu {
21*9516f41fSGeorge Liu constexpr std::array<std::string_view, 1> fanInterface = {
22*9516f41fSGeorge Liu     "xyz.openbmc_project.Inventory.Item.Fan"};
23*9516f41fSGeorge Liu 
24*9516f41fSGeorge Liu inline void
25*9516f41fSGeorge Liu     updateFanList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
26*9516f41fSGeorge Liu                   const std::string& chassisId,
27*9516f41fSGeorge Liu                   const dbus::utility::MapperGetSubTreePathsResponse& fanPaths)
28*9516f41fSGeorge Liu {
29*9516f41fSGeorge Liu     nlohmann::json& fanList = asyncResp->res.jsonValue["Members"];
30*9516f41fSGeorge Liu     for (const std::string& fanPath : fanPaths)
31*9516f41fSGeorge Liu     {
32*9516f41fSGeorge Liu         std::string fanName =
33*9516f41fSGeorge Liu             sdbusplus::message::object_path(fanPath).filename();
34*9516f41fSGeorge Liu         if (fanName.empty())
35*9516f41fSGeorge Liu         {
36*9516f41fSGeorge Liu             continue;
37*9516f41fSGeorge Liu         }
38*9516f41fSGeorge Liu 
39*9516f41fSGeorge Liu         nlohmann::json item = nlohmann::json::object();
40*9516f41fSGeorge Liu         item["@odata.id"] = boost::urls::format(
41*9516f41fSGeorge Liu             "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans/{}", chassisId,
42*9516f41fSGeorge Liu             fanName);
43*9516f41fSGeorge Liu 
44*9516f41fSGeorge Liu         fanList.emplace_back(std::move(item));
45*9516f41fSGeorge Liu     }
46*9516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = fanList.size();
47*9516f41fSGeorge Liu }
48*9516f41fSGeorge Liu 
49*9516f41fSGeorge Liu inline void getFanPaths(
50*9516f41fSGeorge Liu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
51*9516f41fSGeorge Liu     const std::optional<std::string>& validChassisPath,
52*9516f41fSGeorge Liu     const std::function<void(const dbus::utility::MapperGetSubTreePathsResponse&
53*9516f41fSGeorge Liu                                  fanPaths)>& callback)
54*9516f41fSGeorge Liu {
55*9516f41fSGeorge Liu     sdbusplus::message::object_path endpointPath{*validChassisPath};
56*9516f41fSGeorge Liu     endpointPath /= "cooled_by";
57*9516f41fSGeorge Liu 
58*9516f41fSGeorge Liu     dbus::utility::getAssociatedSubTreePaths(
59*9516f41fSGeorge Liu         endpointPath,
60*9516f41fSGeorge Liu         sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0,
61*9516f41fSGeorge Liu         fanInterface,
62*9516f41fSGeorge Liu         [asyncResp, callback](
63*9516f41fSGeorge Liu             const boost::system::error_code& ec,
64*9516f41fSGeorge Liu             const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) {
65*9516f41fSGeorge Liu         if (ec)
66*9516f41fSGeorge Liu         {
67*9516f41fSGeorge Liu             if (ec.value() != EBADR)
68*9516f41fSGeorge Liu             {
69*9516f41fSGeorge Liu                 BMCWEB_LOG_ERROR
70*9516f41fSGeorge Liu                     << "DBUS response error for getAssociatedSubTreePaths "
71*9516f41fSGeorge Liu                     << ec.value();
72*9516f41fSGeorge Liu                 messages::internalError(asyncResp->res);
73*9516f41fSGeorge Liu             }
74*9516f41fSGeorge Liu             return;
75*9516f41fSGeorge Liu         }
76*9516f41fSGeorge Liu         callback(subtreePaths);
77*9516f41fSGeorge Liu         });
78*9516f41fSGeorge Liu }
79*9516f41fSGeorge Liu 
80*9516f41fSGeorge Liu inline void doFanCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
81*9516f41fSGeorge Liu                             const std::string& chassisId,
82*9516f41fSGeorge Liu                             const std::optional<std::string>& validChassisPath)
83*9516f41fSGeorge Liu {
84*9516f41fSGeorge Liu     if (!validChassisPath)
85*9516f41fSGeorge Liu     {
86*9516f41fSGeorge Liu         messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
87*9516f41fSGeorge Liu         return;
88*9516f41fSGeorge Liu     }
89*9516f41fSGeorge Liu 
90*9516f41fSGeorge Liu     asyncResp->res.addHeader(
91*9516f41fSGeorge Liu         boost::beast::http::field::link,
92*9516f41fSGeorge Liu         "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
93*9516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.type"] = "#FanCollection.FanCollection";
94*9516f41fSGeorge Liu     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
95*9516f41fSGeorge Liu         "/redfish/v1/Chassis/{}/ThermalSubsystem/Fans", chassisId);
96*9516f41fSGeorge Liu     asyncResp->res.jsonValue["Name"] = "Fan Collection";
97*9516f41fSGeorge Liu     asyncResp->res.jsonValue["Description"] =
98*9516f41fSGeorge Liu         "The collection of Fan resource instances " + chassisId;
99*9516f41fSGeorge Liu     asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
100*9516f41fSGeorge Liu     asyncResp->res.jsonValue["Members@odata.count"] = 0;
101*9516f41fSGeorge Liu 
102*9516f41fSGeorge Liu     getFanPaths(asyncResp, validChassisPath,
103*9516f41fSGeorge Liu                 std::bind_front(updateFanList, asyncResp, chassisId));
104*9516f41fSGeorge Liu }
105*9516f41fSGeorge Liu 
106*9516f41fSGeorge Liu inline void
107*9516f41fSGeorge Liu     handleFanCollectionHead(App& app, const crow::Request& req,
108*9516f41fSGeorge Liu                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
109*9516f41fSGeorge Liu                             const std::string& chassisId)
110*9516f41fSGeorge Liu {
111*9516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
112*9516f41fSGeorge Liu     {
113*9516f41fSGeorge Liu         return;
114*9516f41fSGeorge Liu     }
115*9516f41fSGeorge Liu 
116*9516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
117*9516f41fSGeorge Liu         asyncResp, chassisId,
118*9516f41fSGeorge Liu         [asyncResp,
119*9516f41fSGeorge Liu          chassisId](const std::optional<std::string>& validChassisPath) {
120*9516f41fSGeorge Liu         if (!validChassisPath)
121*9516f41fSGeorge Liu         {
122*9516f41fSGeorge Liu             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
123*9516f41fSGeorge Liu             return;
124*9516f41fSGeorge Liu         }
125*9516f41fSGeorge Liu         asyncResp->res.addHeader(
126*9516f41fSGeorge Liu             boost::beast::http::field::link,
127*9516f41fSGeorge Liu             "</redfish/v1/JsonSchemas/FanCollection/FanCollection.json>; rel=describedby");
128*9516f41fSGeorge Liu         });
129*9516f41fSGeorge Liu }
130*9516f41fSGeorge Liu 
131*9516f41fSGeorge Liu inline void
132*9516f41fSGeorge Liu     handleFanCollectionGet(App& app, const crow::Request& req,
133*9516f41fSGeorge Liu                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
134*9516f41fSGeorge Liu                            const std::string& chassisId)
135*9516f41fSGeorge Liu {
136*9516f41fSGeorge Liu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
137*9516f41fSGeorge Liu     {
138*9516f41fSGeorge Liu         return;
139*9516f41fSGeorge Liu     }
140*9516f41fSGeorge Liu 
141*9516f41fSGeorge Liu     redfish::chassis_utils::getValidChassisPath(
142*9516f41fSGeorge Liu         asyncResp, chassisId,
143*9516f41fSGeorge Liu         std::bind_front(doFanCollection, asyncResp, chassisId));
144*9516f41fSGeorge Liu }
145*9516f41fSGeorge Liu 
146*9516f41fSGeorge Liu inline void requestRoutesFanCollection(App& app)
147*9516f41fSGeorge Liu {
148*9516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
149*9516f41fSGeorge Liu         .privileges(redfish::privileges::headFanCollection)
150*9516f41fSGeorge Liu         .methods(boost::beast::http::verb::head)(
151*9516f41fSGeorge Liu             std::bind_front(handleFanCollectionHead, std::ref(app)));
152*9516f41fSGeorge Liu 
153*9516f41fSGeorge Liu     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ThermalSubsystem/Fans/")
154*9516f41fSGeorge Liu         .privileges(redfish::privileges::getFanCollection)
155*9516f41fSGeorge Liu         .methods(boost::beast::http::verb::get)(
156*9516f41fSGeorge Liu             std::bind_front(handleFanCollectionGet, std::ref(app)));
157*9516f41fSGeorge Liu }
158*9516f41fSGeorge Liu 
159*9516f41fSGeorge Liu } // namespace redfish
160