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