blob: f4a66041b6fb33b5000fbee003749eb473751dbe [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
James Feist2ad9c2f2019-10-29 16:26:48 -070018#include "health.hpp"
James Feiste284a7c2019-11-20 16:20:23 -080019#include "openbmc_dbus_rest.hpp"
James Feist2ad9c2f2019-10-29 16:26:48 -070020
John Edward Broadbent7e860f12021-04-08 15:57:16 -070021#include <app.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080022#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070023#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070024#include <registries/privilege_registry.hpp>
Jonathan Doman1e1e5982021-06-11 09:36:17 -070025#include <sdbusplus/asio/property.hpp>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020026#include <sdbusplus/unpack_properties.hpp>
27#include <utils/dbus_utils.hpp>
Nikhil Potadea25aecc2019-08-23 16:35:26 -070028
29namespace redfish
30{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070031inline void requestRoutesStorageCollection(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070032{
Ed Tanous22d268c2022-05-19 09:39:07 -070033 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
Ed Tanoused398212021-06-09 17:05:54 -070034 .privileges(redfish::privileges::getStorageCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070035 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -070036 [&app](const crow::Request& req,
Ed Tanous22d268c2022-05-19 09:39:07 -070037 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
38 const std::string& systemName) {
Carson Labrado3ba00072022-06-06 19:40:56 +000039 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070040 {
41 return;
42 }
Ed Tanous22d268c2022-05-19 09:39:07 -070043 if (systemName != "system")
44 {
45 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
46 systemName);
47 return;
48 }
49
Ed Tanous002d39b2022-05-31 08:59:27 -070050 asyncResp->res.jsonValue["@odata.type"] =
51 "#StorageCollection.StorageCollection";
52 asyncResp->res.jsonValue["@odata.id"] =
53 "/redfish/v1/Systems/system/Storage";
54 asyncResp->res.jsonValue["Name"] = "Storage Collection";
55 nlohmann::json::array_t members;
56 nlohmann::json::object_t member;
57 member["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
58 members.emplace_back(member);
59 asyncResp->res.jsonValue["Members"] = std::move(members);
60 asyncResp->res.jsonValue["Members@odata.count"] = 1;
61 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070062}
Nikhil Potadea25aecc2019-08-23 16:35:26 -070063
Willy Tua85afbe2021-12-28 14:43:47 -080064inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
65 const std::shared_ptr<HealthPopulate>& health)
66{
67 crow::connections::systemBus->async_method_call(
68 [asyncResp, health](
69 const boost::system::error_code ec,
70 const dbus::utility::MapperGetSubTreePathsResponse& driveList) {
71 if (ec)
72 {
73 BMCWEB_LOG_ERROR << "Drive mapper call error";
74 messages::internalError(asyncResp->res);
75 return;
76 }
77
78 nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
79 driveArray = nlohmann::json::array();
80 auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
81 count = 0;
82
83 health->inventory.insert(health->inventory.end(), driveList.begin(),
84 driveList.end());
85
86 for (const std::string& drive : driveList)
87 {
88 sdbusplus::message::object_path object(drive);
89 if (object.filename().empty())
90 {
91 BMCWEB_LOG_ERROR << "Failed to find filename in " << drive;
92 return;
93 }
94
95 nlohmann::json::object_t driveJson;
96 driveJson["@odata.id"] =
97 "/redfish/v1/Systems/system/Storage/1/Drives/" +
98 object.filename();
99 driveArray.push_back(std::move(driveJson));
100 }
101
102 count = driveArray.size();
103 },
104 "xyz.openbmc_project.ObjectMapper",
105 "/xyz/openbmc_project/object_mapper",
106 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
107 "/xyz/openbmc_project/inventory", int32_t(0),
108 std::array<const char*, 1>{"xyz.openbmc_project.Inventory.Item.Drive"});
109}
110
111inline void
112 getStorageControllers(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
113 const std::shared_ptr<HealthPopulate>& health)
114{
115 crow::connections::systemBus->async_method_call(
116 [asyncResp,
117 health](const boost::system::error_code ec,
118 const dbus::utility::MapperGetSubTreeResponse& subtree) {
119 if (ec || subtree.empty())
120 {
121 // doesn't have to be there
122 return;
123 }
124
125 nlohmann::json& root = asyncResp->res.jsonValue["StorageControllers"];
126 root = nlohmann::json::array();
127 for (const auto& [path, interfaceDict] : subtree)
128 {
129 sdbusplus::message::object_path object(path);
130 std::string id = object.filename();
131 if (id.empty())
132 {
133 BMCWEB_LOG_ERROR << "Failed to find filename in " << path;
134 return;
135 }
136
137 if (interfaceDict.size() != 1)
138 {
139 BMCWEB_LOG_ERROR << "Connection size " << interfaceDict.size()
140 << ", greater than 1";
141 messages::internalError(asyncResp->res);
142 return;
143 }
144
145 const std::string& connectionName = interfaceDict.front().first;
146
147 size_t index = root.size();
148 nlohmann::json& storageController =
149 root.emplace_back(nlohmann::json::object());
150
151 storageController["@odata.type"] =
152 "#Storage.v1_7_0.StorageController";
153 storageController["@odata.id"] =
154 "/redfish/v1/Systems/system/Storage/1#/StorageControllers/" +
155 std::to_string(index);
156 storageController["Name"] = id;
157 storageController["MemberId"] = id;
158 storageController["Status"]["State"] = "Enabled";
159
160 sdbusplus::asio::getProperty<bool>(
161 *crow::connections::systemBus, connectionName, path,
162 "xyz.openbmc_project.Inventory.Item", "Present",
163 [asyncResp, index](const boost::system::error_code ec2,
164 bool enabled) {
165 // this interface isn't necessary, only check it
166 // if we get a good return
167 if (ec2)
168 {
169 return;
170 }
171 if (!enabled)
172 {
173 asyncResp->res.jsonValue["StorageControllers"][index]
174 ["Status"]["State"] = "Disabled";
175 }
176 });
177
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200178 sdbusplus::asio::getAllProperties(
179 *crow::connections::systemBus, connectionName, path,
180 "xyz.openbmc_project.Inventory.Decorator.Asset",
Willy Tua85afbe2021-12-28 14:43:47 -0800181 [asyncResp, index](
182 const boost::system::error_code ec2,
183 const std::vector<
184 std::pair<std::string, dbus::utility::DbusVariantType>>&
185 propertiesList) {
186 if (ec2)
187 {
188 // this interface isn't necessary
189 return;
190 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200191
192 const std::string* partNumber = nullptr;
193 const std::string* serialNumber = nullptr;
194 const std::string* manufacturer = nullptr;
195 const std::string* model = nullptr;
196
197 const bool success = sdbusplus::unpackPropertiesNoThrow(
198 dbus_utils::UnpackErrorPrinter(), propertiesList,
199 "PartNumber", partNumber, "SerialNumber", serialNumber,
200 "Manufacturer", manufacturer, "Model", model);
201
202 if (!success)
Willy Tua85afbe2021-12-28 14:43:47 -0800203 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200204 messages::internalError(asyncResp->res);
205 return;
Willy Tua85afbe2021-12-28 14:43:47 -0800206 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200207
208 nlohmann::json& controller =
209 asyncResp->res.jsonValue["StorageControllers"][index];
210
211 if (partNumber != nullptr)
212 {
213 controller["PartNumber"] = *partNumber;
214 }
215
216 if (serialNumber != nullptr)
217 {
218 controller["SerialNumber"] = *serialNumber;
219 }
220
221 if (manufacturer != nullptr)
222 {
223 controller["Manufacturer"] = *manufacturer;
224 }
225
226 if (model != nullptr)
227 {
228 controller["Model"] = *model;
229 }
230 });
Willy Tua85afbe2021-12-28 14:43:47 -0800231 }
232
233 // this is done after we know the json array will no longer
234 // be resized, as json::array uses vector underneath and we
235 // need references to its members that won't change
236 size_t count = 0;
237 // Pointer based on |asyncResp->res.jsonValue|
238 nlohmann::json::json_pointer rootPtr =
239 "/StorageControllers"_json_pointer;
240 for (const auto& [path, interfaceDict] : subtree)
241 {
242 auto subHealth = std::make_shared<HealthPopulate>(
243 asyncResp, rootPtr / count / "Status");
244 subHealth->inventory.emplace_back(path);
245 health->inventory.emplace_back(path);
246 health->children.emplace_back(subHealth);
247 count++;
248 }
249 },
250 "xyz.openbmc_project.ObjectMapper",
251 "/xyz/openbmc_project/object_mapper",
252 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
253 "/xyz/openbmc_project/inventory", int32_t(0),
254 std::array<const char*, 1>{
255 "xyz.openbmc_project.Inventory.Item.StorageController"});
256}
257
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700258inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700259{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700260 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/")
Ed Tanoused398212021-06-09 17:05:54 -0700261 .privileges(redfish::privileges::getStorage)
Ed Tanous002d39b2022-05-31 08:59:27 -0700262 .methods(boost::beast::http::verb::get)(
263 [&app](const crow::Request& req,
264 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000265 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700266 {
267 return;
268 }
269 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
270 asyncResp->res.jsonValue["@odata.id"] =
271 "/redfish/v1/Systems/system/Storage/1";
272 asyncResp->res.jsonValue["Name"] = "Storage";
273 asyncResp->res.jsonValue["Id"] = "1";
274 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
275
276 auto health = std::make_shared<HealthPopulate>(asyncResp);
277 health->populate();
278
Willy Tua85afbe2021-12-28 14:43:47 -0800279 getDrives(asyncResp, health);
280 getStorageControllers(asyncResp, health);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700281 });
282}
283
Willy Tu03913172021-11-08 02:03:19 -0800284inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
285 const std::string& connectionName,
286 const std::string& path)
287{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200288 sdbusplus::asio::getAllProperties(
289 *crow::connections::systemBus, connectionName, path,
290 "xyz.openbmc_project.Inventory.Decorator.Asset",
Ed Tanous168e20c2021-12-13 14:39:53 -0800291 [asyncResp](const boost::system::error_code ec,
292 const std::vector<
293 std::pair<std::string, dbus::utility::DbusVariantType>>&
294 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700295 if (ec)
296 {
297 // this interface isn't necessary
298 return;
299 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200300
301 const std::string* partNumber = nullptr;
302 const std::string* serialNumber = nullptr;
303 const std::string* manufacturer = nullptr;
304 const std::string* model = nullptr;
305
306 const bool success = sdbusplus::unpackPropertiesNoThrow(
307 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
308 partNumber, "SerialNumber", serialNumber, "Manufacturer",
309 manufacturer, "Model", model);
310
311 if (!success)
Ed Tanous002d39b2022-05-31 08:59:27 -0700312 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200313 messages::internalError(asyncResp->res);
314 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700315 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200316
317 if (partNumber != nullptr)
318 {
319 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
320 }
321
322 if (serialNumber != nullptr)
323 {
324 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
325 }
326
327 if (manufacturer != nullptr)
328 {
329 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
330 }
331
332 if (model != nullptr)
333 {
334 asyncResp->res.jsonValue["Model"] = *model;
335 }
336 });
Willy Tu03913172021-11-08 02:03:19 -0800337}
338
339inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
340 const std::string& connectionName,
341 const std::string& path)
342{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700343 sdbusplus::asio::getProperty<bool>(
344 *crow::connections::systemBus, connectionName, path,
345 "xyz.openbmc_project.Inventory.Item", "Present",
Willy Tu03913172021-11-08 02:03:19 -0800346 [asyncResp, path](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700347 const bool enabled) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700348 // this interface isn't necessary, only check it if
349 // we get a good return
350 if (ec)
351 {
352 return;
353 }
Willy Tu03913172021-11-08 02:03:19 -0800354
Ed Tanous002d39b2022-05-31 08:59:27 -0700355 if (!enabled)
356 {
357 asyncResp->res.jsonValue["Status"]["State"] = "Disabled";
358 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700359 });
Willy Tu03913172021-11-08 02:03:19 -0800360}
361
362inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
363 const std::string& connectionName,
364 const std::string& path)
365{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700366 sdbusplus::asio::getProperty<bool>(
367 *crow::connections::systemBus, connectionName, path,
368 "xyz.openbmc_project.State.Drive", "Rebuilding",
369 [asyncResp](const boost::system::error_code ec, const bool updating) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700370 // this interface isn't necessary, only check it
371 // if we get a good return
372 if (ec)
373 {
374 return;
375 }
Willy Tu03913172021-11-08 02:03:19 -0800376
Ed Tanous002d39b2022-05-31 08:59:27 -0700377 // updating and disabled in the backend shouldn't be
378 // able to be set at the same time, so we don't need
379 // to check for the race condition of these two
380 // calls
381 if (updating)
382 {
383 asyncResp->res.jsonValue["Status"]["State"] = "Updating";
384 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700385 });
Willy Tu03913172021-11-08 02:03:19 -0800386}
387
Willy Tu19b8e9a2021-11-08 02:55:03 -0800388inline std::optional<std::string> convertDriveType(const std::string& type)
389{
390 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
391 {
392 return "HDD";
393 }
394 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
395 {
396 return "SSD";
397 }
398
399 return std::nullopt;
400}
401
402inline std::optional<std::string> convertDriveProtocol(const std::string& proto)
403{
404 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
405 {
406 return "SAS";
407 }
408 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
409 {
410 return "SATA";
411 }
412 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
413 {
414 return "NVMe";
415 }
416 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
417 {
418 return "FC";
419 }
420
421 return std::nullopt;
422}
423
424inline void
425 getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
426 const std::string& connectionName,
427 const std::string& path)
428{
429 sdbusplus::asio::getAllProperties(
430 *crow::connections::systemBus, connectionName, path,
431 "xyz.openbmc_project.Inventory.Item.Drive",
432 [asyncResp](const boost::system::error_code ec,
433 const std::vector<
434 std::pair<std::string, dbus::utility::DbusVariantType>>&
435 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700436 if (ec)
437 {
438 // this interface isn't required
439 return;
440 }
441 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
442 property : propertiesList)
443 {
444 const std::string& propertyName = property.first;
445 if (propertyName == "Type")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800446 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700447 const std::string* value =
448 std::get_if<std::string>(&property.second);
449 if (value == nullptr)
450 {
451 // illegal property
452 BMCWEB_LOG_ERROR << "Illegal property: Type";
453 messages::internalError(asyncResp->res);
454 return;
455 }
456
457 std::optional<std::string> mediaType = convertDriveType(*value);
458 if (!mediaType)
459 {
460 BMCWEB_LOG_ERROR << "Unsupported DriveType Interface: "
461 << *value;
462 messages::internalError(asyncResp->res);
463 return;
464 }
465
466 asyncResp->res.jsonValue["MediaType"] = *mediaType;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800467 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700468 else if (propertyName == "Capacity")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800469 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700470 const uint64_t* capacity =
471 std::get_if<uint64_t>(&property.second);
472 if (capacity == nullptr)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800473 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700474 BMCWEB_LOG_ERROR << "Illegal property: Capacity";
475 messages::internalError(asyncResp->res);
476 return;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800477 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700478 if (*capacity == 0)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800479 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700480 // drive capacity not known
481 continue;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800482 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800483
Ed Tanous002d39b2022-05-31 08:59:27 -0700484 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800485 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700486 else if (propertyName == "Protocol")
487 {
488 const std::string* value =
489 std::get_if<std::string>(&property.second);
490 if (value == nullptr)
491 {
492 BMCWEB_LOG_ERROR << "Illegal property: Protocol";
493 messages::internalError(asyncResp->res);
494 return;
495 }
496
497 std::optional<std::string> proto = convertDriveProtocol(*value);
498 if (!proto)
499 {
500 BMCWEB_LOG_ERROR << "Unsupported DrivePrototype Interface: "
501 << *value;
502 messages::internalError(asyncResp->res);
503 return;
504 }
505 asyncResp->res.jsonValue["Protocol"] = *proto;
506 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700507 else if (propertyName == "PredictedMediaLifeLeftPercent")
508 {
509 const uint8_t* lifeLeft =
510 std::get_if<uint8_t>(&property.second);
511 if (lifeLeft == nullptr)
512 {
513 BMCWEB_LOG_ERROR
514 << "Illegal property: PredictedMediaLifeLeftPercent";
515 messages::internalError(asyncResp->res);
516 return;
517 }
518 // 255 means reading the value is not supported
519 if (*lifeLeft != 255)
520 {
521 asyncResp->res.jsonValue["PredictedMediaLifeLeftPercent"] =
522 *lifeLeft;
523 }
524 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700525 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800526 });
527}
528
Nan Zhoub53dcd92022-06-21 17:47:50 +0000529static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
530 const std::string& connectionName,
531 const std::string& path,
532 const std::vector<std::string>& interfaces)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700533{
534 for (const std::string& interface : interfaces)
535 {
536 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
537 {
538 getDriveAsset(asyncResp, connectionName, path);
539 }
540 else if (interface == "xyz.openbmc_project.Inventory.Item")
541 {
542 getDrivePresent(asyncResp, connectionName, path);
543 }
544 else if (interface == "xyz.openbmc_project.State.Drive")
545 {
546 getDriveState(asyncResp, connectionName, path);
547 }
548 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
549 {
550 getDriveItemProperties(asyncResp, connectionName, path);
551 }
552 }
553}
554
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700555inline void requestRoutesDrive(App& app)
556{
Ed Tanous22d268c2022-05-19 09:39:07 -0700557 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700558 .privileges(redfish::privileges::getDrive)
Ed Tanous002d39b2022-05-31 08:59:27 -0700559 .methods(boost::beast::http::verb::get)(
560 [&app](const crow::Request& req,
561 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous22d268c2022-05-19 09:39:07 -0700562 const std::string& systemName, const std::string& driveId) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000563 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700564 {
565 return;
566 }
Ed Tanous22d268c2022-05-19 09:39:07 -0700567 if (systemName != "system")
568 {
569 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
570 systemName);
571 return;
572 }
573
Ed Tanous002d39b2022-05-31 08:59:27 -0700574 crow::connections::systemBus->async_method_call(
575 [asyncResp,
576 driveId](const boost::system::error_code ec,
577 const dbus::utility::MapperGetSubTreeResponse& subtree) {
578 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700579 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700580 BMCWEB_LOG_ERROR << "Drive mapper call error";
581 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700582 return;
583 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700584
Ed Tanous002d39b2022-05-31 08:59:27 -0700585 auto drive = std::find_if(
586 subtree.begin(), subtree.end(),
587 [&driveId](
Nan Zhou8cb65f82022-06-15 05:12:24 +0000588 const std::pair<std::string,
589 dbus::utility::MapperServiceMap>& object) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700590 return sdbusplus::message::object_path(object.first)
591 .filename() == driveId;
592 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700593
Ed Tanous002d39b2022-05-31 08:59:27 -0700594 if (drive == subtree.end())
595 {
596 messages::resourceNotFound(asyncResp->res, "Drive", driveId);
597 return;
598 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700599
Ed Tanous002d39b2022-05-31 08:59:27 -0700600 const std::string& path = drive->first;
Nan Zhou8cb65f82022-06-15 05:12:24 +0000601 const dbus::utility::MapperServiceMap& connectionNames =
602 drive->second;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700603
Ed Tanous002d39b2022-05-31 08:59:27 -0700604 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
605 asyncResp->res.jsonValue["@odata.id"] =
606 "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId;
607 asyncResp->res.jsonValue["Name"] = driveId;
608 asyncResp->res.jsonValue["Id"] = driveId;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700609
Ed Tanous002d39b2022-05-31 08:59:27 -0700610 if (connectionNames.size() != 1)
611 {
612 BMCWEB_LOG_ERROR << "Connection size " << connectionNames.size()
613 << ", not equal to 1";
614 messages::internalError(asyncResp->res);
615 return;
616 }
James Feiste284a7c2019-11-20 16:20:23 -0800617
Ed Tanous002d39b2022-05-31 08:59:27 -0700618 getMainChassisId(
619 asyncResp, [](const std::string& chassisId,
620 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
621 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
622 "/redfish/v1/Chassis/" + chassisId;
623 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700624
Ed Tanous002d39b2022-05-31 08:59:27 -0700625 // default it to Enabled
626 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700627
Ed Tanous002d39b2022-05-31 08:59:27 -0700628 auto health = std::make_shared<HealthPopulate>(asyncResp);
629 health->inventory.emplace_back(path);
630 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700631
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700632 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
633 connectionNames[0].second);
Ed Tanous002d39b2022-05-31 08:59:27 -0700634 },
635 "xyz.openbmc_project.ObjectMapper",
636 "/xyz/openbmc_project/object_mapper",
637 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
638 "/xyz/openbmc_project/inventory", int32_t(0),
639 std::array<const char*, 1>{
640 "xyz.openbmc_project.Inventory.Item.Drive"});
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
659 crow::connections::systemBus->async_method_call(
660 [asyncResp,
661 chassisId](const boost::system::error_code ec,
662 const dbus::utility::MapperGetSubTreeResponse& subtree) {
663 if (ec)
664 {
665 if (ec == boost::system::errc::host_unreachable)
666 {
667 messages::resourceNotFound(asyncResp->res, "Chassis",
668 chassisId);
669 return;
670 }
671 messages::internalError(asyncResp->res);
672 return;
673 }
674
675 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000676 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700677 {
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700678 sdbusplus::message::object_path objPath(path);
679 if (objPath.filename() != chassisId)
680 {
681 continue;
682 }
683
684 if (connectionNames.empty())
685 {
686 BMCWEB_LOG_ERROR << "Got 0 Connection names";
687 continue;
688 }
689
690 asyncResp->res.jsonValue["@odata.type"] =
691 "#DriveCollection.DriveCollection";
692 asyncResp->res.jsonValue["@odata.id"] =
John Edward Broadbent14bd7d92022-06-14 17:17:43 -0700693 crow::utility::urlFromPieces("redfish", "v1", "Chassis",
694 chassisId, "Drives");
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700695 asyncResp->res.jsonValue["Name"] = "Drive Collection";
696
697 // Association lambda
698 sdbusplus::asio::getProperty<std::vector<std::string>>(
699 *crow::connections::systemBus,
700 "xyz.openbmc_project.ObjectMapper", path + "/drive",
701 "xyz.openbmc_project.Association", "endpoints",
702 [asyncResp, chassisId](const boost::system::error_code ec3,
703 const std::vector<std::string>& resp) {
704 if (ec3)
705 {
706 BMCWEB_LOG_ERROR << "Error in chassis Drive association ";
707 }
708 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
709 // important if array is empty
710 members = nlohmann::json::array();
711
712 std::vector<std::string> leafNames;
713 for (const auto& drive : resp)
714 {
Ed Tanous8a592812022-06-04 09:06:59 -0700715 sdbusplus::message::object_path drivePath(drive);
716 leafNames.push_back(drivePath.filename());
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700717 }
718
719 std::sort(leafNames.begin(), leafNames.end(),
720 AlphanumLess<std::string>());
721
722 for (const auto& leafName : leafNames)
723 {
724 nlohmann::json::object_t member;
725 member["@odata.id"] = crow::utility::urlFromPieces(
726 "redfish", "v1", "Chassis", chassisId, "Drives",
727 leafName);
728 members.push_back(std::move(member));
729 // navigation links will be registered in next patch set
730 }
731 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
732 }); // end association lambda
733
734 } // end Iterate over all retrieved ObjectPaths
735 },
736 "xyz.openbmc_project.ObjectMapper",
737 "/xyz/openbmc_project/object_mapper",
738 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
739 "/xyz/openbmc_project/inventory", 0,
740 std::array<const char*, 2>{
741 "xyz.openbmc_project.Inventory.Item.Board",
742 "xyz.openbmc_project.Inventory.Item.Chassis"});
743}
744
745inline void requestRoutesChassisDrive(App& app)
746{
747 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
748 .privileges(redfish::privileges::getDriveCollection)
749 .methods(boost::beast::http::verb::get)(
750 std::bind_front(chassisDriveCollectionGet, std::ref(app)));
751}
752
Nan Zhoub53dcd92022-06-21 17:47:50 +0000753inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
754 const std::string& chassisId,
755 const std::string& driveName,
756 const boost::system::error_code ec,
757 const dbus::utility::MapperGetSubTreeResponse& subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700758{
759
760 if (ec)
761 {
762 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
763 messages::internalError(asyncResp->res);
764 return;
765 }
766
767 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000768 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700769 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700770 sdbusplus::message::object_path objPath(path);
771 if (objPath.filename() != driveName)
772 {
773 continue;
774 }
775
776 if (connectionNames.empty())
777 {
778 BMCWEB_LOG_ERROR << "Got 0 Connection names";
779 continue;
780 }
781
782 asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700783 "redfish", "v1", "Chassis", chassisId, "Drives", driveName);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700784
785 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700786 asyncResp->res.jsonValue["Name"] = driveName;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700787 asyncResp->res.jsonValue["Id"] = driveName;
788 // default it to Enabled
789 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
790
791 nlohmann::json::object_t linkChassisNav;
792 linkChassisNav["@odata.id"] =
793 crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId);
794 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
795
796 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
797 connectionNames[0].second);
798 }
799}
800
Nan Zhoub53dcd92022-06-21 17:47:50 +0000801inline void
802 matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
803 const std::string& chassisId,
804 const std::string& driveName,
805 const std::vector<std::string>& resp)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700806{
807
808 for (const std::string& drivePath : resp)
809 {
810 sdbusplus::message::object_path path(drivePath);
811 std::string leaf = path.filename();
812 if (leaf != driveName)
813 {
814 continue;
815 }
816 // mapper call drive
817 const std::array<const char*, 1> driveInterface = {
818 "xyz.openbmc_project.Inventory.Item.Drive"};
819
820 crow::connections::systemBus->async_method_call(
821 [asyncResp, chassisId, driveName](
822 const boost::system::error_code ec,
823 const dbus::utility::MapperGetSubTreeResponse& subtree) {
824 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
825 },
826 "xyz.openbmc_project.ObjectMapper",
827 "/xyz/openbmc_project/object_mapper",
828 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
829 "/xyz/openbmc_project/inventory", 0, driveInterface);
830 }
831}
832
Nan Zhoub53dcd92022-06-21 17:47:50 +0000833inline void
834 handleChassisDriveGet(crow::App& app, const crow::Request& req,
835 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
836 const std::string& chassisId,
837 const std::string& driveName)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700838{
Michal Orzel03810a12022-06-15 14:04:28 +0200839 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700840 {
841 return;
842 }
843 const std::array<const char*, 2> interfaces = {
844 "xyz.openbmc_project.Inventory.Item.Board",
845 "xyz.openbmc_project.Inventory.Item.Chassis"};
846
847 // mapper call chassis
848 crow::connections::systemBus->async_method_call(
849 [asyncResp, chassisId,
850 driveName](const boost::system::error_code ec,
851 const dbus::utility::MapperGetSubTreeResponse& subtree) {
852 if (ec)
853 {
854 messages::internalError(asyncResp->res);
855 return;
856 }
857
858 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000859 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700860 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700861 sdbusplus::message::object_path objPath(path);
862 if (objPath.filename() != chassisId)
863 {
864 continue;
865 }
866
867 if (connectionNames.empty())
868 {
869 BMCWEB_LOG_ERROR << "Got 0 Connection names";
870 continue;
871 }
872
873 sdbusplus::asio::getProperty<std::vector<std::string>>(
874 *crow::connections::systemBus,
875 "xyz.openbmc_project.ObjectMapper", path + "/drive",
876 "xyz.openbmc_project.Association", "endpoints",
877 [asyncResp, chassisId,
878 driveName](const boost::system::error_code ec3,
879 const std::vector<std::string>& resp) {
880 if (ec3)
881 {
882 return; // no drives = no failures
883 }
884 matchAndFillDrive(asyncResp, chassisId, driveName, resp);
885 });
886 break;
887 }
888 },
889 "xyz.openbmc_project.ObjectMapper",
890 "/xyz/openbmc_project/object_mapper",
891 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
892 "/xyz/openbmc_project/inventory", 0, interfaces);
893}
894
895/**
896 * This URL will show the drive interface for the specific drive in the chassis
897 */
898inline void requestRoutesChassisDriveName(App& app)
899{
900 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
901 .privileges(redfish::privileges::getChassis)
902 .methods(boost::beast::http::verb::get)(
903 std::bind_front(handleChassisDriveGet, std::ref(app)));
904}
905
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700906} // namespace redfish