blob: 389a20b953a90d1b9ce55a67aeda2f15d0c6c87a [file] [log] [blame]
Nikhil Potadea25aecc2019-08-23 16:35:26 -07001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
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"
James Feist2ad9c2f2019-10-29 16:26:48 -070024#include "health.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080025#include "human_sort.hpp"
James Feiste284a7c2019-11-20 16:20:23 -080026#include "openbmc_dbus_rest.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080027#include "query.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080028#include "redfish_util.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080029#include "registries/privilege_registry.hpp"
Willy Tu5e577bc2022-07-26 00:41:55 +000030#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080031#include "utils/dbus_utils.hpp"
James Feist2ad9c2f2019-10-29 16:26:48 -070032
George Liue99073f2022-12-09 11:06:16 +080033#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070034#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070035#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020036#include <sdbusplus/unpack_properties.hpp>
Nikhil Potadea25aecc2019-08-23 16:35:26 -070037
George Liu7a1dbc42022-12-07 16:03:22 +080038#include <array>
Ed Tanous3544d2a2023-08-06 18:12:20 -070039#include <ranges>
George Liu7a1dbc42022-12-07 16:03:22 +080040#include <string_view>
41
Nikhil Potadea25aecc2019-08-23 16:35:26 -070042namespace redfish
43{
Ed Tanous36d52332023-06-09 13:18:40 -070044
45inline void handleSystemsStorageCollectionGet(
46 App& app, const crow::Request& req,
47 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
48 const std::string& systemName)
49{
50 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
51 {
52 return;
53 }
54 if (systemName != "system")
55 {
56 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
57 systemName);
58 return;
59 }
60
61 asyncResp->res.jsonValue["@odata.type"] =
62 "#StorageCollection.StorageCollection";
63 asyncResp->res.jsonValue["@odata.id"] =
64 "/redfish/v1/Systems/system/Storage";
65 asyncResp->res.jsonValue["Name"] = "Storage Collection";
Willy Tu5e577bc2022-07-26 00:41:55 +000066
67 constexpr std::array<std::string_view, 1> interface {
68 "xyz.openbmc_project.Inventory.Item.Storage"
69 };
70 collection_util::getCollectionMembers(
71 asyncResp, boost::urls::format("/redfish/v1/Systems/system/Storage"),
72 interface);
73}
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";
87 constexpr std::array<std::string_view, 1> interface {
88 "xyz.openbmc_project.Inventory.Item.Storage"
89 };
90 collection_util::getCollectionMembers(
91 asyncResp, boost::urls::format("/redfish/v1/Storage"), interface);
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,
108 const std::shared_ptr<HealthPopulate>& health,
109 const boost::system::error_code& ec,
110 const dbus::utility::MapperGetSubTreePathsResponse& driveList)
111{
112 if (ec)
113 {
Ed Tanous62598e32023-07-17 17:06:25 -0700114 BMCWEB_LOG_ERROR("Drive mapper call error");
Ed Tanous36d52332023-06-09 13:18:40 -0700115 messages::internalError(asyncResp->res);
116 return;
117 }
118
119 nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
120 driveArray = nlohmann::json::array();
121 auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
122 count = 0;
123
124 if constexpr (bmcwebEnableHealthPopulate)
125 {
126 health->inventory.insert(health->inventory.end(), driveList.begin(),
127 driveList.end());
128 }
129
130 for (const std::string& drive : driveList)
131 {
132 sdbusplus::message::object_path object(drive);
133 if (object.filename().empty())
134 {
Ed Tanous62598e32023-07-17 17:06:25 -0700135 BMCWEB_LOG_ERROR("Failed to find filename in {}", drive);
Ed Tanous36d52332023-06-09 13:18:40 -0700136 return;
137 }
138
139 nlohmann::json::object_t driveJson;
140 driveJson["@odata.id"] = boost::urls::format(
141 "/redfish/v1/Systems/system/Storage/1/Drives/{}",
142 object.filename());
143 driveArray.emplace_back(std::move(driveJson));
144 }
145
146 count = driveArray.size();
147}
Willy Tua85afbe2021-12-28 14:43:47 -0800148inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
149 const std::shared_ptr<HealthPopulate>& health)
150{
George Liu7a1dbc42022-12-07 16:03:22 +0800151 const std::array<std::string_view, 1> interfaces = {
152 "xyz.openbmc_project.Inventory.Item.Drive"};
153 dbus::utility::getSubTreePaths(
154 "/xyz/openbmc_project/inventory", 0, interfaces,
Ed Tanous36d52332023-06-09 13:18:40 -0700155 std::bind_front(afterChassisDriveCollectionSubtree, asyncResp, health));
156}
Willy Tua85afbe2021-12-28 14:43:47 -0800157
Willy Tu5e577bc2022-07-26 00:41:55 +0000158inline void afterSystemsStorageGetSubtree(
159 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
160 const std::string& storageId, const boost::system::error_code& ec,
161 const dbus::utility::MapperGetSubTreeResponse& subtree)
Ed Tanous36d52332023-06-09 13:18:40 -0700162{
Willy Tu5e577bc2022-07-26 00:41:55 +0000163 if (ec)
Ed Tanous36d52332023-06-09 13:18:40 -0700164 {
Ed Tanous62598e32023-07-17 17:06:25 -0700165 BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
Willy Tu5e577bc2022-07-26 00:41:55 +0000166 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
167 storageId);
Ed Tanous36d52332023-06-09 13:18:40 -0700168 return;
169 }
Ed Tanous3544d2a2023-08-06 18:12:20 -0700170 auto storage = std::ranges::find_if(
171 subtree,
Willy Tu5e577bc2022-07-26 00:41:55 +0000172 [&storageId](const std::pair<std::string,
173 dbus::utility::MapperServiceMap>& object) {
174 return sdbusplus::message::object_path(object.first).filename() ==
175 storageId;
176 });
177 if (storage == subtree.end())
178 {
179 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
180 storageId);
181 return;
182 }
183
Ed Tanous36d52332023-06-09 13:18:40 -0700184 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
185 asyncResp->res.jsonValue["@odata.id"] =
Willy Tu5e577bc2022-07-26 00:41:55 +0000186 boost::urls::format("/redfish/v1/Systems/system/Storage/{}", storageId);
Ed Tanous36d52332023-06-09 13:18:40 -0700187 asyncResp->res.jsonValue["Name"] = "Storage";
Willy Tu5e577bc2022-07-26 00:41:55 +0000188 asyncResp->res.jsonValue["Id"] = storageId;
Ed Tanous36d52332023-06-09 13:18:40 -0700189 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
Willy Tua85afbe2021-12-28 14:43:47 -0800190
Ed Tanous36d52332023-06-09 13:18:40 -0700191 auto health = std::make_shared<HealthPopulate>(asyncResp);
192 if constexpr (bmcwebEnableHealthPopulate)
193 {
194 health->populate();
195 }
Willy Tua85afbe2021-12-28 14:43:47 -0800196
Ed Tanous36d52332023-06-09 13:18:40 -0700197 getDrives(asyncResp, health);
Willy Tu5e577bc2022-07-26 00:41:55 +0000198 asyncResp->res.jsonValue["Controllers"]["@odata.id"] = boost::urls::format(
199 "/redfish/v1/Systems/system/Storage/{}/Controllers", storageId);
200}
201
202inline void
203 handleSystemsStorageGet(App& app, const crow::Request& req,
204 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800205 const std::string& systemName,
Willy Tu5e577bc2022-07-26 00:41:55 +0000206 const std::string& storageId)
207{
208 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
209 {
210 return;
211 }
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800212 if constexpr (bmcwebEnableMultiHost)
213 {
214 // Option currently returns no systems. TBD
215 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
216 systemName);
217 return;
218 }
Willy Tu5e577bc2022-07-26 00:41:55 +0000219
220 constexpr std::array<std::string_view, 1> interfaces = {
221 "xyz.openbmc_project.Inventory.Item.Storage"};
222 dbus::utility::getSubTree(
223 "/xyz/openbmc_project/inventory", 0, interfaces,
224 std::bind_front(afterSystemsStorageGetSubtree, asyncResp, storageId));
225}
226
227inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
228 const std::string& storageId,
229 const boost::system::error_code& ec,
230 const dbus::utility::MapperGetSubTreeResponse& subtree)
231{
232 if (ec)
233 {
Ed Tanous62598e32023-07-17 17:06:25 -0700234 BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
Willy Tu5e577bc2022-07-26 00:41:55 +0000235 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
236 storageId);
237 return;
238 }
Ed Tanous3544d2a2023-08-06 18:12:20 -0700239 auto storage = std::ranges::find_if(
240 subtree,
Willy Tu5e577bc2022-07-26 00:41:55 +0000241 [&storageId](const std::pair<std::string,
242 dbus::utility::MapperServiceMap>& object) {
243 return sdbusplus::message::object_path(object.first).filename() ==
244 storageId;
245 });
246 if (storage == subtree.end())
247 {
248 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
249 storageId);
250 return;
251 }
252
253 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
254 asyncResp->res.jsonValue["@odata.id"] =
255 boost::urls::format("/redfish/v1/Storage/{}", storageId);
256 asyncResp->res.jsonValue["Name"] = "Storage";
257 asyncResp->res.jsonValue["Id"] = storageId;
258 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
259
260 // Storage subsystem to Storage link.
261 nlohmann::json::array_t storageServices;
262 nlohmann::json::object_t storageService;
263 storageService["@odata.id"] =
264 boost::urls::format("/redfish/v1/Systems/system/Storage/{}", storageId);
265 storageServices.emplace_back(storageService);
266 asyncResp->res.jsonValue["Links"]["StorageServices"] =
267 std::move(storageServices);
268 asyncResp->res.jsonValue["Links"]["StorageServices@odata.count"] = 1;
269}
270
271inline void
272 handleStorageGet(App& app, const crow::Request& req,
273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
274 const std::string& storageId)
275{
276 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
277 {
Ed Tanous62598e32023-07-17 17:06:25 -0700278 BMCWEB_LOG_DEBUG("requestRoutesStorage setUpRedfishRoute failed");
Willy Tu5e577bc2022-07-26 00:41:55 +0000279 return;
280 }
281
282 constexpr std::array<std::string_view, 1> interfaces = {
283 "xyz.openbmc_project.Inventory.Item.Storage"};
284 dbus::utility::getSubTree(
285 "/xyz/openbmc_project/inventory", 0, interfaces,
286 std::bind_front(afterSubtree, asyncResp, storageId));
Willy Tua85afbe2021-12-28 14:43:47 -0800287}
288
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700289inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700290{
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800291 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700292 .privileges(redfish::privileges::getStorage)
Ed Tanous002d39b2022-05-31 08:59:27 -0700293 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -0700294 std::bind_front(handleSystemsStorageGet, std::ref(app)));
Willy Tu5e577bc2022-07-26 00:41:55 +0000295
296 BMCWEB_ROUTE(app, "/redfish/v1/Storage/<str>/")
297 .privileges(redfish::privileges::getStorage)
298 .methods(boost::beast::http::verb::get)(
299 std::bind_front(handleStorageGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700300}
301
Willy Tu03913172021-11-08 02:03:19 -0800302inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
303 const std::string& connectionName,
304 const std::string& path)
305{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200306 sdbusplus::asio::getAllProperties(
307 *crow::connections::systemBus, connectionName, path,
308 "xyz.openbmc_project.Inventory.Decorator.Asset",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800309 [asyncResp](const boost::system::error_code& ec,
Ed Tanous168e20c2021-12-13 14:39:53 -0800310 const std::vector<
311 std::pair<std::string, dbus::utility::DbusVariantType>>&
312 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700313 if (ec)
314 {
315 // this interface isn't necessary
316 return;
317 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200318
319 const std::string* partNumber = nullptr;
320 const std::string* serialNumber = nullptr;
321 const std::string* manufacturer = nullptr;
322 const std::string* model = nullptr;
323
324 const bool success = sdbusplus::unpackPropertiesNoThrow(
325 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
326 partNumber, "SerialNumber", serialNumber, "Manufacturer",
327 manufacturer, "Model", model);
328
329 if (!success)
Ed Tanous002d39b2022-05-31 08:59:27 -0700330 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200331 messages::internalError(asyncResp->res);
332 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700333 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200334
335 if (partNumber != nullptr)
336 {
337 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
338 }
339
340 if (serialNumber != nullptr)
341 {
342 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
343 }
344
345 if (manufacturer != nullptr)
346 {
347 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
348 }
349
350 if (model != nullptr)
351 {
352 asyncResp->res.jsonValue["Model"] = *model;
353 }
354 });
Willy Tu03913172021-11-08 02:03:19 -0800355}
356
357inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
358 const std::string& connectionName,
359 const std::string& path)
360{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700361 sdbusplus::asio::getProperty<bool>(
362 *crow::connections::systemBus, connectionName, path,
363 "xyz.openbmc_project.Inventory.Item", "Present",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800364 [asyncResp, path](const boost::system::error_code& ec,
Willy Tucef57e82022-12-15 16:42:02 -0800365 const bool isPresent) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700366 // this interface isn't necessary, only check it if
367 // we get a good return
368 if (ec)
369 {
370 return;
371 }
Willy Tu03913172021-11-08 02:03:19 -0800372
Willy Tucef57e82022-12-15 16:42:02 -0800373 if (!isPresent)
Ed Tanous002d39b2022-05-31 08:59:27 -0700374 {
Willy Tucef57e82022-12-15 16:42:02 -0800375 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
Ed Tanous002d39b2022-05-31 08:59:27 -0700376 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700377 });
Willy Tu03913172021-11-08 02:03:19 -0800378}
379
380inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
381 const std::string& connectionName,
382 const std::string& path)
383{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700384 sdbusplus::asio::getProperty<bool>(
385 *crow::connections::systemBus, connectionName, path,
386 "xyz.openbmc_project.State.Drive", "Rebuilding",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800387 [asyncResp](const boost::system::error_code& ec, const bool updating) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700388 // this interface isn't necessary, only check it
389 // if we get a good return
390 if (ec)
391 {
392 return;
393 }
Willy Tu03913172021-11-08 02:03:19 -0800394
Ed Tanous002d39b2022-05-31 08:59:27 -0700395 // updating and disabled in the backend shouldn't be
396 // able to be set at the same time, so we don't need
397 // to check for the race condition of these two
398 // calls
399 if (updating)
400 {
401 asyncResp->res.jsonValue["Status"]["State"] = "Updating";
402 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700403 });
Willy Tu03913172021-11-08 02:03:19 -0800404}
405
George Liudde9bc12023-02-22 09:35:51 +0800406inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800407{
408 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
409 {
George Liudde9bc12023-02-22 09:35:51 +0800410 return drive::MediaType::HDD;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800411 }
412 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
413 {
George Liudde9bc12023-02-22 09:35:51 +0800414 return drive::MediaType::SSD;
415 }
416 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
417 {
418 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800419 }
420
George Liudde9bc12023-02-22 09:35:51 +0800421 return drive::MediaType::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800422}
423
George Liudde9bc12023-02-22 09:35:51 +0800424inline std::optional<protocol::Protocol>
425 convertDriveProtocol(std::string_view proto)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800426{
427 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
428 {
George Liudde9bc12023-02-22 09:35:51 +0800429 return protocol::Protocol::SAS;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800430 }
431 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
432 {
George Liudde9bc12023-02-22 09:35:51 +0800433 return protocol::Protocol::SATA;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800434 }
435 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
436 {
George Liudde9bc12023-02-22 09:35:51 +0800437 return protocol::Protocol::NVMe;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800438 }
439 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
440 {
George Liudde9bc12023-02-22 09:35:51 +0800441 return protocol::Protocol::FC;
442 }
443 if (proto ==
444 "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
445 {
446 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800447 }
448
George Liudde9bc12023-02-22 09:35:51 +0800449 return protocol::Protocol::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800450}
451
452inline void
453 getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
454 const std::string& connectionName,
455 const std::string& path)
456{
457 sdbusplus::asio::getAllProperties(
458 *crow::connections::systemBus, connectionName, path,
459 "xyz.openbmc_project.Inventory.Item.Drive",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800460 [asyncResp](const boost::system::error_code& ec,
Willy Tu19b8e9a2021-11-08 02:55:03 -0800461 const std::vector<
462 std::pair<std::string, dbus::utility::DbusVariantType>>&
463 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700464 if (ec)
465 {
466 // this interface isn't required
467 return;
468 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700469 const std::string* encryptionStatus = nullptr;
470 const bool* isLocked = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700471 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
472 property : propertiesList)
473 {
474 const std::string& propertyName = property.first;
475 if (propertyName == "Type")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800476 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700477 const std::string* value =
478 std::get_if<std::string>(&property.second);
479 if (value == nullptr)
480 {
481 // illegal property
Ed Tanous62598e32023-07-17 17:06:25 -0700482 BMCWEB_LOG_ERROR("Illegal property: Type");
Ed Tanous002d39b2022-05-31 08:59:27 -0700483 messages::internalError(asyncResp->res);
484 return;
485 }
486
George Liudde9bc12023-02-22 09:35:51 +0800487 std::optional<drive::MediaType> mediaType =
488 convertDriveType(*value);
Ed Tanous002d39b2022-05-31 08:59:27 -0700489 if (!mediaType)
490 {
Ed Tanous62598e32023-07-17 17:06:25 -0700491 BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}",
492 *value);
George Liudde9bc12023-02-22 09:35:51 +0800493 continue;
494 }
495 if (*mediaType == drive::MediaType::Invalid)
496 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700497 messages::internalError(asyncResp->res);
498 return;
499 }
500
501 asyncResp->res.jsonValue["MediaType"] = *mediaType;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800502 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700503 else if (propertyName == "Capacity")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800504 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700505 const uint64_t* capacity =
506 std::get_if<uint64_t>(&property.second);
507 if (capacity == nullptr)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800508 {
Ed Tanous62598e32023-07-17 17:06:25 -0700509 BMCWEB_LOG_ERROR("Illegal property: Capacity");
Ed Tanous002d39b2022-05-31 08:59:27 -0700510 messages::internalError(asyncResp->res);
511 return;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800512 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700513 if (*capacity == 0)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800514 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700515 // drive capacity not known
516 continue;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800517 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800518
Ed Tanous002d39b2022-05-31 08:59:27 -0700519 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800520 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700521 else if (propertyName == "Protocol")
522 {
523 const std::string* value =
524 std::get_if<std::string>(&property.second);
525 if (value == nullptr)
526 {
Ed Tanous62598e32023-07-17 17:06:25 -0700527 BMCWEB_LOG_ERROR("Illegal property: Protocol");
Ed Tanous002d39b2022-05-31 08:59:27 -0700528 messages::internalError(asyncResp->res);
529 return;
530 }
531
George Liudde9bc12023-02-22 09:35:51 +0800532 std::optional<protocol::Protocol> proto =
533 convertDriveProtocol(*value);
Ed Tanous002d39b2022-05-31 08:59:27 -0700534 if (!proto)
535 {
Ed Tanous62598e32023-07-17 17:06:25 -0700536 BMCWEB_LOG_WARNING("Unknown DrivePrototype Interface: {}",
537 *value);
George Liudde9bc12023-02-22 09:35:51 +0800538 continue;
539 }
540 if (*proto == protocol::Protocol::Invalid)
541 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700542 messages::internalError(asyncResp->res);
543 return;
544 }
545 asyncResp->res.jsonValue["Protocol"] = *proto;
546 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700547 else if (propertyName == "PredictedMediaLifeLeftPercent")
548 {
549 const uint8_t* lifeLeft =
550 std::get_if<uint8_t>(&property.second);
551 if (lifeLeft == nullptr)
552 {
Ed Tanous62598e32023-07-17 17:06:25 -0700553 BMCWEB_LOG_ERROR(
554 "Illegal property: PredictedMediaLifeLeftPercent");
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700555 messages::internalError(asyncResp->res);
556 return;
557 }
558 // 255 means reading the value is not supported
559 if (*lifeLeft != 255)
560 {
561 asyncResp->res.jsonValue["PredictedMediaLifeLeftPercent"] =
562 *lifeLeft;
563 }
564 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700565 else if (propertyName == "EncryptionStatus")
566 {
567 encryptionStatus = std::get_if<std::string>(&property.second);
568 if (encryptionStatus == nullptr)
569 {
Ed Tanous62598e32023-07-17 17:06:25 -0700570 BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus");
John Edward Broadbente5029d82022-06-08 14:35:21 -0700571 messages::internalError(asyncResp->res);
572 return;
573 }
574 }
575 else if (propertyName == "Locked")
576 {
577 isLocked = std::get_if<bool>(&property.second);
578 if (isLocked == nullptr)
579 {
Ed Tanous62598e32023-07-17 17:06:25 -0700580 BMCWEB_LOG_ERROR("Illegal property: Locked");
John Edward Broadbente5029d82022-06-08 14:35:21 -0700581 messages::internalError(asyncResp->res);
582 return;
583 }
584 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700585 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700586
587 if (encryptionStatus == nullptr || isLocked == nullptr ||
588 *encryptionStatus ==
589 "xyz.openbmc_project.Drive.DriveEncryptionState.Unknown")
590 {
591 return;
592 }
593 if (*encryptionStatus !=
594 "xyz.openbmc_project.Drive.DriveEncryptionState.Encrypted")
595 {
596 //"The drive is not currently encrypted."
597 asyncResp->res.jsonValue["EncryptionStatus"] =
598 drive::EncryptionStatus::Unencrypted;
599 return;
600 }
601 if (*isLocked)
602 {
603 //"The drive is currently encrypted and the data is not
604 // accessible to the user."
605 asyncResp->res.jsonValue["EncryptionStatus"] =
606 drive::EncryptionStatus::Locked;
607 return;
608 }
609 // if not locked
610 // "The drive is currently encrypted but the data is accessible
611 // to the user in unencrypted form."
612 asyncResp->res.jsonValue["EncryptionStatus"] =
613 drive::EncryptionStatus::Unlocked;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800614 });
615}
616
Nan Zhoub53dcd92022-06-21 17:47:50 +0000617static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
618 const std::string& connectionName,
619 const std::string& path,
620 const std::vector<std::string>& interfaces)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700621{
622 for (const std::string& interface : interfaces)
623 {
624 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
625 {
626 getDriveAsset(asyncResp, connectionName, path);
627 }
628 else if (interface == "xyz.openbmc_project.Inventory.Item")
629 {
630 getDrivePresent(asyncResp, connectionName, path);
631 }
632 else if (interface == "xyz.openbmc_project.State.Drive")
633 {
634 getDriveState(asyncResp, connectionName, path);
635 }
636 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
637 {
638 getDriveItemProperties(asyncResp, connectionName, path);
639 }
640 }
641}
642
Ed Tanous36d52332023-06-09 13:18:40 -0700643inline void afterGetSubtreeSystemsStorageDrive(
644 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
645 const std::string& driveId, const boost::system::error_code& ec,
646 const dbus::utility::MapperGetSubTreeResponse& subtree)
647{
648 if (ec)
649 {
Ed Tanous62598e32023-07-17 17:06:25 -0700650 BMCWEB_LOG_ERROR("Drive mapper call error");
Ed Tanous36d52332023-06-09 13:18:40 -0700651 messages::internalError(asyncResp->res);
652 return;
653 }
654
Ed Tanous3544d2a2023-08-06 18:12:20 -0700655 auto drive = std::ranges::find_if(
656 subtree,
Ed Tanous36d52332023-06-09 13:18:40 -0700657 [&driveId](const std::pair<std::string,
658 dbus::utility::MapperServiceMap>& object) {
659 return sdbusplus::message::object_path(object.first).filename() ==
660 driveId;
661 });
662
663 if (drive == subtree.end())
664 {
665 messages::resourceNotFound(asyncResp->res, "Drive", driveId);
666 return;
667 }
668
669 const std::string& path = drive->first;
670 const dbus::utility::MapperServiceMap& connectionNames = drive->second;
671
672 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
673 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
674 "/redfish/v1/Systems/system/Storage/1/Drives/{}", driveId);
675 asyncResp->res.jsonValue["Name"] = driveId;
676 asyncResp->res.jsonValue["Id"] = driveId;
677
678 if (connectionNames.size() != 1)
679 {
Ed Tanous62598e32023-07-17 17:06:25 -0700680 BMCWEB_LOG_ERROR("Connection size {}, not equal to 1",
681 connectionNames.size());
Ed Tanous36d52332023-06-09 13:18:40 -0700682 messages::internalError(asyncResp->res);
683 return;
684 }
685
686 getMainChassisId(asyncResp,
687 [](const std::string& chassisId,
688 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
689 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
690 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
691 });
692
693 // default it to Enabled
694 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
695
696 if constexpr (bmcwebEnableHealthPopulate)
697 {
698 auto health = std::make_shared<HealthPopulate>(asyncResp);
699 health->inventory.emplace_back(path);
700 health->populate();
701 }
702
703 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
704 connectionNames[0].second);
705}
706
707inline void handleSystemsStorageDriveGet(
708 App& app, const crow::Request& req,
709 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
710 const std::string& systemName, const std::string& driveId)
711{
712 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
713 {
714 return;
715 }
Ed Tanous7f3e84a2022-12-28 16:22:54 -0800716 if constexpr (bmcwebEnableMultiHost)
717 {
718 // Option currently returns no systems. TBD
719 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
720 systemName);
721 return;
722 }
723
Ed Tanous36d52332023-06-09 13:18:40 -0700724 if (systemName != "system")
725 {
726 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
727 systemName);
728 return;
729 }
730
731 constexpr std::array<std::string_view, 1> interfaces = {
732 "xyz.openbmc_project.Inventory.Item.Drive"};
733 dbus::utility::getSubTree(
734 "/xyz/openbmc_project/inventory", 0, interfaces,
735 std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
736 driveId));
737}
738
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700739inline void requestRoutesDrive(App& app)
740{
Ed Tanous22d268c2022-05-19 09:39:07 -0700741 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700742 .privileges(redfish::privileges::getDrive)
Ed Tanous002d39b2022-05-31 08:59:27 -0700743 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -0700744 std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700745}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700746
Ed Tanous36d52332023-06-09 13:18:40 -0700747inline void afterChassisDriveCollectionSubtreeGet(
748 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
749 const std::string& chassisId, const boost::system::error_code& ec,
750 const dbus::utility::MapperGetSubTreeResponse& subtree)
751{
752 if (ec)
753 {
754 if (ec == boost::system::errc::host_unreachable)
755 {
756 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
757 return;
758 }
759 messages::internalError(asyncResp->res);
760 return;
761 }
762
763 // Iterate over all retrieved ObjectPaths.
764 for (const auto& [path, connectionNames] : subtree)
765 {
766 sdbusplus::message::object_path objPath(path);
767 if (objPath.filename() != chassisId)
768 {
769 continue;
770 }
771
772 if (connectionNames.empty())
773 {
Ed Tanous62598e32023-07-17 17:06:25 -0700774 BMCWEB_LOG_ERROR("Got 0 Connection names");
Ed Tanous36d52332023-06-09 13:18:40 -0700775 continue;
776 }
777
778 asyncResp->res.jsonValue["@odata.type"] =
779 "#DriveCollection.DriveCollection";
780 asyncResp->res.jsonValue["@odata.id"] =
781 boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
782 asyncResp->res.jsonValue["Name"] = "Drive Collection";
783
784 // Association lambda
785 dbus::utility::getAssociationEndPoints(
786 path + "/drive",
787 [asyncResp, chassisId](const boost::system::error_code& ec3,
788 const dbus::utility::MapperEndPoints& resp) {
789 if (ec3)
790 {
Ed Tanous62598e32023-07-17 17:06:25 -0700791 BMCWEB_LOG_ERROR("Error in chassis Drive association ");
Ed Tanous36d52332023-06-09 13:18:40 -0700792 }
793 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
794 // important if array is empty
795 members = nlohmann::json::array();
796
797 std::vector<std::string> leafNames;
798 for (const auto& drive : resp)
799 {
800 sdbusplus::message::object_path drivePath(drive);
801 leafNames.push_back(drivePath.filename());
802 }
803
Ed Tanous3544d2a2023-08-06 18:12:20 -0700804 std::ranges::sort(leafNames, AlphanumLess<std::string>());
Ed Tanous36d52332023-06-09 13:18:40 -0700805
806 for (const auto& leafName : leafNames)
807 {
808 nlohmann::json::object_t member;
809 member["@odata.id"] = boost::urls::format(
810 "/redfish/v1/Chassis/{}/Drives/{}", chassisId, leafName);
811 members.emplace_back(std::move(member));
812 // navigation links will be registered in next patch set
813 }
814 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
815 }); // end association lambda
816
817 } // end Iterate over all retrieved ObjectPaths
818}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700819/**
820 * Chassis drives, this URL will show all the DriveCollection
821 * information
822 */
Nan Zhoub53dcd92022-06-21 17:47:50 +0000823inline void chassisDriveCollectionGet(
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700824 crow::App& app, const crow::Request& req,
825 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
826 const std::string& chassisId)
827{
Carson Labrado3ba00072022-06-06 19:40:56 +0000828 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700829 {
830 return;
831 }
832
833 // mapper call lambda
George Liue99073f2022-12-09 11:06:16 +0800834 constexpr std::array<std::string_view, 2> interfaces = {
835 "xyz.openbmc_project.Inventory.Item.Board",
836 "xyz.openbmc_project.Inventory.Item.Chassis"};
837 dbus::utility::getSubTree(
838 "/xyz/openbmc_project/inventory", 0, interfaces,
Ed Tanous36d52332023-06-09 13:18:40 -0700839 std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
840 chassisId));
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700841}
842
843inline void requestRoutesChassisDrive(App& app)
844{
845 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
846 .privileges(redfish::privileges::getDriveCollection)
847 .methods(boost::beast::http::verb::get)(
848 std::bind_front(chassisDriveCollectionGet, std::ref(app)));
849}
850
Nan Zhoub53dcd92022-06-21 17:47:50 +0000851inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
852 const std::string& chassisId,
853 const std::string& driveName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800854 const boost::system::error_code& ec,
Nan Zhoub53dcd92022-06-21 17:47:50 +0000855 const dbus::utility::MapperGetSubTreeResponse& subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700856{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700857 if (ec)
858 {
Ed Tanous62598e32023-07-17 17:06:25 -0700859 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700860 messages::internalError(asyncResp->res);
861 return;
862 }
863
864 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000865 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700866 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700867 sdbusplus::message::object_path objPath(path);
868 if (objPath.filename() != driveName)
869 {
870 continue;
871 }
872
873 if (connectionNames.empty())
874 {
Ed Tanous62598e32023-07-17 17:06:25 -0700875 BMCWEB_LOG_ERROR("Got 0 Connection names");
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700876 continue;
877 }
878
Ed Tanousef4c65b2023-04-24 15:28:50 -0700879 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
880 "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700881
882 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700883 asyncResp->res.jsonValue["Name"] = driveName;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700884 asyncResp->res.jsonValue["Id"] = driveName;
885 // default it to Enabled
886 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
887
888 nlohmann::json::object_t linkChassisNav;
889 linkChassisNav["@odata.id"] =
Ed Tanousef4c65b2023-04-24 15:28:50 -0700890 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700891 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
892
893 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
894 connectionNames[0].second);
895 }
896}
897
Nan Zhoub53dcd92022-06-21 17:47:50 +0000898inline void
899 matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
900 const std::string& chassisId,
901 const std::string& driveName,
902 const std::vector<std::string>& resp)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700903{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700904 for (const std::string& drivePath : resp)
905 {
906 sdbusplus::message::object_path path(drivePath);
907 std::string leaf = path.filename();
908 if (leaf != driveName)
909 {
910 continue;
911 }
912 // mapper call drive
George Liue99073f2022-12-09 11:06:16 +0800913 constexpr std::array<std::string_view, 1> driveInterface = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700914 "xyz.openbmc_project.Inventory.Item.Drive"};
George Liue99073f2022-12-09 11:06:16 +0800915 dbus::utility::getSubTree(
916 "/xyz/openbmc_project/inventory", 0, driveInterface,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700917 [asyncResp, chassisId, driveName](
George Liue99073f2022-12-09 11:06:16 +0800918 const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700919 const dbus::utility::MapperGetSubTreeResponse& subtree) {
920 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
George Liue99073f2022-12-09 11:06:16 +0800921 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700922 }
923}
924
Nan Zhoub53dcd92022-06-21 17:47:50 +0000925inline void
926 handleChassisDriveGet(crow::App& app, const crow::Request& req,
927 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
928 const std::string& chassisId,
929 const std::string& driveName)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700930{
Michal Orzel03810a12022-06-15 14:04:28 +0200931 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700932 {
933 return;
934 }
George Liue99073f2022-12-09 11:06:16 +0800935 constexpr std::array<std::string_view, 2> interfaces = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700936 "xyz.openbmc_project.Inventory.Item.Board",
937 "xyz.openbmc_project.Inventory.Item.Chassis"};
938
939 // mapper call chassis
George Liue99073f2022-12-09 11:06:16 +0800940 dbus::utility::getSubTree(
941 "/xyz/openbmc_project/inventory", 0, interfaces,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700942 [asyncResp, chassisId,
George Liue99073f2022-12-09 11:06:16 +0800943 driveName](const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700944 const dbus::utility::MapperGetSubTreeResponse& subtree) {
945 if (ec)
946 {
947 messages::internalError(asyncResp->res);
948 return;
949 }
950
951 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000952 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700953 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700954 sdbusplus::message::object_path objPath(path);
955 if (objPath.filename() != chassisId)
956 {
957 continue;
958 }
959
960 if (connectionNames.empty())
961 {
Ed Tanous62598e32023-07-17 17:06:25 -0700962 BMCWEB_LOG_ERROR("Got 0 Connection names");
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700963 continue;
964 }
965
George Liu6c3e9452023-03-03 13:55:29 +0800966 dbus::utility::getAssociationEndPoints(
967 path + "/drive",
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700968 [asyncResp, chassisId,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800969 driveName](const boost::system::error_code& ec3,
George Liu6c3e9452023-03-03 13:55:29 +0800970 const dbus::utility::MapperEndPoints& resp) {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700971 if (ec3)
972 {
973 return; // no drives = no failures
974 }
975 matchAndFillDrive(asyncResp, chassisId, driveName, resp);
976 });
977 break;
978 }
George Liue99073f2022-12-09 11:06:16 +0800979 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700980}
981
982/**
983 * This URL will show the drive interface for the specific drive in the chassis
984 */
985inline void requestRoutesChassisDriveName(App& app)
986{
987 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
988 .privileges(redfish::privileges::getChassis)
989 .methods(boost::beast::http::verb::get)(
990 std::bind_front(handleChassisDriveGet, std::ref(app)));
991}
992
Willy Tu61b1eb22023-03-14 11:29:50 -0700993inline void getStorageControllerAsset(
994 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
995 const boost::system::error_code& ec,
996 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
997 propertiesList)
998{
999 if (ec)
1000 {
1001 // this interface isn't necessary
Ed Tanous62598e32023-07-17 17:06:25 -07001002 BMCWEB_LOG_DEBUG("Failed to get StorageControllerAsset");
Willy Tu61b1eb22023-03-14 11:29:50 -07001003 return;
1004 }
1005
1006 const std::string* partNumber = nullptr;
1007 const std::string* serialNumber = nullptr;
1008 const std::string* manufacturer = nullptr;
1009 const std::string* model = nullptr;
1010 if (!sdbusplus::unpackPropertiesNoThrow(
1011 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
1012 partNumber, "SerialNumber", serialNumber, "Manufacturer",
1013 manufacturer, "Model", model))
1014 {
1015 messages::internalError(asyncResp->res);
1016 return;
1017 }
1018
1019 if (partNumber != nullptr)
1020 {
1021 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
1022 }
1023
1024 if (serialNumber != nullptr)
1025 {
1026 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
1027 }
1028
1029 if (manufacturer != nullptr)
1030 {
1031 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
1032 }
1033
1034 if (model != nullptr)
1035 {
1036 asyncResp->res.jsonValue["Model"] = *model;
1037 }
1038}
1039
1040inline void populateStorageController(
1041 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1042 const std::string& controllerId, const std::string& connectionName,
1043 const std::string& path)
1044{
1045 asyncResp->res.jsonValue["@odata.type"] =
1046 "#StorageController.v1_6_0.StorageController";
1047 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1048 "/redfish/v1/Systems/system/Storage/1/Controllers/{}", controllerId);
1049 asyncResp->res.jsonValue["Name"] = controllerId;
1050 asyncResp->res.jsonValue["Id"] = controllerId;
1051 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
1052
1053 sdbusplus::asio::getProperty<bool>(
1054 *crow::connections::systemBus, connectionName, path,
1055 "xyz.openbmc_project.Inventory.Item", "Present",
1056 [asyncResp](const boost::system::error_code& ec, bool isPresent) {
1057 // this interface isn't necessary, only check it
1058 // if we get a good return
1059 if (ec)
1060 {
Ed Tanous62598e32023-07-17 17:06:25 -07001061 BMCWEB_LOG_DEBUG("Failed to get Present property");
Willy Tu61b1eb22023-03-14 11:29:50 -07001062 return;
1063 }
1064 if (!isPresent)
1065 {
1066 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
1067 }
1068 });
1069
1070 sdbusplus::asio::getAllProperties(
1071 *crow::connections::systemBus, connectionName, path,
1072 "xyz.openbmc_project.Inventory.Decorator.Asset",
1073 [asyncResp](const boost::system::error_code& ec,
1074 const std::vector<
1075 std::pair<std::string, dbus::utility::DbusVariantType>>&
1076 propertiesList) {
1077 getStorageControllerAsset(asyncResp, ec, propertiesList);
1078 });
1079}
1080
1081inline void getStorageControllerHandler(
1082 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1083 const std::string& controllerId, const boost::system::error_code& ec,
1084 const dbus::utility::MapperGetSubTreeResponse& subtree)
1085{
1086 if (ec || subtree.empty())
1087 {
1088 // doesn't have to be there
Ed Tanous62598e32023-07-17 17:06:25 -07001089 BMCWEB_LOG_DEBUG("Failed to handle StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001090 return;
1091 }
1092
1093 for (const auto& [path, interfaceDict] : subtree)
1094 {
1095 sdbusplus::message::object_path object(path);
1096 std::string id = object.filename();
1097 if (id.empty())
1098 {
Ed Tanous62598e32023-07-17 17:06:25 -07001099 BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
Willy Tu61b1eb22023-03-14 11:29:50 -07001100 return;
1101 }
1102 if (id != controllerId)
1103 {
1104 continue;
1105 }
1106
1107 if (interfaceDict.size() != 1)
1108 {
Ed Tanous62598e32023-07-17 17:06:25 -07001109 BMCWEB_LOG_ERROR("Connection size {}, greater than 1",
1110 interfaceDict.size());
Willy Tu61b1eb22023-03-14 11:29:50 -07001111 messages::internalError(asyncResp->res);
1112 return;
1113 }
1114
1115 const std::string& connectionName = interfaceDict.front().first;
1116 populateStorageController(asyncResp, controllerId, connectionName,
1117 path);
1118 }
1119}
1120
1121inline void populateStorageControllerCollection(
1122 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1123 const boost::system::error_code& ec,
1124 const dbus::utility::MapperGetSubTreePathsResponse& controllerList)
1125{
1126 nlohmann::json::array_t members;
1127 if (ec || controllerList.empty())
1128 {
1129 asyncResp->res.jsonValue["Members"] = std::move(members);
1130 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous62598e32023-07-17 17:06:25 -07001131 BMCWEB_LOG_DEBUG("Failed to find any StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001132 return;
1133 }
1134
1135 for (const std::string& path : controllerList)
1136 {
1137 std::string id = sdbusplus::message::object_path(path).filename();
1138 if (id.empty())
1139 {
Ed Tanous62598e32023-07-17 17:06:25 -07001140 BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
Willy Tu61b1eb22023-03-14 11:29:50 -07001141 return;
1142 }
1143 nlohmann::json::object_t member;
1144 member["@odata.id"] = boost::urls::format(
1145 "/redfish/v1/Systems/system/Storage/1/Controllers/{}", id);
1146 members.emplace_back(member);
1147 }
1148 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
1149 asyncResp->res.jsonValue["Members"] = std::move(members);
1150}
1151
Ed Tanous36d52332023-06-09 13:18:40 -07001152inline void handleSystemsStorageControllerCollectionGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001153 App& app, const crow::Request& req,
1154 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1155 const std::string& systemName)
1156{
1157 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1158 {
Ed Tanous62598e32023-07-17 17:06:25 -07001159 BMCWEB_LOG_DEBUG(
1160 "Failed to setup Redfish Route for StorageController Collection");
Willy Tu61b1eb22023-03-14 11:29:50 -07001161 return;
1162 }
1163 if (systemName != "system")
1164 {
1165 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1166 systemName);
Ed Tanous62598e32023-07-17 17:06:25 -07001167 BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
Willy Tu61b1eb22023-03-14 11:29:50 -07001168 return;
1169 }
1170
1171 asyncResp->res.jsonValue["@odata.type"] =
1172 "#StorageControllerCollection.StorageControllerCollection";
1173 asyncResp->res.jsonValue["@odata.id"] =
1174 "/redfish/v1/Systems/system/Storage/1/Controllers";
1175 asyncResp->res.jsonValue["Name"] = "Storage Controller Collection";
1176
1177 constexpr std::array<std::string_view, 1> interfaces = {
1178 "xyz.openbmc_project.Inventory.Item.StorageController"};
1179 dbus::utility::getSubTreePaths(
1180 "/xyz/openbmc_project/inventory", 0, interfaces,
1181 [asyncResp](const boost::system::error_code& ec,
1182 const dbus::utility::MapperGetSubTreePathsResponse&
1183 controllerList) {
1184 populateStorageControllerCollection(asyncResp, ec, controllerList);
1185 });
1186}
1187
Ed Tanous36d52332023-06-09 13:18:40 -07001188inline void handleSystemsStorageControllerGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001189 App& app, const crow::Request& req,
1190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1191 const std::string& systemName, const std::string& controllerId)
1192{
1193 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1194 {
Ed Tanous62598e32023-07-17 17:06:25 -07001195 BMCWEB_LOG_DEBUG("Failed to setup Redfish Route for StorageController");
Willy Tu61b1eb22023-03-14 11:29:50 -07001196 return;
1197 }
1198 if (systemName != "system")
1199 {
1200 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1201 systemName);
Ed Tanous62598e32023-07-17 17:06:25 -07001202 BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
Willy Tu61b1eb22023-03-14 11:29:50 -07001203 return;
1204 }
1205 constexpr std::array<std::string_view, 1> interfaces = {
1206 "xyz.openbmc_project.Inventory.Item.StorageController"};
1207 dbus::utility::getSubTree(
1208 "/xyz/openbmc_project/inventory", 0, interfaces,
1209 [asyncResp,
1210 controllerId](const boost::system::error_code& ec,
1211 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1212 getStorageControllerHandler(asyncResp, controllerId, ec, subtree);
1213 });
1214}
1215
1216inline void requestRoutesStorageControllerCollection(App& app)
1217{
1218 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/")
1219 .privileges(redfish::privileges::getStorageControllerCollection)
Ed Tanous36d52332023-06-09 13:18:40 -07001220 .methods(boost::beast::http::verb::get)(std::bind_front(
1221 handleSystemsStorageControllerCollectionGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001222}
1223
1224inline void requestRoutesStorageController(App& app)
1225{
1226 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>")
1227 .privileges(redfish::privileges::getStorageController)
1228 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -07001229 std::bind_front(handleSystemsStorageControllerGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001230}
1231
Nikhil Potadea25aecc2019-08-23 16:35:26 -07001232} // namespace redfish