blob: e670de72e390d774ea42e3890fbb4db082427469 [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{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200294 sdbusplus::asio::getAllProperties(
295 *crow::connections::systemBus, connectionName, path,
296 "xyz.openbmc_project.Inventory.Decorator.Asset",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800297 [asyncResp](const boost::system::error_code& ec,
Ed Tanous168e20c2021-12-13 14:39:53 -0800298 const std::vector<
299 std::pair<std::string, dbus::utility::DbusVariantType>>&
300 propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400301 if (ec)
302 {
303 // this interface isn't necessary
304 return;
305 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200306
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400307 const std::string* partNumber = nullptr;
308 const std::string* serialNumber = nullptr;
309 const std::string* manufacturer = nullptr;
310 const std::string* model = nullptr;
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200311
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400312 const bool success = sdbusplus::unpackPropertiesNoThrow(
313 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
314 partNumber, "SerialNumber", serialNumber, "Manufacturer",
315 manufacturer, "Model", model);
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200316
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400317 if (!success)
318 {
319 messages::internalError(asyncResp->res);
320 return;
321 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200322
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400323 if (partNumber != nullptr)
324 {
325 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
326 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200327
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400328 if (serialNumber != nullptr)
329 {
330 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
331 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200332
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400333 if (manufacturer != nullptr)
334 {
335 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
336 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200337
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400338 if (model != nullptr)
339 {
340 asyncResp->res.jsonValue["Model"] = *model;
341 }
342 });
Willy Tu03913172021-11-08 02:03:19 -0800343}
344
345inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
346 const std::string& connectionName,
347 const std::string& path)
348{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700349 sdbusplus::asio::getProperty<bool>(
350 *crow::connections::systemBus, connectionName, path,
351 "xyz.openbmc_project.Inventory.Item", "Present",
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400352 [asyncResp,
353 path](const boost::system::error_code& ec, const bool isPresent) {
354 // this interface isn't necessary, only check it if
355 // we get a good return
356 if (ec)
357 {
358 return;
359 }
Willy Tu03913172021-11-08 02:03:19 -0800360
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400361 if (!isPresent)
362 {
363 asyncResp->res.jsonValue["Status"]["State"] =
364 resource::State::Absent;
365 }
366 });
Willy Tu03913172021-11-08 02:03:19 -0800367}
368
369inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
370 const std::string& connectionName,
371 const std::string& path)
372{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700373 sdbusplus::asio::getProperty<bool>(
374 *crow::connections::systemBus, connectionName, path,
375 "xyz.openbmc_project.State.Drive", "Rebuilding",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800376 [asyncResp](const boost::system::error_code& ec, const bool updating) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400377 // this interface isn't necessary, only check it
378 // if we get a good return
379 if (ec)
380 {
381 return;
382 }
Willy Tu03913172021-11-08 02:03:19 -0800383
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400384 // updating and disabled in the backend shouldn't be
385 // able to be set at the same time, so we don't need
386 // to check for the race condition of these two
387 // calls
388 if (updating)
389 {
390 asyncResp->res.jsonValue["Status"]["State"] =
391 resource::State::Updating;
392 }
393 });
Willy Tu03913172021-11-08 02:03:19 -0800394}
395
George Liudde9bc12023-02-22 09:35:51 +0800396inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800397{
398 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
399 {
George Liudde9bc12023-02-22 09:35:51 +0800400 return drive::MediaType::HDD;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800401 }
402 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
403 {
George Liudde9bc12023-02-22 09:35:51 +0800404 return drive::MediaType::SSD;
405 }
406 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
407 {
408 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800409 }
410
George Liudde9bc12023-02-22 09:35:51 +0800411 return drive::MediaType::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800412}
413
George Liudde9bc12023-02-22 09:35:51 +0800414inline std::optional<protocol::Protocol>
415 convertDriveProtocol(std::string_view proto)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800416{
417 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
418 {
George Liudde9bc12023-02-22 09:35:51 +0800419 return protocol::Protocol::SAS;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800420 }
421 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
422 {
George Liudde9bc12023-02-22 09:35:51 +0800423 return protocol::Protocol::SATA;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800424 }
425 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
426 {
George Liudde9bc12023-02-22 09:35:51 +0800427 return protocol::Protocol::NVMe;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800428 }
429 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
430 {
George Liudde9bc12023-02-22 09:35:51 +0800431 return protocol::Protocol::FC;
432 }
433 if (proto ==
434 "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
435 {
436 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800437 }
438
George Liudde9bc12023-02-22 09:35:51 +0800439 return protocol::Protocol::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800440}
441
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400442inline void getDriveItemProperties(
443 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
444 const std::string& connectionName, const std::string& path)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800445{
446 sdbusplus::asio::getAllProperties(
447 *crow::connections::systemBus, connectionName, path,
448 "xyz.openbmc_project.Inventory.Item.Drive",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800449 [asyncResp](const boost::system::error_code& ec,
Willy Tu19b8e9a2021-11-08 02:55:03 -0800450 const std::vector<
451 std::pair<std::string, dbus::utility::DbusVariantType>>&
452 propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400453 if (ec)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800454 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400455 // this interface isn't required
456 return;
457 }
458 const std::string* encryptionStatus = nullptr;
459 const bool* isLocked = nullptr;
460 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
461 property : propertiesList)
462 {
463 const std::string& propertyName = property.first;
464 if (propertyName == "Type")
Ed Tanous002d39b2022-05-31 08:59:27 -0700465 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400466 const std::string* value =
467 std::get_if<std::string>(&property.second);
468 if (value == nullptr)
469 {
470 // illegal property
471 BMCWEB_LOG_ERROR("Illegal property: Type");
472 messages::internalError(asyncResp->res);
473 return;
474 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700475
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400476 std::optional<drive::MediaType> mediaType =
477 convertDriveType(*value);
478 if (!mediaType)
479 {
480 BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}",
481 *value);
482 continue;
483 }
484 if (*mediaType == drive::MediaType::Invalid)
485 {
486 messages::internalError(asyncResp->res);
487 return;
488 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700489
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400490 asyncResp->res.jsonValue["MediaType"] = *mediaType;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800491 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400492 else if (propertyName == "Capacity")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800493 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400494 const uint64_t* capacity =
495 std::get_if<uint64_t>(&property.second);
496 if (capacity == nullptr)
497 {
498 BMCWEB_LOG_ERROR("Illegal property: Capacity");
499 messages::internalError(asyncResp->res);
500 return;
501 }
502 if (*capacity == 0)
503 {
504 // drive capacity not known
505 continue;
506 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800507
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400508 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
Ed Tanous002d39b2022-05-31 08:59:27 -0700509 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400510 else if (propertyName == "Protocol")
511 {
512 const std::string* value =
513 std::get_if<std::string>(&property.second);
514 if (value == nullptr)
515 {
516 BMCWEB_LOG_ERROR("Illegal property: Protocol");
517 messages::internalError(asyncResp->res);
518 return;
519 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700520
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400521 std::optional<protocol::Protocol> proto =
522 convertDriveProtocol(*value);
523 if (!proto)
524 {
525 BMCWEB_LOG_WARNING(
526 "Unknown DrivePrototype Interface: {}", *value);
527 continue;
528 }
529 if (*proto == protocol::Protocol::Invalid)
530 {
531 messages::internalError(asyncResp->res);
532 return;
533 }
534 asyncResp->res.jsonValue["Protocol"] = *proto;
George Liudde9bc12023-02-22 09:35:51 +0800535 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400536 else if (propertyName == "PredictedMediaLifeLeftPercent")
George Liudde9bc12023-02-22 09:35:51 +0800537 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400538 const uint8_t* lifeLeft =
539 std::get_if<uint8_t>(&property.second);
540 if (lifeLeft == nullptr)
541 {
542 BMCWEB_LOG_ERROR(
543 "Illegal property: PredictedMediaLifeLeftPercent");
544 messages::internalError(asyncResp->res);
545 return;
546 }
547 // 255 means reading the value is not supported
548 if (*lifeLeft != 255)
549 {
550 asyncResp->res
551 .jsonValue["PredictedMediaLifeLeftPercent"] =
552 *lifeLeft;
553 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700554 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400555 else if (propertyName == "EncryptionStatus")
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700556 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400557 encryptionStatus =
558 std::get_if<std::string>(&property.second);
559 if (encryptionStatus == nullptr)
560 {
561 BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus");
562 messages::internalError(asyncResp->res);
563 return;
564 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700565 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400566 else if (propertyName == "Locked")
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700567 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400568 isLocked = std::get_if<bool>(&property.second);
569 if (isLocked == nullptr)
570 {
571 BMCWEB_LOG_ERROR("Illegal property: Locked");
572 messages::internalError(asyncResp->res);
573 return;
574 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700575 }
576 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700577
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400578 if (encryptionStatus == nullptr || isLocked == nullptr ||
579 *encryptionStatus ==
580 "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown")
581 {
582 return;
583 }
584 if (*encryptionStatus !=
585 "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted")
586 {
587 //"The drive is not currently encrypted."
588 asyncResp->res.jsonValue["EncryptionStatus"] =
589 drive::EncryptionStatus::Unencrypted;
590 return;
591 }
592 if (*isLocked)
593 {
594 //"The drive is currently encrypted and the data is not
595 // accessible to the user."
596 asyncResp->res.jsonValue["EncryptionStatus"] =
597 drive::EncryptionStatus::Locked;
598 return;
599 }
600 // if not locked
601 // "The drive is currently encrypted but the data is accessible
602 // to the user in unencrypted form."
John Edward Broadbente5029d82022-06-08 14:35:21 -0700603 asyncResp->res.jsonValue["EncryptionStatus"] =
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400604 drive::EncryptionStatus::Unlocked;
605 });
Willy Tu19b8e9a2021-11-08 02:55:03 -0800606}
607
Ed Tanous4ff0f1f2024-09-04 17:27:37 -0700608inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Nan Zhoub53dcd92022-06-21 17:47:50 +0000609 const std::string& connectionName,
610 const std::string& path,
611 const std::vector<std::string>& interfaces)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700612{
613 for (const std::string& interface : interfaces)
614 {
615 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
616 {
617 getDriveAsset(asyncResp, connectionName, path);
618 }
619 else if (interface == "xyz.openbmc_project.Inventory.Item")
620 {
621 getDrivePresent(asyncResp, connectionName, path);
622 }
623 else if (interface == "xyz.openbmc_project.State.Drive")
624 {
625 getDriveState(asyncResp, connectionName, path);
626 }
627 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
628 {
629 getDriveItemProperties(asyncResp, connectionName, path);
630 }
631 }
632}
633
Ed Tanous36d52332023-06-09 13:18:40 -0700634inline void afterGetSubtreeSystemsStorageDrive(
635 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
636 const std::string& driveId, const boost::system::error_code& ec,
637 const dbus::utility::MapperGetSubTreeResponse& subtree)
638{
639 if (ec)
640 {
Ed Tanous62598e32023-07-17 17:06:25 -0700641 BMCWEB_LOG_ERROR("Drive mapper call error");
Ed Tanous36d52332023-06-09 13:18:40 -0700642 messages::internalError(asyncResp->res);
643 return;
644 }
645
Ed Tanous3544d2a2023-08-06 18:12:20 -0700646 auto drive = std::ranges::find_if(
647 subtree,
Ed Tanous36d52332023-06-09 13:18:40 -0700648 [&driveId](const std::pair<std::string,
649 dbus::utility::MapperServiceMap>& object) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400650 return sdbusplus::message::object_path(object.first).filename() ==
651 driveId;
652 });
Ed Tanous36d52332023-06-09 13:18:40 -0700653
654 if (drive == subtree.end())
655 {
656 messages::resourceNotFound(asyncResp->res, "Drive", driveId);
657 return;
658 }
659
660 const std::string& path = drive->first;
661 const dbus::utility::MapperServiceMap& connectionNames = drive->second;
662
663 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
Ed Tanous253f11b2024-05-16 09:38:31 -0700664 asyncResp->res.jsonValue["@odata.id"] =
665 boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Drives/{}",
666 BMCWEB_REDFISH_SYSTEM_URI_NAME, driveId);
Ed Tanous36d52332023-06-09 13:18:40 -0700667 asyncResp->res.jsonValue["Name"] = driveId;
668 asyncResp->res.jsonValue["Id"] = driveId;
669
670 if (connectionNames.size() != 1)
671 {
Ed Tanous62598e32023-07-17 17:06:25 -0700672 BMCWEB_LOG_ERROR("Connection size {}, not equal to 1",
673 connectionNames.size());
Ed Tanous36d52332023-06-09 13:18:40 -0700674 messages::internalError(asyncResp->res);
675 return;
676 }
677
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400678 getMainChassisId(
679 asyncResp, [](const std::string& chassisId,
680 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
681 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
682 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
683 });
Ed Tanous36d52332023-06-09 13:18:40 -0700684
685 // default it to Enabled
Ed Tanous539d8c62024-06-19 14:38:27 -0700686 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
Ed Tanous36d52332023-06-09 13:18:40 -0700687
Ed Tanous36d52332023-06-09 13:18:40 -0700688 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
689 connectionNames[0].second);
690}
691
692inline void handleSystemsStorageDriveGet(
693 App& app, const crow::Request& req,
694 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
695 const std::string& systemName, const std::string& driveId)
696{
697 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
698 {
699 return;
700 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700701 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800702 {
703 // Option currently returns no systems. TBD
704 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
705 systemName);
706 return;
707 }
708
Ed Tanous253f11b2024-05-16 09:38:31 -0700709 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
Ed Tanous36d52332023-06-09 13:18:40 -0700710 {
711 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
712 systemName);
713 return;
714 }
715
716 constexpr std::array<std::string_view, 1> interfaces = {
717 "xyz.openbmc_project.Inventory.Item.Drive"};
718 dbus::utility::getSubTree(
719 "/xyz/openbmc_project/inventory", 0, interfaces,
720 std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
721 driveId));
722}
723
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700724inline void requestRoutesDrive(App& app)
725{
Ed Tanous22d268c2022-05-19 09:39:07 -0700726 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700727 .privileges(redfish::privileges::getDrive)
Ed Tanous002d39b2022-05-31 08:59:27 -0700728 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -0700729 std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700730}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700731
Ed Tanous36d52332023-06-09 13:18:40 -0700732inline void afterChassisDriveCollectionSubtreeGet(
733 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
734 const std::string& chassisId, const boost::system::error_code& ec,
735 const dbus::utility::MapperGetSubTreeResponse& subtree)
736{
737 if (ec)
738 {
739 if (ec == boost::system::errc::host_unreachable)
740 {
741 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
742 return;
743 }
744 messages::internalError(asyncResp->res);
745 return;
746 }
747
748 // Iterate over all retrieved ObjectPaths.
749 for (const auto& [path, connectionNames] : subtree)
750 {
751 sdbusplus::message::object_path objPath(path);
752 if (objPath.filename() != chassisId)
753 {
754 continue;
755 }
756
757 if (connectionNames.empty())
758 {
Ed Tanous62598e32023-07-17 17:06:25 -0700759 BMCWEB_LOG_ERROR("Got 0 Connection names");
Ed Tanous36d52332023-06-09 13:18:40 -0700760 continue;
761 }
762
763 asyncResp->res.jsonValue["@odata.type"] =
764 "#DriveCollection.DriveCollection";
765 asyncResp->res.jsonValue["@odata.id"] =
766 boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
767 asyncResp->res.jsonValue["Name"] = "Drive Collection";
768
769 // Association lambda
770 dbus::utility::getAssociationEndPoints(
771 path + "/drive",
772 [asyncResp, chassisId](const boost::system::error_code& ec3,
773 const dbus::utility::MapperEndPoints& resp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400774 if (ec3)
775 {
776 BMCWEB_LOG_ERROR("Error in chassis Drive association ");
777 }
778 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
779 // important if array is empty
780 members = nlohmann::json::array();
Ed Tanous36d52332023-06-09 13:18:40 -0700781
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400782 std::vector<std::string> leafNames;
783 for (const auto& drive : resp)
784 {
785 sdbusplus::message::object_path drivePath(drive);
786 leafNames.push_back(drivePath.filename());
787 }
Ed Tanous36d52332023-06-09 13:18:40 -0700788
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400789 std::ranges::sort(leafNames, AlphanumLess<std::string>());
Ed Tanous36d52332023-06-09 13:18:40 -0700790
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400791 for (const auto& leafName : leafNames)
792 {
793 nlohmann::json::object_t member;
794 member["@odata.id"] =
795 boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}",
796 chassisId, leafName);
797 members.emplace_back(std::move(member));
798 // navigation links will be registered in next patch set
799 }
800 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
801 }); // end association lambda
Ed Tanous36d52332023-06-09 13:18:40 -0700802
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400803 } // end Iterate over all retrieved ObjectPaths
Ed Tanous36d52332023-06-09 13:18:40 -0700804}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700805/**
806 * Chassis drives, this URL will show all the DriveCollection
807 * information
808 */
Nan Zhoub53dcd92022-06-21 17:47:50 +0000809inline void chassisDriveCollectionGet(
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700810 crow::App& app, const crow::Request& req,
811 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
812 const std::string& chassisId)
813{
Carson Labrado3ba00072022-06-06 19:40:56 +0000814 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700815 {
816 return;
817 }
818
819 // mapper call lambda
George Liue99073f2022-12-09 11:06:16 +0800820 constexpr std::array<std::string_view, 2> interfaces = {
821 "xyz.openbmc_project.Inventory.Item.Board",
822 "xyz.openbmc_project.Inventory.Item.Chassis"};
823 dbus::utility::getSubTree(
824 "/xyz/openbmc_project/inventory", 0, interfaces,
Ed Tanous36d52332023-06-09 13:18:40 -0700825 std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
826 chassisId));
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700827}
828
829inline void requestRoutesChassisDrive(App& app)
830{
831 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
832 .privileges(redfish::privileges::getDriveCollection)
833 .methods(boost::beast::http::verb::get)(
834 std::bind_front(chassisDriveCollectionGet, std::ref(app)));
835}
836
Nan Zhoub53dcd92022-06-21 17:47:50 +0000837inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
838 const std::string& chassisId,
839 const std::string& driveName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800840 const boost::system::error_code& ec,
Nan Zhoub53dcd92022-06-21 17:47:50 +0000841 const dbus::utility::MapperGetSubTreeResponse& subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700842{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700843 if (ec)
844 {
Ed Tanous62598e32023-07-17 17:06:25 -0700845 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700846 messages::internalError(asyncResp->res);
847 return;
848 }
849
850 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000851 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700852 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700853 sdbusplus::message::object_path objPath(path);
854 if (objPath.filename() != driveName)
855 {
856 continue;
857 }
858
859 if (connectionNames.empty())
860 {
Ed Tanous62598e32023-07-17 17:06:25 -0700861 BMCWEB_LOG_ERROR("Got 0 Connection names");
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700862 continue;
863 }
864
Ed Tanousef4c65b2023-04-24 15:28:50 -0700865 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
866 "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700867
868 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700869 asyncResp->res.jsonValue["Name"] = driveName;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700870 asyncResp->res.jsonValue["Id"] = driveName;
871 // default it to Enabled
Ed Tanous539d8c62024-06-19 14:38:27 -0700872 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700873
874 nlohmann::json::object_t linkChassisNav;
875 linkChassisNav["@odata.id"] =
Ed Tanousef4c65b2023-04-24 15:28:50 -0700876 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700877 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
878
879 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
880 connectionNames[0].second);
881 }
882}
883
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400884inline void matchAndFillDrive(
885 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
886 const std::string& chassisId, const std::string& driveName,
887 const std::vector<std::string>& resp)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700888{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700889 for (const std::string& drivePath : resp)
890 {
891 sdbusplus::message::object_path path(drivePath);
892 std::string leaf = path.filename();
893 if (leaf != driveName)
894 {
895 continue;
896 }
897 // mapper call drive
George Liue99073f2022-12-09 11:06:16 +0800898 constexpr std::array<std::string_view, 1> driveInterface = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700899 "xyz.openbmc_project.Inventory.Item.Drive"};
George Liue99073f2022-12-09 11:06:16 +0800900 dbus::utility::getSubTree(
901 "/xyz/openbmc_project/inventory", 0, driveInterface,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700902 [asyncResp, chassisId, driveName](
George Liue99073f2022-12-09 11:06:16 +0800903 const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700904 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400905 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
906 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700907 }
908}
909
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400910inline void handleChassisDriveGet(
911 crow::App& app, const crow::Request& req,
912 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
913 const std::string& chassisId, const std::string& driveName)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700914{
Michal Orzel03810a12022-06-15 14:04:28 +0200915 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700916 {
917 return;
918 }
George Liue99073f2022-12-09 11:06:16 +0800919 constexpr std::array<std::string_view, 2> interfaces = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700920 "xyz.openbmc_project.Inventory.Item.Board",
921 "xyz.openbmc_project.Inventory.Item.Chassis"};
922
923 // mapper call chassis
George Liue99073f2022-12-09 11:06:16 +0800924 dbus::utility::getSubTree(
925 "/xyz/openbmc_project/inventory", 0, interfaces,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700926 [asyncResp, chassisId,
George Liue99073f2022-12-09 11:06:16 +0800927 driveName](const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700928 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400929 if (ec)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700930 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400931 messages::internalError(asyncResp->res);
932 return;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700933 }
934
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400935 // Iterate over all retrieved ObjectPaths.
936 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700937 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400938 sdbusplus::message::object_path objPath(path);
939 if (objPath.filename() != chassisId)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700940 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400941 continue;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700942 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400943
944 if (connectionNames.empty())
945 {
946 BMCWEB_LOG_ERROR("Got 0 Connection names");
947 continue;
948 }
949
950 dbus::utility::getAssociationEndPoints(
951 path + "/drive",
952 [asyncResp, chassisId,
953 driveName](const boost::system::error_code& ec3,
954 const dbus::utility::MapperEndPoints& resp) {
955 if (ec3)
956 {
957 return; // no drives = no failures
958 }
959 matchAndFillDrive(asyncResp, chassisId, driveName,
960 resp);
961 });
962 break;
963 }
964 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700965}
966
967/**
968 * This URL will show the drive interface for the specific drive in the chassis
969 */
970inline void requestRoutesChassisDriveName(App& app)
971{
972 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
973 .privileges(redfish::privileges::getChassis)
974 .methods(boost::beast::http::verb::get)(
975 std::bind_front(handleChassisDriveGet, std::ref(app)));
976}
977
Willy Tu61b1eb22023-03-14 11:29:50 -0700978inline void getStorageControllerAsset(
979 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
980 const boost::system::error_code& ec,
981 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
982 propertiesList)
983{
984 if (ec)
985 {
986 // this interface isn't necessary
Ed Tanous62598e32023-07-17 17:06:25 -0700987 BMCWEB_LOG_DEBUG("Failed to get StorageControllerAsset");
Willy Tu61b1eb22023-03-14 11:29:50 -0700988 return;
989 }
990
991 const std::string* partNumber = nullptr;
992 const std::string* serialNumber = nullptr;
993 const std::string* manufacturer = nullptr;
994 const std::string* model = nullptr;
995 if (!sdbusplus::unpackPropertiesNoThrow(
996 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
997 partNumber, "SerialNumber", serialNumber, "Manufacturer",
998 manufacturer, "Model", model))
999 {
1000 messages::internalError(asyncResp->res);
1001 return;
1002 }
1003
1004 if (partNumber != nullptr)
1005 {
1006 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
1007 }
1008
1009 if (serialNumber != nullptr)
1010 {
1011 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
1012 }
1013
1014 if (manufacturer != nullptr)
1015 {
1016 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
1017 }
1018
1019 if (model != nullptr)
1020 {
1021 asyncResp->res.jsonValue["Model"] = *model;
1022 }
1023}
1024
1025inline void populateStorageController(
1026 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1027 const std::string& controllerId, const std::string& connectionName,
1028 const std::string& path)
1029{
1030 asyncResp->res.jsonValue["@odata.type"] =
1031 "#StorageController.v1_6_0.StorageController";
Ed Tanous253f11b2024-05-16 09:38:31 -07001032 asyncResp->res.jsonValue["@odata.id"] =
1033 boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1034 BMCWEB_REDFISH_SYSTEM_URI_NAME, controllerId);
Willy Tu61b1eb22023-03-14 11:29:50 -07001035 asyncResp->res.jsonValue["Name"] = controllerId;
1036 asyncResp->res.jsonValue["Id"] = controllerId;
Ed Tanous539d8c62024-06-19 14:38:27 -07001037 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
Willy Tu61b1eb22023-03-14 11:29:50 -07001038
1039 sdbusplus::asio::getProperty<bool>(
1040 *crow::connections::systemBus, connectionName, path,
1041 "xyz.openbmc_project.Inventory.Item", "Present",
1042 [asyncResp](const boost::system::error_code& ec, bool isPresent) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001043 // this interface isn't necessary, only check it
1044 // if we get a good return
1045 if (ec)
1046 {
1047 BMCWEB_LOG_DEBUG("Failed to get Present property");
1048 return;
1049 }
1050 if (!isPresent)
1051 {
1052 asyncResp->res.jsonValue["Status"]["State"] =
1053 resource::State::Absent;
1054 }
1055 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001056
1057 sdbusplus::asio::getAllProperties(
1058 *crow::connections::systemBus, connectionName, path,
1059 "xyz.openbmc_project.Inventory.Decorator.Asset",
1060 [asyncResp](const boost::system::error_code& ec,
1061 const std::vector<
1062 std::pair<std::string, dbus::utility::DbusVariantType>>&
1063 propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001064 getStorageControllerAsset(asyncResp, ec, propertiesList);
1065 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001066}
1067
1068inline void getStorageControllerHandler(
1069 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1070 const std::string& controllerId, const boost::system::error_code& ec,
1071 const dbus::utility::MapperGetSubTreeResponse& subtree)
1072{
1073 if (ec || subtree.empty())
1074 {
1075 // doesn't have to be there
Ed Tanous62598e32023-07-17 17:06:25 -07001076 BMCWEB_LOG_DEBUG("Failed to handle StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001077 return;
1078 }
1079
1080 for (const auto& [path, interfaceDict] : subtree)
1081 {
1082 sdbusplus::message::object_path object(path);
1083 std::string id = object.filename();
1084 if (id.empty())
1085 {
Ed Tanous62598e32023-07-17 17:06:25 -07001086 BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
Willy Tu61b1eb22023-03-14 11:29:50 -07001087 return;
1088 }
1089 if (id != controllerId)
1090 {
1091 continue;
1092 }
1093
1094 if (interfaceDict.size() != 1)
1095 {
Ed Tanous62598e32023-07-17 17:06:25 -07001096 BMCWEB_LOG_ERROR("Connection size {}, greater than 1",
1097 interfaceDict.size());
Willy Tu61b1eb22023-03-14 11:29:50 -07001098 messages::internalError(asyncResp->res);
1099 return;
1100 }
1101
1102 const std::string& connectionName = interfaceDict.front().first;
1103 populateStorageController(asyncResp, controllerId, connectionName,
1104 path);
1105 }
1106}
1107
1108inline void populateStorageControllerCollection(
1109 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1110 const boost::system::error_code& ec,
1111 const dbus::utility::MapperGetSubTreePathsResponse& controllerList)
1112{
1113 nlohmann::json::array_t members;
1114 if (ec || controllerList.empty())
1115 {
1116 asyncResp->res.jsonValue["Members"] = std::move(members);
1117 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous62598e32023-07-17 17:06:25 -07001118 BMCWEB_LOG_DEBUG("Failed to find any StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001119 return;
1120 }
1121
1122 for (const std::string& path : controllerList)
1123 {
1124 std::string id = sdbusplus::message::object_path(path).filename();
1125 if (id.empty())
1126 {
Ed Tanous62598e32023-07-17 17:06:25 -07001127 BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
Willy Tu61b1eb22023-03-14 11:29:50 -07001128 return;
1129 }
1130 nlohmann::json::object_t member;
1131 member["@odata.id"] = boost::urls::format(
Ed Tanous253f11b2024-05-16 09:38:31 -07001132 "/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1133 BMCWEB_REDFISH_SYSTEM_URI_NAME, id);
Willy Tu61b1eb22023-03-14 11:29:50 -07001134 members.emplace_back(member);
1135 }
1136 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
1137 asyncResp->res.jsonValue["Members"] = std::move(members);
1138}
1139
Ed Tanous36d52332023-06-09 13:18:40 -07001140inline void handleSystemsStorageControllerCollectionGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001141 App& app, const crow::Request& req,
1142 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1143 const std::string& systemName)
1144{
1145 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1146 {
Ed Tanous62598e32023-07-17 17:06:25 -07001147 BMCWEB_LOG_DEBUG(
1148 "Failed to setup Redfish Route for StorageController Collection");
Willy Tu61b1eb22023-03-14 11:29:50 -07001149 return;
1150 }
Ed Tanous253f11b2024-05-16 09:38:31 -07001151 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
Willy Tu61b1eb22023-03-14 11:29:50 -07001152 {
1153 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1154 systemName);
Ed Tanous62598e32023-07-17 17:06:25 -07001155 BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
Willy Tu61b1eb22023-03-14 11:29:50 -07001156 return;
1157 }
1158
1159 asyncResp->res.jsonValue["@odata.type"] =
1160 "#StorageControllerCollection.StorageControllerCollection";
1161 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous253f11b2024-05-16 09:38:31 -07001162 std::format("/redfish/v1/Systems/{}/Storage/1/Controllers",
1163 BMCWEB_REDFISH_SYSTEM_URI_NAME);
Willy Tu61b1eb22023-03-14 11:29:50 -07001164 asyncResp->res.jsonValue["Name"] = "Storage Controller Collection";
1165
1166 constexpr std::array<std::string_view, 1> interfaces = {
1167 "xyz.openbmc_project.Inventory.Item.StorageController"};
1168 dbus::utility::getSubTreePaths(
1169 "/xyz/openbmc_project/inventory", 0, interfaces,
1170 [asyncResp](const boost::system::error_code& ec,
1171 const dbus::utility::MapperGetSubTreePathsResponse&
1172 controllerList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001173 populateStorageControllerCollection(asyncResp, ec, controllerList);
1174 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001175}
1176
Ed Tanous36d52332023-06-09 13:18:40 -07001177inline void handleSystemsStorageControllerGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001178 App& app, const crow::Request& req,
1179 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1180 const std::string& systemName, const std::string& controllerId)
1181{
1182 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1183 {
Ed Tanous62598e32023-07-17 17:06:25 -07001184 BMCWEB_LOG_DEBUG("Failed to setup Redfish Route for StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001185 return;
1186 }
Ed Tanous253f11b2024-05-16 09:38:31 -07001187 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
Willy Tu61b1eb22023-03-14 11:29:50 -07001188 {
1189 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1190 systemName);
Ed Tanous62598e32023-07-17 17:06:25 -07001191 BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
Willy Tu61b1eb22023-03-14 11:29:50 -07001192 return;
1193 }
1194 constexpr std::array<std::string_view, 1> interfaces = {
1195 "xyz.openbmc_project.Inventory.Item.StorageController"};
1196 dbus::utility::getSubTree(
1197 "/xyz/openbmc_project/inventory", 0, interfaces,
1198 [asyncResp,
1199 controllerId](const boost::system::error_code& ec,
1200 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001201 getStorageControllerHandler(asyncResp, controllerId, ec, subtree);
1202 });
Willy Tu61b1eb22023-03-14 11:29:50 -07001203}
1204
1205inline void requestRoutesStorageControllerCollection(App& app)
1206{
1207 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/")
1208 .privileges(redfish::privileges::getStorageControllerCollection)
Ed Tanous36d52332023-06-09 13:18:40 -07001209 .methods(boost::beast::http::verb::get)(std::bind_front(
1210 handleSystemsStorageControllerCollectionGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001211}
1212
1213inline void requestRoutesStorageController(App& app)
1214{
1215 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>")
1216 .privileges(redfish::privileges::getStorageController)
1217 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -07001218 std::bind_front(handleSystemsStorageControllerGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001219}
1220
Nikhil Potadea25aecc2019-08-23 16:35:26 -07001221} // namespace redfish