Systems: Add PowerMode support
The computer system power mode defines the behavior of a system
based on the performance and power saving requirements.
For example, a system could be set to MaximumPerformance to
run as fast as possible without regard to power consumption.
A system could also be configured to run in PowerSaving mode
which would be running at slower speeds to try to save power.
More information can be found at https://gerrit.openbmc-project.xyz/c/openbmc/docs/+/38786
This commit will allow GET / PATCH operations to the PowerMode property
PowerMode was added in Redfish Release 2021.1:
https://www.dmtf.org/content/redfish-release-20211-now-available
Tested: I manually tested retrieving and setting the PowerMode property
on a Rainier system (with good and bad values):
# curl -k https://$bmc/redfish/v1/Systems/system
{
"@odata.id": "/redfish/v1/Systems/system",
"@odata.type": "#ComputerSystem.v1_15_0.ComputerSystem
...
"PartNumber": "",
"PowerMode": "MaximumPerformance",
"PowerMode@Redfish.AllowableValues": [
"Static",
"MaximumPerformance",
"PowerSaving"
],
"PowerRestorePolicy": "AlwaysOff",
...
# curl -k https://$bmc/xyz/openbmc_project/control/host0/power_mode
{
"data": {
"PowerMode": "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance"
},
"message": "200 OK",
"status": "ok"
}
# curl -k -X PATCH -d '{ "PowerMode":"Static"}' https://$bmc/redfish/v1/Systems/system
curl -k https://$bmc/xyz/openbmc_project/control/host0/power_mode
{
"data": {
"PowerMode": "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static"
},
"message": "200 OK",
"status": "ok"
}
Ran Validator on hardware and all tests passed:
Elapsed time: 0:05:07
Counter({'skipOptional': 7128, 'pass': 6020, 'metadataNamespaces': 2217, 'passGet': 315, 'warnDeprecated': 212, 'serviceNamespaces': 79, 'warningPresent': 47, 'warnTrailingSlashLink': 24, 'invalidPropertyValue': 18, 'passAction': 14, 'optionalAction': 11, 'repeat': 3, 'unverifiedComplexAdditional': 1})
Validation has succeeded.
Signed-off-by: Chris Cain <cjcain@us.ibm.com>
Change-Id: I5523a0ebe4a2a77ea4709a14863bff7b55f0303d
diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp
index ce7bd5b..3e7b4739 100644
--- a/redfish-core/lib/systems.hpp
+++ b/redfish-core/lib/systems.hpp
@@ -1763,6 +1763,263 @@
#endif
/**
+ * @brief Translate the PowerMode to a response message.
+ *
+ * @param[in] aResp Shared pointer for generating response message.
+ * @param[in] modeValue PowerMode value to be translated
+ *
+ * @return None.
+ */
+inline void translatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ const std::string& modeValue)
+{
+ std::string modeString;
+
+ if (modeValue == "xyz.openbmc_project.Control.Power.Mode."
+ "PowerMode.Static")
+ {
+ aResp->res.jsonValue["PowerMode"] = "Static";
+ }
+ else if (modeValue == "xyz.openbmc_project.Control.Power.Mode."
+ "PowerMode.MaximumPerformance")
+ {
+ aResp->res.jsonValue["PowerMode"] = "MaximumPerformance";
+ }
+ else if (modeValue == "xyz.openbmc_project.Control.Power.Mode."
+ "PowerMode.PowerSaving")
+ {
+ aResp->res.jsonValue["PowerMode"] = "PowerSaving";
+ }
+ else if (modeValue == "xyz.openbmc_project.Control.Power.Mode."
+ "PowerMode.OEM")
+ {
+ aResp->res.jsonValue["PowerMode"] = "OEM";
+ }
+ else
+ {
+ // Any other values would be invalid
+ BMCWEB_LOG_DEBUG << "PowerMode value was not valid: " << modeValue;
+ messages::internalError(aResp->res);
+ }
+}
+
+/**
+ * @brief Retrieves system power mode
+ *
+ * @param[in] aResp Shared pointer for generating response message.
+ *
+ * @return None.
+ */
+inline void getPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
+{
+ BMCWEB_LOG_DEBUG << "Get power mode.";
+
+ // Get Power Mode object path:
+ crow::connections::systemBus->async_method_call(
+ [aResp](
+ const boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string, std::vector<std::string>>>>>&
+ subtree) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG
+ << "DBUS response error on Power.Mode 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;
+ }
+ if (subtree.size() > 1)
+ {
+ // More then one PowerMode object is not supported and is an
+ // error
+ BMCWEB_LOG_DEBUG
+ << "Found more than 1 system D-Bus Power.Mode objects: "
+ << subtree.size();
+ messages::internalError(aResp->res);
+ return;
+ }
+ if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1))
+ {
+ BMCWEB_LOG_DEBUG << "Power.Mode mapper error!";
+ messages::internalError(aResp->res);
+ return;
+ }
+ const std::string& path = subtree[0].first;
+ const std::string& service = subtree[0].second.begin()->first;
+ if (service.empty())
+ {
+ BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!";
+ messages::internalError(aResp->res);
+ return;
+ }
+ // Valid Power Mode object found, now read the current value
+ crow::connections::systemBus->async_method_call(
+ [aResp](const boost::system::error_code ec,
+ const std::variant<std::string>& pmode) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG
+ << "DBUS response error on PowerMode Get: " << ec;
+ messages::internalError(aResp->res);
+ return;
+ }
+
+ const std::string* s = std::get_if<std::string>(&pmode);
+ if (s == nullptr)
+ {
+ BMCWEB_LOG_DEBUG << "Unable to get PowerMode value";
+ messages::internalError(aResp->res);
+ return;
+ }
+
+ aResp->res.jsonValue["PowerMode@Redfish.AllowableValues"] =
+ {"Static", "MaximumPerformance", "PowerSaving"};
+
+ BMCWEB_LOG_DEBUG << "Current power mode: " << *s;
+ translatePowerMode(aResp, *s);
+ },
+ service, path, "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.Control.Power.Mode", "PowerMode");
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
+ std::array<const char*, 1>{"xyz.openbmc_project.Control.Power.Mode"});
+}
+
+/**
+ * @brief Validate the specified mode is valid and return the PowerMode
+ * name associated with that string
+ *
+ * @param[in] aResp Shared pointer for generating response message.
+ * @param[in] modeString String representing the desired PowerMode
+ *
+ * @return PowerMode value or empty string if mode is not valid
+ */
+inline std::string
+ validatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ const std::string& modeString)
+{
+ std::string mode;
+
+ if (modeString == "Static")
+ {
+ mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static";
+ }
+ else if (modeString == "MaximumPerformance")
+ {
+ mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode."
+ "MaximumPerformance";
+ }
+ else if (modeString == "PowerSaving")
+ {
+ mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving";
+ }
+ else
+ {
+ messages::propertyValueNotInList(aResp->res, modeString, "PowerMode");
+ }
+ return mode;
+}
+
+/**
+ * @brief Sets system power mode.
+ *
+ * @param[in] aResp Shared pointer for generating response message.
+ * @param[in] pmode System power mode from request.
+ *
+ * @return None.
+ */
+inline void setPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
+ const std::string& pmode)
+{
+ BMCWEB_LOG_DEBUG << "Set power mode.";
+
+ std::string powerMode = validatePowerMode(aResp, pmode);
+ if (powerMode.empty())
+ {
+ return;
+ }
+
+ // Get Power Mode object path:
+ crow::connections::systemBus->async_method_call(
+ [aResp, powerMode](
+ const boost::system::error_code ec,
+ const std::vector<std::pair<
+ std::string,
+ std::vector<std::pair<std::string, std::vector<std::string>>>>>&
+ subtree) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG
+ << "DBUS response error on Power.Mode GetSubTree " << ec;
+ // This is an optional D-Bus object, but user attempted to patch
+ messages::internalError(aResp->res);
+ return;
+ }
+ if (subtree.empty())
+ {
+ // This is an optional D-Bus object, but user attempted to patch
+ messages::resourceNotFound(aResp->res, "ComputerSystem",
+ "PowerMode");
+ return;
+ }
+ if (subtree.size() > 1)
+ {
+ // More then one PowerMode object is not supported and is an
+ // error
+ BMCWEB_LOG_DEBUG
+ << "Found more than 1 system D-Bus Power.Mode objects: "
+ << subtree.size();
+ messages::internalError(aResp->res);
+ return;
+ }
+ if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1))
+ {
+ BMCWEB_LOG_DEBUG << "Power.Mode mapper error!";
+ messages::internalError(aResp->res);
+ return;
+ }
+ const std::string& path = subtree[0].first;
+ const std::string& service = subtree[0].second.begin()->first;
+ if (service.empty())
+ {
+ BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!";
+ messages::internalError(aResp->res);
+ return;
+ }
+
+ BMCWEB_LOG_DEBUG << "Setting power mode(" << powerMode << ") -> "
+ << path;
+
+ // Set the Power Mode property
+ crow::connections::systemBus->async_method_call(
+ [aResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+ messages::internalError(aResp->res);
+ return;
+ }
+ },
+ service, path, "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Control.Power.Mode", "PowerMode",
+ std::variant<std::string>(powerMode));
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", int32_t(0),
+ std::array<const char*, 1>{"xyz.openbmc_project.Control.Power.Mode"});
+}
+
+/**
* @brief Translates watchdog timeout action DBUS property value to redfish.
*
* @param[in] dbusAction The watchdog timeout action in D-BUS.
@@ -2170,7 +2427,7 @@
get)([](const crow::Request&,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
asyncResp->res.jsonValue["@odata.type"] =
- "#ComputerSystem.v1_14_0.ComputerSystem";
+ "#ComputerSystem.v1_15_0.ComputerSystem";
asyncResp->res.jsonValue["Name"] = "system";
asyncResp->res.jsonValue["Id"] = "system";
asyncResp->res.jsonValue["SystemType"] = "Physical";
@@ -2282,6 +2539,7 @@
getProvisioningStatus(asyncResp);
#endif
getTrustedModuleRequiredToBoot(asyncResp);
+ getPowerMode(asyncResp);
});
BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/")
.privileges({{"ConfigureComponent"}})
@@ -2294,13 +2552,14 @@
std::optional<nlohmann::json> wdtTimerProps;
std::optional<std::string> assetTag;
std::optional<std::string> powerRestorePolicy;
+ std::optional<std::string> powerMode;
if (!json_util::readJson(
req, asyncResp->res, "IndicatorLED", indicatorLed,
"LocationIndicatorActive", locationIndicatorActive,
"Boot", bootProps, "WatchdogTimer", wdtTimerProps,
"PowerRestorePolicy", powerRestorePolicy, "AssetTag",
- assetTag))
+ assetTag, "PowerMode", powerMode))
{
return;
}
@@ -2373,6 +2632,11 @@
{
setPowerRestorePolicy(asyncResp, *powerRestorePolicy);
}
+
+ if (powerMode)
+ {
+ setPowerMode(asyncResp, *powerMode);
+ }
});
}