Implement LocationIndicatorActive for PowerSupplies
Implement LocationIndicatorActive for PowerSupplies schema to set/get
the status of the location LED for each power supply.[1] When working
with Redfish to get or set the "LocationIndicatorActive" property, the
initial step involves locating the corresponding LED group through the
"identifying" association.[2][3][4] Following this, we can proceed to
either get or set the "Asserted" property.[5]
[1] https://redfish.dmtf.org/schemas/v1/PowerSupply.v1_5_0.json
[2] https://github.com/openbmc/phosphor-dbus-interfaces/tree/master/yaml/xyz/openbmc_project/Led#dbus-interfaces-for-led-groups
[3] https://github.com/openbmc/docs/blob/master/architecture/LED-architecture.md#redfish
[4] https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/58299
[5] https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/xyz/openbmc_project/Led/Group.interface.yaml
Tested:
- Validator passes.
- Tested on p10bmc hardware simulator
1) Get LocationIndicatorActive
```
$ curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"LocationIndicatorActive": false,
...
}
```
We will see the powersupply0 identify led is false too.
```
$ busctl get-property xyz.openbmc_project.LED.GroupManager /xyz/openbmc_project/led/groups/powersupply0_identify xyz.openbmc_project.Led.Group Asserted
b false
```
2) Set LocationIndicatorActive to true
```
$ curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":true}' https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0
```
Then we will see the powersupply0 location LED lit up, and the value
becomes true:
```
$ curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"LocationIndicatorActive": true,
...
}
$ busctl get-property xyz.openbmc_project.LED.GroupManager /xyz/openbmc_project/led/groups/powersupply0_identify xyz.openbmc_project.Led.Group Asserted
b true
```
3) Use set-property to change the value back to false
```
$ busctl set-property xyz.openbmc_project.LED.GroupManager /xyz/openbmc_project/led/groups/powersupply0_identify xyz.openbmc_project.Led.Group Asserted b false
$ busctl get-property xyz.openbmc_project.LED.GroupManager /xyz/openbmc_project/led/groups/powersupply0_identify xyz.openbmc_project.Led.Group Asserted
b false
```
Then we will see the value reflected in the Redfish query:
```
$ curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"LocationIndicatorActive": false,
...
}
```
4) Error returned when trying to set the value to non-boolean:
```
$ curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":"unknown"}' https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0
{
"LocationIndicatorActive@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The value '\"unknown\"' for the property LocationIndicatorActive is not a type that the property can accept.",
"MessageArgs": [
"\"unknown\"",
"LocationIndicatorActive"
],
"MessageId": "Base.1.19.PropertyValueTypeError",
"MessageSeverity": "Warning",
"Resolution": "Correct the value for the property in the request body and resubmit the request if the operation failed."
}
]
}
```
5) Error returned when specifying a unknown power supply:
```
$ curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":true}' https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupplyBAD
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The requested resource of type PowerSupplies named 'powersupplyBAD' was not found.",
"MessageArgs": [
"PowerSupplies",
"powersupplyBAD"
],
"MessageId": "Base.1.19.ResourceNotFound",
"MessageSeverity": "Critical",
"Resolution": "Provide a valid resource identifier and resubmit the request."
}
],
"code": "Base.1.19.ResourceNotFound",
"message": "The requested resource of type PowerSupplies named 'powersupplyBAD' was not found."
}
}
6) Forced no LED group to test failure paths for GET/PATCH
/* GET succeeds with no LocationIndicatorActive */
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0
{
"@odata.id": "/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0",
"@odata.type": "#PowerSupply.v1_5_0.PowerSupply",
"EfficiencyRatings": [
{
"EfficiencyPercent": 90
}
],
"FirmwareVersion": "313033323330",
"Id": "powersupply0",
"Location": {
"PartLocation": {
"ServiceLabel": "U78DA.ND0.1234567-E0"
}
},
"Manufacturer": "",
"Model": "51E9",
"Name": "Power Supply",
"PartNumber": "revisio",
"SerialNumber": "YL10K serial",
"SparePartNumber": "c ",
"Status": {
"Health": "OK",
"State": "Enabled"
}
}
/* PATCH fails when no LocationIndicatorActive property exists for
* object
*/
$ curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":true}' https://${bmc}/redfish/v1/Chassis/chassis/PowerSubsystem/PowerSupplies/powersupply0
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The property LocationIndicatorActive is not in the list of valid properties for the resource.",
"MessageArgs": [
"LocationIndicatorActive"
],
"MessageId": "Base.1.19.PropertyUnknown",
"MessageSeverity": "Warning",
"Resolution": "Remove the unknown property from the request body and resubmit the request if the operation failed."
}
],
"code": "Base.1.19.PropertyUnknown",
"message": "The property LocationIndicatorActive is not in the list of valid properties for the resource."
}
}
```
Signed-off-by: Chicago Duan <duanzhijia01@inspur.com>
Change-Id: Id0c30fa3d7c1e7ed4ff7f8fd1d5951d24053fbde
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
Signed-off-by: Myung Bae <myungbae@us.ibm.com>
Signed-off-by: Janet Adkins <janeta@us.ibm.com>
diff --git a/Redfish.md b/Redfish.md
index 238ac77..d09927f 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -454,6 +454,7 @@
- EfficiencyPercent
- FirmwareVersion
- Location
+- LocationIndicatorActive
- Manufacturer
- Model
- PartNumber
diff --git a/redfish-core/lib/led.hpp b/redfish-core/lib/led.hpp
index 2bb2cc8..e868bf3 100644
--- a/redfish-core/lib/led.hpp
+++ b/redfish-core/lib/led.hpp
@@ -11,13 +11,22 @@
#include "logging.hpp"
#include "utils/dbus_utils.hpp"
+#include <asm-generic/errno.h>
+
+#include <boost/system/error_code.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/message/native_types.hpp>
+#include <array>
+#include <functional>
#include <memory>
+#include <string_view>
+#include <utility>
namespace redfish
{
+static constexpr std::array<std::string_view, 1> ledGroupInterface = {
+ "xyz.openbmc_project.Led.Group"};
/**
* @brief Retrieves identify led group properties over dbus
*
@@ -232,4 +241,185 @@
}
});
}
+
+inline void handleLedGroupSubtree(
+ const std::string& objPath, const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreeResponse& subtree,
+ const std::function<void(const boost::system::error_code& ec,
+ const std::string& ledGroupPath,
+ const std::string& service)>& callback)
+{
+ if (ec)
+ {
+ // Callback will handle the error
+ callback(ec, "", "");
+ return;
+ }
+
+ if (subtree.empty())
+ {
+ // Callback will handle the error
+ BMCWEB_LOG_DEBUG(
+ "No LED group associated with the specified object path: {}",
+ objPath);
+ callback(ec, "", "");
+ return;
+ }
+
+ if (subtree.size() > 1)
+ {
+ // Callback will handle the error
+ BMCWEB_LOG_DEBUG(
+ "More than one LED group associated with the object {}: {}",
+ objPath, subtree.size());
+ callback(ec, "", "");
+ return;
+ }
+
+ const auto& [ledGroupPath, serviceMap] = *subtree.begin();
+ const auto& [service, interfaces] = *serviceMap.begin();
+ callback(ec, ledGroupPath, service);
+}
+
+inline void getLedGroupPath(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& objPath,
+ std::function<void(const boost::system::error_code& ec,
+ const std::string& ledGroupPath,
+ const std::string& service)>&& callback)
+{
+ static constexpr const char* ledObjectPath =
+ "/xyz/openbmc_project/led/groups";
+ sdbusplus::message::object_path ledGroupAssociatedPath =
+ objPath + "/identifying";
+
+ dbus::utility::getAssociatedSubTree(
+ ledGroupAssociatedPath, sdbusplus::message::object_path(ledObjectPath),
+ 0, ledGroupInterface,
+ [asyncResp, objPath, callback{std::move(callback)}](
+ const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreeResponse& subtree) {
+ handleLedGroupSubtree(objPath, ec, subtree, callback);
+ });
+}
+
+inline void afterGetLedState(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const boost::system::error_code& ec, bool assert)
+{
+ if (ec)
+ {
+ if (ec.value() != EBADR)
+ {
+ BMCWEB_LOG_ERROR("DBUS response error for get ledState {}",
+ ec.value());
+ messages::internalError(asyncResp->res);
+ }
+ return;
+ }
+
+ asyncResp->res.jsonValue["LocationIndicatorActive"] = assert;
+}
+
+inline void getLedState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const boost::system::error_code& ec,
+ const std::string& ledGroupPath,
+ const std::string& service)
+{
+ if (ec)
+ {
+ if (ec.value() != EBADR)
+ {
+ BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
+ messages::internalError(asyncResp->res);
+ }
+ return;
+ }
+
+ if (ledGroupPath.empty() || service.empty())
+ {
+ // No LED group associated, not an error
+ return;
+ }
+
+ sdbusplus::asio::getProperty<bool>(
+ *crow::connections::systemBus, service, ledGroupPath,
+ "xyz.openbmc_project.Led.Group", "Asserted",
+ std::bind_front(afterGetLedState, asyncResp));
+}
+
+/**
+ * @brief Retrieves identify led group properties over dbus
+ *
+ * @param[in] asyncResp Shared pointer for generating response
+ * message.
+ * @param[in] objPath Object path on PIM
+ *
+ * @return None.
+ */
+inline void getLocationIndicatorActive(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& objPath)
+{
+ BMCWEB_LOG_DEBUG("Get LocationIndicatorActive for {}", objPath);
+ getLedGroupPath(asyncResp, objPath,
+ [asyncResp](const boost::system::error_code& ec,
+ const std::string& ledGroupPath,
+ const std::string& service) {
+ getLedState(asyncResp, ec, ledGroupPath, service);
+ });
+}
+
+inline void setLedState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ bool ledState, const boost::system::error_code& ec,
+ const std::string& ledGroupPath,
+ const std::string& service)
+{
+ if (ec)
+ {
+ if (ec.value() == EBADR)
+ {
+ messages::propertyUnknown(asyncResp->res,
+ "LocationIndicatorActive");
+ return;
+ }
+ BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ if (ledGroupPath.empty() || service.empty())
+ {
+ messages::propertyUnknown(asyncResp->res, "LocationIndicatorActive");
+ return;
+ }
+
+ setDbusProperty(asyncResp, "LocationIndicatorActive", service, ledGroupPath,
+ "xyz.openbmc_project.Led.Group", "Asserted", ledState);
+}
+
+/**
+ * @brief Sets identify led group properties
+ *
+ * @param[in] asyncResp Shared pointer for generating response
+ * message.
+ * @param[in] objPath Object path on PIM
+ * @param[in] ledState LED state passed from request
+ *
+ * @return None.
+ */
+inline void setLocationIndicatorActive(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& objPath, bool ledState)
+{
+ BMCWEB_LOG_DEBUG("Set LocationIndicatorActive for {}", objPath);
+ getLedGroupPath(
+ asyncResp, objPath,
+ [asyncResp, ledState](const boost::system::error_code& ec,
+ const std::string& ledGroupPath,
+ const std::string& service) {
+ setLedState(asyncResp, ledState, ec, ledGroupPath, service);
+ });
+}
+
} // namespace redfish
diff --git a/redfish-core/lib/power_supply.hpp b/redfish-core/lib/power_supply.hpp
index 7316b5f..89c84e1 100644
--- a/redfish-core/lib/power_supply.hpp
+++ b/redfish-core/lib/power_supply.hpp
@@ -8,11 +8,13 @@
#include "error_messages.hpp"
#include "generated/enums/resource.hpp"
#include "http_request.hpp"
+#include "led.hpp"
#include "logging.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
#include "utils/chassis_utils.hpp"
#include "utils/dbus_utils.hpp"
+#include "utils/json_utils.hpp"
#include "utils/time_utils.hpp"
#include <asm-generic/errno.h>
@@ -505,6 +507,7 @@
getPowerSupplyFirmwareVersion(asyncResp, service, powerSupplyPath);
getPowerSupplyLocation(asyncResp, service, powerSupplyPath);
getEfficiencyPercent(asyncResp);
+ getLocationIndicatorActive(asyncResp, powerSupplyPath);
}
inline void handlePowerSupplyHead(
@@ -549,6 +552,43 @@
std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId));
}
+inline void doPatchPowerSupply(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const bool locationIndicatorActive, const std::string& powerSupplyPath,
+ const std::string& /*service*/)
+{
+ setLocationIndicatorActive(asyncResp, powerSupplyPath,
+ locationIndicatorActive);
+}
+
+inline void handlePowerSupplyPatch(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& powerSupplyId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ std::optional<bool> locationIndicatorActive;
+ if (!json_util::readJsonPatch( //
+ req, asyncResp->res, //
+ "LocationIndicatorActive", locationIndicatorActive //
+ ))
+ {
+ return;
+ }
+
+ if (locationIndicatorActive)
+ {
+ // Get the correct power supply Path that match the input parameters
+ getValidPowerSupplyPath(asyncResp, chassisId, powerSupplyId,
+ std::bind_front(doPatchPowerSupply, asyncResp,
+ *locationIndicatorActive));
+ }
+}
+
inline void requestRoutesPowerSupply(App& app)
{
BMCWEB_ROUTE(
@@ -562,6 +602,12 @@
.privileges(redfish::privileges::getPowerSupply)
.methods(boost::beast::http::verb::get)(
std::bind_front(handlePowerSupplyGet, std::ref(app)));
+
+ BMCWEB_ROUTE(
+ app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/")
+ .privileges(redfish::privileges::patchPowerSupply)
+ .methods(boost::beast::http::verb::patch)(
+ std::bind_front(handlePowerSupplyPatch, std::ref(app)));
}
} // namespace redfish