| #pragma once |
| |
| #include "async_resp.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "managed_store.hpp" |
| |
| #include <array> |
| #include <string_view> |
| |
| #include "managed_store.hpp" |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| namespace redfish |
| { |
| |
| namespace chassis_utils |
| { |
| |
| constexpr std::array<std::string_view, 2> chassisInterfaces = { |
| "xyz.openbmc_project.Inventory.Item.Board", |
| "xyz.openbmc_project.Inventory.Item.Chassis"}; |
| |
| /** |
| * @brief Retrieves valid chassis path |
| * @param asyncResp Pointer to object holding response data |
| * @param callback Callback for next step to get valid chassis path |
| */ |
| template <typename Callback> |
| void getValidChassisPath(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& chassisId, Callback&& callback) |
| { |
| BMCWEB_LOG_DEBUG << "checkChassisId enter"; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| // Get the Chassis Collection |
| managedStore::GetManagedObjectStore()->getSubTreePaths( |
| "/xyz/openbmc_project/inventory", 0, chassisInterfaces, requestContext, |
| [callback{std::forward<Callback>(callback)}, asyncResp, |
| chassisId](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& |
| chassisPaths) mutable { |
| BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter"; |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "getValidChassisPath respHandler DBUS error: " |
| << ec; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| std::optional<std::string> chassisPath; |
| for (const std::string& chassis : chassisPaths) |
| { |
| sdbusplus::message::object_path path(chassis); |
| std::string chassisName = path.filename(); |
| if (chassisName.empty()) |
| { |
| BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis; |
| continue; |
| } |
| if (chassisName == chassisId) |
| { |
| chassisPath = chassis; |
| break; |
| } |
| } |
| callback(chassisPath); |
| }); |
| BMCWEB_LOG_DEBUG << "checkChassisId exit"; |
| } |
| |
| /** |
| * @brief Get subtree for associated endpoint with chassis interfaces. |
| * @param asyncResp Pointer to object holding response data |
| * @param associationPath Path used to associate with chassis endpoints |
| * @param callback Callback to process subtree response |
| */ |
| template <typename Callback> |
| inline void getAssociatedChassisSubtree( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& associationPath, Callback&& callback) |
| { |
| sdbusplus::message::object_path association(associationPath); |
| sdbusplus::message::object_path root("/xyz/openbmc_project/inventory"); |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getAssociatedSubTree( |
| association, root, 0, chassisInterfaces, requestContext, |
| [asyncResp, associationPath, |
| callback{std::forward<Callback>(callback)}]( |
| const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "Failed to get chassis associations for " |
| << associationPath << " ec: " << ec.message(); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| callback(asyncResp, subtree); |
| }); |
| } |
| |
| /** |
| * @brief Recursively get upstream Chassis object path to find root chassis. |
| * |
| * @param[in,out] visitedPaths Set of chassis object paths queried. |
| * @param[in,out] asyncResp Pointer to object holding response data |
| * @param[in] subtreePaths Subtree associated with chassis endpoint |
| * @param callback Callback to invoke with root chassis object path. |
| */ |
| template <typename Callback> |
| inline void getRootChassisPath( |
| std::vector<std::string> visitedPaths, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths, |
| Callback&& callback) |
| { |
| // Base case as we recurse to get upstream chassis association. |
| if (subtreePaths.empty()) |
| { |
| const std::string &rootChassisPath = visitedPaths.back(); |
| BMCWEB_LOG_DEBUG << "Root chassis path found " << rootChassisPath; |
| callback(rootChassisPath); |
| return; |
| } |
| |
| if (subtreePaths.size() > 1) |
| { |
| BMCWEB_LOG_ERROR << "Found multiple upstream chassis."; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| // Upstream chassis. |
| const std::string& chassisPath = subtreePaths[0]; |
| if (find(visitedPaths.begin(), visitedPaths.end(), chassisPath) != visitedPaths.end()) |
| { |
| BMCWEB_LOG_ERROR << "Loop detected in upstream chassis associations."; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| visitedPaths.push_back(chassisPath); |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| sdbusplus::message::object_path association(chassisPath + "/contained_by"); |
| sdbusplus::message::object_path root("/xyz/openbmc_project/inventory"); |
| managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths( |
| association, root, 0, chassisInterfaces, requestContext, |
| [requestContext, visitedPaths, asyncResp, association, |
| callback{std::forward<Callback>(callback)}]( |
| const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "Can't get chassis associations for " |
| << association.str << " ec: " << ec.message(); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| getRootChassisPath(visitedPaths, asyncResp, subtreePaths, callback); |
| }); |
| } |
| |
| } // namespace chassis_utils |
| } // namespace redfish |