xref: /openbmc/bmcweb/features/redfish/lib/fan.hpp (revision 9516f41f9295170ad5d59ded36584d22776417d5)
1 #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 
19 namespace redfish
20 {
21 constexpr std::array<std::string_view, 1> fanInterface = {
22     "xyz.openbmc_project.Inventory.Item.Fan"};
23 
24 inline 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 
49 inline 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 
80 inline 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 
106 inline 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 
131 inline 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 
146 inline 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 
159 } // namespace redfish
160