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 static void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 475 const std::string& connectionName, 476 const std::string& path, 477 const std::vector<std::string>& interfaces) 478 { 479 for (const std::string& interface : interfaces) 480 { 481 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") 482 { 483 getDriveAsset(asyncResp, connectionName, path); 484 } 485 else if (interface == "xyz.openbmc_project.Inventory.Item") 486 { 487 getDrivePresent(asyncResp, connectionName, path); 488 } 489 else if (interface == "xyz.openbmc_project.State.Drive") 490 { 491 getDriveState(asyncResp, connectionName, path); 492 } 493 else if (interface == "xyz.openbmc_project.Inventory.Item.Drive") 494 { 495 getDriveItemProperties(asyncResp, connectionName, path); 496 } 497 } 498 } 499 500 inline void requestRoutesDrive(App& app) 501 { 502 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/") 503 .privileges(redfish::privileges::getDrive) 504 .methods(boost::beast::http::verb::get)( 505 [&app](const crow::Request& req, 506 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 507 const std::string& driveId) { 508 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 509 { 510 return; 511 } 512 crow::connections::systemBus->async_method_call( 513 [asyncResp, 514 driveId](const boost::system::error_code ec, 515 const dbus::utility::MapperGetSubTreeResponse& subtree) { 516 if (ec) 517 { 518 BMCWEB_LOG_ERROR << "Drive mapper call error"; 519 messages::internalError(asyncResp->res); 520 return; 521 } 522 523 auto drive = std::find_if( 524 subtree.begin(), subtree.end(), 525 [&driveId]( 526 const std::pair< 527 std::string, 528 std::vector<std::pair< 529 std::string, std::vector<std::string>>>>& object) { 530 return sdbusplus::message::object_path(object.first) 531 .filename() == driveId; 532 }); 533 534 if (drive == subtree.end()) 535 { 536 messages::resourceNotFound(asyncResp->res, "Drive", driveId); 537 return; 538 } 539 540 const std::string& path = drive->first; 541 const std::vector<std::pair<std::string, std::vector<std::string>>>& 542 connectionNames = drive->second; 543 544 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 545 asyncResp->res.jsonValue["@odata.id"] = 546 "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId; 547 asyncResp->res.jsonValue["Name"] = driveId; 548 asyncResp->res.jsonValue["Id"] = driveId; 549 550 if (connectionNames.size() != 1) 551 { 552 BMCWEB_LOG_ERROR << "Connection size " << connectionNames.size() 553 << ", not equal to 1"; 554 messages::internalError(asyncResp->res); 555 return; 556 } 557 558 getMainChassisId( 559 asyncResp, [](const std::string& chassisId, 560 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 561 aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] = 562 "/redfish/v1/Chassis/" + chassisId; 563 }); 564 565 // default it to Enabled 566 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 567 568 auto health = std::make_shared<HealthPopulate>(asyncResp); 569 health->inventory.emplace_back(path); 570 health->populate(); 571 572 addAllDriveInfo(asyncResp, connectionNames[0].first, path, 573 connectionNames[0].second); 574 }, 575 "xyz.openbmc_project.ObjectMapper", 576 "/xyz/openbmc_project/object_mapper", 577 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 578 "/xyz/openbmc_project/inventory", int32_t(0), 579 std::array<const char*, 1>{ 580 "xyz.openbmc_project.Inventory.Item.Drive"}); 581 }); 582 } 583 584 /** 585 * Chassis drives, this URL will show all the DriveCollection 586 * information 587 */ 588 inline void chassisDriveCollectionGet( 589 crow::App& app, const crow::Request& req, 590 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 591 const std::string& chassisId) 592 { 593 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 594 { 595 return; 596 } 597 598 // mapper call lambda 599 crow::connections::systemBus->async_method_call( 600 [asyncResp, 601 chassisId](const boost::system::error_code ec, 602 const dbus::utility::MapperGetSubTreeResponse& subtree) { 603 if (ec) 604 { 605 if (ec == boost::system::errc::host_unreachable) 606 { 607 messages::resourceNotFound(asyncResp->res, "Chassis", 608 chassisId); 609 return; 610 } 611 messages::internalError(asyncResp->res); 612 return; 613 } 614 615 // Iterate over all retrieved ObjectPaths. 616 for (const std::pair< 617 std::string, 618 std::vector<std::pair<std::string, std::vector<std::string>>>>& 619 object : subtree) 620 { 621 const std::string& path = object.first; 622 const dbus::utility::MapperGetObject& connectionNames = 623 object.second; 624 625 sdbusplus::message::object_path objPath(path); 626 if (objPath.filename() != chassisId) 627 { 628 continue; 629 } 630 631 if (connectionNames.empty()) 632 { 633 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 634 continue; 635 } 636 637 asyncResp->res.jsonValue["@odata.type"] = 638 "#DriveCollection.DriveCollection"; 639 asyncResp->res.jsonValue["@odata.id"] = 640 crow::utility::urlFromPieces("redfish", "v1", "Chassis", 641 chassisId, "Drives"); 642 asyncResp->res.jsonValue["Name"] = "Drive Collection"; 643 644 // Association lambda 645 sdbusplus::asio::getProperty<std::vector<std::string>>( 646 *crow::connections::systemBus, 647 "xyz.openbmc_project.ObjectMapper", path + "/drive", 648 "xyz.openbmc_project.Association", "endpoints", 649 [asyncResp, chassisId](const boost::system::error_code ec3, 650 const std::vector<std::string>& resp) { 651 if (ec3) 652 { 653 BMCWEB_LOG_ERROR << "Error in chassis Drive association "; 654 } 655 nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 656 // important if array is empty 657 members = nlohmann::json::array(); 658 659 std::vector<std::string> leafNames; 660 for (const auto& drive : resp) 661 { 662 sdbusplus::message::object_path path(drive); 663 leafNames.push_back(path.filename()); 664 } 665 666 std::sort(leafNames.begin(), leafNames.end(), 667 AlphanumLess<std::string>()); 668 669 for (const auto& leafName : leafNames) 670 { 671 nlohmann::json::object_t member; 672 member["@odata.id"] = crow::utility::urlFromPieces( 673 "redfish", "v1", "Chassis", chassisId, "Drives", 674 leafName); 675 members.push_back(std::move(member)); 676 // navigation links will be registered in next patch set 677 } 678 asyncResp->res.jsonValue["Members@odata.count"] = resp.size(); 679 }); // end association lambda 680 681 } // end Iterate over all retrieved ObjectPaths 682 }, 683 "xyz.openbmc_project.ObjectMapper", 684 "/xyz/openbmc_project/object_mapper", 685 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 686 "/xyz/openbmc_project/inventory", 0, 687 std::array<const char*, 2>{ 688 "xyz.openbmc_project.Inventory.Item.Board", 689 "xyz.openbmc_project.Inventory.Item.Chassis"}); 690 } 691 692 inline void requestRoutesChassisDrive(App& app) 693 { 694 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/") 695 .privileges(redfish::privileges::getDriveCollection) 696 .methods(boost::beast::http::verb::get)( 697 std::bind_front(chassisDriveCollectionGet, std::ref(app))); 698 } 699 700 inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 701 const std::string& chassisId, 702 const std::string& driveName, 703 const boost::system::error_code ec, 704 const dbus::utility::MapperGetSubTreeResponse& subtree) 705 { 706 707 if (ec) 708 { 709 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 710 messages::internalError(asyncResp->res); 711 return; 712 } 713 714 // Iterate over all retrieved ObjectPaths. 715 for (const std::pair< 716 std::string, 717 std::vector<std::pair<std::string, std::vector<std::string>>>>& 718 object : subtree) 719 { 720 const std::string& path = object.first; 721 const std::vector<std::pair<std::string, std::vector<std::string>>>& 722 connectionNames = object.second; 723 724 sdbusplus::message::object_path objPath(path); 725 if (objPath.filename() != driveName) 726 { 727 continue; 728 } 729 730 if (connectionNames.empty()) 731 { 732 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 733 continue; 734 } 735 736 asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( 737 "redfish", "v1", "Chassis", "Drives", driveName); 738 739 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 740 asyncResp->res.jsonValue["Name"] = "Name"; 741 asyncResp->res.jsonValue["Id"] = driveName; 742 // default it to Enabled 743 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 744 745 nlohmann::json::object_t linkChassisNav; 746 linkChassisNav["@odata.id"] = 747 crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId); 748 asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav; 749 750 addAllDriveInfo(asyncResp, connectionNames[0].first, path, 751 connectionNames[0].second); 752 } 753 } 754 755 inline void 756 matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 757 const std::string& chassisId, 758 const std::string& driveName, 759 const std::vector<std::string>& resp) 760 { 761 762 for (const std::string& drivePath : resp) 763 { 764 sdbusplus::message::object_path path(drivePath); 765 std::string leaf = path.filename(); 766 if (leaf != driveName) 767 { 768 continue; 769 } 770 // mapper call drive 771 const std::array<const char*, 1> driveInterface = { 772 "xyz.openbmc_project.Inventory.Item.Drive"}; 773 774 crow::connections::systemBus->async_method_call( 775 [asyncResp, chassisId, driveName]( 776 const boost::system::error_code ec, 777 const dbus::utility::MapperGetSubTreeResponse& subtree) { 778 buildDrive(asyncResp, chassisId, driveName, ec, subtree); 779 }, 780 "xyz.openbmc_project.ObjectMapper", 781 "/xyz/openbmc_project/object_mapper", 782 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 783 "/xyz/openbmc_project/inventory", 0, driveInterface); 784 } 785 } 786 787 inline void 788 handleChassisDriveGet(crow::App& app, const crow::Request& req, 789 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 790 const std::string& chassisId, 791 const std::string& driveName) 792 { 793 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 794 { 795 return; 796 } 797 const std::array<const char*, 2> interfaces = { 798 "xyz.openbmc_project.Inventory.Item.Board", 799 "xyz.openbmc_project.Inventory.Item.Chassis"}; 800 801 // mapper call chassis 802 crow::connections::systemBus->async_method_call( 803 [asyncResp, chassisId, 804 driveName](const boost::system::error_code ec, 805 const dbus::utility::MapperGetSubTreeResponse& subtree) { 806 if (ec) 807 { 808 messages::internalError(asyncResp->res); 809 return; 810 } 811 812 // Iterate over all retrieved ObjectPaths. 813 for (const std::pair< 814 std::string, 815 std::vector<std::pair<std::string, std::vector<std::string>>>>& 816 object : subtree) 817 { 818 const std::string& path = object.first; 819 const std::vector<std::pair<std::string, std::vector<std::string>>>& 820 connectionNames = object.second; 821 822 sdbusplus::message::object_path objPath(path); 823 if (objPath.filename() != chassisId) 824 { 825 continue; 826 } 827 828 if (connectionNames.empty()) 829 { 830 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 831 continue; 832 } 833 834 sdbusplus::asio::getProperty<std::vector<std::string>>( 835 *crow::connections::systemBus, 836 "xyz.openbmc_project.ObjectMapper", path + "/drive", 837 "xyz.openbmc_project.Association", "endpoints", 838 [asyncResp, chassisId, 839 driveName](const boost::system::error_code ec3, 840 const std::vector<std::string>& resp) { 841 if (ec3) 842 { 843 return; // no drives = no failures 844 } 845 matchAndFillDrive(asyncResp, chassisId, driveName, resp); 846 }); 847 break; 848 } 849 }, 850 "xyz.openbmc_project.ObjectMapper", 851 "/xyz/openbmc_project/object_mapper", 852 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 853 "/xyz/openbmc_project/inventory", 0, interfaces); 854 } 855 856 /** 857 * This URL will show the drive interface for the specific drive in the chassis 858 */ 859 inline void requestRoutesChassisDriveName(App& app) 860 { 861 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/") 862 .privileges(redfish::privileges::getChassis) 863 .methods(boost::beast::http::verb::get)( 864 std::bind_front(handleChassisDriveGet, std::ref(app))); 865 } 866 867 } // namespace redfish 868