| #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_ |