Multi-host helper functions

The change implements the getComputerSystemIndex function which
leverages the xyz.openbmc_project.ManagedHost interface to retrieve the
index corresponding to the host the request was made for, when
on a multi-host machine. On single-host the index always defaults
to 0. It is not the perfect solution, but it is a starting point
to introduce multi-host support to bmcweb, allowing for power control,
via redfish and in general something we can build from.
More efficient ways of doing things are already discussed,
but will need more time to design properly.

The implementation relies on the
experimental-redfish-multi-computer-system meson flag to give bmcweb an
indication, whether it is running on single- or multi-host and to drop
unsupported redfish resources for the time being.

Multi-host meson options needed:
-Dexperimental-redfish-multi-computer-system=enabled
-Dredfish-system-uri-name=

These helper functions are needed for the multi-host patches further
down the patch chain.

Tested: All relevant tests have been made in the following patches.

Change-Id: Ie481aa8b05903dab59f39a1134463ac0c54aa781
Signed-off-by: Oliver Brewka <oliver.brewka@9elements.com>
diff --git a/redfish-core/include/utils/systems_utils.hpp b/redfish-core/include/utils/systems_utils.hpp
index e376f0f..4f61490 100644
--- a/redfish-core/include/utils/systems_utils.hpp
+++ b/redfish-core/include/utils/systems_utils.hpp
@@ -5,6 +5,7 @@
 #include "bmcweb_config.h"
 
 #include "async_resp.hpp"
+#include "dbus_singleton.hpp"
 #include "dbus_utility.hpp"
 #include "error_messages.hpp"
 #include "human_sort.hpp"
@@ -17,6 +18,7 @@
 
 #include <algorithm>
 #include <array>
+#include <cstdint>
 #include <functional>
 #include <memory>
 #include <string>
@@ -116,4 +118,144 @@
         "/xyz/openbmc_project/inventory", 0, interfaces,
         std::bind_front(handleSystemCollectionMembers, asyncResp));
 }
+
+inline void getManagedHostProperty(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName, const std::string& systemPath,
+    std::function<void(const uint64_t computerSystemIndex)> callback)
+{
+    dbus::utility::getProperty<uint64_t>(
+        "xyz.openbmc_project.EntityManager", systemPath,
+        "xyz.openbmc_project.Inventory.Decorator.ManagedHost", "HostIndex",
+        [asyncResp, systemName, systemPath, callback = std::move(callback)](
+            const boost::system::error_code& ec, const uint64_t hostIndex) {
+            if (ec)
+            {
+                BMCWEB_LOG_WARNING("DBUS response error {}", ec);
+                messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                           systemName);
+                return;
+            }
+            BMCWEB_LOG_DEBUG("Got index {} for path {}", hostIndex, systemPath);
+            callback(hostIndex);
+        });
+}
+
+inline void afterGetComputerSystemSubTreePaths(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName,
+    std::function<void(const uint64_t computerSystemIndex)>& callback,
+    const boost::system::error_code& ec,
+    const dbus::utility::MapperGetSubTreePathsResponse& objects)
+{
+    sdbusplus::message::object_path systemPath;
+    if (ec)
+    {
+        if (ec.value() == boost::system::errc::io_error)
+        {
+            BMCWEB_LOG_WARNING("EIO - System not found");
+            messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                       systemName);
+            return;
+        }
+
+        BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
+        messages::internalError(asyncResp->res);
+        return;
+    }
+
+    const auto& found = std::ranges::find_if(
+        objects, [systemName](const sdbusplus::message::object_path& path) {
+            return path.filename() == systemName;
+        });
+
+    if (found == objects.end())
+    {
+        BMCWEB_LOG_WARNING("Failed to match systemName: {}", systemName);
+        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                   systemName);
+        return;
+    }
+
+    systemPath = *found;
+
+    getManagedHostProperty(asyncResp, systemName, systemPath,
+                           std::move(callback));
+}
+
+/**
+ * @brief Retrieve the index associated with the requested system based on the
+ * ManagedHost interface
+ *
+ * @param[i] asyncResp  Async response object
+ * @param[i] systemName The requested system
+ * @param[i] callback   Callback to call once the index has been found
+ *
+ * @return None
+ */
+inline void getComputerSystemIndex(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName,
+    std::function<void(const uint64_t computerSystemIndex)>&& callback)
+{
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        constexpr std::array<std::string_view, 1> interfaces{
+            "xyz.openbmc_project.Inventory.Decorator.ManagedHost"};
+        dbus::utility::getSubTreePaths(
+            "/xyz/openbmc_project/inventory", 0, interfaces,
+            std::bind_front(afterGetComputerSystemSubTreePaths, asyncResp,
+                            systemName, std::move(callback)));
+    }
+    else
+    {
+        // on single-host, fallback to index 0
+        BMCWEB_LOG_DEBUG(
+            "Single-host detected, fallback to computerSystemIndex 0");
+        callback(0);
+    }
+}
+
+inline sdbusplus::message::object_path getHostStateObjectPath(
+    const uint64_t computerSystemIndex)
+{
+    const sdbusplus::message::object_path hostStatePath(
+        "/xyz/openbmc_project/state/host" +
+        std::to_string(computerSystemIndex));
+
+    return hostStatePath;
+}
+
+inline std::string getHostStateServiceName(const uint64_t computerSystemIndex)
+{
+    std::string hostStateService = "xyz.openbmc_project.State.Host";
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        hostStateService += std::to_string(computerSystemIndex);
+    }
+
+    return hostStateService;
+}
+
+inline sdbusplus::message::object_path getChassisStateObjectPath(
+    const uint64_t computerSystemIndex)
+{
+    const sdbusplus::message::object_path chassisStatePath(
+        "/xyz/openbmc_project/state/chassis" +
+        std::to_string(computerSystemIndex));
+
+    return chassisStatePath;
+}
+
+inline std::string getChassisStateServiceName(
+    const uint64_t computerSystemIndex)
+{
+    std::string chassisStateService = "xyz.openbmc_project.State.Chassis";
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        chassisStateService += std::to_string(computerSystemIndex);
+    }
+
+    return chassisStateService;
+}
 } // namespace redfish