blob: 30b679deeaddd5c308941b732b43a600ffc15b63 [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>
Nikhil Potadea25aecc2019-08-23 16:35:26 -070026
27namespace redfish
28{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070029inline void requestRoutesStorageCollection(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -070030{
John Edward Broadbent7e860f12021-04-08 15:57:16 -070031 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/")
Ed Tanoused398212021-06-09 17:05:54 -070032 .privileges(redfish::privileges::getStorageCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -070033 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -070034 [&app](const crow::Request& req,
35 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +000036 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070037 {
38 return;
39 }
40 asyncResp->res.jsonValue["@odata.type"] =
41 "#StorageCollection.StorageCollection";
42 asyncResp->res.jsonValue["@odata.id"] =
43 "/redfish/v1/Systems/system/Storage";
44 asyncResp->res.jsonValue["Name"] = "Storage Collection";
45 nlohmann::json::array_t members;
46 nlohmann::json::object_t member;
47 member["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
48 members.emplace_back(member);
49 asyncResp->res.jsonValue["Members"] = std::move(members);
50 asyncResp->res.jsonValue["Members@odata.count"] = 1;
51 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070052}
Nikhil Potadea25aecc2019-08-23 16:35:26 -070053
Willy Tua85afbe2021-12-28 14:43:47 -080054inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
55 const std::shared_ptr<HealthPopulate>& health)
56{
57 crow::connections::systemBus->async_method_call(
58 [asyncResp, health](
59 const boost::system::error_code ec,
60 const dbus::utility::MapperGetSubTreePathsResponse& driveList) {
61 if (ec)
62 {
63 BMCWEB_LOG_ERROR << "Drive mapper call error";
64 messages::internalError(asyncResp->res);
65 return;
66 }
67
68 nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
69 driveArray = nlohmann::json::array();
70 auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
71 count = 0;
72
73 health->inventory.insert(health->inventory.end(), driveList.begin(),
74 driveList.end());
75
76 for (const std::string& drive : driveList)
77 {
78 sdbusplus::message::object_path object(drive);
79 if (object.filename().empty())
80 {
81 BMCWEB_LOG_ERROR << "Failed to find filename in " << drive;
82 return;
83 }
84
85 nlohmann::json::object_t driveJson;
86 driveJson["@odata.id"] =
87 "/redfish/v1/Systems/system/Storage/1/Drives/" +
88 object.filename();
89 driveArray.push_back(std::move(driveJson));
90 }
91
92 count = driveArray.size();
93 },
94 "xyz.openbmc_project.ObjectMapper",
95 "/xyz/openbmc_project/object_mapper",
96 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
97 "/xyz/openbmc_project/inventory", int32_t(0),
98 std::array<const char*, 1>{"xyz.openbmc_project.Inventory.Item.Drive"});
99}
100
101inline void
102 getStorageControllers(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
103 const std::shared_ptr<HealthPopulate>& health)
104{
105 crow::connections::systemBus->async_method_call(
106 [asyncResp,
107 health](const boost::system::error_code ec,
108 const dbus::utility::MapperGetSubTreeResponse& subtree) {
109 if (ec || subtree.empty())
110 {
111 // doesn't have to be there
112 return;
113 }
114
115 nlohmann::json& root = asyncResp->res.jsonValue["StorageControllers"];
116 root = nlohmann::json::array();
117 for (const auto& [path, interfaceDict] : subtree)
118 {
119 sdbusplus::message::object_path object(path);
120 std::string id = object.filename();
121 if (id.empty())
122 {
123 BMCWEB_LOG_ERROR << "Failed to find filename in " << path;
124 return;
125 }
126
127 if (interfaceDict.size() != 1)
128 {
129 BMCWEB_LOG_ERROR << "Connection size " << interfaceDict.size()
130 << ", greater than 1";
131 messages::internalError(asyncResp->res);
132 return;
133 }
134
135 const std::string& connectionName = interfaceDict.front().first;
136
137 size_t index = root.size();
138 nlohmann::json& storageController =
139 root.emplace_back(nlohmann::json::object());
140
141 storageController["@odata.type"] =
142 "#Storage.v1_7_0.StorageController";
143 storageController["@odata.id"] =
144 "/redfish/v1/Systems/system/Storage/1#/StorageControllers/" +
145 std::to_string(index);
146 storageController["Name"] = id;
147 storageController["MemberId"] = id;
148 storageController["Status"]["State"] = "Enabled";
149
150 sdbusplus::asio::getProperty<bool>(
151 *crow::connections::systemBus, connectionName, path,
152 "xyz.openbmc_project.Inventory.Item", "Present",
153 [asyncResp, index](const boost::system::error_code ec2,
154 bool enabled) {
155 // this interface isn't necessary, only check it
156 // if we get a good return
157 if (ec2)
158 {
159 return;
160 }
161 if (!enabled)
162 {
163 asyncResp->res.jsonValue["StorageControllers"][index]
164 ["Status"]["State"] = "Disabled";
165 }
166 });
167
168 crow::connections::systemBus->async_method_call(
169 [asyncResp, index](
170 const boost::system::error_code ec2,
171 const std::vector<
172 std::pair<std::string, dbus::utility::DbusVariantType>>&
173 propertiesList) {
174 if (ec2)
175 {
176 // this interface isn't necessary
177 return;
178 }
179 for (const std::pair<std::string,
180 dbus::utility::DbusVariantType>& property :
181 propertiesList)
182 {
183 // Store DBus properties that are also
184 // Redfish properties with same name and a
185 // string value
186 const std::string& propertyName = property.first;
Ed Tanous8a592812022-06-04 09:06:59 -0700187 nlohmann::json& controller =
Willy Tua85afbe2021-12-28 14:43:47 -0800188 asyncResp->res.jsonValue["StorageControllers"][index];
189 if ((propertyName == "PartNumber") ||
190 (propertyName == "SerialNumber") ||
191 (propertyName == "Manufacturer") ||
192 (propertyName == "Model"))
193 {
194 const std::string* value =
195 std::get_if<std::string>(&property.second);
196 if (value == nullptr)
197 {
198 // illegal property
199 messages::internalError(asyncResp->res);
200 return;
201 }
Ed Tanous8a592812022-06-04 09:06:59 -0700202 controller[propertyName] = *value;
Willy Tua85afbe2021-12-28 14:43:47 -0800203 }
204 }
205 },
206 connectionName, path, "org.freedesktop.DBus.Properties",
207 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
208 }
209
210 // this is done after we know the json array will no longer
211 // be resized, as json::array uses vector underneath and we
212 // need references to its members that won't change
213 size_t count = 0;
214 // Pointer based on |asyncResp->res.jsonValue|
215 nlohmann::json::json_pointer rootPtr =
216 "/StorageControllers"_json_pointer;
217 for (const auto& [path, interfaceDict] : subtree)
218 {
219 auto subHealth = std::make_shared<HealthPopulate>(
220 asyncResp, rootPtr / count / "Status");
221 subHealth->inventory.emplace_back(path);
222 health->inventory.emplace_back(path);
223 health->children.emplace_back(subHealth);
224 count++;
225 }
226 },
227 "xyz.openbmc_project.ObjectMapper",
228 "/xyz/openbmc_project/object_mapper",
229 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
230 "/xyz/openbmc_project/inventory", int32_t(0),
231 std::array<const char*, 1>{
232 "xyz.openbmc_project.Inventory.Item.StorageController"});
233}
234
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700235inline void requestRoutesStorage(App& app)
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700236{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700237 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/")
Ed Tanoused398212021-06-09 17:05:54 -0700238 .privileges(redfish::privileges::getStorage)
Ed Tanous002d39b2022-05-31 08:59:27 -0700239 .methods(boost::beast::http::verb::get)(
240 [&app](const crow::Request& req,
241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000242 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700243 {
244 return;
245 }
246 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
247 asyncResp->res.jsonValue["@odata.id"] =
248 "/redfish/v1/Systems/system/Storage/1";
249 asyncResp->res.jsonValue["Name"] = "Storage";
250 asyncResp->res.jsonValue["Id"] = "1";
251 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
252
253 auto health = std::make_shared<HealthPopulate>(asyncResp);
254 health->populate();
255
Willy Tua85afbe2021-12-28 14:43:47 -0800256 getDrives(asyncResp, health);
257 getStorageControllers(asyncResp, health);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700258 });
259}
260
Willy Tu03913172021-11-08 02:03:19 -0800261inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262 const std::string& connectionName,
263 const std::string& path)
264{
265 crow::connections::systemBus->async_method_call(
Ed Tanous168e20c2021-12-13 14:39:53 -0800266 [asyncResp](const boost::system::error_code ec,
267 const std::vector<
268 std::pair<std::string, dbus::utility::DbusVariantType>>&
269 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700270 if (ec)
271 {
272 // this interface isn't necessary
273 return;
274 }
275 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
276 property : propertiesList)
277 {
278 // Store DBus properties that are also
279 // Redfish properties with same name and a
280 // string value
281 const std::string& propertyName = property.first;
282 if ((propertyName == "PartNumber") ||
283 (propertyName == "SerialNumber") ||
284 (propertyName == "Manufacturer") || (propertyName == "Model"))
Willy Tu03913172021-11-08 02:03:19 -0800285 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700286 const std::string* value =
287 std::get_if<std::string>(&property.second);
288 if (value == nullptr)
Willy Tu03913172021-11-08 02:03:19 -0800289 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700290 // illegal property
291 messages::internalError(asyncResp->res);
292 return;
Willy Tu03913172021-11-08 02:03:19 -0800293 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700294 asyncResp->res.jsonValue[propertyName] = *value;
Willy Tu03913172021-11-08 02:03:19 -0800295 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700296 }
Willy Tu03913172021-11-08 02:03:19 -0800297 },
298 connectionName, path, "org.freedesktop.DBus.Properties", "GetAll",
299 "xyz.openbmc_project.Inventory.Decorator.Asset");
300}
301
302inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
303 const std::string& connectionName,
304 const std::string& path)
305{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700306 sdbusplus::asio::getProperty<bool>(
307 *crow::connections::systemBus, connectionName, path,
308 "xyz.openbmc_project.Inventory.Item", "Present",
Willy Tu03913172021-11-08 02:03:19 -0800309 [asyncResp, path](const boost::system::error_code ec,
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700310 const bool enabled) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700311 // this interface isn't necessary, only check it if
312 // we get a good return
313 if (ec)
314 {
315 return;
316 }
Willy Tu03913172021-11-08 02:03:19 -0800317
Ed Tanous002d39b2022-05-31 08:59:27 -0700318 if (!enabled)
319 {
320 asyncResp->res.jsonValue["Status"]["State"] = "Disabled";
321 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700322 });
Willy Tu03913172021-11-08 02:03:19 -0800323}
324
325inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
326 const std::string& connectionName,
327 const std::string& path)
328{
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700329 sdbusplus::asio::getProperty<bool>(
330 *crow::connections::systemBus, connectionName, path,
331 "xyz.openbmc_project.State.Drive", "Rebuilding",
332 [asyncResp](const boost::system::error_code ec, const bool updating) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700333 // this interface isn't necessary, only check it
334 // if we get a good return
335 if (ec)
336 {
337 return;
338 }
Willy Tu03913172021-11-08 02:03:19 -0800339
Ed Tanous002d39b2022-05-31 08:59:27 -0700340 // updating and disabled in the backend shouldn't be
341 // able to be set at the same time, so we don't need
342 // to check for the race condition of these two
343 // calls
344 if (updating)
345 {
346 asyncResp->res.jsonValue["Status"]["State"] = "Updating";
347 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -0700348 });
Willy Tu03913172021-11-08 02:03:19 -0800349}
350
Willy Tu19b8e9a2021-11-08 02:55:03 -0800351inline std::optional<std::string> convertDriveType(const std::string& type)
352{
353 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
354 {
355 return "HDD";
356 }
357 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
358 {
359 return "SSD";
360 }
361
362 return std::nullopt;
363}
364
365inline std::optional<std::string> convertDriveProtocol(const std::string& proto)
366{
367 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
368 {
369 return "SAS";
370 }
371 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
372 {
373 return "SATA";
374 }
375 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
376 {
377 return "NVMe";
378 }
379 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
380 {
381 return "FC";
382 }
383
384 return std::nullopt;
385}
386
387inline void
388 getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
389 const std::string& connectionName,
390 const std::string& path)
391{
392 sdbusplus::asio::getAllProperties(
393 *crow::connections::systemBus, connectionName, path,
394 "xyz.openbmc_project.Inventory.Item.Drive",
395 [asyncResp](const boost::system::error_code ec,
396 const std::vector<
397 std::pair<std::string, dbus::utility::DbusVariantType>>&
398 propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700399 if (ec)
400 {
401 // this interface isn't required
402 return;
403 }
404 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
405 property : propertiesList)
406 {
407 const std::string& propertyName = property.first;
408 if (propertyName == "Type")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800409 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700410 const std::string* value =
411 std::get_if<std::string>(&property.second);
412 if (value == nullptr)
413 {
414 // illegal property
415 BMCWEB_LOG_ERROR << "Illegal property: Type";
416 messages::internalError(asyncResp->res);
417 return;
418 }
419
420 std::optional<std::string> mediaType = convertDriveType(*value);
421 if (!mediaType)
422 {
423 BMCWEB_LOG_ERROR << "Unsupported DriveType Interface: "
424 << *value;
425 messages::internalError(asyncResp->res);
426 return;
427 }
428
429 asyncResp->res.jsonValue["MediaType"] = *mediaType;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800430 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700431 else if (propertyName == "Capacity")
Willy Tu19b8e9a2021-11-08 02:55:03 -0800432 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700433 const uint64_t* capacity =
434 std::get_if<uint64_t>(&property.second);
435 if (capacity == nullptr)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800436 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700437 BMCWEB_LOG_ERROR << "Illegal property: Capacity";
438 messages::internalError(asyncResp->res);
439 return;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800440 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700441 if (*capacity == 0)
Willy Tu19b8e9a2021-11-08 02:55:03 -0800442 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700443 // drive capacity not known
444 continue;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800445 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800446
Ed Tanous002d39b2022-05-31 08:59:27 -0700447 asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
Willy Tu19b8e9a2021-11-08 02:55:03 -0800448 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700449 else if (propertyName == "Protocol")
450 {
451 const std::string* value =
452 std::get_if<std::string>(&property.second);
453 if (value == nullptr)
454 {
455 BMCWEB_LOG_ERROR << "Illegal property: Protocol";
456 messages::internalError(asyncResp->res);
457 return;
458 }
459
460 std::optional<std::string> proto = convertDriveProtocol(*value);
461 if (!proto)
462 {
463 BMCWEB_LOG_ERROR << "Unsupported DrivePrototype Interface: "
464 << *value;
465 messages::internalError(asyncResp->res);
466 return;
467 }
468 asyncResp->res.jsonValue["Protocol"] = *proto;
469 }
John Edward Broadbent3fe4d5c2022-05-06 14:42:35 -0700470 else if (propertyName == "PredictedMediaLifeLeftPercent")
471 {
472 const uint8_t* lifeLeft =
473 std::get_if<uint8_t>(&property.second);
474 if (lifeLeft == nullptr)
475 {
476 BMCWEB_LOG_ERROR
477 << "Illegal property: PredictedMediaLifeLeftPercent";
478 messages::internalError(asyncResp->res);
479 return;
480 }
481 // 255 means reading the value is not supported
482 if (*lifeLeft != 255)
483 {
484 asyncResp->res.jsonValue["PredictedMediaLifeLeftPercent"] =
485 *lifeLeft;
486 }
487 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700488 }
Willy Tu19b8e9a2021-11-08 02:55:03 -0800489 });
490}
491
Nan Zhoub53dcd92022-06-21 17:47:50 +0000492static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
493 const std::string& connectionName,
494 const std::string& path,
495 const std::vector<std::string>& interfaces)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700496{
497 for (const std::string& interface : interfaces)
498 {
499 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
500 {
501 getDriveAsset(asyncResp, connectionName, path);
502 }
503 else if (interface == "xyz.openbmc_project.Inventory.Item")
504 {
505 getDrivePresent(asyncResp, connectionName, path);
506 }
507 else if (interface == "xyz.openbmc_project.State.Drive")
508 {
509 getDriveState(asyncResp, connectionName, path);
510 }
511 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
512 {
513 getDriveItemProperties(asyncResp, connectionName, path);
514 }
515 }
516}
517
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700518inline void requestRoutesDrive(App& app)
519{
520 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700521 .privileges(redfish::privileges::getDrive)
Ed Tanous002d39b2022-05-31 08:59:27 -0700522 .methods(boost::beast::http::verb::get)(
523 [&app](const crow::Request& req,
524 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
525 const std::string& driveId) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000526 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700527 {
528 return;
529 }
530 crow::connections::systemBus->async_method_call(
531 [asyncResp,
532 driveId](const boost::system::error_code ec,
533 const dbus::utility::MapperGetSubTreeResponse& subtree) {
534 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700535 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700536 BMCWEB_LOG_ERROR << "Drive mapper call error";
537 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700538 return;
539 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700540
Ed Tanous002d39b2022-05-31 08:59:27 -0700541 auto drive = std::find_if(
542 subtree.begin(), subtree.end(),
543 [&driveId](
Nan Zhou8cb65f82022-06-15 05:12:24 +0000544 const std::pair<std::string,
545 dbus::utility::MapperServiceMap>& object) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700546 return sdbusplus::message::object_path(object.first)
547 .filename() == driveId;
548 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700549
Ed Tanous002d39b2022-05-31 08:59:27 -0700550 if (drive == subtree.end())
551 {
552 messages::resourceNotFound(asyncResp->res, "Drive", driveId);
553 return;
554 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700555
Ed Tanous002d39b2022-05-31 08:59:27 -0700556 const std::string& path = drive->first;
Nan Zhou8cb65f82022-06-15 05:12:24 +0000557 const dbus::utility::MapperServiceMap& connectionNames =
558 drive->second;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700559
Ed Tanous002d39b2022-05-31 08:59:27 -0700560 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
561 asyncResp->res.jsonValue["@odata.id"] =
562 "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId;
563 asyncResp->res.jsonValue["Name"] = driveId;
564 asyncResp->res.jsonValue["Id"] = driveId;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700565
Ed Tanous002d39b2022-05-31 08:59:27 -0700566 if (connectionNames.size() != 1)
567 {
568 BMCWEB_LOG_ERROR << "Connection size " << connectionNames.size()
569 << ", not equal to 1";
570 messages::internalError(asyncResp->res);
571 return;
572 }
James Feiste284a7c2019-11-20 16:20:23 -0800573
Ed Tanous002d39b2022-05-31 08:59:27 -0700574 getMainChassisId(
575 asyncResp, [](const std::string& chassisId,
576 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
577 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
578 "/redfish/v1/Chassis/" + chassisId;
579 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700580
Ed Tanous002d39b2022-05-31 08:59:27 -0700581 // default it to Enabled
582 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700583
Ed Tanous002d39b2022-05-31 08:59:27 -0700584 auto health = std::make_shared<HealthPopulate>(asyncResp);
585 health->inventory.emplace_back(path);
586 health->populate();
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700587
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700588 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
589 connectionNames[0].second);
Ed Tanous002d39b2022-05-31 08:59:27 -0700590 },
591 "xyz.openbmc_project.ObjectMapper",
592 "/xyz/openbmc_project/object_mapper",
593 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
594 "/xyz/openbmc_project/inventory", int32_t(0),
595 std::array<const char*, 1>{
596 "xyz.openbmc_project.Inventory.Item.Drive"});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700597 });
598}
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700599
600/**
601 * Chassis drives, this URL will show all the DriveCollection
602 * information
603 */
Nan Zhoub53dcd92022-06-21 17:47:50 +0000604inline void chassisDriveCollectionGet(
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700605 crow::App& app, const crow::Request& req,
606 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
607 const std::string& chassisId)
608{
Carson Labrado3ba00072022-06-06 19:40:56 +0000609 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700610 {
611 return;
612 }
613
614 // mapper call lambda
615 crow::connections::systemBus->async_method_call(
616 [asyncResp,
617 chassisId](const boost::system::error_code ec,
618 const dbus::utility::MapperGetSubTreeResponse& subtree) {
619 if (ec)
620 {
621 if (ec == boost::system::errc::host_unreachable)
622 {
623 messages::resourceNotFound(asyncResp->res, "Chassis",
624 chassisId);
625 return;
626 }
627 messages::internalError(asyncResp->res);
628 return;
629 }
630
631 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000632 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700633 {
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700634 sdbusplus::message::object_path objPath(path);
635 if (objPath.filename() != chassisId)
636 {
637 continue;
638 }
639
640 if (connectionNames.empty())
641 {
642 BMCWEB_LOG_ERROR << "Got 0 Connection names";
643 continue;
644 }
645
646 asyncResp->res.jsonValue["@odata.type"] =
647 "#DriveCollection.DriveCollection";
648 asyncResp->res.jsonValue["@odata.id"] =
John Edward Broadbent14bd7d92022-06-14 17:17:43 -0700649 crow::utility::urlFromPieces("redfish", "v1", "Chassis",
650 chassisId, "Drives");
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700651 asyncResp->res.jsonValue["Name"] = "Drive Collection";
652
653 // Association lambda
654 sdbusplus::asio::getProperty<std::vector<std::string>>(
655 *crow::connections::systemBus,
656 "xyz.openbmc_project.ObjectMapper", path + "/drive",
657 "xyz.openbmc_project.Association", "endpoints",
658 [asyncResp, chassisId](const boost::system::error_code ec3,
659 const std::vector<std::string>& resp) {
660 if (ec3)
661 {
662 BMCWEB_LOG_ERROR << "Error in chassis Drive association ";
663 }
664 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
665 // important if array is empty
666 members = nlohmann::json::array();
667
668 std::vector<std::string> leafNames;
669 for (const auto& drive : resp)
670 {
Ed Tanous8a592812022-06-04 09:06:59 -0700671 sdbusplus::message::object_path drivePath(drive);
672 leafNames.push_back(drivePath.filename());
John Edward Broadbent92903bd2022-04-26 13:40:59 -0700673 }
674
675 std::sort(leafNames.begin(), leafNames.end(),
676 AlphanumLess<std::string>());
677
678 for (const auto& leafName : leafNames)
679 {
680 nlohmann::json::object_t member;
681 member["@odata.id"] = crow::utility::urlFromPieces(
682 "redfish", "v1", "Chassis", chassisId, "Drives",
683 leafName);
684 members.push_back(std::move(member));
685 // navigation links will be registered in next patch set
686 }
687 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
688 }); // end association lambda
689
690 } // end Iterate over all retrieved ObjectPaths
691 },
692 "xyz.openbmc_project.ObjectMapper",
693 "/xyz/openbmc_project/object_mapper",
694 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
695 "/xyz/openbmc_project/inventory", 0,
696 std::array<const char*, 2>{
697 "xyz.openbmc_project.Inventory.Item.Board",
698 "xyz.openbmc_project.Inventory.Item.Chassis"});
699}
700
701inline void requestRoutesChassisDrive(App& app)
702{
703 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
704 .privileges(redfish::privileges::getDriveCollection)
705 .methods(boost::beast::http::verb::get)(
706 std::bind_front(chassisDriveCollectionGet, std::ref(app)));
707}
708
Nan Zhoub53dcd92022-06-21 17:47:50 +0000709inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
710 const std::string& chassisId,
711 const std::string& driveName,
712 const boost::system::error_code ec,
713 const dbus::utility::MapperGetSubTreeResponse& subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700714{
715
716 if (ec)
717 {
718 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
719 messages::internalError(asyncResp->res);
720 return;
721 }
722
723 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000724 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700725 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700726 sdbusplus::message::object_path objPath(path);
727 if (objPath.filename() != driveName)
728 {
729 continue;
730 }
731
732 if (connectionNames.empty())
733 {
734 BMCWEB_LOG_ERROR << "Got 0 Connection names";
735 continue;
736 }
737
738 asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700739 "redfish", "v1", "Chassis", chassisId, "Drives", driveName);
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700740
741 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
John Edward Broadbenta0cb40c2022-06-29 17:27:38 -0700742 asyncResp->res.jsonValue["Name"] = driveName;
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700743 asyncResp->res.jsonValue["Id"] = driveName;
744 // default it to Enabled
745 asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
746
747 nlohmann::json::object_t linkChassisNav;
748 linkChassisNav["@odata.id"] =
749 crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId);
750 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
751
752 addAllDriveInfo(asyncResp, connectionNames[0].first, path,
753 connectionNames[0].second);
754 }
755}
756
Nan Zhoub53dcd92022-06-21 17:47:50 +0000757inline void
758 matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
759 const std::string& chassisId,
760 const std::string& driveName,
761 const std::vector<std::string>& resp)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700762{
763
764 for (const std::string& drivePath : resp)
765 {
766 sdbusplus::message::object_path path(drivePath);
767 std::string leaf = path.filename();
768 if (leaf != driveName)
769 {
770 continue;
771 }
772 // mapper call drive
773 const std::array<const char*, 1> driveInterface = {
774 "xyz.openbmc_project.Inventory.Item.Drive"};
775
776 crow::connections::systemBus->async_method_call(
777 [asyncResp, chassisId, driveName](
778 const boost::system::error_code ec,
779 const dbus::utility::MapperGetSubTreeResponse& subtree) {
780 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
781 },
782 "xyz.openbmc_project.ObjectMapper",
783 "/xyz/openbmc_project/object_mapper",
784 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
785 "/xyz/openbmc_project/inventory", 0, driveInterface);
786 }
787}
788
Nan Zhoub53dcd92022-06-21 17:47:50 +0000789inline void
790 handleChassisDriveGet(crow::App& app, const crow::Request& req,
791 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
792 const std::string& chassisId,
793 const std::string& driveName)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700794{
Michal Orzel03810a12022-06-15 14:04:28 +0200795 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700796 {
797 return;
798 }
799 const std::array<const char*, 2> interfaces = {
800 "xyz.openbmc_project.Inventory.Item.Board",
801 "xyz.openbmc_project.Inventory.Item.Chassis"};
802
803 // mapper call chassis
804 crow::connections::systemBus->async_method_call(
805 [asyncResp, chassisId,
806 driveName](const boost::system::error_code ec,
807 const dbus::utility::MapperGetSubTreeResponse& subtree) {
808 if (ec)
809 {
810 messages::internalError(asyncResp->res);
811 return;
812 }
813
814 // Iterate over all retrieved ObjectPaths.
Nan Zhou8cb65f82022-06-15 05:12:24 +0000815 for (const auto& [path, connectionNames] : subtree)
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700816 {
John Edward Broadbente56ed6b2022-04-26 13:40:59 -0700817 sdbusplus::message::object_path objPath(path);
818 if (objPath.filename() != chassisId)
819 {
820 continue;
821 }
822
823 if (connectionNames.empty())
824 {
825 BMCWEB_LOG_ERROR << "Got 0 Connection names";
826 continue;
827 }
828
829 sdbusplus::asio::getProperty<std::vector<std::string>>(
830 *crow::connections::systemBus,
831 "xyz.openbmc_project.ObjectMapper", path + "/drive",
832 "xyz.openbmc_project.Association", "endpoints",
833 [asyncResp, chassisId,
834 driveName](const boost::system::error_code ec3,
835 const std::vector<std::string>& resp) {
836 if (ec3)
837 {
838 return; // no drives = no failures
839 }
840 matchAndFillDrive(asyncResp, chassisId, driveName, resp);
841 });
842 break;
843 }
844 },
845 "xyz.openbmc_project.ObjectMapper",
846 "/xyz/openbmc_project/object_mapper",
847 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
848 "/xyz/openbmc_project/inventory", 0, interfaces);
849}
850
851/**
852 * This URL will show the drive interface for the specific drive in the chassis
853 */
854inline void requestRoutesChassisDriveName(App& app)
855{
856 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
857 .privileges(redfish::privileges::getChassis)
858 .methods(boost::beast::http::verb::get)(
859 std::bind_front(handleChassisDriveGet, std::ref(app)));
860}
861
Nikhil Potadea25aecc2019-08-23 16:35:26 -0700862} // namespace redfish