Skip to content

Commit

Permalink
Redfish: Set the power cap
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wangkair committed Nov 19, 2019
1 parent 51dae67 commit 4bb3dc3
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 69 deletions.
136 changes: 135 additions & 1 deletion redfish-core/lib/power.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,109 @@ class Power : public Node
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
{
Expand Down Expand Up @@ -227,7 +330,38 @@ class Power : public Node
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);
}
}
};

Expand Down
134 changes: 67 additions & 67 deletions redfish-core/lib/sensors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,62 @@ void reduceSensorList(
}
}

/**
* @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
Expand Down Expand Up @@ -2329,72 +2385,18 @@ bool findSensorNameUsingSensorPath(
* @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;
Expand All @@ -2416,18 +2418,16 @@ void setSensorOverride(crow::Response& res, const crow::Request& req,
}
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;
}
overrideMap.emplace(memberId,
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<
Expand Down
40 changes: 39 additions & 1 deletion redfish-core/lib/thermal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,45 @@ class Thermal : public Node
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);
}
};

Expand Down

0 comments on commit 4bb3dc3

Please sign in to comment.