xref: /openbmc/bmcweb/redfish-core/lib/storage.hpp (revision 6be832e2)
1 /*
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 
18 #include "bmcweb_config.h"
19 
20 #include "app.hpp"
21 #include "dbus_utility.hpp"
22 #include "generated/enums/drive.hpp"
23 #include "generated/enums/protocol.hpp"
24 #include "generated/enums/resource.hpp"
25 #include "human_sort.hpp"
26 #include "query.hpp"
27 #include "redfish_util.hpp"
28 #include "registries/privilege_registry.hpp"
29 #include "utils/collection.hpp"
30 #include "utils/dbus_utils.hpp"
31 
32 #include <boost/system/error_code.hpp>
33 #include <boost/url/format.hpp>
34 #include <sdbusplus/asio/property.hpp>
35 #include <sdbusplus/unpack_properties.hpp>
36 
37 #include <array>
38 #include <ranges>
39 #include <string_view>
40 
41 namespace redfish
42 {
43 
handleSystemsStorageCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)44 inline void handleSystemsStorageCollectionGet(
45     App& app, const crow::Request& req,
46     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
47     const std::string& systemName)
48 {
49     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
50     {
51         return;
52     }
53     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
54     {
55         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
56                                    systemName);
57         return;
58     }
59 
60     asyncResp->res.jsonValue["@odata.type"] =
61         "#StorageCollection.StorageCollection";
62     asyncResp->res.jsonValue["@odata.id"] = std::format(
63         "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME);
64     asyncResp->res.jsonValue["Name"] = "Storage Collection";
65 
66     constexpr std::array<std::string_view, 1> interface{
67         "xyz.openbmc_project.Inventory.Item.Storage"};
68     collection_util::getCollectionMembers(
69         asyncResp,
70         boost::urls::format("/redfish/v1/Systems/{}/Storage",
71                             BMCWEB_REDFISH_SYSTEM_URI_NAME),
72         interface, "/xyz/openbmc_project/inventory");
73 }
74 
handleStorageCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)75 inline void handleStorageCollectionGet(
76     App& app, const crow::Request& req,
77     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
78 {
79     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
80     {
81         return;
82     }
83     asyncResp->res.jsonValue["@odata.type"] =
84         "#StorageCollection.StorageCollection";
85     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Storage";
86     asyncResp->res.jsonValue["Name"] = "Storage Collection";
87     constexpr std::array<std::string_view, 1> interface{
88         "xyz.openbmc_project.Inventory.Item.Storage"};
89     collection_util::getCollectionMembers(
90         asyncResp, boost::urls::format("/redfish/v1/Storage"), interface,
91         "/xyz/openbmc_project/inventory");
92 }
93 
requestRoutesStorageCollection(App & app)94 inline void requestRoutesStorageCollection(App& app)
95 {
96     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
97         .privileges(redfish::privileges::getStorageCollection)
98         .methods(boost::beast::http::verb::get)(
99             std::bind_front(handleSystemsStorageCollectionGet, std::ref(app)));
100     BMCWEB_ROUTE(app, "/redfish/v1/Storage/")
101         .privileges(redfish::privileges::getStorageCollection)
102         .methods(boost::beast::http::verb::get)(
103             std::bind_front(handleStorageCollectionGet, std::ref(app)));
104 }
105 
afterChassisDriveCollectionSubtree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & driveList)106 inline void afterChassisDriveCollectionSubtree(
107     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
108     const boost::system::error_code& ec,
109     const dbus::utility::MapperGetSubTreePathsResponse& driveList)
110 {
111     if (ec)
112     {
113         BMCWEB_LOG_ERROR("Drive mapper call error");
114         messages::internalError(asyncResp->res);
115         return;
116     }
117 
118     nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
119     driveArray = nlohmann::json::array();
120     auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
121     count = 0;
122 
123     for (const std::string& drive : driveList)
124     {
125         sdbusplus::message::object_path object(drive);
126         if (object.filename().empty())
127         {
128             BMCWEB_LOG_ERROR("Failed to find filename in {}", drive);
129             return;
130         }
131 
132         nlohmann::json::object_t driveJson;
133         driveJson["@odata.id"] = boost::urls::format(
134             "/redfish/v1/Systems/{}/Storage/1/Drives/{}",
135             BMCWEB_REDFISH_SYSTEM_URI_NAME, object.filename());
136         driveArray.emplace_back(std::move(driveJson));
137     }
138 
139     count = driveArray.size();
140 }
getDrives(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)141 inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
142 {
143     const std::array<std::string_view, 1> interfaces = {
144         "xyz.openbmc_project.Inventory.Item.Drive"};
145     dbus::utility::getSubTreePaths(
146         "/xyz/openbmc_project/inventory", 0, interfaces,
147         std::bind_front(afterChassisDriveCollectionSubtree, asyncResp));
148 }
149 
afterSystemsStorageGetSubtree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & storageId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)150 inline void afterSystemsStorageGetSubtree(
151     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
152     const std::string& storageId, const boost::system::error_code& ec,
153     const dbus::utility::MapperGetSubTreeResponse& subtree)
154 {
155     if (ec)
156     {
157         BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
158         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
159                                    storageId);
160         return;
161     }
162     auto storage = std::ranges::find_if(
163         subtree,
164         [&storageId](const std::pair<std::string,
165                                      dbus::utility::MapperServiceMap>& object) {
166             return sdbusplus::message::object_path(object.first).filename() ==
167                    storageId;
168         });
169     if (storage == subtree.end())
170     {
171         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
172                                    storageId);
173         return;
174     }
175 
176     asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
177     asyncResp->res.jsonValue["@odata.id"] =
178         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
179                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
180     asyncResp->res.jsonValue["Name"] = "Storage";
181     asyncResp->res.jsonValue["Id"] = storageId;
182     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
183 
184     getDrives(asyncResp);
185     asyncResp->res.jsonValue["Controllers"]["@odata.id"] =
186         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}/Controllers",
187                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
188 }
189 
handleSystemsStorageGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & storageId)190 inline void handleSystemsStorageGet(
191     App& app, const crow::Request& req,
192     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
193     const std::string& systemName, const std::string& storageId)
194 {
195     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
196     {
197         return;
198     }
199     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
200     {
201         // Option currently returns no systems.  TBD
202         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
203                                    systemName);
204         return;
205     }
206 
207     constexpr std::array<std::string_view, 1> interfaces = {
208         "xyz.openbmc_project.Inventory.Item.Storage"};
209     dbus::utility::getSubTree(
210         "/xyz/openbmc_project/inventory", 0, interfaces,
211         std::bind_front(afterSystemsStorageGetSubtree, asyncResp, storageId));
212 }
213 
afterSubtree(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & storageId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)214 inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
215                          const std::string& storageId,
216                          const boost::system::error_code& ec,
217                          const dbus::utility::MapperGetSubTreeResponse& subtree)
218 {
219     if (ec)
220     {
221         BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
222         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
223                                    storageId);
224         return;
225     }
226     auto storage = std::ranges::find_if(
227         subtree,
228         [&storageId](const std::pair<std::string,
229                                      dbus::utility::MapperServiceMap>& object) {
230             return sdbusplus::message::object_path(object.first).filename() ==
231                    storageId;
232         });
233     if (storage == subtree.end())
234     {
235         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
236                                    storageId);
237         return;
238     }
239 
240     asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
241     asyncResp->res.jsonValue["@odata.id"] =
242         boost::urls::format("/redfish/v1/Storage/{}", storageId);
243     asyncResp->res.jsonValue["Name"] = "Storage";
244     asyncResp->res.jsonValue["Id"] = storageId;
245     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
246 
247     // Storage subsystem to Storage link.
248     nlohmann::json::array_t storageServices;
249     nlohmann::json::object_t storageService;
250     storageService["@odata.id"] =
251         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
252                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
253     storageServices.emplace_back(storageService);
254     asyncResp->res.jsonValue["Links"]["StorageServices"] =
255         std::move(storageServices);
256     asyncResp->res.jsonValue["Links"]["StorageServices@odata.count"] = 1;
257 }
258 
259 inline void
handleStorageGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & storageId)260     handleStorageGet(App& app, const crow::Request& req,
261                      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262                      const std::string& storageId)
263 {
264     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
265     {
266         BMCWEB_LOG_DEBUG("requestRoutesStorage setUpRedfishRoute failed");
267         return;
268     }
269 
270     constexpr std::array<std::string_view, 1> interfaces = {
271         "xyz.openbmc_project.Inventory.Item.Storage"};
272     dbus::utility::getSubTree(
273         "/xyz/openbmc_project/inventory", 0, interfaces,
274         std::bind_front(afterSubtree, asyncResp, storageId));
275 }
276 
requestRoutesStorage(App & app)277 inline void requestRoutesStorage(App& app)
278 {
279     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/<str>/")
280         .privileges(redfish::privileges::getStorage)
281         .methods(boost::beast::http::verb::get)(
282             std::bind_front(handleSystemsStorageGet, std::ref(app)));
283 
284     BMCWEB_ROUTE(app, "/redfish/v1/Storage/<str>/")
285         .privileges(redfish::privileges::getStorage)
286         .methods(boost::beast::http::verb::get)(
287             std::bind_front(handleStorageGet, std::ref(app)));
288 }
289 
getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)290 inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
291                           const std::string& connectionName,
292                           const std::string& path)
293 {
294     sdbusplus::asio::getAllProperties(
295         *crow::connections::systemBus, connectionName, path,
296         "xyz.openbmc_project.Inventory.Decorator.Asset",
297         [asyncResp](const boost::system::error_code& ec,
298                     const std::vector<
299                         std::pair<std::string, dbus::utility::DbusVariantType>>&
300                         propertiesList) {
301             if (ec)
302             {
303                 // this interface isn't necessary
304                 return;
305             }
306 
307             const std::string* partNumber = nullptr;
308             const std::string* serialNumber = nullptr;
309             const std::string* manufacturer = nullptr;
310             const std::string* model = nullptr;
311 
312             const bool success = sdbusplus::unpackPropertiesNoThrow(
313                 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
314                 partNumber, "SerialNumber", serialNumber, "Manufacturer",
315                 manufacturer, "Model", model);
316 
317             if (!success)
318             {
319                 messages::internalError(asyncResp->res);
320                 return;
321             }
322 
323             if (partNumber != nullptr)
324             {
325                 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
326             }
327 
328             if (serialNumber != nullptr)
329             {
330                 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
331             }
332 
333             if (manufacturer != nullptr)
334             {
335                 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
336             }
337 
338             if (model != nullptr)
339             {
340                 asyncResp->res.jsonValue["Model"] = *model;
341             }
342         });
343 }
344 
getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)345 inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
346                             const std::string& connectionName,
347                             const std::string& path)
348 {
349     sdbusplus::asio::getProperty<bool>(
350         *crow::connections::systemBus, connectionName, path,
351         "xyz.openbmc_project.Inventory.Item", "Present",
352         [asyncResp,
353          path](const boost::system::error_code& ec, const bool isPresent) {
354             // this interface isn't necessary, only check it if
355             // we get a good return
356             if (ec)
357             {
358                 return;
359             }
360 
361             if (!isPresent)
362             {
363                 asyncResp->res.jsonValue["Status"]["State"] =
364                     resource::State::Absent;
365             }
366         });
367 }
368 
getDriveState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)369 inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
370                           const std::string& connectionName,
371                           const std::string& path)
372 {
373     sdbusplus::asio::getProperty<bool>(
374         *crow::connections::systemBus, connectionName, path,
375         "xyz.openbmc_project.State.Drive", "Rebuilding",
376         [asyncResp](const boost::system::error_code& ec, const bool updating) {
377             // this interface isn't necessary, only check it
378             // if we get a good return
379             if (ec)
380             {
381                 return;
382             }
383 
384             // updating and disabled in the backend shouldn't be
385             // able to be set at the same time, so we don't need
386             // to check for the race condition of these two
387             // calls
388             if (updating)
389             {
390                 asyncResp->res.jsonValue["Status"]["State"] =
391                     resource::State::Updating;
392             }
393         });
394 }
395 
convertDriveType(std::string_view type)396 inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
397 {
398     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
399     {
400         return drive::MediaType::HDD;
401     }
402     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
403     {
404         return drive::MediaType::SSD;
405     }
406     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
407     {
408         return std::nullopt;
409     }
410 
411     return drive::MediaType::Invalid;
412 }
413 
414 inline std::optional<protocol::Protocol>
convertDriveProtocol(std::string_view proto)415     convertDriveProtocol(std::string_view proto)
416 {
417     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
418     {
419         return protocol::Protocol::SAS;
420     }
421     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
422     {
423         return protocol::Protocol::SATA;
424     }
425     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
426     {
427         return protocol::Protocol::NVMe;
428     }
429     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
430     {
431         return protocol::Protocol::FC;
432     }
433     if (proto ==
434         "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
435     {
436         return std::nullopt;
437     }
438 
439     return protocol::Protocol::Invalid;
440 }
441 
getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)442 inline void getDriveItemProperties(
443     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
444     const std::string& connectionName, const std::string& path)
445 {
446     sdbusplus::asio::getAllProperties(
447         *crow::connections::systemBus, connectionName, path,
448         "xyz.openbmc_project.Inventory.Item.Drive",
449         [asyncResp](const boost::system::error_code& ec,
450                     const std::vector<
451                         std::pair<std::string, dbus::utility::DbusVariantType>>&
452                         propertiesList) {
453             if (ec)
454             {
455                 // this interface isn't required
456                 return;
457             }
458             const std::string* encryptionStatus = nullptr;
459             const bool* isLocked = nullptr;
460             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
461                      property : propertiesList)
462             {
463                 const std::string& propertyName = property.first;
464                 if (propertyName == "Type")
465                 {
466                     const std::string* value =
467                         std::get_if<std::string>(&property.second);
468                     if (value == nullptr)
469                     {
470                         // illegal property
471                         BMCWEB_LOG_ERROR("Illegal property: Type");
472                         messages::internalError(asyncResp->res);
473                         return;
474                     }
475 
476                     std::optional<drive::MediaType> mediaType =
477                         convertDriveType(*value);
478                     if (!mediaType)
479                     {
480                         BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}",
481                                            *value);
482                         continue;
483                     }
484                     if (*mediaType == drive::MediaType::Invalid)
485                     {
486                         messages::internalError(asyncResp->res);
487                         return;
488                     }
489 
490                     asyncResp->res.jsonValue["MediaType"] = *mediaType;
491                 }
492                 else if (propertyName == "Capacity")
493                 {
494                     const uint64_t* capacity =
495                         std::get_if<uint64_t>(&property.second);
496                     if (capacity == nullptr)
497                     {
498                         BMCWEB_LOG_ERROR("Illegal property: Capacity");
499                         messages::internalError(asyncResp->res);
500                         return;
501                     }
502                     if (*capacity == 0)
503                     {
504                         // drive capacity not known
505                         continue;
506                     }
507 
508                     asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
509                 }
510                 else if (propertyName == "Protocol")
511                 {
512                     const std::string* value =
513                         std::get_if<std::string>(&property.second);
514                     if (value == nullptr)
515                     {
516                         BMCWEB_LOG_ERROR("Illegal property: Protocol");
517                         messages::internalError(asyncResp->res);
518                         return;
519                     }
520 
521                     std::optional<protocol::Protocol> proto =
522                         convertDriveProtocol(*value);
523                     if (!proto)
524                     {
525                         BMCWEB_LOG_WARNING(
526                             "Unknown DrivePrototype Interface: {}", *value);
527                         continue;
528                     }
529                     if (*proto == protocol::Protocol::Invalid)
530                     {
531                         messages::internalError(asyncResp->res);
532                         return;
533                     }
534                     asyncResp->res.jsonValue["Protocol"] = *proto;
535                 }
536                 else if (propertyName == "PredictedMediaLifeLeftPercent")
537                 {
538                     const uint8_t* lifeLeft =
539                         std::get_if<uint8_t>(&property.second);
540                     if (lifeLeft == nullptr)
541                     {
542                         BMCWEB_LOG_ERROR(
543                             "Illegal property: PredictedMediaLifeLeftPercent");
544                         messages::internalError(asyncResp->res);
545                         return;
546                     }
547                     // 255 means reading the value is not supported
548                     if (*lifeLeft != 255)
549                     {
550                         asyncResp->res
551                             .jsonValue["PredictedMediaLifeLeftPercent"] =
552                             *lifeLeft;
553                     }
554                 }
555                 else if (propertyName == "EncryptionStatus")
556                 {
557                     encryptionStatus =
558                         std::get_if<std::string>(&property.second);
559                     if (encryptionStatus == nullptr)
560                     {
561                         BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus");
562                         messages::internalError(asyncResp->res);
563                         return;
564                     }
565                 }
566                 else if (propertyName == "Locked")
567                 {
568                     isLocked = std::get_if<bool>(&property.second);
569                     if (isLocked == nullptr)
570                     {
571                         BMCWEB_LOG_ERROR("Illegal property: Locked");
572                         messages::internalError(asyncResp->res);
573                         return;
574                     }
575                 }
576             }
577 
578             if (encryptionStatus == nullptr || isLocked == nullptr ||
579                 *encryptionStatus ==
580                     "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown")
581             {
582                 return;
583             }
584             if (*encryptionStatus !=
585                 "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted")
586             {
587                 //"The drive is not currently encrypted."
588                 asyncResp->res.jsonValue["EncryptionStatus"] =
589                     drive::EncryptionStatus::Unencrypted;
590                 return;
591             }
592             if (*isLocked)
593             {
594                 //"The drive is currently encrypted and the data is not
595                 // accessible to the user."
596                 asyncResp->res.jsonValue["EncryptionStatus"] =
597                     drive::EncryptionStatus::Locked;
598                 return;
599             }
600             // if not locked
601             // "The drive is currently encrypted but the data is accessible
602             // to the user in unencrypted form."
603             asyncResp->res.jsonValue["EncryptionStatus"] =
604                 drive::EncryptionStatus::Unlocked;
605         });
606 }
607 
addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path,const std::vector<std::string> & interfaces)608 inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
609                             const std::string& connectionName,
610                             const std::string& path,
611                             const std::vector<std::string>& interfaces)
612 {
613     for (const std::string& interface : interfaces)
614     {
615         if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
616         {
617             getDriveAsset(asyncResp, connectionName, path);
618         }
619         else if (interface == "xyz.openbmc_project.Inventory.Item")
620         {
621             getDrivePresent(asyncResp, connectionName, path);
622         }
623         else if (interface == "xyz.openbmc_project.State.Drive")
624         {
625             getDriveState(asyncResp, connectionName, path);
626         }
627         else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
628         {
629             getDriveItemProperties(asyncResp, connectionName, path);
630         }
631     }
632 }
633 
afterGetSubtreeSystemsStorageDrive(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & driveId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)634 inline void afterGetSubtreeSystemsStorageDrive(
635     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
636     const std::string& driveId, const boost::system::error_code& ec,
637     const dbus::utility::MapperGetSubTreeResponse& subtree)
638 {
639     if (ec)
640     {
641         BMCWEB_LOG_ERROR("Drive mapper call error");
642         messages::internalError(asyncResp->res);
643         return;
644     }
645 
646     auto drive = std::ranges::find_if(
647         subtree,
648         [&driveId](const std::pair<std::string,
649                                    dbus::utility::MapperServiceMap>& object) {
650             return sdbusplus::message::object_path(object.first).filename() ==
651                    driveId;
652         });
653 
654     if (drive == subtree.end())
655     {
656         messages::resourceNotFound(asyncResp->res, "Drive", driveId);
657         return;
658     }
659 
660     const std::string& path = drive->first;
661     const dbus::utility::MapperServiceMap& connectionNames = drive->second;
662 
663     asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
664     asyncResp->res.jsonValue["@odata.id"] =
665         boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Drives/{}",
666                             BMCWEB_REDFISH_SYSTEM_URI_NAME, driveId);
667     asyncResp->res.jsonValue["Name"] = driveId;
668     asyncResp->res.jsonValue["Id"] = driveId;
669 
670     if (connectionNames.size() != 1)
671     {
672         BMCWEB_LOG_ERROR("Connection size {}, not equal to 1",
673                          connectionNames.size());
674         messages::internalError(asyncResp->res);
675         return;
676     }
677 
678     getMainChassisId(
679         asyncResp, [](const std::string& chassisId,
680                       const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
681             aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
682                 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
683         });
684 
685     // default it to Enabled
686     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
687 
688     addAllDriveInfo(asyncResp, connectionNames[0].first, path,
689                     connectionNames[0].second);
690 }
691 
handleSystemsStorageDriveGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & driveId)692 inline void handleSystemsStorageDriveGet(
693     App& app, const crow::Request& req,
694     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
695     const std::string& systemName, const std::string& driveId)
696 {
697     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
698     {
699         return;
700     }
701     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
702     {
703         // Option currently returns no systems.  TBD
704         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
705                                    systemName);
706         return;
707     }
708 
709     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
710     {
711         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
712                                    systemName);
713         return;
714     }
715 
716     constexpr std::array<std::string_view, 1> interfaces = {
717         "xyz.openbmc_project.Inventory.Item.Drive"};
718     dbus::utility::getSubTree(
719         "/xyz/openbmc_project/inventory", 0, interfaces,
720         std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
721                         driveId));
722 }
723 
requestRoutesDrive(App & app)724 inline void requestRoutesDrive(App& app)
725 {
726     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
727         .privileges(redfish::privileges::getDrive)
728         .methods(boost::beast::http::verb::get)(
729             std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
730 }
731 
afterChassisDriveCollectionSubtreeGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)732 inline void afterChassisDriveCollectionSubtreeGet(
733     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
734     const std::string& chassisId, const boost::system::error_code& ec,
735     const dbus::utility::MapperGetSubTreeResponse& subtree)
736 {
737     if (ec)
738     {
739         if (ec == boost::system::errc::host_unreachable)
740         {
741             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
742             return;
743         }
744         messages::internalError(asyncResp->res);
745         return;
746     }
747 
748     // Iterate over all retrieved ObjectPaths.
749     for (const auto& [path, connectionNames] : subtree)
750     {
751         sdbusplus::message::object_path objPath(path);
752         if (objPath.filename() != chassisId)
753         {
754             continue;
755         }
756 
757         if (connectionNames.empty())
758         {
759             BMCWEB_LOG_ERROR("Got 0 Connection names");
760             continue;
761         }
762 
763         asyncResp->res.jsonValue["@odata.type"] =
764             "#DriveCollection.DriveCollection";
765         asyncResp->res.jsonValue["@odata.id"] =
766             boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
767         asyncResp->res.jsonValue["Name"] = "Drive Collection";
768 
769         // Association lambda
770         dbus::utility::getAssociationEndPoints(
771             path + "/drive",
772             [asyncResp, chassisId](const boost::system::error_code& ec3,
773                                    const dbus::utility::MapperEndPoints& resp) {
774                 if (ec3)
775                 {
776                     BMCWEB_LOG_ERROR("Error in chassis Drive association ");
777                 }
778                 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
779                 // important if array is empty
780                 members = nlohmann::json::array();
781 
782                 std::vector<std::string> leafNames;
783                 for (const auto& drive : resp)
784                 {
785                     sdbusplus::message::object_path drivePath(drive);
786                     leafNames.push_back(drivePath.filename());
787                 }
788 
789                 std::ranges::sort(leafNames, AlphanumLess<std::string>());
790 
791                 for (const auto& leafName : leafNames)
792                 {
793                     nlohmann::json::object_t member;
794                     member["@odata.id"] =
795                         boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}",
796                                             chassisId, leafName);
797                     members.emplace_back(std::move(member));
798                     // navigation links will be registered in next patch set
799                 }
800                 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
801             }); // end association lambda
802 
803     } // end Iterate over all retrieved ObjectPaths
804 }
805 /**
806  * Chassis drives, this URL will show all the DriveCollection
807  * information
808  */
chassisDriveCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)809 inline void chassisDriveCollectionGet(
810     crow::App& app, const crow::Request& req,
811     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
812     const std::string& chassisId)
813 {
814     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
815     {
816         return;
817     }
818 
819     // mapper call lambda
820     constexpr std::array<std::string_view, 2> interfaces = {
821         "xyz.openbmc_project.Inventory.Item.Board",
822         "xyz.openbmc_project.Inventory.Item.Chassis"};
823     dbus::utility::getSubTree(
824         "/xyz/openbmc_project/inventory", 0, interfaces,
825         std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
826                         chassisId));
827 }
828 
requestRoutesChassisDrive(App & app)829 inline void requestRoutesChassisDrive(App& app)
830 {
831     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
832         .privileges(redfish::privileges::getDriveCollection)
833         .methods(boost::beast::http::verb::get)(
834             std::bind_front(chassisDriveCollectionGet, std::ref(app)));
835 }
836 
buildDrive(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & driveName,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)837 inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
838                        const std::string& chassisId,
839                        const std::string& driveName,
840                        const boost::system::error_code& ec,
841                        const dbus::utility::MapperGetSubTreeResponse& subtree)
842 {
843     if (ec)
844     {
845         BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
846         messages::internalError(asyncResp->res);
847         return;
848     }
849 
850     // Iterate over all retrieved ObjectPaths.
851     for (const auto& [path, connectionNames] : subtree)
852     {
853         sdbusplus::message::object_path objPath(path);
854         if (objPath.filename() != driveName)
855         {
856             continue;
857         }
858 
859         if (connectionNames.empty())
860         {
861             BMCWEB_LOG_ERROR("Got 0 Connection names");
862             continue;
863         }
864 
865         asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
866             "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
867 
868         asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
869         asyncResp->res.jsonValue["Name"] = driveName;
870         asyncResp->res.jsonValue["Id"] = driveName;
871         // default it to Enabled
872         asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
873 
874         nlohmann::json::object_t linkChassisNav;
875         linkChassisNav["@odata.id"] =
876             boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
877         asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
878 
879         addAllDriveInfo(asyncResp, connectionNames[0].first, path,
880                         connectionNames[0].second);
881     }
882 }
883 
matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & driveName,const std::vector<std::string> & resp)884 inline void matchAndFillDrive(
885     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
886     const std::string& chassisId, const std::string& driveName,
887     const std::vector<std::string>& resp)
888 {
889     for (const std::string& drivePath : resp)
890     {
891         sdbusplus::message::object_path path(drivePath);
892         std::string leaf = path.filename();
893         if (leaf != driveName)
894         {
895             continue;
896         }
897         //  mapper call drive
898         constexpr std::array<std::string_view, 1> driveInterface = {
899             "xyz.openbmc_project.Inventory.Item.Drive"};
900         dbus::utility::getSubTree(
901             "/xyz/openbmc_project/inventory", 0, driveInterface,
902             [asyncResp, chassisId, driveName](
903                 const boost::system::error_code& ec,
904                 const dbus::utility::MapperGetSubTreeResponse& subtree) {
905                 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
906             });
907     }
908 }
909 
handleChassisDriveGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & driveName)910 inline void handleChassisDriveGet(
911     crow::App& app, const crow::Request& req,
912     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
913     const std::string& chassisId, const std::string& driveName)
914 {
915     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
916     {
917         return;
918     }
919     constexpr std::array<std::string_view, 2> interfaces = {
920         "xyz.openbmc_project.Inventory.Item.Board",
921         "xyz.openbmc_project.Inventory.Item.Chassis"};
922 
923     // mapper call chassis
924     dbus::utility::getSubTree(
925         "/xyz/openbmc_project/inventory", 0, interfaces,
926         [asyncResp, chassisId,
927          driveName](const boost::system::error_code& ec,
928                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
929             if (ec)
930             {
931                 messages::internalError(asyncResp->res);
932                 return;
933             }
934 
935             // Iterate over all retrieved ObjectPaths.
936             for (const auto& [path, connectionNames] : subtree)
937             {
938                 sdbusplus::message::object_path objPath(path);
939                 if (objPath.filename() != chassisId)
940                 {
941                     continue;
942                 }
943 
944                 if (connectionNames.empty())
945                 {
946                     BMCWEB_LOG_ERROR("Got 0 Connection names");
947                     continue;
948                 }
949 
950                 dbus::utility::getAssociationEndPoints(
951                     path + "/drive",
952                     [asyncResp, chassisId,
953                      driveName](const boost::system::error_code& ec3,
954                                 const dbus::utility::MapperEndPoints& resp) {
955                         if (ec3)
956                         {
957                             return; // no drives = no failures
958                         }
959                         matchAndFillDrive(asyncResp, chassisId, driveName,
960                                           resp);
961                     });
962                 break;
963             }
964         });
965 }
966 
967 /**
968  * This URL will show the drive interface for the specific drive in the chassis
969  */
requestRoutesChassisDriveName(App & app)970 inline void requestRoutesChassisDriveName(App& app)
971 {
972     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
973         .privileges(redfish::privileges::getChassis)
974         .methods(boost::beast::http::verb::get)(
975             std::bind_front(handleChassisDriveGet, std::ref(app)));
976 }
977 
getStorageControllerAsset(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::vector<std::pair<std::string,dbus::utility::DbusVariantType>> & propertiesList)978 inline void getStorageControllerAsset(
979     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
980     const boost::system::error_code& ec,
981     const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
982         propertiesList)
983 {
984     if (ec)
985     {
986         // this interface isn't necessary
987         BMCWEB_LOG_DEBUG("Failed to get StorageControllerAsset");
988         return;
989     }
990 
991     const std::string* partNumber = nullptr;
992     const std::string* serialNumber = nullptr;
993     const std::string* manufacturer = nullptr;
994     const std::string* model = nullptr;
995     if (!sdbusplus::unpackPropertiesNoThrow(
996             dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
997             partNumber, "SerialNumber", serialNumber, "Manufacturer",
998             manufacturer, "Model", model))
999     {
1000         messages::internalError(asyncResp->res);
1001         return;
1002     }
1003 
1004     if (partNumber != nullptr)
1005     {
1006         asyncResp->res.jsonValue["PartNumber"] = *partNumber;
1007     }
1008 
1009     if (serialNumber != nullptr)
1010     {
1011         asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
1012     }
1013 
1014     if (manufacturer != nullptr)
1015     {
1016         asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
1017     }
1018 
1019     if (model != nullptr)
1020     {
1021         asyncResp->res.jsonValue["Model"] = *model;
1022     }
1023 }
1024 
populateStorageController(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & controllerId,const std::string & connectionName,const std::string & path)1025 inline void populateStorageController(
1026     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1027     const std::string& controllerId, const std::string& connectionName,
1028     const std::string& path)
1029 {
1030     asyncResp->res.jsonValue["@odata.type"] =
1031         "#StorageController.v1_6_0.StorageController";
1032     asyncResp->res.jsonValue["@odata.id"] =
1033         boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1034                             BMCWEB_REDFISH_SYSTEM_URI_NAME, controllerId);
1035     asyncResp->res.jsonValue["Name"] = controllerId;
1036     asyncResp->res.jsonValue["Id"] = controllerId;
1037     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
1038 
1039     sdbusplus::asio::getProperty<bool>(
1040         *crow::connections::systemBus, connectionName, path,
1041         "xyz.openbmc_project.Inventory.Item", "Present",
1042         [asyncResp](const boost::system::error_code& ec, bool isPresent) {
1043             // this interface isn't necessary, only check it
1044             // if we get a good return
1045             if (ec)
1046             {
1047                 BMCWEB_LOG_DEBUG("Failed to get Present property");
1048                 return;
1049             }
1050             if (!isPresent)
1051             {
1052                 asyncResp->res.jsonValue["Status"]["State"] =
1053                     resource::State::Absent;
1054             }
1055         });
1056 
1057     sdbusplus::asio::getAllProperties(
1058         *crow::connections::systemBus, connectionName, path,
1059         "xyz.openbmc_project.Inventory.Decorator.Asset",
1060         [asyncResp](const boost::system::error_code& ec,
1061                     const std::vector<
1062                         std::pair<std::string, dbus::utility::DbusVariantType>>&
1063                         propertiesList) {
1064             getStorageControllerAsset(asyncResp, ec, propertiesList);
1065         });
1066 }
1067 
getStorageControllerHandler(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & controllerId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)1068 inline void getStorageControllerHandler(
1069     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1070     const std::string& controllerId, const boost::system::error_code& ec,
1071     const dbus::utility::MapperGetSubTreeResponse& subtree)
1072 {
1073     if (ec || subtree.empty())
1074     {
1075         // doesn't have to be there
1076         BMCWEB_LOG_DEBUG("Failed to handle StorageController");
1077         return;
1078     }
1079 
1080     for (const auto& [path, interfaceDict] : subtree)
1081     {
1082         sdbusplus::message::object_path object(path);
1083         std::string id = object.filename();
1084         if (id.empty())
1085         {
1086             BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
1087             return;
1088         }
1089         if (id != controllerId)
1090         {
1091             continue;
1092         }
1093 
1094         if (interfaceDict.size() != 1)
1095         {
1096             BMCWEB_LOG_ERROR("Connection size {}, greater than 1",
1097                              interfaceDict.size());
1098             messages::internalError(asyncResp->res);
1099             return;
1100         }
1101 
1102         const std::string& connectionName = interfaceDict.front().first;
1103         populateStorageController(asyncResp, controllerId, connectionName,
1104                                   path);
1105     }
1106 }
1107 
populateStorageControllerCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & controllerList)1108 inline void populateStorageControllerCollection(
1109     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1110     const boost::system::error_code& ec,
1111     const dbus::utility::MapperGetSubTreePathsResponse& controllerList)
1112 {
1113     nlohmann::json::array_t members;
1114     if (ec || controllerList.empty())
1115     {
1116         asyncResp->res.jsonValue["Members"] = std::move(members);
1117         asyncResp->res.jsonValue["Members@odata.count"] = 0;
1118         BMCWEB_LOG_DEBUG("Failed to find any StorageController");
1119         return;
1120     }
1121 
1122     for (const std::string& path : controllerList)
1123     {
1124         std::string id = sdbusplus::message::object_path(path).filename();
1125         if (id.empty())
1126         {
1127             BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
1128             return;
1129         }
1130         nlohmann::json::object_t member;
1131         member["@odata.id"] = boost::urls::format(
1132             "/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1133             BMCWEB_REDFISH_SYSTEM_URI_NAME, id);
1134         members.emplace_back(member);
1135     }
1136     asyncResp->res.jsonValue["Members@odata.count"] = members.size();
1137     asyncResp->res.jsonValue["Members"] = std::move(members);
1138 }
1139 
handleSystemsStorageControllerCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1140 inline void handleSystemsStorageControllerCollectionGet(
1141     App& app, const crow::Request& req,
1142     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1143     const std::string& systemName)
1144 {
1145     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1146     {
1147         BMCWEB_LOG_DEBUG(
1148             "Failed to setup Redfish Route for StorageController Collection");
1149         return;
1150     }
1151     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1152     {
1153         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1154                                    systemName);
1155         BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
1156         return;
1157     }
1158 
1159     asyncResp->res.jsonValue["@odata.type"] =
1160         "#StorageControllerCollection.StorageControllerCollection";
1161     asyncResp->res.jsonValue["@odata.id"] =
1162         std::format("/redfish/v1/Systems/{}/Storage/1/Controllers",
1163                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
1164     asyncResp->res.jsonValue["Name"] = "Storage Controller Collection";
1165 
1166     constexpr std::array<std::string_view, 1> interfaces = {
1167         "xyz.openbmc_project.Inventory.Item.StorageController"};
1168     dbus::utility::getSubTreePaths(
1169         "/xyz/openbmc_project/inventory", 0, interfaces,
1170         [asyncResp](const boost::system::error_code& ec,
1171                     const dbus::utility::MapperGetSubTreePathsResponse&
1172                         controllerList) {
1173             populateStorageControllerCollection(asyncResp, ec, controllerList);
1174         });
1175 }
1176 
handleSystemsStorageControllerGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & controllerId)1177 inline void handleSystemsStorageControllerGet(
1178     App& app, const crow::Request& req,
1179     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1180     const std::string& systemName, const std::string& controllerId)
1181 {
1182     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1183     {
1184         BMCWEB_LOG_DEBUG("Failed to setup Redfish Route for StorageController");
1185         return;
1186     }
1187     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1188     {
1189         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1190                                    systemName);
1191         BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
1192         return;
1193     }
1194     constexpr std::array<std::string_view, 1> interfaces = {
1195         "xyz.openbmc_project.Inventory.Item.StorageController"};
1196     dbus::utility::getSubTree(
1197         "/xyz/openbmc_project/inventory", 0, interfaces,
1198         [asyncResp,
1199          controllerId](const boost::system::error_code& ec,
1200                        const dbus::utility::MapperGetSubTreeResponse& subtree) {
1201             getStorageControllerHandler(asyncResp, controllerId, ec, subtree);
1202         });
1203 }
1204 
requestRoutesStorageControllerCollection(App & app)1205 inline void requestRoutesStorageControllerCollection(App& app)
1206 {
1207     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/")
1208         .privileges(redfish::privileges::getStorageControllerCollection)
1209         .methods(boost::beast::http::verb::get)(std::bind_front(
1210             handleSystemsStorageControllerCollectionGet, std::ref(app)));
1211 }
1212 
requestRoutesStorageController(App & app)1213 inline void requestRoutesStorageController(App& app)
1214 {
1215     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>")
1216         .privileges(redfish::privileges::getStorageController)
1217         .methods(boost::beast::http::verb::get)(
1218             std::bind_front(handleSystemsStorageControllerGet, std::ref(app)));
1219 }
1220 
1221 } // namespace redfish
1222