blob: 5a9eba4147279f781cac6f414cb59aa81e1b0a4a [file] [log] [blame]
#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