blob: adcb7880a64470fd27a5d230fc7a2d49bfe0509d [file] [log] [blame]
Nikhil Potadea25aecc2019-08-23 16:35:26 -07001/*
Ed Tanous6be832e2024-09-10 11:44:48 -07002Copyright (c) 2019 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
Nikhil Potadea25aecc2019-08-23 16:35:26 -070015*/
16#pragma once
17
Willy Tu13451e32023-05-24 16:08:18 -070018#include "bmcweb_config.h"
19
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080020#include "app.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080021#include "dbus_utility.hpp"
John Edward Broadbente5029d82022-06-08 14:35:21 -070022#include "generated/enums/drive.hpp"
George Liudde9bc12023-02-22 09:35:51 +080023#include "generated/enums/protocol.hpp"
Ed Tanous539d8c62024-06-19 14:38:27 -070024#include "generated/enums/resource.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080025#include "human_sort.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080026#include "query.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080027#include "redfish_util.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080028#include "registries/privilege_registry.hpp"
Willy Tu5e577bc2022-07-26 00:41:55 +000029#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080030#include "utils/dbus_utils.hpp"
James Feist2ad9c2f2019-10-29 16:26:48 -070031
George Liue99073f2022-12-09 11:06:16 +080032#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070033#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070034#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020035#include <sdbusplus/unpack_properties.hpp>
Nikhil Potadea25aecc2019-08-23 16:35:26 -070036
George Liu7a1dbc42022-12-07 16:03:22 +080037#include <array>
Ed Tanous3544d2a2023-08-06 18:12:20 -070038#include <ranges>
George Liu7a1dbc42022-12-07 16:03:22 +080039#include <string_view>
40
Nikhil Potadea25aecc2019-08-23 16:35:26 -070041namespace redfish
42{
Ed Tanous36d52332023-06-09 13:18:40 -070043
44inline void handleSystemsStorageCollectionGet(
45 App& app, const crow::Request& req,
46 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
47 const std::string& systemName)
48{
49 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
50 {
51 return;
52 }
Ed Tanous253f11b2024-05-16 09:38:31 -070053 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
Ed Tanous36d52332023-06-09 13:18:40 -070054 {
55 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
56 systemName);
57 return;
58 }
59
60 asyncResp->res.jsonValue["@odata.type"] =
61 "#StorageCollection.StorageCollection";
Ed Tanous253f11b2024-05-16 09:38:31 -070062 asyncResp->res.jsonValue["@odata.id"] = std::format(
63 "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME);
Ed Tanous36d52332023-06-09 13:18:40 -070064 asyncResp->res.jsonValue["Name"] = "Storage Collection";
Willy Tu5e577bc2022-07-26 00:41:55 +000065
Patrick Williams5a39f772023-10-20 11:20:21 -050066 constexpr std::array<std::string_view, 1> interface{
67 "xyz.openbmc_project.Inventory.Item.Storage"};
Willy Tu5e577bc2022-07-26 00:41:55 +000068 collection_util::getCollectionMembers(
Ed Tanous253f11b2024-05-16 09:38:31 -070069 asyncResp,
70 boost::urls::format("/redfish/v1/Systems/{}/Storage",
71 BMCWEB_REDFISH_SYSTEM_URI_NAME),
Lakshmi Yadlapati36b5f1e2023-09-26 23:53:28 -050072 interface, "/xyz/openbmc_project/inventory");
Willy Tu5e577bc2022-07-26 00:41:55 +000073}
74
75inline void handleStorageCollectionGet(
76 App& app, const crow::Request& req,
77 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
78{
79 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
80 {
81 return;
82 }
83 asyncResp->res.jsonValue["@odata.type"] =
84 "#StorageCollection.StorageCollection";
85 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Storage";
86 asyncResp->res.jsonValue["Name"] = "Storage Collection";
Patrick Williams5a39f772023-10-20 11:20:21 -050087 constexpr std::array<std::string_view, 1> interface{
88 "xyz.openbmc_project.Inventory.Item.Storage"};
Willy Tu5e577bc2022-07-26 00:41:55 +000089 collection_util::getCollectionMembers(
Lakshmi Yadlapati36b5f1e2023-09-26 23:53:28 -050090 asyncResp, boost::urls::format("/redfish/v1/Storage"), interface,
91 "/xyz/openbmc_project/inventory");
Ed Tanous36d52332023-06-09 13:18:40 -070092}
93
John Edward Broadbent7e860f12021-04-08 15:57:16 -070094inline void requestRoutesStorageCollection(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070095{
Ed Tanous22d268c2022-05-19 09:39:07 -070096 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
Ed Tanoused398212021-06-09 17:05:54 -070097 .privileges(redfish::privileges::getStorageCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070098 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -070099 std::bind_front(handleSystemsStorageCollectionGet, std::ref(app)));
Willy Tu5e577bc2022-07-26 00:41:55 +0000100 BMCWEB_ROUTE(app, "/redfish/v1/Storage/")
101 .privileges(redfish::privileges::getStorageCollection)
102 .methods(boost::beast::http::verb::get)(
103 std::bind_front(handleStorageCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700104}
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700105
Ed Tanous36d52332023-06-09 13:18:40 -0700106inline void afterChassisDriveCollectionSubtree(
107 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous36d52332023-06-09 13:18:40 -0700108 const boost::system::error_code& ec,
109 const dbus::utility::MapperGetSubTreePathsResponse& driveList)
110{
111 if (ec)
112 {
Ed Tanous62598e32023-07-17 17:06:25 -0700113 BMCWEB_LOG_ERROR("Drive mapper call error");
Ed Tanous36d52332023-06-09 13:18:40 -0700114 messages::internalError(asyncResp->res);
115 return;
116 }
117
118 nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
119 driveArray = nlohmann::json::array();
120 auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
121 count = 0;
122
Ed Tanous36d52332023-06-09 13:18:40 -0700123 for (const std::string& drive : driveList)
124 {
125 sdbusplus::message::object_path object(drive);
126 if (object.filename().empty())
127 {
Ed Tanous62598e32023-07-17 17:06:25 -0700128 BMCWEB_LOG_ERROR("Failed to find filename in {}", drive);
Ed Tanous36d52332023-06-09 13:18:40 -0700129 return;
130 }
131
132 nlohmann::json::object_t driveJson;
133 driveJson["@odata.id"] = boost::urls::format(
Ed Tanous253f11b2024-05-16 09:38:31 -0700134 "/redfish/v1/Systems/{}/Storage/1/Drives/{}",
135 BMCWEB_REDFISH_SYSTEM_URI_NAME, object.filename());
Ed Tanous36d52332023-06-09 13:18:40 -0700136 driveArray.emplace_back(std::move(driveJson));
137 }
138
139 count = driveArray.size();
140}
Gunnar Mills7ac13cc2024-04-01 16:05:21 -0500141inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Willy Tua85afbe2021-12-28 14:43:47 -0800142{
George Liu7a1dbc42022-12-07 16:03:22 +0800143 const std::array<std::string_view, 1> interfaces = {
144 "xyz.openbmc_project.Inventory.Item.Drive"};
145 dbus::utility::getSubTreePaths(
146 "/xyz/openbmc_project/inventory", 0, interfaces,
Gunnar Mills7ac13cc2024-04-01 16:05:21 -0500147 std::bind_front(afterChassisDriveCollectionSubtree, asyncResp));
Ed Tanous36d52332023-06-09 13:18:40 -0700148}
Willy Tua85afbe2021-12-28 14:43:47 -0800149
Willy Tu5e577bc2022-07-26 00:41:55 +0000150inline void afterSystemsStorageGetSubtree(
151 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
152 const std::string& storageId, const boost::system::error_code& ec,
153 const dbus::utility::MapperGetSubTreeResponse& subtree)
Ed Tanous36d52332023-06-09 13:18:40 -0700154{
Willy Tu5e577bc2022-07-26 00:41:55 +0000155 if (ec)
Ed Tanous36d52332023-06-09 13:18:40 -0700156 {
Ed Tanous62598e32023-07-17 17:06:25 -0700157 BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
Willy Tu5e577bc2022-07-26 00:41:55 +0000158 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
159 storageId);
Ed Tanous36d52332023-06-09 13:18:40 -0700160 return;
161 }
Ed Tanous3544d2a2023-08-06 18:12:20 -0700162 auto storage = std::ranges::find_if(
163 subtree,
Willy Tu5e577bc2022-07-26 00:41:55 +0000164 [&storageId](const std::pair<std::string,
165 dbus::utility::MapperServiceMap>& object) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400166 return sdbusplus::message::object_path(object.first).filename() ==
167 storageId;
168 });
Willy Tu5e577bc2022-07-26 00:41:55 +0000169 if (storage == subtree.end())
170 {
171 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
172 storageId);
173 return;
174 }
175
Ed Tanous36d52332023-06-09 13:18:40 -0700176 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
177 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous253f11b2024-05-16 09:38:31 -0700178 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
179 BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
Ed Tanous36d52332023-06-09 13:18:40 -0700180 asyncResp->res.jsonValue["Name"] = "Storage";
Willy Tu5e577bc2022-07-26 00:41:55 +0000181 asyncResp->res.jsonValue["Id"] = storageId;
Ed Tanous539d8c62024-06-19 14:38:27 -0700182 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
Willy Tua85afbe2021-12-28 14:43:47 -0800183
Gunnar Mills7ac13cc2024-04-01 16:05:21 -0500184 getDrives(asyncResp);
Ed Tanous253f11b2024-05-16 09:38:31 -0700185 asyncResp->res.jsonValue["Controllers"]["@odata.id"] =
186 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}/Controllers",
187 BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
Willy Tu5e577bc2022-07-26 00:41:55 +0000188}
189
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400190inline void handleSystemsStorageGet(
191 App& app, const crow::Request& req,
192 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
193 const std::string& systemName, const std::string& storageId)
Willy Tu5e577bc2022-07-26 00:41:55 +0000194{
195 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
196 {
197 return;
198 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700199 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800200 {
201 // Option currently returns no systems. TBD
202 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
203 systemName);
204 return;
205 }
Willy Tu5e577bc2022-07-26 00:41:55 +0000206
207 constexpr std::array<std::string_view, 1> interfaces = {
208 "xyz.openbmc_project.Inventory.Item.Storage"};
209 dbus::utility::getSubTree(
210 "/xyz/openbmc_project/inventory", 0, interfaces,
211 std::bind_front(afterSystemsStorageGetSubtree, asyncResp, storageId));
212}
213
214inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
215 const std::string& storageId,
216 const boost::system::error_code& ec,
217 const dbus::utility::MapperGetSubTreeResponse& subtree)
218{
219 if (ec)
220 {
Ed Tanous62598e32023-07-17 17:06:25 -0700221 BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
Willy Tu5e577bc2022-07-26 00:41:55 +0000222 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
223 storageId);
224 return;
225 }
Ed Tanous3544d2a2023-08-06 18:12:20 -0700226 auto storage = std::ranges::find_if(
227 subtree,
Willy Tu5e577bc2022-07-26 00:41:55 +0000228 [&storageId](const std::pair<std::string,
229 dbus::utility::MapperServiceMap>& object) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400230 return sdbusplus::message::object_path(object.first).filename() ==
231 storageId;
232 });
Willy Tu5e577bc2022-07-26 00:41:55 +0000233 if (storage == subtree.end())
234 {
235 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
236 storageId);
237 return;
238 }
239
240 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
241 asyncResp->res.jsonValue["@odata.id"] =
242 boost::urls::format("/redfish/v1/Storage/{}", storageId);
243 asyncResp->res.jsonValue["Name"] = "Storage";
244 asyncResp->res.jsonValue["Id"] = storageId;
Ed Tanous539d8c62024-06-19 14:38:27 -0700245 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
Willy Tu5e577bc2022-07-26 00:41:55 +0000246
247 // Storage subsystem to Storage link.
248 nlohmann::json::array_t storageServices;
249 nlohmann::json::object_t storageService;
250 storageService["@odata.id"] =
Ed Tanous253f11b2024-05-16 09:38:31 -0700251 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
252 BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
Willy Tu5e577bc2022-07-26 00:41:55 +0000253 storageServices.emplace_back(storageService);
254 asyncResp->res.jsonValue["Links"]["StorageServices"] =
255 std::move(storageServices);
256 asyncResp->res.jsonValue["Links"]["StorageServices@odata.count"] = 1;
257}
258
259inline void
260 handleStorageGet(App& app, const crow::Request& req,
261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262 const std::string& storageId)
263{
264 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
265 {
Ed Tanous62598e32023-07-17 17:06:25 -0700266 BMCWEB_LOG_DEBUG("requestRoutesStorage setUpRedfishRoute failed");
Willy Tu5e577bc2022-07-26 00:41:55 +0000267 return;
268 }
269
270 constexpr std::array<std::string_view, 1> interfaces = {
271 "xyz.openbmc_project.Inventory.Item.Storage"};
272 dbus::utility::getSubTree(
273 "/xyz/openbmc_project/inventory", 0, interfaces,
274 std::bind_front(afterSubtree, asyncResp, storageId));
Willy Tua85afbe2021-12-28 14:43:47 -0800275}
276
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700277inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700278{
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800279 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700280 .privileges(redfish::privileges::getStorage)
Ed Tanous002d39b2022-05-31 08:59:27 -0700281 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -0700282 std::bind_front(handleSystemsStorageGet, std::ref(app)));
Willy Tu5e577bc2022-07-26 00:41:55 +0000283
284 BMCWEB_ROUTE(app, "/redfish/v1/Storage/<str>/")
285 .privileges(redfish::privileges::getStorage)
286 .methods(boost::beast::http::verb::get)(
287 std::bind_front(handleStorageGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700288}
289
Willy Tu03913172021-11-08 02:03:19 -0800290inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
291 const std::string& connectionName,
292 const std::string& path)
293{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800294 dbus::utility::getAllProperties(
295 connectionName, path, "xyz.openbmc_project.Inventory.Decorator.Asset",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800296 [asyncResp](const boost::system::error_code& ec,
Ed Tanous168e20c2021-12-13 14:39:53 -0800297 const std::vector<
298 std::pair<std::string, dbus::utility::DbusVariantType>>&
299 propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400300 if (ec)
301 {
302 // this interface isn't necessary
303 return;
304 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200305
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400306 const std::string* partNumber = nullptr;
307 const std::string* serialNumber = nullptr;
308 const std::string* manufacturer = nullptr;
309 const std::string* model = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200310
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400311 const bool success = sdbusplus::unpackPropertiesNoThrow(
312 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
313 partNumber, "SerialNumber", serialNumber, "Manufacturer",
314 manufacturer, "Model", model);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200315
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400316 if (!success)
317 {
318 messages::internalError(asyncResp->res);
319 return;
320 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200321
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400322 if (partNumber != nullptr)
323 {
324 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
325 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200326
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400327 if (serialNumber != nullptr)
328 {
329 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
330 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200331
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400332 if (manufacturer != nullptr)
333 {
334 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
335 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200336
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400337 if (model != nullptr)
338 {
339 asyncResp->res.jsonValue["Model"] = *model;
340 }
341 });
Willy Tu03913172021-11-08 02:03:19 -0800342}
343
344inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
345 const std::string& connectionName,
346 const std::string& path)
347{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800348 dbus::utility::getProperty<bool>(
349 connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present",
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400350 [asyncResp,
351 path](const boost::system::error_code& ec, const bool isPresent) {
352 // this interface isn't necessary, only check it if
353 // we get a good return
354 if (ec)
355 {
356 return;
357 }
Willy Tu03913172021-11-08 02:03:19 -0800358
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400359 if (!isPresent)
360 {
361 asyncResp->res.jsonValue["Status"]["State"] =
362 resource::State::Absent;
363 }
364 });
Willy Tu03913172021-11-08 02:03:19 -0800365}
366
367inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
368 const std::string& connectionName,
369 const std::string& path)
370{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800371 dbus::utility::getProperty<bool>(
372 connectionName, path, "xyz.openbmc_project.State.Drive", "Rebuilding",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800373 [asyncResp](const boost::system::error_code& ec, const bool updating) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400374 // this interface isn't necessary, only check it
375 // if we get a good return
376 if (ec)
377 {
378 return;
379 }
Willy Tu03913172021-11-08 02:03:19 -0800380
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400381 // updating and disabled in the backend shouldn't be
382 // able to be set at the same time, so we don't need
383 // to check for the race condition of these two
384 // calls
385 if (updating)
386 {
387 asyncResp->res.jsonValue["Status"]["State"] =
388 resource::State::Updating;
389 }
390 });
Willy Tu03913172021-11-08 02:03:19 -0800391}
392
George Liudde9bc12023-02-22 09:35:51 +0800393inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800394{
395 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
396 {
George Liudde9bc12023-02-22 09:35:51 +0800397 return drive::MediaType::HDD;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800398 }
399 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
400 {
George Liudde9bc12023-02-22 09:35:51 +0800401 return drive::MediaType::SSD;
402 }
403 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
404 {
405 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800406 }
407
George Liudde9bc12023-02-22 09:35:51 +0800408 return drive::MediaType::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800409}
410
George Liudde9bc12023-02-22 09:35:51 +0800411inline std::optional<protocol::Protocol>
412 convertDriveProtocol(std::string_view proto)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800413{
414 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
415 {
George Liudde9bc12023-02-22 09:35:51 +0800416 return protocol::Protocol::SAS;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800417 }
418 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
419 {
George Liudde9bc12023-02-22 09:35:51 +0800420 return protocol::Protocol::SATA;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800421 }
422 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
423 {
George Liudde9bc12023-02-22 09:35:51 +0800424 return protocol::Protocol::NVMe;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800425 }
426 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
427 {
George Liudde9bc12023-02-22 09:35:51 +0800428 return protocol::Protocol::FC;
429 }
430 if (proto ==
431 "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
432 {
433 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800434 }
435
George Liudde9bc12023-02-22 09:35:51 +0800436 return protocol::Protocol::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800437}
438
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400439inline void getDriveItemProperties(
440 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
441 const std::string& connectionName, const std::string& path)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800442{
Ed Tanousdeae6a72024-11-11 21:58:57 -0800443 dbus::utility::getAllProperties(
444 connectionName, path, "xyz.openbmc_project.Inventory.Item.Drive",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800445 [asyncResp](const boost::system::error_code& ec,
Willy Tu19b8e9a2021-11-08 02:55:03 -0800446 const std::vector<
447 std::pair<std::string, dbus::utility::DbusVariantType>>&
448 propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400449 if (ec)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800450 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400451 // this interface isn't required
452 return;
453 }
454 const std::string* encryptionStatus = nullptr;
455 const bool* isLocked = nullptr;
456 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
457 property : propertiesList)
458 {
459 const std::string& propertyName = property.first;
460 if (propertyName == "Type")
Ed Tanous002d39b2022-05-31 08:59:27 -0700461 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400462 const std::string* value =
463 std::get_if<std::string>(&property.second);
464 if (value == nullptr)
465 {
466 // illegal property
467 BMCWEB_LOG_ERROR("Illegal property: Type");
468 messages::internalError(asyncResp->res);
469 return;
470 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700471
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400472 std::optional<drive::MediaType> mediaType =
473 convertDriveType(*value);
474 if (!mediaType)
475 {
476 BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}",
477 *value);
478 continue;
479 }
480 if (*mediaType == drive::MediaType::Invalid)
481 {
482 messages::internalError(asyncResp->res);
483 return;
484 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700485
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400486 asyncResp->res.jsonValue["MediaType"] = *mediaType;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800487 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400488 else if (propertyName == "Capacity")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800489 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400490 const uint64_t* capacity =
491 std::get_if<uint64_t>(&property.second);
492 if (capacity == nullptr)
493 {
494 BMCWEB_LOG_ERROR("Illegal property: Capacity");
495 messages::internalError(asyncResp->res);
496 return;
497 }
498 if (*capacity == 0)
499 {
500 // drive capacity not known
501 continue;
502 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800503
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400504 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
Ed Tanous002d39b2022-05-31 08:59:27 -0700505 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400506 else if (propertyName == "Protocol")
507 {
508 const std::string* value =
509 std::get_if<std::string>(&property.second);
510 if (value == nullptr)
511 {
512 BMCWEB_LOG_ERROR("Illegal property: Protocol");
513 messages::internalError(asyncResp->res);
514 return;
515 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700516
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400517 std::optional<protocol::Protocol> proto =
518 convertDriveProtocol(*value);
519 if (!proto)
520 {
521 BMCWEB_LOG_WARNING(
522 "Unknown DrivePrototype Interface: {}", *value);
523 continue;
524 }
525 if (*proto == protocol::Protocol::Invalid)
526 {
527 messages::internalError(asyncResp->res);
528 return;
529 }
530 asyncResp->res.jsonValue["Protocol"] = *proto;
George Liudde9bc12023-02-22 09:35:51 +0800531 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400532 else if (propertyName == "PredictedMediaLifeLeftPercent")
George Liudde9bc12023-02-22 09:35:51 +0800533 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400534 const uint8_t* lifeLeft =
535 std::get_if<uint8_t>(&property.second);
536 if (lifeLeft == nullptr)
537 {
538 BMCWEB_LOG_ERROR(
539 "Illegal property: PredictedMediaLifeLeftPercent");
540 messages::internalError(asyncResp->res);
541 return;
542 }
543 // 255 means reading the value is not supported
544 if (*lifeLeft != 255)
545 {
546 asyncResp->res
547 .jsonValue["PredictedMediaLifeLeftPercent"] =
548 *lifeLeft;
549 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700550 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400551 else if (propertyName == "EncryptionStatus")
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700552 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400553 encryptionStatus =
554 std::get_if<std::string>(&property.second);
555 if (encryptionStatus == nullptr)
556 {
557 BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus");
558 messages::internalError(asyncResp->res);
559 return;
560 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700561 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400562 else if (propertyName == "Locked")
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700563 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400564 isLocked = std::get_if<bool>(&property.second);
565 if (isLocked == nullptr)
566 {
567 BMCWEB_LOG_ERROR("Illegal property: Locked");
568 messages::internalError(asyncResp->res);
569 return;
570 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700571 }
572 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700573
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400574 if (encryptionStatus == nullptr || isLocked == nullptr ||
575 *encryptionStatus ==
576 "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown")
577 {
578 return;
579 }
580 if (*encryptionStatus !=
581 "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted")
582 {
583 //"The drive is not currently encrypted."
584 asyncResp->res.jsonValue["EncryptionStatus"] =
585 drive::EncryptionStatus::Unencrypted;
586 return;
587 }
588 if (*isLocked)
589 {
590 //"The drive is currently encrypted and the data is not
591 // accessible to the user."
592 asyncResp->res.jsonValue["EncryptionStatus"] =
593 drive::EncryptionStatus::Locked;
594 return;
595 }
596 // if not locked
597 // "The drive is currently encrypted but the data is accessible
598 // to the user in unencrypted form."
John Edward Broadbente5029d82022-06-08 14:35:21 -0700599 asyncResp->res.jsonValue["EncryptionStatus"] =
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400600 drive::EncryptionStatus::Unlocked;
601 });
Willy Tu19b8e9a2021-11-08 02:55:03 -0800602}
603
Ed Tanous4ff0f1f2024-09-04 17:27:37 -0700604inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Nan Zhoub53dcd92022-06-21 17:47:50 +0000605 const std::string& connectionName,
606 const std::string& path,
607 const std::vector<std::string>& interfaces)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700608{
609 for (const std::string& interface : interfaces)
610 {
611 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
612 {
613 getDriveAsset(asyncResp, connectionName, path);
614 }
615 else if (interface == "xyz.openbmc_project.Inventory.Item")
616 {
617 getDrivePresent(asyncResp, connectionName, path);
618 }
619 else if (interface == "xyz.openbmc_project.State.Drive")
620 {
621 getDriveState(asyncResp, connectionName, path);
622 }
623 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
624 {
625 getDriveItemProperties(asyncResp, connectionName, path);
626 }
627 }
628}
629
Ed Tanous36d52332023-06-09 13:18:40 -0700630inline void afterGetSubtreeSystemsStorageDrive(
631 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
632 const std::string& driveId, const boost::system::error_code& ec,
633 const dbus::utility::MapperGetSubTreeResponse& subtree)
634{
635 if (ec)
636 {
Ed Tanous62598e32023-07-17 17:06:25 -0700637 BMCWEB_LOG_ERROR("Drive mapper call error");
Ed Tanous36d52332023-06-09 13:18:40 -0700638 messages::internalError(asyncResp->res);
639 return;
640 }
641
Ed Tanous3544d2a2023-08-06 18:12:20 -0700642 auto drive = std::ranges::find_if(
643 subtree,
Ed Tanous36d52332023-06-09 13:18:40 -0700644 [&driveId](const std::pair<std::string,
645 dbus::utility::MapperServiceMap>& object) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400646 return sdbusplus::message::object_path(object.first).filename() ==
647 driveId;
648 });
Ed Tanous36d52332023-06-09 13:18:40 -0700649
650 if (drive == subtree.end())
651 {
652 messages::resourceNotFound(asyncResp->res, "Drive", driveId);
653 return;
654 }
655
656 const std::string& path = drive->first;
657 const dbus::utility::MapperServiceMap& connectionNames = drive->second;
658
659 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
Ed Tanous253f11b2024-05-16 09:38:31 -0700660 asyncResp->res.jsonValue["@odata.id"] =
661 boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Drives/{}",
662 BMCWEB_REDFISH_SYSTEM_URI_NAME, driveId);
Ed Tanous36d52332023-06-09 13:18:40 -0700663 asyncResp->res.jsonValue["Name"] = driveId;
664 asyncResp->res.jsonValue["Id"] = driveId;
665
666 if (connectionNames.size() != 1)
667 {
Ed Tanous62598e32023-07-17 17:06:25 -0700668 BMCWEB_LOG_ERROR("Connection size {}, not equal to 1",
669 connectionNames.size());
Ed Tanous36d52332023-06-09 13:18:40 -0700670 messages::internalError(asyncResp->res);
671 return;
672 }
673
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400674 getMainChassisId(
675 asyncResp, [](const std::string& chassisId,
676 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
677 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
678 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
679 });
Ed Tanous36d52332023-06-09 13:18:40 -0700680
681 // default it to Enabled
Ed Tanous539d8c62024-06-19 14:38:27 -0700682 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
Ed Tanous36d52332023-06-09 13:18:40 -0700683
Ed Tanous36d52332023-06-09 13:18:40 -0700684 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
685 connectionNames[0].second);
686}
687
688inline void handleSystemsStorageDriveGet(
689 App& app, const crow::Request& req,
690 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
691 const std::string& systemName, const std::string& driveId)
692{
693 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
694 {
695 return;
696 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700697 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800698 {
699 // Option currently returns no systems. TBD
700 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
701 systemName);
702 return;
703 }
704
Ed Tanous253f11b2024-05-16 09:38:31 -0700705 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
Ed Tanous36d52332023-06-09 13:18:40 -0700706 {
707 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
708 systemName);
709 return;
710 }
711
712 constexpr std::array<std::string_view, 1> interfaces = {
713 "xyz.openbmc_project.Inventory.Item.Drive"};
714 dbus::utility::getSubTree(
715 "/xyz/openbmc_project/inventory", 0, interfaces,
716 std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
717 driveId));
718}
719
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700720inline void requestRoutesDrive(App& app)
721{
Ed Tanous22d268c2022-05-19 09:39:07 -0700722 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700723 .privileges(redfish::privileges::getDrive)
Ed Tanous002d39b2022-05-31 08:59:27 -0700724 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -0700725 std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700726}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700727
Ed Tanous36d52332023-06-09 13:18:40 -0700728inline void afterChassisDriveCollectionSubtreeGet(
729 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
730 const std::string& chassisId, const boost::system::error_code& ec,
731 const dbus::utility::MapperGetSubTreeResponse& subtree)
732{
733 if (ec)
734 {
735 if (ec == boost::system::errc::host_unreachable)
736 {
737 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
738 return;
739 }
740 messages::internalError(asyncResp->res);
741 return;
742 }
743
744 // Iterate over all retrieved ObjectPaths.
745 for (const auto& [path, connectionNames] : subtree)
746 {
747 sdbusplus::message::object_path objPath(path);
748 if (objPath.filename() != chassisId)
749 {
750 continue;
751 }
752
753 if (connectionNames.empty())
754 {
Ed Tanous62598e32023-07-17 17:06:25 -0700755 BMCWEB_LOG_ERROR("Got 0 Connection names");
Ed Tanous36d52332023-06-09 13:18:40 -0700756 continue;
757 }
758
759 asyncResp->res.jsonValue["@odata.type"] =
760 "#DriveCollection.DriveCollection";
761 asyncResp->res.jsonValue["@odata.id"] =
762 boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
763 asyncResp->res.jsonValue["Name"] = "Drive Collection";
764
765 // Association lambda
766 dbus::utility::getAssociationEndPoints(
767 path + "/drive",
768 [asyncResp, chassisId](const boost::system::error_code& ec3,
769 const dbus::utility::MapperEndPoints& resp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400770 if (ec3)
771 {
772 BMCWEB_LOG_ERROR("Error in chassis Drive association ");
773 }
774 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
775 // important if array is empty
776 members = nlohmann::json::array();
Ed Tanous36d52332023-06-09 13:18:40 -0700777
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400778 std::vector<std::string> leafNames;
779 for (const auto& drive : resp)
780 {
781 sdbusplus::message::object_path drivePath(drive);
782 leafNames.push_back(drivePath.filename());
783 }
Ed Tanous36d52332023-06-09 13:18:40 -0700784
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400785 std::ranges::sort(leafNames, AlphanumLess<std::string>());
Ed Tanous36d52332023-06-09 13:18:40 -0700786
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400787 for (const auto& leafName : leafNames)
788 {
789 nlohmann::json::object_t member;
790 member["@odata.id"] =
791 boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}",
792 chassisId, leafName);
793 members.emplace_back(std::move(member));
794 // navigation links will be registered in next patch set
795 }
796 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
797 }); // end association lambda
Ed Tanous36d52332023-06-09 13:18:40 -0700798
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400799 } // end Iterate over all retrieved ObjectPaths
Ed Tanous36d52332023-06-09 13:18:40 -0700800}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700801/**
802 * Chassis drives, this URL will show all the DriveCollection
803 * information
804 */
Nan Zhoub53dcd92022-06-21 17:47:50 +0000805inline void chassisDriveCollectionGet(
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700806 crow::App& app, const crow::Request& req,
807 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
808 const std::string& chassisId)
809{
Carson Labrado3ba00072022-06-06 19:40:56 +0000810 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700811 {
812 return;
813 }
814
815 // mapper call lambda
George Liue99073f2022-12-09 11:06:16 +0800816 constexpr std::array<std::string_view, 2> interfaces = {
817 "xyz.openbmc_project.Inventory.Item.Board",
818 "xyz.openbmc_project.Inventory.Item.Chassis"};
819 dbus::utility::getSubTree(
820 "/xyz/openbmc_project/inventory", 0, interfaces,
Ed Tanous36d52332023-06-09 13:18:40 -0700821 std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
822 chassisId));
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700823}
824
825inline void requestRoutesChassisDrive(App& app)
826{
827 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
828 .privileges(redfish::privileges::getDriveCollection)
829 .methods(boost::beast::http::verb::get)(
830 std::bind_front(chassisDriveCollectionGet, std::ref(app)));
831}
832
Nan Zhoub53dcd92022-06-21 17:47:50 +0000833inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
834 const std::string& chassisId,
835 const std::string& driveName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800836 const boost::system::error_code& ec,
Nan Zhoub53dcd92022-06-21 17:47:50 +0000837 const dbus::utility::MapperGetSubTreeResponse& subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700838{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700839 if (ec)
840 {
Ed Tanous62598e32023-07-17 17:06:25 -0700841 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700842 messages::internalError(asyncResp->res);
843 return;
844 }
845
846 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000847 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700848 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700849 sdbusplus::message::object_path objPath(path);
850 if (objPath.filename() != driveName)
851 {
852 continue;
853 }
854
855 if (connectionNames.empty())
856 {
Ed Tanous62598e32023-07-17 17:06:25 -0700857 BMCWEB_LOG_ERROR("Got 0 Connection names");
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700858 continue;
859 }
860
Ed Tanousef4c65b2023-04-24 15:28:50 -0700861 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
862 "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700863
864 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700865 asyncResp->res.jsonValue["Name"] = driveName;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700866 asyncResp->res.jsonValue["Id"] = driveName;
867 // default it to Enabled
Ed Tanous539d8c62024-06-19 14:38:27 -0700868 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700869
870 nlohmann::json::object_t linkChassisNav;
871 linkChassisNav["@odata.id"] =
Ed Tanousef4c65b2023-04-24 15:28:50 -0700872 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700873 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
874
875 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
876 connectionNames[0].second);
877 }
878}
879
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400880inline void matchAndFillDrive(
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
882 const std::string& chassisId, const std::string& driveName,
883 const std::vector<std::string>& resp)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700884{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700885 for (const std::string& drivePath : resp)
886 {
887 sdbusplus::message::object_path path(drivePath);
888 std::string leaf = path.filename();
889 if (leaf != driveName)
890 {
891 continue;
892 }
893 // mapper call drive
George Liue99073f2022-12-09 11:06:16 +0800894 constexpr std::array<std::string_view, 1> driveInterface = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700895 "xyz.openbmc_project.Inventory.Item.Drive"};
George Liue99073f2022-12-09 11:06:16 +0800896 dbus::utility::getSubTree(
897 "/xyz/openbmc_project/inventory", 0, driveInterface,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700898 [asyncResp, chassisId, driveName](
George Liue99073f2022-12-09 11:06:16 +0800899 const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700900 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400901 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
902 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700903 }
904}
905
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400906inline void handleChassisDriveGet(
907 crow::App& app, const crow::Request& req,
908 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
909 const std::string& chassisId, const std::string& driveName)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700910{
Michal Orzel03810a12022-06-15 14:04:28 +0200911 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700912 {
913 return;
914 }
George Liue99073f2022-12-09 11:06:16 +0800915 constexpr std::array<std::string_view, 2> interfaces = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700916 "xyz.openbmc_project.Inventory.Item.Board",
917 "xyz.openbmc_project.Inventory.Item.Chassis"};
918
919 // mapper call chassis
George Liue99073f2022-12-09 11:06:16 +0800920 dbus::utility::getSubTree(
921 "/xyz/openbmc_project/inventory", 0, interfaces,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700922 [asyncResp, chassisId,
George Liue99073f2022-12-09 11:06:16 +0800923 driveName](const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700924 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400925 if (ec)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700926 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400927 messages::internalError(asyncResp->res);
928 return;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700929 }
930
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400931 // Iterate over all retrieved ObjectPaths.
932 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700933 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400934 sdbusplus::message::object_path objPath(path);
935 if (objPath.filename() != chassisId)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700936 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400937 continue;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700938 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400939
940 if (connectionNames.empty())
941 {
942 BMCWEB_LOG_ERROR("Got 0 Connection names");
943 continue;
944 }
945
946 dbus::utility::getAssociationEndPoints(
947 path + "/drive",
948 [asyncResp, chassisId,
949 driveName](const boost::system::error_code& ec3,
950 const dbus::utility::MapperEndPoints& resp) {
951 if (ec3)
952 {
953 return; // no drives = no failures
954 }
955 matchAndFillDrive(asyncResp, chassisId, driveName,
956 resp);
957 });
958 break;
959 }
960 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700961}
962
963/**
964 * This URL will show the drive interface for the specific drive in the chassis
965 */
966inline void requestRoutesChassisDriveName(App& app)
967{
968 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
969 .privileges(redfish::privileges::getChassis)
970 .methods(boost::beast::http::verb::get)(
971 std::bind_front(handleChassisDriveGet, std::ref(app)));
972}
973
Willy Tu61b1eb22023-03-14 11:29:50 -0700974inline void getStorageControllerAsset(
975 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
976 const boost::system::error_code& ec,
977 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
978 propertiesList)
979{
980 if (ec)
981 {
982 // this interface isn't necessary
Ed Tanous62598e32023-07-17 17:06:25 -0700983 BMCWEB_LOG_DEBUG("Failed to get StorageControllerAsset");
Willy Tu61b1eb22023-03-14 11:29:50 -0700984 return;
985 }
986
987 const std::string* partNumber = nullptr;
988 const std::string* serialNumber = nullptr;
989 const std::string* manufacturer = nullptr;
990 const std::string* model = nullptr;
991 if (!sdbusplus::unpackPropertiesNoThrow(
992 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
993 partNumber, "SerialNumber", serialNumber, "Manufacturer",
994 manufacturer, "Model", model))
995 {
996 messages::internalError(asyncResp->res);
997 return;
998 }
999
1000 if (partNumber != nullptr)
1001 {
1002 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
1003 }
1004
1005 if (serialNumber != nullptr)
1006 {
1007 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
1008 }
1009
1010 if (manufacturer != nullptr)
1011 {
1012 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
1013 }
1014
1015 if (model != nullptr)
1016 {
1017 asyncResp->res.jsonValue["Model"] = *model;
1018 }
1019}
1020
1021inline void populateStorageController(
1022 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1023 const std::string& controllerId, const std::string& connectionName,
1024 const std::string& path)
1025{
1026 asyncResp->res.jsonValue["@odata.type"] =
1027 "#StorageController.v1_6_0.StorageController";
Ed Tanous253f11b2024-05-16 09:38:31 -07001028 asyncResp->res.jsonValue["@odata.id"] =
1029 boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1030 BMCWEB_REDFISH_SYSTEM_URI_NAME, controllerId);
Willy Tu61b1eb22023-03-14 11:29:50 -07001031 asyncResp->res.jsonValue["Name"] = controllerId;
1032 asyncResp->res.jsonValue["Id"] = controllerId;
Ed Tanous539d8c62024-06-19 14:38:27 -07001033 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
Willy Tu61b1eb22023-03-14 11:29:50 -07001034
Ed Tanousdeae6a72024-11-11 21:58:57 -08001035 dbus::utility::getProperty<bool>(
1036 connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present",
Willy Tu61b1eb22023-03-14 11:29:50 -07001037 [asyncResp](const boost::system::error_code& ec, bool isPresent) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001038 // this interface isn't necessary, only check it
1039 // if we get a good return
1040 if (ec)
1041 {
1042 BMCWEB_LOG_DEBUG("Failed to get Present property");
1043 return;
1044 }
1045 if (!isPresent)
1046 {
1047 asyncResp->res.jsonValue["Status"]["State"] =
1048 resource::State::Absent;
1049 }
1050 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001051
Ed Tanousdeae6a72024-11-11 21:58:57 -08001052 dbus::utility::getAllProperties(
1053 connectionName, path, "xyz.openbmc_project.Inventory.Decorator.Asset",
Willy Tu61b1eb22023-03-14 11:29:50 -07001054 [asyncResp](const boost::system::error_code& ec,
1055 const std::vector<
1056 std::pair<std::string, dbus::utility::DbusVariantType>>&
1057 propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001058 getStorageControllerAsset(asyncResp, ec, propertiesList);
1059 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001060}
1061
1062inline void getStorageControllerHandler(
1063 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1064 const std::string& controllerId, const boost::system::error_code& ec,
1065 const dbus::utility::MapperGetSubTreeResponse& subtree)
1066{
1067 if (ec || subtree.empty())
1068 {
1069 // doesn't have to be there
Ed Tanous62598e32023-07-17 17:06:25 -07001070 BMCWEB_LOG_DEBUG("Failed to handle StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001071 return;
1072 }
1073
1074 for (const auto& [path, interfaceDict] : subtree)
1075 {
1076 sdbusplus::message::object_path object(path);
1077 std::string id = object.filename();
1078 if (id.empty())
1079 {
Ed Tanous62598e32023-07-17 17:06:25 -07001080 BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
Willy Tu61b1eb22023-03-14 11:29:50 -07001081 return;
1082 }
1083 if (id != controllerId)
1084 {
1085 continue;
1086 }
1087
1088 if (interfaceDict.size() != 1)
1089 {
Ed Tanous62598e32023-07-17 17:06:25 -07001090 BMCWEB_LOG_ERROR("Connection size {}, greater than 1",
1091 interfaceDict.size());
Willy Tu61b1eb22023-03-14 11:29:50 -07001092 messages::internalError(asyncResp->res);
1093 return;
1094 }
1095
1096 const std::string& connectionName = interfaceDict.front().first;
1097 populateStorageController(asyncResp, controllerId, connectionName,
1098 path);
1099 }
1100}
1101
1102inline void populateStorageControllerCollection(
1103 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1104 const boost::system::error_code& ec,
1105 const dbus::utility::MapperGetSubTreePathsResponse& controllerList)
1106{
1107 nlohmann::json::array_t members;
1108 if (ec || controllerList.empty())
1109 {
1110 asyncResp->res.jsonValue["Members"] = std::move(members);
1111 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous62598e32023-07-17 17:06:25 -07001112 BMCWEB_LOG_DEBUG("Failed to find any StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001113 return;
1114 }
1115
1116 for (const std::string& path : controllerList)
1117 {
1118 std::string id = sdbusplus::message::object_path(path).filename();
1119 if (id.empty())
1120 {
Ed Tanous62598e32023-07-17 17:06:25 -07001121 BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
Willy Tu61b1eb22023-03-14 11:29:50 -07001122 return;
1123 }
1124 nlohmann::json::object_t member;
1125 member["@odata.id"] = boost::urls::format(
Ed Tanous253f11b2024-05-16 09:38:31 -07001126 "/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1127 BMCWEB_REDFISH_SYSTEM_URI_NAME, id);
Willy Tu61b1eb22023-03-14 11:29:50 -07001128 members.emplace_back(member);
1129 }
1130 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
1131 asyncResp->res.jsonValue["Members"] = std::move(members);
1132}
1133
Ed Tanous36d52332023-06-09 13:18:40 -07001134inline void handleSystemsStorageControllerCollectionGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001135 App& app, const crow::Request& req,
1136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1137 const std::string& systemName)
1138{
1139 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1140 {
Ed Tanous62598e32023-07-17 17:06:25 -07001141 BMCWEB_LOG_DEBUG(
1142 "Failed to setup Redfish Route for StorageController Collection");
Willy Tu61b1eb22023-03-14 11:29:50 -07001143 return;
1144 }
Ed Tanous253f11b2024-05-16 09:38:31 -07001145 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
Willy Tu61b1eb22023-03-14 11:29:50 -07001146 {
1147 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1148 systemName);
Ed Tanous62598e32023-07-17 17:06:25 -07001149 BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
Willy Tu61b1eb22023-03-14 11:29:50 -07001150 return;
1151 }
1152
1153 asyncResp->res.jsonValue["@odata.type"] =
1154 "#StorageControllerCollection.StorageControllerCollection";
1155 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous253f11b2024-05-16 09:38:31 -07001156 std::format("/redfish/v1/Systems/{}/Storage/1/Controllers",
1157 BMCWEB_REDFISH_SYSTEM_URI_NAME);
Willy Tu61b1eb22023-03-14 11:29:50 -07001158 asyncResp->res.jsonValue["Name"] = "Storage Controller Collection";
1159
1160 constexpr std::array<std::string_view, 1> interfaces = {
1161 "xyz.openbmc_project.Inventory.Item.StorageController"};
1162 dbus::utility::getSubTreePaths(
1163 "/xyz/openbmc_project/inventory", 0, interfaces,
1164 [asyncResp](const boost::system::error_code& ec,
1165 const dbus::utility::MapperGetSubTreePathsResponse&
1166 controllerList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001167 populateStorageControllerCollection(asyncResp, ec, controllerList);
1168 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001169}
1170
Ed Tanous36d52332023-06-09 13:18:40 -07001171inline void handleSystemsStorageControllerGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001172 App& app, const crow::Request& req,
1173 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1174 const std::string& systemName, const std::string& controllerId)
1175{
1176 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1177 {
Ed Tanous62598e32023-07-17 17:06:25 -07001178 BMCWEB_LOG_DEBUG("Failed to setup Redfish Route for StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001179 return;
1180 }
Ed Tanous253f11b2024-05-16 09:38:31 -07001181 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
Willy Tu61b1eb22023-03-14 11:29:50 -07001182 {
1183 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1184 systemName);
Ed Tanous62598e32023-07-17 17:06:25 -07001185 BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
Willy Tu61b1eb22023-03-14 11:29:50 -07001186 return;
1187 }
1188 constexpr std::array<std::string_view, 1> interfaces = {
1189 "xyz.openbmc_project.Inventory.Item.StorageController"};
1190 dbus::utility::getSubTree(
1191 "/xyz/openbmc_project/inventory", 0, interfaces,
1192 [asyncResp,
1193 controllerId](const boost::system::error_code& ec,
1194 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001195 getStorageControllerHandler(asyncResp, controllerId, ec, subtree);
1196 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001197}
1198
1199inline void requestRoutesStorageControllerCollection(App& app)
1200{
1201 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/")
1202 .privileges(redfish::privileges::getStorageControllerCollection)
Ed Tanous36d52332023-06-09 13:18:40 -07001203 .methods(boost::beast::http::verb::get)(std::bind_front(
1204 handleSystemsStorageControllerCollectionGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001205}
1206
1207inline void requestRoutesStorageController(App& app)
1208{
1209 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>")
1210 .privileges(redfish::privileges::getStorageController)
1211 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -07001212 std::bind_front(handleSystemsStorageControllerGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001213}
1214
Nikhil Potadea25aecc2019-08-23 16:35:26 -07001215} // namespace redfish