|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include "bmcweb_config.h" | 
|  |  | 
|  | #include "error_messages.hpp" | 
|  | #include "managed_store_types.hpp" | 
|  |  | 
|  | // TODO(wltu): Move to a different file until this is fully cleaned up. | 
|  | #include <sdbusplus/asio/property.hpp> | 
|  |  | 
|  | #include "managed_store.hpp" | 
|  | #include "utility.hpp" | 
|  | #include "utils/dbus_utils.hpp" | 
|  |  | 
|  | #ifdef UNIT_TEST_BUILD | 
|  | #include "test/g3/mock_managed_store.hpp" // NOLINT | 
|  | #endif | 
|  | namespace redfish | 
|  | { | 
|  | inline static void | 
|  | getRelatedItemsDrive(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | 
|  | const sdbusplus::message::object_path& objPath) | 
|  | { | 
|  | // Drive is expected to be under a Chassis | 
|  | constexpr std::array<std::string_view, 2> interfaces = { | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis"}; | 
|  | managedStore::ManagedObjectStoreContext requestContext(aResp); | 
|  | managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( | 
|  | objPath / "chassis", | 
|  | sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, | 
|  | interfaces, requestContext, | 
|  | [aResp, objPath]( | 
|  | const boost::system::error_code& ec, | 
|  | const dbus::utility::MapperGetSubTreePathsResponse& chassisList) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG | 
|  | << "Failed to call getProperty for Drive -> Chassis Association: " | 
|  | << ec; | 
|  | messages::internalError(aResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (chassisList.empty()) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "Can not find the Chassis containing the drive"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (chassisList.size() != 1) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "Resource can only be included in one storage"; | 
|  | messages::internalError(aResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | nlohmann::json::array_t relatedItems; | 
|  | nlohmann::json::object_t relatedItem; | 
|  | relatedItem["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Chassis", | 
|  | sdbusplus::message::object_path(chassisList[0]).filename(), | 
|  | "Drives", objPath.filename()); | 
|  | relatedItems.emplace_back(std::move(relatedItem)); | 
|  | aResp->res.jsonValue["RelatedItem@odata.count"] = relatedItems.size(); | 
|  | aResp->res.jsonValue["RelatedItem"] = std::move(relatedItems); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline static void getRelatedItemsStorageController( | 
|  | const std::shared_ptr<bmcweb::AsyncResp>& aResp, | 
|  | const sdbusplus::message::object_path& objPath) | 
|  | { | 
|  | constexpr std::array<std::string_view, 1> interfaces = { | 
|  | "xyz.openbmc_project.Inventory.Item.Storage"}; | 
|  | managedStore::ManagedObjectStoreContext requestContext(aResp); | 
|  | managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( | 
|  | objPath / "storage", | 
|  | sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, | 
|  | interfaces, requestContext, | 
|  | [aResp, objPath, | 
|  | requestContext](const boost::system::error_code ec, | 
|  | const std::vector<std::string>& storageList) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG | 
|  | << "Failed to call getProperty for Storage Association: " << ec; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (storageList.size() != 1) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "Resource can only be included in one storage"; | 
|  | messages::internalError(aResp->res); | 
|  | return; | 
|  | } | 
|  |  | 
|  | sdbusplus::message::object_path storage(storageList[0]); | 
|  |  | 
|  | constexpr std::array<std::string_view, 1> storageControllerInterfaces = | 
|  | {"xyz.openbmc_project.Inventory.Item.StorageController"}; | 
|  | managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( | 
|  | storage / "storage_controller", | 
|  | sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), | 
|  | 0, storageControllerInterfaces, requestContext, | 
|  | [aResp, storage, | 
|  | objPath](const boost::system::error_code ec2, | 
|  | const std::vector<std::string>& storageControllerList) { | 
|  | if (ec2) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG | 
|  | << "Failed to call getProperty for Storage -> StorageController Association: " | 
|  | << ec2; | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (const std::string& storageController : storageControllerList) | 
|  | { | 
|  | const std::string& id = | 
|  | sdbusplus::message::object_path(storageController) | 
|  | .filename(); | 
|  | if (id.empty()) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "filename() is empty in " | 
|  | << storageController; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (storageController != objPath.str) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | nlohmann::json::array_t relatedItems; | 
|  | nlohmann::json::object_t relatedItem; | 
|  | relatedItem["@odata.id"] = crow::utility::urlFromPieces( | 
|  | "redfish", "v1", "Systems", "system", "Storage", | 
|  | storage.filename(), "Controllers", id); | 
|  | relatedItems.emplace_back(std::move(relatedItem)); | 
|  | aResp->res.jsonValue["RelatedItem@odata.count"] = | 
|  | relatedItems.size(); | 
|  | aResp->res.jsonValue["RelatedItem"] = std::move(relatedItems); | 
|  |  | 
|  | break; | 
|  | } | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | inline static void | 
|  | getRelatedItemsOther(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | 
|  | const sdbusplus::message::object_path& association) | 
|  | { | 
|  | constexpr std::array<std::string_view, 6> inventoryInterfaces = { | 
|  | "xyz.openbmc_project.Inventory.Item.Accelerator", | 
|  | "xyz.openbmc_project.Inventory.Item.Cpu", | 
|  | "xyz.openbmc_project.Inventory.Item.Drive", | 
|  | "xyz.openbmc_project.Inventory.Item.Board", | 
|  | "xyz.openbmc_project.Inventory.Item.Chassis", | 
|  | "xyz.openbmc_project.Inventory.Item.StorageController"}; | 
|  |  | 
|  | // Find supported device types. | 
|  | managedStore::ManagedObjectStoreContext context(aResp); | 
|  | managedStore::GetManagedObjectStore()->getDbusObject( | 
|  | association.str, inventoryInterfaces, context, | 
|  | [aResp, association]( | 
|  | const boost::system::error_code ec, | 
|  | const std::vector<std::pair<std::string, std::vector<std::string>>>& | 
|  | objects) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_ERROR << "error_code = " << ec | 
|  | << ", error msg = " << ec.message(); | 
|  | return; | 
|  | } | 
|  | if (objects.empty()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | nlohmann::json& relatedItem = aResp->res.jsonValue["RelatedItem"]; | 
|  | nlohmann::json& relatedItemCount = | 
|  | aResp->res.jsonValue["RelatedItem@odata.count"]; | 
|  |  | 
|  | for (const std::pair<std::string, std::vector<std::string>>& object : | 
|  | objects) | 
|  | { | 
|  | for (const std::string& interfaces : object.second) | 
|  | { | 
|  | if (interfaces == "xyz.openbmc_project.Inventory.Item.Drive") | 
|  | { | 
|  | getRelatedItemsDrive(aResp, association); | 
|  | } | 
|  |  | 
|  | if (interfaces == | 
|  | "xyz.openbmc_project.Inventory.Item.Accelerator" || | 
|  | interfaces == "xyz.openbmc_project.Inventory.Item.Cpu") | 
|  | { | 
|  | relatedItem.emplace_back( | 
|  | nlohmann::json({{"@odata.id", | 
|  | "/redfish/v1/Systems/system/Processors/" + | 
|  | association.filename()}})); | 
|  | } | 
|  |  | 
|  | if (interfaces == "xyz.openbmc_project.Inventory.Item.Board" || | 
|  | interfaces == "xyz.openbmc_project.Inventory.Item.Chassis") | 
|  | { | 
|  | relatedItem.emplace_back( | 
|  | nlohmann::json({{"@odata.id", | 
|  | "/redfish/v1/Chassis/" + association.filename()}})); | 
|  | } | 
|  |  | 
|  | if (interfaces == "xyz.openbmc_project.Inventory." | 
|  | "Item.StorageController") | 
|  | { | 
|  | getRelatedItemsStorageController(aResp, association); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | relatedItemCount = relatedItem.size(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | /* | 
|  | Fill related item links for Software with other purposes. | 
|  | Use other purpose for device level softwares. | 
|  | */ | 
|  | inline static void | 
|  | getRelatedItemsOthers(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | 
|  | std::string_view path) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "getRelatedItemsOthers enter"; | 
|  |  | 
|  | aResp->res.jsonValue["RelatedItem"] = nlohmann::json::array(); | 
|  | aResp->res.jsonValue["RelatedItem@odata.count"] = 0; | 
|  |  | 
|  | BMCWEB_LOG_DEBUG << "GetReltatedItemsOthers for " << std::string(path) | 
|  | << "/software_related"; | 
|  |  | 
|  | managedStore::ManagedObjectStoreContext requestContext(aResp); | 
|  | dbus_utils::getProperty<std::vector<std::string>>( | 
|  | "xyz.openbmc_project.ObjectMapper", | 
|  | std::string(path) + "/software_related", | 
|  | "xyz.openbmc_project.Association", "endpoints", requestContext, | 
|  | [aResp](const boost::system::error_code ec, | 
|  | const std::vector<std::string>& relatedList) { | 
|  | if (ec) | 
|  | { | 
|  | BMCWEB_LOG_DEBUG << "Failed to call dbus for getLogEntry"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (const auto& item : relatedList) | 
|  | { | 
|  | sdbusplus::message::object_path itemPath(item); | 
|  | if (itemPath.filename().empty()) | 
|  | { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | getRelatedItemsOther(aResp, itemPath); | 
|  | } | 
|  | }); | 
|  | } | 
|  | } // namespace redfish |