1 /* 2 // Copyright (c) 2019 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "health.hpp" 19 #include "openbmc_dbus_rest.hpp" 20 21 #include <app.hpp> 22 #include <dbus_utility.hpp> 23 #include <query.hpp> 24 #include <registries/privilege_registry.hpp> 25 #include <sdbusplus/asio/property.hpp> 26 27 namespace redfish 28 { 29 inline void requestRoutesStorageCollection(App& app) 30 { 31 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/") 32 .privileges(redfish::privileges::getStorageCollection) 33 .methods(boost::beast::http::verb::get)( 34 [&app](const crow::Request& req, 35 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 36 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 37 { 38 return; 39 } 40 asyncResp->res.jsonValue["@odata.type"] = 41 "#StorageCollection.StorageCollection"; 42 asyncResp->res.jsonValue["@odata.id"] = 43 "/redfish/v1/Systems/system/Storage"; 44 asyncResp->res.jsonValue["Name"] = "Storage Collection"; 45 nlohmann::json::array_t members; 46 nlohmann::json::object_t member; 47 member["@odata.id"] = "/redfish/v1/Systems/system/Storage/1"; 48 members.emplace_back(member); 49 asyncResp->res.jsonValue["Members"] = std::move(members); 50 asyncResp->res.jsonValue["Members@odata.count"] = 1; 51 }); 52 } 53 54 inline void getDrives(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 55 const std::shared_ptr<HealthPopulate>& health) 56 { 57 crow::connections::systemBus->async_method_call( 58 [asyncResp, health]( 59 const boost::system::error_code ec, 60 const dbus::utility::MapperGetSubTreePathsResponse& driveList) { 61 if (ec) 62 { 63 BMCWEB_LOG_ERROR << "Drive mapper call error"; 64 messages::internalError(asyncResp->res); 65 return; 66 } 67 68 nlohmann::json& driveArray = asyncResp->res.jsonValue["Drives"]; 69 driveArray = nlohmann::json::array(); 70 auto& count = asyncResp->res.jsonValue["Drives@odata.count"]; 71 count = 0; 72 73 health->inventory.insert(health->inventory.end(), driveList.begin(), 74 driveList.end()); 75 76 for (const std::string& drive : driveList) 77 { 78 sdbusplus::message::object_path object(drive); 79 if (object.filename().empty()) 80 { 81 BMCWEB_LOG_ERROR << "Failed to find filename in " << drive; 82 return; 83 } 84 85 nlohmann::json::object_t driveJson; 86 driveJson["@odata.id"] = 87 "/redfish/v1/Systems/system/Storage/1/Drives/" + 88 object.filename(); 89 driveArray.push_back(std::move(driveJson)); 90 } 91 92 count = driveArray.size(); 93 }, 94 "xyz.openbmc_project.ObjectMapper", 95 "/xyz/openbmc_project/object_mapper", 96 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 97 "/xyz/openbmc_project/inventory", int32_t(0), 98 std::array<const char*, 1>{"xyz.openbmc_project.Inventory.Item.Drive"}); 99 } 100 101 inline void 102 getStorageControllers(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 103 const std::shared_ptr<HealthPopulate>& health) 104 { 105 crow::connections::systemBus->async_method_call( 106 [asyncResp, 107 health](const boost::system::error_code ec, 108 const dbus::utility::MapperGetSubTreeResponse& subtree) { 109 if (ec || subtree.empty()) 110 { 111 // doesn't have to be there 112 return; 113 } 114 115 nlohmann::json& root = asyncResp->res.jsonValue["StorageControllers"]; 116 root = nlohmann::json::array(); 117 for (const auto& [path, interfaceDict] : subtree) 118 { 119 sdbusplus::message::object_path object(path); 120 std::string id = object.filename(); 121 if (id.empty()) 122 { 123 BMCWEB_LOG_ERROR << "Failed to find filename in " << path; 124 return; 125 } 126 127 if (interfaceDict.size() != 1) 128 { 129 BMCWEB_LOG_ERROR << "Connection size " << interfaceDict.size() 130 << ", greater than 1"; 131 messages::internalError(asyncResp->res); 132 return; 133 } 134 135 const std::string& connectionName = interfaceDict.front().first; 136 137 size_t index = root.size(); 138 nlohmann::json& storageController = 139 root.emplace_back(nlohmann::json::object()); 140 141 storageController["@odata.type"] = 142 "#Storage.v1_7_0.StorageController"; 143 storageController["@odata.id"] = 144 "/redfish/v1/Systems/system/Storage/1#/StorageControllers/" + 145 std::to_string(index); 146 storageController["Name"] = id; 147 storageController["MemberId"] = id; 148 storageController["Status"]["State"] = "Enabled"; 149 150 sdbusplus::asio::getProperty<bool>( 151 *crow::connections::systemBus, connectionName, path, 152 "xyz.openbmc_project.Inventory.Item", "Present", 153 [asyncResp, index](const boost::system::error_code ec2, 154 bool enabled) { 155 // this interface isn't necessary, only check it 156 // if we get a good return 157 if (ec2) 158 { 159 return; 160 } 161 if (!enabled) 162 { 163 asyncResp->res.jsonValue["StorageControllers"][index] 164 ["Status"]["State"] = "Disabled"; 165 } 166 }); 167 168 crow::connections::systemBus->async_method_call( 169 [asyncResp, index]( 170 const boost::system::error_code ec2, 171 const std::vector< 172 std::pair<std::string, dbus::utility::DbusVariantType>>& 173 propertiesList) { 174 if (ec2) 175 { 176 // this interface isn't necessary 177 return; 178 } 179 for (const std::pair<std::string, 180 dbus::utility::DbusVariantType>& property : 181 propertiesList) 182 { 183 // Store DBus properties that are also 184 // Redfish properties with same name and a 185 // string value 186 const std::string& propertyName = property.first; 187 nlohmann::json& object = 188 asyncResp->res.jsonValue["StorageControllers"][index]; 189 if ((propertyName == "PartNumber") || 190 (propertyName == "SerialNumber") || 191 (propertyName == "Manufacturer") || 192 (propertyName == "Model")) 193 { 194 const std::string* value = 195 std::get_if<std::string>(&property.second); 196 if (value == nullptr) 197 { 198 // illegal property 199 messages::internalError(asyncResp->res); 200 return; 201 } 202 object[propertyName] = *value; 203 } 204 } 205 }, 206 connectionName, path, "org.freedesktop.DBus.Properties", 207 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); 208 } 209 210 // this is done after we know the json array will no longer 211 // be resized, as json::array uses vector underneath and we 212 // need references to its members that won't change 213 size_t count = 0; 214 // Pointer based on |asyncResp->res.jsonValue| 215 nlohmann::json::json_pointer rootPtr = 216 "/StorageControllers"_json_pointer; 217 for (const auto& [path, interfaceDict] : subtree) 218 { 219 auto subHealth = std::make_shared<HealthPopulate>( 220 asyncResp, rootPtr / count / "Status"); 221 subHealth->inventory.emplace_back(path); 222 health->inventory.emplace_back(path); 223 health->children.emplace_back(subHealth); 224 count++; 225 } 226 }, 227 "xyz.openbmc_project.ObjectMapper", 228 "/xyz/openbmc_project/object_mapper", 229 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 230 "/xyz/openbmc_project/inventory", int32_t(0), 231 std::array<const char*, 1>{ 232 "xyz.openbmc_project.Inventory.Item.StorageController"}); 233 } 234 235 inline void requestRoutesStorage(App& app) 236 { 237 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/") 238 .privileges(redfish::privileges::getStorage) 239 .methods(boost::beast::http::verb::get)( 240 [&app](const crow::Request& req, 241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 242 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 243 { 244 return; 245 } 246 asyncResp->res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage"; 247 asyncResp->res.jsonValue["@odata.id"] = 248 "/redfish/v1/Systems/system/Storage/1"; 249 asyncResp->res.jsonValue["Name"] = "Storage"; 250 asyncResp->res.jsonValue["Id"] = "1"; 251 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 252 253 auto health = std::make_shared<HealthPopulate>(asyncResp); 254 health->populate(); 255 256 getDrives(asyncResp, health); 257 getStorageControllers(asyncResp, health); 258 }); 259 } 260 261 inline void getDriveAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 262 const std::string& connectionName, 263 const std::string& path) 264 { 265 crow::connections::systemBus->async_method_call( 266 [asyncResp](const boost::system::error_code ec, 267 const std::vector< 268 std::pair<std::string, dbus::utility::DbusVariantType>>& 269 propertiesList) { 270 if (ec) 271 { 272 // this interface isn't necessary 273 return; 274 } 275 for (const std::pair<std::string, dbus::utility::DbusVariantType>& 276 property : propertiesList) 277 { 278 // Store DBus properties that are also 279 // Redfish properties with same name and a 280 // string value 281 const std::string& propertyName = property.first; 282 if ((propertyName == "PartNumber") || 283 (propertyName == "SerialNumber") || 284 (propertyName == "Manufacturer") || (propertyName == "Model")) 285 { 286 const std::string* value = 287 std::get_if<std::string>(&property.second); 288 if (value == nullptr) 289 { 290 // illegal property 291 messages::internalError(asyncResp->res); 292 return; 293 } 294 asyncResp->res.jsonValue[propertyName] = *value; 295 } 296 } 297 }, 298 connectionName, path, "org.freedesktop.DBus.Properties", "GetAll", 299 "xyz.openbmc_project.Inventory.Decorator.Asset"); 300 } 301 302 inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 303 const std::string& connectionName, 304 const std::string& path) 305 { 306 sdbusplus::asio::getProperty<bool>( 307 *crow::connections::systemBus, connectionName, path, 308 "xyz.openbmc_project.Inventory.Item", "Present", 309 [asyncResp, path](const boost::system::error_code ec, 310 const bool enabled) { 311 // this interface isn't necessary, only check it if 312 // we get a good return 313 if (ec) 314 { 315 return; 316 } 317 318 if (!enabled) 319 { 320 asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; 321 } 322 }); 323 } 324 325 inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 326 const std::string& connectionName, 327 const std::string& path) 328 { 329 sdbusplus::asio::getProperty<bool>( 330 *crow::connections::systemBus, connectionName, path, 331 "xyz.openbmc_project.State.Drive", "Rebuilding", 332 [asyncResp](const boost::system::error_code ec, const bool updating) { 333 // this interface isn't necessary, only check it 334 // if we get a good return 335 if (ec) 336 { 337 return; 338 } 339 340 // updating and disabled in the backend shouldn't be 341 // able to be set at the same time, so we don't need 342 // to check for the race condition of these two 343 // calls 344 if (updating) 345 { 346 asyncResp->res.jsonValue["Status"]["State"] = "Updating"; 347 } 348 }); 349 } 350 351 inline std::optional<std::string> convertDriveType(const std::string& type) 352 { 353 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD") 354 { 355 return "HDD"; 356 } 357 if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD") 358 { 359 return "SSD"; 360 } 361 362 return std::nullopt; 363 } 364 365 inline std::optional<std::string> convertDriveProtocol(const std::string& proto) 366 { 367 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS") 368 { 369 return "SAS"; 370 } 371 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA") 372 { 373 return "SATA"; 374 } 375 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe") 376 { 377 return "NVMe"; 378 } 379 if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC") 380 { 381 return "FC"; 382 } 383 384 return std::nullopt; 385 } 386 387 inline void 388 getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 389 const std::string& connectionName, 390 const std::string& path) 391 { 392 sdbusplus::asio::getAllProperties( 393 *crow::connections::systemBus, connectionName, path, 394 "xyz.openbmc_project.Inventory.Item.Drive", 395 [asyncResp](const boost::system::error_code ec, 396 const std::vector< 397 std::pair<std::string, dbus::utility::DbusVariantType>>& 398 propertiesList) { 399 if (ec) 400 { 401 // this interface isn't required 402 return; 403 } 404 for (const std::pair<std::string, dbus::utility::DbusVariantType>& 405 property : propertiesList) 406 { 407 const std::string& propertyName = property.first; 408 if (propertyName == "Type") 409 { 410 const std::string* value = 411 std::get_if<std::string>(&property.second); 412 if (value == nullptr) 413 { 414 // illegal property 415 BMCWEB_LOG_ERROR << "Illegal property: Type"; 416 messages::internalError(asyncResp->res); 417 return; 418 } 419 420 std::optional<std::string> mediaType = convertDriveType(*value); 421 if (!mediaType) 422 { 423 BMCWEB_LOG_ERROR << "Unsupported DriveType Interface: " 424 << *value; 425 messages::internalError(asyncResp->res); 426 return; 427 } 428 429 asyncResp->res.jsonValue["MediaType"] = *mediaType; 430 } 431 else if (propertyName == "Capacity") 432 { 433 const uint64_t* capacity = 434 std::get_if<uint64_t>(&property.second); 435 if (capacity == nullptr) 436 { 437 BMCWEB_LOG_ERROR << "Illegal property: Capacity"; 438 messages::internalError(asyncResp->res); 439 return; 440 } 441 if (*capacity == 0) 442 { 443 // drive capacity not known 444 continue; 445 } 446 447 asyncResp->res.jsonValue["CapacityBytes"] = *capacity; 448 } 449 else if (propertyName == "Protocol") 450 { 451 const std::string* value = 452 std::get_if<std::string>(&property.second); 453 if (value == nullptr) 454 { 455 BMCWEB_LOG_ERROR << "Illegal property: Protocol"; 456 messages::internalError(asyncResp->res); 457 return; 458 } 459 460 std::optional<std::string> proto = convertDriveProtocol(*value); 461 if (!proto) 462 { 463 BMCWEB_LOG_ERROR << "Unsupported DrivePrototype Interface: " 464 << *value; 465 messages::internalError(asyncResp->res); 466 return; 467 } 468 asyncResp->res.jsonValue["Protocol"] = *proto; 469 } 470 } 471 }); 472 } 473 474 void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 475 const std::string& connectionName, const std::string& path, 476 const std::vector<std::string>& interfaces) 477 { 478 for (const std::string& interface : interfaces) 479 { 480 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 481 { 482 getDriveAsset(asyncResp, connectionName, path); 483 } 484 else if (interface == "xyz.openbmc_project.Inventory.Item") 485 { 486 getDrivePresent(asyncResp, connectionName, path); 487 } 488 else if (interface == "xyz.openbmc_project.State.Drive") 489 { 490 getDriveState(asyncResp, connectionName, path); 491 } 492 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive") 493 { 494 getDriveItemProperties(asyncResp, connectionName, path); 495 } 496 } 497 } 498 499 inline void requestRoutesDrive(App& app) 500 { 501 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/") 502 .privileges(redfish::privileges::getDrive) 503 .methods(boost::beast::http::verb::get)( 504 [&app](const crow::Request& req, 505 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 506 const std::string& driveId) { 507 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 508 { 509 return; 510 } 511 crow::connections::systemBus->async_method_call( 512 [asyncResp, 513 driveId](const boost::system::error_code ec, 514 const dbus::utility::MapperGetSubTreeResponse& subtree) { 515 if (ec) 516 { 517 BMCWEB_LOG_ERROR << "Drive mapper call error"; 518 messages::internalError(asyncResp->res); 519 return; 520 } 521 522 auto drive = std::find_if( 523 subtree.begin(), subtree.end(), 524 [&driveId]( 525 const std::pair< 526 std::string, 527 std::vector<std::pair< 528 std::string, std::vector<std::string>>>>& object) { 529 return sdbusplus::message::object_path(object.first) 530 .filename() == driveId; 531 }); 532 533 if (drive == subtree.end()) 534 { 535 messages::resourceNotFound(asyncResp->res, "Drive", driveId); 536 return; 537 } 538 539 const std::string& path = drive->first; 540 const std::vector<std::pair<std::string, std::vector<std::string>>>& 541 connectionNames = drive->second; 542 543 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 544 asyncResp->res.jsonValue["@odata.id"] = 545 "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId; 546 asyncResp->res.jsonValue["Name"] = driveId; 547 asyncResp->res.jsonValue["Id"] = driveId; 548 549 if (connectionNames.size() != 1) 550 { 551 BMCWEB_LOG_ERROR << "Connection size " << connectionNames.size() 552 << ", not equal to 1"; 553 messages::internalError(asyncResp->res); 554 return; 555 } 556 557 getMainChassisId( 558 asyncResp, [](const std::string& chassisId, 559 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 560 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] = 561 "/redfish/v1/Chassis/" + chassisId; 562 }); 563 564 // default it to Enabled 565 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 566 567 auto health = std::make_shared<HealthPopulate>(asyncResp); 568 health->inventory.emplace_back(path); 569 health->populate(); 570 571 addAllDriveInfo(asyncResp, connectionNames[0].first, path, 572 connectionNames[0].second); 573 }, 574 "xyz.openbmc_project.ObjectMapper", 575 "/xyz/openbmc_project/object_mapper", 576 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 577 "/xyz/openbmc_project/inventory", int32_t(0), 578 std::array<const char*, 1>{ 579 "xyz.openbmc_project.Inventory.Item.Drive"}); 580 }); 581 } 582 583 /** 584 * Chassis drives, this URL will show all the DriveCollection 585 * information 586 */ 587 void chassisDriveCollectionGet( 588 crow::App& app, const crow::Request& req, 589 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 590 const std::string& chassisId) 591 { 592 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 593 { 594 return; 595 } 596 597 // mapper call lambda 598 crow::connections::systemBus->async_method_call( 599 [asyncResp, 600 chassisId](const boost::system::error_code ec, 601 const dbus::utility::MapperGetSubTreeResponse& subtree) { 602 if (ec) 603 { 604 if (ec == boost::system::errc::host_unreachable) 605 { 606 messages::resourceNotFound(asyncResp->res, "Chassis", 607 chassisId); 608 return; 609 } 610 messages::internalError(asyncResp->res); 611 return; 612 } 613 614 // Iterate over all retrieved ObjectPaths. 615 for (const std::pair< 616 std::string, 617 std::vector<std::pair<std::string, std::vector<std::string>>>>& 618 object : subtree) 619 { 620 const std::string& path = object.first; 621 const dbus::utility::MapperGetObject& connectionNames = 622 object.second; 623 624 sdbusplus::message::object_path objPath(path); 625 if (objPath.filename() != chassisId) 626 { 627 continue; 628 } 629 630 if (connectionNames.empty()) 631 { 632 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 633 continue; 634 } 635 636 asyncResp->res.jsonValue["@odata.type"] = 637 "#DriveCollection.DriveCollection"; 638 asyncResp->res.jsonValue["@odata.id"] = 639 crow::utility::urlFromPieces("redfish", "v1", "Chassis", 640 chassisId, "Drives"); 641 asyncResp->res.jsonValue["Name"] = "Drive Collection"; 642 643 // Association lambda 644 sdbusplus::asio::getProperty<std::vector<std::string>>( 645 *crow::connections::systemBus, 646 "xyz.openbmc_project.ObjectMapper", path + "/drive", 647 "xyz.openbmc_project.Association", "endpoints", 648 [asyncResp, chassisId](const boost::system::error_code ec3, 649 const std::vector<std::string>& resp) { 650 if (ec3) 651 { 652 BMCWEB_LOG_ERROR << "Error in chassis Drive association "; 653 } 654 nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 655 // important if array is empty 656 members = nlohmann::json::array(); 657 658 std::vector<std::string> leafNames; 659 for (const auto& drive : resp) 660 { 661 sdbusplus::message::object_path path(drive); 662 leafNames.push_back(path.filename()); 663 } 664 665 std::sort(leafNames.begin(), leafNames.end(), 666 AlphanumLess<std::string>()); 667 668 for (const auto& leafName : leafNames) 669 { 670 nlohmann::json::object_t member; 671 member["@odata.id"] = crow::utility::urlFromPieces( 672 "redfish", "v1", "Chassis", chassisId, "Drives", 673 leafName); 674 members.push_back(std::move(member)); 675 // navigation links will be registered in next patch set 676 } 677 asyncResp->res.jsonValue["Members@odata.count"] = resp.size(); 678 }); // end association lambda 679 680 } // end Iterate over all retrieved ObjectPaths 681 }, 682 "xyz.openbmc_project.ObjectMapper", 683 "/xyz/openbmc_project/object_mapper", 684 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 685 "/xyz/openbmc_project/inventory", 0, 686 std::array<const char*, 2>{ 687 "xyz.openbmc_project.Inventory.Item.Board", 688 "xyz.openbmc_project.Inventory.Item.Chassis"}); 689 } 690 691 inline void requestRoutesChassisDrive(App& app) 692 { 693 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/") 694 .privileges(redfish::privileges::getDriveCollection) 695 .methods(boost::beast::http::verb::get)( 696 std::bind_front(chassisDriveCollectionGet, std::ref(app))); 697 } 698 699 void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 700 const std::string& chassisId, const std::string& driveName, 701 const boost::system::error_code ec, 702 const dbus::utility::MapperGetSubTreeResponse& subtree) 703 { 704 705 if (ec) 706 { 707 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 708 messages::internalError(asyncResp->res); 709 return; 710 } 711 712 // Iterate over all retrieved ObjectPaths. 713 for (const std::pair< 714 std::string, 715 std::vector<std::pair<std::string, std::vector<std::string>>>>& 716 object : subtree) 717 { 718 const std::string& path = object.first; 719 const std::vector<std::pair<std::string, std::vector<std::string>>>& 720 connectionNames = object.second; 721 722 sdbusplus::message::object_path objPath(path); 723 if (objPath.filename() != driveName) 724 { 725 continue; 726 } 727 728 if (connectionNames.empty()) 729 { 730 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 731 continue; 732 } 733 734 asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( 735 "redfish", "v1", "Chassis", "Drives", driveName); 736 737 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 738 asyncResp->res.jsonValue["Name"] = "Name"; 739 asyncResp->res.jsonValue["Id"] = driveName; 740 // default it to Enabled 741 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 742 743 nlohmann::json::object_t linkChassisNav; 744 linkChassisNav["@odata.id"] = 745 crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId); 746 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav; 747 748 addAllDriveInfo(asyncResp, connectionNames[0].first, path, 749 connectionNames[0].second); 750 } 751 } 752 753 void matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 754 const std::string& chassisId, 755 const std::string& driveName, 756 const std::vector<std::string>& resp) 757 { 758 759 for (const std::string& drivePath : resp) 760 { 761 sdbusplus::message::object_path path(drivePath); 762 std::string leaf = path.filename(); 763 if (leaf != driveName) 764 { 765 continue; 766 } 767 // mapper call drive 768 const std::array<const char*, 1> driveInterface = { 769 "xyz.openbmc_project.Inventory.Item.Drive"}; 770 771 crow::connections::systemBus->async_method_call( 772 [asyncResp, chassisId, driveName]( 773 const boost::system::error_code ec, 774 const dbus::utility::MapperGetSubTreeResponse& subtree) { 775 buildDrive(asyncResp, chassisId, driveName, ec, subtree); 776 }, 777 "xyz.openbmc_project.ObjectMapper", 778 "/xyz/openbmc_project/object_mapper", 779 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 780 "/xyz/openbmc_project/inventory", 0, driveInterface); 781 } 782 } 783 784 void handleChassisDriveGet(crow::App& app, const crow::Request& req, 785 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 786 const std::string& chassisId, 787 const std::string& driveName) 788 { 789 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 790 { 791 return; 792 } 793 const std::array<const char*, 2> interfaces = { 794 "xyz.openbmc_project.Inventory.Item.Board", 795 "xyz.openbmc_project.Inventory.Item.Chassis"}; 796 797 // mapper call chassis 798 crow::connections::systemBus->async_method_call( 799 [asyncResp, chassisId, 800 driveName](const boost::system::error_code ec, 801 const dbus::utility::MapperGetSubTreeResponse& subtree) { 802 if (ec) 803 { 804 messages::internalError(asyncResp->res); 805 return; 806 } 807 808 // Iterate over all retrieved ObjectPaths. 809 for (const std::pair< 810 std::string, 811 std::vector<std::pair<std::string, std::vector<std::string>>>>& 812 object : subtree) 813 { 814 const std::string& path = object.first; 815 const std::vector<std::pair<std::string, std::vector<std::string>>>& 816 connectionNames = object.second; 817 818 sdbusplus::message::object_path objPath(path); 819 if (objPath.filename() != chassisId) 820 { 821 continue; 822 } 823 824 if (connectionNames.empty()) 825 { 826 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 827 continue; 828 } 829 830 sdbusplus::asio::getProperty<std::vector<std::string>>( 831 *crow::connections::systemBus, 832 "xyz.openbmc_project.ObjectMapper", path + "/drive", 833 "xyz.openbmc_project.Association", "endpoints", 834 [asyncResp, chassisId, 835 driveName](const boost::system::error_code ec3, 836 const std::vector<std::string>& resp) { 837 if (ec3) 838 { 839 return; // no drives = no failures 840 } 841 matchAndFillDrive(asyncResp, chassisId, driveName, resp); 842 }); 843 break; 844 } 845 }, 846 "xyz.openbmc_project.ObjectMapper", 847 "/xyz/openbmc_project/object_mapper", 848 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 849 "/xyz/openbmc_project/inventory", 0, interfaces); 850 } 851 852 /** 853 * This URL will show the drive interface for the specific drive in the chassis 854 */ 855 inline void requestRoutesChassisDriveName(App& app) 856 { 857 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/") 858 .privileges(redfish::privileges::getChassis) 859 .methods(boost::beast::http::verb::get)( 860 std::bind_front(handleChassisDriveGet, std::ref(app))); 861 } 862 863 } // namespace redfish 864