Multi-host support for GET routes in systems.hpp

Add support for multi-host GET request-handling under the
/redfish/v1/Systems/{computerSystemId}/ redfish resource.

All multi-host supported redfish URIs can be found in this listing [1].

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

Tested: Validator passes on single-host machine and yv4 qemu emulation.

[1] https://gerrit.openbmc.org/c/openbmc/bmcweb/+/76118

Change-Id: I67c17c3dd7a354fa9a2ebbc56d4def7a7e788909
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 2d510d5..97afc0c 100644
--- a/redfish-core/include/utils/systems_utils.hpp
+++ b/redfish-core/include/utils/systems_utils.hpp
@@ -316,4 +316,57 @@
 }
 
 } // namespace systems_utils
+
+/**
+ * @brief Match computerSystemIndex with index contained by an object path
+ *        i.e 1 in /xyz/openbmc/project/control/host1/policy/TPMEnable
+ *
+ * @param[i] asyncResp           Shared pointer for generating response
+ * @param[i] computerSystemIndex The index to match against
+ * @param[i] subtree             Mapper response object
+ * @param[o] objectPath          Buffer for matched object path
+ * @param[o] service             Buffer for service of matched object
+ *                               path
+ *
+ * @return true if match found, else false
+ */
+inline bool indexMatchingSubTreeMapObjectPath(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex,
+    const dbus::utility::MapperGetSubTreeResponse& subtree,
+    std::string& objectPath, std::string& service)
+{
+    const std::string host = std::format("host{}", computerSystemIndex);
+
+    for (const auto& obj : subtree)
+    {
+        std::string tmp = host;
+        const sdbusplus::message::object_path path{obj.first};
+        const std::string serv = obj.second.begin()->first;
+
+        if (path.str.empty() || obj.second.size() != 1)
+        {
+            BMCWEB_LOG_DEBUG("Error finding index in object path");
+            messages::internalError(asyncResp->res);
+            return false;
+        }
+
+        objectPath = path;
+        service = serv;
+
+        if (path.filename() == host)
+        {
+            return true;
+        }
+
+        tmp.insert(0, 1, '/');
+        tmp.append("/");
+        if (path.str.find(host) != std::string::npos)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
 } // namespace redfish
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index 31cf897..fd03018 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -526,14 +526,18 @@
  * @brief Retrieves host state properties over dbus
  *
  * @param[in] asyncResp     Shared pointer for completing asynchronous calls.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
-inline void getHostState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+inline void getHostState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                         const uint64_t computerSystemIndex)
 {
     BMCWEB_LOG_DEBUG("Get host information.");
+
     dbus::utility::getProperty<std::string>(
-        "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
+        getHostStateServiceName(computerSystemIndex),
+        getHostStateObjectPath(computerSystemIndex),
         "xyz.openbmc_project.State.Host", "CurrentHostState",
         [asyncResp](const boost::system::error_code& ec,
                     const std::string& hostState) {
@@ -827,13 +831,16 @@
  * @brief Retrieves boot progress of the system
  *
  * @param[in] asyncResp  Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
-inline void getBootProgress(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+inline void getBootProgress(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                            const uint64_t computerSystemIndex)
 {
     dbus::utility::getProperty<std::string>(
-        "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
+        getHostStateServiceName(computerSystemIndex),
+        getHostStateObjectPath(computerSystemIndex),
         "xyz.openbmc_project.State.Boot.Progress", "BootProgress",
         [asyncResp](const boost::system::error_code& ec,
                     const std::string& bootProgressStr) {
@@ -855,14 +862,17 @@
  * @brief Retrieves boot progress Last Update of the system
  *
  * @param[in] asyncResp  Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
 inline void getBootProgressLastStateTime(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
     dbus::utility::getProperty<uint64_t>(
-        "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
+        getHostStateServiceName(computerSystemIndex),
+        getHostStateObjectPath(computerSystemIndex),
         "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate",
         [asyncResp](const boost::system::error_code& ec,
                     const uint64_t lastStateTime) {
@@ -888,16 +898,20 @@
  * @brief Retrieves boot override type over DBUS and fills out the response
  *
  * @param[in] asyncResp         Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
-
 inline void getBootOverrideType(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
+
     dbus::utility::getProperty<std::string>(
-        "xyz.openbmc_project.Settings",
-        "/xyz/openbmc_project/control/host0/boot",
+        "xyz.openbmc_project.Settings", path,
         "xyz.openbmc_project.Control.Boot.Type", "BootType",
         [asyncResp](const boost::system::error_code& ec,
                     const std::string& bootType) {
@@ -929,16 +943,19 @@
  * @brief Retrieves boot override mode over DBUS and fills out the response
  *
  * @param[in] asyncResp         Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
-
 inline void getBootOverrideMode(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
     dbus::utility::getProperty<std::string>(
-        "xyz.openbmc_project.Settings",
-        "/xyz/openbmc_project/control/host0/boot",
+        "xyz.openbmc_project.Settings", path,
         "xyz.openbmc_project.Control.Boot.Mode", "BootMode",
         [asyncResp](const boost::system::error_code& ec,
                     const std::string& bootModeStr) {
@@ -981,19 +998,23 @@
  * @brief Retrieves boot override source over DBUS
  *
  * @param[in] asyncResp         Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
-
 inline void getBootOverrideSource(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
+
     dbus::utility::getProperty<std::string>(
-        "xyz.openbmc_project.Settings",
-        "/xyz/openbmc_project/control/host0/boot",
+        "xyz.openbmc_project.Settings", path,
         "xyz.openbmc_project.Control.Boot.Source", "BootSource",
-        [asyncResp](const boost::system::error_code& ec,
-                    const std::string& bootSourceStr) {
+        [asyncResp, computerSystemIndex](const boost::system::error_code& ec,
+                                         const std::string& bootSourceStr) {
             if (ec)
             {
                 // Service not available, no error, just don't return
@@ -1018,7 +1039,7 @@
 
             // Get BootMode as BootSourceOverrideTarget is constructed
             // from both BootSource and BootMode
-            getBootOverrideMode(asyncResp);
+            getBootOverrideMode(asyncResp, computerSystemIndex);
         });
 }
 
@@ -1028,13 +1049,15 @@
  * state
  *
  * @param[in] asyncResp     Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
+ * @param[in] bootOverrideEnableSetting
  *
  * @return None.
  */
 
 inline void processBootOverrideEnable(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const bool bootOverrideEnableSetting)
+    const uint64_t computerSystemIndex, const bool bootOverrideEnableSetting)
 {
     if (!bootOverrideEnableSetting)
     {
@@ -1043,11 +1066,15 @@
         return;
     }
 
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
+    path /= "one_time";
+
     // If boot source override is enabled, we need to check 'one_time'
     // property to set a correct value for the "BootSourceOverrideEnabled"
     dbus::utility::getProperty<bool>(
-        "xyz.openbmc_project.Settings",
-        "/xyz/openbmc_project/control/host0/boot/one_time",
+        "xyz.openbmc_project.Settings", path,
         "xyz.openbmc_project.Object.Enable", "Enabled",
         [asyncResp](const boost::system::error_code& ec, bool oneTimeSetting) {
             if (ec)
@@ -1074,19 +1101,23 @@
  * @brief Retrieves boot override enable over DBUS
  *
  * @param[in] asyncResp     Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
-
 inline void getBootOverrideEnable(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
+
     dbus::utility::getProperty<bool>(
-        "xyz.openbmc_project.Settings",
-        "/xyz/openbmc_project/control/host0/boot",
+        "xyz.openbmc_project.Settings", path,
         "xyz.openbmc_project.Object.Enable", "Enabled",
-        [asyncResp](const boost::system::error_code& ec,
-                    const bool bootOverrideEnable) {
+        [asyncResp, computerSystemIndex](const boost::system::error_code& ec,
+                                         const bool bootOverrideEnable) {
             if (ec)
             {
                 // Service not available, no error, just don't return
@@ -1100,7 +1131,8 @@
                 return;
             }
 
-            processBootOverrideEnable(asyncResp, bootOverrideEnable);
+            processBootOverrideEnable(asyncResp, computerSystemIndex,
+                                      bootOverrideEnable);
         });
 }
 
@@ -1108,17 +1140,19 @@
  * @brief Retrieves boot source override properties
  *
  * @param[in] asyncResp     Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
 inline void getBootProperties(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
     BMCWEB_LOG_DEBUG("Get boot information.");
 
-    getBootOverrideSource(asyncResp);
-    getBootOverrideType(asyncResp);
-    getBootOverrideEnable(asyncResp);
+    getBootOverrideSource(asyncResp, computerSystemIndex);
+    getBootOverrideType(asyncResp, computerSystemIndex);
+    getBootOverrideEnable(asyncResp, computerSystemIndex);
 }
 
 /**
@@ -1134,13 +1168,14 @@
  * @return None.
  */
 inline void getLastResetTime(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
     BMCWEB_LOG_DEBUG("Getting System Last Reset Time");
 
     dbus::utility::getProperty<uint64_t>(
-        "xyz.openbmc_project.State.Chassis",
-        "/xyz/openbmc_project/state/chassis0",
+        getChassisStateServiceName(computerSystemIndex),
+        getChassisStateObjectPath(computerSystemIndex),
         "xyz.openbmc_project.State.Chassis", "LastStateChangeTime",
         [asyncResp](const boost::system::error_code& ec,
                     uint64_t lastResetTime) {
@@ -1169,16 +1204,19 @@
  * dbus.
  *
  * @param[in] asyncResp     Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
 inline void getAutomaticRebootAttempts(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
     BMCWEB_LOG_DEBUG("Get Automatic Retry policy");
 
     dbus::utility::getAllProperties(
-        "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
+        getHostStateServiceName(computerSystemIndex),
+        getHostStateObjectPath(computerSystemIndex),
         "xyz.openbmc_project.Control.Boot.RebootAttempts",
         [asyncResp{asyncResp}](
             const boost::system::error_code& ec,
@@ -1228,20 +1266,25 @@
  * @brief Retrieves Automatic Retry properties. Known on D-Bus as AutoReboot.
  *
  * @param[in] asyncResp     Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
 inline void getAutomaticRetryPolicy(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
     BMCWEB_LOG_DEBUG("Get Automatic Retry policy");
 
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "auto_reboot";
+
     dbus::utility::getProperty<bool>(
-        "xyz.openbmc_project.Settings",
-        "/xyz/openbmc_project/control/host0/auto_reboot",
+        "xyz.openbmc_project.Settings", path,
         "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot",
-        [asyncResp](const boost::system::error_code& ec,
-                    bool autoRebootEnabled) {
+        [asyncResp, computerSystemIndex](const boost::system::error_code& ec,
+                                         bool autoRebootEnabled) {
             if (ec)
             {
                 // Service not available, no error, just don't return
@@ -1266,7 +1309,7 @@
                 asyncResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] =
                     "Disabled";
             }
-            getAutomaticRebootAttempts(asyncResp);
+            getAutomaticRebootAttempts(asyncResp, computerSystemIndex);
 
             // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways,
             // and RetryAttempts. OpenBMC only supports Disabled and
@@ -1285,6 +1328,7 @@
  * @brief Sets RetryAttempts
  *
  * @param[in] asyncResp   Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  * @param[in] retryAttempts  "AutomaticRetryAttempts" from request.
  *
  *@return None.
@@ -1335,13 +1379,17 @@
  * @return None.
  */
 inline void getPowerRestorePolicy(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
     BMCWEB_LOG_DEBUG("Get power restore policy");
 
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "power_restore_policy";
+
     dbus::utility::getProperty<std::string>(
-        "xyz.openbmc_project.Settings",
-        "/xyz/openbmc_project/control/host0/power_restore_policy",
+        "xyz.openbmc_project.Settings", path,
         "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy",
         [asyncResp](const boost::system::error_code& ec,
                     const std::string& policy) {
@@ -1404,90 +1452,101 @@
         });
 }
 
+inline void getTrustedModuleRequiredToBootCallback(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex, const boost::system::error_code& ec,
+    const dbus::utility::MapperGetSubTreeResponse& subtree)
+{
+    if (ec)
+    {
+        BMCWEB_LOG_DEBUG("DBUS response error on TPM.Policy GetSubTree{}", ec);
+        // This is an optional D-Bus object so just return if
+        // error occurs
+        return;
+    }
+    if (subtree.empty())
+    {
+        // As noted above, this is an optional interface so just return
+        // if there is no instance found
+        return;
+    }
+
+    std::string path;
+    std::string service;
+
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        if (!indexMatchingSubTreeMapObjectPath(asyncResp, computerSystemIndex,
+                                               subtree, path, service))
+        {
+            return;
+        }
+    }
+    else
+    {
+        // Make sure the Dbus response map has a service and objectPath
+        // field
+        if (subtree[0].first.empty() || subtree[0].second.size() != 1)
+        {
+            BMCWEB_LOG_DEBUG("TPM.Policy mapper error!");
+            messages::internalError(asyncResp->res);
+            return;
+        }
+
+        path = subtree[0].first;
+        service = subtree[0].second.begin()->first;
+    }
+
+    BMCWEB_LOG_DEBUG("found tpm service {}", service);
+    BMCWEB_LOG_DEBUG("found tpm path {}", path);
+
+    // Valid TPM Enable object found, now reading the current value
+    dbus::utility::getProperty<bool>(
+        service, path, "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable",
+        [asyncResp](const boost::system::error_code& ec2, bool tpmRequired) {
+            if (ec2)
+            {
+                BMCWEB_LOG_ERROR("D-BUS response error on TPM.Policy Get{}",
+                                 ec2);
+                messages::internalError(asyncResp->res);
+                return;
+            }
+
+            if (tpmRequired)
+            {
+                asyncResp->res
+                    .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] =
+                    "Required";
+            }
+            else
+            {
+                asyncResp->res
+                    .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] =
+                    "Disabled";
+            }
+        });
+}
+
 /**
  * @brief Get TrustedModuleRequiredToBoot property. Determines whether or not
  * TPM is required for booting the host.
  *
  * @param[in] asyncResp     Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  *
  * @return None.
  */
 inline void getTrustedModuleRequiredToBoot(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
 {
     BMCWEB_LOG_DEBUG("Get TPM required to boot.");
     constexpr std::array<std::string_view, 1> interfaces = {
         "xyz.openbmc_project.Control.TPM.Policy"};
     dbus::utility::getSubTree(
         "/", 0, interfaces,
-        [asyncResp](const boost::system::error_code& ec,
-                    const dbus::utility::MapperGetSubTreeResponse& subtree) {
-            if (ec)
-            {
-                BMCWEB_LOG_DEBUG(
-                    "DBUS response error on TPM.Policy GetSubTree{}", ec);
-                // This is an optional D-Bus object so just return if
-                // error occurs
-                return;
-            }
-            if (subtree.empty())
-            {
-                // As noted above, this is an optional interface so just return
-                // if there is no instance found
-                return;
-            }
-
-            /* When there is more than one TPMEnable object... */
-            if (subtree.size() > 1)
-            {
-                BMCWEB_LOG_DEBUG(
-                    "DBUS response has more than 1 TPM Enable object:{}",
-                    subtree.size());
-                // Throw an internal Error and return
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            // Make sure the Dbus response map has a service and objectPath
-            // field
-            if (subtree[0].first.empty() || subtree[0].second.size() != 1)
-            {
-                BMCWEB_LOG_DEBUG("TPM.Policy mapper error!");
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            const std::string& path = subtree[0].first;
-            const std::string& serv = subtree[0].second.begin()->first;
-
-            // Valid TPM Enable object found, now reading the current value
-            dbus::utility::getProperty<bool>(
-                serv, path, "xyz.openbmc_project.Control.TPM.Policy",
-                "TPMEnable",
-                [asyncResp](const boost::system::error_code& ec2,
-                            bool tpmRequired) {
-                    if (ec2)
-                    {
-                        BMCWEB_LOG_ERROR(
-                            "D-BUS response error on TPM.Policy Get{}", ec2);
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-
-                    if (tpmRequired)
-                    {
-                        asyncResp->res
-                            .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] =
-                            "Required";
-                    }
-                    else
-                    {
-                        asyncResp->res
-                            .jsonValue["Boot"]["TrustedModuleRequiredToBoot"] =
-                            "Disabled";
-                    }
-                });
-        });
+        std::bind_front(getTrustedModuleRequiredToBootCallback, asyncResp,
+                        computerSystemIndex));
 }
 
 /**
@@ -2585,7 +2644,7 @@
 }
 
 /**
- * @brief Retrieves host watchdog timer properties over DBUS
+ * @brief Retrieves idle power saver properties over DBUS
  *
  * @param[in] asyncResp     Shared pointer for completing asynchronous calls.
  *
@@ -2622,9 +2681,9 @@
             {
                 // More then one PowerIdlePowerSaver object is not supported and
                 // is an error
-                BMCWEB_LOG_DEBUG("Found more than 1 system D-Bus "
-                                 "Power.IdlePowerSaver objects: {}",
-                                 subtree.size());
+                BMCWEB_LOG_DEBUG(
+                    "Found more than 1 system D-Bus Power.IdlePowerSaver objects: {}",
+                    subtree.size());
                 messages::internalError(asyncResp->res);
                 return;
             }
@@ -3002,78 +3061,63 @@
     }
 }
 
-inline void handleComputerSystemGet(
-    crow::App& app, const crow::Request& req,
+/**
+ * @brief process the GET request after getting the computerSystemIndex
+ *
+ * @param[in] asyncResp           Shared pointer for completing asynchronous
+ *                                calls
+ * @param[in] systemName          Name of the requested system
+ * @param[in] computerSystemIndex Index associated with the requested system
+ *
+ * @return None
+ */
+inline void processComputerSystemGet(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const std::string& systemName)
+    const std::string& systemName, const uint64_t computerSystemIndex)
 {
-    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-    {
-        return;
-    }
-
-    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-    {
-        // Option currently returns no systems.  TBD
-        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                   systemName);
-        return;
-    }
-
-    if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM)
-    {
-        if (systemName == "hypervisor")
-        {
-            handleHypervisorSystemGet(asyncResp);
-            return;
-        }
-    }
-
-    if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
-    {
-        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                   systemName);
-        return;
-    }
     asyncResp->res.addHeader(
         boost::beast::http::field::link,
         "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby");
     asyncResp->res.jsonValue["@odata.type"] =
         "#ComputerSystem.v1_22_0.ComputerSystem";
-    asyncResp->res.jsonValue["Name"] = BMCWEB_REDFISH_SYSTEM_URI_NAME;
-    asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_SYSTEM_URI_NAME;
+    asyncResp->res.jsonValue["Name"] = systemName;
+    asyncResp->res.jsonValue["Id"] = systemName;
     asyncResp->res.jsonValue["SystemType"] =
         computer_system::SystemType::Physical;
     asyncResp->res.jsonValue["Description"] = "Computer System";
     asyncResp->res.jsonValue["ProcessorSummary"]["Count"] = 0;
     asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] =
         double(0);
-    asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME);
+    asyncResp->res.jsonValue["@odata.id"] =
+        boost::urls::format("/redfish/v1/Systems/{}", systemName);
 
-    asyncResp->res.jsonValue["Processors"]["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Systems/{}/Processors", BMCWEB_REDFISH_SYSTEM_URI_NAME);
-    asyncResp->res.jsonValue["Memory"]["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Systems/{}/Memory", BMCWEB_REDFISH_SYSTEM_URI_NAME);
-    asyncResp->res.jsonValue["Storage"]["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME);
-    asyncResp->res.jsonValue["FabricAdapters"]["@odata.id"] =
-        boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters",
-                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
+    // Currently not supported on multi-host. TBD
+    if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        asyncResp->res.jsonValue["Bios"]["@odata.id"] =
+            boost::urls::format("/redfish/v1/Systems/{}/Bios", systemName);
+        asyncResp->res.jsonValue["Processors"]["@odata.id"] =
+            boost::urls::format("/redfish/v1/Systems/{}/Processors",
+                                systemName);
+        asyncResp->res.jsonValue["Memory"]["@odata.id"] =
+            boost::urls::format("/redfish/v1/Systems/{}/Memory", systemName);
+        asyncResp->res.jsonValue["Storage"]["@odata.id"] =
+            boost::urls::format("/redfish/v1/Systems/{}/Storage", systemName);
+        asyncResp->res.jsonValue["FabricAdapters"]["@odata.id"] =
+            boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters",
+                                systemName);
+    }
 
     asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]["target"] =
         boost::urls::format(
-            "/redfish/v1/Systems/{}/Actions/ComputerSystem.Reset",
-            BMCWEB_REDFISH_SYSTEM_URI_NAME);
+            "/redfish/v1/Systems/{}/Actions/ComputerSystem.Reset", systemName);
     asyncResp->res
         .jsonValue["Actions"]["#ComputerSystem.Reset"]["@Redfish.ActionInfo"] =
         boost::urls::format("/redfish/v1/Systems/{}/ResetActionInfo",
-                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
+                            systemName);
 
-    asyncResp->res.jsonValue["LogServices"]["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Systems/{}/LogServices", BMCWEB_REDFISH_SYSTEM_URI_NAME);
-    asyncResp->res.jsonValue["Bios"]["@odata.id"] = boost::urls::format(
-        "/redfish/v1/Systems/{}/Bios", BMCWEB_REDFISH_SYSTEM_URI_NAME);
+    asyncResp->res.jsonValue["LogServices"]["@odata.id"] =
+        boost::urls::format("/redfish/v1/Systems/{}/LogServices", systemName);
 
     nlohmann::json::array_t managedBy;
     nlohmann::json& manager = managedBy.emplace_back();
@@ -3104,16 +3148,6 @@
             nlohmann::json::array_t({"KVMIP"});
     }
 
-    getMainChassisId(
-        asyncResp, [](const std::string& chassisId,
-                      const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
-            nlohmann::json::array_t chassisArray;
-            nlohmann::json& chassis = chassisArray.emplace_back();
-            chassis["@odata.id"] =
-                boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
-            aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray);
-        });
-
     systems_utils::getValidSystemsPath(
         asyncResp, systemName,
         [asyncResp,
@@ -3129,27 +3163,79 @@
         getIndicatorLedState(asyncResp);
     }
 
-    getComputerSystem(asyncResp);
-    getHostState(asyncResp);
-    getBootProperties(asyncResp);
-    getBootProgress(asyncResp);
-    getBootProgressLastStateTime(asyncResp);
-    pcie_util::getPCIeDeviceList(asyncResp,
-                                 nlohmann::json::json_pointer("/PCIeDevices"));
+    // Currently not supported on multi-host.
+    if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        getComputerSystem(asyncResp);
+        // Todo: chassis matching could be handled by patch
+        // https://gerrit.openbmc.org/c/openbmc/bmcweb/+/60793
+        getMainChassisId(
+            asyncResp, [](const std::string& chassisId,
+                          const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
+                nlohmann::json::array_t chassisArray;
+                nlohmann::json& chassis = chassisArray.emplace_back();
+                chassis["@odata.id"] =
+                    boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
+                aRsp->res.jsonValue["Links"]["Chassis"] =
+                    std::move(chassisArray);
+            });
+
+        pcie_util::getPCIeDeviceList(
+            asyncResp, nlohmann::json::json_pointer("/PCIeDevices"));
+    }
+    getHostState(asyncResp, computerSystemIndex);
+    getBootProperties(asyncResp, computerSystemIndex);
+    getBootProgress(asyncResp, computerSystemIndex);
+    getBootProgressLastStateTime(asyncResp, computerSystemIndex);
     getHostWatchdogTimer(asyncResp);
-    getPowerRestorePolicy(asyncResp);
+    getPowerRestorePolicy(asyncResp, computerSystemIndex);
     getStopBootOnFault(asyncResp);
-    getAutomaticRetryPolicy(asyncResp);
-    getLastResetTime(asyncResp);
+    getAutomaticRetryPolicy(asyncResp, computerSystemIndex);
+    getLastResetTime(asyncResp, computerSystemIndex);
     if constexpr (BMCWEB_REDFISH_PROVISIONING_FEATURE)
     {
         getProvisioningStatus(asyncResp);
     }
-    getTrustedModuleRequiredToBoot(asyncResp);
+    getTrustedModuleRequiredToBoot(asyncResp, computerSystemIndex);
     getPowerMode(asyncResp);
     getIdlePowerSaver(asyncResp);
 }
 
+inline void handleComputerSystemGet(
+    crow::App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM)
+    {
+        if (systemName == "hypervisor")
+        {
+            handleHypervisorSystemGet(asyncResp);
+            return;
+        }
+    }
+
+    if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+        {
+            messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                       systemName);
+            return;
+        }
+    }
+
+    BMCWEB_LOG_DEBUG("requested system = {}", systemName);
+    getComputerSystemIndex(
+        asyncResp, systemName,
+        std::bind_front(processComputerSystemGet, asyncResp, systemName));
+}
+
 inline void handleComputerSystemPatch(
     crow::App& app, const crow::Request& req,
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
@@ -3419,6 +3505,17 @@
     asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
 }
 
+inline void getAllowedHostTransitions(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex)
+{
+    dbus::utility::getProperty<std::vector<std::string>>(
+        getHostStateServiceName(computerSystemIndex),
+        getHostStateObjectPath(computerSystemIndex),
+        "xyz.openbmc_project.State.Host", "AllowedHostTransitions",
+        std::bind_front(afterGetAllowedHostTransitions, asyncResp));
+}
+
 inline void handleSystemCollectionResetActionGet(
     crow::App& app, const crow::Request& req,
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
@@ -3428,13 +3525,6 @@
     {
         return;
     }
-    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
-    {
-        // Option currently returns no systems.  TBD
-        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                   systemName);
-        return;
-    }
 
     if constexpr (BMCWEB_HYPERVISOR_COMPUTER_SYSTEM)
     {
@@ -3445,34 +3535,32 @@
         }
     }
 
-    if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+    if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
     {
-        messages::resourceNotFound(asyncResp->res, "ComputerSystem",
-                                   systemName);
-        return;
+        if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+        {
+            messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+                                       systemName);
+            return;
+        }
     }
 
     asyncResp->res.addHeader(
         boost::beast::http::field::link,
         "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby");
 
-    asyncResp->res.jsonValue["@odata.id"] =
-        boost::urls::format("/redfish/v1/Systems/{}/ResetActionInfo",
-                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
+    asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
+        "/redfish/v1/Systems/{}/ResetActionInfo", systemName);
     asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
     asyncResp->res.jsonValue["Name"] = "Reset Action Info";
     asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
 
     // Look to see if system defines AllowedHostTransitions
-    dbus::utility::getProperty<std::vector<std::string>>(
-        "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
-        "xyz.openbmc_project.State.Host", "AllowedHostTransitions",
-        [asyncResp](const boost::system::error_code& ec,
-                    const std::vector<std::string>& allowedHostTransitions) {
-            afterGetAllowedHostTransitions(asyncResp, ec,
-                                           allowedHostTransitions);
-        });
+    getComputerSystemIndex(
+        asyncResp, systemName,
+        std::bind_front(getAllowedHostTransitions, asyncResp));
 }
+
 /**
  * SystemResetActionInfo derived class for delivering Computer Systems
  * ResetType AllowableValues using ResetInfo schema.
diff --git a/test/meson.build b/test/meson.build
index 09b29d3..f66bc52 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -42,6 +42,7 @@
     'redfish-core/include/utils/query_param_test.cpp',
     'redfish-core/include/utils/sensor_utils_test.cpp',
     'redfish-core/include/utils/stl_utils_test.cpp',
+    'redfish-core/include/utils/systems_utils_test.cpp',
     'redfish-core/include/utils/time_utils_test.cpp',
     'redfish-core/lib/chassis_test.cpp',
     'redfish-core/lib/ethernet_test.cpp',
diff --git a/test/redfish-core/include/utils/systems_utils_test.cpp b/test/redfish-core/include/utils/systems_utils_test.cpp
new file mode 100644
index 0000000..b2fceba
--- /dev/null
+++ b/test/redfish-core/include/utils/systems_utils_test.cpp
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: Copyright OpenBMC Authors
+
+#include "dbus_utility.hpp"
+#include "utils/systems_utils.hpp"
+
+#include <boost/beast/http/status.hpp>
+#include <boost/system/errc.hpp>
+#include <nlohmann/json.hpp>
+#include <sdbusplus/message.hpp>
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+namespace redfish
+{
+namespace
+{
+
+TEST(SystemsUtils, IndexMatchingObjectPath)
+{
+    auto asyncResp = std::make_shared<bmcweb::AsyncResp>();
+
+    std::string objectPath;
+    std::string service;
+
+    const dbus::utility::MapperGetSubTreeResponse subtree = {
+        {"/xyz/openbmc_project/control/host1",
+         {{"xyz.openbmc_project.Settings", {}}}},
+        {"/xyz/openbmc_project/control/host2/policy/TPMEnable",
+         {{"xyz.openbmc_project.Settings", {}}}},
+        {"/xyz/openbmc_project/control/host10/policy/TPMEnable",
+         {{"xyz.openbmc_project.Settings", {}}}},
+        {"/xyz/openbmc_project/control/host999/",
+         {{"xyz.openbmc_project.Settings", {}}}}};
+
+    EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 1, subtree,
+                                                  objectPath, service));
+    EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 2, subtree,
+                                                  objectPath, service));
+    EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 10, subtree,
+                                                  objectPath, service));
+    EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 999, subtree,
+                                                  objectPath, service));
+    EXPECT_FALSE(indexMatchingSubTreeMapObjectPath(asyncResp, 100, subtree,
+                                                   objectPath, service));
+    EXPECT_FALSE(indexMatchingSubTreeMapObjectPath(asyncResp, 11, subtree,
+                                                   objectPath, service));
+    EXPECT_FALSE(indexMatchingSubTreeMapObjectPath(asyncResp, 0, subtree,
+                                                   objectPath, service));
+
+    indexMatchingSubTreeMapObjectPath(asyncResp, 1, subtree, objectPath,
+                                      service);
+    EXPECT_EQ(objectPath, "/xyz/openbmc_project/control/host1");
+    EXPECT_EQ(service, "xyz.openbmc_project.Settings");
+
+    indexMatchingSubTreeMapObjectPath(asyncResp, 10, subtree, objectPath,
+                                      service);
+    EXPECT_EQ(objectPath,
+              "/xyz/openbmc_project/control/host10/policy/TPMEnable");
+
+    indexMatchingSubTreeMapObjectPath(asyncResp, 999, subtree, objectPath,
+                                      service);
+    EXPECT_EQ(objectPath, "/xyz/openbmc_project/control/host999/");
+}
+} // namespace
+} // namespace redfish