xref: /openbmc/bmcweb/features/redfish/lib/storage.hpp (revision deae6a789444debc4724fb6902fc5def299afbee)
1a25aeccfSNikhil Potade /*
26be832e2SEd Tanous Copyright (c) 2019 Intel Corporation
36be832e2SEd Tanous 
46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
56be832e2SEd Tanous you may not use this file except in compliance with the License.
66be832e2SEd Tanous You may obtain a copy of the License at
76be832e2SEd Tanous 
86be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
96be832e2SEd Tanous 
106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136be832e2SEd Tanous See the License for the specific language governing permissions and
146be832e2SEd Tanous limitations under the License.
15a25aeccfSNikhil Potade */
16a25aeccfSNikhil Potade #pragma once
17a25aeccfSNikhil Potade 
1813451e39SWilly Tu #include "bmcweb_config.h"
1913451e39SWilly Tu 
203ccb3adbSEd Tanous #include "app.hpp"
217a1dbc48SGeorge Liu #include "dbus_utility.hpp"
22e5029d88SJohn Edward Broadbent #include "generated/enums/drive.hpp"
23dde9bc12SGeorge Liu #include "generated/enums/protocol.hpp"
24539d8c6bSEd Tanous #include "generated/enums/resource.hpp"
25a8e884fcSEd Tanous #include "human_sort.hpp"
263ccb3adbSEd Tanous #include "query.hpp"
27a8e884fcSEd Tanous #include "redfish_util.hpp"
283ccb3adbSEd Tanous #include "registries/privilege_registry.hpp"
295e577bc1SWilly Tu #include "utils/collection.hpp"
303ccb3adbSEd Tanous #include "utils/dbus_utils.hpp"
312ad9c2f6SJames Feist 
32e99073f5SGeorge Liu #include <boost/system/error_code.hpp>
33ef4c65b7SEd Tanous #include <boost/url/format.hpp>
341e1e598dSJonathan Doman #include <sdbusplus/asio/property.hpp>
35d1bde9e5SKrzysztof Grobelny #include <sdbusplus/unpack_properties.hpp>
36a25aeccfSNikhil Potade 
377a1dbc48SGeorge Liu #include <array>
383544d2a7SEd Tanous #include <ranges>
397a1dbc48SGeorge Liu #include <string_view>
407a1dbc48SGeorge Liu 
41a25aeccfSNikhil Potade namespace redfish
42a25aeccfSNikhil Potade {
4336d52334SEd Tanous 
4436d52334SEd Tanous inline void handleSystemsStorageCollectionGet(
4536d52334SEd Tanous     App& app, const crow::Request& req,
4622d268cbSEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
4736d52334SEd Tanous     const std::string& systemName)
4836d52334SEd Tanous {
493ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
5045ca1b86SEd Tanous     {
5145ca1b86SEd Tanous         return;
5245ca1b86SEd Tanous     }
53253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
5422d268cbSEd Tanous     {
5522d268cbSEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
5622d268cbSEd Tanous                                    systemName);
5722d268cbSEd Tanous         return;
5822d268cbSEd Tanous     }
5922d268cbSEd Tanous 
608d1b46d7Szhanghch05     asyncResp->res.jsonValue["@odata.type"] =
618d1b46d7Szhanghch05         "#StorageCollection.StorageCollection";
62253f11b8SEd Tanous     asyncResp->res.jsonValue["@odata.id"] = std::format(
63253f11b8SEd Tanous         "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME);
648d1b46d7Szhanghch05     asyncResp->res.jsonValue["Name"] = "Storage Collection";
655e577bc1SWilly Tu 
665e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interface{
675a39f77aSPatrick Williams         "xyz.openbmc_project.Inventory.Item.Storage"};
685e577bc1SWilly Tu     collection_util::getCollectionMembers(
69253f11b8SEd Tanous         asyncResp,
70253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage",
71253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME),
7236b5f1edSLakshmi Yadlapati         interface, "/xyz/openbmc_project/inventory");
735e577bc1SWilly Tu }
745e577bc1SWilly Tu 
755e577bc1SWilly Tu inline void handleStorageCollectionGet(
765e577bc1SWilly Tu     App& app, const crow::Request& req,
775e577bc1SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
785e577bc1SWilly Tu {
795e577bc1SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
805e577bc1SWilly Tu     {
815e577bc1SWilly Tu         return;
825e577bc1SWilly Tu     }
835e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.type"] =
845e577bc1SWilly Tu         "#StorageCollection.StorageCollection";
855e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Storage";
865e577bc1SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage Collection";
875e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interface{
885a39f77aSPatrick Williams         "xyz.openbmc_project.Inventory.Item.Storage"};
895e577bc1SWilly Tu     collection_util::getCollectionMembers(
9036b5f1edSLakshmi Yadlapati         asyncResp, boost::urls::format("/redfish/v1/Storage"), interface,
9136b5f1edSLakshmi Yadlapati         "/xyz/openbmc_project/inventory");
92a25aeccfSNikhil Potade }
93a25aeccfSNikhil Potade 
9436d52334SEd Tanous inline void requestRoutesStorageCollection(App& app)
95a25aeccfSNikhil Potade {
9636d52334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/")
9736d52334SEd Tanous         .privileges(redfish::privileges::getStorageCollection)
9836d52334SEd Tanous         .methods(boost::beast::http::verb::get)(
9936d52334SEd Tanous             std::bind_front(handleSystemsStorageCollectionGet, std::ref(app)));
1005e577bc1SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Storage/")
1015e577bc1SWilly Tu         .privileges(redfish::privileges::getStorageCollection)
1025e577bc1SWilly Tu         .methods(boost::beast::http::verb::get)(
1035e577bc1SWilly Tu             std::bind_front(handleStorageCollectionGet, std::ref(app)));
10436d52334SEd Tanous }
10536d52334SEd Tanous 
10636d52334SEd Tanous inline void afterChassisDriveCollectionSubtree(
10736d52334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1087a1dbc48SGeorge Liu     const boost::system::error_code& ec,
10936d52334SEd Tanous     const dbus::utility::MapperGetSubTreePathsResponse& driveList)
11036d52334SEd Tanous {
111a25aeccfSNikhil Potade     if (ec)
112a25aeccfSNikhil Potade     {
11362598e31SEd Tanous         BMCWEB_LOG_ERROR("Drive mapper call error");
114a25aeccfSNikhil Potade         messages::internalError(asyncResp->res);
115a25aeccfSNikhil Potade         return;
116a25aeccfSNikhil Potade     }
1172ad9c2f6SJames Feist 
118a85afbe1SWilly Tu     nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"];
119a85afbe1SWilly Tu     driveArray = nlohmann::json::array();
120a85afbe1SWilly Tu     auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
121a85afbe1SWilly Tu     count = 0;
1222ad9c2f6SJames Feist 
123a85afbe1SWilly Tu     for (const std::string& drive : driveList)
124a25aeccfSNikhil Potade     {
125a85afbe1SWilly Tu         sdbusplus::message::object_path object(drive);
126a85afbe1SWilly Tu         if (object.filename().empty())
127a25aeccfSNikhil Potade         {
12862598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to find filename in {}", drive);
129a85afbe1SWilly Tu             return;
130a25aeccfSNikhil Potade         }
131a85afbe1SWilly Tu 
132a85afbe1SWilly Tu         nlohmann::json::object_t driveJson;
133ef4c65b7SEd Tanous         driveJson["@odata.id"] = boost::urls::format(
134253f11b8SEd Tanous             "/redfish/v1/Systems/{}/Storage/1/Drives/{}",
135253f11b8SEd Tanous             BMCWEB_REDFISH_SYSTEM_URI_NAME, object.filename());
136b2ba3072SPatrick Williams         driveArray.emplace_back(std::move(driveJson));
137a25aeccfSNikhil Potade     }
138a25aeccfSNikhil Potade 
139a85afbe1SWilly Tu     count = driveArray.size();
14036d52334SEd Tanous }
1417ac13cc9SGunnar Mills inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
14236d52334SEd Tanous {
14336d52334SEd Tanous     const std::array<std::string_view, 1> interfaces = {
14436d52334SEd Tanous         "xyz.openbmc_project.Inventory.Item.Drive"};
14536d52334SEd Tanous     dbus::utility::getSubTreePaths(
14636d52334SEd Tanous         "/xyz/openbmc_project/inventory", 0, interfaces,
1477ac13cc9SGunnar Mills         std::bind_front(afterChassisDriveCollectionSubtree, asyncResp));
148a85afbe1SWilly Tu }
149e284a7c1SJames Feist 
1505e577bc1SWilly Tu inline void afterSystemsStorageGetSubtree(
1515e577bc1SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1525e577bc1SWilly Tu     const std::string& storageId, const boost::system::error_code& ec,
1535e577bc1SWilly Tu     const dbus::utility::MapperGetSubTreeResponse& subtree)
154a85afbe1SWilly Tu {
1555e577bc1SWilly Tu     if (ec)
156a85afbe1SWilly Tu     {
15762598e31SEd Tanous         BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
1585e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
1595e577bc1SWilly Tu                                    storageId);
160a85afbe1SWilly Tu         return;
161a85afbe1SWilly Tu     }
1623544d2a7SEd Tanous     auto storage = std::ranges::find_if(
1633544d2a7SEd Tanous         subtree,
1645e577bc1SWilly Tu         [&storageId](const std::pair<std::string,
1655e577bc1SWilly Tu                                      dbus::utility::MapperServiceMap>& object) {
1665e577bc1SWilly Tu             return sdbusplus::message::object_path(object.first).filename() ==
1675e577bc1SWilly Tu                    storageId;
1685e577bc1SWilly Tu         });
1695e577bc1SWilly Tu     if (storage == subtree.end())
1705e577bc1SWilly Tu     {
1715e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
1725e577bc1SWilly Tu                                    storageId);
1735e577bc1SWilly Tu         return;
1745e577bc1SWilly Tu     }
1755e577bc1SWilly Tu 
17661b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
177a85afbe1SWilly Tu     asyncResp->res.jsonValue["@odata.id"] =
178253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
179253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
180a85afbe1SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage";
1815e577bc1SWilly Tu     asyncResp->res.jsonValue["Id"] = storageId;
182539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
183a85afbe1SWilly Tu 
1847ac13cc9SGunnar Mills     getDrives(asyncResp);
185253f11b8SEd Tanous     asyncResp->res.jsonValue["Controllers"]["@odata.id"] =
186253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}/Controllers",
187253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
1885e577bc1SWilly Tu }
1895e577bc1SWilly Tu 
190bd79bce8SPatrick Williams inline void handleSystemsStorageGet(
191bd79bce8SPatrick Williams     App& app, const crow::Request& req,
1925e577bc1SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
193bd79bce8SPatrick Williams     const std::string& systemName, const std::string& storageId)
1945e577bc1SWilly Tu {
1955e577bc1SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1965e577bc1SWilly Tu     {
1975e577bc1SWilly Tu         return;
1985e577bc1SWilly Tu     }
19925b54dbaSEd Tanous     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2007f3e84a1SEd Tanous     {
2017f3e84a1SEd Tanous         // Option currently returns no systems.  TBD
2027f3e84a1SEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2037f3e84a1SEd Tanous                                    systemName);
2047f3e84a1SEd Tanous         return;
2057f3e84a1SEd Tanous     }
2065e577bc1SWilly Tu 
2075e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
2085e577bc1SWilly Tu         "xyz.openbmc_project.Inventory.Item.Storage"};
2095e577bc1SWilly Tu     dbus::utility::getSubTree(
2105e577bc1SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
2115e577bc1SWilly Tu         std::bind_front(afterSystemsStorageGetSubtree, asyncResp, storageId));
2125e577bc1SWilly Tu }
2135e577bc1SWilly Tu 
2145e577bc1SWilly Tu inline void afterSubtree(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2155e577bc1SWilly Tu                          const std::string& storageId,
2165e577bc1SWilly Tu                          const boost::system::error_code& ec,
2175e577bc1SWilly Tu                          const dbus::utility::MapperGetSubTreeResponse& subtree)
2185e577bc1SWilly Tu {
2195e577bc1SWilly Tu     if (ec)
2205e577bc1SWilly Tu     {
22162598e31SEd Tanous         BMCWEB_LOG_DEBUG("requestRoutesStorage DBUS response error");
2225e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
2235e577bc1SWilly Tu                                    storageId);
2245e577bc1SWilly Tu         return;
2255e577bc1SWilly Tu     }
2263544d2a7SEd Tanous     auto storage = std::ranges::find_if(
2273544d2a7SEd Tanous         subtree,
2285e577bc1SWilly Tu         [&storageId](const std::pair<std::string,
2295e577bc1SWilly Tu                                      dbus::utility::MapperServiceMap>& object) {
2305e577bc1SWilly Tu             return sdbusplus::message::object_path(object.first).filename() ==
2315e577bc1SWilly Tu                    storageId;
2325e577bc1SWilly Tu         });
2335e577bc1SWilly Tu     if (storage == subtree.end())
2345e577bc1SWilly Tu     {
2355e577bc1SWilly Tu         messages::resourceNotFound(asyncResp->res, "#Storage.v1_13_0.Storage",
2365e577bc1SWilly Tu                                    storageId);
2375e577bc1SWilly Tu         return;
2385e577bc1SWilly Tu     }
2395e577bc1SWilly Tu 
2405e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage";
2415e577bc1SWilly Tu     asyncResp->res.jsonValue["@odata.id"] =
2425e577bc1SWilly Tu         boost::urls::format("/redfish/v1/Storage/{}", storageId);
2435e577bc1SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage";
2445e577bc1SWilly Tu     asyncResp->res.jsonValue["Id"] = storageId;
245539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
2465e577bc1SWilly Tu 
2475e577bc1SWilly Tu     // Storage subsystem to Storage link.
2485e577bc1SWilly Tu     nlohmann::json::array_t storageServices;
2495e577bc1SWilly Tu     nlohmann::json::object_t storageService;
2505e577bc1SWilly Tu     storageService["@odata.id"] =
251253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/{}",
252253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, storageId);
2535e577bc1SWilly Tu     storageServices.emplace_back(storageService);
2545e577bc1SWilly Tu     asyncResp->res.jsonValue["Links"]["StorageServices"] =
2555e577bc1SWilly Tu         std::move(storageServices);
2565e577bc1SWilly Tu     asyncResp->res.jsonValue["Links"]["StorageServices@odata.count"] = 1;
2575e577bc1SWilly Tu }
2585e577bc1SWilly Tu 
2595e577bc1SWilly Tu inline void
2605e577bc1SWilly Tu     handleStorageGet(App& app, const crow::Request& req,
2615e577bc1SWilly Tu                      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2625e577bc1SWilly Tu                      const std::string& storageId)
2635e577bc1SWilly Tu {
2645e577bc1SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2655e577bc1SWilly Tu     {
26662598e31SEd Tanous         BMCWEB_LOG_DEBUG("requestRoutesStorage setUpRedfishRoute failed");
2675e577bc1SWilly Tu         return;
2685e577bc1SWilly Tu     }
2695e577bc1SWilly Tu 
2705e577bc1SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
2715e577bc1SWilly Tu         "xyz.openbmc_project.Inventory.Item.Storage"};
2725e577bc1SWilly Tu     dbus::utility::getSubTree(
2735e577bc1SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
2745e577bc1SWilly Tu         std::bind_front(afterSubtree, asyncResp, storageId));
27536d52334SEd Tanous }
27636d52334SEd Tanous 
27736d52334SEd Tanous inline void requestRoutesStorage(App& app)
27836d52334SEd Tanous {
2797f3e84a1SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/<str>/")
28036d52334SEd Tanous         .privileges(redfish::privileges::getStorage)
28136d52334SEd Tanous         .methods(boost::beast::http::verb::get)(
28236d52334SEd Tanous             std::bind_front(handleSystemsStorageGet, std::ref(app)));
2835e577bc1SWilly Tu 
2845e577bc1SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Storage/<str>/")
2855e577bc1SWilly Tu         .privileges(redfish::privileges::getStorage)
2865e577bc1SWilly Tu         .methods(boost::beast::http::verb::get)(
2875e577bc1SWilly Tu             std::bind_front(handleStorageGet, std::ref(app)));
2887e860f15SJohn Edward Broadbent }
2897e860f15SJohn Edward Broadbent 
29003913171SWilly Tu inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
29103913171SWilly Tu                           const std::string& connectionName,
29203913171SWilly Tu                           const std::string& path)
29303913171SWilly Tu {
294*deae6a78SEd Tanous     dbus::utility::getAllProperties(
295*deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Decorator.Asset",
2965e7e2dc5SEd Tanous         [asyncResp](const boost::system::error_code& ec,
297168e20c1SEd Tanous                     const std::vector<
298168e20c1SEd Tanous                         std::pair<std::string, dbus::utility::DbusVariantType>>&
29903913171SWilly Tu                         propertiesList) {
30003913171SWilly Tu             if (ec)
30103913171SWilly Tu             {
30203913171SWilly Tu                 // this interface isn't necessary
30303913171SWilly Tu                 return;
30403913171SWilly Tu             }
305d1bde9e5SKrzysztof Grobelny 
306d1bde9e5SKrzysztof Grobelny             const std::string* partNumber = nullptr;
307d1bde9e5SKrzysztof Grobelny             const std::string* serialNumber = nullptr;
308d1bde9e5SKrzysztof Grobelny             const std::string* manufacturer = nullptr;
309d1bde9e5SKrzysztof Grobelny             const std::string* model = nullptr;
310d1bde9e5SKrzysztof Grobelny 
311d1bde9e5SKrzysztof Grobelny             const bool success = sdbusplus::unpackPropertiesNoThrow(
312d1bde9e5SKrzysztof Grobelny                 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
313d1bde9e5SKrzysztof Grobelny                 partNumber, "SerialNumber", serialNumber, "Manufacturer",
314d1bde9e5SKrzysztof Grobelny                 manufacturer, "Model", model);
315d1bde9e5SKrzysztof Grobelny 
316d1bde9e5SKrzysztof Grobelny             if (!success)
31703913171SWilly Tu             {
31803913171SWilly Tu                 messages::internalError(asyncResp->res);
31903913171SWilly Tu                 return;
32003913171SWilly Tu             }
321d1bde9e5SKrzysztof Grobelny 
322d1bde9e5SKrzysztof Grobelny             if (partNumber != nullptr)
323d1bde9e5SKrzysztof Grobelny             {
324d1bde9e5SKrzysztof Grobelny                 asyncResp->res.jsonValue["PartNumber"] = *partNumber;
32503913171SWilly Tu             }
326d1bde9e5SKrzysztof Grobelny 
327d1bde9e5SKrzysztof Grobelny             if (serialNumber != nullptr)
328d1bde9e5SKrzysztof Grobelny             {
329d1bde9e5SKrzysztof Grobelny                 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
33003913171SWilly Tu             }
331d1bde9e5SKrzysztof Grobelny 
332d1bde9e5SKrzysztof Grobelny             if (manufacturer != nullptr)
333d1bde9e5SKrzysztof Grobelny             {
334d1bde9e5SKrzysztof Grobelny                 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
335d1bde9e5SKrzysztof Grobelny             }
336d1bde9e5SKrzysztof Grobelny 
337d1bde9e5SKrzysztof Grobelny             if (model != nullptr)
338d1bde9e5SKrzysztof Grobelny             {
339d1bde9e5SKrzysztof Grobelny                 asyncResp->res.jsonValue["Model"] = *model;
340d1bde9e5SKrzysztof Grobelny             }
341d1bde9e5SKrzysztof Grobelny         });
34203913171SWilly Tu }
34303913171SWilly Tu 
34403913171SWilly Tu inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
34503913171SWilly Tu                             const std::string& connectionName,
34603913171SWilly Tu                             const std::string& path)
34703913171SWilly Tu {
348*deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
349*deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present",
350bd79bce8SPatrick Williams         [asyncResp,
351bd79bce8SPatrick Williams          path](const boost::system::error_code& ec, const bool isPresent) {
35203913171SWilly Tu             // this interface isn't necessary, only check it if
35303913171SWilly Tu             // we get a good return
35403913171SWilly Tu             if (ec)
35503913171SWilly Tu             {
35603913171SWilly Tu                 return;
35703913171SWilly Tu             }
35803913171SWilly Tu 
359cef57e85SWilly Tu             if (!isPresent)
36003913171SWilly Tu             {
361539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
362539d8c6bSEd Tanous                     resource::State::Absent;
36303913171SWilly Tu             }
3641e1e598dSJonathan Doman         });
36503913171SWilly Tu }
36603913171SWilly Tu 
36703913171SWilly Tu inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
36803913171SWilly Tu                           const std::string& connectionName,
36903913171SWilly Tu                           const std::string& path)
37003913171SWilly Tu {
371*deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
372*deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.State.Drive", "Rebuilding",
3735e7e2dc5SEd Tanous         [asyncResp](const boost::system::error_code& ec, const bool updating) {
37403913171SWilly Tu             // this interface isn't necessary, only check it
37503913171SWilly Tu             // if we get a good return
37603913171SWilly Tu             if (ec)
37703913171SWilly Tu             {
37803913171SWilly Tu                 return;
37903913171SWilly Tu             }
38003913171SWilly Tu 
38103913171SWilly Tu             // updating and disabled in the backend shouldn't be
38203913171SWilly Tu             // able to be set at the same time, so we don't need
38303913171SWilly Tu             // to check for the race condition of these two
38403913171SWilly Tu             // calls
3851e1e598dSJonathan Doman             if (updating)
38603913171SWilly Tu             {
387539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
388539d8c6bSEd Tanous                     resource::State::Updating;
38903913171SWilly Tu             }
3901e1e598dSJonathan Doman         });
39103913171SWilly Tu }
39203913171SWilly Tu 
393dde9bc12SGeorge Liu inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
39419b8e9a0SWilly Tu {
39519b8e9a0SWilly Tu     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
39619b8e9a0SWilly Tu     {
397dde9bc12SGeorge Liu         return drive::MediaType::HDD;
39819b8e9a0SWilly Tu     }
39919b8e9a0SWilly Tu     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
40019b8e9a0SWilly Tu     {
401dde9bc12SGeorge Liu         return drive::MediaType::SSD;
40219b8e9a0SWilly Tu     }
403dde9bc12SGeorge Liu     if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
404dde9bc12SGeorge Liu     {
40519b8e9a0SWilly Tu         return std::nullopt;
40619b8e9a0SWilly Tu     }
40719b8e9a0SWilly Tu 
408dde9bc12SGeorge Liu     return drive::MediaType::Invalid;
409dde9bc12SGeorge Liu }
410dde9bc12SGeorge Liu 
411dde9bc12SGeorge Liu inline std::optional<protocol::Protocol>
412dde9bc12SGeorge Liu     convertDriveProtocol(std::string_view proto)
41319b8e9a0SWilly Tu {
41419b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
41519b8e9a0SWilly Tu     {
416dde9bc12SGeorge Liu         return protocol::Protocol::SAS;
41719b8e9a0SWilly Tu     }
41819b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
41919b8e9a0SWilly Tu     {
420dde9bc12SGeorge Liu         return protocol::Protocol::SATA;
42119b8e9a0SWilly Tu     }
42219b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
42319b8e9a0SWilly Tu     {
424dde9bc12SGeorge Liu         return protocol::Protocol::NVMe;
42519b8e9a0SWilly Tu     }
42619b8e9a0SWilly Tu     if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
42719b8e9a0SWilly Tu     {
428dde9bc12SGeorge Liu         return protocol::Protocol::FC;
429dde9bc12SGeorge Liu     }
430dde9bc12SGeorge Liu     if (proto ==
431dde9bc12SGeorge Liu         "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
432dde9bc12SGeorge Liu     {
433dde9bc12SGeorge Liu         return std::nullopt;
43419b8e9a0SWilly Tu     }
43519b8e9a0SWilly Tu 
436dde9bc12SGeorge Liu     return protocol::Protocol::Invalid;
43719b8e9a0SWilly Tu }
43819b8e9a0SWilly Tu 
439bd79bce8SPatrick Williams inline void getDriveItemProperties(
440bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
441bd79bce8SPatrick Williams     const std::string& connectionName, const std::string& path)
44219b8e9a0SWilly Tu {
443*deae6a78SEd Tanous     dbus::utility::getAllProperties(
444*deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Item.Drive",
4455e7e2dc5SEd Tanous         [asyncResp](const boost::system::error_code& ec,
44619b8e9a0SWilly Tu                     const std::vector<
44719b8e9a0SWilly Tu                         std::pair<std::string, dbus::utility::DbusVariantType>>&
44819b8e9a0SWilly Tu                         propertiesList) {
44919b8e9a0SWilly Tu             if (ec)
45019b8e9a0SWilly Tu             {
45119b8e9a0SWilly Tu                 // this interface isn't required
45219b8e9a0SWilly Tu                 return;
45319b8e9a0SWilly Tu             }
454e5029d88SJohn Edward Broadbent             const std::string* encryptionStatus = nullptr;
455e5029d88SJohn Edward Broadbent             const bool* isLocked = nullptr;
45619b8e9a0SWilly Tu             for (const std::pair<std::string, dbus::utility::DbusVariantType>&
45719b8e9a0SWilly Tu                      property : propertiesList)
45819b8e9a0SWilly Tu             {
45919b8e9a0SWilly Tu                 const std::string& propertyName = property.first;
46019b8e9a0SWilly Tu                 if (propertyName == "Type")
46119b8e9a0SWilly Tu                 {
46219b8e9a0SWilly Tu                     const std::string* value =
46319b8e9a0SWilly Tu                         std::get_if<std::string>(&property.second);
46419b8e9a0SWilly Tu                     if (value == nullptr)
46519b8e9a0SWilly Tu                     {
46619b8e9a0SWilly Tu                         // illegal property
46762598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Type");
46819b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
46919b8e9a0SWilly Tu                         return;
47019b8e9a0SWilly Tu                     }
47119b8e9a0SWilly Tu 
472dde9bc12SGeorge Liu                     std::optional<drive::MediaType> mediaType =
473dde9bc12SGeorge Liu                         convertDriveType(*value);
47419b8e9a0SWilly Tu                     if (!mediaType)
47519b8e9a0SWilly Tu                     {
47662598e31SEd Tanous                         BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}",
47762598e31SEd Tanous                                            *value);
478dde9bc12SGeorge Liu                         continue;
479dde9bc12SGeorge Liu                     }
480dde9bc12SGeorge Liu                     if (*mediaType == drive::MediaType::Invalid)
481dde9bc12SGeorge Liu                     {
48219b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
48319b8e9a0SWilly Tu                         return;
48419b8e9a0SWilly Tu                     }
48519b8e9a0SWilly Tu 
48619b8e9a0SWilly Tu                     asyncResp->res.jsonValue["MediaType"] = *mediaType;
48719b8e9a0SWilly Tu                 }
48819b8e9a0SWilly Tu                 else if (propertyName == "Capacity")
48919b8e9a0SWilly Tu                 {
49019b8e9a0SWilly Tu                     const uint64_t* capacity =
49119b8e9a0SWilly Tu                         std::get_if<uint64_t>(&property.second);
49219b8e9a0SWilly Tu                     if (capacity == nullptr)
49319b8e9a0SWilly Tu                     {
49462598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Capacity");
49519b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
49619b8e9a0SWilly Tu                         return;
49719b8e9a0SWilly Tu                     }
49819b8e9a0SWilly Tu                     if (*capacity == 0)
49919b8e9a0SWilly Tu                     {
50019b8e9a0SWilly Tu                         // drive capacity not known
50119b8e9a0SWilly Tu                         continue;
50219b8e9a0SWilly Tu                     }
50319b8e9a0SWilly Tu 
50419b8e9a0SWilly Tu                     asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
50519b8e9a0SWilly Tu                 }
50619b8e9a0SWilly Tu                 else if (propertyName == "Protocol")
50719b8e9a0SWilly Tu                 {
50819b8e9a0SWilly Tu                     const std::string* value =
50919b8e9a0SWilly Tu                         std::get_if<std::string>(&property.second);
51019b8e9a0SWilly Tu                     if (value == nullptr)
51119b8e9a0SWilly Tu                     {
51262598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Protocol");
51319b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
51419b8e9a0SWilly Tu                         return;
51519b8e9a0SWilly Tu                     }
51619b8e9a0SWilly Tu 
517dde9bc12SGeorge Liu                     std::optional<protocol::Protocol> proto =
518dde9bc12SGeorge Liu                         convertDriveProtocol(*value);
51919b8e9a0SWilly Tu                     if (!proto)
52019b8e9a0SWilly Tu                     {
521bd79bce8SPatrick Williams                         BMCWEB_LOG_WARNING(
522bd79bce8SPatrick Williams                             "Unknown DrivePrototype Interface: {}", *value);
523dde9bc12SGeorge Liu                         continue;
524dde9bc12SGeorge Liu                     }
525dde9bc12SGeorge Liu                     if (*proto == protocol::Protocol::Invalid)
526dde9bc12SGeorge Liu                     {
52719b8e9a0SWilly Tu                         messages::internalError(asyncResp->res);
52819b8e9a0SWilly Tu                         return;
52919b8e9a0SWilly Tu                     }
53019b8e9a0SWilly Tu                     asyncResp->res.jsonValue["Protocol"] = *proto;
53119b8e9a0SWilly Tu                 }
5323fe4d5ccSJohn Edward Broadbent                 else if (propertyName == "PredictedMediaLifeLeftPercent")
5333fe4d5ccSJohn Edward Broadbent                 {
5343fe4d5ccSJohn Edward Broadbent                     const uint8_t* lifeLeft =
5353fe4d5ccSJohn Edward Broadbent                         std::get_if<uint8_t>(&property.second);
5363fe4d5ccSJohn Edward Broadbent                     if (lifeLeft == nullptr)
5373fe4d5ccSJohn Edward Broadbent                     {
53862598e31SEd Tanous                         BMCWEB_LOG_ERROR(
53962598e31SEd Tanous                             "Illegal property: PredictedMediaLifeLeftPercent");
5403fe4d5ccSJohn Edward Broadbent                         messages::internalError(asyncResp->res);
5413fe4d5ccSJohn Edward Broadbent                         return;
5423fe4d5ccSJohn Edward Broadbent                     }
5433fe4d5ccSJohn Edward Broadbent                     // 255 means reading the value is not supported
5443fe4d5ccSJohn Edward Broadbent                     if (*lifeLeft != 255)
5453fe4d5ccSJohn Edward Broadbent                     {
546bd79bce8SPatrick Williams                         asyncResp->res
547bd79bce8SPatrick Williams                             .jsonValue["PredictedMediaLifeLeftPercent"] =
5483fe4d5ccSJohn Edward Broadbent                             *lifeLeft;
5493fe4d5ccSJohn Edward Broadbent                     }
5503fe4d5ccSJohn Edward Broadbent                 }
551e5029d88SJohn Edward Broadbent                 else if (propertyName == "EncryptionStatus")
552e5029d88SJohn Edward Broadbent                 {
553bd79bce8SPatrick Williams                     encryptionStatus =
554bd79bce8SPatrick Williams                         std::get_if<std::string>(&property.second);
555e5029d88SJohn Edward Broadbent                     if (encryptionStatus == nullptr)
556e5029d88SJohn Edward Broadbent                     {
55762598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus");
558e5029d88SJohn Edward Broadbent                         messages::internalError(asyncResp->res);
559e5029d88SJohn Edward Broadbent                         return;
56019b8e9a0SWilly Tu                     }
561e5029d88SJohn Edward Broadbent                 }
562e5029d88SJohn Edward Broadbent                 else if (propertyName == "Locked")
563e5029d88SJohn Edward Broadbent                 {
564e5029d88SJohn Edward Broadbent                     isLocked = std::get_if<bool>(&property.second);
565e5029d88SJohn Edward Broadbent                     if (isLocked == nullptr)
566e5029d88SJohn Edward Broadbent                     {
56762598e31SEd Tanous                         BMCWEB_LOG_ERROR("Illegal property: Locked");
568e5029d88SJohn Edward Broadbent                         messages::internalError(asyncResp->res);
569e5029d88SJohn Edward Broadbent                         return;
570e5029d88SJohn Edward Broadbent                     }
571e5029d88SJohn Edward Broadbent                 }
572e5029d88SJohn Edward Broadbent             }
573e5029d88SJohn Edward Broadbent 
574e5029d88SJohn Edward Broadbent             if (encryptionStatus == nullptr || isLocked == nullptr ||
575e5029d88SJohn Edward Broadbent                 *encryptionStatus ==
576a684c226SKonda Reddy Kachana                     "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown")
577e5029d88SJohn Edward Broadbent             {
578e5029d88SJohn Edward Broadbent                 return;
579e5029d88SJohn Edward Broadbent             }
580e5029d88SJohn Edward Broadbent             if (*encryptionStatus !=
581a684c226SKonda Reddy Kachana                 "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted")
582e5029d88SJohn Edward Broadbent             {
583e5029d88SJohn Edward Broadbent                 //"The drive is not currently encrypted."
584e5029d88SJohn Edward Broadbent                 asyncResp->res.jsonValue["EncryptionStatus"] =
585e5029d88SJohn Edward Broadbent                     drive::EncryptionStatus::Unencrypted;
586e5029d88SJohn Edward Broadbent                 return;
587e5029d88SJohn Edward Broadbent             }
588e5029d88SJohn Edward Broadbent             if (*isLocked)
589e5029d88SJohn Edward Broadbent             {
590e5029d88SJohn Edward Broadbent                 //"The drive is currently encrypted and the data is not
591e5029d88SJohn Edward Broadbent                 // accessible to the user."
592e5029d88SJohn Edward Broadbent                 asyncResp->res.jsonValue["EncryptionStatus"] =
593e5029d88SJohn Edward Broadbent                     drive::EncryptionStatus::Locked;
594e5029d88SJohn Edward Broadbent                 return;
595e5029d88SJohn Edward Broadbent             }
596e5029d88SJohn Edward Broadbent             // if not locked
597e5029d88SJohn Edward Broadbent             // "The drive is currently encrypted but the data is accessible
598e5029d88SJohn Edward Broadbent             // to the user in unencrypted form."
599e5029d88SJohn Edward Broadbent             asyncResp->res.jsonValue["EncryptionStatus"] =
600e5029d88SJohn Edward Broadbent                 drive::EncryptionStatus::Unlocked;
60119b8e9a0SWilly Tu         });
60219b8e9a0SWilly Tu }
60319b8e9a0SWilly Tu 
6044ff0f1f4SEd Tanous inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
605b53dcd9dSNan Zhou                             const std::string& connectionName,
606b53dcd9dSNan Zhou                             const std::string& path,
607e56ed6b9SJohn Edward Broadbent                             const std::vector<std::string>& interfaces)
608e56ed6b9SJohn Edward Broadbent {
609e56ed6b9SJohn Edward Broadbent     for (const std::string& interface : interfaces)
610e56ed6b9SJohn Edward Broadbent     {
611e56ed6b9SJohn Edward Broadbent         if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
612e56ed6b9SJohn Edward Broadbent         {
613e56ed6b9SJohn Edward Broadbent             getDriveAsset(asyncResp, connectionName, path);
614e56ed6b9SJohn Edward Broadbent         }
615e56ed6b9SJohn Edward Broadbent         else if (interface == "xyz.openbmc_project.Inventory.Item")
616e56ed6b9SJohn Edward Broadbent         {
617e56ed6b9SJohn Edward Broadbent             getDrivePresent(asyncResp, connectionName, path);
618e56ed6b9SJohn Edward Broadbent         }
619e56ed6b9SJohn Edward Broadbent         else if (interface == "xyz.openbmc_project.State.Drive")
620e56ed6b9SJohn Edward Broadbent         {
621e56ed6b9SJohn Edward Broadbent             getDriveState(asyncResp, connectionName, path);
622e56ed6b9SJohn Edward Broadbent         }
623e56ed6b9SJohn Edward Broadbent         else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
624e56ed6b9SJohn Edward Broadbent         {
625e56ed6b9SJohn Edward Broadbent             getDriveItemProperties(asyncResp, connectionName, path);
626e56ed6b9SJohn Edward Broadbent         }
627e56ed6b9SJohn Edward Broadbent     }
628e56ed6b9SJohn Edward Broadbent }
629e56ed6b9SJohn Edward Broadbent 
63036d52334SEd Tanous inline void afterGetSubtreeSystemsStorageDrive(
63145ca1b86SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
63236d52334SEd Tanous     const std::string& driveId, const boost::system::error_code& ec,
63336d52334SEd Tanous     const dbus::utility::MapperGetSubTreeResponse& subtree)
63445ca1b86SEd Tanous {
6357e860f15SJohn Edward Broadbent     if (ec)
6367e860f15SJohn Edward Broadbent     {
63762598e31SEd Tanous         BMCWEB_LOG_ERROR("Drive mapper call error");
6387e860f15SJohn Edward Broadbent         messages::internalError(asyncResp->res);
6397e860f15SJohn Edward Broadbent         return;
6407e860f15SJohn Edward Broadbent     }
6417e860f15SJohn Edward Broadbent 
6423544d2a7SEd Tanous     auto drive = std::ranges::find_if(
6433544d2a7SEd Tanous         subtree,
64436d52334SEd Tanous         [&driveId](const std::pair<std::string,
6458cb65f8aSNan Zhou                                    dbus::utility::MapperServiceMap>& object) {
64636d52334SEd Tanous             return sdbusplus::message::object_path(object.first).filename() ==
64736d52334SEd Tanous                    driveId;
6487e860f15SJohn Edward Broadbent         });
6497e860f15SJohn Edward Broadbent 
65003913171SWilly Tu     if (drive == subtree.end())
6517e860f15SJohn Edward Broadbent     {
652002d39b4SEd Tanous         messages::resourceNotFound(asyncResp->res, "Drive", driveId);
6537e860f15SJohn Edward Broadbent         return;
6547e860f15SJohn Edward Broadbent     }
6557e860f15SJohn Edward Broadbent 
65603913171SWilly Tu     const std::string& path = drive->first;
65736d52334SEd Tanous     const dbus::utility::MapperServiceMap& connectionNames = drive->second;
6587e860f15SJohn Edward Broadbent 
659002d39b4SEd Tanous     asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
660253f11b8SEd Tanous     asyncResp->res.jsonValue["@odata.id"] =
661253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Drives/{}",
662253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, driveId);
6637e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["Name"] = driveId;
6647e860f15SJohn Edward Broadbent     asyncResp->res.jsonValue["Id"] = driveId;
6657e860f15SJohn Edward Broadbent 
6667e860f15SJohn Edward Broadbent     if (connectionNames.size() != 1)
6677e860f15SJohn Edward Broadbent     {
66862598e31SEd Tanous         BMCWEB_LOG_ERROR("Connection size {}, not equal to 1",
66962598e31SEd Tanous                          connectionNames.size());
6707e860f15SJohn Edward Broadbent         messages::internalError(asyncResp->res);
6717e860f15SJohn Edward Broadbent         return;
6727e860f15SJohn Edward Broadbent     }
6737e860f15SJohn Edward Broadbent 
674bd79bce8SPatrick Williams     getMainChassisId(
675bd79bce8SPatrick Williams         asyncResp, [](const std::string& chassisId,
6767e860f15SJohn Edward Broadbent                       const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
677002d39b4SEd Tanous             aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
678ef4c65b7SEd Tanous                 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
6797e860f15SJohn Edward Broadbent         });
6807e860f15SJohn Edward Broadbent 
681a25aeccfSNikhil Potade     // default it to Enabled
682539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
683a25aeccfSNikhil Potade 
684e56ed6b9SJohn Edward Broadbent     addAllDriveInfo(asyncResp, connectionNames[0].first, path,
685e56ed6b9SJohn Edward Broadbent                     connectionNames[0].second);
686a25aeccfSNikhil Potade }
68792903bd4SJohn Edward Broadbent 
68836d52334SEd Tanous inline void handleSystemsStorageDriveGet(
68936d52334SEd Tanous     App& app, const crow::Request& req,
69092903bd4SJohn Edward Broadbent     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
69136d52334SEd Tanous     const std::string& systemName, const std::string& driveId)
69292903bd4SJohn Edward Broadbent {
6933ba00073SCarson Labrado     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
69492903bd4SJohn Edward Broadbent     {
69592903bd4SJohn Edward Broadbent         return;
69692903bd4SJohn Edward Broadbent     }
69725b54dbaSEd Tanous     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
6987f3e84a1SEd Tanous     {
6997f3e84a1SEd Tanous         // Option currently returns no systems.  TBD
7007f3e84a1SEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
7017f3e84a1SEd Tanous                                    systemName);
7027f3e84a1SEd Tanous         return;
7037f3e84a1SEd Tanous     }
7047f3e84a1SEd Tanous 
705253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
70636d52334SEd Tanous     {
70736d52334SEd Tanous         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
70836d52334SEd Tanous                                    systemName);
70936d52334SEd Tanous         return;
71036d52334SEd Tanous     }
71192903bd4SJohn Edward Broadbent 
71236d52334SEd Tanous     constexpr std::array<std::string_view, 1> interfaces = {
71336d52334SEd Tanous         "xyz.openbmc_project.Inventory.Item.Drive"};
714e99073f5SGeorge Liu     dbus::utility::getSubTree(
715e99073f5SGeorge Liu         "/xyz/openbmc_project/inventory", 0, interfaces,
71636d52334SEd Tanous         std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp,
71736d52334SEd Tanous                         driveId));
71836d52334SEd Tanous }
71936d52334SEd Tanous 
72036d52334SEd Tanous inline void requestRoutesDrive(App& app)
72136d52334SEd Tanous {
72236d52334SEd Tanous     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/")
72336d52334SEd Tanous         .privileges(redfish::privileges::getDrive)
72436d52334SEd Tanous         .methods(boost::beast::http::verb::get)(
72536d52334SEd Tanous             std::bind_front(handleSystemsStorageDriveGet, std::ref(app)));
72636d52334SEd Tanous }
72736d52334SEd Tanous 
72836d52334SEd Tanous inline void afterChassisDriveCollectionSubtreeGet(
72936d52334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
73036d52334SEd Tanous     const std::string& chassisId, const boost::system::error_code& ec,
73136d52334SEd Tanous     const dbus::utility::MapperGetSubTreeResponse& subtree)
73236d52334SEd Tanous {
73392903bd4SJohn Edward Broadbent     if (ec)
73492903bd4SJohn Edward Broadbent     {
73592903bd4SJohn Edward Broadbent         if (ec == boost::system::errc::host_unreachable)
73692903bd4SJohn Edward Broadbent         {
73736d52334SEd Tanous             messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
73892903bd4SJohn Edward Broadbent             return;
73992903bd4SJohn Edward Broadbent         }
74092903bd4SJohn Edward Broadbent         messages::internalError(asyncResp->res);
74192903bd4SJohn Edward Broadbent         return;
74292903bd4SJohn Edward Broadbent     }
74392903bd4SJohn Edward Broadbent 
74492903bd4SJohn Edward Broadbent     // Iterate over all retrieved ObjectPaths.
7458cb65f8aSNan Zhou     for (const auto& [path, connectionNames] : subtree)
74692903bd4SJohn Edward Broadbent     {
74792903bd4SJohn Edward Broadbent         sdbusplus::message::object_path objPath(path);
74892903bd4SJohn Edward Broadbent         if (objPath.filename() != chassisId)
74992903bd4SJohn Edward Broadbent         {
75092903bd4SJohn Edward Broadbent             continue;
75192903bd4SJohn Edward Broadbent         }
75292903bd4SJohn Edward Broadbent 
75392903bd4SJohn Edward Broadbent         if (connectionNames.empty())
75492903bd4SJohn Edward Broadbent         {
75562598e31SEd Tanous             BMCWEB_LOG_ERROR("Got 0 Connection names");
75692903bd4SJohn Edward Broadbent             continue;
75792903bd4SJohn Edward Broadbent         }
75892903bd4SJohn Edward Broadbent 
75992903bd4SJohn Edward Broadbent         asyncResp->res.jsonValue["@odata.type"] =
76092903bd4SJohn Edward Broadbent             "#DriveCollection.DriveCollection";
76192903bd4SJohn Edward Broadbent         asyncResp->res.jsonValue["@odata.id"] =
762ef4c65b7SEd Tanous             boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
76392903bd4SJohn Edward Broadbent         asyncResp->res.jsonValue["Name"] = "Drive Collection";
76492903bd4SJohn Edward Broadbent 
76592903bd4SJohn Edward Broadbent         // Association lambda
7666c3e9451SGeorge Liu         dbus::utility::getAssociationEndPoints(
7676c3e9451SGeorge Liu             path + "/drive",
76836d52334SEd Tanous             [asyncResp, chassisId](const boost::system::error_code& ec3,
7696c3e9451SGeorge Liu                                    const dbus::utility::MapperEndPoints& resp) {
77092903bd4SJohn Edward Broadbent                 if (ec3)
77192903bd4SJohn Edward Broadbent                 {
77262598e31SEd Tanous                     BMCWEB_LOG_ERROR("Error in chassis Drive association ");
77392903bd4SJohn Edward Broadbent                 }
77492903bd4SJohn Edward Broadbent                 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
77592903bd4SJohn Edward Broadbent                 // important if array is empty
77692903bd4SJohn Edward Broadbent                 members = nlohmann::json::array();
77792903bd4SJohn Edward Broadbent 
77892903bd4SJohn Edward Broadbent                 std::vector<std::string> leafNames;
77992903bd4SJohn Edward Broadbent                 for (const auto& drive : resp)
78092903bd4SJohn Edward Broadbent                 {
7818a592810SEd Tanous                     sdbusplus::message::object_path drivePath(drive);
7828a592810SEd Tanous                     leafNames.push_back(drivePath.filename());
78392903bd4SJohn Edward Broadbent                 }
78492903bd4SJohn Edward Broadbent 
7853544d2a7SEd Tanous                 std::ranges::sort(leafNames, AlphanumLess<std::string>());
78692903bd4SJohn Edward Broadbent 
78792903bd4SJohn Edward Broadbent                 for (const auto& leafName : leafNames)
78892903bd4SJohn Edward Broadbent                 {
78992903bd4SJohn Edward Broadbent                     nlohmann::json::object_t member;
790bd79bce8SPatrick Williams                     member["@odata.id"] =
791bd79bce8SPatrick Williams                         boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}",
792bd79bce8SPatrick Williams                                             chassisId, leafName);
793b2ba3072SPatrick Williams                     members.emplace_back(std::move(member));
79492903bd4SJohn Edward Broadbent                     // navigation links will be registered in next patch set
79592903bd4SJohn Edward Broadbent                 }
79692903bd4SJohn Edward Broadbent                 asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
79792903bd4SJohn Edward Broadbent             }); // end association lambda
79892903bd4SJohn Edward Broadbent 
79992903bd4SJohn Edward Broadbent     } // end Iterate over all retrieved ObjectPaths
80036d52334SEd Tanous }
80136d52334SEd Tanous /**
80236d52334SEd Tanous  * Chassis drives, this URL will show all the DriveCollection
80336d52334SEd Tanous  * information
80436d52334SEd Tanous  */
80536d52334SEd Tanous inline void chassisDriveCollectionGet(
80636d52334SEd Tanous     crow::App& app, const crow::Request& req,
80736d52334SEd Tanous     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
80836d52334SEd Tanous     const std::string& chassisId)
80936d52334SEd Tanous {
81036d52334SEd Tanous     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
81136d52334SEd Tanous     {
81236d52334SEd Tanous         return;
81336d52334SEd Tanous     }
81436d52334SEd Tanous 
81536d52334SEd Tanous     // mapper call lambda
81636d52334SEd Tanous     constexpr std::array<std::string_view, 2> interfaces = {
81736d52334SEd Tanous         "xyz.openbmc_project.Inventory.Item.Board",
81836d52334SEd Tanous         "xyz.openbmc_project.Inventory.Item.Chassis"};
81936d52334SEd Tanous     dbus::utility::getSubTree(
82036d52334SEd Tanous         "/xyz/openbmc_project/inventory", 0, interfaces,
82136d52334SEd Tanous         std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
82236d52334SEd Tanous                         chassisId));
82392903bd4SJohn Edward Broadbent }
82492903bd4SJohn Edward Broadbent 
82592903bd4SJohn Edward Broadbent inline void requestRoutesChassisDrive(App& app)
82692903bd4SJohn Edward Broadbent {
82792903bd4SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
82892903bd4SJohn Edward Broadbent         .privileges(redfish::privileges::getDriveCollection)
82992903bd4SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
83092903bd4SJohn Edward Broadbent             std::bind_front(chassisDriveCollectionGet, std::ref(app)));
83192903bd4SJohn Edward Broadbent }
83292903bd4SJohn Edward Broadbent 
833b53dcd9dSNan Zhou inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
834b53dcd9dSNan Zhou                        const std::string& chassisId,
835b53dcd9dSNan Zhou                        const std::string& driveName,
8365e7e2dc5SEd Tanous                        const boost::system::error_code& ec,
837e56ed6b9SJohn Edward Broadbent                        const dbus::utility::MapperGetSubTreeResponse& subtree)
838e56ed6b9SJohn Edward Broadbent {
839e56ed6b9SJohn Edward Broadbent     if (ec)
840e56ed6b9SJohn Edward Broadbent     {
84162598e31SEd Tanous         BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
842e56ed6b9SJohn Edward Broadbent         messages::internalError(asyncResp->res);
843e56ed6b9SJohn Edward Broadbent         return;
844e56ed6b9SJohn Edward Broadbent     }
845e56ed6b9SJohn Edward Broadbent 
846e56ed6b9SJohn Edward Broadbent     // Iterate over all retrieved ObjectPaths.
8478cb65f8aSNan Zhou     for (const auto& [path, connectionNames] : subtree)
848e56ed6b9SJohn Edward Broadbent     {
849e56ed6b9SJohn Edward Broadbent         sdbusplus::message::object_path objPath(path);
850e56ed6b9SJohn Edward Broadbent         if (objPath.filename() != driveName)
851e56ed6b9SJohn Edward Broadbent         {
852e56ed6b9SJohn Edward Broadbent             continue;
853e56ed6b9SJohn Edward Broadbent         }
854e56ed6b9SJohn Edward Broadbent 
855e56ed6b9SJohn Edward Broadbent         if (connectionNames.empty())
856e56ed6b9SJohn Edward Broadbent         {
85762598e31SEd Tanous             BMCWEB_LOG_ERROR("Got 0 Connection names");
858e56ed6b9SJohn Edward Broadbent             continue;
859e56ed6b9SJohn Edward Broadbent         }
860e56ed6b9SJohn Edward Broadbent 
861ef4c65b7SEd Tanous         asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
862ef4c65b7SEd Tanous             "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
863e56ed6b9SJohn Edward Broadbent 
864e56ed6b9SJohn Edward Broadbent         asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
865a0cb40cbSJohn Edward Broadbent         asyncResp->res.jsonValue["Name"] = driveName;
866e56ed6b9SJohn Edward Broadbent         asyncResp->res.jsonValue["Id"] = driveName;
867e56ed6b9SJohn Edward Broadbent         // default it to Enabled
868539d8c6bSEd Tanous         asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
869e56ed6b9SJohn Edward Broadbent 
870e56ed6b9SJohn Edward Broadbent         nlohmann::json::object_t linkChassisNav;
871e56ed6b9SJohn Edward Broadbent         linkChassisNav["@odata.id"] =
872ef4c65b7SEd Tanous             boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
873e56ed6b9SJohn Edward Broadbent         asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
874e56ed6b9SJohn Edward Broadbent 
875e56ed6b9SJohn Edward Broadbent         addAllDriveInfo(asyncResp, connectionNames[0].first, path,
876e56ed6b9SJohn Edward Broadbent                         connectionNames[0].second);
877e56ed6b9SJohn Edward Broadbent     }
878e56ed6b9SJohn Edward Broadbent }
879e56ed6b9SJohn Edward Broadbent 
880bd79bce8SPatrick Williams inline void matchAndFillDrive(
881bd79bce8SPatrick Williams     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
882bd79bce8SPatrick Williams     const std::string& chassisId, const std::string& driveName,
883e56ed6b9SJohn Edward Broadbent     const std::vector<std::string>& resp)
884e56ed6b9SJohn Edward Broadbent {
885e56ed6b9SJohn Edward Broadbent     for (const std::string& drivePath : resp)
886e56ed6b9SJohn Edward Broadbent     {
887e56ed6b9SJohn Edward Broadbent         sdbusplus::message::object_path path(drivePath);
888e56ed6b9SJohn Edward Broadbent         std::string leaf = path.filename();
889e56ed6b9SJohn Edward Broadbent         if (leaf != driveName)
890e56ed6b9SJohn Edward Broadbent         {
891e56ed6b9SJohn Edward Broadbent             continue;
892e56ed6b9SJohn Edward Broadbent         }
893e56ed6b9SJohn Edward Broadbent         //  mapper call drive
894e99073f5SGeorge Liu         constexpr std::array<std::string_view, 1> driveInterface = {
895e56ed6b9SJohn Edward Broadbent             "xyz.openbmc_project.Inventory.Item.Drive"};
896e99073f5SGeorge Liu         dbus::utility::getSubTree(
897e99073f5SGeorge Liu             "/xyz/openbmc_project/inventory", 0, driveInterface,
898e56ed6b9SJohn Edward Broadbent             [asyncResp, chassisId, driveName](
899e99073f5SGeorge Liu                 const boost::system::error_code& ec,
900e56ed6b9SJohn Edward Broadbent                 const dbus::utility::MapperGetSubTreeResponse& subtree) {
901e56ed6b9SJohn Edward Broadbent                 buildDrive(asyncResp, chassisId, driveName, ec, subtree);
902e99073f5SGeorge Liu             });
903e56ed6b9SJohn Edward Broadbent     }
904e56ed6b9SJohn Edward Broadbent }
905e56ed6b9SJohn Edward Broadbent 
906bd79bce8SPatrick Williams inline void handleChassisDriveGet(
907bd79bce8SPatrick Williams     crow::App& app, const crow::Request& req,
908e56ed6b9SJohn Edward Broadbent     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
909bd79bce8SPatrick Williams     const std::string& chassisId, const std::string& driveName)
910e56ed6b9SJohn Edward Broadbent {
91103810a11SMichal Orzel     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
912e56ed6b9SJohn Edward Broadbent     {
913e56ed6b9SJohn Edward Broadbent         return;
914e56ed6b9SJohn Edward Broadbent     }
915e99073f5SGeorge Liu     constexpr std::array<std::string_view, 2> interfaces = {
916e56ed6b9SJohn Edward Broadbent         "xyz.openbmc_project.Inventory.Item.Board",
917e56ed6b9SJohn Edward Broadbent         "xyz.openbmc_project.Inventory.Item.Chassis"};
918e56ed6b9SJohn Edward Broadbent 
919e56ed6b9SJohn Edward Broadbent     // mapper call chassis
920e99073f5SGeorge Liu     dbus::utility::getSubTree(
921e99073f5SGeorge Liu         "/xyz/openbmc_project/inventory", 0, interfaces,
922e56ed6b9SJohn Edward Broadbent         [asyncResp, chassisId,
923e99073f5SGeorge Liu          driveName](const boost::system::error_code& ec,
924e56ed6b9SJohn Edward Broadbent                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
925e56ed6b9SJohn Edward Broadbent             if (ec)
926e56ed6b9SJohn Edward Broadbent             {
927e56ed6b9SJohn Edward Broadbent                 messages::internalError(asyncResp->res);
928e56ed6b9SJohn Edward Broadbent                 return;
929e56ed6b9SJohn Edward Broadbent             }
930e56ed6b9SJohn Edward Broadbent 
931e56ed6b9SJohn Edward Broadbent             // Iterate over all retrieved ObjectPaths.
9328cb65f8aSNan Zhou             for (const auto& [path, connectionNames] : subtree)
933e56ed6b9SJohn Edward Broadbent             {
934e56ed6b9SJohn Edward Broadbent                 sdbusplus::message::object_path objPath(path);
935e56ed6b9SJohn Edward Broadbent                 if (objPath.filename() != chassisId)
936e56ed6b9SJohn Edward Broadbent                 {
937e56ed6b9SJohn Edward Broadbent                     continue;
938e56ed6b9SJohn Edward Broadbent                 }
939e56ed6b9SJohn Edward Broadbent 
940e56ed6b9SJohn Edward Broadbent                 if (connectionNames.empty())
941e56ed6b9SJohn Edward Broadbent                 {
94262598e31SEd Tanous                     BMCWEB_LOG_ERROR("Got 0 Connection names");
943e56ed6b9SJohn Edward Broadbent                     continue;
944e56ed6b9SJohn Edward Broadbent                 }
945e56ed6b9SJohn Edward Broadbent 
9466c3e9451SGeorge Liu                 dbus::utility::getAssociationEndPoints(
9476c3e9451SGeorge Liu                     path + "/drive",
948e56ed6b9SJohn Edward Broadbent                     [asyncResp, chassisId,
9495e7e2dc5SEd Tanous                      driveName](const boost::system::error_code& ec3,
9506c3e9451SGeorge Liu                                 const dbus::utility::MapperEndPoints& resp) {
951e56ed6b9SJohn Edward Broadbent                         if (ec3)
952e56ed6b9SJohn Edward Broadbent                         {
953e56ed6b9SJohn Edward Broadbent                             return; // no drives = no failures
954e56ed6b9SJohn Edward Broadbent                         }
955bd79bce8SPatrick Williams                         matchAndFillDrive(asyncResp, chassisId, driveName,
956bd79bce8SPatrick Williams                                           resp);
957e56ed6b9SJohn Edward Broadbent                     });
958e56ed6b9SJohn Edward Broadbent                 break;
959e56ed6b9SJohn Edward Broadbent             }
960e99073f5SGeorge Liu         });
961e56ed6b9SJohn Edward Broadbent }
962e56ed6b9SJohn Edward Broadbent 
963e56ed6b9SJohn Edward Broadbent /**
964e56ed6b9SJohn Edward Broadbent  * This URL will show the drive interface for the specific drive in the chassis
965e56ed6b9SJohn Edward Broadbent  */
966e56ed6b9SJohn Edward Broadbent inline void requestRoutesChassisDriveName(App& app)
967e56ed6b9SJohn Edward Broadbent {
968e56ed6b9SJohn Edward Broadbent     BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
969e56ed6b9SJohn Edward Broadbent         .privileges(redfish::privileges::getChassis)
970e56ed6b9SJohn Edward Broadbent         .methods(boost::beast::http::verb::get)(
971e56ed6b9SJohn Edward Broadbent             std::bind_front(handleChassisDriveGet, std::ref(app)));
972e56ed6b9SJohn Edward Broadbent }
973e56ed6b9SJohn Edward Broadbent 
97461b1eb21SWilly Tu inline void getStorageControllerAsset(
97561b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
97661b1eb21SWilly Tu     const boost::system::error_code& ec,
97761b1eb21SWilly Tu     const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
97861b1eb21SWilly Tu         propertiesList)
97961b1eb21SWilly Tu {
98061b1eb21SWilly Tu     if (ec)
98161b1eb21SWilly Tu     {
98261b1eb21SWilly Tu         // this interface isn't necessary
98362598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to get StorageControllerAsset");
98461b1eb21SWilly Tu         return;
98561b1eb21SWilly Tu     }
98661b1eb21SWilly Tu 
98761b1eb21SWilly Tu     const std::string* partNumber = nullptr;
98861b1eb21SWilly Tu     const std::string* serialNumber = nullptr;
98961b1eb21SWilly Tu     const std::string* manufacturer = nullptr;
99061b1eb21SWilly Tu     const std::string* model = nullptr;
99161b1eb21SWilly Tu     if (!sdbusplus::unpackPropertiesNoThrow(
99261b1eb21SWilly Tu             dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
99361b1eb21SWilly Tu             partNumber, "SerialNumber", serialNumber, "Manufacturer",
99461b1eb21SWilly Tu             manufacturer, "Model", model))
99561b1eb21SWilly Tu     {
99661b1eb21SWilly Tu         messages::internalError(asyncResp->res);
99761b1eb21SWilly Tu         return;
99861b1eb21SWilly Tu     }
99961b1eb21SWilly Tu 
100061b1eb21SWilly Tu     if (partNumber != nullptr)
100161b1eb21SWilly Tu     {
100261b1eb21SWilly Tu         asyncResp->res.jsonValue["PartNumber"] = *partNumber;
100361b1eb21SWilly Tu     }
100461b1eb21SWilly Tu 
100561b1eb21SWilly Tu     if (serialNumber != nullptr)
100661b1eb21SWilly Tu     {
100761b1eb21SWilly Tu         asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
100861b1eb21SWilly Tu     }
100961b1eb21SWilly Tu 
101061b1eb21SWilly Tu     if (manufacturer != nullptr)
101161b1eb21SWilly Tu     {
101261b1eb21SWilly Tu         asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
101361b1eb21SWilly Tu     }
101461b1eb21SWilly Tu 
101561b1eb21SWilly Tu     if (model != nullptr)
101661b1eb21SWilly Tu     {
101761b1eb21SWilly Tu         asyncResp->res.jsonValue["Model"] = *model;
101861b1eb21SWilly Tu     }
101961b1eb21SWilly Tu }
102061b1eb21SWilly Tu 
102161b1eb21SWilly Tu inline void populateStorageController(
102261b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
102361b1eb21SWilly Tu     const std::string& controllerId, const std::string& connectionName,
102461b1eb21SWilly Tu     const std::string& path)
102561b1eb21SWilly Tu {
102661b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.type"] =
102761b1eb21SWilly Tu         "#StorageController.v1_6_0.StorageController";
1028253f11b8SEd Tanous     asyncResp->res.jsonValue["@odata.id"] =
1029253f11b8SEd Tanous         boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1030253f11b8SEd Tanous                             BMCWEB_REDFISH_SYSTEM_URI_NAME, controllerId);
103161b1eb21SWilly Tu     asyncResp->res.jsonValue["Name"] = controllerId;
103261b1eb21SWilly Tu     asyncResp->res.jsonValue["Id"] = controllerId;
1033539d8c6bSEd Tanous     asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
103461b1eb21SWilly Tu 
1035*deae6a78SEd Tanous     dbus::utility::getProperty<bool>(
1036*deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present",
103761b1eb21SWilly Tu         [asyncResp](const boost::system::error_code& ec, bool isPresent) {
103861b1eb21SWilly Tu             // this interface isn't necessary, only check it
103961b1eb21SWilly Tu             // if we get a good return
104061b1eb21SWilly Tu             if (ec)
104161b1eb21SWilly Tu             {
104262598e31SEd Tanous                 BMCWEB_LOG_DEBUG("Failed to get Present property");
104361b1eb21SWilly Tu                 return;
104461b1eb21SWilly Tu             }
104561b1eb21SWilly Tu             if (!isPresent)
104661b1eb21SWilly Tu             {
1047539d8c6bSEd Tanous                 asyncResp->res.jsonValue["Status"]["State"] =
1048539d8c6bSEd Tanous                     resource::State::Absent;
104961b1eb21SWilly Tu             }
105061b1eb21SWilly Tu         });
105161b1eb21SWilly Tu 
1052*deae6a78SEd Tanous     dbus::utility::getAllProperties(
1053*deae6a78SEd Tanous         connectionName, path, "xyz.openbmc_project.Inventory.Decorator.Asset",
105461b1eb21SWilly Tu         [asyncResp](const boost::system::error_code& ec,
105561b1eb21SWilly Tu                     const std::vector<
105661b1eb21SWilly Tu                         std::pair<std::string, dbus::utility::DbusVariantType>>&
105761b1eb21SWilly Tu                         propertiesList) {
105861b1eb21SWilly Tu             getStorageControllerAsset(asyncResp, ec, propertiesList);
105961b1eb21SWilly Tu         });
106061b1eb21SWilly Tu }
106161b1eb21SWilly Tu 
106261b1eb21SWilly Tu inline void getStorageControllerHandler(
106361b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
106461b1eb21SWilly Tu     const std::string& controllerId, const boost::system::error_code& ec,
106561b1eb21SWilly Tu     const dbus::utility::MapperGetSubTreeResponse& subtree)
106661b1eb21SWilly Tu {
106761b1eb21SWilly Tu     if (ec || subtree.empty())
106861b1eb21SWilly Tu     {
106961b1eb21SWilly Tu         // doesn't have to be there
107062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to handle StorageController");
107161b1eb21SWilly Tu         return;
107261b1eb21SWilly Tu     }
107361b1eb21SWilly Tu 
107461b1eb21SWilly Tu     for (const auto& [path, interfaceDict] : subtree)
107561b1eb21SWilly Tu     {
107661b1eb21SWilly Tu         sdbusplus::message::object_path object(path);
107761b1eb21SWilly Tu         std::string id = object.filename();
107861b1eb21SWilly Tu         if (id.empty())
107961b1eb21SWilly Tu         {
108062598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
108161b1eb21SWilly Tu             return;
108261b1eb21SWilly Tu         }
108361b1eb21SWilly Tu         if (id != controllerId)
108461b1eb21SWilly Tu         {
108561b1eb21SWilly Tu             continue;
108661b1eb21SWilly Tu         }
108761b1eb21SWilly Tu 
108861b1eb21SWilly Tu         if (interfaceDict.size() != 1)
108961b1eb21SWilly Tu         {
109062598e31SEd Tanous             BMCWEB_LOG_ERROR("Connection size {}, greater than 1",
109162598e31SEd Tanous                              interfaceDict.size());
109261b1eb21SWilly Tu             messages::internalError(asyncResp->res);
109361b1eb21SWilly Tu             return;
109461b1eb21SWilly Tu         }
109561b1eb21SWilly Tu 
109661b1eb21SWilly Tu         const std::string& connectionName = interfaceDict.front().first;
109761b1eb21SWilly Tu         populateStorageController(asyncResp, controllerId, connectionName,
109861b1eb21SWilly Tu                                   path);
109961b1eb21SWilly Tu     }
110061b1eb21SWilly Tu }
110161b1eb21SWilly Tu 
110261b1eb21SWilly Tu inline void populateStorageControllerCollection(
110361b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
110461b1eb21SWilly Tu     const boost::system::error_code& ec,
110561b1eb21SWilly Tu     const dbus::utility::MapperGetSubTreePathsResponse& controllerList)
110661b1eb21SWilly Tu {
110761b1eb21SWilly Tu     nlohmann::json::array_t members;
110861b1eb21SWilly Tu     if (ec || controllerList.empty())
110961b1eb21SWilly Tu     {
111061b1eb21SWilly Tu         asyncResp->res.jsonValue["Members"] = std::move(members);
111161b1eb21SWilly Tu         asyncResp->res.jsonValue["Members@odata.count"] = 0;
111262598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to find any StorageController");
111361b1eb21SWilly Tu         return;
111461b1eb21SWilly Tu     }
111561b1eb21SWilly Tu 
111661b1eb21SWilly Tu     for (const std::string& path : controllerList)
111761b1eb21SWilly Tu     {
111861b1eb21SWilly Tu         std::string id = sdbusplus::message::object_path(path).filename();
111961b1eb21SWilly Tu         if (id.empty())
112061b1eb21SWilly Tu         {
112162598e31SEd Tanous             BMCWEB_LOG_ERROR("Failed to find filename in {}", path);
112261b1eb21SWilly Tu             return;
112361b1eb21SWilly Tu         }
112461b1eb21SWilly Tu         nlohmann::json::object_t member;
112561b1eb21SWilly Tu         member["@odata.id"] = boost::urls::format(
1126253f11b8SEd Tanous             "/redfish/v1/Systems/{}/Storage/1/Controllers/{}",
1127253f11b8SEd Tanous             BMCWEB_REDFISH_SYSTEM_URI_NAME, id);
112861b1eb21SWilly Tu         members.emplace_back(member);
112961b1eb21SWilly Tu     }
113061b1eb21SWilly Tu     asyncResp->res.jsonValue["Members@odata.count"] = members.size();
113161b1eb21SWilly Tu     asyncResp->res.jsonValue["Members"] = std::move(members);
113261b1eb21SWilly Tu }
113361b1eb21SWilly Tu 
113436d52334SEd Tanous inline void handleSystemsStorageControllerCollectionGet(
113561b1eb21SWilly Tu     App& app, const crow::Request& req,
113661b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
113761b1eb21SWilly Tu     const std::string& systemName)
113861b1eb21SWilly Tu {
113961b1eb21SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
114061b1eb21SWilly Tu     {
114162598e31SEd Tanous         BMCWEB_LOG_DEBUG(
114262598e31SEd Tanous             "Failed to setup Redfish Route for StorageController Collection");
114361b1eb21SWilly Tu         return;
114461b1eb21SWilly Tu     }
1145253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
114661b1eb21SWilly Tu     {
114761b1eb21SWilly Tu         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
114861b1eb21SWilly Tu                                    systemName);
114962598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
115061b1eb21SWilly Tu         return;
115161b1eb21SWilly Tu     }
115261b1eb21SWilly Tu 
115361b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.type"] =
115461b1eb21SWilly Tu         "#StorageControllerCollection.StorageControllerCollection";
115561b1eb21SWilly Tu     asyncResp->res.jsonValue["@odata.id"] =
1156253f11b8SEd Tanous         std::format("/redfish/v1/Systems/{}/Storage/1/Controllers",
1157253f11b8SEd Tanous                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
115861b1eb21SWilly Tu     asyncResp->res.jsonValue["Name"] = "Storage Controller Collection";
115961b1eb21SWilly Tu 
116061b1eb21SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
116161b1eb21SWilly Tu         "xyz.openbmc_project.Inventory.Item.StorageController"};
116261b1eb21SWilly Tu     dbus::utility::getSubTreePaths(
116361b1eb21SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
116461b1eb21SWilly Tu         [asyncResp](const boost::system::error_code& ec,
116561b1eb21SWilly Tu                     const dbus::utility::MapperGetSubTreePathsResponse&
116661b1eb21SWilly Tu                         controllerList) {
116761b1eb21SWilly Tu             populateStorageControllerCollection(asyncResp, ec, controllerList);
116861b1eb21SWilly Tu         });
116961b1eb21SWilly Tu }
117061b1eb21SWilly Tu 
117136d52334SEd Tanous inline void handleSystemsStorageControllerGet(
117261b1eb21SWilly Tu     App& app, const crow::Request& req,
117361b1eb21SWilly Tu     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
117461b1eb21SWilly Tu     const std::string& systemName, const std::string& controllerId)
117561b1eb21SWilly Tu {
117661b1eb21SWilly Tu     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
117761b1eb21SWilly Tu     {
117862598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to setup Redfish Route for StorageController");
117961b1eb21SWilly Tu         return;
118061b1eb21SWilly Tu     }
1181253f11b8SEd Tanous     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
118261b1eb21SWilly Tu     {
118361b1eb21SWilly Tu         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
118461b1eb21SWilly Tu                                    systemName);
118562598e31SEd Tanous         BMCWEB_LOG_DEBUG("Failed to find ComputerSystem of {}", systemName);
118661b1eb21SWilly Tu         return;
118761b1eb21SWilly Tu     }
118861b1eb21SWilly Tu     constexpr std::array<std::string_view, 1> interfaces = {
118961b1eb21SWilly Tu         "xyz.openbmc_project.Inventory.Item.StorageController"};
119061b1eb21SWilly Tu     dbus::utility::getSubTree(
119161b1eb21SWilly Tu         "/xyz/openbmc_project/inventory", 0, interfaces,
119261b1eb21SWilly Tu         [asyncResp,
119361b1eb21SWilly Tu          controllerId](const boost::system::error_code& ec,
119461b1eb21SWilly Tu                        const dbus::utility::MapperGetSubTreeResponse& subtree) {
119561b1eb21SWilly Tu             getStorageControllerHandler(asyncResp, controllerId, ec, subtree);
119661b1eb21SWilly Tu         });
119761b1eb21SWilly Tu }
119861b1eb21SWilly Tu 
119961b1eb21SWilly Tu inline void requestRoutesStorageControllerCollection(App& app)
120061b1eb21SWilly Tu {
120161b1eb21SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/")
120261b1eb21SWilly Tu         .privileges(redfish::privileges::getStorageControllerCollection)
120336d52334SEd Tanous         .methods(boost::beast::http::verb::get)(std::bind_front(
120436d52334SEd Tanous             handleSystemsStorageControllerCollectionGet, std::ref(app)));
120561b1eb21SWilly Tu }
120661b1eb21SWilly Tu 
120761b1eb21SWilly Tu inline void requestRoutesStorageController(App& app)
120861b1eb21SWilly Tu {
120961b1eb21SWilly Tu     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>")
121061b1eb21SWilly Tu         .privileges(redfish::privileges::getStorageController)
121161b1eb21SWilly Tu         .methods(boost::beast::http::verb::get)(
121236d52334SEd Tanous             std::bind_front(handleSystemsStorageControllerGet, std::ref(app)));
121361b1eb21SWilly Tu }
121461b1eb21SWilly Tu 
1215a25aeccfSNikhil Potade } // namespace redfish
1216