1a25aeccfSNikhil Potade /* 2a25aeccfSNikhil Potade // Copyright (c) 2019 Intel Corporation 3a25aeccfSNikhil Potade // 4a25aeccfSNikhil Potade // Licensed under the Apache License, Version 2.0 (the "License"); 5a25aeccfSNikhil Potade // you may not use this file except in compliance with the License. 6a25aeccfSNikhil Potade // You may obtain a copy of the License at 7a25aeccfSNikhil Potade // 8a25aeccfSNikhil Potade // http://www.apache.org/licenses/LICENSE-2.0 9a25aeccfSNikhil Potade // 10a25aeccfSNikhil Potade // Unless required by applicable law or agreed to in writing, software 11a25aeccfSNikhil Potade // distributed under the License is distributed on an "AS IS" BASIS, 12a25aeccfSNikhil Potade // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a25aeccfSNikhil Potade // See the License for the specific language governing permissions and 14a25aeccfSNikhil Potade // 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" 242ad9c2f6SJames Feist #include "health.hpp" 25a8e884fcSEd Tanous #include "human_sort.hpp" 26e284a7c1SJames Feist #include "openbmc_dbus_rest.hpp" 273ccb3adbSEd Tanous #include "query.hpp" 28a8e884fcSEd Tanous #include "redfish_util.hpp" 293ccb3adbSEd Tanous #include "registries/privilege_registry.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> 387a1dbc48SGeorge Liu #include <string_view> 397a1dbc48SGeorge Liu 40a25aeccfSNikhil Potade namespace redfish 41a25aeccfSNikhil Potade { 42*36d52334SEd Tanous 43*36d52334SEd Tanous inline void handleSystemsStorageCollectionGet( 44*36d52334SEd Tanous App& app, const crow::Request& req, 4522d268cbSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 46*36d52334SEd Tanous const std::string& systemName) 47*36d52334SEd Tanous { 483ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 4945ca1b86SEd Tanous { 5045ca1b86SEd Tanous return; 5145ca1b86SEd Tanous } 5222d268cbSEd Tanous if (systemName != "system") 5322d268cbSEd Tanous { 5422d268cbSEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 5522d268cbSEd Tanous systemName); 5622d268cbSEd Tanous return; 5722d268cbSEd Tanous } 5822d268cbSEd Tanous 598d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.type"] = 608d1b46d7Szhanghch05 "#StorageCollection.StorageCollection"; 618d1b46d7Szhanghch05 asyncResp->res.jsonValue["@odata.id"] = 628d1b46d7Szhanghch05 "/redfish/v1/Systems/system/Storage"; 638d1b46d7Szhanghch05 asyncResp->res.jsonValue["Name"] = "Storage Collection"; 641476687dSEd Tanous nlohmann::json::array_t members; 651476687dSEd Tanous nlohmann::json::object_t member; 661476687dSEd Tanous member["@odata.id"] = "/redfish/v1/Systems/system/Storage/1"; 671476687dSEd Tanous members.emplace_back(member); 681476687dSEd Tanous asyncResp->res.jsonValue["Members"] = std::move(members); 698d1b46d7Szhanghch05 asyncResp->res.jsonValue["Members@odata.count"] = 1; 70a25aeccfSNikhil Potade } 71a25aeccfSNikhil Potade 72*36d52334SEd Tanous inline void requestRoutesStorageCollection(App& app) 73a25aeccfSNikhil Potade { 74*36d52334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/") 75*36d52334SEd Tanous .privileges(redfish::privileges::getStorageCollection) 76*36d52334SEd Tanous .methods(boost::beast::http::verb::get)( 77*36d52334SEd Tanous std::bind_front(handleSystemsStorageCollectionGet, std::ref(app))); 78*36d52334SEd Tanous } 79*36d52334SEd Tanous 80*36d52334SEd Tanous inline void afterChassisDriveCollectionSubtree( 81*36d52334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 82*36d52334SEd Tanous const std::shared_ptr<HealthPopulate>& health, 837a1dbc48SGeorge Liu const boost::system::error_code& ec, 84*36d52334SEd Tanous const dbus::utility::MapperGetSubTreePathsResponse& driveList) 85*36d52334SEd Tanous { 86a25aeccfSNikhil Potade if (ec) 87a25aeccfSNikhil Potade { 88a25aeccfSNikhil Potade BMCWEB_LOG_ERROR << "Drive mapper call error"; 89a25aeccfSNikhil Potade messages::internalError(asyncResp->res); 90a25aeccfSNikhil Potade return; 91a25aeccfSNikhil Potade } 922ad9c2f6SJames Feist 93a85afbe1SWilly Tu nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"]; 94a85afbe1SWilly Tu driveArray = nlohmann::json::array(); 95a85afbe1SWilly Tu auto& count = asyncResp->res.jsonValue["Drives@odata.count"]; 96a85afbe1SWilly Tu count = 0; 972ad9c2f6SJames Feist 9813451e39SWilly Tu if constexpr (bmcwebEnableHealthPopulate) 9913451e39SWilly Tu { 100a85afbe1SWilly Tu health->inventory.insert(health->inventory.end(), driveList.begin(), 101a85afbe1SWilly Tu driveList.end()); 10213451e39SWilly Tu } 103a85afbe1SWilly Tu 104a85afbe1SWilly Tu for (const std::string& drive : driveList) 105a25aeccfSNikhil Potade { 106a85afbe1SWilly Tu sdbusplus::message::object_path object(drive); 107a85afbe1SWilly Tu if (object.filename().empty()) 108a25aeccfSNikhil Potade { 109a85afbe1SWilly Tu BMCWEB_LOG_ERROR << "Failed to find filename in " << drive; 110a85afbe1SWilly Tu return; 111a25aeccfSNikhil Potade } 112a85afbe1SWilly Tu 113a85afbe1SWilly Tu nlohmann::json::object_t driveJson; 114ef4c65b7SEd Tanous driveJson["@odata.id"] = boost::urls::format( 115ef4c65b7SEd Tanous "/redfish/v1/Systems/system/Storage/1/Drives/{}", 116eddfc437SWilly Tu object.filename()); 117b2ba3072SPatrick Williams driveArray.emplace_back(std::move(driveJson)); 118a25aeccfSNikhil Potade } 119a25aeccfSNikhil Potade 120a85afbe1SWilly Tu count = driveArray.size(); 121*36d52334SEd Tanous } 122*36d52334SEd Tanous inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 123*36d52334SEd Tanous const std::shared_ptr<HealthPopulate>& health) 124*36d52334SEd Tanous { 125*36d52334SEd Tanous const std::array<std::string_view, 1> interfaces = { 126*36d52334SEd Tanous "xyz.openbmc_project.Inventory.Item.Drive"}; 127*36d52334SEd Tanous dbus::utility::getSubTreePaths( 128*36d52334SEd Tanous "/xyz/openbmc_project/inventory", 0, interfaces, 129*36d52334SEd Tanous std::bind_front(afterChassisDriveCollectionSubtree, asyncResp, health)); 130a85afbe1SWilly Tu } 131e284a7c1SJames Feist 132*36d52334SEd Tanous inline void 133*36d52334SEd Tanous handleSystemsStorageGet(App& app, const crow::Request& req, 134*36d52334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 135a85afbe1SWilly Tu { 1363ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 137a85afbe1SWilly Tu { 138a85afbe1SWilly Tu return; 139a85afbe1SWilly Tu } 14061b1eb21SWilly Tu asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_13_0.Storage"; 141a85afbe1SWilly Tu asyncResp->res.jsonValue["@odata.id"] = 142a85afbe1SWilly Tu "/redfish/v1/Systems/system/Storage/1"; 143a85afbe1SWilly Tu asyncResp->res.jsonValue["Name"] = "Storage"; 144a85afbe1SWilly Tu asyncResp->res.jsonValue["Id"] = "1"; 145a85afbe1SWilly Tu asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 146a85afbe1SWilly Tu 147a85afbe1SWilly Tu auto health = std::make_shared<HealthPopulate>(asyncResp); 14813451e39SWilly Tu if constexpr (bmcwebEnableHealthPopulate) 14913451e39SWilly Tu { 150a85afbe1SWilly Tu health->populate(); 15113451e39SWilly Tu } 152a85afbe1SWilly Tu 153a85afbe1SWilly Tu getDrives(asyncResp, health); 15461b1eb21SWilly Tu asyncResp->res.jsonValue["Controllers"]["@odata.id"] = 15561b1eb21SWilly Tu "/redfish/v1/Systems/system/Storage/1/Controllers"; 156*36d52334SEd Tanous } 157*36d52334SEd Tanous 158*36d52334SEd Tanous inline void requestRoutesStorage(App& app) 159*36d52334SEd Tanous { 160*36d52334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/") 161*36d52334SEd Tanous .privileges(redfish::privileges::getStorage) 162*36d52334SEd Tanous .methods(boost::beast::http::verb::get)( 163*36d52334SEd Tanous std::bind_front(handleSystemsStorageGet, std::ref(app))); 1647e860f15SJohn Edward Broadbent } 1657e860f15SJohn Edward Broadbent 16603913171SWilly Tu inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 16703913171SWilly Tu const std::string& connectionName, 16803913171SWilly Tu const std::string& path) 16903913171SWilly Tu { 170d1bde9e5SKrzysztof Grobelny sdbusplus::asio::getAllProperties( 171d1bde9e5SKrzysztof Grobelny *crow::connections::systemBus, connectionName, path, 172d1bde9e5SKrzysztof Grobelny "xyz.openbmc_project.Inventory.Decorator.Asset", 1735e7e2dc5SEd Tanous [asyncResp](const boost::system::error_code& ec, 174168e20c1SEd Tanous const std::vector< 175168e20c1SEd Tanous std::pair<std::string, dbus::utility::DbusVariantType>>& 17603913171SWilly Tu propertiesList) { 17703913171SWilly Tu if (ec) 17803913171SWilly Tu { 17903913171SWilly Tu // this interface isn't necessary 18003913171SWilly Tu return; 18103913171SWilly Tu } 182d1bde9e5SKrzysztof Grobelny 183d1bde9e5SKrzysztof Grobelny const std::string* partNumber = nullptr; 184d1bde9e5SKrzysztof Grobelny const std::string* serialNumber = nullptr; 185d1bde9e5SKrzysztof Grobelny const std::string* manufacturer = nullptr; 186d1bde9e5SKrzysztof Grobelny const std::string* model = nullptr; 187d1bde9e5SKrzysztof Grobelny 188d1bde9e5SKrzysztof Grobelny const bool success = sdbusplus::unpackPropertiesNoThrow( 189d1bde9e5SKrzysztof Grobelny dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", 190d1bde9e5SKrzysztof Grobelny partNumber, "SerialNumber", serialNumber, "Manufacturer", 191d1bde9e5SKrzysztof Grobelny manufacturer, "Model", model); 192d1bde9e5SKrzysztof Grobelny 193d1bde9e5SKrzysztof Grobelny if (!success) 19403913171SWilly Tu { 19503913171SWilly Tu messages::internalError(asyncResp->res); 19603913171SWilly Tu return; 19703913171SWilly Tu } 198d1bde9e5SKrzysztof Grobelny 199d1bde9e5SKrzysztof Grobelny if (partNumber != nullptr) 200d1bde9e5SKrzysztof Grobelny { 201d1bde9e5SKrzysztof Grobelny asyncResp->res.jsonValue["PartNumber"] = *partNumber; 20203913171SWilly Tu } 203d1bde9e5SKrzysztof Grobelny 204d1bde9e5SKrzysztof Grobelny if (serialNumber != nullptr) 205d1bde9e5SKrzysztof Grobelny { 206d1bde9e5SKrzysztof Grobelny asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 20703913171SWilly Tu } 208d1bde9e5SKrzysztof Grobelny 209d1bde9e5SKrzysztof Grobelny if (manufacturer != nullptr) 210d1bde9e5SKrzysztof Grobelny { 211d1bde9e5SKrzysztof Grobelny asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 212d1bde9e5SKrzysztof Grobelny } 213d1bde9e5SKrzysztof Grobelny 214d1bde9e5SKrzysztof Grobelny if (model != nullptr) 215d1bde9e5SKrzysztof Grobelny { 216d1bde9e5SKrzysztof Grobelny asyncResp->res.jsonValue["Model"] = *model; 217d1bde9e5SKrzysztof Grobelny } 218d1bde9e5SKrzysztof Grobelny }); 21903913171SWilly Tu } 22003913171SWilly Tu 22103913171SWilly Tu inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 22203913171SWilly Tu const std::string& connectionName, 22303913171SWilly Tu const std::string& path) 22403913171SWilly Tu { 2251e1e598dSJonathan Doman sdbusplus::asio::getProperty<bool>( 2261e1e598dSJonathan Doman *crow::connections::systemBus, connectionName, path, 2271e1e598dSJonathan Doman "xyz.openbmc_project.Inventory.Item", "Present", 2285e7e2dc5SEd Tanous [asyncResp, path](const boost::system::error_code& ec, 229cef57e85SWilly Tu const bool isPresent) { 23003913171SWilly Tu // this interface isn't necessary, only check it if 23103913171SWilly Tu // we get a good return 23203913171SWilly Tu if (ec) 23303913171SWilly Tu { 23403913171SWilly Tu return; 23503913171SWilly Tu } 23603913171SWilly Tu 237cef57e85SWilly Tu if (!isPresent) 23803913171SWilly Tu { 239cef57e85SWilly Tu asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 24003913171SWilly Tu } 2411e1e598dSJonathan Doman }); 24203913171SWilly Tu } 24303913171SWilly Tu 24403913171SWilly Tu inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 24503913171SWilly Tu const std::string& connectionName, 24603913171SWilly Tu const std::string& path) 24703913171SWilly Tu { 2481e1e598dSJonathan Doman sdbusplus::asio::getProperty<bool>( 2491e1e598dSJonathan Doman *crow::connections::systemBus, connectionName, path, 2501e1e598dSJonathan Doman "xyz.openbmc_project.State.Drive", "Rebuilding", 2515e7e2dc5SEd Tanous [asyncResp](const boost::system::error_code& ec, const bool updating) { 25203913171SWilly Tu // this interface isn't necessary, only check it 25303913171SWilly Tu // if we get a good return 25403913171SWilly Tu if (ec) 25503913171SWilly Tu { 25603913171SWilly Tu return; 25703913171SWilly Tu } 25803913171SWilly Tu 25903913171SWilly Tu // updating and disabled in the backend shouldn't be 26003913171SWilly Tu // able to be set at the same time, so we don't need 26103913171SWilly Tu // to check for the race condition of these two 26203913171SWilly Tu // calls 2631e1e598dSJonathan Doman if (updating) 26403913171SWilly Tu { 26503913171SWilly Tu asyncResp->res.jsonValue["Status"]["State"] = "Updating"; 26603913171SWilly Tu } 2671e1e598dSJonathan Doman }); 26803913171SWilly Tu } 26903913171SWilly Tu 270dde9bc12SGeorge Liu inline std::optional<drive::MediaType> convertDriveType(std::string_view type) 27119b8e9a0SWilly Tu { 27219b8e9a0SWilly Tu if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD") 27319b8e9a0SWilly Tu { 274dde9bc12SGeorge Liu return drive::MediaType::HDD; 27519b8e9a0SWilly Tu } 27619b8e9a0SWilly Tu if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD") 27719b8e9a0SWilly Tu { 278dde9bc12SGeorge Liu return drive::MediaType::SSD; 27919b8e9a0SWilly Tu } 280dde9bc12SGeorge Liu if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown") 281dde9bc12SGeorge Liu { 28219b8e9a0SWilly Tu return std::nullopt; 28319b8e9a0SWilly Tu } 28419b8e9a0SWilly Tu 285dde9bc12SGeorge Liu return drive::MediaType::Invalid; 286dde9bc12SGeorge Liu } 287dde9bc12SGeorge Liu 288dde9bc12SGeorge Liu inline std::optional<protocol::Protocol> 289dde9bc12SGeorge Liu convertDriveProtocol(std::string_view proto) 29019b8e9a0SWilly Tu { 29119b8e9a0SWilly Tu if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS") 29219b8e9a0SWilly Tu { 293dde9bc12SGeorge Liu return protocol::Protocol::SAS; 29419b8e9a0SWilly Tu } 29519b8e9a0SWilly Tu if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA") 29619b8e9a0SWilly Tu { 297dde9bc12SGeorge Liu return protocol::Protocol::SATA; 29819b8e9a0SWilly Tu } 29919b8e9a0SWilly Tu if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe") 30019b8e9a0SWilly Tu { 301dde9bc12SGeorge Liu return protocol::Protocol::NVMe; 30219b8e9a0SWilly Tu } 30319b8e9a0SWilly Tu if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC") 30419b8e9a0SWilly Tu { 305dde9bc12SGeorge Liu return protocol::Protocol::FC; 306dde9bc12SGeorge Liu } 307dde9bc12SGeorge Liu if (proto == 308dde9bc12SGeorge Liu "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown") 309dde9bc12SGeorge Liu { 310dde9bc12SGeorge Liu return std::nullopt; 31119b8e9a0SWilly Tu } 31219b8e9a0SWilly Tu 313dde9bc12SGeorge Liu return protocol::Protocol::Invalid; 31419b8e9a0SWilly Tu } 31519b8e9a0SWilly Tu 31619b8e9a0SWilly Tu inline void 31719b8e9a0SWilly Tu getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 31819b8e9a0SWilly Tu const std::string& connectionName, 31919b8e9a0SWilly Tu const std::string& path) 32019b8e9a0SWilly Tu { 32119b8e9a0SWilly Tu sdbusplus::asio::getAllProperties( 32219b8e9a0SWilly Tu *crow::connections::systemBus, connectionName, path, 32319b8e9a0SWilly Tu "xyz.openbmc_project.Inventory.Item.Drive", 3245e7e2dc5SEd Tanous [asyncResp](const boost::system::error_code& ec, 32519b8e9a0SWilly Tu const std::vector< 32619b8e9a0SWilly Tu std::pair<std::string, dbus::utility::DbusVariantType>>& 32719b8e9a0SWilly Tu propertiesList) { 32819b8e9a0SWilly Tu if (ec) 32919b8e9a0SWilly Tu { 33019b8e9a0SWilly Tu // this interface isn't required 33119b8e9a0SWilly Tu return; 33219b8e9a0SWilly Tu } 333e5029d88SJohn Edward Broadbent const std::string* encryptionStatus = nullptr; 334e5029d88SJohn Edward Broadbent const bool* isLocked = nullptr; 33519b8e9a0SWilly Tu for (const std::pair<std::string, dbus::utility::DbusVariantType>& 33619b8e9a0SWilly Tu property : propertiesList) 33719b8e9a0SWilly Tu { 33819b8e9a0SWilly Tu const std::string& propertyName = property.first; 33919b8e9a0SWilly Tu if (propertyName == "Type") 34019b8e9a0SWilly Tu { 34119b8e9a0SWilly Tu const std::string* value = 34219b8e9a0SWilly Tu std::get_if<std::string>(&property.second); 34319b8e9a0SWilly Tu if (value == nullptr) 34419b8e9a0SWilly Tu { 34519b8e9a0SWilly Tu // illegal property 34619b8e9a0SWilly Tu BMCWEB_LOG_ERROR << "Illegal property: Type"; 34719b8e9a0SWilly Tu messages::internalError(asyncResp->res); 34819b8e9a0SWilly Tu return; 34919b8e9a0SWilly Tu } 35019b8e9a0SWilly Tu 351dde9bc12SGeorge Liu std::optional<drive::MediaType> mediaType = 352dde9bc12SGeorge Liu convertDriveType(*value); 35319b8e9a0SWilly Tu if (!mediaType) 35419b8e9a0SWilly Tu { 355dde9bc12SGeorge Liu BMCWEB_LOG_WARNING << "UnknownDriveType Interface: " 35619b8e9a0SWilly Tu << *value; 357dde9bc12SGeorge Liu continue; 358dde9bc12SGeorge Liu } 359dde9bc12SGeorge Liu if (*mediaType == drive::MediaType::Invalid) 360dde9bc12SGeorge Liu { 36119b8e9a0SWilly Tu messages::internalError(asyncResp->res); 36219b8e9a0SWilly Tu return; 36319b8e9a0SWilly Tu } 36419b8e9a0SWilly Tu 36519b8e9a0SWilly Tu asyncResp->res.jsonValue["MediaType"] = *mediaType; 36619b8e9a0SWilly Tu } 36719b8e9a0SWilly Tu else if (propertyName == "Capacity") 36819b8e9a0SWilly Tu { 36919b8e9a0SWilly Tu const uint64_t* capacity = 37019b8e9a0SWilly Tu std::get_if<uint64_t>(&property.second); 37119b8e9a0SWilly Tu if (capacity == nullptr) 37219b8e9a0SWilly Tu { 37319b8e9a0SWilly Tu BMCWEB_LOG_ERROR << "Illegal property: Capacity"; 37419b8e9a0SWilly Tu messages::internalError(asyncResp->res); 37519b8e9a0SWilly Tu return; 37619b8e9a0SWilly Tu } 37719b8e9a0SWilly Tu if (*capacity == 0) 37819b8e9a0SWilly Tu { 37919b8e9a0SWilly Tu // drive capacity not known 38019b8e9a0SWilly Tu continue; 38119b8e9a0SWilly Tu } 38219b8e9a0SWilly Tu 38319b8e9a0SWilly Tu asyncResp->res.jsonValue["CapacityBytes"] = *capacity; 38419b8e9a0SWilly Tu } 38519b8e9a0SWilly Tu else if (propertyName == "Protocol") 38619b8e9a0SWilly Tu { 38719b8e9a0SWilly Tu const std::string* value = 38819b8e9a0SWilly Tu std::get_if<std::string>(&property.second); 38919b8e9a0SWilly Tu if (value == nullptr) 39019b8e9a0SWilly Tu { 39119b8e9a0SWilly Tu BMCWEB_LOG_ERROR << "Illegal property: Protocol"; 39219b8e9a0SWilly Tu messages::internalError(asyncResp->res); 39319b8e9a0SWilly Tu return; 39419b8e9a0SWilly Tu } 39519b8e9a0SWilly Tu 396dde9bc12SGeorge Liu std::optional<protocol::Protocol> proto = 397dde9bc12SGeorge Liu convertDriveProtocol(*value); 39819b8e9a0SWilly Tu if (!proto) 39919b8e9a0SWilly Tu { 400dde9bc12SGeorge Liu BMCWEB_LOG_WARNING << "Unknown DrivePrototype Interface: " 40119b8e9a0SWilly Tu << *value; 402dde9bc12SGeorge Liu continue; 403dde9bc12SGeorge Liu } 404dde9bc12SGeorge Liu if (*proto == protocol::Protocol::Invalid) 405dde9bc12SGeorge Liu { 40619b8e9a0SWilly Tu messages::internalError(asyncResp->res); 40719b8e9a0SWilly Tu return; 40819b8e9a0SWilly Tu } 40919b8e9a0SWilly Tu asyncResp->res.jsonValue["Protocol"] = *proto; 41019b8e9a0SWilly Tu } 4113fe4d5ccSJohn Edward Broadbent else if (propertyName == "PredictedMediaLifeLeftPercent") 4123fe4d5ccSJohn Edward Broadbent { 4133fe4d5ccSJohn Edward Broadbent const uint8_t* lifeLeft = 4143fe4d5ccSJohn Edward Broadbent std::get_if<uint8_t>(&property.second); 4153fe4d5ccSJohn Edward Broadbent if (lifeLeft == nullptr) 4163fe4d5ccSJohn Edward Broadbent { 4173fe4d5ccSJohn Edward Broadbent BMCWEB_LOG_ERROR 4183fe4d5ccSJohn Edward Broadbent << "Illegal property: PredictedMediaLifeLeftPercent"; 4193fe4d5ccSJohn Edward Broadbent messages::internalError(asyncResp->res); 4203fe4d5ccSJohn Edward Broadbent return; 4213fe4d5ccSJohn Edward Broadbent } 4223fe4d5ccSJohn Edward Broadbent // 255 means reading the value is not supported 4233fe4d5ccSJohn Edward Broadbent if (*lifeLeft != 255) 4243fe4d5ccSJohn Edward Broadbent { 4253fe4d5ccSJohn Edward Broadbent asyncResp->res.jsonValue["PredictedMediaLifeLeftPercent"] = 4263fe4d5ccSJohn Edward Broadbent *lifeLeft; 4273fe4d5ccSJohn Edward Broadbent } 4283fe4d5ccSJohn Edward Broadbent } 429e5029d88SJohn Edward Broadbent else if (propertyName == "EncryptionStatus") 430e5029d88SJohn Edward Broadbent { 431e5029d88SJohn Edward Broadbent encryptionStatus = std::get_if<std::string>(&property.second); 432e5029d88SJohn Edward Broadbent if (encryptionStatus == nullptr) 433e5029d88SJohn Edward Broadbent { 434e5029d88SJohn Edward Broadbent BMCWEB_LOG_ERROR << "Illegal property: EncryptionStatus"; 435e5029d88SJohn Edward Broadbent messages::internalError(asyncResp->res); 436e5029d88SJohn Edward Broadbent return; 43719b8e9a0SWilly Tu } 438e5029d88SJohn Edward Broadbent } 439e5029d88SJohn Edward Broadbent else if (propertyName == "Locked") 440e5029d88SJohn Edward Broadbent { 441e5029d88SJohn Edward Broadbent isLocked = std::get_if<bool>(&property.second); 442e5029d88SJohn Edward Broadbent if (isLocked == nullptr) 443e5029d88SJohn Edward Broadbent { 444e5029d88SJohn Edward Broadbent BMCWEB_LOG_ERROR << "Illegal property: Locked"; 445e5029d88SJohn Edward Broadbent messages::internalError(asyncResp->res); 446e5029d88SJohn Edward Broadbent return; 447e5029d88SJohn Edward Broadbent } 448e5029d88SJohn Edward Broadbent } 449e5029d88SJohn Edward Broadbent } 450e5029d88SJohn Edward Broadbent 451e5029d88SJohn Edward Broadbent if (encryptionStatus == nullptr || isLocked == nullptr || 452e5029d88SJohn Edward Broadbent *encryptionStatus == 453e5029d88SJohn Edward Broadbent "xyz.openbmc_project.Drive.DriveEncryptionState.Unknown") 454e5029d88SJohn Edward Broadbent { 455e5029d88SJohn Edward Broadbent return; 456e5029d88SJohn Edward Broadbent } 457e5029d88SJohn Edward Broadbent if (*encryptionStatus != 458e5029d88SJohn Edward Broadbent "xyz.openbmc_project.Drive.DriveEncryptionState.Encrypted") 459e5029d88SJohn Edward Broadbent { 460e5029d88SJohn Edward Broadbent //"The drive is not currently encrypted." 461e5029d88SJohn Edward Broadbent asyncResp->res.jsonValue["EncryptionStatus"] = 462e5029d88SJohn Edward Broadbent drive::EncryptionStatus::Unencrypted; 463e5029d88SJohn Edward Broadbent return; 464e5029d88SJohn Edward Broadbent } 465e5029d88SJohn Edward Broadbent if (*isLocked) 466e5029d88SJohn Edward Broadbent { 467e5029d88SJohn Edward Broadbent //"The drive is currently encrypted and the data is not 468e5029d88SJohn Edward Broadbent // accessible to the user." 469e5029d88SJohn Edward Broadbent asyncResp->res.jsonValue["EncryptionStatus"] = 470e5029d88SJohn Edward Broadbent drive::EncryptionStatus::Locked; 471e5029d88SJohn Edward Broadbent return; 472e5029d88SJohn Edward Broadbent } 473e5029d88SJohn Edward Broadbent // if not locked 474e5029d88SJohn Edward Broadbent // "The drive is currently encrypted but the data is accessible 475e5029d88SJohn Edward Broadbent // to the user in unencrypted form." 476e5029d88SJohn Edward Broadbent asyncResp->res.jsonValue["EncryptionStatus"] = 477e5029d88SJohn Edward Broadbent drive::EncryptionStatus::Unlocked; 47819b8e9a0SWilly Tu }); 47919b8e9a0SWilly Tu } 48019b8e9a0SWilly Tu 481b53dcd9dSNan Zhou static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 482b53dcd9dSNan Zhou const std::string& connectionName, 483b53dcd9dSNan Zhou const std::string& path, 484e56ed6b9SJohn Edward Broadbent const std::vector<std::string>& interfaces) 485e56ed6b9SJohn Edward Broadbent { 486e56ed6b9SJohn Edward Broadbent for (const std::string& interface : interfaces) 487e56ed6b9SJohn Edward Broadbent { 488e56ed6b9SJohn Edward Broadbent if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 489e56ed6b9SJohn Edward Broadbent { 490e56ed6b9SJohn Edward Broadbent getDriveAsset(asyncResp, connectionName, path); 491e56ed6b9SJohn Edward Broadbent } 492e56ed6b9SJohn Edward Broadbent else if (interface == "xyz.openbmc_project.Inventory.Item") 493e56ed6b9SJohn Edward Broadbent { 494e56ed6b9SJohn Edward Broadbent getDrivePresent(asyncResp, connectionName, path); 495e56ed6b9SJohn Edward Broadbent } 496e56ed6b9SJohn Edward Broadbent else if (interface == "xyz.openbmc_project.State.Drive") 497e56ed6b9SJohn Edward Broadbent { 498e56ed6b9SJohn Edward Broadbent getDriveState(asyncResp, connectionName, path); 499e56ed6b9SJohn Edward Broadbent } 500e56ed6b9SJohn Edward Broadbent else if (interface == "xyz.openbmc_project.Inventory.Item.Drive") 501e56ed6b9SJohn Edward Broadbent { 502e56ed6b9SJohn Edward Broadbent getDriveItemProperties(asyncResp, connectionName, path); 503e56ed6b9SJohn Edward Broadbent } 504e56ed6b9SJohn Edward Broadbent } 505e56ed6b9SJohn Edward Broadbent } 506e56ed6b9SJohn Edward Broadbent 507*36d52334SEd Tanous inline void afterGetSubtreeSystemsStorageDrive( 50845ca1b86SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 509*36d52334SEd Tanous const std::string& driveId, const boost::system::error_code& ec, 510*36d52334SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) 51145ca1b86SEd Tanous { 5127e860f15SJohn Edward Broadbent if (ec) 5137e860f15SJohn Edward Broadbent { 5147e860f15SJohn Edward Broadbent BMCWEB_LOG_ERROR << "Drive mapper call error"; 5157e860f15SJohn Edward Broadbent messages::internalError(asyncResp->res); 5167e860f15SJohn Edward Broadbent return; 5177e860f15SJohn Edward Broadbent } 5187e860f15SJohn Edward Broadbent 51903913171SWilly Tu auto drive = std::find_if( 5207e860f15SJohn Edward Broadbent subtree.begin(), subtree.end(), 521*36d52334SEd Tanous [&driveId](const std::pair<std::string, 5228cb65f8aSNan Zhou dbus::utility::MapperServiceMap>& object) { 523*36d52334SEd Tanous return sdbusplus::message::object_path(object.first).filename() == 524*36d52334SEd Tanous driveId; 5257e860f15SJohn Edward Broadbent }); 5267e860f15SJohn Edward Broadbent 52703913171SWilly Tu if (drive == subtree.end()) 5287e860f15SJohn Edward Broadbent { 529002d39b4SEd Tanous messages::resourceNotFound(asyncResp->res, "Drive", driveId); 5307e860f15SJohn Edward Broadbent return; 5317e860f15SJohn Edward Broadbent } 5327e860f15SJohn Edward Broadbent 53303913171SWilly Tu const std::string& path = drive->first; 534*36d52334SEd Tanous const dbus::utility::MapperServiceMap& connectionNames = drive->second; 5357e860f15SJohn Edward Broadbent 536002d39b4SEd Tanous asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 537ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 538ef4c65b7SEd Tanous "/redfish/v1/Systems/system/Storage/1/Drives/{}", driveId); 5397e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["Name"] = driveId; 5407e860f15SJohn Edward Broadbent asyncResp->res.jsonValue["Id"] = driveId; 5417e860f15SJohn Edward Broadbent 5427e860f15SJohn Edward Broadbent if (connectionNames.size() != 1) 5437e860f15SJohn Edward Broadbent { 544002d39b4SEd Tanous BMCWEB_LOG_ERROR << "Connection size " << connectionNames.size() 54503913171SWilly Tu << ", not equal to 1"; 5467e860f15SJohn Edward Broadbent messages::internalError(asyncResp->res); 5477e860f15SJohn Edward Broadbent return; 5487e860f15SJohn Edward Broadbent } 5497e860f15SJohn Edward Broadbent 550*36d52334SEd Tanous getMainChassisId(asyncResp, 551ef4c65b7SEd Tanous [](const std::string& chassisId, 5527e860f15SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 553002d39b4SEd Tanous aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] = 554ef4c65b7SEd Tanous boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 5557e860f15SJohn Edward Broadbent }); 5567e860f15SJohn Edward Broadbent 557a25aeccfSNikhil Potade // default it to Enabled 558a25aeccfSNikhil Potade asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 559a25aeccfSNikhil Potade 56013451e39SWilly Tu if constexpr (bmcwebEnableHealthPopulate) 56113451e39SWilly Tu { 5622ad9c2f6SJames Feist auto health = std::make_shared<HealthPopulate>(asyncResp); 563e284a7c1SJames Feist health->inventory.emplace_back(path); 5642ad9c2f6SJames Feist health->populate(); 56513451e39SWilly Tu } 5662ad9c2f6SJames Feist 567e56ed6b9SJohn Edward Broadbent addAllDriveInfo(asyncResp, connectionNames[0].first, path, 568e56ed6b9SJohn Edward Broadbent connectionNames[0].second); 569a25aeccfSNikhil Potade } 57092903bd4SJohn Edward Broadbent 571*36d52334SEd Tanous inline void handleSystemsStorageDriveGet( 572*36d52334SEd Tanous App& app, const crow::Request& req, 57392903bd4SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 574*36d52334SEd Tanous const std::string& systemName, const std::string& driveId) 57592903bd4SJohn Edward Broadbent { 5763ba00073SCarson Labrado if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 57792903bd4SJohn Edward Broadbent { 57892903bd4SJohn Edward Broadbent return; 57992903bd4SJohn Edward Broadbent } 580*36d52334SEd Tanous if (systemName != "system") 581*36d52334SEd Tanous { 582*36d52334SEd Tanous messages::resourceNotFound(asyncResp->res, "ComputerSystem", 583*36d52334SEd Tanous systemName); 584*36d52334SEd Tanous return; 585*36d52334SEd Tanous } 58692903bd4SJohn Edward Broadbent 587*36d52334SEd Tanous constexpr std::array<std::string_view, 1> interfaces = { 588*36d52334SEd Tanous "xyz.openbmc_project.Inventory.Item.Drive"}; 589e99073f5SGeorge Liu dbus::utility::getSubTree( 590e99073f5SGeorge Liu "/xyz/openbmc_project/inventory", 0, interfaces, 591*36d52334SEd Tanous std::bind_front(afterGetSubtreeSystemsStorageDrive, asyncResp, 592*36d52334SEd Tanous driveId)); 593*36d52334SEd Tanous } 594*36d52334SEd Tanous 595*36d52334SEd Tanous inline void requestRoutesDrive(App& app) 596*36d52334SEd Tanous { 597*36d52334SEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Drives/<str>/") 598*36d52334SEd Tanous .privileges(redfish::privileges::getDrive) 599*36d52334SEd Tanous .methods(boost::beast::http::verb::get)( 600*36d52334SEd Tanous std::bind_front(handleSystemsStorageDriveGet, std::ref(app))); 601*36d52334SEd Tanous } 602*36d52334SEd Tanous 603*36d52334SEd Tanous inline void afterChassisDriveCollectionSubtreeGet( 604*36d52334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 605*36d52334SEd Tanous const std::string& chassisId, const boost::system::error_code& ec, 606*36d52334SEd Tanous const dbus::utility::MapperGetSubTreeResponse& subtree) 607*36d52334SEd Tanous { 60892903bd4SJohn Edward Broadbent if (ec) 60992903bd4SJohn Edward Broadbent { 61092903bd4SJohn Edward Broadbent if (ec == boost::system::errc::host_unreachable) 61192903bd4SJohn Edward Broadbent { 612*36d52334SEd Tanous messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 61392903bd4SJohn Edward Broadbent return; 61492903bd4SJohn Edward Broadbent } 61592903bd4SJohn Edward Broadbent messages::internalError(asyncResp->res); 61692903bd4SJohn Edward Broadbent return; 61792903bd4SJohn Edward Broadbent } 61892903bd4SJohn Edward Broadbent 61992903bd4SJohn Edward Broadbent // Iterate over all retrieved ObjectPaths. 6208cb65f8aSNan Zhou for (const auto& [path, connectionNames] : subtree) 62192903bd4SJohn Edward Broadbent { 62292903bd4SJohn Edward Broadbent sdbusplus::message::object_path objPath(path); 62392903bd4SJohn Edward Broadbent if (objPath.filename() != chassisId) 62492903bd4SJohn Edward Broadbent { 62592903bd4SJohn Edward Broadbent continue; 62692903bd4SJohn Edward Broadbent } 62792903bd4SJohn Edward Broadbent 62892903bd4SJohn Edward Broadbent if (connectionNames.empty()) 62992903bd4SJohn Edward Broadbent { 63092903bd4SJohn Edward Broadbent BMCWEB_LOG_ERROR << "Got 0 Connection names"; 63192903bd4SJohn Edward Broadbent continue; 63292903bd4SJohn Edward Broadbent } 63392903bd4SJohn Edward Broadbent 63492903bd4SJohn Edward Broadbent asyncResp->res.jsonValue["@odata.type"] = 63592903bd4SJohn Edward Broadbent "#DriveCollection.DriveCollection"; 63692903bd4SJohn Edward Broadbent asyncResp->res.jsonValue["@odata.id"] = 637ef4c65b7SEd Tanous boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId); 63892903bd4SJohn Edward Broadbent asyncResp->res.jsonValue["Name"] = "Drive Collection"; 63992903bd4SJohn Edward Broadbent 64092903bd4SJohn Edward Broadbent // Association lambda 6416c3e9451SGeorge Liu dbus::utility::getAssociationEndPoints( 6426c3e9451SGeorge Liu path + "/drive", 643*36d52334SEd Tanous [asyncResp, chassisId](const boost::system::error_code& ec3, 6446c3e9451SGeorge Liu const dbus::utility::MapperEndPoints& resp) { 64592903bd4SJohn Edward Broadbent if (ec3) 64692903bd4SJohn Edward Broadbent { 64792903bd4SJohn Edward Broadbent BMCWEB_LOG_ERROR << "Error in chassis Drive association "; 64892903bd4SJohn Edward Broadbent } 64992903bd4SJohn Edward Broadbent nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 65092903bd4SJohn Edward Broadbent // important if array is empty 65192903bd4SJohn Edward Broadbent members = nlohmann::json::array(); 65292903bd4SJohn Edward Broadbent 65392903bd4SJohn Edward Broadbent std::vector<std::string> leafNames; 65492903bd4SJohn Edward Broadbent for (const auto& drive : resp) 65592903bd4SJohn Edward Broadbent { 6568a592810SEd Tanous sdbusplus::message::object_path drivePath(drive); 6578a592810SEd Tanous leafNames.push_back(drivePath.filename()); 65892903bd4SJohn Edward Broadbent } 65992903bd4SJohn Edward Broadbent 66092903bd4SJohn Edward Broadbent std::sort(leafNames.begin(), leafNames.end(), 66192903bd4SJohn Edward Broadbent AlphanumLess<std::string>()); 66292903bd4SJohn Edward Broadbent 66392903bd4SJohn Edward Broadbent for (const auto& leafName : leafNames) 66492903bd4SJohn Edward Broadbent { 66592903bd4SJohn Edward Broadbent nlohmann::json::object_t member; 666*36d52334SEd Tanous member["@odata.id"] = boost::urls::format( 667*36d52334SEd Tanous "/redfish/v1/Chassis/{}/Drives/{}", chassisId, leafName); 668b2ba3072SPatrick Williams members.emplace_back(std::move(member)); 66992903bd4SJohn Edward Broadbent // navigation links will be registered in next patch set 67092903bd4SJohn Edward Broadbent } 67192903bd4SJohn Edward Broadbent asyncResp->res.jsonValue["Members@odata.count"] = resp.size(); 67292903bd4SJohn Edward Broadbent }); // end association lambda 67392903bd4SJohn Edward Broadbent 67492903bd4SJohn Edward Broadbent } // end Iterate over all retrieved ObjectPaths 675*36d52334SEd Tanous } 676*36d52334SEd Tanous /** 677*36d52334SEd Tanous * Chassis drives, this URL will show all the DriveCollection 678*36d52334SEd Tanous * information 679*36d52334SEd Tanous */ 680*36d52334SEd Tanous inline void chassisDriveCollectionGet( 681*36d52334SEd Tanous crow::App& app, const crow::Request& req, 682*36d52334SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 683*36d52334SEd Tanous const std::string& chassisId) 684*36d52334SEd Tanous { 685*36d52334SEd Tanous if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 686*36d52334SEd Tanous { 687*36d52334SEd Tanous return; 688*36d52334SEd Tanous } 689*36d52334SEd Tanous 690*36d52334SEd Tanous // mapper call lambda 691*36d52334SEd Tanous constexpr std::array<std::string_view, 2> interfaces = { 692*36d52334SEd Tanous "xyz.openbmc_project.Inventory.Item.Board", 693*36d52334SEd Tanous "xyz.openbmc_project.Inventory.Item.Chassis"}; 694*36d52334SEd Tanous dbus::utility::getSubTree( 695*36d52334SEd Tanous "/xyz/openbmc_project/inventory", 0, interfaces, 696*36d52334SEd Tanous std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp, 697*36d52334SEd Tanous chassisId)); 69892903bd4SJohn Edward Broadbent } 69992903bd4SJohn Edward Broadbent 70092903bd4SJohn Edward Broadbent inline void requestRoutesChassisDrive(App& app) 70192903bd4SJohn Edward Broadbent { 70292903bd4SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/") 70392903bd4SJohn Edward Broadbent .privileges(redfish::privileges::getDriveCollection) 70492903bd4SJohn Edward Broadbent .methods(boost::beast::http::verb::get)( 70592903bd4SJohn Edward Broadbent std::bind_front(chassisDriveCollectionGet, std::ref(app))); 70692903bd4SJohn Edward Broadbent } 70792903bd4SJohn Edward Broadbent 708b53dcd9dSNan Zhou inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 709b53dcd9dSNan Zhou const std::string& chassisId, 710b53dcd9dSNan Zhou const std::string& driveName, 7115e7e2dc5SEd Tanous const boost::system::error_code& ec, 712e56ed6b9SJohn Edward Broadbent const dbus::utility::MapperGetSubTreeResponse& subtree) 713e56ed6b9SJohn Edward Broadbent { 714e56ed6b9SJohn Edward Broadbent if (ec) 715e56ed6b9SJohn Edward Broadbent { 716e56ed6b9SJohn Edward Broadbent BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 717e56ed6b9SJohn Edward Broadbent messages::internalError(asyncResp->res); 718e56ed6b9SJohn Edward Broadbent return; 719e56ed6b9SJohn Edward Broadbent } 720e56ed6b9SJohn Edward Broadbent 721e56ed6b9SJohn Edward Broadbent // Iterate over all retrieved ObjectPaths. 7228cb65f8aSNan Zhou for (const auto& [path, connectionNames] : subtree) 723e56ed6b9SJohn Edward Broadbent { 724e56ed6b9SJohn Edward Broadbent sdbusplus::message::object_path objPath(path); 725e56ed6b9SJohn Edward Broadbent if (objPath.filename() != driveName) 726e56ed6b9SJohn Edward Broadbent { 727e56ed6b9SJohn Edward Broadbent continue; 728e56ed6b9SJohn Edward Broadbent } 729e56ed6b9SJohn Edward Broadbent 730e56ed6b9SJohn Edward Broadbent if (connectionNames.empty()) 731e56ed6b9SJohn Edward Broadbent { 732e56ed6b9SJohn Edward Broadbent BMCWEB_LOG_ERROR << "Got 0 Connection names"; 733e56ed6b9SJohn Edward Broadbent continue; 734e56ed6b9SJohn Edward Broadbent } 735e56ed6b9SJohn Edward Broadbent 736ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 737ef4c65b7SEd Tanous "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName); 738e56ed6b9SJohn Edward Broadbent 739e56ed6b9SJohn Edward Broadbent asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 740a0cb40cbSJohn Edward Broadbent asyncResp->res.jsonValue["Name"] = driveName; 741e56ed6b9SJohn Edward Broadbent asyncResp->res.jsonValue["Id"] = driveName; 742e56ed6b9SJohn Edward Broadbent // default it to Enabled 743e56ed6b9SJohn Edward Broadbent asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 744e56ed6b9SJohn Edward Broadbent 745e56ed6b9SJohn Edward Broadbent nlohmann::json::object_t linkChassisNav; 746e56ed6b9SJohn Edward Broadbent linkChassisNav["@odata.id"] = 747ef4c65b7SEd Tanous boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 748e56ed6b9SJohn Edward Broadbent asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav; 749e56ed6b9SJohn Edward Broadbent 750e56ed6b9SJohn Edward Broadbent addAllDriveInfo(asyncResp, connectionNames[0].first, path, 751e56ed6b9SJohn Edward Broadbent connectionNames[0].second); 752e56ed6b9SJohn Edward Broadbent } 753e56ed6b9SJohn Edward Broadbent } 754e56ed6b9SJohn Edward Broadbent 755b53dcd9dSNan Zhou inline void 756b53dcd9dSNan Zhou matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 757e56ed6b9SJohn Edward Broadbent const std::string& chassisId, 758e56ed6b9SJohn Edward Broadbent const std::string& driveName, 759e56ed6b9SJohn Edward Broadbent const std::vector<std::string>& resp) 760e56ed6b9SJohn Edward Broadbent { 761e56ed6b9SJohn Edward Broadbent for (const std::string& drivePath : resp) 762e56ed6b9SJohn Edward Broadbent { 763e56ed6b9SJohn Edward Broadbent sdbusplus::message::object_path path(drivePath); 764e56ed6b9SJohn Edward Broadbent std::string leaf = path.filename(); 765e56ed6b9SJohn Edward Broadbent if (leaf != driveName) 766e56ed6b9SJohn Edward Broadbent { 767e56ed6b9SJohn Edward Broadbent continue; 768e56ed6b9SJohn Edward Broadbent } 769e56ed6b9SJohn Edward Broadbent // mapper call drive 770e99073f5SGeorge Liu constexpr std::array<std::string_view, 1> driveInterface = { 771e56ed6b9SJohn Edward Broadbent "xyz.openbmc_project.Inventory.Item.Drive"}; 772e99073f5SGeorge Liu dbus::utility::getSubTree( 773e99073f5SGeorge Liu "/xyz/openbmc_project/inventory", 0, driveInterface, 774e56ed6b9SJohn Edward Broadbent [asyncResp, chassisId, driveName]( 775e99073f5SGeorge Liu const boost::system::error_code& ec, 776e56ed6b9SJohn Edward Broadbent const dbus::utility::MapperGetSubTreeResponse& subtree) { 777e56ed6b9SJohn Edward Broadbent buildDrive(asyncResp, chassisId, driveName, ec, subtree); 778e99073f5SGeorge Liu }); 779e56ed6b9SJohn Edward Broadbent } 780e56ed6b9SJohn Edward Broadbent } 781e56ed6b9SJohn Edward Broadbent 782b53dcd9dSNan Zhou inline void 783b53dcd9dSNan Zhou handleChassisDriveGet(crow::App& app, const crow::Request& req, 784e56ed6b9SJohn Edward Broadbent const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 785e56ed6b9SJohn Edward Broadbent const std::string& chassisId, 786e56ed6b9SJohn Edward Broadbent const std::string& driveName) 787e56ed6b9SJohn Edward Broadbent { 78803810a11SMichal Orzel if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 789e56ed6b9SJohn Edward Broadbent { 790e56ed6b9SJohn Edward Broadbent return; 791e56ed6b9SJohn Edward Broadbent } 792e99073f5SGeorge Liu constexpr std::array<std::string_view, 2> interfaces = { 793e56ed6b9SJohn Edward Broadbent "xyz.openbmc_project.Inventory.Item.Board", 794e56ed6b9SJohn Edward Broadbent "xyz.openbmc_project.Inventory.Item.Chassis"}; 795e56ed6b9SJohn Edward Broadbent 796e56ed6b9SJohn Edward Broadbent // mapper call chassis 797e99073f5SGeorge Liu dbus::utility::getSubTree( 798e99073f5SGeorge Liu "/xyz/openbmc_project/inventory", 0, interfaces, 799e56ed6b9SJohn Edward Broadbent [asyncResp, chassisId, 800e99073f5SGeorge Liu driveName](const boost::system::error_code& ec, 801e56ed6b9SJohn Edward Broadbent const dbus::utility::MapperGetSubTreeResponse& subtree) { 802e56ed6b9SJohn Edward Broadbent if (ec) 803e56ed6b9SJohn Edward Broadbent { 804e56ed6b9SJohn Edward Broadbent messages::internalError(asyncResp->res); 805e56ed6b9SJohn Edward Broadbent return; 806e56ed6b9SJohn Edward Broadbent } 807e56ed6b9SJohn Edward Broadbent 808e56ed6b9SJohn Edward Broadbent // Iterate over all retrieved ObjectPaths. 8098cb65f8aSNan Zhou for (const auto& [path, connectionNames] : subtree) 810e56ed6b9SJohn Edward Broadbent { 811e56ed6b9SJohn Edward Broadbent sdbusplus::message::object_path objPath(path); 812e56ed6b9SJohn Edward Broadbent if (objPath.filename() != chassisId) 813e56ed6b9SJohn Edward Broadbent { 814e56ed6b9SJohn Edward Broadbent continue; 815e56ed6b9SJohn Edward Broadbent } 816e56ed6b9SJohn Edward Broadbent 817e56ed6b9SJohn Edward Broadbent if (connectionNames.empty()) 818e56ed6b9SJohn Edward Broadbent { 819e56ed6b9SJohn Edward Broadbent BMCWEB_LOG_ERROR << "Got 0 Connection names"; 820e56ed6b9SJohn Edward Broadbent continue; 821e56ed6b9SJohn Edward Broadbent } 822e56ed6b9SJohn Edward Broadbent 8236c3e9451SGeorge Liu dbus::utility::getAssociationEndPoints( 8246c3e9451SGeorge Liu path + "/drive", 825e56ed6b9SJohn Edward Broadbent [asyncResp, chassisId, 8265e7e2dc5SEd Tanous driveName](const boost::system::error_code& ec3, 8276c3e9451SGeorge Liu const dbus::utility::MapperEndPoints& resp) { 828e56ed6b9SJohn Edward Broadbent if (ec3) 829e56ed6b9SJohn Edward Broadbent { 830e56ed6b9SJohn Edward Broadbent return; // no drives = no failures 831e56ed6b9SJohn Edward Broadbent } 832e56ed6b9SJohn Edward Broadbent matchAndFillDrive(asyncResp, chassisId, driveName, resp); 833e56ed6b9SJohn Edward Broadbent }); 834e56ed6b9SJohn Edward Broadbent break; 835e56ed6b9SJohn Edward Broadbent } 836e99073f5SGeorge Liu }); 837e56ed6b9SJohn Edward Broadbent } 838e56ed6b9SJohn Edward Broadbent 839e56ed6b9SJohn Edward Broadbent /** 840e56ed6b9SJohn Edward Broadbent * This URL will show the drive interface for the specific drive in the chassis 841e56ed6b9SJohn Edward Broadbent */ 842e56ed6b9SJohn Edward Broadbent inline void requestRoutesChassisDriveName(App& app) 843e56ed6b9SJohn Edward Broadbent { 844e56ed6b9SJohn Edward Broadbent BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/") 845e56ed6b9SJohn Edward Broadbent .privileges(redfish::privileges::getChassis) 846e56ed6b9SJohn Edward Broadbent .methods(boost::beast::http::verb::get)( 847e56ed6b9SJohn Edward Broadbent std::bind_front(handleChassisDriveGet, std::ref(app))); 848e56ed6b9SJohn Edward Broadbent } 849e56ed6b9SJohn Edward Broadbent 85061b1eb21SWilly Tu inline void getStorageControllerAsset( 85161b1eb21SWilly Tu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 85261b1eb21SWilly Tu const boost::system::error_code& ec, 85361b1eb21SWilly Tu const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>& 85461b1eb21SWilly Tu propertiesList) 85561b1eb21SWilly Tu { 85661b1eb21SWilly Tu if (ec) 85761b1eb21SWilly Tu { 85861b1eb21SWilly Tu // this interface isn't necessary 85961b1eb21SWilly Tu BMCWEB_LOG_DEBUG << "Failed to get StorageControllerAsset"; 86061b1eb21SWilly Tu return; 86161b1eb21SWilly Tu } 86261b1eb21SWilly Tu 86361b1eb21SWilly Tu const std::string* partNumber = nullptr; 86461b1eb21SWilly Tu const std::string* serialNumber = nullptr; 86561b1eb21SWilly Tu const std::string* manufacturer = nullptr; 86661b1eb21SWilly Tu const std::string* model = nullptr; 86761b1eb21SWilly Tu if (!sdbusplus::unpackPropertiesNoThrow( 86861b1eb21SWilly Tu dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", 86961b1eb21SWilly Tu partNumber, "SerialNumber", serialNumber, "Manufacturer", 87061b1eb21SWilly Tu manufacturer, "Model", model)) 87161b1eb21SWilly Tu { 87261b1eb21SWilly Tu messages::internalError(asyncResp->res); 87361b1eb21SWilly Tu return; 87461b1eb21SWilly Tu } 87561b1eb21SWilly Tu 87661b1eb21SWilly Tu if (partNumber != nullptr) 87761b1eb21SWilly Tu { 87861b1eb21SWilly Tu asyncResp->res.jsonValue["PartNumber"] = *partNumber; 87961b1eb21SWilly Tu } 88061b1eb21SWilly Tu 88161b1eb21SWilly Tu if (serialNumber != nullptr) 88261b1eb21SWilly Tu { 88361b1eb21SWilly Tu asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 88461b1eb21SWilly Tu } 88561b1eb21SWilly Tu 88661b1eb21SWilly Tu if (manufacturer != nullptr) 88761b1eb21SWilly Tu { 88861b1eb21SWilly Tu asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 88961b1eb21SWilly Tu } 89061b1eb21SWilly Tu 89161b1eb21SWilly Tu if (model != nullptr) 89261b1eb21SWilly Tu { 89361b1eb21SWilly Tu asyncResp->res.jsonValue["Model"] = *model; 89461b1eb21SWilly Tu } 89561b1eb21SWilly Tu } 89661b1eb21SWilly Tu 89761b1eb21SWilly Tu inline void populateStorageController( 89861b1eb21SWilly Tu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 89961b1eb21SWilly Tu const std::string& controllerId, const std::string& connectionName, 90061b1eb21SWilly Tu const std::string& path) 90161b1eb21SWilly Tu { 90261b1eb21SWilly Tu asyncResp->res.jsonValue["@odata.type"] = 90361b1eb21SWilly Tu "#StorageController.v1_6_0.StorageController"; 90461b1eb21SWilly Tu asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 90561b1eb21SWilly Tu "/redfish/v1/Systems/system/Storage/1/Controllers/{}", controllerId); 90661b1eb21SWilly Tu asyncResp->res.jsonValue["Name"] = controllerId; 90761b1eb21SWilly Tu asyncResp->res.jsonValue["Id"] = controllerId; 90861b1eb21SWilly Tu asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 90961b1eb21SWilly Tu 91061b1eb21SWilly Tu sdbusplus::asio::getProperty<bool>( 91161b1eb21SWilly Tu *crow::connections::systemBus, connectionName, path, 91261b1eb21SWilly Tu "xyz.openbmc_project.Inventory.Item", "Present", 91361b1eb21SWilly Tu [asyncResp](const boost::system::error_code& ec, bool isPresent) { 91461b1eb21SWilly Tu // this interface isn't necessary, only check it 91561b1eb21SWilly Tu // if we get a good return 91661b1eb21SWilly Tu if (ec) 91761b1eb21SWilly Tu { 91861b1eb21SWilly Tu BMCWEB_LOG_DEBUG << "Failed to get Present property"; 91961b1eb21SWilly Tu return; 92061b1eb21SWilly Tu } 92161b1eb21SWilly Tu if (!isPresent) 92261b1eb21SWilly Tu { 92361b1eb21SWilly Tu asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 92461b1eb21SWilly Tu } 92561b1eb21SWilly Tu }); 92661b1eb21SWilly Tu 92761b1eb21SWilly Tu sdbusplus::asio::getAllProperties( 92861b1eb21SWilly Tu *crow::connections::systemBus, connectionName, path, 92961b1eb21SWilly Tu "xyz.openbmc_project.Inventory.Decorator.Asset", 93061b1eb21SWilly Tu [asyncResp](const boost::system::error_code& ec, 93161b1eb21SWilly Tu const std::vector< 93261b1eb21SWilly Tu std::pair<std::string, dbus::utility::DbusVariantType>>& 93361b1eb21SWilly Tu propertiesList) { 93461b1eb21SWilly Tu getStorageControllerAsset(asyncResp, ec, propertiesList); 93561b1eb21SWilly Tu }); 93661b1eb21SWilly Tu } 93761b1eb21SWilly Tu 93861b1eb21SWilly Tu inline void getStorageControllerHandler( 93961b1eb21SWilly Tu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 94061b1eb21SWilly Tu const std::string& controllerId, const boost::system::error_code& ec, 94161b1eb21SWilly Tu const dbus::utility::MapperGetSubTreeResponse& subtree) 94261b1eb21SWilly Tu { 94361b1eb21SWilly Tu if (ec || subtree.empty()) 94461b1eb21SWilly Tu { 94561b1eb21SWilly Tu // doesn't have to be there 94661b1eb21SWilly Tu BMCWEB_LOG_DEBUG << "Failed to handle StorageController"; 94761b1eb21SWilly Tu return; 94861b1eb21SWilly Tu } 94961b1eb21SWilly Tu 95061b1eb21SWilly Tu for (const auto& [path, interfaceDict] : subtree) 95161b1eb21SWilly Tu { 95261b1eb21SWilly Tu sdbusplus::message::object_path object(path); 95361b1eb21SWilly Tu std::string id = object.filename(); 95461b1eb21SWilly Tu if (id.empty()) 95561b1eb21SWilly Tu { 95661b1eb21SWilly Tu BMCWEB_LOG_ERROR << "Failed to find filename in " << path; 95761b1eb21SWilly Tu return; 95861b1eb21SWilly Tu } 95961b1eb21SWilly Tu if (id != controllerId) 96061b1eb21SWilly Tu { 96161b1eb21SWilly Tu continue; 96261b1eb21SWilly Tu } 96361b1eb21SWilly Tu 96461b1eb21SWilly Tu if (interfaceDict.size() != 1) 96561b1eb21SWilly Tu { 96661b1eb21SWilly Tu BMCWEB_LOG_ERROR << "Connection size " << interfaceDict.size() 96761b1eb21SWilly Tu << ", greater than 1"; 96861b1eb21SWilly Tu messages::internalError(asyncResp->res); 96961b1eb21SWilly Tu return; 97061b1eb21SWilly Tu } 97161b1eb21SWilly Tu 97261b1eb21SWilly Tu const std::string& connectionName = interfaceDict.front().first; 97361b1eb21SWilly Tu populateStorageController(asyncResp, controllerId, connectionName, 97461b1eb21SWilly Tu path); 97561b1eb21SWilly Tu } 97661b1eb21SWilly Tu } 97761b1eb21SWilly Tu 97861b1eb21SWilly Tu inline void populateStorageControllerCollection( 97961b1eb21SWilly Tu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 98061b1eb21SWilly Tu const boost::system::error_code& ec, 98161b1eb21SWilly Tu const dbus::utility::MapperGetSubTreePathsResponse& controllerList) 98261b1eb21SWilly Tu { 98361b1eb21SWilly Tu nlohmann::json::array_t members; 98461b1eb21SWilly Tu if (ec || controllerList.empty()) 98561b1eb21SWilly Tu { 98661b1eb21SWilly Tu asyncResp->res.jsonValue["Members"] = std::move(members); 98761b1eb21SWilly Tu asyncResp->res.jsonValue["Members@odata.count"] = 0; 98861b1eb21SWilly Tu BMCWEB_LOG_DEBUG << "Failed to find any StorageController"; 98961b1eb21SWilly Tu return; 99061b1eb21SWilly Tu } 99161b1eb21SWilly Tu 99261b1eb21SWilly Tu for (const std::string& path : controllerList) 99361b1eb21SWilly Tu { 99461b1eb21SWilly Tu std::string id = sdbusplus::message::object_path(path).filename(); 99561b1eb21SWilly Tu if (id.empty()) 99661b1eb21SWilly Tu { 99761b1eb21SWilly Tu BMCWEB_LOG_ERROR << "Failed to find filename in " << path; 99861b1eb21SWilly Tu return; 99961b1eb21SWilly Tu } 100061b1eb21SWilly Tu nlohmann::json::object_t member; 100161b1eb21SWilly Tu member["@odata.id"] = boost::urls::format( 100261b1eb21SWilly Tu "/redfish/v1/Systems/system/Storage/1/Controllers/{}", id); 100361b1eb21SWilly Tu members.emplace_back(member); 100461b1eb21SWilly Tu } 100561b1eb21SWilly Tu asyncResp->res.jsonValue["Members@odata.count"] = members.size(); 100661b1eb21SWilly Tu asyncResp->res.jsonValue["Members"] = std::move(members); 100761b1eb21SWilly Tu } 100861b1eb21SWilly Tu 1009*36d52334SEd Tanous inline void handleSystemsStorageControllerCollectionGet( 101061b1eb21SWilly Tu App& app, const crow::Request& req, 101161b1eb21SWilly Tu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 101261b1eb21SWilly Tu const std::string& systemName) 101361b1eb21SWilly Tu { 101461b1eb21SWilly Tu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 101561b1eb21SWilly Tu { 101661b1eb21SWilly Tu BMCWEB_LOG_DEBUG 101761b1eb21SWilly Tu << "Failed to setup Redfish Route for StorageController Collection"; 101861b1eb21SWilly Tu return; 101961b1eb21SWilly Tu } 102061b1eb21SWilly Tu if (systemName != "system") 102161b1eb21SWilly Tu { 102261b1eb21SWilly Tu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 102361b1eb21SWilly Tu systemName); 102461b1eb21SWilly Tu BMCWEB_LOG_DEBUG << "Failed to find ComputerSystem of " << systemName; 102561b1eb21SWilly Tu return; 102661b1eb21SWilly Tu } 102761b1eb21SWilly Tu 102861b1eb21SWilly Tu asyncResp->res.jsonValue["@odata.type"] = 102961b1eb21SWilly Tu "#StorageControllerCollection.StorageControllerCollection"; 103061b1eb21SWilly Tu asyncResp->res.jsonValue["@odata.id"] = 103161b1eb21SWilly Tu "/redfish/v1/Systems/system/Storage/1/Controllers"; 103261b1eb21SWilly Tu asyncResp->res.jsonValue["Name"] = "Storage Controller Collection"; 103361b1eb21SWilly Tu 103461b1eb21SWilly Tu constexpr std::array<std::string_view, 1> interfaces = { 103561b1eb21SWilly Tu "xyz.openbmc_project.Inventory.Item.StorageController"}; 103661b1eb21SWilly Tu dbus::utility::getSubTreePaths( 103761b1eb21SWilly Tu "/xyz/openbmc_project/inventory", 0, interfaces, 103861b1eb21SWilly Tu [asyncResp](const boost::system::error_code& ec, 103961b1eb21SWilly Tu const dbus::utility::MapperGetSubTreePathsResponse& 104061b1eb21SWilly Tu controllerList) { 104161b1eb21SWilly Tu populateStorageControllerCollection(asyncResp, ec, controllerList); 104261b1eb21SWilly Tu }); 104361b1eb21SWilly Tu } 104461b1eb21SWilly Tu 1045*36d52334SEd Tanous inline void handleSystemsStorageControllerGet( 104661b1eb21SWilly Tu App& app, const crow::Request& req, 104761b1eb21SWilly Tu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 104861b1eb21SWilly Tu const std::string& systemName, const std::string& controllerId) 104961b1eb21SWilly Tu { 105061b1eb21SWilly Tu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 105161b1eb21SWilly Tu { 105261b1eb21SWilly Tu BMCWEB_LOG_DEBUG 105361b1eb21SWilly Tu << "Failed to setup Redfish Route for StorageController"; 105461b1eb21SWilly Tu return; 105561b1eb21SWilly Tu } 105661b1eb21SWilly Tu if (systemName != "system") 105761b1eb21SWilly Tu { 105861b1eb21SWilly Tu messages::resourceNotFound(asyncResp->res, "ComputerSystem", 105961b1eb21SWilly Tu systemName); 106061b1eb21SWilly Tu BMCWEB_LOG_DEBUG << "Failed to find ComputerSystem of " << systemName; 106161b1eb21SWilly Tu return; 106261b1eb21SWilly Tu } 106361b1eb21SWilly Tu constexpr std::array<std::string_view, 1> interfaces = { 106461b1eb21SWilly Tu "xyz.openbmc_project.Inventory.Item.StorageController"}; 106561b1eb21SWilly Tu dbus::utility::getSubTree( 106661b1eb21SWilly Tu "/xyz/openbmc_project/inventory", 0, interfaces, 106761b1eb21SWilly Tu [asyncResp, 106861b1eb21SWilly Tu controllerId](const boost::system::error_code& ec, 106961b1eb21SWilly Tu const dbus::utility::MapperGetSubTreeResponse& subtree) { 107061b1eb21SWilly Tu getStorageControllerHandler(asyncResp, controllerId, ec, subtree); 107161b1eb21SWilly Tu }); 107261b1eb21SWilly Tu } 107361b1eb21SWilly Tu 107461b1eb21SWilly Tu inline void requestRoutesStorageControllerCollection(App& app) 107561b1eb21SWilly Tu { 107661b1eb21SWilly Tu BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/") 107761b1eb21SWilly Tu .privileges(redfish::privileges::getStorageControllerCollection) 1078*36d52334SEd Tanous .methods(boost::beast::http::verb::get)(std::bind_front( 1079*36d52334SEd Tanous handleSystemsStorageControllerCollectionGet, std::ref(app))); 108061b1eb21SWilly Tu } 108161b1eb21SWilly Tu 108261b1eb21SWilly Tu inline void requestRoutesStorageController(App& app) 108361b1eb21SWilly Tu { 108461b1eb21SWilly Tu BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Storage/1/Controllers/<str>") 108561b1eb21SWilly Tu .privileges(redfish::privileges::getStorageController) 108661b1eb21SWilly Tu .methods(boost::beast::http::verb::get)( 1087*36d52334SEd Tanous std::bind_front(handleSystemsStorageControllerGet, std::ref(app))); 108861b1eb21SWilly Tu } 108961b1eb21SWilly Tu 1090a25aeccfSNikhil Potade } // namespace redfish 1091