xref: /openbmc/bmcweb/features/redfish/lib/storage.hpp (revision f7e62c142ced153e9400b519bdd66062dd6bbf0e)
140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
340e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright 2019 Intel Corporation
4a25aeccfSNikhil Potade #pragma once
5a25aeccfSNikhil Potade 
613451e39SWilly Tu #include "bmcweb_config.h"
713451e39SWilly Tu 
83ccb3adbSEd Tanous #include "app.hpp"
9d7857201SEd Tanous #include "async_resp.hpp"
107a1dbc48SGeorge Liu #include "dbus_utility.hpp"
11d7857201SEd Tanous #include "error_messages.hpp"
12e5029d88SJohn Edward Broadbent #include "generated/enums/drive.hpp"
13dde9bc12SGeorge Liu #include "generated/enums/protocol.hpp"
14539d8c6bSEd Tanous #include "generated/enums/resource.hpp"
15d7857201SEd Tanous #include "http_request.hpp"
16a8e884fcSEd Tanous #include "human_sort.hpp"
17d7857201SEd Tanous #include "logging.hpp"
183ccb3adbSEd Tanous #include "query.hpp"
19a8e884fcSEd Tanous #include "redfish_util.hpp"
203ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
213f95a277SMyung Bae #include "utils/chassis_utils.hpp"
225e577bc1SWilly Tu #include "utils/collection.hpp"
233ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
242ad9c2f6SJames Feist 
25d7857201SEd Tanous #include <boost/beast/http/verb.hpp>
26e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
27ef4c65b7SEd Tanous #include <boost/url/format.hpp>
28d7857201SEd Tanous #include <sdbusplus/message/native_types.hpp>
29d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
30a25aeccfSNikhil Potade 
31d7857201SEd Tanous #include <algorithm>
327a1dbc48SGeorge Liu #include <array>
33d7857201SEd Tanous #include <cstdint>
34d7857201SEd Tanous #include <format>
35d7857201SEd Tanous #include <functional>
36d7857201SEd Tanous #include <memory>
37d7857201SEd Tanous #include <optional>
383544d2a7SEd Tanous #include <ranges>
39d7857201SEd Tanous #include <string>
407a1dbc48SGeorge Liu #include <string_view>
41d7857201SEd Tanous #include <utility>
42d7857201SEd Tanous #include <variant>
43d7857201SEd Tanous #include <vector>
447a1dbc48SGeorge Liu 
45a25aeccfSNikhil Potade namespace redfish
46a25aeccfSNikhil Potade {
4736d52334SEd Tanous 
4836d52334SEd Tanous inline void handleSystemsStorageCollectionGet(
4936d52334SEd Tanous     App& app, const crow::Request& req,
5022d268cbSEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
5136d52334SEd Tanous     const std::string& systemName)
5236d52334SEd Tanous {
533ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
5445ca1b86SEd Tanous     {
5545ca1b86SEd Tanous         return;
5645ca1b86SEd Tanous     }
57253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
5822d268cbSEd Tanous     {
5922d268cbSEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
6022d268cbSEd Tanous                                    systemName);
6122d268cbSEd Tanous         return;
6222d268cbSEd Tanous     }
6322d268cbSEd Tanous 
648d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
658d1b46d7Szhanghch05         "#StorageCollection.StorageCollection";
66253f11b8SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = std::format(
67253f11b8SEd Tanous         "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME);
688d1b46d7Szhanghch05     asyncResp->res.jsonValue["Name"] = "Storage Collection";
695e577bc1SWilly Tu 
705e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interface{
715a39f77aSPatrick Williams         "xyz.openbmc_project.Inventory.Item.Storage"};
725e577bc1SWilly Tu     collection_util::getCollectionMembers(
73253f11b8SEd Tanous         asyncResp,
74253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage",
75253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME),
7636b5f1edSLakshmi Yadlapati         interface, "/xyz/openbmc_project/inventory");
775e577bc1SWilly Tu }
785e577bc1SWilly Tu 
795e577bc1SWilly Tu inline void handleStorageCollectionGet(
805e577bc1SWilly Tu     App& app, const crow::Request& req,
815e577bc1SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
825e577bc1SWilly Tu {
835e577bc1SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
845e577bc1SWilly Tu     {
855e577bc1SWilly Tu         return;
865e577bc1SWilly Tu     }
875e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.type"] =
885e577bc1SWilly Tu         "#StorageCollection.StorageCollection";
895e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Storage";
905e577bc1SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage Collection";
915e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interface{
925a39f77aSPatrick Williams         "xyz.openbmc_project.Inventory.Item.Storage"};
935e577bc1SWilly Tu     collection_util::getCollectionMembers(
9436b5f1edSLakshmi Yadlapati         asyncResp, boost::urls::format("/redfish/v1/Storage"), interface,
9536b5f1edSLakshmi Yadlapati         "/xyz/openbmc_project/inventory");
96a25aeccfSNikhil Potade }
97a25aeccfSNikhil Potade 
9836d52334SEd Tanous inline void requestRoutesStorageCollection(App& app)
99a25aeccfSNikhil Potade {
10036d52334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
10136d52334SEd Tanous         .privileges(redfish::privileges::getStorageCollection)
10236d52334SEd Tanous         .methods(boost::beast::http::verb::get)(
10336d52334SEd Tanous             std::bind_front(handleSystemsStorageCollectionGet, std::ref(app)));
1045e577bc1SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Storage/")
1055e577bc1SWilly Tu         .privileges(redfish::privileges::getStorageCollection)
1065e577bc1SWilly Tu         .methods(boost::beast::http::verb::get)(
1075e577bc1SWilly Tu             std::bind_front(handleStorageCollectionGet, std::ref(app)));
10836d52334SEd Tanous }
10936d52334SEd Tanous 
11036d52334SEd Tanous inline void afterChassisDriveCollectionSubtree(
11136d52334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1127a1dbc48SGeorge Liu     const boost::system::error_code& ec,
11336d52334SEd Tanous     const dbus::utility::MapperGetSubTreePathsResponse& driveList)
11436d52334SEd Tanous {
115a25aeccfSNikhil Potade     if (ec)
116a25aeccfSNikhil Potade     {
11762598e31SEd Tanous         BMCWEB_LOG_ERROR("Drive mapper call error");
118a25aeccfSNikhil Potade         messages::internalError(asyncResp->res);
119a25aeccfSNikhil Potade         return;
120a25aeccfSNikhil Potade     }
1212ad9c2f6SJames Feist 
122a85afbe1SWilly Tu     nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
123a85afbe1SWilly Tu     driveArray = nlohmann::json::array();
124a85afbe1SWilly Tu     auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
125a85afbe1SWilly Tu     count = 0;
1262ad9c2f6SJames Feist 
127a85afbe1SWilly Tu     for (const std::string& drive : driveList)
128a25aeccfSNikhil Potade     {
129a85afbe1SWilly Tu         sdbusplus::message::object_path object(drive);
130a85afbe1SWilly Tu         if (object.filename().empty())
131a25aeccfSNikhil Potade         {
13262598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to find filename in {}", drive);
133a85afbe1SWilly Tu             return;
134a25aeccfSNikhil Potade         }
135a85afbe1SWilly Tu 
136a85afbe1SWilly Tu         nlohmann::json::object_t driveJson;
137ef4c65b7SEd Tanous         driveJson["@odata.id"] = boost::urls::format(
138253f11b8SEd Tanous             "/redfish/v1/Systems/{}/Storage/1/Drives/{}",
139253f11b8SEd Tanous             BMCWEB_REDFISH_SYSTEM_URI_NAME, object.filename());
140b2ba3072SPatrick Williams         driveArray.emplace_back(std::move(driveJson));
141a25aeccfSNikhil Potade     }
142a25aeccfSNikhil Potade 
143a85afbe1SWilly Tu     count = driveArray.size();
14436d52334SEd Tanous }
1457ac13cc9SGunnar Mills inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
14636d52334SEd Tanous {
14736d52334SEd Tanous     const std::array<std::string_view, 1> interfaces = {
14836d52334SEd Tanous         "xyz.openbmc_project.Inventory.Item.Drive"};
14936d52334SEd Tanous     dbus::utility::getSubTreePaths(
15036d52334SEd Tanous         "/xyz/openbmc_project/inventory", 0, interfaces,
1517ac13cc9SGunnar Mills         std::bind_front(afterChassisDriveCollectionSubtree, asyncResp));
152a85afbe1SWilly Tu }
153e284a7c1SJames Feist 
1545e577bc1SWilly Tu inline void afterSystemsStorageGetSubtree(
1555e577bc1SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1565e577bc1SWilly Tu     const std::string& storageId, const boost::system::error_code& ec,
1575e577bc1SWilly Tu     const dbus::utility::MapperGetSubTreeResponse& subtree)
158a85afbe1SWilly Tu {
1595e577bc1SWilly Tu     if (ec)
160a85afbe1SWilly Tu     {
16162598e31SEd Tanous         BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
1625e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
1635e577bc1SWilly Tu                                    storageId);
164a85afbe1SWilly Tu         return;
165a85afbe1SWilly Tu     }
1663544d2a7SEd Tanous     auto storage = std::ranges::find_if(
1673544d2a7SEd Tanous         subtree,
1685e577bc1SWilly Tu         [&storageId](const std::pair<std::string,
1695e577bc1SWilly Tu                                      dbus::utility::MapperServiceMap>& object) {
1705e577bc1SWilly Tu             return sdbusplus::message::object_path(object.first).filename() ==
1715e577bc1SWilly Tu                    storageId;
1725e577bc1SWilly Tu         });
1735e577bc1SWilly Tu     if (storage == subtree.end())
1745e577bc1SWilly Tu     {
1755e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
1765e577bc1SWilly Tu                                    storageId);
1775e577bc1SWilly Tu         return;
1785e577bc1SWilly Tu     }
1795e577bc1SWilly Tu 
18061b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
181a85afbe1SWilly Tu     asyncResp->res.jsonValue["@odata.id"] =
182253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
183253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
184a85afbe1SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage";
1855e577bc1SWilly Tu     asyncResp->res.jsonValue["Id"] = storageId;
186539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
187a85afbe1SWilly Tu 
1887ac13cc9SGunnar Mills     getDrives(asyncResp);
189253f11b8SEd Tanous     asyncResp->res.jsonValue["Controllers"]["@odata.id"] =
190253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}/Controllers",
191253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
1925e577bc1SWilly Tu }
1935e577bc1SWilly Tu 
194bd79bce8SPatrick Williams inline void handleSystemsStorageGet(
195bd79bce8SPatrick Williams     App& app, const crow::Request& req,
1965e577bc1SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
197bd79bce8SPatrick Williams     const std::string& systemName, const std::string& storageId)
1985e577bc1SWilly Tu {
1995e577bc1SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2005e577bc1SWilly Tu     {
2015e577bc1SWilly Tu         return;
2025e577bc1SWilly Tu     }
20325b54dbaSEd Tanous     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2047f3e84a1SEd Tanous     {
2057f3e84a1SEd Tanous         // Option currently returns no systems.  TBD
2067f3e84a1SEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2077f3e84a1SEd Tanous                                    systemName);
2087f3e84a1SEd Tanous         return;
2097f3e84a1SEd Tanous     }
2105e577bc1SWilly Tu 
2115e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
2125e577bc1SWilly Tu         "xyz.openbmc_project.Inventory.Item.Storage"};
2135e577bc1SWilly Tu     dbus::utility::getSubTree(
2145e577bc1SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
2155e577bc1SWilly Tu         std::bind_front(afterSystemsStorageGetSubtree, asyncResp, storageId));
2165e577bc1SWilly Tu }
2175e577bc1SWilly Tu 
2185e577bc1SWilly Tu inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2195e577bc1SWilly Tu                          const std::string& storageId,
2205e577bc1SWilly Tu                          const boost::system::error_code& ec,
2215e577bc1SWilly Tu                          const dbus::utility::MapperGetSubTreeResponse& subtree)
2225e577bc1SWilly Tu {
2235e577bc1SWilly Tu     if (ec)
2245e577bc1SWilly Tu     {
22562598e31SEd Tanous         BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
2265e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
2275e577bc1SWilly Tu                                    storageId);
2285e577bc1SWilly Tu         return;
2295e577bc1SWilly Tu     }
2303544d2a7SEd Tanous     auto storage = std::ranges::find_if(
2313544d2a7SEd Tanous         subtree,
2325e577bc1SWilly Tu         [&storageId](const std::pair<std::string,
2335e577bc1SWilly Tu                                      dbus::utility::MapperServiceMap>& object) {
2345e577bc1SWilly Tu             return sdbusplus::message::object_path(object.first).filename() ==
2355e577bc1SWilly Tu                    storageId;
2365e577bc1SWilly Tu         });
2375e577bc1SWilly Tu     if (storage == subtree.end())
2385e577bc1SWilly Tu     {
2395e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
2405e577bc1SWilly Tu                                    storageId);
2415e577bc1SWilly Tu         return;
2425e577bc1SWilly Tu     }
2435e577bc1SWilly Tu 
2445e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
2455e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.id"] =
2465e577bc1SWilly Tu         boost::urls::format("/redfish/v1/Storage/{}", storageId);
2475e577bc1SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage";
2485e577bc1SWilly Tu     asyncResp->res.jsonValue["Id"] = storageId;
249539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
2505e577bc1SWilly Tu 
2515e577bc1SWilly Tu     // Storage subsystem to Storage link.
2525e577bc1SWilly Tu     nlohmann::json::array_t storageServices;
2535e577bc1SWilly Tu     nlohmann::json::object_t storageService;
2545e577bc1SWilly Tu     storageService["@odata.id"] =
255253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
256253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
2575e577bc1SWilly Tu     storageServices.emplace_back(storageService);
2585e577bc1SWilly Tu     asyncResp->res.jsonValue["Links"]["StorageServices"] =
2595e577bc1SWilly Tu         std::move(storageServices);
2605e577bc1SWilly Tu     asyncResp->res.jsonValue["Links"]["StorageServices@odata.count"] = 1;
2615e577bc1SWilly Tu }
2625e577bc1SWilly Tu 
263504af5a0SPatrick Williams inline void handleStorageGet(
264504af5a0SPatrick Williams     App& app, const crow::Request& req,
2655e577bc1SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2665e577bc1SWilly Tu     const std::string& storageId)
2675e577bc1SWilly Tu {
2685e577bc1SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2695e577bc1SWilly Tu     {
27062598e31SEd Tanous         BMCWEB_LOG_DEBUG("requestRoutesStorage setUpRedfishRoute failed");
2715e577bc1SWilly Tu         return;
2725e577bc1SWilly Tu     }
2735e577bc1SWilly Tu 
2745e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
2755e577bc1SWilly Tu         "xyz.openbmc_project.Inventory.Item.Storage"};
2765e577bc1SWilly Tu     dbus::utility::getSubTree(
2775e577bc1SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
2785e577bc1SWilly Tu         std::bind_front(afterSubtree, asyncResp, storageId));
27936d52334SEd Tanous }
28036d52334SEd Tanous 
28136d52334SEd Tanous inline void requestRoutesStorage(App& app)
28236d52334SEd Tanous {
2837f3e84a1SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/<str>/")
28436d52334SEd Tanous         .privileges(redfish::privileges::getStorage)
28536d52334SEd Tanous         .methods(boost::beast::http::verb::get)(
28636d52334SEd Tanous             std::bind_front(handleSystemsStorageGet, std::ref(app)));
2875e577bc1SWilly Tu 
2885e577bc1SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Storage/<str>/")
2895e577bc1SWilly Tu         .privileges(redfish::privileges::getStorage)
2905e577bc1SWilly Tu         .methods(boost::beast::http::verb::get)(
2915e577bc1SWilly Tu             std::bind_front(handleStorageGet, std::ref(app)));
2927e860f15SJohn Edward Broadbent }
2937e860f15SJohn Edward Broadbent 
29403913171SWilly Tu inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
29503913171SWilly Tu                             const std::string& connectionName,
29603913171SWilly Tu                             const std::string& path)
29703913171SWilly Tu {
298deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
299deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present",
300bd79bce8SPatrick Williams         [asyncResp,
301bd79bce8SPatrick Williams          path](const boost::system::error_code& ec, const bool isPresent) {
30203913171SWilly Tu             // this interface isn't necessary, only check it if
30303913171SWilly Tu             // we get a good return
30403913171SWilly Tu             if (ec)
30503913171SWilly Tu             {
30603913171SWilly Tu                 return;
30703913171SWilly Tu             }
30803913171SWilly Tu 
309cef57e85SWilly Tu             if (!isPresent)
31003913171SWilly Tu             {
311539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
312539d8c6bSEd Tanous                     resource::State::Absent;
31303913171SWilly Tu             }
3141e1e598dSJonathan Doman         });
31503913171SWilly Tu }
31603913171SWilly Tu 
31703913171SWilly Tu inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
31803913171SWilly Tu                           const std::string& connectionName,
31903913171SWilly Tu                           const std::string& path)
32003913171SWilly Tu {
321deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
322deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.State.Drive", "Rebuilding",
3235e7e2dc5SEd Tanous         [asyncResp](const boost::system::error_code& ec, const bool updating) {
32403913171SWilly Tu             // this interface isn't necessary, only check it
32503913171SWilly Tu             // if we get a good return
32603913171SWilly Tu             if (ec)
32703913171SWilly Tu             {
32803913171SWilly Tu                 return;
32903913171SWilly Tu             }
33003913171SWilly Tu 
33103913171SWilly Tu             // updating and disabled in the backend shouldn't be
33203913171SWilly Tu             // able to be set at the same time, so we don't need
33303913171SWilly Tu             // to check for the race condition of these two
33403913171SWilly Tu             // calls
3351e1e598dSJonathan Doman             if (updating)
33603913171SWilly Tu             {
337539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
338539d8c6bSEd Tanous                     resource::State::Updating;
33903913171SWilly Tu             }
3401e1e598dSJonathan Doman         });
34103913171SWilly Tu }
34203913171SWilly Tu 
343dde9bc12SGeorge Liu inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
34419b8e9a0SWilly Tu {
34519b8e9a0SWilly Tu     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
34619b8e9a0SWilly Tu     {
347dde9bc12SGeorge Liu         return drive::MediaType::HDD;
34819b8e9a0SWilly Tu     }
34919b8e9a0SWilly Tu     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
35019b8e9a0SWilly Tu     {
351dde9bc12SGeorge Liu         return drive::MediaType::SSD;
35219b8e9a0SWilly Tu     }
353dde9bc12SGeorge Liu     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
354dde9bc12SGeorge Liu     {
35519b8e9a0SWilly Tu         return std::nullopt;
35619b8e9a0SWilly Tu     }
35719b8e9a0SWilly Tu 
358dde9bc12SGeorge Liu     return drive::MediaType::Invalid;
359dde9bc12SGeorge Liu }
360dde9bc12SGeorge Liu 
361504af5a0SPatrick Williams inline std::optional<protocol::Protocol> convertDriveProtocol(
362504af5a0SPatrick Williams     std::string_view proto)
36319b8e9a0SWilly Tu {
36419b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
36519b8e9a0SWilly Tu     {
366dde9bc12SGeorge Liu         return protocol::Protocol::SAS;
36719b8e9a0SWilly Tu     }
36819b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
36919b8e9a0SWilly Tu     {
370dde9bc12SGeorge Liu         return protocol::Protocol::SATA;
37119b8e9a0SWilly Tu     }
37219b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
37319b8e9a0SWilly Tu     {
374dde9bc12SGeorge Liu         return protocol::Protocol::NVMe;
37519b8e9a0SWilly Tu     }
37619b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
37719b8e9a0SWilly Tu     {
378dde9bc12SGeorge Liu         return protocol::Protocol::FC;
379dde9bc12SGeorge Liu     }
380dde9bc12SGeorge Liu     if (proto ==
381dde9bc12SGeorge Liu         "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
382dde9bc12SGeorge Liu     {
383dde9bc12SGeorge Liu         return std::nullopt;
38419b8e9a0SWilly Tu     }
38519b8e9a0SWilly Tu 
386dde9bc12SGeorge Liu     return protocol::Protocol::Invalid;
38719b8e9a0SWilly Tu }
38819b8e9a0SWilly Tu 
389bd79bce8SPatrick Williams inline void getDriveItemProperties(
390bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
391bd79bce8SPatrick Williams     const std::string& connectionName, const std::string& path)
39219b8e9a0SWilly Tu {
393deae6a78SEd Tanous     dbus::utility::getAllProperties(
394deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Item.Drive",
3955e7e2dc5SEd Tanous         [asyncResp](const boost::system::error_code& ec,
39619b8e9a0SWilly Tu                     const std::vector<
39719b8e9a0SWilly Tu                         std::pair<std::string, dbus::utility::DbusVariantType>>&
39819b8e9a0SWilly Tu                         propertiesList) {
39919b8e9a0SWilly Tu             if (ec)
40019b8e9a0SWilly Tu             {
40119b8e9a0SWilly Tu                 // this interface isn't required
40219b8e9a0SWilly Tu                 return;
40319b8e9a0SWilly Tu             }
404e5029d88SJohn Edward Broadbent             const std::string* encryptionStatus = nullptr;
405e5029d88SJohn Edward Broadbent             const bool* isLocked = nullptr;
40619b8e9a0SWilly Tu             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
40719b8e9a0SWilly Tu                      property : propertiesList)
40819b8e9a0SWilly Tu             {
40919b8e9a0SWilly Tu                 const std::string& propertyName = property.first;
41019b8e9a0SWilly Tu                 if (propertyName == "Type")
41119b8e9a0SWilly Tu                 {
41219b8e9a0SWilly Tu                     const std::string* value =
41319b8e9a0SWilly Tu                         std::get_if<std::string>(&property.second);
41419b8e9a0SWilly Tu                     if (value == nullptr)
41519b8e9a0SWilly Tu                     {
41619b8e9a0SWilly Tu                         // illegal property
41762598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Type");
41819b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
41919b8e9a0SWilly Tu                         return;
42019b8e9a0SWilly Tu                     }
42119b8e9a0SWilly Tu 
422dde9bc12SGeorge Liu                     std::optional<drive::MediaType> mediaType =
423dde9bc12SGeorge Liu                         convertDriveType(*value);
42419b8e9a0SWilly Tu                     if (!mediaType)
42519b8e9a0SWilly Tu                     {
42662598e31SEd Tanous                         BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}",
42762598e31SEd Tanous                                            *value);
428dde9bc12SGeorge Liu                         continue;
429dde9bc12SGeorge Liu                     }
430dde9bc12SGeorge Liu                     if (*mediaType == drive::MediaType::Invalid)
431dde9bc12SGeorge Liu                     {
43219b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
43319b8e9a0SWilly Tu                         return;
43419b8e9a0SWilly Tu                     }
43519b8e9a0SWilly Tu 
43619b8e9a0SWilly Tu                     asyncResp->res.jsonValue["MediaType"] = *mediaType;
43719b8e9a0SWilly Tu                 }
43819b8e9a0SWilly Tu                 else if (propertyName == "Capacity")
43919b8e9a0SWilly Tu                 {
44019b8e9a0SWilly Tu                     const uint64_t* capacity =
44119b8e9a0SWilly Tu                         std::get_if<uint64_t>(&property.second);
44219b8e9a0SWilly Tu                     if (capacity == nullptr)
44319b8e9a0SWilly Tu                     {
44462598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Capacity");
44519b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
44619b8e9a0SWilly Tu                         return;
44719b8e9a0SWilly Tu                     }
44819b8e9a0SWilly Tu                     if (*capacity == 0)
44919b8e9a0SWilly Tu                     {
45019b8e9a0SWilly Tu                         // drive capacity not known
45119b8e9a0SWilly Tu                         continue;
45219b8e9a0SWilly Tu                     }
45319b8e9a0SWilly Tu 
45419b8e9a0SWilly Tu                     asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
45519b8e9a0SWilly Tu                 }
45619b8e9a0SWilly Tu                 else if (propertyName == "Protocol")
45719b8e9a0SWilly Tu                 {
45819b8e9a0SWilly Tu                     const std::string* value =
45919b8e9a0SWilly Tu                         std::get_if<std::string>(&property.second);
46019b8e9a0SWilly Tu                     if (value == nullptr)
46119b8e9a0SWilly Tu                     {
46262598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Protocol");
46319b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
46419b8e9a0SWilly Tu                         return;
46519b8e9a0SWilly Tu                     }
46619b8e9a0SWilly Tu 
467dde9bc12SGeorge Liu                     std::optional<protocol::Protocol> proto =
468dde9bc12SGeorge Liu                         convertDriveProtocol(*value);
46919b8e9a0SWilly Tu                     if (!proto)
47019b8e9a0SWilly Tu                     {
471bd79bce8SPatrick Williams                         BMCWEB_LOG_WARNING(
472bd79bce8SPatrick Williams                             "Unknown DrivePrototype Interface: {}", *value);
473dde9bc12SGeorge Liu                         continue;
474dde9bc12SGeorge Liu                     }
475dde9bc12SGeorge Liu                     if (*proto == protocol::Protocol::Invalid)
476dde9bc12SGeorge Liu                     {
47719b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
47819b8e9a0SWilly Tu                         return;
47919b8e9a0SWilly Tu                     }
48019b8e9a0SWilly Tu                     asyncResp->res.jsonValue["Protocol"] = *proto;
48119b8e9a0SWilly Tu                 }
4823fe4d5ccSJohn Edward Broadbent                 else if (propertyName == "PredictedMediaLifeLeftPercent")
4833fe4d5ccSJohn Edward Broadbent                 {
4843fe4d5ccSJohn Edward Broadbent                     const uint8_t* lifeLeft =
4853fe4d5ccSJohn Edward Broadbent                         std::get_if<uint8_t>(&property.second);
4863fe4d5ccSJohn Edward Broadbent                     if (lifeLeft == nullptr)
4873fe4d5ccSJohn Edward Broadbent                     {
48862598e31SEd Tanous                         BMCWEB_LOG_ERROR(
48962598e31SEd Tanous                             "Illegal property: PredictedMediaLifeLeftPercent");
4903fe4d5ccSJohn Edward Broadbent                         messages::internalError(asyncResp->res);
4913fe4d5ccSJohn Edward Broadbent                         return;
4923fe4d5ccSJohn Edward Broadbent                     }
4933fe4d5ccSJohn Edward Broadbent                     // 255 means reading the value is not supported
4943fe4d5ccSJohn Edward Broadbent                     if (*lifeLeft != 255)
4953fe4d5ccSJohn Edward Broadbent                     {
496bd79bce8SPatrick Williams                         asyncResp->res
497bd79bce8SPatrick Williams                             .jsonValue["PredictedMediaLifeLeftPercent"] =
4983fe4d5ccSJohn Edward Broadbent                             *lifeLeft;
4993fe4d5ccSJohn Edward Broadbent                     }
5003fe4d5ccSJohn Edward Broadbent                 }
501e5029d88SJohn Edward Broadbent                 else if (propertyName == "EncryptionStatus")
502e5029d88SJohn Edward Broadbent                 {
503bd79bce8SPatrick Williams                     encryptionStatus =
504bd79bce8SPatrick Williams                         std::get_if<std::string>(&property.second);
505e5029d88SJohn Edward Broadbent                     if (encryptionStatus == nullptr)
506e5029d88SJohn Edward Broadbent                     {
50762598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus");
508e5029d88SJohn Edward Broadbent                         messages::internalError(asyncResp->res);
509e5029d88SJohn Edward Broadbent                         return;
51019b8e9a0SWilly Tu                     }
511e5029d88SJohn Edward Broadbent                 }
512e5029d88SJohn Edward Broadbent                 else if (propertyName == "Locked")
513e5029d88SJohn Edward Broadbent                 {
514e5029d88SJohn Edward Broadbent                     isLocked = std::get_if<bool>(&property.second);
515e5029d88SJohn Edward Broadbent                     if (isLocked == nullptr)
516e5029d88SJohn Edward Broadbent                     {
51762598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Locked");
518e5029d88SJohn Edward Broadbent                         messages::internalError(asyncResp->res);
519e5029d88SJohn Edward Broadbent                         return;
520e5029d88SJohn Edward Broadbent                     }
521e5029d88SJohn Edward Broadbent                 }
522e5029d88SJohn Edward Broadbent             }
523e5029d88SJohn Edward Broadbent 
524e5029d88SJohn Edward Broadbent             if (encryptionStatus == nullptr || isLocked == nullptr ||
525e5029d88SJohn Edward Broadbent                 *encryptionStatus ==
526a684c226SKonda Reddy Kachana                     "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown")
527e5029d88SJohn Edward Broadbent             {
528e5029d88SJohn Edward Broadbent                 return;
529e5029d88SJohn Edward Broadbent             }
530e5029d88SJohn Edward Broadbent             if (*encryptionStatus !=
531a684c226SKonda Reddy Kachana                 "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted")
532e5029d88SJohn Edward Broadbent             {
533e5029d88SJohn Edward Broadbent                 //"The drive is not currently encrypted."
534e5029d88SJohn Edward Broadbent                 asyncResp->res.jsonValue["EncryptionStatus"] =
535e5029d88SJohn Edward Broadbent                     drive::EncryptionStatus::Unencrypted;
536e5029d88SJohn Edward Broadbent                 return;
537e5029d88SJohn Edward Broadbent             }
538e5029d88SJohn Edward Broadbent             if (*isLocked)
539e5029d88SJohn Edward Broadbent             {
540e5029d88SJohn Edward Broadbent                 //"The drive is currently encrypted and the data is not
541e5029d88SJohn Edward Broadbent                 // accessible to the user."
542e5029d88SJohn Edward Broadbent                 asyncResp->res.jsonValue["EncryptionStatus"] =
543e5029d88SJohn Edward Broadbent                     drive::EncryptionStatus::Locked;
544e5029d88SJohn Edward Broadbent                 return;
545e5029d88SJohn Edward Broadbent             }
546e5029d88SJohn Edward Broadbent             // if not locked
547e5029d88SJohn Edward Broadbent             // "The drive is currently encrypted but the data is accessible
548e5029d88SJohn Edward Broadbent             // to the user in unencrypted form."
549e5029d88SJohn Edward Broadbent             asyncResp->res.jsonValue["EncryptionStatus"] =
550e5029d88SJohn Edward Broadbent                 drive::EncryptionStatus::Unlocked;
55119b8e9a0SWilly Tu         });
55219b8e9a0SWilly Tu }
55319b8e9a0SWilly Tu 
5544ff0f1f4SEd Tanous inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
555b53dcd9dSNan Zhou                             const std::string& connectionName,
556b53dcd9dSNan Zhou                             const std::string& path,
557e56ed6b9SJohn Edward Broadbent                             const std::vector<std::string>& interfaces)
558e56ed6b9SJohn Edward Broadbent {
559e56ed6b9SJohn Edward Broadbent     for (const std::string& interface : interfaces)
560e56ed6b9SJohn Edward Broadbent     {
561e56ed6b9SJohn Edward Broadbent         if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
562e56ed6b9SJohn Edward Broadbent         {
563*f7e62c14SMyung Bae             asset_utils::getAssetInfo(asyncResp, connectionName, path,
564*f7e62c14SMyung Bae                                       ""_json_pointer, false);
565e56ed6b9SJohn Edward Broadbent         }
566e56ed6b9SJohn Edward Broadbent         else if (interface == "xyz.openbmc_project.Inventory.Item")
567e56ed6b9SJohn Edward Broadbent         {
568e56ed6b9SJohn Edward Broadbent             getDrivePresent(asyncResp, connectionName, path);
569e56ed6b9SJohn Edward Broadbent         }
570e56ed6b9SJohn Edward Broadbent         else if (interface == "xyz.openbmc_project.State.Drive")
571e56ed6b9SJohn Edward Broadbent         {
572e56ed6b9SJohn Edward Broadbent             getDriveState(asyncResp, connectionName, path);
573e56ed6b9SJohn Edward Broadbent         }
574e56ed6b9SJohn Edward Broadbent         else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
575e56ed6b9SJohn Edward Broadbent         {
576e56ed6b9SJohn Edward Broadbent             getDriveItemProperties(asyncResp, connectionName, path);
577e56ed6b9SJohn Edward Broadbent         }
578e56ed6b9SJohn Edward Broadbent     }
579e56ed6b9SJohn Edward Broadbent }
580e56ed6b9SJohn Edward Broadbent 
58136d52334SEd Tanous inline void afterGetSubtreeSystemsStorageDrive(
58245ca1b86SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
58336d52334SEd Tanous     const std::string& driveId, const boost::system::error_code& ec,
58436d52334SEd Tanous     const dbus::utility::MapperGetSubTreeResponse& subtree)
58545ca1b86SEd Tanous {
5867e860f15SJohn Edward Broadbent     if (ec)
5877e860f15SJohn Edward Broadbent     {
58862598e31SEd Tanous         BMCWEB_LOG_ERROR("Drive mapper call error");
5897e860f15SJohn Edward Broadbent         messages::internalError(asyncResp->res);
5907e860f15SJohn Edward Broadbent         return;
5917e860f15SJohn Edward Broadbent     }
5927e860f15SJohn Edward Broadbent 
5933544d2a7SEd Tanous     auto drive = std::ranges::find_if(
5943544d2a7SEd Tanous         subtree,
59536d52334SEd Tanous         [&driveId](const std::pair<std::string,
5968cb65f8aSNan Zhou                                    dbus::utility::MapperServiceMap>& object) {
59736d52334SEd Tanous             return sdbusplus::message::object_path(object.first).filename() ==
59836d52334SEd Tanous                    driveId;
5997e860f15SJohn Edward Broadbent         });
6007e860f15SJohn Edward Broadbent 
60103913171SWilly Tu     if (drive == subtree.end())
6027e860f15SJohn Edward Broadbent     {
603002d39b4SEd Tanous         messages::resourceNotFound(asyncResp->res, "Drive", driveId);
6047e860f15SJohn Edward Broadbent         return;
6057e860f15SJohn Edward Broadbent     }
6067e860f15SJohn Edward Broadbent 
60703913171SWilly Tu     const std::string& path = drive->first;
60836d52334SEd Tanous     const dbus::utility::MapperServiceMap& connectionNames = drive->second;
6097e860f15SJohn Edward Broadbent 
610002d39b4SEd Tanous     asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
611253f11b8SEd Tanous     asyncResp->res.jsonValue["@odata.id"] =
612253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Drives/{}",
613253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, driveId);
6147e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["Name"] = driveId;
6157e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["Id"] = driveId;
6167e860f15SJohn Edward Broadbent 
6177e860f15SJohn Edward Broadbent     if (connectionNames.size() != 1)
6187e860f15SJohn Edward Broadbent     {
61962598e31SEd Tanous         BMCWEB_LOG_ERROR("Connection size {}, not equal to 1",
62062598e31SEd Tanous                          connectionNames.size());
6217e860f15SJohn Edward Broadbent         messages::internalError(asyncResp->res);
6227e860f15SJohn Edward Broadbent         return;
6237e860f15SJohn Edward Broadbent     }
6247e860f15SJohn Edward Broadbent 
625bd79bce8SPatrick Williams     getMainChassisId(
626bd79bce8SPatrick Williams         asyncResp, [](const std::string& chassisId,
6277e860f15SJohn Edward Broadbent                       const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
628002d39b4SEd Tanous             aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
629ef4c65b7SEd Tanous                 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
6307e860f15SJohn Edward Broadbent         });
6317e860f15SJohn Edward Broadbent 
632a25aeccfSNikhil Potade     // default it to Enabled
633539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
634a25aeccfSNikhil Potade 
635e56ed6b9SJohn Edward Broadbent     addAllDriveInfo(asyncResp, connectionNames[0].first, path,
636e56ed6b9SJohn Edward Broadbent                     connectionNames[0].second);
637a25aeccfSNikhil Potade }
63892903bd4SJohn Edward Broadbent 
63936d52334SEd Tanous inline void handleSystemsStorageDriveGet(
64036d52334SEd Tanous     App& app, const crow::Request& req,
64192903bd4SJohn Edward Broadbent     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
64236d52334SEd Tanous     const std::string& systemName, const std::string& driveId)
64392903bd4SJohn Edward Broadbent {
6443ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
64592903bd4SJohn Edward Broadbent     {
64692903bd4SJohn Edward Broadbent         return;
64792903bd4SJohn Edward Broadbent     }
64825b54dbaSEd Tanous     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
6497f3e84a1SEd Tanous     {
6507f3e84a1SEd Tanous         // Option currently returns no systems.  TBD
6517f3e84a1SEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
6527f3e84a1SEd Tanous                                    systemName);
6537f3e84a1SEd Tanous         return;
6547f3e84a1SEd Tanous     }
6557f3e84a1SEd Tanous 
656253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
65736d52334SEd Tanous     {
65836d52334SEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
65936d52334SEd Tanous                                    systemName);
66036d52334SEd Tanous         return;
66136d52334SEd Tanous     }
66292903bd4SJohn Edward Broadbent 
66336d52334SEd Tanous     constexpr std::array<std::string_view, 1> interfaces = {
66436d52334SEd Tanous         "xyz.openbmc_project.Inventory.Item.Drive"};
665e99073f5SGeorge Liu     dbus::utility::getSubTree(
666e99073f5SGeorge Liu         "/xyz/openbmc_project/inventory", 0, interfaces,
66736d52334SEd Tanous         std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
66836d52334SEd Tanous                         driveId));
66936d52334SEd Tanous }
67036d52334SEd Tanous 
67136d52334SEd Tanous inline void requestRoutesDrive(App& app)
67236d52334SEd Tanous {
67336d52334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
67436d52334SEd Tanous         .privileges(redfish::privileges::getDrive)
67536d52334SEd Tanous         .methods(boost::beast::http::verb::get)(
67636d52334SEd Tanous             std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
67736d52334SEd Tanous }
67836d52334SEd Tanous 
67936d52334SEd Tanous inline void afterChassisDriveCollectionSubtreeGet(
68036d52334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
68136d52334SEd Tanous     const std::string& chassisId, const boost::system::error_code& ec,
68236d52334SEd Tanous     const dbus::utility::MapperGetSubTreeResponse& subtree)
68336d52334SEd Tanous {
68492903bd4SJohn Edward Broadbent     if (ec)
68592903bd4SJohn Edward Broadbent     {
68692903bd4SJohn Edward Broadbent         if (ec == boost::system::errc::host_unreachable)
68792903bd4SJohn Edward Broadbent         {
68836d52334SEd Tanous             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
68992903bd4SJohn Edward Broadbent             return;
69092903bd4SJohn Edward Broadbent         }
69192903bd4SJohn Edward Broadbent         messages::internalError(asyncResp->res);
69292903bd4SJohn Edward Broadbent         return;
69392903bd4SJohn Edward Broadbent     }
69492903bd4SJohn Edward Broadbent 
69592903bd4SJohn Edward Broadbent     // Iterate over all retrieved ObjectPaths.
6968cb65f8aSNan Zhou     for (const auto& [path, connectionNames] : subtree)
69792903bd4SJohn Edward Broadbent     {
69892903bd4SJohn Edward Broadbent         sdbusplus::message::object_path objPath(path);
69992903bd4SJohn Edward Broadbent         if (objPath.filename() != chassisId)
70092903bd4SJohn Edward Broadbent         {
70192903bd4SJohn Edward Broadbent             continue;
70292903bd4SJohn Edward Broadbent         }
70392903bd4SJohn Edward Broadbent 
70492903bd4SJohn Edward Broadbent         if (connectionNames.empty())
70592903bd4SJohn Edward Broadbent         {
70662598e31SEd Tanous             BMCWEB_LOG_ERROR("Got 0 Connection names");
70792903bd4SJohn Edward Broadbent             continue;
70892903bd4SJohn Edward Broadbent         }
70992903bd4SJohn Edward Broadbent 
71092903bd4SJohn Edward Broadbent         asyncResp->res.jsonValue["@odata.type"] =
71192903bd4SJohn Edward Broadbent             "#DriveCollection.DriveCollection";
71292903bd4SJohn Edward Broadbent         asyncResp->res.jsonValue["@odata.id"] =
713ef4c65b7SEd Tanous             boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
71492903bd4SJohn Edward Broadbent         asyncResp->res.jsonValue["Name"] = "Drive Collection";
71592903bd4SJohn Edward Broadbent 
71692903bd4SJohn Edward Broadbent         // Association lambda
7176c3e9451SGeorge Liu         dbus::utility::getAssociationEndPoints(
7186c3e9451SGeorge Liu             path + "/drive",
71936d52334SEd Tanous             [asyncResp, chassisId](const boost::system::error_code& ec3,
7206c3e9451SGeorge Liu                                    const dbus::utility::MapperEndPoints& resp) {
72192903bd4SJohn Edward Broadbent                 if (ec3)
72292903bd4SJohn Edward Broadbent                 {
72362598e31SEd Tanous                     BMCWEB_LOG_ERROR("Error in chassis Drive association ");
72492903bd4SJohn Edward Broadbent                 }
72592903bd4SJohn Edward Broadbent                 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
72692903bd4SJohn Edward Broadbent                 // important if array is empty
72792903bd4SJohn Edward Broadbent                 members = nlohmann::json::array();
72892903bd4SJohn Edward Broadbent 
72992903bd4SJohn Edward Broadbent                 std::vector<std::string> leafNames;
73092903bd4SJohn Edward Broadbent                 for (const auto& drive : resp)
73192903bd4SJohn Edward Broadbent                 {
7328a592810SEd Tanous                     sdbusplus::message::object_path drivePath(drive);
7338a592810SEd Tanous                     leafNames.push_back(drivePath.filename());
73492903bd4SJohn Edward Broadbent                 }
73592903bd4SJohn Edward Broadbent 
7363544d2a7SEd Tanous                 std::ranges::sort(leafNames, AlphanumLess<std::string>());
73792903bd4SJohn Edward Broadbent 
73892903bd4SJohn Edward Broadbent                 for (const auto& leafName : leafNames)
73992903bd4SJohn Edward Broadbent                 {
74092903bd4SJohn Edward Broadbent                     nlohmann::json::object_t member;
741bd79bce8SPatrick Williams                     member["@odata.id"] =
742bd79bce8SPatrick Williams                         boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}",
743bd79bce8SPatrick Williams                                             chassisId, leafName);
744b2ba3072SPatrick Williams                     members.emplace_back(std::move(member));
74592903bd4SJohn Edward Broadbent                     // navigation links will be registered in next patch set
74692903bd4SJohn Edward Broadbent                 }
74792903bd4SJohn Edward Broadbent                 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
74892903bd4SJohn Edward Broadbent             }); // end association lambda
74992903bd4SJohn Edward Broadbent 
75092903bd4SJohn Edward Broadbent     } // end Iterate over all retrieved ObjectPaths
75136d52334SEd Tanous }
75236d52334SEd Tanous /**
75336d52334SEd Tanous  * Chassis drives, this URL will show all the DriveCollection
75436d52334SEd Tanous  * information
75536d52334SEd Tanous  */
75636d52334SEd Tanous inline void chassisDriveCollectionGet(
75736d52334SEd Tanous     crow::App& app, const crow::Request& req,
75836d52334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
75936d52334SEd Tanous     const std::string& chassisId)
76036d52334SEd Tanous {
76136d52334SEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
76236d52334SEd Tanous     {
76336d52334SEd Tanous         return;
76436d52334SEd Tanous     }
76536d52334SEd Tanous 
76636d52334SEd Tanous     // mapper call lambda
76736d52334SEd Tanous     dbus::utility::getSubTree(
7683f95a277SMyung Bae         "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
76936d52334SEd Tanous         std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
77036d52334SEd Tanous                         chassisId));
77192903bd4SJohn Edward Broadbent }
77292903bd4SJohn Edward Broadbent 
77392903bd4SJohn Edward Broadbent inline void requestRoutesChassisDrive(App& app)
77492903bd4SJohn Edward Broadbent {
77592903bd4SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
77692903bd4SJohn Edward Broadbent         .privileges(redfish::privileges::getDriveCollection)
77792903bd4SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
77892903bd4SJohn Edward Broadbent             std::bind_front(chassisDriveCollectionGet, std::ref(app)));
77992903bd4SJohn Edward Broadbent }
78092903bd4SJohn Edward Broadbent 
781b53dcd9dSNan Zhou inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
782b53dcd9dSNan Zhou                        const std::string& chassisId,
783b53dcd9dSNan Zhou                        const std::string& driveName,
7845e7e2dc5SEd Tanous                        const boost::system::error_code& ec,
785e56ed6b9SJohn Edward Broadbent                        const dbus::utility::MapperGetSubTreeResponse& subtree)
786e56ed6b9SJohn Edward Broadbent {
787e56ed6b9SJohn Edward Broadbent     if (ec)
788e56ed6b9SJohn Edward Broadbent     {
78962598e31SEd Tanous         BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
790e56ed6b9SJohn Edward Broadbent         messages::internalError(asyncResp->res);
791e56ed6b9SJohn Edward Broadbent         return;
792e56ed6b9SJohn Edward Broadbent     }
793e56ed6b9SJohn Edward Broadbent 
794e56ed6b9SJohn Edward Broadbent     // Iterate over all retrieved ObjectPaths.
7958cb65f8aSNan Zhou     for (const auto& [path, connectionNames] : subtree)
796e56ed6b9SJohn Edward Broadbent     {
797e56ed6b9SJohn Edward Broadbent         sdbusplus::message::object_path objPath(path);
798e56ed6b9SJohn Edward Broadbent         if (objPath.filename() != driveName)
799e56ed6b9SJohn Edward Broadbent         {
800e56ed6b9SJohn Edward Broadbent             continue;
801e56ed6b9SJohn Edward Broadbent         }
802e56ed6b9SJohn Edward Broadbent 
803e56ed6b9SJohn Edward Broadbent         if (connectionNames.empty())
804e56ed6b9SJohn Edward Broadbent         {
80562598e31SEd Tanous             BMCWEB_LOG_ERROR("Got 0 Connection names");
806e56ed6b9SJohn Edward Broadbent             continue;
807e56ed6b9SJohn Edward Broadbent         }
808e56ed6b9SJohn Edward Broadbent 
809ef4c65b7SEd Tanous         asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
810ef4c65b7SEd Tanous             "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
811e56ed6b9SJohn Edward Broadbent 
812e56ed6b9SJohn Edward Broadbent         asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
813a0cb40cbSJohn Edward Broadbent         asyncResp->res.jsonValue["Name"] = driveName;
814e56ed6b9SJohn Edward Broadbent         asyncResp->res.jsonValue["Id"] = driveName;
815e56ed6b9SJohn Edward Broadbent         // default it to Enabled
816539d8c6bSEd Tanous         asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
817e56ed6b9SJohn Edward Broadbent 
818e56ed6b9SJohn Edward Broadbent         nlohmann::json::object_t linkChassisNav;
819e56ed6b9SJohn Edward Broadbent         linkChassisNav["@odata.id"] =
820ef4c65b7SEd Tanous             boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
821e56ed6b9SJohn Edward Broadbent         asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
822e56ed6b9SJohn Edward Broadbent 
823e56ed6b9SJohn Edward Broadbent         addAllDriveInfo(asyncResp, connectionNames[0].first, path,
824e56ed6b9SJohn Edward Broadbent                         connectionNames[0].second);
825e56ed6b9SJohn Edward Broadbent     }
826e56ed6b9SJohn Edward Broadbent }
827e56ed6b9SJohn Edward Broadbent 
828bd79bce8SPatrick Williams inline void matchAndFillDrive(
829bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
830bd79bce8SPatrick Williams     const std::string& chassisId, const std::string& driveName,
831e56ed6b9SJohn Edward Broadbent     const std::vector<std::string>& resp)
832e56ed6b9SJohn Edward Broadbent {
833e56ed6b9SJohn Edward Broadbent     for (const std::string& drivePath : resp)
834e56ed6b9SJohn Edward Broadbent     {
835e56ed6b9SJohn Edward Broadbent         sdbusplus::message::object_path path(drivePath);
836e56ed6b9SJohn Edward Broadbent         std::string leaf = path.filename();
837e56ed6b9SJohn Edward Broadbent         if (leaf != driveName)
838e56ed6b9SJohn Edward Broadbent         {
839e56ed6b9SJohn Edward Broadbent             continue;
840e56ed6b9SJohn Edward Broadbent         }
841e56ed6b9SJohn Edward Broadbent         //  mapper call drive
842e99073f5SGeorge Liu         constexpr std::array<std::string_view, 1> driveInterface = {
843e56ed6b9SJohn Edward Broadbent             "xyz.openbmc_project.Inventory.Item.Drive"};
844e99073f5SGeorge Liu         dbus::utility::getSubTree(
845e99073f5SGeorge Liu             "/xyz/openbmc_project/inventory", 0, driveInterface,
846e56ed6b9SJohn Edward Broadbent             [asyncResp, chassisId, driveName](
847e99073f5SGeorge Liu                 const boost::system::error_code& ec,
848e56ed6b9SJohn Edward Broadbent                 const dbus::utility::MapperGetSubTreeResponse& subtree) {
849e56ed6b9SJohn Edward Broadbent                 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
850e99073f5SGeorge Liu             });
851e56ed6b9SJohn Edward Broadbent     }
852e56ed6b9SJohn Edward Broadbent }
853e56ed6b9SJohn Edward Broadbent 
854bd79bce8SPatrick Williams inline void handleChassisDriveGet(
855bd79bce8SPatrick Williams     crow::App& app, const crow::Request& req,
856e56ed6b9SJohn Edward Broadbent     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
857bd79bce8SPatrick Williams     const std::string& chassisId, const std::string& driveName)
858e56ed6b9SJohn Edward Broadbent {
85903810a11SMichal Orzel     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
860e56ed6b9SJohn Edward Broadbent     {
861e56ed6b9SJohn Edward Broadbent         return;
862e56ed6b9SJohn Edward Broadbent     }
863e56ed6b9SJohn Edward Broadbent 
864e56ed6b9SJohn Edward Broadbent     // mapper call chassis
865e99073f5SGeorge Liu     dbus::utility::getSubTree(
8663f95a277SMyung Bae         "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
867e56ed6b9SJohn Edward Broadbent         [asyncResp, chassisId,
868e99073f5SGeorge Liu          driveName](const boost::system::error_code& ec,
869e56ed6b9SJohn Edward Broadbent                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
870e56ed6b9SJohn Edward Broadbent             if (ec)
871e56ed6b9SJohn Edward Broadbent             {
872e56ed6b9SJohn Edward Broadbent                 messages::internalError(asyncResp->res);
873e56ed6b9SJohn Edward Broadbent                 return;
874e56ed6b9SJohn Edward Broadbent             }
875e56ed6b9SJohn Edward Broadbent 
876e56ed6b9SJohn Edward Broadbent             // Iterate over all retrieved ObjectPaths.
8778cb65f8aSNan Zhou             for (const auto& [path, connectionNames] : subtree)
878e56ed6b9SJohn Edward Broadbent             {
879e56ed6b9SJohn Edward Broadbent                 sdbusplus::message::object_path objPath(path);
880e56ed6b9SJohn Edward Broadbent                 if (objPath.filename() != chassisId)
881e56ed6b9SJohn Edward Broadbent                 {
882e56ed6b9SJohn Edward Broadbent                     continue;
883e56ed6b9SJohn Edward Broadbent                 }
884e56ed6b9SJohn Edward Broadbent 
885e56ed6b9SJohn Edward Broadbent                 if (connectionNames.empty())
886e56ed6b9SJohn Edward Broadbent                 {
88762598e31SEd Tanous                     BMCWEB_LOG_ERROR("Got 0 Connection names");
888e56ed6b9SJohn Edward Broadbent                     continue;
889e56ed6b9SJohn Edward Broadbent                 }
890e56ed6b9SJohn Edward Broadbent 
8916c3e9451SGeorge Liu                 dbus::utility::getAssociationEndPoints(
8926c3e9451SGeorge Liu                     path + "/drive",
893e56ed6b9SJohn Edward Broadbent                     [asyncResp, chassisId,
8945e7e2dc5SEd Tanous                      driveName](const boost::system::error_code& ec3,
8956c3e9451SGeorge Liu                                 const dbus::utility::MapperEndPoints& resp) {
896e56ed6b9SJohn Edward Broadbent                         if (ec3)
897e56ed6b9SJohn Edward Broadbent                         {
898e56ed6b9SJohn Edward Broadbent                             return; // no drives = no failures
899e56ed6b9SJohn Edward Broadbent                         }
900bd79bce8SPatrick Williams                         matchAndFillDrive(asyncResp, chassisId, driveName,
901bd79bce8SPatrick Williams                                           resp);
902e56ed6b9SJohn Edward Broadbent                     });
903d8f53b82SJian Zhang                 return;
904e56ed6b9SJohn Edward Broadbent             }
905d8f53b82SJian Zhang             // Couldn't find an object with that name.  return an error
906d8f53b82SJian Zhang             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
907e99073f5SGeorge Liu         });
908e56ed6b9SJohn Edward Broadbent }
909e56ed6b9SJohn Edward Broadbent 
910e56ed6b9SJohn Edward Broadbent /**
911e56ed6b9SJohn Edward Broadbent  * This URL will show the drive interface for the specific drive in the chassis
912e56ed6b9SJohn Edward Broadbent  */
913e56ed6b9SJohn Edward Broadbent inline void requestRoutesChassisDriveName(App& app)
914e56ed6b9SJohn Edward Broadbent {
915e56ed6b9SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
916e56ed6b9SJohn Edward Broadbent         .privileges(redfish::privileges::getChassis)
917e56ed6b9SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
918e56ed6b9SJohn Edward Broadbent             std::bind_front(handleChassisDriveGet, std::ref(app)));
919e56ed6b9SJohn Edward Broadbent }
920e56ed6b9SJohn Edward Broadbent 
92161b1eb21SWilly Tu inline void populateStorageController(
92261b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
92361b1eb21SWilly Tu     const std::string& controllerId, const std::string& connectionName,
92461b1eb21SWilly Tu     const std::string& path)
92561b1eb21SWilly Tu {
92661b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.type"] =
92761b1eb21SWilly Tu         "#StorageController.v1_6_0.StorageController";
928253f11b8SEd Tanous     asyncResp->res.jsonValue["@odata.id"] =
929253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
930253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, controllerId);
93161b1eb21SWilly Tu     asyncResp->res.jsonValue["Name"] = controllerId;
93261b1eb21SWilly Tu     asyncResp->res.jsonValue["Id"] = controllerId;
933539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
93461b1eb21SWilly Tu 
935deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
936deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present",
93761b1eb21SWilly Tu         [asyncResp](const boost::system::error_code& ec, bool isPresent) {
93861b1eb21SWilly Tu             // this interface isn't necessary, only check it
93961b1eb21SWilly Tu             // if we get a good return
94061b1eb21SWilly Tu             if (ec)
94161b1eb21SWilly Tu             {
94262598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Failed to get Present property");
94361b1eb21SWilly Tu                 return;
94461b1eb21SWilly Tu             }
94561b1eb21SWilly Tu             if (!isPresent)
94661b1eb21SWilly Tu             {
947539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
948539d8c6bSEd Tanous                     resource::State::Absent;
94961b1eb21SWilly Tu             }
95061b1eb21SWilly Tu         });
95161b1eb21SWilly Tu 
952*f7e62c14SMyung Bae     asset_utils::getAssetInfo(asyncResp, connectionName, path, ""_json_pointer,
953*f7e62c14SMyung Bae                               false);
95461b1eb21SWilly Tu }
95561b1eb21SWilly Tu 
95661b1eb21SWilly Tu inline void getStorageControllerHandler(
95761b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
95861b1eb21SWilly Tu     const std::string& controllerId, const boost::system::error_code& ec,
95961b1eb21SWilly Tu     const dbus::utility::MapperGetSubTreeResponse& subtree)
96061b1eb21SWilly Tu {
96161b1eb21SWilly Tu     if (ec || subtree.empty())
96261b1eb21SWilly Tu     {
96361b1eb21SWilly Tu         // doesn't have to be there
96462598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to handle StorageController");
96561b1eb21SWilly Tu         return;
96661b1eb21SWilly Tu     }
96761b1eb21SWilly Tu 
96861b1eb21SWilly Tu     for (const auto& [path, interfaceDict] : subtree)
96961b1eb21SWilly Tu     {
97061b1eb21SWilly Tu         sdbusplus::message::object_path object(path);
97161b1eb21SWilly Tu         std::string id = object.filename();
97261b1eb21SWilly Tu         if (id.empty())
97361b1eb21SWilly Tu         {
97462598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
97561b1eb21SWilly Tu             return;
97661b1eb21SWilly Tu         }
97761b1eb21SWilly Tu         if (id != controllerId)
97861b1eb21SWilly Tu         {
97961b1eb21SWilly Tu             continue;
98061b1eb21SWilly Tu         }
98161b1eb21SWilly Tu 
98261b1eb21SWilly Tu         if (interfaceDict.size() != 1)
98361b1eb21SWilly Tu         {
98462598e31SEd Tanous             BMCWEB_LOG_ERROR("Connection size {}, greater than 1",
98562598e31SEd Tanous                              interfaceDict.size());
98661b1eb21SWilly Tu             messages::internalError(asyncResp->res);
98761b1eb21SWilly Tu             return;
98861b1eb21SWilly Tu         }
98961b1eb21SWilly Tu 
99061b1eb21SWilly Tu         const std::string& connectionName = interfaceDict.front().first;
99161b1eb21SWilly Tu         populateStorageController(asyncResp, controllerId, connectionName,
99261b1eb21SWilly Tu                                   path);
99361b1eb21SWilly Tu     }
99461b1eb21SWilly Tu }
99561b1eb21SWilly Tu 
99661b1eb21SWilly Tu inline void populateStorageControllerCollection(
99761b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
99861b1eb21SWilly Tu     const boost::system::error_code& ec,
99961b1eb21SWilly Tu     const dbus::utility::MapperGetSubTreePathsResponse& controllerList)
100061b1eb21SWilly Tu {
100161b1eb21SWilly Tu     nlohmann::json::array_t members;
100261b1eb21SWilly Tu     if (ec || controllerList.empty())
100361b1eb21SWilly Tu     {
100461b1eb21SWilly Tu         asyncResp->res.jsonValue["Members"] = std::move(members);
100561b1eb21SWilly Tu         asyncResp->res.jsonValue["Members@odata.count"] = 0;
100662598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to find any StorageController");
100761b1eb21SWilly Tu         return;
100861b1eb21SWilly Tu     }
100961b1eb21SWilly Tu 
101061b1eb21SWilly Tu     for (const std::string& path : controllerList)
101161b1eb21SWilly Tu     {
101261b1eb21SWilly Tu         std::string id = sdbusplus::message::object_path(path).filename();
101361b1eb21SWilly Tu         if (id.empty())
101461b1eb21SWilly Tu         {
101562598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
101661b1eb21SWilly Tu             return;
101761b1eb21SWilly Tu         }
101861b1eb21SWilly Tu         nlohmann::json::object_t member;
101961b1eb21SWilly Tu         member["@odata.id"] = boost::urls::format(
1020253f11b8SEd Tanous             "/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1021253f11b8SEd Tanous             BMCWEB_REDFISH_SYSTEM_URI_NAME, id);
102261b1eb21SWilly Tu         members.emplace_back(member);
102361b1eb21SWilly Tu     }
102461b1eb21SWilly Tu     asyncResp->res.jsonValue["Members@odata.count"] = members.size();
102561b1eb21SWilly Tu     asyncResp->res.jsonValue["Members"] = std::move(members);
102661b1eb21SWilly Tu }
102761b1eb21SWilly Tu 
102836d52334SEd Tanous inline void handleSystemsStorageControllerCollectionGet(
102961b1eb21SWilly Tu     App& app, const crow::Request& req,
103061b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
103161b1eb21SWilly Tu     const std::string& systemName)
103261b1eb21SWilly Tu {
103361b1eb21SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
103461b1eb21SWilly Tu     {
103562598e31SEd Tanous         BMCWEB_LOG_DEBUG(
103662598e31SEd Tanous             "Failed to setup Redfish Route for StorageController Collection");
103761b1eb21SWilly Tu         return;
103861b1eb21SWilly Tu     }
1039253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
104061b1eb21SWilly Tu     {
104161b1eb21SWilly Tu         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
104261b1eb21SWilly Tu                                    systemName);
104362598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
104461b1eb21SWilly Tu         return;
104561b1eb21SWilly Tu     }
104661b1eb21SWilly Tu 
104761b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.type"] =
104861b1eb21SWilly Tu         "#StorageControllerCollection.StorageControllerCollection";
104961b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.id"] =
1050253f11b8SEd Tanous         std::format("/redfish/v1/Systems/{}/Storage/1/Controllers",
1051253f11b8SEd Tanous                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
105261b1eb21SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage Controller Collection";
105361b1eb21SWilly Tu 
105461b1eb21SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
105561b1eb21SWilly Tu         "xyz.openbmc_project.Inventory.Item.StorageController"};
105661b1eb21SWilly Tu     dbus::utility::getSubTreePaths(
105761b1eb21SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
105861b1eb21SWilly Tu         [asyncResp](const boost::system::error_code& ec,
105961b1eb21SWilly Tu                     const dbus::utility::MapperGetSubTreePathsResponse&
106061b1eb21SWilly Tu                         controllerList) {
106161b1eb21SWilly Tu             populateStorageControllerCollection(asyncResp, ec, controllerList);
106261b1eb21SWilly Tu         });
106361b1eb21SWilly Tu }
106461b1eb21SWilly Tu 
106536d52334SEd Tanous inline void handleSystemsStorageControllerGet(
106661b1eb21SWilly Tu     App& app, const crow::Request& req,
106761b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
106861b1eb21SWilly Tu     const std::string& systemName, const std::string& controllerId)
106961b1eb21SWilly Tu {
107061b1eb21SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
107161b1eb21SWilly Tu     {
107262598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to setup Redfish Route for StorageController");
107361b1eb21SWilly Tu         return;
107461b1eb21SWilly Tu     }
1075253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
107661b1eb21SWilly Tu     {
107761b1eb21SWilly Tu         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
107861b1eb21SWilly Tu                                    systemName);
107962598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
108061b1eb21SWilly Tu         return;
108161b1eb21SWilly Tu     }
108261b1eb21SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
108361b1eb21SWilly Tu         "xyz.openbmc_project.Inventory.Item.StorageController"};
108461b1eb21SWilly Tu     dbus::utility::getSubTree(
108561b1eb21SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
108661b1eb21SWilly Tu         [asyncResp,
108761b1eb21SWilly Tu          controllerId](const boost::system::error_code& ec,
108861b1eb21SWilly Tu                        const dbus::utility::MapperGetSubTreeResponse& subtree) {
108961b1eb21SWilly Tu             getStorageControllerHandler(asyncResp, controllerId, ec, subtree);
109061b1eb21SWilly Tu         });
109161b1eb21SWilly Tu }
109261b1eb21SWilly Tu 
109361b1eb21SWilly Tu inline void requestRoutesStorageControllerCollection(App& app)
109461b1eb21SWilly Tu {
109561b1eb21SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/")
109661b1eb21SWilly Tu         .privileges(redfish::privileges::getStorageControllerCollection)
109736d52334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
109836d52334SEd Tanous             handleSystemsStorageControllerCollectionGet, std::ref(app)));
109961b1eb21SWilly Tu }
110061b1eb21SWilly Tu 
110161b1eb21SWilly Tu inline void requestRoutesStorageController(App& app)
110261b1eb21SWilly Tu {
110361b1eb21SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>")
110461b1eb21SWilly Tu         .privileges(redfish::privileges::getStorageController)
110561b1eb21SWilly Tu         .methods(boost::beast::http::verb::get)(
110636d52334SEd Tanous             std::bind_front(handleSystemsStorageControllerGet, std::ref(app)));
110761b1eb21SWilly Tu }
110861b1eb21SWilly Tu 
1109a25aeccfSNikhil Potade } // namespace redfish
1110