blob: fce53f41a64f8aacc20429f4cc6df8a2e990ef53 [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>
39#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 }
53 if (systemName != "system")
54 {
55 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
56 systemName);
57 return;
58 }
59
60 asyncResp->res.jsonValue["@odata.type"] =
61 "#StorageCollection.StorageCollection";
62 asyncResp->res.jsonValue["@odata.id"] =
63 "/redfish/v1/Systems/system/Storage";
64 asyncResp->res.jsonValue["Name"] = "Storage Collection";
Willy Tu5e577bc2022-07-26 00:41:55 +000065
66 constexpr std::array<std::string_view, 1> interface {
67 "xyz.openbmc_project.Inventory.Item.Storage"
68 };
69 collection_util::getCollectionMembers(
70 asyncResp, boost::urls::format("/redfish/v1/Systems/system/Storage"),
71 interface);
72}
73
74inline void handleStorageCollectionGet(
75 App& app, const crow::Request& req,
76 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
77{
78 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
79 {
80 return;
81 }
82 asyncResp->res.jsonValue["@odata.type"] =
83 "#StorageCollection.StorageCollection";
84 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Storage";
85 asyncResp->res.jsonValue["Name"] = "Storage Collection";
86 constexpr std::array<std::string_view, 1> interface {
87 "xyz.openbmc_project.Inventory.Item.Storage"
88 };
89 collection_util::getCollectionMembers(
90 asyncResp, boost::urls::format("/redfish/v1/Storage"), interface);
Ed Tanous36d52332023-06-09 13:18:40 -070091}
92
John Edward Broadbent7e860f12021-04-08 15:57:16 -070093inline void requestRoutesStorageCollection(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070094{
Ed Tanous22d268c2022-05-19 09:39:07 -070095 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
Ed Tanoused398212021-06-09 17:05:54 -070096 .privileges(redfish::privileges::getStorageCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070097 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -070098 std::bind_front(handleSystemsStorageCollectionGet, std::ref(app)));
Willy Tu5e577bc2022-07-26 00:41:55 +000099
100 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 {
114 BMCWEB_LOG_ERROR << "Drive mapper call error";
115 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 {
135 BMCWEB_LOG_ERROR << "Failed to find filename in " << drive;
136 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 {
Willy Tu5e577bc2022-07-26 00:41:55 +0000165 BMCWEB_LOG_DEBUG << "requestRoutesStorage DBUS response error";
166 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
167 storageId);
Ed Tanous36d52332023-06-09 13:18:40 -0700168 return;
169 }
Willy Tu5e577bc2022-07-26 00:41:55 +0000170 auto storage = std::find_if(
171 subtree.begin(), subtree.end(),
172 [&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,
205 const std::string& storageId)
206{
207 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
208 {
209 return;
210 }
211
212 constexpr std::array<std::string_view, 1> interfaces = {
213 "xyz.openbmc_project.Inventory.Item.Storage"};
214 dbus::utility::getSubTree(
215 "/xyz/openbmc_project/inventory", 0, interfaces,
216 std::bind_front(afterSystemsStorageGetSubtree, asyncResp, storageId));
217}
218
219inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
220 const std::string& storageId,
221 const boost::system::error_code& ec,
222 const dbus::utility::MapperGetSubTreeResponse& subtree)
223{
224 if (ec)
225 {
226 BMCWEB_LOG_DEBUG << "requestRoutesStorage DBUS response error";
227 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
228 storageId);
229 return;
230 }
231 auto storage = std::find_if(
232 subtree.begin(), subtree.end(),
233 [&storageId](const std::pair<std::string,
234 dbus::utility::MapperServiceMap>& object) {
235 return sdbusplus::message::object_path(object.first).filename() ==
236 storageId;
237 });
238 if (storage == subtree.end())
239 {
240 messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
241 storageId);
242 return;
243 }
244
245 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
246 asyncResp->res.jsonValue["@odata.id"] =
247 boost::urls::format("/redfish/v1/Storage/{}", storageId);
248 asyncResp->res.jsonValue["Name"] = "Storage";
249 asyncResp->res.jsonValue["Id"] = storageId;
250 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
251
252 // Storage subsystem to Storage link.
253 nlohmann::json::array_t storageServices;
254 nlohmann::json::object_t storageService;
255 storageService["@odata.id"] =
256 boost::urls::format("/redfish/v1/Systems/system/Storage/{}", storageId);
257 storageServices.emplace_back(storageService);
258 asyncResp->res.jsonValue["Links"]["StorageServices"] =
259 std::move(storageServices);
260 asyncResp->res.jsonValue["Links"]["StorageServices@odata.count"] = 1;
261}
262
263inline void
264 handleStorageGet(App& app, const crow::Request& req,
265 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
266 const std::string& storageId)
267{
268 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
269 {
270 BMCWEB_LOG_DEBUG << "requestRoutesStorage setUpRedfishRoute failed";
271 return;
272 }
273
274 constexpr std::array<std::string_view, 1> interfaces = {
275 "xyz.openbmc_project.Inventory.Item.Storage"};
276 dbus::utility::getSubTree(
277 "/xyz/openbmc_project/inventory", 0, interfaces,
278 std::bind_front(afterSubtree, asyncResp, storageId));
Willy Tua85afbe2021-12-28 14:43:47 -0800279}
280
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700281inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700282{
Willy Tu5e577bc2022-07-26 00:41:55 +0000283 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700284 .privileges(redfish::privileges::getStorage)
Ed Tanous002d39b2022-05-31 08:59:27 -0700285 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -0700286 std::bind_front(handleSystemsStorageGet, std::ref(app)));
Willy Tu5e577bc2022-07-26 00:41:55 +0000287
288 BMCWEB_ROUTE(app, "/redfish/v1/Storage/<str>/")
289 .privileges(redfish::privileges::getStorage)
290 .methods(boost::beast::http::verb::get)(
291 std::bind_front(handleStorageGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700292}
293
Willy Tu03913172021-11-08 02:03:19 -0800294inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
295 const std::string& connectionName,
296 const std::string& path)
297{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200298 sdbusplus::asio::getAllProperties(
299 *crow::connections::systemBus, connectionName, path,
300 "xyz.openbmc_project.Inventory.Decorator.Asset",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800301 [asyncResp](const boost::system::error_code& ec,
Ed Tanous168e20c2021-12-13 14:39:53 -0800302 const std::vector<
303 std::pair<std::string, dbus::utility::DbusVariantType>>&
304 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700305 if (ec)
306 {
307 // this interface isn't necessary
308 return;
309 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200310
311 const std::string* partNumber = nullptr;
312 const std::string* serialNumber = nullptr;
313 const std::string* manufacturer = nullptr;
314 const std::string* model = nullptr;
315
316 const bool success = sdbusplus::unpackPropertiesNoThrow(
317 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
318 partNumber, "SerialNumber", serialNumber, "Manufacturer",
319 manufacturer, "Model", model);
320
321 if (!success)
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200323 messages::internalError(asyncResp->res);
324 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700325 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200326
327 if (partNumber != nullptr)
328 {
329 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
330 }
331
332 if (serialNumber != nullptr)
333 {
334 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
335 }
336
337 if (manufacturer != nullptr)
338 {
339 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
340 }
341
342 if (model != nullptr)
343 {
344 asyncResp->res.jsonValue["Model"] = *model;
345 }
346 });
Willy Tu03913172021-11-08 02:03:19 -0800347}
348
349inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
350 const std::string& connectionName,
351 const std::string& path)
352{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700353 sdbusplus::asio::getProperty<bool>(
354 *crow::connections::systemBus, connectionName, path,
355 "xyz.openbmc_project.Inventory.Item", "Present",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800356 [asyncResp, path](const boost::system::error_code& ec,
Willy Tucef57e82022-12-15 16:42:02 -0800357 const bool isPresent) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700358 // this interface isn't necessary, only check it if
359 // we get a good return
360 if (ec)
361 {
362 return;
363 }
Willy Tu03913172021-11-08 02:03:19 -0800364
Willy Tucef57e82022-12-15 16:42:02 -0800365 if (!isPresent)
Ed Tanous002d39b2022-05-31 08:59:27 -0700366 {
Willy Tucef57e82022-12-15 16:42:02 -0800367 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
Ed Tanous002d39b2022-05-31 08:59:27 -0700368 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700369 });
Willy Tu03913172021-11-08 02:03:19 -0800370}
371
372inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
373 const std::string& connectionName,
374 const std::string& path)
375{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700376 sdbusplus::asio::getProperty<bool>(
377 *crow::connections::systemBus, connectionName, path,
378 "xyz.openbmc_project.State.Drive", "Rebuilding",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800379 [asyncResp](const boost::system::error_code& ec, const bool updating) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700380 // this interface isn't necessary, only check it
381 // if we get a good return
382 if (ec)
383 {
384 return;
385 }
Willy Tu03913172021-11-08 02:03:19 -0800386
Ed Tanous002d39b2022-05-31 08:59:27 -0700387 // updating and disabled in the backend shouldn't be
388 // able to be set at the same time, so we don't need
389 // to check for the race condition of these two
390 // calls
391 if (updating)
392 {
393 asyncResp->res.jsonValue["Status"]["State"] = "Updating";
394 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700395 });
Willy Tu03913172021-11-08 02:03:19 -0800396}
397
George Liudde9bc12023-02-22 09:35:51 +0800398inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800399{
400 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
401 {
George Liudde9bc12023-02-22 09:35:51 +0800402 return drive::MediaType::HDD;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800403 }
404 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
405 {
George Liudde9bc12023-02-22 09:35:51 +0800406 return drive::MediaType::SSD;
407 }
408 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
409 {
410 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800411 }
412
George Liudde9bc12023-02-22 09:35:51 +0800413 return drive::MediaType::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800414}
415
George Liudde9bc12023-02-22 09:35:51 +0800416inline std::optional<protocol::Protocol>
417 convertDriveProtocol(std::string_view proto)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800418{
419 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
420 {
George Liudde9bc12023-02-22 09:35:51 +0800421 return protocol::Protocol::SAS;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800422 }
423 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
424 {
George Liudde9bc12023-02-22 09:35:51 +0800425 return protocol::Protocol::SATA;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800426 }
427 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
428 {
George Liudde9bc12023-02-22 09:35:51 +0800429 return protocol::Protocol::NVMe;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800430 }
431 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
432 {
George Liudde9bc12023-02-22 09:35:51 +0800433 return protocol::Protocol::FC;
434 }
435 if (proto ==
436 "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
437 {
438 return std::nullopt;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800439 }
440
George Liudde9bc12023-02-22 09:35:51 +0800441 return protocol::Protocol::Invalid;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800442}
443
444inline void
445 getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
446 const std::string& connectionName,
447 const std::string& path)
448{
449 sdbusplus::asio::getAllProperties(
450 *crow::connections::systemBus, connectionName, path,
451 "xyz.openbmc_project.Inventory.Item.Drive",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800452 [asyncResp](const boost::system::error_code& ec,
Willy Tu19b8e9a2021-11-08 02:55:03 -0800453 const std::vector<
454 std::pair<std::string, dbus::utility::DbusVariantType>>&
455 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 if (ec)
457 {
458 // this interface isn't required
459 return;
460 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700461 const std::string* encryptionStatus = nullptr;
462 const bool* isLocked = nullptr;
Ed Tanous002d39b2022-05-31 08:59:27 -0700463 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
464 property : propertiesList)
465 {
466 const std::string& propertyName = property.first;
467 if (propertyName == "Type")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800468 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700469 const std::string* value =
470 std::get_if<std::string>(&property.second);
471 if (value == nullptr)
472 {
473 // illegal property
474 BMCWEB_LOG_ERROR << "Illegal property: Type";
475 messages::internalError(asyncResp->res);
476 return;
477 }
478
George Liudde9bc12023-02-22 09:35:51 +0800479 std::optional<drive::MediaType> mediaType =
480 convertDriveType(*value);
Ed Tanous002d39b2022-05-31 08:59:27 -0700481 if (!mediaType)
482 {
George Liudde9bc12023-02-22 09:35:51 +0800483 BMCWEB_LOG_WARNING << "UnknownDriveType Interface: "
484 << *value;
485 continue;
486 }
487 if (*mediaType == drive::MediaType::Invalid)
488 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700489 messages::internalError(asyncResp->res);
490 return;
491 }
492
493 asyncResp->res.jsonValue["MediaType"] = *mediaType;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800494 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700495 else if (propertyName == "Capacity")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800496 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700497 const uint64_t* capacity =
498 std::get_if<uint64_t>(&property.second);
499 if (capacity == nullptr)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800500 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700501 BMCWEB_LOG_ERROR << "Illegal property: Capacity";
502 messages::internalError(asyncResp->res);
503 return;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800504 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700505 if (*capacity == 0)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800506 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700507 // drive capacity not known
508 continue;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800509 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800510
Ed Tanous002d39b2022-05-31 08:59:27 -0700511 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800512 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700513 else if (propertyName == "Protocol")
514 {
515 const std::string* value =
516 std::get_if<std::string>(&property.second);
517 if (value == nullptr)
518 {
519 BMCWEB_LOG_ERROR << "Illegal property: Protocol";
520 messages::internalError(asyncResp->res);
521 return;
522 }
523
George Liudde9bc12023-02-22 09:35:51 +0800524 std::optional<protocol::Protocol> proto =
525 convertDriveProtocol(*value);
Ed Tanous002d39b2022-05-31 08:59:27 -0700526 if (!proto)
527 {
George Liudde9bc12023-02-22 09:35:51 +0800528 BMCWEB_LOG_WARNING << "Unknown DrivePrototype Interface: "
529 << *value;
530 continue;
531 }
532 if (*proto == protocol::Protocol::Invalid)
533 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700534 messages::internalError(asyncResp->res);
535 return;
536 }
537 asyncResp->res.jsonValue["Protocol"] = *proto;
538 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700539 else if (propertyName == "PredictedMediaLifeLeftPercent")
540 {
541 const uint8_t* lifeLeft =
542 std::get_if<uint8_t>(&property.second);
543 if (lifeLeft == nullptr)
544 {
545 BMCWEB_LOG_ERROR
546 << "Illegal property: PredictedMediaLifeLeftPercent";
547 messages::internalError(asyncResp->res);
548 return;
549 }
550 // 255 means reading the value is not supported
551 if (*lifeLeft != 255)
552 {
553 asyncResp->res.jsonValue["PredictedMediaLifeLeftPercent"] =
554 *lifeLeft;
555 }
556 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700557 else if (propertyName == "EncryptionStatus")
558 {
559 encryptionStatus = std::get_if<std::string>(&property.second);
560 if (encryptionStatus == nullptr)
561 {
562 BMCWEB_LOG_ERROR << "Illegal property: EncryptionStatus";
563 messages::internalError(asyncResp->res);
564 return;
565 }
566 }
567 else if (propertyName == "Locked")
568 {
569 isLocked = std::get_if<bool>(&property.second);
570 if (isLocked == nullptr)
571 {
572 BMCWEB_LOG_ERROR << "Illegal property: Locked";
573 messages::internalError(asyncResp->res);
574 return;
575 }
576 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700577 }
John Edward Broadbente5029d82022-06-08 14:35:21 -0700578
579 if (encryptionStatus == nullptr || isLocked == nullptr ||
580 *encryptionStatus ==
581 "xyz.openbmc_project.Drive.DriveEncryptionState.Unknown")
582 {
583 return;
584 }
585 if (*encryptionStatus !=
586 "xyz.openbmc_project.Drive.DriveEncryptionState.Encrypted")
587 {
588 //"The drive is not currently encrypted."
589 asyncResp->res.jsonValue["EncryptionStatus"] =
590 drive::EncryptionStatus::Unencrypted;
591 return;
592 }
593 if (*isLocked)
594 {
595 //"The drive is currently encrypted and the data is not
596 // accessible to the user."
597 asyncResp->res.jsonValue["EncryptionStatus"] =
598 drive::EncryptionStatus::Locked;
599 return;
600 }
601 // if not locked
602 // "The drive is currently encrypted but the data is accessible
603 // to the user in unencrypted form."
604 asyncResp->res.jsonValue["EncryptionStatus"] =
605 drive::EncryptionStatus::Unlocked;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800606 });
607}
608
Nan Zhoub53dcd92022-06-21 17:47:50 +0000609static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
610 const std::string& connectionName,
611 const std::string& path,
612 const std::vector<std::string>& interfaces)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700613{
614 for (const std::string& interface : interfaces)
615 {
616 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
617 {
618 getDriveAsset(asyncResp, connectionName, path);
619 }
620 else if (interface == "xyz.openbmc_project.Inventory.Item")
621 {
622 getDrivePresent(asyncResp, connectionName, path);
623 }
624 else if (interface == "xyz.openbmc_project.State.Drive")
625 {
626 getDriveState(asyncResp, connectionName, path);
627 }
628 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
629 {
630 getDriveItemProperties(asyncResp, connectionName, path);
631 }
632 }
633}
634
Ed Tanous36d52332023-06-09 13:18:40 -0700635inline void afterGetSubtreeSystemsStorageDrive(
636 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
637 const std::string& driveId, const boost::system::error_code& ec,
638 const dbus::utility::MapperGetSubTreeResponse& subtree)
639{
640 if (ec)
641 {
642 BMCWEB_LOG_ERROR << "Drive mapper call error";
643 messages::internalError(asyncResp->res);
644 return;
645 }
646
647 auto drive = std::find_if(
648 subtree.begin(), subtree.end(),
649 [&driveId](const std::pair<std::string,
650 dbus::utility::MapperServiceMap>& object) {
651 return sdbusplus::message::object_path(object.first).filename() ==
652 driveId;
653 });
654
655 if (drive == subtree.end())
656 {
657 messages::resourceNotFound(asyncResp->res, "Drive", driveId);
658 return;
659 }
660
661 const std::string& path = drive->first;
662 const dbus::utility::MapperServiceMap& connectionNames = drive->second;
663
664 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
665 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
666 "/redfish/v1/Systems/system/Storage/1/Drives/{}", driveId);
667 asyncResp->res.jsonValue["Name"] = driveId;
668 asyncResp->res.jsonValue["Id"] = driveId;
669
670 if (connectionNames.size() != 1)
671 {
672 BMCWEB_LOG_ERROR << "Connection size " << connectionNames.size()
673 << ", not equal to 1";
674 messages::internalError(asyncResp->res);
675 return;
676 }
677
678 getMainChassisId(asyncResp,
679 [](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 });
684
685 // default it to Enabled
686 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
687
688 if constexpr (bmcwebEnableHealthPopulate)
689 {
690 auto health = std::make_shared<HealthPopulate>(asyncResp);
691 health->inventory.emplace_back(path);
692 health->populate();
693 }
694
695 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
696 connectionNames[0].second);
697}
698
699inline void handleSystemsStorageDriveGet(
700 App& app, const crow::Request& req,
701 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
702 const std::string& systemName, const std::string& driveId)
703{
704 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
705 {
706 return;
707 }
708 if (systemName != "system")
709 {
710 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
711 systemName);
712 return;
713 }
714
715 constexpr std::array<std::string_view, 1> interfaces = {
716 "xyz.openbmc_project.Inventory.Item.Drive"};
717 dbus::utility::getSubTree(
718 "/xyz/openbmc_project/inventory", 0, interfaces,
719 std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
720 driveId));
721}
722
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700723inline void requestRoutesDrive(App& app)
724{
Ed Tanous22d268c2022-05-19 09:39:07 -0700725 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700726 .privileges(redfish::privileges::getDrive)
Ed Tanous002d39b2022-05-31 08:59:27 -0700727 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -0700728 std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700729}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700730
Ed Tanous36d52332023-06-09 13:18:40 -0700731inline void afterChassisDriveCollectionSubtreeGet(
732 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
733 const std::string& chassisId, const boost::system::error_code& ec,
734 const dbus::utility::MapperGetSubTreeResponse& subtree)
735{
736 if (ec)
737 {
738 if (ec == boost::system::errc::host_unreachable)
739 {
740 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
741 return;
742 }
743 messages::internalError(asyncResp->res);
744 return;
745 }
746
747 // Iterate over all retrieved ObjectPaths.
748 for (const auto& [path, connectionNames] : subtree)
749 {
750 sdbusplus::message::object_path objPath(path);
751 if (objPath.filename() != chassisId)
752 {
753 continue;
754 }
755
756 if (connectionNames.empty())
757 {
758 BMCWEB_LOG_ERROR << "Got 0 Connection names";
759 continue;
760 }
761
762 asyncResp->res.jsonValue["@odata.type"] =
763 "#DriveCollection.DriveCollection";
764 asyncResp->res.jsonValue["@odata.id"] =
765 boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
766 asyncResp->res.jsonValue["Name"] = "Drive Collection";
767
768 // Association lambda
769 dbus::utility::getAssociationEndPoints(
770 path + "/drive",
771 [asyncResp, chassisId](const boost::system::error_code& ec3,
772 const dbus::utility::MapperEndPoints& resp) {
773 if (ec3)
774 {
775 BMCWEB_LOG_ERROR << "Error in chassis Drive association ";
776 }
777 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
778 // important if array is empty
779 members = nlohmann::json::array();
780
781 std::vector<std::string> leafNames;
782 for (const auto& drive : resp)
783 {
784 sdbusplus::message::object_path drivePath(drive);
785 leafNames.push_back(drivePath.filename());
786 }
787
788 std::sort(leafNames.begin(), leafNames.end(),
789 AlphanumLess<std::string>());
790
791 for (const auto& leafName : leafNames)
792 {
793 nlohmann::json::object_t member;
794 member["@odata.id"] = boost::urls::format(
795 "/redfish/v1/Chassis/{}/Drives/{}", chassisId, leafName);
796 members.emplace_back(std::move(member));
797 // navigation links will be registered in next patch set
798 }
799 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
800 }); // end association lambda
801
802 } // end Iterate over all retrieved ObjectPaths
803}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700804/**
805 * Chassis drives, this URL will show all the DriveCollection
806 * information
807 */
Nan Zhoub53dcd92022-06-21 17:47:50 +0000808inline void chassisDriveCollectionGet(
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700809 crow::App& app, const crow::Request& req,
810 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
811 const std::string& chassisId)
812{
Carson Labrado3ba00072022-06-06 19:40:56 +0000813 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700814 {
815 return;
816 }
817
818 // mapper call lambda
George Liue99073f2022-12-09 11:06:16 +0800819 constexpr std::array<std::string_view, 2> interfaces = {
820 "xyz.openbmc_project.Inventory.Item.Board",
821 "xyz.openbmc_project.Inventory.Item.Chassis"};
822 dbus::utility::getSubTree(
823 "/xyz/openbmc_project/inventory", 0, interfaces,
Ed Tanous36d52332023-06-09 13:18:40 -0700824 std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
825 chassisId));
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700826}
827
828inline void requestRoutesChassisDrive(App& app)
829{
830 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
831 .privileges(redfish::privileges::getDriveCollection)
832 .methods(boost::beast::http::verb::get)(
833 std::bind_front(chassisDriveCollectionGet, std::ref(app)));
834}
835
Nan Zhoub53dcd92022-06-21 17:47:50 +0000836inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
837 const std::string& chassisId,
838 const std::string& driveName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800839 const boost::system::error_code& ec,
Nan Zhoub53dcd92022-06-21 17:47:50 +0000840 const dbus::utility::MapperGetSubTreeResponse& subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700841{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700842 if (ec)
843 {
844 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
845 messages::internalError(asyncResp->res);
846 return;
847 }
848
849 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000850 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700851 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700852 sdbusplus::message::object_path objPath(path);
853 if (objPath.filename() != driveName)
854 {
855 continue;
856 }
857
858 if (connectionNames.empty())
859 {
860 BMCWEB_LOG_ERROR << "Got 0 Connection names";
861 continue;
862 }
863
Ed Tanousef4c65b2023-04-24 15:28:50 -0700864 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
865 "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700866
867 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700868 asyncResp->res.jsonValue["Name"] = driveName;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700869 asyncResp->res.jsonValue["Id"] = driveName;
870 // default it to Enabled
871 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
872
873 nlohmann::json::object_t linkChassisNav;
874 linkChassisNav["@odata.id"] =
Ed Tanousef4c65b2023-04-24 15:28:50 -0700875 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700876 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
877
878 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
879 connectionNames[0].second);
880 }
881}
882
Nan Zhoub53dcd92022-06-21 17:47:50 +0000883inline void
884 matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
885 const std::string& chassisId,
886 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) {
905 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
George Liue99073f2022-12-09 11:06:16 +0800906 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700907 }
908}
909
Nan Zhoub53dcd92022-06-21 17:47:50 +0000910inline void
911 handleChassisDriveGet(crow::App& app, const crow::Request& req,
912 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
913 const std::string& chassisId,
914 const std::string& driveName)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700915{
Michal Orzel03810a12022-06-15 14:04:28 +0200916 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700917 {
918 return;
919 }
George Liue99073f2022-12-09 11:06:16 +0800920 constexpr std::array<std::string_view, 2> interfaces = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700921 "xyz.openbmc_project.Inventory.Item.Board",
922 "xyz.openbmc_project.Inventory.Item.Chassis"};
923
924 // mapper call chassis
George Liue99073f2022-12-09 11:06:16 +0800925 dbus::utility::getSubTree(
926 "/xyz/openbmc_project/inventory", 0, interfaces,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700927 [asyncResp, chassisId,
George Liue99073f2022-12-09 11:06:16 +0800928 driveName](const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700929 const dbus::utility::MapperGetSubTreeResponse& subtree) {
930 if (ec)
931 {
932 messages::internalError(asyncResp->res);
933 return;
934 }
935
936 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000937 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700938 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700939 sdbusplus::message::object_path objPath(path);
940 if (objPath.filename() != chassisId)
941 {
942 continue;
943 }
944
945 if (connectionNames.empty())
946 {
947 BMCWEB_LOG_ERROR << "Got 0 Connection names";
948 continue;
949 }
950
George Liu6c3e9452023-03-03 13:55:29 +0800951 dbus::utility::getAssociationEndPoints(
952 path + "/drive",
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700953 [asyncResp, chassisId,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800954 driveName](const boost::system::error_code& ec3,
George Liu6c3e9452023-03-03 13:55:29 +0800955 const dbus::utility::MapperEndPoints& resp) {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700956 if (ec3)
957 {
958 return; // no drives = no failures
959 }
960 matchAndFillDrive(asyncResp, chassisId, driveName, resp);
961 });
962 break;
963 }
George Liue99073f2022-12-09 11:06:16 +0800964 });
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
987 BMCWEB_LOG_DEBUG << "Failed to get StorageControllerAsset";
988 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";
1032 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1033 "/redfish/v1/Systems/system/Storage/1/Controllers/{}", controllerId);
1034 asyncResp->res.jsonValue["Name"] = controllerId;
1035 asyncResp->res.jsonValue["Id"] = controllerId;
1036 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
1037
1038 sdbusplus::asio::getProperty<bool>(
1039 *crow::connections::systemBus, connectionName, path,
1040 "xyz.openbmc_project.Inventory.Item", "Present",
1041 [asyncResp](const boost::system::error_code& ec, bool isPresent) {
1042 // this interface isn't necessary, only check it
1043 // if we get a good return
1044 if (ec)
1045 {
1046 BMCWEB_LOG_DEBUG << "Failed to get Present property";
1047 return;
1048 }
1049 if (!isPresent)
1050 {
1051 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
1052 }
1053 });
1054
1055 sdbusplus::asio::getAllProperties(
1056 *crow::connections::systemBus, connectionName, path,
1057 "xyz.openbmc_project.Inventory.Decorator.Asset",
1058 [asyncResp](const boost::system::error_code& ec,
1059 const std::vector<
1060 std::pair<std::string, dbus::utility::DbusVariantType>>&
1061 propertiesList) {
1062 getStorageControllerAsset(asyncResp, ec, propertiesList);
1063 });
1064}
1065
1066inline void getStorageControllerHandler(
1067 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1068 const std::string& controllerId, const boost::system::error_code& ec,
1069 const dbus::utility::MapperGetSubTreeResponse& subtree)
1070{
1071 if (ec || subtree.empty())
1072 {
1073 // doesn't have to be there
1074 BMCWEB_LOG_DEBUG << "Failed to handle StorageController";
1075 return;
1076 }
1077
1078 for (const auto& [path, interfaceDict] : subtree)
1079 {
1080 sdbusplus::message::object_path object(path);
1081 std::string id = object.filename();
1082 if (id.empty())
1083 {
1084 BMCWEB_LOG_ERROR << "Failed to find filename in " << path;
1085 return;
1086 }
1087 if (id != controllerId)
1088 {
1089 continue;
1090 }
1091
1092 if (interfaceDict.size() != 1)
1093 {
1094 BMCWEB_LOG_ERROR << "Connection size " << interfaceDict.size()
1095 << ", greater than 1";
1096 messages::internalError(asyncResp->res);
1097 return;
1098 }
1099
1100 const std::string& connectionName = interfaceDict.front().first;
1101 populateStorageController(asyncResp, controllerId, connectionName,
1102 path);
1103 }
1104}
1105
1106inline void populateStorageControllerCollection(
1107 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1108 const boost::system::error_code& ec,
1109 const dbus::utility::MapperGetSubTreePathsResponse& controllerList)
1110{
1111 nlohmann::json::array_t members;
1112 if (ec || controllerList.empty())
1113 {
1114 asyncResp->res.jsonValue["Members"] = std::move(members);
1115 asyncResp->res.jsonValue["Members@odata.count"] = 0;
1116 BMCWEB_LOG_DEBUG << "Failed to find any StorageController";
1117 return;
1118 }
1119
1120 for (const std::string& path : controllerList)
1121 {
1122 std::string id = sdbusplus::message::object_path(path).filename();
1123 if (id.empty())
1124 {
1125 BMCWEB_LOG_ERROR << "Failed to find filename in " << path;
1126 return;
1127 }
1128 nlohmann::json::object_t member;
1129 member["@odata.id"] = boost::urls::format(
1130 "/redfish/v1/Systems/system/Storage/1/Controllers/{}", id);
1131 members.emplace_back(member);
1132 }
1133 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
1134 asyncResp->res.jsonValue["Members"] = std::move(members);
1135}
1136
Ed Tanous36d52332023-06-09 13:18:40 -07001137inline void handleSystemsStorageControllerCollectionGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001138 App& app, const crow::Request& req,
1139 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1140 const std::string& systemName)
1141{
1142 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1143 {
1144 BMCWEB_LOG_DEBUG
1145 << "Failed to setup Redfish Route for StorageController Collection";
1146 return;
1147 }
1148 if (systemName != "system")
1149 {
1150 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1151 systemName);
1152 BMCWEB_LOG_DEBUG << "Failed to find ComputerSystem of " << systemName;
1153 return;
1154 }
1155
1156 asyncResp->res.jsonValue["@odata.type"] =
1157 "#StorageControllerCollection.StorageControllerCollection";
1158 asyncResp->res.jsonValue["@odata.id"] =
1159 "/redfish/v1/Systems/system/Storage/1/Controllers";
1160 asyncResp->res.jsonValue["Name"] = "Storage Controller Collection";
1161
1162 constexpr std::array<std::string_view, 1> interfaces = {
1163 "xyz.openbmc_project.Inventory.Item.StorageController"};
1164 dbus::utility::getSubTreePaths(
1165 "/xyz/openbmc_project/inventory", 0, interfaces,
1166 [asyncResp](const boost::system::error_code& ec,
1167 const dbus::utility::MapperGetSubTreePathsResponse&
1168 controllerList) {
1169 populateStorageControllerCollection(asyncResp, ec, controllerList);
1170 });
1171}
1172
Ed Tanous36d52332023-06-09 13:18:40 -07001173inline void handleSystemsStorageControllerGet(
Willy Tu61b1eb22023-03-14 11:29:50 -07001174 App& app, const crow::Request& req,
1175 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1176 const std::string& systemName, const std::string& controllerId)
1177{
1178 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1179 {
1180 BMCWEB_LOG_DEBUG
1181 << "Failed to setup Redfish Route for StorageController";
1182 return;
1183 }
1184 if (systemName != "system")
1185 {
1186 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1187 systemName);
1188 BMCWEB_LOG_DEBUG << "Failed to find ComputerSystem of " << systemName;
1189 return;
1190 }
1191 constexpr std::array<std::string_view, 1> interfaces = {
1192 "xyz.openbmc_project.Inventory.Item.StorageController"};
1193 dbus::utility::getSubTree(
1194 "/xyz/openbmc_project/inventory", 0, interfaces,
1195 [asyncResp,
1196 controllerId](const boost::system::error_code& ec,
1197 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1198 getStorageControllerHandler(asyncResp, controllerId, ec, subtree);
1199 });
1200}
1201
1202inline void requestRoutesStorageControllerCollection(App& app)
1203{
1204 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/")
1205 .privileges(redfish::privileges::getStorageControllerCollection)
Ed Tanous36d52332023-06-09 13:18:40 -07001206 .methods(boost::beast::http::verb::get)(std::bind_front(
1207 handleSystemsStorageControllerCollectionGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001208}
1209
1210inline void requestRoutesStorageController(App& app)
1211{
1212 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>")
1213 .privileges(redfish::privileges::getStorageController)
1214 .methods(boost::beast::http::verb::get)(
Ed Tanous36d52332023-06-09 13:18:40 -07001215 std::bind_front(handleSystemsStorageControllerGet, std::ref(app)));
Willy Tu61b1eb22023-03-14 11:29:50 -07001216}
1217
Nikhil Potadea25aecc2019-08-23 16:35:26 -07001218} // namespace redfish