1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2019 Intel Corporation
4 #pragma once
5
6 #include "bmcweb_config.h"
7
8 #include "app.hpp"
9 #include "async_resp.hpp"
10 #include "dbus_utility.hpp"
11 #include "error_messages.hpp"
12 #include "generated/enums/resource.hpp"
13 #include "http_request.hpp"
14 #include "logging.hpp"
15 #include "query.hpp"
16 #include "registries/privilege_registry.hpp"
17 #include "storage_chassis.hpp"
18 #include "utils/collection.hpp"
19
20 #include <boost/beast/http/verb.hpp>
21 #include <boost/system/error_code.hpp>
22 #include <boost/url/format.hpp>
23 #include <sdbusplus/message/native_types.hpp>
24 #include <sdbusplus/unpack_properties.hpp>
25
26 #include <algorithm>
27 #include <array>
28 #include <format>
29 #include <functional>
30 #include <memory>
31 #include <string>
32 #include <string_view>
33 #include <utility>
34
35 namespace redfish
36 {
37
handleSystemsStorageCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)38 inline void handleSystemsStorageCollectionGet(
39 App& app, const crow::Request& req,
40 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
41 const std::string& systemName)
42 {
43 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
44 {
45 return;
46 }
47 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
48 {
49 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
50 systemName);
51 return;
52 }
53
54 asyncResp->res.jsonValue["@odata.type"] =
55 "#StorageCollection.StorageCollection";
56 asyncResp->res.jsonValue["@odata.id"] = std::format(
57 "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME);
58 asyncResp->res.jsonValue["Name"] = "Storage Collection";
59
60 constexpr std::array<std::string_view, 1> interface{
61 "xyz.openbmc_project.Inventory.Item.Storage"};
62 collection_util::getCollectionMembers(
63 asyncResp,
64 boost::urls::format("/redfish/v1/Systems/{}/Storage",
65 BMCWEB_REDFISH_SYSTEM_URI_NAME),
66 interface, "/xyz/openbmc_project/inventory");
67 }
68
handleStorageCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)69 inline void handleStorageCollectionGet(
70 App& app, const crow::Request& req,
71 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
72 {
73 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
74 {
75 return;
76 }
77 asyncResp->res.jsonValue["@odata.type"] =
78 "#StorageCollection.StorageCollection";
79 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Storage";
80 asyncResp->res.jsonValue["Name"] = "Storage Collection";
81 constexpr std::array<std::string_view, 1> interface{
82 "xyz.openbmc_project.Inventory.Item.Storage"};
83 collection_util::getCollectionMembers(
84 asyncResp, boost::urls::format("/redfish/v1/Storage"), interface,
85 "/xyz/openbmc_project/inventory");
86 }
87
afterChassisDriveCollectionSubtree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & driveList)88 inline void afterChassisDriveCollectionSubtree(
89 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
90 const boost::system::error_code& ec,
91 const dbus::utility::MapperGetSubTreePathsResponse& driveList)
92 {
93 if (ec)
94 {
95 BMCWEB_LOG_ERROR("Drive mapper call error");
96 messages::internalError(asyncResp->res);
97 return;
98 }
99
100 nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
101 driveArray = nlohmann::json::array();
102 auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
103 count = 0;
104
105 for (const std::string& drive : driveList)
106 {
107 sdbusplus::message::object_path object(drive);
108 if (object.filename().empty())
109 {
110 BMCWEB_LOG_ERROR("Failed to find filename in {}", drive);
111 return;
112 }
113
114 nlohmann::json::object_t driveJson;
115 driveJson["@odata.id"] = boost::urls::format(
116 "/redfish/v1/Systems/{}/Storage/1/Drives/{}",
117 BMCWEB_REDFISH_SYSTEM_URI_NAME, object.filename());
118 driveArray.emplace_back(std::move(driveJson));
119 }
120
121 count = driveArray.size();
122 }
getDrives(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)123 inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
124 {
125 const std::array<std::string_view, 1> interfaces = {
126 "xyz.openbmc_project.Inventory.Item.Drive"};
127 dbus::utility::getSubTreePaths(
128 "/xyz/openbmc_project/inventory", 0, interfaces,
129 std::bind_front(afterChassisDriveCollectionSubtree, asyncResp));
130 }
131
afterSystemsStorageGetSubtree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & storageId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)132 inline void afterSystemsStorageGetSubtree(
133 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
134 const std::string& storageId, const boost::system::error_code& ec,
135 const dbus::utility::MapperGetSubTreeResponse& subtree)
136 {
137 if (ec)
138 {
139 BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
140 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
141 storageId);
142 return;
143 }
144 auto storage = std::ranges::find_if(
145 subtree,
146 [&storageId](const std::pair<std::string,
147 dbus::utility::MapperServiceMap>& object) {
148 return sdbusplus::message::object_path(object.first).filename() ==
149 storageId;
150 });
151 if (storage == subtree.end())
152 {
153 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
154 storageId);
155 return;
156 }
157
158 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
159 asyncResp->res.jsonValue["@odata.id"] =
160 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
161 BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
162 asyncResp->res.jsonValue["Name"] = "Storage";
163 asyncResp->res.jsonValue["Id"] = storageId;
164 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
165
166 getDrives(asyncResp);
167 asyncResp->res.jsonValue["Controllers"]["@odata.id"] =
168 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}/Controllers",
169 BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
170 }
171
handleSystemsStorageGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & storageId)172 inline void handleSystemsStorageGet(
173 App& app, const crow::Request& req,
174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
175 const std::string& systemName, const std::string& storageId)
176 {
177 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
178 {
179 return;
180 }
181 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
182 {
183 // Option currently returns no systems. TBD
184 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
185 systemName);
186 return;
187 }
188
189 constexpr std::array<std::string_view, 1> interfaces = {
190 "xyz.openbmc_project.Inventory.Item.Storage"};
191 dbus::utility::getSubTree(
192 "/xyz/openbmc_project/inventory", 0, interfaces,
193 std::bind_front(afterSystemsStorageGetSubtree, asyncResp, storageId));
194 }
195
afterSubtree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & storageId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)196 inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
197 const std::string& storageId,
198 const boost::system::error_code& ec,
199 const dbus::utility::MapperGetSubTreeResponse& subtree)
200 {
201 if (ec)
202 {
203 BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
204 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
205 storageId);
206 return;
207 }
208 auto storage = std::ranges::find_if(
209 subtree,
210 [&storageId](const std::pair<std::string,
211 dbus::utility::MapperServiceMap>& object) {
212 return sdbusplus::message::object_path(object.first).filename() ==
213 storageId;
214 });
215 if (storage == subtree.end())
216 {
217 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
218 storageId);
219 return;
220 }
221
222 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
223 asyncResp->res.jsonValue["@odata.id"] =
224 boost::urls::format("/redfish/v1/Storage/{}", storageId);
225 asyncResp->res.jsonValue["Name"] = "Storage";
226 asyncResp->res.jsonValue["Id"] = storageId;
227 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
228
229 // Storage subsystem to Storage link.
230 nlohmann::json::array_t storageServices;
231 nlohmann::json::object_t storageService;
232 storageService["@odata.id"] =
233 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
234 BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
235 storageServices.emplace_back(storageService);
236 asyncResp->res.jsonValue["Links"]["StorageServices"] =
237 std::move(storageServices);
238 asyncResp->res.jsonValue["Links"]["StorageServices@odata.count"] = 1;
239 }
240
handleStorageGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & storageId)241 inline void handleStorageGet(
242 App& app, const crow::Request& req,
243 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
244 const std::string& storageId)
245 {
246 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
247 {
248 BMCWEB_LOG_DEBUG("requestRoutesStorage setUpRedfishRoute failed");
249 return;
250 }
251
252 constexpr std::array<std::string_view, 1> interfaces = {
253 "xyz.openbmc_project.Inventory.Item.Storage"};
254 dbus::utility::getSubTree(
255 "/xyz/openbmc_project/inventory", 0, interfaces,
256 std::bind_front(afterSubtree, asyncResp, storageId));
257 }
258
handleSystemsStorageDriveGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & driveId)259 inline void handleSystemsStorageDriveGet(
260 App& app, const crow::Request& req,
261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262 const std::string& systemName, const std::string& driveId)
263 {
264 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
265 {
266 return;
267 }
268 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
269 {
270 // Option currently returns no systems. TBD
271 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
272 systemName);
273 return;
274 }
275
276 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
277 {
278 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
279 systemName);
280 return;
281 }
282
283 constexpr std::array<std::string_view, 1> interfaces = {
284 "xyz.openbmc_project.Inventory.Item.Drive"};
285 dbus::utility::getSubTree(
286 "/xyz/openbmc_project/inventory", 0, interfaces,
287 std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
288 driveId));
289 }
290
requestRoutesStorage(App & app)291 inline void requestRoutesStorage(App& app)
292 {
293 BMCWEB_ROUTE(app, "/redfish/v1/Storage/")
294 .privileges(redfish::privileges::getStorageCollection)
295 .methods(boost::beast::http::verb::get)(
296 std::bind_front(handleStorageCollectionGet, std::ref(app)));
297
298 BMCWEB_ROUTE(app, "/redfish/v1/Storage/<str>/")
299 .privileges(redfish::privileges::getStorage)
300 .methods(boost::beast::http::verb::get)(
301 std::bind_front(handleStorageGet, std::ref(app)));
302
303 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
304 .privileges(redfish::privileges::getStorageCollection)
305 .methods(boost::beast::http::verb::get)(
306 std::bind_front(handleSystemsStorageCollectionGet, std::ref(app)));
307
308 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/<str>/")
309 .privileges(redfish::privileges::getStorage)
310 .methods(boost::beast::http::verb::get)(
311 std::bind_front(handleSystemsStorageGet, std::ref(app)));
312
313 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
314 .privileges(redfish::privileges::getDrive)
315 .methods(boost::beast::http::verb::get)(
316 std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
317 }
318
319 } // namespace redfish
320