blob: 9099b7cfc72dc76c3f69498a602efc6e63e014b5 [file]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_SYSTEM_UTILS_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_SYSTEM_UTILS_H_
#include <array>
#include <charconv>
#include <cstddef>
#include <memory>
#include <string>
#include <string_view>
#include <system_error> // NOLINT
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "boost/system/error_code.hpp" // NOLINT
#include "logging.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/message/native_types.hpp"
namespace redfish {
namespace system_utils {
constexpr std::array<std::string_view, 1> systemInterfaces{
{"xyz.openbmc_project.Inventory.Item.System"}};
/**
* @brief Checks whether single-system fallback logic should be triggered based on
* enumerated system D-Bus objects count.
*/
inline bool isHardcodedSingleHost(
[[maybe_unused]] const size_t systemObjectsCount) {
#ifdef BMCWEB_ALLOW_NON_HARDCODED_SINGLEHOST_SYSTEM
return false;
#else
return systemObjectsCount < 2;
#endif
}
template <typename Callback>
inline void findSystemPathAndTriggerCallback(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
const dbus::utility::MapperGetSubTreePathsResponse& objects,
Callback&& callback) {
if (isHardcodedSingleHost(objects.size())) {
if (systemName != "system") {
messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName);
return;
}
callback(asyncResp, "");
return;
}
// Otherwise find system
for (const auto& object : objects) {
std::string objectName = sdbusplus::message::object_path(object).filename();
// Found system object
if (systemName == objectName) {
callback(asyncResp, object);
return;
}
}
// Could not find system object
messages::resourceNotFound(asyncResp->res, "ComputerSystem", systemName);
}
/**
* @brief Retrieves valid system path
* @param asyncResp Pointer to object holding response data
* @param callback Callback for next step to get valid system path
* Callback should take in asyncResp pointer and full object path of Item.System
* Single Host Systems will return empty string as the object path of
* Item.System.
*/
template <typename Callback>
void getSystemInformation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, Callback&& callback) {
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTreePaths(
"/", 0, systemInterfaces, requestContext,
[callback{std::forward<Callback>(callback)}, asyncResp, systemName](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreePathsResponse& objects) {
if (ec) {
BMCWEB_LOG_ERROR << "DBUS response error " << ec;
messages::internalError(asyncResp->res);
return;
}
findSystemPathAndTriggerCallback(asyncResp, systemName, objects,
callback);
});
}
template <typename Callback>
void getAllSystems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Callback&& callback) {
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTreePaths(
"/", 0, systemInterfaces, requestContext,
[callback{std::forward<Callback>(callback)}, asyncResp](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreePathsResponse& objects) {
if (ec) {
BMCWEB_LOG_ERROR << "DBUS response error " << ec;
messages::internalError(asyncResp->res);
return;
}
callback(asyncResp, objects);
});
}
inline std::string getSystemName(const std::string& systemPath) {
// If the systemPath is empty, then the system is single host
bool multiHost = !systemPath.empty();
// Default systemName is system
if (!multiHost) {
return "system";
}
return sdbusplus::message::object_path(systemPath).filename();
}
/**
* @brief Parses a system name to determine its corresponding host index.
*
* This function validates and extracts a host index from a system name string,
* adjusting its behavior based on whether the system is in a multihost
* configuration.
*
* In a multihost configuration (`multihost` is `true`), this function expects
* the system name to be in the format "system<N>", where <N> is a 1-based
* integer (e.g., "system1", "system2").
*
* In a single-host configuration (`multihost` is `false`), if
* allow-non-hardcoded-singlehost-system is disabled, this function expects the system name to be
* exactly "system"; on success, the `index` parameter is set to `0`.
* If allow-non-hardcoded-singlehost-system is enabled, this function expects the system name to
* be "system" or "system<N>"; on success, the `index` parameter is set to the
* corresponding 0-based index (N-1) or 0 for "system".
*
* @param systemName The system name string to parse (e.g., "system1").
* @param multihost A flag indicating if the system is in a
* multihost environment.
* @return An `absl::StatusOr<size_t>` containing the parsed 0-based host index
* if successful, or an `absl::Status` with an error otherwise.
*/
inline absl::StatusOr<size_t> parseSystemNameToIndex(
const std::string_view systemName, const bool multihost) {
if (!multihost) {
// 0 if systemName is system in single host.
if (systemName == "system") {
return 0;
}
if (isHardcodedSingleHost(0)) {
return absl::NotFoundError(absl::StrCat(
"Invalid single node machine system name: ", systemName));
}
}
constexpr std::string_view prefix = "system";
if (systemName.size() < prefix.size() ||
systemName.substr(0, prefix.size()) != prefix) {
return absl::NotFoundError(
"system name must start with 'system' but got: " +
std::string(systemName));
}
std::string_view number_part = systemName.substr(prefix.length());
if (number_part.empty()) {
return absl::NotFoundError(
"system name must has index on multi-node machines.");
}
size_t number = 0;
const auto result = std::from_chars(
number_part.data(), number_part.data() + number_part.size(), number);
if (result.ec == std::errc() &&
result.ptr == number_part.data() + number_part.size()) {
if (number > 0) {
return number - 1; // System names are 1-based, index is 0-based
}
}
return absl::NotFoundError(
"system index parsing failed or number was not a positive integer.");
}
} // namespace system_utils
} // namespace redfish
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_SYSTEM_UTILS_H_