blob: fe4842c02c84bcc403209972a4fa6a7a77048d9d [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
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080019#include "dbus_utility.hpp"
James Feist2ad9c2f2019-10-29 16:26:48 -070020#include "health.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080021#include "human_sort.hpp"
James Feiste284a7c2019-11-20 16:20:23 -080022#include "openbmc_dbus_rest.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080023#include "query.hpp"
Ed Tanousa8e884f2023-01-13 17:40:03 -080024#include "redfish_util.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080025#include "registries/privilege_registry.hpp"
26#include "utils/dbus_utils.hpp"
James Feist2ad9c2f2019-10-29 16:26:48 -070027
George Liue99073f2022-12-09 11:06:16 +080028#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070029#include <boost/url/format.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070030#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020031#include <sdbusplus/unpack_properties.hpp>
Nikhil Potadea25aecc2019-08-23 16:35:26 -070032
George Liu7a1dbc42022-12-07 16:03:22 +080033#include <array>
34#include <string_view>
35
Nikhil Potadea25aecc2019-08-23 16:35:26 -070036namespace redfish
37{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038inline void requestRoutesStorageCollection(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070039{
Ed Tanous22d268c2022-05-19 09:39:07 -070040 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
Ed Tanoused398212021-06-09 17:05:54 -070041 .privileges(redfish::privileges::getStorageCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070042 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -070043 [&app](const crow::Request& req,
Ed Tanous22d268c2022-05-19 09:39:07 -070044 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
45 const std::string& systemName) {
Carson Labrado3ba00072022-06-06 19:40:56 +000046 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070047 {
48 return;
49 }
Ed Tanous22d268c2022-05-19 09:39:07 -070050 if (systemName != "system")
51 {
52 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
53 systemName);
54 return;
55 }
56
Ed Tanous002d39b2022-05-31 08:59:27 -070057 asyncResp->res.jsonValue["@odata.type"] =
58 "#StorageCollection.StorageCollection";
59 asyncResp->res.jsonValue["@odata.id"] =
60 "/redfish/v1/Systems/system/Storage";
61 asyncResp->res.jsonValue["Name"] = "Storage Collection";
62 nlohmann::json::array_t members;
63 nlohmann::json::object_t member;
64 member["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
65 members.emplace_back(member);
66 asyncResp->res.jsonValue["Members"] = std::move(members);
67 asyncResp->res.jsonValue["Members@odata.count"] = 1;
68 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070069}
Nikhil Potadea25aecc2019-08-23 16:35:26 -070070
Willy Tua85afbe2021-12-28 14:43:47 -080071inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
72 const std::shared_ptr<HealthPopulate>& health)
73{
George Liu7a1dbc42022-12-07 16:03:22 +080074 const std::array<std::string_view, 1> interfaces = {
75 "xyz.openbmc_project.Inventory.Item.Drive"};
76 dbus::utility::getSubTreePaths(
77 "/xyz/openbmc_project/inventory", 0, interfaces,
Willy Tua85afbe2021-12-28 14:43:47 -080078 [asyncResp, health](
George Liu7a1dbc42022-12-07 16:03:22 +080079 const boost::system::error_code& ec,
Willy Tua85afbe2021-12-28 14:43:47 -080080 const dbus::utility::MapperGetSubTreePathsResponse& driveList) {
81 if (ec)
82 {
83 BMCWEB_LOG_ERROR << "Drive mapper call error";
84 messages::internalError(asyncResp->res);
85 return;
86 }
87
88 nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
89 driveArray = nlohmann::json::array();
90 auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
91 count = 0;
92
93 health->inventory.insert(health->inventory.end(), driveList.begin(),
94 driveList.end());
95
96 for (const std::string& drive : driveList)
97 {
98 sdbusplus::message::object_path object(drive);
99 if (object.filename().empty())
100 {
101 BMCWEB_LOG_ERROR << "Failed to find filename in " << drive;
102 return;
103 }
104
105 nlohmann::json::object_t driveJson;
Ed Tanousef4c65b2023-04-24 15:28:50 -0700106 driveJson["@odata.id"] = boost::urls::format(
107 "/redfish/v1/Systems/system/Storage/1/Drives/{}",
Willy Tueddfc432022-09-26 16:46:38 +0000108 object.filename());
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500109 driveArray.emplace_back(std::move(driveJson));
Willy Tua85afbe2021-12-28 14:43:47 -0800110 }
111
112 count = driveArray.size();
George Liu7a1dbc42022-12-07 16:03:22 +0800113 });
Willy Tua85afbe2021-12-28 14:43:47 -0800114}
115
116inline void
117 getStorageControllers(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
118 const std::shared_ptr<HealthPopulate>& health)
119{
George Liue99073f2022-12-09 11:06:16 +0800120 constexpr std::array<std::string_view, 1> interfaces = {
121 "xyz.openbmc_project.Inventory.Item.StorageController"};
122 dbus::utility::getSubTree(
123 "/xyz/openbmc_project/inventory", 0, interfaces,
Willy Tua85afbe2021-12-28 14:43:47 -0800124 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +0800125 health](const boost::system::error_code& ec,
Willy Tua85afbe2021-12-28 14:43:47 -0800126 const dbus::utility::MapperGetSubTreeResponse& subtree) {
127 if (ec || subtree.empty())
128 {
129 // doesn't have to be there
130 return;
131 }
132
133 nlohmann::json& root = asyncResp->res.jsonValue["StorageControllers"];
134 root = nlohmann::json::array();
135 for (const auto& [path, interfaceDict] : subtree)
136 {
137 sdbusplus::message::object_path object(path);
138 std::string id = object.filename();
139 if (id.empty())
140 {
141 BMCWEB_LOG_ERROR << "Failed to find filename in " << path;
142 return;
143 }
144
145 if (interfaceDict.size() != 1)
146 {
147 BMCWEB_LOG_ERROR << "Connection size " << interfaceDict.size()
148 << ", greater than 1";
149 messages::internalError(asyncResp->res);
150 return;
151 }
152
153 const std::string& connectionName = interfaceDict.front().first;
154
155 size_t index = root.size();
156 nlohmann::json& storageController =
157 root.emplace_back(nlohmann::json::object());
158
159 storageController["@odata.type"] =
160 "#Storage.v1_7_0.StorageController";
Ed Tanousef4c65b2023-04-24 15:28:50 -0700161 storageController["@odata.id"] = boost::urls::format(
162 "/redfish/v1/Systems/system/Storage/1#{}",
163 ("/StorageControllers"_json_pointer / index).to_string());
Willy Tua85afbe2021-12-28 14:43:47 -0800164 storageController["Name"] = id;
165 storageController["MemberId"] = id;
166 storageController["Status"]["State"] = "Enabled";
167
168 sdbusplus::asio::getProperty<bool>(
169 *crow::connections::systemBus, connectionName, path,
170 "xyz.openbmc_project.Inventory.Item", "Present",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800171 [asyncResp, index](const boost::system::error_code& ec2,
Willy Tucef57e82022-12-15 16:42:02 -0800172 bool isPresent) {
Willy Tua85afbe2021-12-28 14:43:47 -0800173 // this interface isn't necessary, only check it
174 // if we get a good return
175 if (ec2)
176 {
177 return;
178 }
Willy Tucef57e82022-12-15 16:42:02 -0800179 if (!isPresent)
Willy Tua85afbe2021-12-28 14:43:47 -0800180 {
181 asyncResp->res.jsonValue["StorageControllers"][index]
Willy Tucef57e82022-12-15 16:42:02 -0800182 ["Status"]["State"] = "Absent";
Willy Tua85afbe2021-12-28 14:43:47 -0800183 }
184 });
185
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200186 sdbusplus::asio::getAllProperties(
187 *crow::connections::systemBus, connectionName, path,
188 "xyz.openbmc_project.Inventory.Decorator.Asset",
Willy Tua85afbe2021-12-28 14:43:47 -0800189 [asyncResp, index](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800190 const boost::system::error_code& ec2,
Willy Tua85afbe2021-12-28 14:43:47 -0800191 const std::vector<
192 std::pair<std::string, dbus::utility::DbusVariantType>>&
193 propertiesList) {
194 if (ec2)
195 {
196 // this interface isn't necessary
197 return;
198 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200199
200 const std::string* partNumber = nullptr;
201 const std::string* serialNumber = nullptr;
202 const std::string* manufacturer = nullptr;
203 const std::string* model = nullptr;
204
205 const bool success = sdbusplus::unpackPropertiesNoThrow(
206 dbus_utils::UnpackErrorPrinter(), propertiesList,
207 "PartNumber", partNumber, "SerialNumber", serialNumber,
208 "Manufacturer", manufacturer, "Model", model);
209
210 if (!success)
Willy Tua85afbe2021-12-28 14:43:47 -0800211 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200212 messages::internalError(asyncResp->res);
213 return;
Willy Tua85afbe2021-12-28 14:43:47 -0800214 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200215
216 nlohmann::json& controller =
217 asyncResp->res.jsonValue["StorageControllers"][index];
218
219 if (partNumber != nullptr)
220 {
221 controller["PartNumber"] = *partNumber;
222 }
223
224 if (serialNumber != nullptr)
225 {
226 controller["SerialNumber"] = *serialNumber;
227 }
228
229 if (manufacturer != nullptr)
230 {
231 controller["Manufacturer"] = *manufacturer;
232 }
233
234 if (model != nullptr)
235 {
236 controller["Model"] = *model;
237 }
238 });
Willy Tua85afbe2021-12-28 14:43:47 -0800239 }
240
241 // this is done after we know the json array will no longer
242 // be resized, as json::array uses vector underneath and we
243 // need references to its members that won't change
244 size_t count = 0;
245 // Pointer based on |asyncResp->res.jsonValue|
246 nlohmann::json::json_pointer rootPtr =
247 "/StorageControllers"_json_pointer;
248 for (const auto& [path, interfaceDict] : subtree)
249 {
250 auto subHealth = std::make_shared<HealthPopulate>(
251 asyncResp, rootPtr / count / "Status");
252 subHealth->inventory.emplace_back(path);
253 health->inventory.emplace_back(path);
254 health->children.emplace_back(subHealth);
255 count++;
256 }
George Liue99073f2022-12-09 11:06:16 +0800257 });
Willy Tua85afbe2021-12-28 14:43:47 -0800258}
259
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700260inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700261{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700262 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/")
Ed Tanoused398212021-06-09 17:05:54 -0700263 .privileges(redfish::privileges::getStorage)
Ed Tanous002d39b2022-05-31 08:59:27 -0700264 .methods(boost::beast::http::verb::get)(
265 [&app](const crow::Request& req,
266 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000267 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700268 {
269 return;
270 }
271 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
272 asyncResp->res.jsonValue["@odata.id"] =
273 "/redfish/v1/Systems/system/Storage/1";
274 asyncResp->res.jsonValue["Name"] = "Storage";
275 asyncResp->res.jsonValue["Id"] = "1";
276 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
277
278 auto health = std::make_shared<HealthPopulate>(asyncResp);
279 health->populate();
280
Willy Tua85afbe2021-12-28 14:43:47 -0800281 getDrives(asyncResp, health);
282 getStorageControllers(asyncResp, health);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700283 });
284}
285
Willy Tu03913172021-11-08 02:03:19 -0800286inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
287 const std::string& connectionName,
288 const std::string& path)
289{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200290 sdbusplus::asio::getAllProperties(
291 *crow::connections::systemBus, connectionName, path,
292 "xyz.openbmc_project.Inventory.Decorator.Asset",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800293 [asyncResp](const boost::system::error_code& ec,
Ed Tanous168e20c2021-12-13 14:39:53 -0800294 const std::vector<
295 std::pair<std::string, dbus::utility::DbusVariantType>>&
296 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700297 if (ec)
298 {
299 // this interface isn't necessary
300 return;
301 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200302
303 const std::string* partNumber = nullptr;
304 const std::string* serialNumber = nullptr;
305 const std::string* manufacturer = nullptr;
306 const std::string* model = nullptr;
307
308 const bool success = sdbusplus::unpackPropertiesNoThrow(
309 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
310 partNumber, "SerialNumber", serialNumber, "Manufacturer",
311 manufacturer, "Model", model);
312
313 if (!success)
Ed Tanous002d39b2022-05-31 08:59:27 -0700314 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200315 messages::internalError(asyncResp->res);
316 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700317 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200318
319 if (partNumber != nullptr)
320 {
321 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
322 }
323
324 if (serialNumber != nullptr)
325 {
326 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
327 }
328
329 if (manufacturer != nullptr)
330 {
331 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
332 }
333
334 if (model != nullptr)
335 {
336 asyncResp->res.jsonValue["Model"] = *model;
337 }
338 });
Willy Tu03913172021-11-08 02:03:19 -0800339}
340
341inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
342 const std::string& connectionName,
343 const std::string& path)
344{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700345 sdbusplus::asio::getProperty<bool>(
346 *crow::connections::systemBus, connectionName, path,
347 "xyz.openbmc_project.Inventory.Item", "Present",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800348 [asyncResp, path](const boost::system::error_code& ec,
Willy Tucef57e82022-12-15 16:42:02 -0800349 const bool isPresent) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700350 // this interface isn't necessary, only check it if
351 // we get a good return
352 if (ec)
353 {
354 return;
355 }
Willy Tu03913172021-11-08 02:03:19 -0800356
Willy Tucef57e82022-12-15 16:42:02 -0800357 if (!isPresent)
Ed Tanous002d39b2022-05-31 08:59:27 -0700358 {
Willy Tucef57e82022-12-15 16:42:02 -0800359 asyncResp->res.jsonValue["Status"]["State"] = "Absent";
Ed Tanous002d39b2022-05-31 08:59:27 -0700360 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700361 });
Willy Tu03913172021-11-08 02:03:19 -0800362}
363
364inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
365 const std::string& connectionName,
366 const std::string& path)
367{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700368 sdbusplus::asio::getProperty<bool>(
369 *crow::connections::systemBus, connectionName, path,
370 "xyz.openbmc_project.State.Drive", "Rebuilding",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800371 [asyncResp](const boost::system::error_code& ec, const bool updating) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700372 // this interface isn't necessary, only check it
373 // if we get a good return
374 if (ec)
375 {
376 return;
377 }
Willy Tu03913172021-11-08 02:03:19 -0800378
Ed Tanous002d39b2022-05-31 08:59:27 -0700379 // updating and disabled in the backend shouldn't be
380 // able to be set at the same time, so we don't need
381 // to check for the race condition of these two
382 // calls
383 if (updating)
384 {
385 asyncResp->res.jsonValue["Status"]["State"] = "Updating";
386 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700387 });
Willy Tu03913172021-11-08 02:03:19 -0800388}
389
Willy Tu19b8e9a2021-11-08 02:55:03 -0800390inline std::optional<std::string> convertDriveType(const std::string& type)
391{
392 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
393 {
394 return "HDD";
395 }
396 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
397 {
398 return "SSD";
399 }
400
401 return std::nullopt;
402}
403
404inline std::optional<std::string> convertDriveProtocol(const std::string& proto)
405{
406 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
407 {
408 return "SAS";
409 }
410 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
411 {
412 return "SATA";
413 }
414 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
415 {
416 return "NVMe";
417 }
418 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
419 {
420 return "FC";
421 }
422
423 return std::nullopt;
424}
425
426inline void
427 getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
428 const std::string& connectionName,
429 const std::string& path)
430{
431 sdbusplus::asio::getAllProperties(
432 *crow::connections::systemBus, connectionName, path,
433 "xyz.openbmc_project.Inventory.Item.Drive",
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800434 [asyncResp](const boost::system::error_code& ec,
Willy Tu19b8e9a2021-11-08 02:55:03 -0800435 const std::vector<
436 std::pair<std::string, dbus::utility::DbusVariantType>>&
437 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700438 if (ec)
439 {
440 // this interface isn't required
441 return;
442 }
443 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
444 property : propertiesList)
445 {
446 const std::string& propertyName = property.first;
447 if (propertyName == "Type")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800448 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700449 const std::string* value =
450 std::get_if<std::string>(&property.second);
451 if (value == nullptr)
452 {
453 // illegal property
454 BMCWEB_LOG_ERROR << "Illegal property: Type";
455 messages::internalError(asyncResp->res);
456 return;
457 }
458
459 std::optional<std::string> mediaType = convertDriveType(*value);
460 if (!mediaType)
461 {
462 BMCWEB_LOG_ERROR << "Unsupported DriveType Interface: "
463 << *value;
464 messages::internalError(asyncResp->res);
465 return;
466 }
467
468 asyncResp->res.jsonValue["MediaType"] = *mediaType;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800469 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700470 else if (propertyName == "Capacity")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800471 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700472 const uint64_t* capacity =
473 std::get_if<uint64_t>(&property.second);
474 if (capacity == nullptr)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800475 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700476 BMCWEB_LOG_ERROR << "Illegal property: Capacity";
477 messages::internalError(asyncResp->res);
478 return;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800479 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700480 if (*capacity == 0)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800481 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700482 // drive capacity not known
483 continue;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800484 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800485
Ed Tanous002d39b2022-05-31 08:59:27 -0700486 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800487 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700488 else if (propertyName == "Protocol")
489 {
490 const std::string* value =
491 std::get_if<std::string>(&property.second);
492 if (value == nullptr)
493 {
494 BMCWEB_LOG_ERROR << "Illegal property: Protocol";
495 messages::internalError(asyncResp->res);
496 return;
497 }
498
499 std::optional<std::string> proto = convertDriveProtocol(*value);
500 if (!proto)
501 {
502 BMCWEB_LOG_ERROR << "Unsupported DrivePrototype Interface: "
503 << *value;
504 messages::internalError(asyncResp->res);
505 return;
506 }
507 asyncResp->res.jsonValue["Protocol"] = *proto;
508 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700509 else if (propertyName == "PredictedMediaLifeLeftPercent")
510 {
511 const uint8_t* lifeLeft =
512 std::get_if<uint8_t>(&property.second);
513 if (lifeLeft == nullptr)
514 {
515 BMCWEB_LOG_ERROR
516 << "Illegal property: PredictedMediaLifeLeftPercent";
517 messages::internalError(asyncResp->res);
518 return;
519 }
520 // 255 means reading the value is not supported
521 if (*lifeLeft != 255)
522 {
523 asyncResp->res.jsonValue["PredictedMediaLifeLeftPercent"] =
524 *lifeLeft;
525 }
526 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700527 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800528 });
529}
530
Nan Zhoub53dcd92022-06-21 17:47:50 +0000531static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
532 const std::string& connectionName,
533 const std::string& path,
534 const std::vector<std::string>& interfaces)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700535{
536 for (const std::string& interface : interfaces)
537 {
538 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
539 {
540 getDriveAsset(asyncResp, connectionName, path);
541 }
542 else if (interface == "xyz.openbmc_project.Inventory.Item")
543 {
544 getDrivePresent(asyncResp, connectionName, path);
545 }
546 else if (interface == "xyz.openbmc_project.State.Drive")
547 {
548 getDriveState(asyncResp, connectionName, path);
549 }
550 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
551 {
552 getDriveItemProperties(asyncResp, connectionName, path);
553 }
554 }
555}
556
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700557inline void requestRoutesDrive(App& app)
558{
Ed Tanous22d268c2022-05-19 09:39:07 -0700559 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700560 .privileges(redfish::privileges::getDrive)
Ed Tanous002d39b2022-05-31 08:59:27 -0700561 .methods(boost::beast::http::verb::get)(
562 [&app](const crow::Request& req,
563 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous22d268c2022-05-19 09:39:07 -0700564 const std::string& systemName, const std::string& driveId) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000565 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700566 {
567 return;
568 }
Ed Tanous22d268c2022-05-19 09:39:07 -0700569 if (systemName != "system")
570 {
571 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
572 systemName);
573 return;
574 }
575
George Liue99073f2022-12-09 11:06:16 +0800576 constexpr std::array<std::string_view, 1> interfaces = {
577 "xyz.openbmc_project.Inventory.Item.Drive"};
578 dbus::utility::getSubTree(
579 "/xyz/openbmc_project/inventory", 0, interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700580 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +0800581 driveId](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700582 const dbus::utility::MapperGetSubTreeResponse& subtree) {
583 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700584 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700585 BMCWEB_LOG_ERROR << "Drive mapper call error";
586 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700587 return;
588 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700589
Ed Tanous002d39b2022-05-31 08:59:27 -0700590 auto drive = std::find_if(
591 subtree.begin(), subtree.end(),
592 [&driveId](
Nan Zhou8cb65f82022-06-15 05:12:24 +0000593 const std::pair<std::string,
594 dbus::utility::MapperServiceMap>& object) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700595 return sdbusplus::message::object_path(object.first)
596 .filename() == driveId;
597 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700598
Ed Tanous002d39b2022-05-31 08:59:27 -0700599 if (drive == subtree.end())
600 {
601 messages::resourceNotFound(asyncResp->res, "Drive", driveId);
602 return;
603 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700604
Ed Tanous002d39b2022-05-31 08:59:27 -0700605 const std::string& path = drive->first;
Nan Zhou8cb65f82022-06-15 05:12:24 +0000606 const dbus::utility::MapperServiceMap& connectionNames =
607 drive->second;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700608
Ed Tanous002d39b2022-05-31 08:59:27 -0700609 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
Ed Tanousef4c65b2023-04-24 15:28:50 -0700610 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
611 "/redfish/v1/Systems/system/Storage/1/Drives/{}", driveId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700612 asyncResp->res.jsonValue["Name"] = driveId;
613 asyncResp->res.jsonValue["Id"] = driveId;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700614
Ed Tanous002d39b2022-05-31 08:59:27 -0700615 if (connectionNames.size() != 1)
616 {
617 BMCWEB_LOG_ERROR << "Connection size " << connectionNames.size()
618 << ", not equal to 1";
619 messages::internalError(asyncResp->res);
620 return;
621 }
James Feiste284a7c2019-11-20 16:20:23 -0800622
Ed Tanous002d39b2022-05-31 08:59:27 -0700623 getMainChassisId(
Ed Tanousef4c65b2023-04-24 15:28:50 -0700624 asyncResp,
625 [](const std::string& chassisId,
626 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
627 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
628 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700629 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700630
Ed Tanous002d39b2022-05-31 08:59:27 -0700631 // default it to Enabled
632 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700633
Ed Tanous002d39b2022-05-31 08:59:27 -0700634 auto health = std::make_shared<HealthPopulate>(asyncResp);
635 health->inventory.emplace_back(path);
636 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700637
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700638 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
639 connectionNames[0].second);
George Liue99073f2022-12-09 11:06:16 +0800640 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700641 });
642}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700643
644/**
645 * Chassis drives, this URL will show all the DriveCollection
646 * information
647 */
Nan Zhoub53dcd92022-06-21 17:47:50 +0000648inline void chassisDriveCollectionGet(
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700649 crow::App& app, const crow::Request& req,
650 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
651 const std::string& chassisId)
652{
Carson Labrado3ba00072022-06-06 19:40:56 +0000653 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700654 {
655 return;
656 }
657
658 // mapper call lambda
George Liue99073f2022-12-09 11:06:16 +0800659 constexpr std::array<std::string_view, 2> interfaces = {
660 "xyz.openbmc_project.Inventory.Item.Board",
661 "xyz.openbmc_project.Inventory.Item.Chassis"};
662 dbus::utility::getSubTree(
663 "/xyz/openbmc_project/inventory", 0, interfaces,
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700664 [asyncResp,
George Liue99073f2022-12-09 11:06:16 +0800665 chassisId](const boost::system::error_code& ec,
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700666 const dbus::utility::MapperGetSubTreeResponse& subtree) {
667 if (ec)
668 {
669 if (ec == boost::system::errc::host_unreachable)
670 {
671 messages::resourceNotFound(asyncResp->res, "Chassis",
672 chassisId);
673 return;
674 }
675 messages::internalError(asyncResp->res);
676 return;
677 }
678
679 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000680 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700681 {
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700682 sdbusplus::message::object_path objPath(path);
683 if (objPath.filename() != chassisId)
684 {
685 continue;
686 }
687
688 if (connectionNames.empty())
689 {
690 BMCWEB_LOG_ERROR << "Got 0 Connection names";
691 continue;
692 }
693
694 asyncResp->res.jsonValue["@odata.type"] =
695 "#DriveCollection.DriveCollection";
696 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanousef4c65b2023-04-24 15:28:50 -0700697 boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700698 asyncResp->res.jsonValue["Name"] = "Drive Collection";
699
700 // Association lambda
George Liu6c3e9452023-03-03 13:55:29 +0800701 dbus::utility::getAssociationEndPoints(
702 path + "/drive",
703 [asyncResp,
704 chassisId](const boost::system::error_code& ec3,
705 const dbus::utility::MapperEndPoints& resp) {
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700706 if (ec3)
707 {
708 BMCWEB_LOG_ERROR << "Error in chassis Drive association ";
709 }
710 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
711 // important if array is empty
712 members = nlohmann::json::array();
713
714 std::vector<std::string> leafNames;
715 for (const auto& drive : resp)
716 {
Ed Tanous8a592812022-06-04 09:06:59 -0700717 sdbusplus::message::object_path drivePath(drive);
718 leafNames.push_back(drivePath.filename());
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700719 }
720
721 std::sort(leafNames.begin(), leafNames.end(),
722 AlphanumLess<std::string>());
723
724 for (const auto& leafName : leafNames)
725 {
726 nlohmann::json::object_t member;
Ed Tanousef4c65b2023-04-24 15:28:50 -0700727 member["@odata.id"] =
728 boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}",
729 chassisId, leafName);
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500730 members.emplace_back(std::move(member));
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700731 // navigation links will be registered in next patch set
732 }
733 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
734 }); // end association lambda
735
Patrick Williams89492a12023-05-10 07:51:34 -0500736 } // end Iterate over all retrieved ObjectPaths
George Liue99073f2022-12-09 11:06:16 +0800737 });
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700738}
739
740inline void requestRoutesChassisDrive(App& app)
741{
742 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
743 .privileges(redfish::privileges::getDriveCollection)
744 .methods(boost::beast::http::verb::get)(
745 std::bind_front(chassisDriveCollectionGet, std::ref(app)));
746}
747
Nan Zhoub53dcd92022-06-21 17:47:50 +0000748inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
749 const std::string& chassisId,
750 const std::string& driveName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800751 const boost::system::error_code& ec,
Nan Zhoub53dcd92022-06-21 17:47:50 +0000752 const dbus::utility::MapperGetSubTreeResponse& subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700753{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700754 if (ec)
755 {
756 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
757 messages::internalError(asyncResp->res);
758 return;
759 }
760
761 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000762 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700763 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700764 sdbusplus::message::object_path objPath(path);
765 if (objPath.filename() != driveName)
766 {
767 continue;
768 }
769
770 if (connectionNames.empty())
771 {
772 BMCWEB_LOG_ERROR << "Got 0 Connection names";
773 continue;
774 }
775
Ed Tanousef4c65b2023-04-24 15:28:50 -0700776 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
777 "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700778
779 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700780 asyncResp->res.jsonValue["Name"] = driveName;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700781 asyncResp->res.jsonValue["Id"] = driveName;
782 // default it to Enabled
783 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
784
785 nlohmann::json::object_t linkChassisNav;
786 linkChassisNav["@odata.id"] =
Ed Tanousef4c65b2023-04-24 15:28:50 -0700787 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700788 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
789
790 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
791 connectionNames[0].second);
792 }
793}
794
Nan Zhoub53dcd92022-06-21 17:47:50 +0000795inline void
796 matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
797 const std::string& chassisId,
798 const std::string& driveName,
799 const std::vector<std::string>& resp)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700800{
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700801 for (const std::string& drivePath : resp)
802 {
803 sdbusplus::message::object_path path(drivePath);
804 std::string leaf = path.filename();
805 if (leaf != driveName)
806 {
807 continue;
808 }
809 // mapper call drive
George Liue99073f2022-12-09 11:06:16 +0800810 constexpr std::array<std::string_view, 1> driveInterface = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700811 "xyz.openbmc_project.Inventory.Item.Drive"};
George Liue99073f2022-12-09 11:06:16 +0800812 dbus::utility::getSubTree(
813 "/xyz/openbmc_project/inventory", 0, driveInterface,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700814 [asyncResp, chassisId, driveName](
George Liue99073f2022-12-09 11:06:16 +0800815 const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700816 const dbus::utility::MapperGetSubTreeResponse& subtree) {
817 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
George Liue99073f2022-12-09 11:06:16 +0800818 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700819 }
820}
821
Nan Zhoub53dcd92022-06-21 17:47:50 +0000822inline void
823 handleChassisDriveGet(crow::App& app, const crow::Request& req,
824 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
825 const std::string& chassisId,
826 const std::string& driveName)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700827{
Michal Orzel03810a12022-06-15 14:04:28 +0200828 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700829 {
830 return;
831 }
George Liue99073f2022-12-09 11:06:16 +0800832 constexpr std::array<std::string_view, 2> interfaces = {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700833 "xyz.openbmc_project.Inventory.Item.Board",
834 "xyz.openbmc_project.Inventory.Item.Chassis"};
835
836 // mapper call chassis
George Liue99073f2022-12-09 11:06:16 +0800837 dbus::utility::getSubTree(
838 "/xyz/openbmc_project/inventory", 0, interfaces,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700839 [asyncResp, chassisId,
George Liue99073f2022-12-09 11:06:16 +0800840 driveName](const boost::system::error_code& ec,
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700841 const dbus::utility::MapperGetSubTreeResponse& subtree) {
842 if (ec)
843 {
844 messages::internalError(asyncResp->res);
845 return;
846 }
847
848 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000849 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700850 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700851 sdbusplus::message::object_path objPath(path);
852 if (objPath.filename() != chassisId)
853 {
854 continue;
855 }
856
857 if (connectionNames.empty())
858 {
859 BMCWEB_LOG_ERROR << "Got 0 Connection names";
860 continue;
861 }
862
George Liu6c3e9452023-03-03 13:55:29 +0800863 dbus::utility::getAssociationEndPoints(
864 path + "/drive",
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700865 [asyncResp, chassisId,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800866 driveName](const boost::system::error_code& ec3,
George Liu6c3e9452023-03-03 13:55:29 +0800867 const dbus::utility::MapperEndPoints& resp) {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700868 if (ec3)
869 {
870 return; // no drives = no failures
871 }
872 matchAndFillDrive(asyncResp, chassisId, driveName, resp);
873 });
874 break;
875 }
George Liue99073f2022-12-09 11:06:16 +0800876 });
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700877}
878
879/**
880 * This URL will show the drive interface for the specific drive in the chassis
881 */
882inline void requestRoutesChassisDriveName(App& app)
883{
884 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
885 .privileges(redfish::privileges::getChassis)
886 .methods(boost::beast::http::verb::get)(
887 std::bind_front(handleChassisDriveGet, std::ref(app)));
888}
889
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700890} // namespace redfish