blob: 62a4b95bf952943f1ac64569fd7edec82d7d2cd0 [file] [log] [blame]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_CHASSIS_UTILS_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_CHASSIS_UTILS_H_
#include <array>
#include <string_view>
#include <utility>
#include <memory>
#include <string>
#include <optional>
#include <vector>
#include <algorithm>
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "logging.hpp"
#include "error_messages.hpp"
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/message/native_types.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
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_CHASSIS_UTILS_H_