1*7bf29ab3SChristopher Meis // SPDX-License-Identifier: Apache-2.0 2*7bf29ab3SChristopher Meis // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3*7bf29ab3SChristopher Meis // SPDX-FileCopyrightText: Copyright 2019 Intel Corporation 4*7bf29ab3SChristopher Meis #pragma once 5*7bf29ab3SChristopher Meis 6*7bf29ab3SChristopher Meis #include "app.hpp" 7*7bf29ab3SChristopher Meis #include "async_resp.hpp" 8*7bf29ab3SChristopher Meis #include "error_messages.hpp" 9*7bf29ab3SChristopher Meis #include "generated/enums/drive.hpp" 10*7bf29ab3SChristopher Meis #include "generated/enums/protocol.hpp" 11*7bf29ab3SChristopher Meis #include "generated/enums/resource.hpp" 12*7bf29ab3SChristopher Meis #include "http_request.hpp" 13*7bf29ab3SChristopher Meis #include "query.hpp" 14*7bf29ab3SChristopher Meis #include "redfish_util.hpp" 15*7bf29ab3SChristopher Meis #include "registries/privilege_registry.hpp" 16*7bf29ab3SChristopher Meis 17*7bf29ab3SChristopher Meis namespace redfish 18*7bf29ab3SChristopher Meis { 19*7bf29ab3SChristopher Meis 20*7bf29ab3SChristopher Meis inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 21*7bf29ab3SChristopher Meis const std::string& connectionName, 22*7bf29ab3SChristopher Meis const std::string& path) 23*7bf29ab3SChristopher Meis { 24*7bf29ab3SChristopher Meis dbus::utility::getProperty<bool>( 25*7bf29ab3SChristopher Meis connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present", 26*7bf29ab3SChristopher Meis [asyncResp, 27*7bf29ab3SChristopher Meis path](const boost::system::error_code& ec, const bool isPresent) { 28*7bf29ab3SChristopher Meis // this interface isn't necessary, only check it if 29*7bf29ab3SChristopher Meis // we get a good return 30*7bf29ab3SChristopher Meis if (ec) 31*7bf29ab3SChristopher Meis { 32*7bf29ab3SChristopher Meis return; 33*7bf29ab3SChristopher Meis } 34*7bf29ab3SChristopher Meis 35*7bf29ab3SChristopher Meis if (!isPresent) 36*7bf29ab3SChristopher Meis { 37*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] = 38*7bf29ab3SChristopher Meis resource::State::Absent; 39*7bf29ab3SChristopher Meis } 40*7bf29ab3SChristopher Meis }); 41*7bf29ab3SChristopher Meis } 42*7bf29ab3SChristopher Meis 43*7bf29ab3SChristopher Meis inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 44*7bf29ab3SChristopher Meis const std::string& connectionName, 45*7bf29ab3SChristopher Meis const std::string& path) 46*7bf29ab3SChristopher Meis { 47*7bf29ab3SChristopher Meis dbus::utility::getProperty<bool>( 48*7bf29ab3SChristopher Meis connectionName, path, "xyz.openbmc_project.State.Drive", "Rebuilding", 49*7bf29ab3SChristopher Meis [asyncResp](const boost::system::error_code& ec, const bool updating) { 50*7bf29ab3SChristopher Meis // this interface isn't necessary, only check it 51*7bf29ab3SChristopher Meis // if we get a good return 52*7bf29ab3SChristopher Meis if (ec) 53*7bf29ab3SChristopher Meis { 54*7bf29ab3SChristopher Meis return; 55*7bf29ab3SChristopher Meis } 56*7bf29ab3SChristopher Meis 57*7bf29ab3SChristopher Meis // updating and disabled in the backend shouldn't be 58*7bf29ab3SChristopher Meis // able to be set at the same time, so we don't need 59*7bf29ab3SChristopher Meis // to check for the race condition of these two 60*7bf29ab3SChristopher Meis // calls 61*7bf29ab3SChristopher Meis if (updating) 62*7bf29ab3SChristopher Meis { 63*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] = 64*7bf29ab3SChristopher Meis resource::State::Updating; 65*7bf29ab3SChristopher Meis } 66*7bf29ab3SChristopher Meis }); 67*7bf29ab3SChristopher Meis } 68*7bf29ab3SChristopher Meis 69*7bf29ab3SChristopher Meis inline std::optional<drive::MediaType> convertDriveType(std::string_view type) 70*7bf29ab3SChristopher Meis { 71*7bf29ab3SChristopher Meis if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD") 72*7bf29ab3SChristopher Meis { 73*7bf29ab3SChristopher Meis return drive::MediaType::HDD; 74*7bf29ab3SChristopher Meis } 75*7bf29ab3SChristopher Meis if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD") 76*7bf29ab3SChristopher Meis { 77*7bf29ab3SChristopher Meis return drive::MediaType::SSD; 78*7bf29ab3SChristopher Meis } 79*7bf29ab3SChristopher Meis if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown") 80*7bf29ab3SChristopher Meis { 81*7bf29ab3SChristopher Meis return std::nullopt; 82*7bf29ab3SChristopher Meis } 83*7bf29ab3SChristopher Meis 84*7bf29ab3SChristopher Meis return drive::MediaType::Invalid; 85*7bf29ab3SChristopher Meis } 86*7bf29ab3SChristopher Meis 87*7bf29ab3SChristopher Meis inline std::optional<protocol::Protocol> convertDriveProtocol( 88*7bf29ab3SChristopher Meis std::string_view proto) 89*7bf29ab3SChristopher Meis { 90*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS") 91*7bf29ab3SChristopher Meis { 92*7bf29ab3SChristopher Meis return protocol::Protocol::SAS; 93*7bf29ab3SChristopher Meis } 94*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA") 95*7bf29ab3SChristopher Meis { 96*7bf29ab3SChristopher Meis return protocol::Protocol::SATA; 97*7bf29ab3SChristopher Meis } 98*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe") 99*7bf29ab3SChristopher Meis { 100*7bf29ab3SChristopher Meis return protocol::Protocol::NVMe; 101*7bf29ab3SChristopher Meis } 102*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC") 103*7bf29ab3SChristopher Meis { 104*7bf29ab3SChristopher Meis return protocol::Protocol::FC; 105*7bf29ab3SChristopher Meis } 106*7bf29ab3SChristopher Meis if (proto == 107*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown") 108*7bf29ab3SChristopher Meis { 109*7bf29ab3SChristopher Meis return std::nullopt; 110*7bf29ab3SChristopher Meis } 111*7bf29ab3SChristopher Meis 112*7bf29ab3SChristopher Meis return protocol::Protocol::Invalid; 113*7bf29ab3SChristopher Meis } 114*7bf29ab3SChristopher Meis 115*7bf29ab3SChristopher Meis inline void getDriveItemProperties( 116*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 117*7bf29ab3SChristopher Meis const std::string& connectionName, const std::string& path) 118*7bf29ab3SChristopher Meis { 119*7bf29ab3SChristopher Meis dbus::utility::getAllProperties( 120*7bf29ab3SChristopher Meis connectionName, path, "xyz.openbmc_project.Inventory.Item.Drive", 121*7bf29ab3SChristopher Meis [asyncResp](const boost::system::error_code& ec, 122*7bf29ab3SChristopher Meis const std::vector< 123*7bf29ab3SChristopher Meis std::pair<std::string, dbus::utility::DbusVariantType>>& 124*7bf29ab3SChristopher Meis propertiesList) { 125*7bf29ab3SChristopher Meis if (ec) 126*7bf29ab3SChristopher Meis { 127*7bf29ab3SChristopher Meis // this interface isn't required 128*7bf29ab3SChristopher Meis return; 129*7bf29ab3SChristopher Meis } 130*7bf29ab3SChristopher Meis const std::string* encryptionStatus = nullptr; 131*7bf29ab3SChristopher Meis const bool* isLocked = nullptr; 132*7bf29ab3SChristopher Meis for (const std::pair<std::string, dbus::utility::DbusVariantType>& 133*7bf29ab3SChristopher Meis property : propertiesList) 134*7bf29ab3SChristopher Meis { 135*7bf29ab3SChristopher Meis const std::string& propertyName = property.first; 136*7bf29ab3SChristopher Meis if (propertyName == "Type") 137*7bf29ab3SChristopher Meis { 138*7bf29ab3SChristopher Meis const std::string* value = 139*7bf29ab3SChristopher Meis std::get_if<std::string>(&property.second); 140*7bf29ab3SChristopher Meis if (value == nullptr) 141*7bf29ab3SChristopher Meis { 142*7bf29ab3SChristopher Meis // illegal property 143*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Type"); 144*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 145*7bf29ab3SChristopher Meis return; 146*7bf29ab3SChristopher Meis } 147*7bf29ab3SChristopher Meis 148*7bf29ab3SChristopher Meis std::optional<drive::MediaType> mediaType = 149*7bf29ab3SChristopher Meis convertDriveType(*value); 150*7bf29ab3SChristopher Meis if (!mediaType) 151*7bf29ab3SChristopher Meis { 152*7bf29ab3SChristopher Meis BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}", 153*7bf29ab3SChristopher Meis *value); 154*7bf29ab3SChristopher Meis continue; 155*7bf29ab3SChristopher Meis } 156*7bf29ab3SChristopher Meis if (*mediaType == drive::MediaType::Invalid) 157*7bf29ab3SChristopher Meis { 158*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 159*7bf29ab3SChristopher Meis return; 160*7bf29ab3SChristopher Meis } 161*7bf29ab3SChristopher Meis 162*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["MediaType"] = *mediaType; 163*7bf29ab3SChristopher Meis } 164*7bf29ab3SChristopher Meis else if (propertyName == "Capacity") 165*7bf29ab3SChristopher Meis { 166*7bf29ab3SChristopher Meis const uint64_t* capacity = 167*7bf29ab3SChristopher Meis std::get_if<uint64_t>(&property.second); 168*7bf29ab3SChristopher Meis if (capacity == nullptr) 169*7bf29ab3SChristopher Meis { 170*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Capacity"); 171*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 172*7bf29ab3SChristopher Meis return; 173*7bf29ab3SChristopher Meis } 174*7bf29ab3SChristopher Meis if (*capacity == 0) 175*7bf29ab3SChristopher Meis { 176*7bf29ab3SChristopher Meis // drive capacity not known 177*7bf29ab3SChristopher Meis continue; 178*7bf29ab3SChristopher Meis } 179*7bf29ab3SChristopher Meis 180*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["CapacityBytes"] = *capacity; 181*7bf29ab3SChristopher Meis } 182*7bf29ab3SChristopher Meis else if (propertyName == "Protocol") 183*7bf29ab3SChristopher Meis { 184*7bf29ab3SChristopher Meis const std::string* value = 185*7bf29ab3SChristopher Meis std::get_if<std::string>(&property.second); 186*7bf29ab3SChristopher Meis if (value == nullptr) 187*7bf29ab3SChristopher Meis { 188*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Protocol"); 189*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 190*7bf29ab3SChristopher Meis return; 191*7bf29ab3SChristopher Meis } 192*7bf29ab3SChristopher Meis 193*7bf29ab3SChristopher Meis std::optional<protocol::Protocol> proto = 194*7bf29ab3SChristopher Meis convertDriveProtocol(*value); 195*7bf29ab3SChristopher Meis if (!proto) 196*7bf29ab3SChristopher Meis { 197*7bf29ab3SChristopher Meis BMCWEB_LOG_WARNING( 198*7bf29ab3SChristopher Meis "Unknown DrivePrototype Interface: {}", *value); 199*7bf29ab3SChristopher Meis continue; 200*7bf29ab3SChristopher Meis } 201*7bf29ab3SChristopher Meis if (*proto == protocol::Protocol::Invalid) 202*7bf29ab3SChristopher Meis { 203*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 204*7bf29ab3SChristopher Meis return; 205*7bf29ab3SChristopher Meis } 206*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Protocol"] = *proto; 207*7bf29ab3SChristopher Meis } 208*7bf29ab3SChristopher Meis else if (propertyName == "PredictedMediaLifeLeftPercent") 209*7bf29ab3SChristopher Meis { 210*7bf29ab3SChristopher Meis const uint8_t* lifeLeft = 211*7bf29ab3SChristopher Meis std::get_if<uint8_t>(&property.second); 212*7bf29ab3SChristopher Meis if (lifeLeft == nullptr) 213*7bf29ab3SChristopher Meis { 214*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR( 215*7bf29ab3SChristopher Meis "Illegal property: PredictedMediaLifeLeftPercent"); 216*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 217*7bf29ab3SChristopher Meis return; 218*7bf29ab3SChristopher Meis } 219*7bf29ab3SChristopher Meis // 255 means reading the value is not supported 220*7bf29ab3SChristopher Meis if (*lifeLeft != 255) 221*7bf29ab3SChristopher Meis { 222*7bf29ab3SChristopher Meis asyncResp->res 223*7bf29ab3SChristopher Meis .jsonValue["PredictedMediaLifeLeftPercent"] = 224*7bf29ab3SChristopher Meis *lifeLeft; 225*7bf29ab3SChristopher Meis } 226*7bf29ab3SChristopher Meis } 227*7bf29ab3SChristopher Meis else if (propertyName == "EncryptionStatus") 228*7bf29ab3SChristopher Meis { 229*7bf29ab3SChristopher Meis encryptionStatus = 230*7bf29ab3SChristopher Meis std::get_if<std::string>(&property.second); 231*7bf29ab3SChristopher Meis if (encryptionStatus == nullptr) 232*7bf29ab3SChristopher Meis { 233*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus"); 234*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 235*7bf29ab3SChristopher Meis return; 236*7bf29ab3SChristopher Meis } 237*7bf29ab3SChristopher Meis } 238*7bf29ab3SChristopher Meis else if (propertyName == "Locked") 239*7bf29ab3SChristopher Meis { 240*7bf29ab3SChristopher Meis isLocked = std::get_if<bool>(&property.second); 241*7bf29ab3SChristopher Meis if (isLocked == nullptr) 242*7bf29ab3SChristopher Meis { 243*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Locked"); 244*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 245*7bf29ab3SChristopher Meis return; 246*7bf29ab3SChristopher Meis } 247*7bf29ab3SChristopher Meis } 248*7bf29ab3SChristopher Meis } 249*7bf29ab3SChristopher Meis 250*7bf29ab3SChristopher Meis if (encryptionStatus == nullptr || isLocked == nullptr || 251*7bf29ab3SChristopher Meis *encryptionStatus == 252*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown") 253*7bf29ab3SChristopher Meis { 254*7bf29ab3SChristopher Meis return; 255*7bf29ab3SChristopher Meis } 256*7bf29ab3SChristopher Meis if (*encryptionStatus != 257*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted") 258*7bf29ab3SChristopher Meis { 259*7bf29ab3SChristopher Meis //"The drive is not currently encrypted." 260*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["EncryptionStatus"] = 261*7bf29ab3SChristopher Meis drive::EncryptionStatus::Unencrypted; 262*7bf29ab3SChristopher Meis return; 263*7bf29ab3SChristopher Meis } 264*7bf29ab3SChristopher Meis if (*isLocked) 265*7bf29ab3SChristopher Meis { 266*7bf29ab3SChristopher Meis //"The drive is currently encrypted and the data is not 267*7bf29ab3SChristopher Meis // accessible to the user." 268*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["EncryptionStatus"] = 269*7bf29ab3SChristopher Meis drive::EncryptionStatus::Locked; 270*7bf29ab3SChristopher Meis return; 271*7bf29ab3SChristopher Meis } 272*7bf29ab3SChristopher Meis // if not locked 273*7bf29ab3SChristopher Meis // "The drive is currently encrypted but the data is accessible 274*7bf29ab3SChristopher Meis // to the user in unencrypted form." 275*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["EncryptionStatus"] = 276*7bf29ab3SChristopher Meis drive::EncryptionStatus::Unlocked; 277*7bf29ab3SChristopher Meis }); 278*7bf29ab3SChristopher Meis } 279*7bf29ab3SChristopher Meis 280*7bf29ab3SChristopher Meis inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 281*7bf29ab3SChristopher Meis const std::string& connectionName, 282*7bf29ab3SChristopher Meis const std::string& path, 283*7bf29ab3SChristopher Meis const std::vector<std::string>& interfaces) 284*7bf29ab3SChristopher Meis { 285*7bf29ab3SChristopher Meis for (const std::string& interface : interfaces) 286*7bf29ab3SChristopher Meis { 287*7bf29ab3SChristopher Meis if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 288*7bf29ab3SChristopher Meis { 289*7bf29ab3SChristopher Meis asset_utils::getAssetInfo(asyncResp, connectionName, path, 290*7bf29ab3SChristopher Meis ""_json_pointer, false); 291*7bf29ab3SChristopher Meis } 292*7bf29ab3SChristopher Meis else if (interface == "xyz.openbmc_project.Inventory.Item") 293*7bf29ab3SChristopher Meis { 294*7bf29ab3SChristopher Meis getDrivePresent(asyncResp, connectionName, path); 295*7bf29ab3SChristopher Meis } 296*7bf29ab3SChristopher Meis else if (interface == "xyz.openbmc_project.State.Drive") 297*7bf29ab3SChristopher Meis { 298*7bf29ab3SChristopher Meis getDriveState(asyncResp, connectionName, path); 299*7bf29ab3SChristopher Meis } 300*7bf29ab3SChristopher Meis else if (interface == "xyz.openbmc_project.Inventory.Item.Drive") 301*7bf29ab3SChristopher Meis { 302*7bf29ab3SChristopher Meis getDriveItemProperties(asyncResp, connectionName, path); 303*7bf29ab3SChristopher Meis } 304*7bf29ab3SChristopher Meis } 305*7bf29ab3SChristopher Meis } 306*7bf29ab3SChristopher Meis 307*7bf29ab3SChristopher Meis inline void afterGetSubtreeSystemsStorageDrive( 308*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 309*7bf29ab3SChristopher Meis const std::string& driveId, const boost::system::error_code& ec, 310*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) 311*7bf29ab3SChristopher Meis { 312*7bf29ab3SChristopher Meis if (ec) 313*7bf29ab3SChristopher Meis { 314*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Drive mapper call error"); 315*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 316*7bf29ab3SChristopher Meis return; 317*7bf29ab3SChristopher Meis } 318*7bf29ab3SChristopher Meis 319*7bf29ab3SChristopher Meis auto drive = std::ranges::find_if( 320*7bf29ab3SChristopher Meis subtree, 321*7bf29ab3SChristopher Meis [&driveId](const std::pair<std::string, 322*7bf29ab3SChristopher Meis dbus::utility::MapperServiceMap>& object) { 323*7bf29ab3SChristopher Meis return sdbusplus::message::object_path(object.first).filename() == 324*7bf29ab3SChristopher Meis driveId; 325*7bf29ab3SChristopher Meis }); 326*7bf29ab3SChristopher Meis 327*7bf29ab3SChristopher Meis if (drive == subtree.end()) 328*7bf29ab3SChristopher Meis { 329*7bf29ab3SChristopher Meis messages::resourceNotFound(asyncResp->res, "Drive", driveId); 330*7bf29ab3SChristopher Meis return; 331*7bf29ab3SChristopher Meis } 332*7bf29ab3SChristopher Meis 333*7bf29ab3SChristopher Meis const std::string& path = drive->first; 334*7bf29ab3SChristopher Meis const dbus::utility::MapperServiceMap& connectionNames = drive->second; 335*7bf29ab3SChristopher Meis 336*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 337*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.id"] = 338*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Drives/{}", 339*7bf29ab3SChristopher Meis BMCWEB_REDFISH_SYSTEM_URI_NAME, driveId); 340*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Name"] = driveId; 341*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Id"] = driveId; 342*7bf29ab3SChristopher Meis 343*7bf29ab3SChristopher Meis if (connectionNames.size() != 1) 344*7bf29ab3SChristopher Meis { 345*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Connection size {}, not equal to 1", 346*7bf29ab3SChristopher Meis connectionNames.size()); 347*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 348*7bf29ab3SChristopher Meis return; 349*7bf29ab3SChristopher Meis } 350*7bf29ab3SChristopher Meis 351*7bf29ab3SChristopher Meis getMainChassisId( 352*7bf29ab3SChristopher Meis asyncResp, [](const std::string& chassisId, 353*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 354*7bf29ab3SChristopher Meis aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] = 355*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 356*7bf29ab3SChristopher Meis }); 357*7bf29ab3SChristopher Meis 358*7bf29ab3SChristopher Meis // default it to Enabled 359*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 360*7bf29ab3SChristopher Meis 361*7bf29ab3SChristopher Meis addAllDriveInfo(asyncResp, connectionNames[0].first, path, 362*7bf29ab3SChristopher Meis connectionNames[0].second); 363*7bf29ab3SChristopher Meis } 364*7bf29ab3SChristopher Meis 365*7bf29ab3SChristopher Meis inline void afterChassisDriveCollectionSubtreeGet( 366*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 367*7bf29ab3SChristopher Meis const std::string& chassisId, const boost::system::error_code& ec, 368*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) 369*7bf29ab3SChristopher Meis { 370*7bf29ab3SChristopher Meis if (ec) 371*7bf29ab3SChristopher Meis { 372*7bf29ab3SChristopher Meis if (ec == boost::system::errc::host_unreachable) 373*7bf29ab3SChristopher Meis { 374*7bf29ab3SChristopher Meis messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 375*7bf29ab3SChristopher Meis return; 376*7bf29ab3SChristopher Meis } 377*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 378*7bf29ab3SChristopher Meis return; 379*7bf29ab3SChristopher Meis } 380*7bf29ab3SChristopher Meis 381*7bf29ab3SChristopher Meis // Iterate over all retrieved ObjectPaths. 382*7bf29ab3SChristopher Meis for (const auto& [path, connectionNames] : subtree) 383*7bf29ab3SChristopher Meis { 384*7bf29ab3SChristopher Meis sdbusplus::message::object_path objPath(path); 385*7bf29ab3SChristopher Meis if (objPath.filename() != chassisId) 386*7bf29ab3SChristopher Meis { 387*7bf29ab3SChristopher Meis continue; 388*7bf29ab3SChristopher Meis } 389*7bf29ab3SChristopher Meis 390*7bf29ab3SChristopher Meis if (connectionNames.empty()) 391*7bf29ab3SChristopher Meis { 392*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Got 0 Connection names"); 393*7bf29ab3SChristopher Meis continue; 394*7bf29ab3SChristopher Meis } 395*7bf29ab3SChristopher Meis 396*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.type"] = 397*7bf29ab3SChristopher Meis "#DriveCollection.DriveCollection"; 398*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.id"] = 399*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId); 400*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Name"] = "Drive Collection"; 401*7bf29ab3SChristopher Meis 402*7bf29ab3SChristopher Meis // Association lambda 403*7bf29ab3SChristopher Meis dbus::utility::getAssociationEndPoints( 404*7bf29ab3SChristopher Meis path + "/drive", 405*7bf29ab3SChristopher Meis [asyncResp, chassisId](const boost::system::error_code& ec3, 406*7bf29ab3SChristopher Meis const dbus::utility::MapperEndPoints& resp) { 407*7bf29ab3SChristopher Meis if (ec3) 408*7bf29ab3SChristopher Meis { 409*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Error in chassis Drive association "); 410*7bf29ab3SChristopher Meis } 411*7bf29ab3SChristopher Meis nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 412*7bf29ab3SChristopher Meis // important if array is empty 413*7bf29ab3SChristopher Meis members = nlohmann::json::array(); 414*7bf29ab3SChristopher Meis 415*7bf29ab3SChristopher Meis std::vector<std::string> leafNames; 416*7bf29ab3SChristopher Meis for (const auto& drive : resp) 417*7bf29ab3SChristopher Meis { 418*7bf29ab3SChristopher Meis sdbusplus::message::object_path drivePath(drive); 419*7bf29ab3SChristopher Meis leafNames.push_back(drivePath.filename()); 420*7bf29ab3SChristopher Meis } 421*7bf29ab3SChristopher Meis 422*7bf29ab3SChristopher Meis std::ranges::sort(leafNames, AlphanumLess<std::string>()); 423*7bf29ab3SChristopher Meis 424*7bf29ab3SChristopher Meis for (const auto& leafName : leafNames) 425*7bf29ab3SChristopher Meis { 426*7bf29ab3SChristopher Meis nlohmann::json::object_t member; 427*7bf29ab3SChristopher Meis member["@odata.id"] = 428*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}", 429*7bf29ab3SChristopher Meis chassisId, leafName); 430*7bf29ab3SChristopher Meis members.emplace_back(std::move(member)); 431*7bf29ab3SChristopher Meis // navigation links will be registered in next patch set 432*7bf29ab3SChristopher Meis } 433*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Members@odata.count"] = resp.size(); 434*7bf29ab3SChristopher Meis }); // end association lambda 435*7bf29ab3SChristopher Meis 436*7bf29ab3SChristopher Meis } // end Iterate over all retrieved ObjectPaths 437*7bf29ab3SChristopher Meis } 438*7bf29ab3SChristopher Meis 439*7bf29ab3SChristopher Meis /** 440*7bf29ab3SChristopher Meis * Chassis drives, this URL will show all the DriveCollection 441*7bf29ab3SChristopher Meis * information 442*7bf29ab3SChristopher Meis */ 443*7bf29ab3SChristopher Meis inline void chassisDriveCollectionGet( 444*7bf29ab3SChristopher Meis crow::App& app, const crow::Request& req, 445*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 446*7bf29ab3SChristopher Meis const std::string& chassisId) 447*7bf29ab3SChristopher Meis { 448*7bf29ab3SChristopher Meis if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 449*7bf29ab3SChristopher Meis { 450*7bf29ab3SChristopher Meis return; 451*7bf29ab3SChristopher Meis } 452*7bf29ab3SChristopher Meis 453*7bf29ab3SChristopher Meis // mapper call lambda 454*7bf29ab3SChristopher Meis dbus::utility::getSubTree( 455*7bf29ab3SChristopher Meis "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 456*7bf29ab3SChristopher Meis std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp, 457*7bf29ab3SChristopher Meis chassisId)); 458*7bf29ab3SChristopher Meis } 459*7bf29ab3SChristopher Meis 460*7bf29ab3SChristopher Meis inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 461*7bf29ab3SChristopher Meis const std::string& chassisId, 462*7bf29ab3SChristopher Meis const std::string& driveName, 463*7bf29ab3SChristopher Meis const boost::system::error_code& ec, 464*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) 465*7bf29ab3SChristopher Meis { 466*7bf29ab3SChristopher Meis if (ec) 467*7bf29ab3SChristopher Meis { 468*7bf29ab3SChristopher Meis BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 469*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 470*7bf29ab3SChristopher Meis return; 471*7bf29ab3SChristopher Meis } 472*7bf29ab3SChristopher Meis 473*7bf29ab3SChristopher Meis // Iterate over all retrieved ObjectPaths. 474*7bf29ab3SChristopher Meis for (const auto& [path, connectionNames] : subtree) 475*7bf29ab3SChristopher Meis { 476*7bf29ab3SChristopher Meis sdbusplus::message::object_path objPath(path); 477*7bf29ab3SChristopher Meis if (objPath.filename() != driveName) 478*7bf29ab3SChristopher Meis { 479*7bf29ab3SChristopher Meis continue; 480*7bf29ab3SChristopher Meis } 481*7bf29ab3SChristopher Meis 482*7bf29ab3SChristopher Meis if (connectionNames.empty()) 483*7bf29ab3SChristopher Meis { 484*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Got 0 Connection names"); 485*7bf29ab3SChristopher Meis continue; 486*7bf29ab3SChristopher Meis } 487*7bf29ab3SChristopher Meis 488*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 489*7bf29ab3SChristopher Meis "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName); 490*7bf29ab3SChristopher Meis 491*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 492*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Name"] = driveName; 493*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Id"] = driveName; 494*7bf29ab3SChristopher Meis // default it to Enabled 495*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 496*7bf29ab3SChristopher Meis 497*7bf29ab3SChristopher Meis nlohmann::json::object_t linkChassisNav; 498*7bf29ab3SChristopher Meis linkChassisNav["@odata.id"] = 499*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 500*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav; 501*7bf29ab3SChristopher Meis 502*7bf29ab3SChristopher Meis addAllDriveInfo(asyncResp, connectionNames[0].first, path, 503*7bf29ab3SChristopher Meis connectionNames[0].second); 504*7bf29ab3SChristopher Meis } 505*7bf29ab3SChristopher Meis } 506*7bf29ab3SChristopher Meis 507*7bf29ab3SChristopher Meis inline void matchAndFillDrive( 508*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 509*7bf29ab3SChristopher Meis const std::string& chassisId, const std::string& driveName, 510*7bf29ab3SChristopher Meis const std::vector<std::string>& resp) 511*7bf29ab3SChristopher Meis { 512*7bf29ab3SChristopher Meis for (const std::string& drivePath : resp) 513*7bf29ab3SChristopher Meis { 514*7bf29ab3SChristopher Meis sdbusplus::message::object_path path(drivePath); 515*7bf29ab3SChristopher Meis std::string leaf = path.filename(); 516*7bf29ab3SChristopher Meis if (leaf != driveName) 517*7bf29ab3SChristopher Meis { 518*7bf29ab3SChristopher Meis continue; 519*7bf29ab3SChristopher Meis } 520*7bf29ab3SChristopher Meis // mapper call drive 521*7bf29ab3SChristopher Meis constexpr std::array<std::string_view, 1> driveInterface = { 522*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive"}; 523*7bf29ab3SChristopher Meis dbus::utility::getSubTree( 524*7bf29ab3SChristopher Meis "/xyz/openbmc_project/inventory", 0, driveInterface, 525*7bf29ab3SChristopher Meis [asyncResp, chassisId, driveName]( 526*7bf29ab3SChristopher Meis const boost::system::error_code& ec, 527*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) { 528*7bf29ab3SChristopher Meis buildDrive(asyncResp, chassisId, driveName, ec, subtree); 529*7bf29ab3SChristopher Meis }); 530*7bf29ab3SChristopher Meis } 531*7bf29ab3SChristopher Meis } 532*7bf29ab3SChristopher Meis 533*7bf29ab3SChristopher Meis inline void handleChassisDriveGet( 534*7bf29ab3SChristopher Meis crow::App& app, const crow::Request& req, 535*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 536*7bf29ab3SChristopher Meis const std::string& chassisId, const std::string& driveName) 537*7bf29ab3SChristopher Meis { 538*7bf29ab3SChristopher Meis if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 539*7bf29ab3SChristopher Meis { 540*7bf29ab3SChristopher Meis return; 541*7bf29ab3SChristopher Meis } 542*7bf29ab3SChristopher Meis 543*7bf29ab3SChristopher Meis // mapper call chassis 544*7bf29ab3SChristopher Meis dbus::utility::getSubTree( 545*7bf29ab3SChristopher Meis "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 546*7bf29ab3SChristopher Meis [asyncResp, chassisId, 547*7bf29ab3SChristopher Meis driveName](const boost::system::error_code& ec, 548*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) { 549*7bf29ab3SChristopher Meis if (ec) 550*7bf29ab3SChristopher Meis { 551*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res); 552*7bf29ab3SChristopher Meis return; 553*7bf29ab3SChristopher Meis } 554*7bf29ab3SChristopher Meis 555*7bf29ab3SChristopher Meis // Iterate over all retrieved ObjectPaths. 556*7bf29ab3SChristopher Meis for (const auto& [path, connectionNames] : subtree) 557*7bf29ab3SChristopher Meis { 558*7bf29ab3SChristopher Meis sdbusplus::message::object_path objPath(path); 559*7bf29ab3SChristopher Meis if (objPath.filename() != chassisId) 560*7bf29ab3SChristopher Meis { 561*7bf29ab3SChristopher Meis continue; 562*7bf29ab3SChristopher Meis } 563*7bf29ab3SChristopher Meis 564*7bf29ab3SChristopher Meis if (connectionNames.empty()) 565*7bf29ab3SChristopher Meis { 566*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Got 0 Connection names"); 567*7bf29ab3SChristopher Meis continue; 568*7bf29ab3SChristopher Meis } 569*7bf29ab3SChristopher Meis 570*7bf29ab3SChristopher Meis dbus::utility::getAssociationEndPoints( 571*7bf29ab3SChristopher Meis path + "/drive", 572*7bf29ab3SChristopher Meis [asyncResp, chassisId, 573*7bf29ab3SChristopher Meis driveName](const boost::system::error_code& ec3, 574*7bf29ab3SChristopher Meis const dbus::utility::MapperEndPoints& resp) { 575*7bf29ab3SChristopher Meis if (ec3) 576*7bf29ab3SChristopher Meis { 577*7bf29ab3SChristopher Meis return; // no drives = no failures 578*7bf29ab3SChristopher Meis } 579*7bf29ab3SChristopher Meis matchAndFillDrive(asyncResp, chassisId, driveName, 580*7bf29ab3SChristopher Meis resp); 581*7bf29ab3SChristopher Meis }); 582*7bf29ab3SChristopher Meis return; 583*7bf29ab3SChristopher Meis } 584*7bf29ab3SChristopher Meis // Couldn't find an object with that name. return an error 585*7bf29ab3SChristopher Meis messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 586*7bf29ab3SChristopher Meis }); 587*7bf29ab3SChristopher Meis } 588*7bf29ab3SChristopher Meis 589*7bf29ab3SChristopher Meis /** 590*7bf29ab3SChristopher Meis * This URL will show the drive interface for the specific drive in the chassis 591*7bf29ab3SChristopher Meis */ 592*7bf29ab3SChristopher Meis inline void requestRoutesChassisDrive(App& app) 593*7bf29ab3SChristopher Meis { 594*7bf29ab3SChristopher Meis BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/") 595*7bf29ab3SChristopher Meis .privileges(redfish::privileges::getDriveCollection) 596*7bf29ab3SChristopher Meis .methods(boost::beast::http::verb::get)( 597*7bf29ab3SChristopher Meis std::bind_front(chassisDriveCollectionGet, std::ref(app))); 598*7bf29ab3SChristopher Meis 599*7bf29ab3SChristopher Meis BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/") 600*7bf29ab3SChristopher Meis .privileges(redfish::privileges::getChassis) 601*7bf29ab3SChristopher Meis .methods(boost::beast::http::verb::get)( 602*7bf29ab3SChristopher Meis std::bind_front(handleChassisDriveGet, std::ref(app))); 603*7bf29ab3SChristopher Meis } 604*7bf29ab3SChristopher Meis 605*7bf29ab3SChristopher Meis } // namespace redfish 606