Patch support for sensor overrride
Support added for overriding sensor, which can be
used for validation / ad-hoc debugging. This provides
option to make PATCH call to redfish/v1/<chassisId>/Thermal
or power id. Based on schema, will accept Temperatures /
Voltages collection with properties MemberId and
ReadingCelsius / ReadingVolts.
TODO:
1. Need to make a dynamic way of enabling / disbaling this command.
Unit-Test:
1. Verified sensor values are getting updated by doing PATCH
method to a known sensor. Verified the value got updated
using ipmitool sensor list.
2. Verified negative cases of making PATCH call on invalid
chasisId, Invalid MemberId etc.
Testedeby:
Used Postman tool to issue the PATCH call to the
1. https://xx.xx.xx.xx/redfish/v1/Chassis/XXYYZZ/Thermal with
content
{
"Temperatures": [
{
"MemberId" : "SensorNameXX",
"ReadingCelsius" : valueXX
}
]
}
2. https://xx.xx.xx.xx/redfish/v1/Chassis/XXYYZZ/Power with
content
{
"Voltages": [
{
"MemberId" : "SensorNameXX",
"ReadingVolts" : valueXX
}
]
}
Change-Id: Idf2d891ac0d10b5d20f78c386232cae8a6896f1a
Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
diff --git a/redfish-core/lib/power.hpp b/redfish-core/lib/power.hpp
index 0cb1aa0..a76f191 100644
--- a/redfish-core/lib/power.hpp
+++ b/redfish-core/lib/power.hpp
@@ -38,6 +38,9 @@
}
private:
+ std::initializer_list<const char*> typeList = {
+ "/xyz/openbmc_project/sensors/voltage",
+ "/xyz/openbmc_project/sensors/power"};
void doGet(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{
@@ -56,14 +59,15 @@
res.jsonValue["Id"] = "Power";
res.jsonValue["Name"] = "Power";
auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
- res, chassis_name,
- std::initializer_list<const char*>{
- "/xyz/openbmc_project/sensors/voltage",
- "/xyz/openbmc_project/sensors/power"},
- "Power");
+ res, chassis_name, typeList, "Power");
// TODO Need to retrieve Power Control information.
getChassisData(sensorAsyncResp);
}
+ void doPatch(crow::Response& res, const crow::Request& req,
+ const std::vector<std::string>& params) override
+ {
+ setSensorOverride(res, req, params, typeList, "Power");
+ }
};
} // namespace redfish
diff --git a/redfish-core/lib/sensors.hpp b/redfish-core/lib/sensors.hpp
index 9fa5d88..34f8f53 100644
--- a/redfish-core/lib/sensors.hpp
+++ b/redfish-core/lib/sensors.hpp
@@ -22,6 +22,7 @@
#include <boost/container/flat_map.hpp>
#include <boost/range/algorithm/replace_copy_if.hpp>
#include <dbus_singleton.hpp>
+#include <utils/json_utils.hpp>
#include <variant>
namespace redfish
@@ -54,7 +55,7 @@
chassisId(chassisId), types(types), chassisSubNode(subNode)
{
res.jsonValue["@odata.id"] =
- "/redfish/v1/Chassis/" + chassisId + "/Thermal";
+ "/redfish/v1/Chassis/" + chassisId + "/" + subNode;
}
~SensorsAsyncResp()
@@ -76,17 +77,18 @@
};
/**
- * @brief Creates connections necessary for chassis sensors
+ * @brief Get objects with connection necessary for sensors
* @param SensorsAsyncResp Pointer to object holding response data
* @param sensorNames Sensors retrieved from chassis
* @param callback Callback for processing gathered connections
*/
template <typename Callback>
-void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
- const boost::container::flat_set<std::string>& sensorNames,
- Callback&& callback)
+void getObjectsWithConnection(
+ std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
+ const boost::container::flat_set<std::string>& sensorNames,
+ Callback&& callback)
{
- BMCWEB_LOG_DEBUG << "getConnections enter";
+ BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter";
const std::string path = "/xyz/openbmc_project/sensors";
const std::array<std::string, 1> interfaces = {
"xyz.openbmc_project.Sensor.Value"};
@@ -95,12 +97,12 @@
auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp,
sensorNames](const boost::system::error_code ec,
const GetSubTreeType& subtree) {
- BMCWEB_LOG_DEBUG << "getConnections resp_handler enter";
+ BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter";
if (ec)
{
messages::internalError(SensorsAsyncResp->res);
- BMCWEB_LOG_ERROR << "getConnections resp_handler: Dbus error "
- << ec;
+ BMCWEB_LOG_ERROR
+ << "getObjectsWithConnection resp_handler: Dbus error " << ec;
return;
}
@@ -109,6 +111,7 @@
// Make unique list of connections only for requested sensor types and
// found in the chassis
boost::container::flat_set<std::string> connections;
+ std::set<std::pair<std::string, std::string>> objectsWithConnection;
// Intrinsic to avoid malloc. Most systems will have < 8 sensor
// producers
connections.reserve(8);
@@ -144,6 +147,8 @@
BMCWEB_LOG_DEBUG << "Adding connection: "
<< objData.first;
connections.insert(objData.first);
+ objectsWithConnection.insert(std::make_pair(
+ object.first, objData.first));
}
}
}
@@ -152,16 +157,36 @@
}
}
BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections";
- callback(std::move(connections));
- BMCWEB_LOG_DEBUG << "getConnections resp_handler exit";
+ callback(std::move(connections), std::move(objectsWithConnection));
+ BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit";
};
-
// Make call to ObjectMapper to find all sensors objects
crow::connections::systemBus->async_method_call(
std::move(respHandler), "xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces);
- BMCWEB_LOG_DEBUG << "getConnections exit";
+ BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit";
+}
+
+/**
+ * @brief Create connections necessary for sensors
+ * @param SensorsAsyncResp Pointer to object holding response data
+ * @param sensorNames Sensors retrieved from chassis
+ * @param callback Callback for processing gathered connections
+ */
+template <typename Callback>
+void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp,
+ const boost::container::flat_set<std::string>& sensorNames,
+ Callback&& callback)
+{
+ auto objectsWithConnectionCb =
+ [callback](const boost::container::flat_set<std::string>& connections,
+ const std::set<std::pair<std::string, std::string>>&
+ objectsWithConnection) {
+ callback(std::move(connections));
+ };
+ getObjectsWithConnection(SensorsAsyncResp, sensorNames,
+ std::move(objectsWithConnectionCb));
}
/**
@@ -541,4 +566,131 @@
BMCWEB_LOG_DEBUG << "getChassisData exit";
};
+/**
+ * @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 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::initializer_list<const char*> typeList,
+ const std::string& chassisSubNode)
+{
+
+ // 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;
+ }
+ const char* collectionName;
+ const char* propertyValueName;
+ if (chassisSubNode == "Thermal")
+ {
+ collectionName = "Temperatures";
+ propertyValueName = "ReadingCelsius";
+ }
+ else if (chassisSubNode == "Power")
+ {
+ collectionName = "Voltages";
+ propertyValueName = "ReadingVolts";
+ }
+ else
+ {
+ res.result(boost::beast::http::status::not_found);
+ res.end();
+ return;
+ }
+ std::vector<nlohmann::json> collections;
+ if (!json_util::readJson(req, res, collectionName, collections))
+ {
+ return;
+ }
+ if (collections.size() != 1)
+ {
+ messages::malformedJSON(res);
+ res.end();
+ return;
+ }
+
+ std::string memberId;
+ double value;
+ if (!json_util::readJson(collections[0], res, "MemberId", memberId,
+ propertyValueName, value))
+ {
+ return;
+ }
+ const std::string& chassisName = params[0];
+ auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
+ res, chassisName, typeList, chassisSubNode);
+ BMCWEB_LOG_INFO << "setSensorOverride for " << memberId
+ << "with value: " << value << "\n";
+ // first check for valid chassis id & sensor in requested chassis.
+ auto getChassisSensorListCb =
+ [sensorAsyncResp, memberId,
+ value](const boost::container::flat_set<std::string>& sensorLists) {
+ if (sensorLists.find(memberId) == sensorLists.end())
+ {
+ BMCWEB_LOG_INFO << "Unable to find memberId " << memberId;
+ messages::resourceNotFound(sensorAsyncResp->res,
+ sensorAsyncResp->chassisSubNode ==
+ "Thermal"
+ ? "Temperatures"
+ : "Voltages",
+ memberId);
+ return;
+ }
+ boost::container::flat_set<std::string> sensorNames;
+ sensorNames.emplace(memberId);
+ // Get the connection to which the memberId belongs
+ auto getObjectsWithConnectionCb =
+ [sensorAsyncResp, memberId, value](
+ const boost::container::flat_set<std::string>& connections,
+ const std::set<std::pair<std::string, std::string>>&
+ objectsWithConnection) {
+ if (objectsWithConnection.size() != 1)
+ {
+ BMCWEB_LOG_INFO
+ << "Unable to find object with proper connection "
+ << objectsWithConnection.size() << "\n";
+ messages::resourceNotFound(
+ sensorAsyncResp->res,
+ sensorAsyncResp->chassisSubNode == "Thermal"
+ ? "Temperatures"
+ : "Voltages",
+ memberId);
+ return;
+ }
+ crow::connections::systemBus->async_method_call(
+ [sensorAsyncResp, memberId,
+ value](const boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_DEBUG
+ << "getOverrideValueStatus DBUS error: "
+ << ec;
+ messages::internalError(sensorAsyncResp->res);
+ return;
+ }
+ },
+ objectsWithConnection.begin()->second,
+ objectsWithConnection.begin()->first,
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.Sensor.Value", "Value",
+ sdbusplus::message::variant<double>(value));
+ };
+ // Get object with connection for the given sensor name
+ getObjectsWithConnection(sensorAsyncResp, sensorNames,
+ std::move(getObjectsWithConnectionCb));
+ };
+ // get full sensor list for the given chassisId and cross verify the sensor.
+ getChassis(sensorAsyncResp, std::move(getChassisSensorListCb));
+}
+
} // namespace redfish
diff --git a/redfish-core/lib/thermal.hpp b/redfish-core/lib/thermal.hpp
index d0fbccf..4b2a0a4 100644
--- a/redfish-core/lib/thermal.hpp
+++ b/redfish-core/lib/thermal.hpp
@@ -37,6 +37,10 @@
}
private:
+ std::initializer_list<const char*> typeList = {
+ "/xyz/openbmc_project/sensors/fan",
+ "/xyz/openbmc_project/sensors/temperature",
+ "/xyz/openbmc_project/sensors/fan_pwm"};
void doGet(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{
@@ -58,16 +62,16 @@
"/redfish/v1/Chassis/" + chassisName + "/Thermal";
auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>(
- res, chassisName,
- std::initializer_list<const char*>{
- "/xyz/openbmc_project/sensors/fan",
- "/xyz/openbmc_project/sensors/temperature",
- "/xyz/openbmc_project/sensors/fan_pwm"},
- "Thermal");
+ res, chassisName, typeList, "Thermal");
// TODO Need to get Chassis Redundancy information.
getChassisData(sensorAsyncResp);
}
+ void doPatch(crow::Response& res, const crow::Request& req,
+ const std::vector<std::string>& params) override
+ {
+ setSensorOverride(res, req, params, typeList, "Thermal");
+ }
};
} // namespace redfish