Redfish: Set the power cap
Set the PowerCap with redfish patch.
Tested:
Case 1: PowerCapEnable is false
$ curl -k -H "X-Auth-Token: $token" -X PUT -d '{"data":false}' https://$bmc/xyz/openbmc_project/control/host0/power_cap/attr/PowerCapEnable
$ curl -k -H "X-Auth-Token: $token"https://${bmc}/redfish/v1/Chassis/chassis/Power
{
"@odata.context": "/redfish/v1/$metadata#Power.Power",
"@odata.id": "/redfish/v1/Chassis/chassis/Power",
"@odata.type": "#Power.v1_5_2.Power",
"Id": "Power",
"Name": "Power",
"PowerControl": [
{
"@odata.id": "/redfish/v1/Chassis/chassis/Power#/PowerControl/0",
"@odata.type": "#Power.v1_0_0.PowerControl",
"MemberId": "0",
"Name": "Chassis Power Control",
"PowerLimit": {
"LimitInWatts": null
}
}
],
...
}
$curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassis/Power -X PATCH -d '{"PowerControl":[{"PowerLimit":{"LimitInWatts":2004}}]}'
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
"Message": "PowerCapEnable is false, can't set the PowerCap.",
"MessageArgs": [],
"MessageId": "Base.1.4.0.UnableToSetPowerCap",
"Resolution": "Set PowerCapEnable to be true before setting PowerCap.",
"Severity": "Warning"
}
],
"code": "Base.1.4.0.UnableToSetPowerCap",
"message": "PowerCapEnable is false, can't set the PowerCap."
}
}
Case 2: PowerCapEnable is true, PowerControl json only
$ curl -k -H "X-Auth-Token: $token" -X PUT -d '{"data":true}' https://$bmc/xyz/openbmc_project/control/host0/power_cap/attr/PowerCapEnable
$ curl -k -H "X-Auth-Token: $token"https://${bmc}/redfish/v1/Chassis/chassis/Power
{
"@odata.context": "/redfish/v1/$metadata#Power.Power",
"@odata.id": "/redfish/v1/Chassis/chassis/Power",
"@odata.type": "#Power.v1_5_2.Power",
"Id": "Power",
"Name": "Power",
"PowerControl": [
{
"@odata.id": "/redfish/v1/Chassis/chassis/Power#/PowerControl/0",
"@odata.type": "#Power.v1_0_0.PowerControl",
"MemberId": "0",
"Name": "Chassis Power Control",
"PowerLimit": {
"LimitInWatts": 2001.0
}
}
],
...
}
$ curl -k -H "X-Auth-Token: $token"https://${bmc}/redfish/v1/Chassis/chassis/Power -X PATCH -d '{"PowerControl":[{"PowerLimit":{"LimitInWatts":2004}}]}' -v
...
< HTTP/1.1 204 No Content
...
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassis/Power
{
"@odata.context": "/redfish/v1/$metadata#Power.Power",
"@odata.id": "/redfish/v1/Chassis/chassis/Power",
"@odata.type": "#Power.v1_5_2.Power",
"Id": "Power",
"Name": "Power",
"PowerControl": [
{
"@odata.id": "/redfish/v1/Chassis/chassis/Power#/PowerControl/0",
"@odata.type": "#Power.v1_0_0.PowerControl",
"MemberId": "0",
"Name": "Chassis Power Control",
"PowerLimit": {
"LimitInWatts": 2004.0
}
}
],
...
}
Case 3: PowerCapEnable is true, PowerControl and Voltages json
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassis/Power -X PATCH -d '{"PowerControl":[{"PowerLimit"{"LimitInWatts":2001}}], "Voltages": [{"MemberId" : "p0_vcs_voltage", "ReadingVolts":8}]}' -v
...
< HTTP/1.1 204 No Content
...
Case 4: Wrong chassis path
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassi/Power -X PATCH -d '{"PowerControl":[{"PowerLimit":{"LimitInWatts":2001}}]}'
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
"Message": "The requested resource of type Chassis named chassi was not found.",
"MessageArgs": [
"Chassis",
"chassi"
],
"MessageId": "Base.1.4.0.ResourceNotFound",
"Resolution": "Provide a valid resource identifier and resubmit the request.",
"Severity": "Critical"
}
],
"code": "Base.1.4.0.ResourceNotFound",
"message": "The requested resource of type Chassis named chassi was not found."
}
}
Signed-off-by: Carol Wang <wangkair@cn.ibm.com>
Change-Id: Ifabdf053005b31cf3e3539009a1ec20ce4d46d5b
diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp
index eaea5ab..ac7503d 100644
--- a/redfish-core/lib/power.hpp
+++ b/redfish-core/lib/power.hpp
@@ -40,6 +40,109 @@
private:
std::vector<const char*> typeList = {"/xyz/openbmc_project/sensors/voltage",
"/xyz/openbmc_project/sensors/power"};
+ void setPowerCapOverride(
+ std::shared_ptr<SensorsAsyncResp> asyncResp,
+ std::vector<nlohmann::json>& powerControlCollections)
+ {
+ auto getChassisPath =
+ [asyncResp, powerControlCollections](
+ const std::optional<std::string>& chassisPath) mutable {
+ if (!chassisPath)
+ {
+ BMCWEB_LOG_ERROR << "Don't find valid chassis path ";
+ messages::resourceNotFound(asyncResp->res, "Chassis",
+ asyncResp->chassisId);
+ return;
+ }
+
+ if (powerControlCollections.size() != 1)
+ {
+ BMCWEB_LOG_ERROR
+ << "Don't support multiple hosts at present ";
+ messages::resourceNotFound(asyncResp->res, "Power",
+ "PowerControl");
+ return;
+ }
+
+ auto& item = powerControlCollections[0];
+
+ std::optional<nlohmann::json> powerLimit;
+ if (!json_util::readJson(item, asyncResp->res, "PowerLimit",
+ powerLimit))
+ {
+ return;
+ }
+ if (!powerLimit)
+ {
+ return;
+ }
+ std::optional<uint32_t> value;
+ if (!json_util::readJson(*powerLimit, asyncResp->res,
+ "LimitInWatts", value))
+ {
+ return;
+ }
+ if (!value)
+ {
+ return;
+ }
+ auto valueHandler = [value, asyncResp](
+ const boost::system::error_code ec,
+ const SensorVariant& powerCapEnable) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ BMCWEB_LOG_ERROR
+ << "powerCapEnable Get handler: Dbus error " << ec;
+ return;
+ }
+ // Check PowerCapEnable
+ const bool* b =
+ sdbusplus::message::variant_ns::get_if<bool>(
+ &powerCapEnable);
+ if (b == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ BMCWEB_LOG_ERROR
+ << "Fail to get PowerCapEnable status ";
+ return;
+ }
+ if (!(*b))
+ {
+ messages::actionNotSupported(
+ asyncResp->res,
+ "Setting LimitInWatts when PowerLimit "
+ "feature is disabled");
+ BMCWEB_LOG_ERROR << "PowerLimit feature is disabled ";
+ return;
+ }
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG
+ << "Power Limit Set: Dbus error: " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ asyncResp->res.result(
+ boost::beast::http::status::no_content);
+ },
+ "xyz.openbmc_project.Settings",
+ "/xyz/openbmc_project/control/host0/power_cap",
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Control.Power.Cap", "PowerCap",
+ sdbusplus::message::variant<uint32_t>(*value));
+ };
+ crow::connections::systemBus->async_method_call(
+ std::move(valueHandler), "xyz.openbmc_project.Settings",
+ "/xyz/openbmc_project/control/host0/power_cap",
+ "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.Control.Power.Cap", "PowerCapEnable");
+ };
+ getValidChassisPath(asyncResp, std::move(getChassisPath));
+ }
void doGet(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{
@@ -227,7 +330,38 @@
void doPatch(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{
- setSensorOverride(res, req, params, typeList, "Power");
+ if (params.size() != 1)
+ {
+ messages::internalError(res);
+ res.end();
+ return;
+ }
+
+ const std::string& chassisName = params[0];
+ auto asyncResp = std::make_shared<SensorsAsyncResp>(res, chassisName,
+ typeList, "Power");
+
+ std::optional<std::vector<nlohmann::json>> voltageCollections;
+ std::optional<std::vector<nlohmann::json>> powerCtlCollections;
+
+ if (!json_util::readJson(req, asyncResp->res, "PowerControl",
+ powerCtlCollections, "Voltages",
+ voltageCollections))
+ {
+ return;
+ }
+
+ if (powerCtlCollections)
+ {
+ setPowerCapOverride(asyncResp, *powerCtlCollections);
+ }
+ if (voltageCollections)
+ {
+ std::unordered_map<std::string, std::vector<nlohmann::json>>
+ allCollections;
+ allCollections.emplace("Voltages", *std::move(voltageCollections));
+ setSensorOverride(asyncResp, allCollections, chassisName, typeList);
+ }
}
};
diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
index c900863..1265783 100644
--- a/redfish-core/lib/sensors.hpp
+++ b/redfish-core/lib/sensors.hpp
@@ -262,6 +262,62 @@
}
/**
+ * @brief Retrieves valid chassis path
+ * @param asyncResp Pointer to object holding response data
+ * @param callback Callback for next step to get valid chassis path
+ */
+template <typename Callback>
+void getValidChassisPath(std::shared_ptr<SensorsAsyncResp> asyncResp,
+ Callback&& callback)
+{
+ BMCWEB_LOG_DEBUG << "checkChassisId enter";
+ const std::array<const char*, 2> interfaces = {
+ "xyz.openbmc_project.Inventory.Item.Board",
+ "xyz.openbmc_project.Inventory.Item.Chassis"};
+
+ auto respHandler =
+ [callback{std::move(callback)},
+ asyncResp](const boost::system::error_code ec,
+ const std::vector<std::string>& chassisPaths) mutable {
+ BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter";
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR
+ << "getValidChassisPath respHandler DBUS error: " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ std::optional<std::string> chassisPath;
+ std::string chassisName;
+ for (const std::string& chassis : chassisPaths)
+ {
+ std::size_t lastPos = chassis.rfind("/");
+ if (lastPos == std::string::npos)
+ {
+ BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis;
+ continue;
+ }
+ chassisName = chassis.substr(lastPos + 1);
+ if (chassisName == asyncResp->chassisId)
+ {
+ chassisPath = chassis;
+ break;
+ }
+ }
+ callback(chassisPath);
+ };
+
+ // Get the Chassis Collection
+ crow::connections::systemBus->async_method_call(
+ respHandler, "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
+ "/xyz/openbmc_project/inventory", 0, interfaces);
+ BMCWEB_LOG_DEBUG << "checkChassisId exit";
+}
+
+/**
* @brief Retrieves requested chassis sensors and redundancy data from DBus .
* @param SensorsAsyncResp Pointer to object holding response data
* @param callback Callback for next step in gathered sensor processing
@@ -2329,72 +2385,18 @@
* @brief Entry point for overriding sensor values of given sensor
*
* @param res response object
- * @param req request object
- * @param params parameter passed for CRUD
+ * @param allCollections Collections extract from sensors' request patch info
* @param typeList TypeList of sensors for the resource queried
* @param chassisSubNode Chassis Node for which the query has to happen
*/
-void setSensorOverride(crow::Response& res, const crow::Request& req,
- const std::vector<std::string>& params,
- const std::vector<const char*> typeList,
- const std::string& chassisSubNode)
+void setSensorOverride(
+ std::shared_ptr<SensorsAsyncResp> sensorAsyncResp,
+ std::unordered_map<std::string, std::vector<nlohmann::json>>&
+ allCollections,
+ const std::string& chassisName, const std::vector<const char*> typeList)
{
-
- // TODO: Need to figure out dynamic way to restrict patch (Set Sensor
- // override) based on another d-bus announcement to be more generic.
- if (params.size() != 1)
- {
- messages::internalError(res);
- res.end();
- return;
- }
-
- std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections;
- std::optional<std::vector<nlohmann::json>> temperatureCollections;
- std::optional<std::vector<nlohmann::json>> fanCollections;
- std::vector<nlohmann::json> voltageCollections;
- BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode
- << "\n";
-
- if (chassisSubNode == "Thermal")
- {
- if (!json_util::readJson(req, res, "Temperatures",
- temperatureCollections, "Fans",
- fanCollections))
- {
- return;
- }
- if (!temperatureCollections && !fanCollections)
- {
- messages::resourceNotFound(res, "Thermal",
- "Temperatures / Voltages");
- res.end();
- return;
- }
- if (temperatureCollections)
- {
- allCollections.emplace("Temperatures",
- *std::move(temperatureCollections));
- }
- if (fanCollections)
- {
- allCollections.emplace("Fans", *std::move(fanCollections));
- }
- }
- else if (chassisSubNode == "Power")
- {
- if (!json_util::readJson(req, res, "Voltages", voltageCollections))
- {
- return;
- }
- allCollections.emplace("Voltages", std::move(voltageCollections));
- }
- else
- {
- res.result(boost::beast::http::status::not_found);
- res.end();
- return;
- }
+ BMCWEB_LOG_INFO << "setSensorOverride for subNode"
+ << sensorAsyncResp->chassisSubNode << "\n";
const char* propertyValueName;
std::unordered_map<std::string, std::pair<double, std::string>> overrideMap;
@@ -2416,8 +2418,8 @@
}
for (auto& item : collectionItems.second)
{
- if (!json_util::readJson(item, res, "MemberId", memberId,
- propertyValueName, value))
+ if (!json_util::readJson(item, sensorAsyncResp->res, "MemberId",
+ memberId, propertyValueName, value))
{
return;
}
@@ -2425,9 +2427,7 @@
std::make_pair(value, collectionItems.first));
}
}
- const std::string& chassisName = params[0];
- auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
- res, chassisName, typeList, chassisSubNode);
+
auto getChassisSensorListCb = [sensorAsyncResp,
overrideMap](const std::shared_ptr<
boost::container::flat_set<
diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp
index e39aa3e..3ab9e44 100644
--- a/redfish-core/lib/thermal.hpp
+++ b/redfish-core/lib/thermal.hpp
@@ -60,7 +60,45 @@
void doPatch(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{
- setSensorOverride(res, req, params, typeList, "Thermal");
+ if (params.size() != 1)
+ {
+ res.end();
+ messages::internalError(res);
+ return;
+ }
+
+ const std::string& chassisName = params[0];
+ std::optional<std::vector<nlohmann::json>> temperatureCollections;
+ std::optional<std::vector<nlohmann::json>> fanCollections;
+ std::unordered_map<std::string, std::vector<nlohmann::json>>
+ allCollections;
+
+ auto asyncResp = std::make_shared<SensorsAsyncResp>(
+ res, chassisName, typeList, "Thermal");
+
+ if (!json_util::readJson(req, asyncResp->res, "Temperatures",
+ temperatureCollections, "Fans",
+ fanCollections))
+ {
+ return;
+ }
+ if (!temperatureCollections && !fanCollections)
+ {
+ messages::resourceNotFound(asyncResp->res, "Thermal",
+ "Temperatures / Voltages");
+ return;
+ }
+ if (temperatureCollections)
+ {
+ allCollections.emplace("Temperatures",
+ *std::move(temperatureCollections));
+ }
+ if (fanCollections)
+ {
+ allCollections.emplace("Fans", *std::move(fanCollections));
+ }
+
+ setSensorOverride(asyncResp, allCollections, chassisName, typeList);
}
};