Multi-host support for PATCH routes in systems.hpp

Add support for multi-host PATCH 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:
PATCH route has been requested via curl manually both on multi-
and single-host for every property, that this patch implements.
After each request the expected change has been observed on dbus.

```
curl -k -X PATCH 'https://localhost:4443/redfish/v1/Systems/Yosemite_4_Sentinel_Dome_T1_Slot_1' \
        -H 'X-Auth-Token: '"$BMCWEB_SESSION_TOKEN"'' \
        -H "Content-Type: application/json" -d '{"Boot":{"BootSourceOverrideTarget": "Hdd"}}'

root@yosemite4:~# busctl introspect -l xyz.openbmc_project.Settings /xyz/openbmc_project/control/host1/boot
NAME                                    TYPE      SIGNATURE RESULT/VALUE                                           FLAGS
...
xyz.openbmc_project.Control.Boot.Source interface -         -                                                      -
.BootSource                             property  s         "xyz.openbmc_project.Control.Boot.Source.Sources.Disk" emits-change writable
...

```

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

Change-Id: Iaf9c1c01d44189707579b468f309577df3b6fad5
Signed-off-by: Oliver Brewka <oliver.brewka@9elements.com>
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index fd03018..10eee6d 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -1336,15 +1336,15 @@
 
 inline void setAutomaticRetryAttempts(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const uint32_t retryAttempts)
+    const uint64_t computerSystemIndex, const uint32_t retryAttempts)
 {
     BMCWEB_LOG_DEBUG("Set Automatic Retry Attempts.");
-    setDbusProperty(
-        asyncResp, "Boot/AutomaticRetryAttempts",
-        "xyz.openbmc_project.State.Host",
-        sdbusplus::message::object_path("/xyz/openbmc_project/state/host0"),
-        "xyz.openbmc_project.Control.Boot.RebootAttempts", "RetryAttempts",
-        retryAttempts);
+
+    setDbusProperty(asyncResp, "Boot/AutomaticRetryAttempts",
+                    getHostStateServiceName(computerSystemIndex),
+                    getHostStateObjectPath(computerSystemIndex),
+                    "xyz.openbmc_project.Control.Boot.RebootAttempts",
+                    "RetryAttempts", retryAttempts);
 }
 
 inline computer_system::PowerRestorePolicyTypes
@@ -1549,86 +1549,99 @@
                         computerSystemIndex));
 }
 
+inline void setTrustedModuleRequiredToBootCallback(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex, const bool tpmRequired,
+    const boost::system::error_code& ec,
+    const dbus::utility::MapperGetSubTreeResponse& subtree)
+{
+    if (ec)
+    {
+        BMCWEB_LOG_ERROR("DBUS response error on TPM.Policy GetSubTree{}", ec);
+        messages::internalError(asyncResp->res);
+        return;
+    }
+    if (subtree.empty())
+    {
+        messages::propertyValueNotInList(asyncResp->res, "ComputerSystem",
+                                         "TrustedModuleRequiredToBoot");
+        return;
+    }
+
+    std::string path;
+    std::string serv;
+
+    if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        if (!indexMatchingSubTreeMapObjectPath(asyncResp, computerSystemIndex,
+                                               subtree, path, serv))
+        {
+            BMCWEB_LOG_DEBUG("TPM.Policy mapper error!");
+            messages::internalError(asyncResp->res);
+            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;
+        serv = subtree[0].second.begin()->first;
+    }
+
+    if (serv.empty())
+    {
+        BMCWEB_LOG_DEBUG("TPM.Policy service mapper error!");
+        messages::internalError(asyncResp->res);
+        return;
+    }
+
+    // Valid TPM Enable object found, now setting the value
+    setDbusProperty(asyncResp, "Boot/TrustedModuleRequiredToBoot", serv, path,
+                    "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable",
+                    tpmRequired);
+}
+
 /**
  * @brief Set 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
  * @param[in] tpmRequired   Value to set TPM Required To Boot property to.
  *
  * @return None.
  */
 inline void setTrustedModuleRequiredToBoot(
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const bool tpmRequired)
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex, const bool tpmRequired)
 {
     BMCWEB_LOG_DEBUG("Set TrustedModuleRequiredToBoot.");
     constexpr std::array<std::string_view, 1> interfaces = {
         "xyz.openbmc_project.Control.TPM.Policy"};
     dbus::utility::getSubTree(
         "/", 0, interfaces,
-        [asyncResp,
-         tpmRequired](const boost::system::error_code& ec,
-                      const dbus::utility::MapperGetSubTreeResponse& subtree) {
-            if (ec)
-            {
-                BMCWEB_LOG_ERROR(
-                    "DBUS response error on TPM.Policy GetSubTree{}", ec);
-                messages::internalError(asyncResp->res);
-                return;
-            }
-            if (subtree.empty())
-            {
-                messages::propertyValueNotInList(asyncResp->res,
-                                                 "ComputerSystem",
-                                                 "TrustedModuleRequiredToBoot");
-                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;
-
-            if (serv.empty())
-            {
-                BMCWEB_LOG_DEBUG("TPM.Policy service mapper error!");
-                messages::internalError(asyncResp->res);
-                return;
-            }
-
-            // Valid TPM Enable object found, now setting the value
-            setDbusProperty(asyncResp, "Boot/TrustedModuleRequiredToBoot", serv,
-                            path, "xyz.openbmc_project.Control.TPM.Policy",
-                            "TPMEnable", tpmRequired);
-        });
+        std::bind_front(setTrustedModuleRequiredToBootCallback, asyncResp,
+                        computerSystemIndex, tpmRequired));
 }
 
 /**
  * @brief Sets boot properties into DBUS object(s).
  *
  * @param[in] asyncResp       Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  * @param[in] bootType        The boot type to set.
  * @return Integer error code.
  */
 inline void setBootType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                        const uint64_t computerSystemIndex,
                         const std::optional<std::string>& bootType)
 {
     std::string bootTypeStr;
@@ -1662,10 +1675,11 @@
     // Act on validated parameters
     BMCWEB_LOG_DEBUG("DBUS boot type: {}", bootTypeStr);
 
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
     setDbusProperty(asyncResp, "Boot/BootSourceOverrideMode",
-                    "xyz.openbmc_project.Settings",
-                    sdbusplus::message::object_path(
-                        "/xyz/openbmc_project/control/host0/boot"),
+                    "xyz.openbmc_project.Settings", path,
                     "xyz.openbmc_project.Control.Boot.Type", "BootType",
                     bootTypeStr);
 }
@@ -1675,10 +1689,12 @@
  *
  * @param[in] asyncResp           Shared pointer for generating response
  * message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  * @param[in] bootType        The boot type to set.
  * @return Integer error code.
  */
 inline void setBootEnable(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                          const uint64_t computerSystemIndex,
                           const std::optional<std::string>& bootEnable)
 {
     if (!bootEnable)
@@ -1718,10 +1734,11 @@
     // Act on validated parameters
     BMCWEB_LOG_DEBUG("DBUS boot override enable: {}", bootOverrideEnable);
 
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
     setDbusProperty(asyncResp, "Boot/BootSourceOverrideEnabled",
-                    "xyz.openbmc_project.Settings",
-                    sdbusplus::message::object_path(
-                        "/xyz/openbmc_project/control/host0/boot"),
+                    "xyz.openbmc_project.Settings", path,
                     "xyz.openbmc_project.Object.Enable", "Enabled",
                     bootOverrideEnable);
 
@@ -1735,10 +1752,9 @@
     BMCWEB_LOG_DEBUG("DBUS boot override persistent: {}",
                      bootOverridePersistent);
 
+    path /= "one_time";
     setDbusProperty(asyncResp, "Boot/BootSourceOverrideEnabled",
-                    "xyz.openbmc_project.Settings",
-                    sdbusplus::message::object_path(
-                        "/xyz/openbmc_project/control/host0/boot/one_time"),
+                    "xyz.openbmc_project.Settings", path,
                     "xyz.openbmc_project.Object.Enable", "Enabled",
                     !bootOverridePersistent);
 }
@@ -1747,12 +1763,14 @@
  * @brief Sets boot properties into DBUS object(s).
  *
  * @param[in] asyncResp       Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  * @param[in] bootSource      The boot source to set.
  *
  * @return Integer error code.
  */
 inline void setBootModeOrSource(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex,
     const std::optional<std::string>& bootSource)
 {
     std::string bootSourceStr;
@@ -1781,16 +1799,15 @@
     BMCWEB_LOG_DEBUG("DBUS boot source: {}", bootSourceStr);
     BMCWEB_LOG_DEBUG("DBUS boot mode: {}", bootModeStr);
 
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "boot";
     setDbusProperty(asyncResp, "Boot/BootSourceOverrideTarget",
-                    "xyz.openbmc_project.Settings",
-                    sdbusplus::message::object_path(
-                        "/xyz/openbmc_project/control/host0/boot"),
+                    "xyz.openbmc_project.Settings", path,
                     "xyz.openbmc_project.Control.Boot.Source", "BootSource",
                     bootSourceStr);
     setDbusProperty(asyncResp, "Boot/BootSourceOverrideTarget",
-                    "xyz.openbmc_project.Settings",
-                    sdbusplus::message::object_path(
-                        "/xyz/openbmc_project/control/host0/boot"),
+                    "xyz.openbmc_project.Settings", path,
                     "xyz.openbmc_project.Control.Boot.Mode", "BootMode",
                     bootModeStr);
 }
@@ -1799,6 +1816,7 @@
  * @brief Sets Boot source override properties.
  *
  * @param[in] asyncResp  Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  * @param[in] bootSource The boot source from incoming RF request.
  * @param[in] bootType   The boot type from incoming RF request.
  * @param[in] bootEnable The boot override enable from incoming RF request.
@@ -1808,15 +1826,16 @@
 
 inline void setBootProperties(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const uint64_t computerSystemIndex,
     const std::optional<std::string>& bootSource,
     const std::optional<std::string>& bootType,
     const std::optional<std::string>& bootEnable)
 {
     BMCWEB_LOG_DEBUG("Set boot information.");
 
-    setBootModeOrSource(asyncResp, bootSource);
-    setBootType(asyncResp, bootType);
-    setBootEnable(asyncResp, bootEnable);
+    setBootModeOrSource(asyncResp, computerSystemIndex, bootSource);
+    setBootType(asyncResp, computerSystemIndex, bootType);
+    setBootEnable(asyncResp, computerSystemIndex, bootEnable);
 }
 
 /**
@@ -1941,13 +1960,14 @@
  * @brief Sets automaticRetry (Auto Reboot)
  *
  * @param[in] asyncResp   Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  * @param[in] automaticRetryConfig  "AutomaticRetryConfig" from request.
  *
  * @return None.
  */
 inline void setAutomaticRetry(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const std::string& automaticRetryConfig)
+    const uint64_t computerSystemIndex, const std::string& automaticRetryConfig)
 {
     BMCWEB_LOG_DEBUG("Set Automatic Retry.");
 
@@ -1971,10 +1991,11 @@
         return;
     }
 
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "auto_reboot";
     setDbusProperty(asyncResp, "Boot/AutomaticRetryConfig",
-                    "xyz.openbmc_project.Settings",
-                    sdbusplus::message::object_path(
-                        "/xyz/openbmc_project/control/host0/auto_reboot"),
+                    "xyz.openbmc_project.Settings", path,
                     "xyz.openbmc_project.Control.Boot.RebootPolicy",
                     "AutoReboot", autoRebootEnabled);
 }
@@ -2000,13 +2021,14 @@
  * @brief Sets power restore policy properties.
  *
  * @param[in] asyncResp   Shared pointer for generating response message.
+ * @param[in] computerSystemIndex Index associated with the requested system
  * @param[in] policy  power restore policy properties from request.
  *
  * @return None.
  */
 inline void setPowerRestorePolicy(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    std::string_view policy)
+    const uint64_t computerSystemIndex, std::string_view policy)
 {
     BMCWEB_LOG_DEBUG("Set power restore policy.");
 
@@ -2019,12 +2041,13 @@
         return;
     }
 
-    setDbusProperty(
-        asyncResp, "PowerRestorePolicy", "xyz.openbmc_project.Settings",
-        sdbusplus::message::object_path(
-            "/xyz/openbmc_project/control/host0/power_restore_policy"),
-        "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy",
-        powerRestorePolicy);
+    sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" +
+                                         std::to_string(computerSystemIndex));
+    path /= "power_restore_policy";
+    setDbusProperty(asyncResp, "PowerRestorePolicy",
+                    "xyz.openbmc_project.Settings", path,
+                    "xyz.openbmc_project.Control.Power.RestorePolicy",
+                    "PowerRestorePolicy", powerRestorePolicy);
 }
 
 /**
@@ -3236,33 +3259,8 @@
         std::bind_front(processComputerSystemGet, asyncResp, systemName));
 }
 
-inline void handleComputerSystemPatch(
-    crow::App& app, const crow::Request& req,
-    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
-    const std::string& systemName)
+struct PatchParams
 {
-    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 (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");
-
     std::optional<bool> locationIndicatorActive;
     std::optional<std::string> indicatorLed;
     std::optional<std::string> assetTag;
@@ -3282,83 +3280,82 @@
     std::optional<uint64_t> ipsEnterTime;
     std::optional<uint8_t> ipsExitUtil;
     std::optional<uint64_t> ipsExitTime;
+};
 
-    if (!json_util::readJsonPatch(                                         //
-            req, asyncResp->res,                                           //
-            "AssetTag", assetTag,                                          //
-            "Boot/AutomaticRetryAttempts", bootAutomaticRetryAttempts,     //
-            "Boot/AutomaticRetryConfig", bootAutomaticRetry,               //
-            "Boot/BootSourceOverrideEnabled", bootEnable,                  //
-            "Boot/BootSourceOverrideMode", bootType,                       //
-            "Boot/BootSourceOverrideTarget", bootSource,                   //
-            "Boot/StopBootOnFault", stopBootOnFault,                       //
-            "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, //
-            "HostWatchdogTimer/FunctionEnabled", wdtEnable,                //
-            "HostWatchdogTimer/TimeoutAction", wdtTimeOutAction,           //
-            "IdlePowerSaver/Enabled", ipsEnable,                           //
-            "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime,          //
-            "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil,        //
-            "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime,            //
-            "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil,          //
-            "IndicatorLED", indicatorLed,                                  //
-            "LocationIndicatorActive", locationIndicatorActive,            //
-            "PowerMode", powerMode,                                        //
-            "PowerRestorePolicy", powerRestorePolicy                       //
-            ))
-    {
-        return;
-    }
+/**
+ * @brief process the POST request after getting the computerSystemIndex
+ *
+ * @param[in] asyncResp           Shared pointer for completing asynchronous
+ *                                calls
+ * @param[in] patchParams         Struct containing the property we want to
+ *                                patch
+ * @param[in] computerSystemIndex Index associated with the requested system
+ *
+ * @return None
+ */
 
+inline void processComputerSystemPatch(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    std::string& systemName, PatchParams& patchParams,
+    const uint64_t computerSystemIndex)
+{
     if constexpr (!BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED)
     {
-        if (indicatorLed)
+        if (patchParams.indicatorLed)
         {
             messages::propertyUnknown(asyncResp->res, "IndicatorLED");
             return;
         }
     }
 
-    if (assetTag)
+    if (patchParams.assetTag)
     {
-        setAssetTag(asyncResp, *assetTag);
+        setAssetTag(asyncResp, *patchParams.assetTag);
     }
 
-    if (wdtEnable || wdtTimeOutAction)
+    if (patchParams.wdtEnable || patchParams.wdtTimeOutAction)
     {
-        setWDTProperties(asyncResp, wdtEnable, wdtTimeOutAction);
+        setWDTProperties(asyncResp, patchParams.wdtEnable,
+                         patchParams.wdtTimeOutAction);
     }
 
-    if (bootSource || bootType || bootEnable)
+    if (patchParams.bootSource || patchParams.bootType ||
+        patchParams.bootEnable)
     {
-        setBootProperties(asyncResp, bootSource, bootType, bootEnable);
+        setBootProperties(asyncResp, computerSystemIndex,
+                          patchParams.bootSource, patchParams.bootType,
+                          patchParams.bootEnable);
     }
-    if (bootAutomaticRetry)
+    if (patchParams.bootAutomaticRetry)
     {
-        setAutomaticRetry(asyncResp, *bootAutomaticRetry);
+        setAutomaticRetry(asyncResp, computerSystemIndex,
+                          *patchParams.bootAutomaticRetry);
     }
 
-    if (bootAutomaticRetryAttempts)
+    if (patchParams.bootAutomaticRetryAttempts)
     {
-        setAutomaticRetryAttempts(asyncResp,
-                                  bootAutomaticRetryAttempts.value());
+        setAutomaticRetryAttempts(
+            asyncResp, computerSystemIndex,
+            patchParams.bootAutomaticRetryAttempts.value());
     }
 
-    if (bootTrustedModuleRequired)
+    if (patchParams.bootTrustedModuleRequired)
     {
-        setTrustedModuleRequiredToBoot(asyncResp, *bootTrustedModuleRequired);
+        setTrustedModuleRequiredToBoot(asyncResp, computerSystemIndex,
+                                       *patchParams.bootTrustedModuleRequired);
     }
 
-    if (stopBootOnFault)
+    if (patchParams.stopBootOnFault)
     {
-        setStopBootOnFault(asyncResp, *stopBootOnFault);
+        setStopBootOnFault(asyncResp, *patchParams.stopBootOnFault);
     }
 
-    if (locationIndicatorActive)
+    if (patchParams.locationIndicatorActive)
     {
         systems_utils::getValidSystemsPath(
             asyncResp, systemName,
             [asyncResp, systemName,
-             locationIndicatorActive{*locationIndicatorActive}](
+             locationIndicatorActive{*patchParams.locationIndicatorActive}](
                 const std::optional<std::string>& validSystemsPath) {
                 if (!validSystemsPath)
                 {
@@ -3373,32 +3370,95 @@
 
     if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED)
     {
-        if (indicatorLed)
+        if (patchParams.indicatorLed)
         {
-            setIndicatorLedState(asyncResp, *indicatorLed);
+            setIndicatorLedState(asyncResp, *patchParams.indicatorLed);
             asyncResp->res.addHeader(boost::beast::http::field::warning,
                                      "299 - \"IndicatorLED is deprecated. Use "
                                      "LocationIndicatorActive instead.\"");
         }
     }
 
-    if (powerRestorePolicy)
+    if (patchParams.powerRestorePolicy)
     {
-        setPowerRestorePolicy(asyncResp, *powerRestorePolicy);
+        setPowerRestorePolicy(asyncResp, computerSystemIndex,
+                              *patchParams.powerRestorePolicy);
     }
 
-    if (powerMode)
+    if (patchParams.powerMode)
     {
-        setPowerMode(asyncResp, *powerMode);
+        setPowerMode(asyncResp, *patchParams.powerMode);
     }
 
-    if (ipsEnable || ipsEnterUtil || ipsEnterTime || ipsExitUtil || ipsExitTime)
+    if (patchParams.ipsEnable || patchParams.ipsEnterUtil ||
+        patchParams.ipsEnterTime || patchParams.ipsExitUtil ||
+        patchParams.ipsExitTime)
     {
-        setIdlePowerSaver(asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime,
-                          ipsExitUtil, ipsExitTime);
+        setIdlePowerSaver(asyncResp, patchParams.ipsEnable,
+                          patchParams.ipsEnterUtil, patchParams.ipsEnterTime,
+                          patchParams.ipsExitUtil, patchParams.ipsExitTime);
     }
 }
 
+inline void handleComputerSystemPatch(
+    crow::App& app, const crow::Request& req,
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& systemName)
+{
+    PatchParams patchParams;
+
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+    {
+        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");
+
+    if (!json_util::readJsonPatch(
+            req, asyncResp->res,                                              //
+            "AssetTag", patchParams.assetTag,                                 //
+            "Boot/AutomaticRetryAttempts",
+            patchParams.bootAutomaticRetryAttempts,                           //
+            "Boot/AutomaticRetryConfig", patchParams.bootAutomaticRetry,      //
+            "Boot/BootSourceOverrideEnabled", patchParams.bootEnable,         //
+            "Boot/BootSourceOverrideMode", patchParams.bootType,              //
+            "Boot/BootSourceOverrideTarget", patchParams.bootSource,          //
+            "Boot/StopBootOnFault", patchParams.stopBootOnFault,              //
+            "Boot/TrustedModuleRequiredToBoot",
+            patchParams.bootTrustedModuleRequired,                            //
+            "HostWatchdogTimer/FunctionEnabled", patchParams.wdtEnable,       //
+            "HostWatchdogTimer/TimeoutAction", patchParams.wdtTimeOutAction,  //
+            "IdlePowerSaver/Enabled", patchParams.ipsEnable,                  //
+            "IdlePowerSaver/EnterDwellTimeSeconds", patchParams.ipsEnterTime, //
+            "IdlePowerSaver/EnterUtilizationPercent",
+            patchParams.ipsEnterUtil,                                         //
+            "IdlePowerSaver/ExitDwellTimeSeconds", patchParams.ipsExitTime,   //
+            "IdlePowerSaver/ExitUtilizationPercent", patchParams.ipsExitUtil, //
+            "IndicatorLED", patchParams.indicatorLed,                         //
+            "LocationIndicatorActive", patchParams.locationIndicatorActive,   //
+            "PowerMode", patchParams.powerMode,                               //
+            "PowerRestorePolicy", patchParams.powerRestorePolicy))
+    {
+        return;
+    }
+
+    getComputerSystemIndex(asyncResp, systemName,
+                           std::bind_front(processComputerSystemPatch,
+                                           asyncResp, systemName, patchParams));
+}
+
 inline void handleSystemCollectionResetActionHead(
     crow::App& app, const crow::Request& req,
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,