xref: /openbmc/bmcweb/redfish-core/lib/storage.hpp (revision 7bf29ab37ad696aed3f0580ef0f6cb90864c89ee)
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