Implement LocationIndicatorActive for Memory resource
Implement LocationIndicatorActive for Memory schema to set/get the
status of the location LED for a Dimm.[1]
Uses the utility functions getLocationIndicatorActive() and
setLocationIndicatorActive() to follow the association and get or set
the LED value.
[1] https://redfish.dmtf.org/schemas/v1/Memory.v1_20_0.json
Tested:
- Note: The pre-existing code had support for finding objects
implementing the interface
"xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition". From
the comments it looks like these are separate objects on the D-Bus.
The property information for these objects are added to the response
for the Dimm. I kept the GET path similar to avoid regressing that
support. However I was not able to test it as our hardware does not
implement that interface. (Those interfaces are not part of the PATCH
path.)
- Redfish service validator passes
- Tested on p10bmc hardware simulator:
1. Get LocationIndicatorActive
```
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Systems/system/Memory/dimm0
{
"@odata.id": "/redfish/v1/Systems/system/Memory/dimm0",
"@odata.type": "#Memory.v1_11_0.Memory",
"LocationIndicatorActive": 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/Systems/system/Memory/dimm0
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Systems/system/Memory/dimm0
{
"@odata.id": "/redfish/v1/Systems/system/Memory/dimm0",
"@odata.type": "#Memory.v1_11_0.Memory",
"LocationIndicatorActive": true,
...
}
```
3. Use busctl set-propery to change the value back to false
```
$ busctl --json=pretty call xyz.openbmc_project.ObjectMapper /xyz/openbmc_project/object_mapper xyz.openbmc_project.ObjectMapper GetAssociatedSubTreePaths ooias /xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0/identifying /xyz/openbmc_project/led/groups 0 1 xyz.openbmc_project.Led.Group
{
"type" : "as",
"data" : [
[
"/xyz/openbmc_project/led/groups/ddimm16_identify"
]
]
}
$ busctl set-property xyz.openbmc_project.LED.GroupManager /xyz/openbmc_project/led/groups/ddimm16_identify xyz.openbmc_project.Led.Group Asserted b false
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Systems/system/Memory/dimm0
{
"@odata.id": "/redfish/v1/Systems/system/Memory/dimm0",
"@odata.type": "#Memory.v1_11_0.Memory",
"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/Systems/system/Memory/dimm0
{
"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 an unknown Memory resource
```
$ curl -k -H "X-Auth-Token: $token" https://${bmc}/redfish/v1/Systems/system/Memory/dimm300
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The requested resource of type Memory named 'dimm300' was not found.",
"MessageArgs": [
"Memory",
"dimm300"
],
"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 Memory named 'dimm300' was not found."
}
}
$ curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" -X PATCH -d '{"LocationIndicatorActive":true}' https://${bmc}/redfish/v1/Systems/system/Memory/dimm300
{
"error": {
"@Message.ExtendedInfo": [
{
"@odata.type": "#Message.v1_1_1.Message",
"Message": "The requested resource of type Memory named 'dimm300' was not found.",
"MessageArgs": [
"Memory",
"dimm300"
],
"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 Memory named 'dimm300' was not found."
}
}
```
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: Ifb705003380c2a83ada8a645b346b0ae52a06183
Signed-off-by: Janet Adkins <janeta@us.ibm.com>
diff --git a/Redfish.md b/Redfish.md
index e8a22fa..c73fda3 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -931,6 +931,7 @@
- DataWidthBits
- ErrorCorrection
- FirmwareRevision
+- LocationIndicatorActive
- Manufacturer
- Model
- OperatingSpeedMhz
diff --git a/redfish-core/lib/memory.hpp b/redfish-core/lib/memory.hpp
index 59b473d..79ccdac 100644
--- a/redfish-core/lib/memory.hpp
+++ b/redfish-core/lib/memory.hpp
@@ -12,12 +12,16 @@
#include "generated/enums/memory.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/collection.hpp"
#include "utils/dbus_utils.hpp"
#include "utils/hex_utils.hpp"
+#include "utils/json_utils.hpp"
+
+#include <asm-generic/errno.h>
#include <boost/beast/http/verb.hpp>
#include <boost/system/error_code.hpp>
@@ -29,7 +33,9 @@
#include <array>
#include <cstddef>
#include <cstdint>
+#include <functional>
#include <memory>
+#include <optional>
#include <string>
#include <string_view>
#include <utility>
@@ -720,71 +726,239 @@
);
}
-inline void getDimmData(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
- const std::string& dimmId)
+inline void afterGetDimmData(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& dimmId, const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreeResponse& subtree)
{
- BMCWEB_LOG_DEBUG("Get available system dimm resources.");
- constexpr std::array<std::string_view, 2> dimmInterfaces = {
- "xyz.openbmc_project.Inventory.Item.Dimm",
- "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
- dbus::utility::getSubTree(
- "/xyz/openbmc_project/inventory", 0, dimmInterfaces,
- [dimmId, asyncResp{std::move(asyncResp)}](
- const boost::system::error_code& ec,
- const dbus::utility::MapperGetSubTreeResponse& subtree) {
- if (ec)
- {
- BMCWEB_LOG_DEBUG("DBUS response error");
- messages::internalError(asyncResp->res);
+ if (ec)
+ {
+ if (ec.value() != EBADR)
+ {
+ BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
+ messages::internalError(asyncResp->res);
+ }
+ return;
+ }
- return;
- }
- bool found = false;
- for (const auto& [rawPath, object] : subtree)
+ bool found = false;
+ for (const auto& [objectPath, serviceMap] : subtree)
+ {
+ sdbusplus::message::object_path path(objectPath);
+
+ bool dimmInterface = false;
+ bool associationInterface = false;
+ /* Note: Multiple D-Bus objects can provide details for the Memory
+ * object: 1) Dimm is the primary object 2) Additional partitions could
+ * exist per Dimm. Only consider the object found if the Dimm is found.
+ */
+ for (const auto& [serviceName, interfaceList] : serviceMap)
+ {
+ for (const auto& interface : interfaceList)
{
- sdbusplus::message::object_path path(rawPath);
- for (const auto& [service, interfaces] : object)
+ if (interface == "xyz.openbmc_project.Inventory.Item.Dimm" &&
+ path.filename() == dimmId)
{
- for (const auto& interface : interfaces)
- {
- if (interface ==
- "xyz.openbmc_project.Inventory.Item.Dimm" &&
- path.filename() == dimmId)
- {
- getDimmDataByService(asyncResp, dimmId, service,
- rawPath);
- found = true;
- }
-
- // partitions are separate as there can be multiple
- // per
- // device, i.e.
- // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
- // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
- if (interface ==
- "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
- path.parent_path().filename() == dimmId)
- {
- getDimmPartitionData(asyncResp, service, rawPath);
- }
- }
+ // Found the single Dimm
+ getDimmDataByService(asyncResp, dimmId, serviceName,
+ objectPath);
+ dimmInterface = true;
+ found = true;
+ }
+ else if (interface ==
+ "xyz.openbmc_project.Association.Definitions")
+ {
+ /* Object has associations. If this object is also a Dimm
+ * then the association might provide the LED state
+ * information. After all interfaces for this object have
+ * been checked the LED information will be gathered if the
+ * object was a Dimm
+ */
+ associationInterface = true;
+ }
+ else if (
+ interface ==
+ "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
+ path.parent_path().filename() == dimmId)
+ {
+ // partitions are separate as there can be multiple per
+ // device, i.e.
+ // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
+ // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
+ getDimmPartitionData(asyncResp, serviceName, objectPath);
}
}
- // Object not found
- if (!found)
- {
- messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
- return;
- }
- // Set @odata only if object is found
- asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
- asyncResp->res.jsonValue["@odata.id"] =
- boost::urls::format("/redfish/v1/Systems/{}/Memory/{}",
- BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId);
- return;
+ }
+
+ /* If a Dimm has an Association check if it has a LED */
+ if (associationInterface && dimmInterface)
+ {
+ getLocationIndicatorActive(asyncResp, objectPath);
+ }
+ }
+
+ if (!found)
+ {
+ // Dimm object not found
+ messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
+ return;
+ }
+ // Set @odata only if object is found
+ asyncResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
+ asyncResp->res.jsonValue["@odata.id"] =
+ boost::urls::format("/redfish/v1/Systems/{}/Memory/{}",
+ BMCWEB_REDFISH_SYSTEM_URI_NAME, dimmId);
+}
+
+inline void getDimmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& dimmId)
+{
+ BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId);
+ constexpr std::array<std::string_view, 2> interfaces = {
+ "xyz.openbmc_project.Inventory.Item.Dimm",
+ "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
+
+ dbus::utility::getSubTree(
+ "/xyz/openbmc_project/inventory", 0, interfaces,
+ [asyncResp,
+ dimmId](const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreeResponse& subtree) {
+ afterGetDimmData(asyncResp, dimmId, ec, subtree);
});
}
+inline void handleSetDimmData(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ bool locationIndicatorActive, const std::string& dimmPath)
+{
+ setLocationIndicatorActive(asyncResp, dimmPath, locationIndicatorActive);
+}
+
+inline void afterGetValidDimmPath(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& dimmId, const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreePathsResponse& subtree,
+ const std::function<void(const std::string& dimmPath)>& callback)
+{
+ if (ec)
+ {
+ if (ec.value() == EBADR)
+ {
+ /* Need to report error for PATCH */
+ BMCWEB_LOG_WARNING("Dimm not found in inventory");
+ messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
+ }
+ else
+ {
+ BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
+ messages::internalError(asyncResp->res);
+ }
+ return;
+ }
+
+ for (const auto& objectPath : subtree)
+ {
+ // Ignore any objects which don't end with our desired dimm name
+ sdbusplus::message::object_path path(objectPath);
+ if (path.filename() == dimmId)
+ {
+ callback(path);
+ return;
+ }
+ }
+
+ // Object not found
+ messages::resourceNotFound(asyncResp->res, "Memory", dimmId);
+}
+
+inline void getValidDimmPath(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& dimmId,
+ std::function<void(const std::string& dimmPath)>&& callback)
+{
+ BMCWEB_LOG_DEBUG("Get dimm path for {}", dimmId);
+ constexpr std::array<std::string_view, 1> interfaces = {
+ "xyz.openbmc_project.Inventory.Item.Dimm"};
+
+ dbus::utility::getSubTreePaths(
+ "/xyz/openbmc_project/inventory", 0, interfaces,
+ [asyncResp, dimmId, callback{std::move(callback)}](
+ const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreePathsResponse& subtree) {
+ afterGetValidDimmPath(asyncResp, dimmId, ec, subtree, callback);
+ });
+}
+
+inline void handleMemoryPatch(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& systemName, const std::string& dimmId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+ {
+ // Option currently returns no systems. TBD
+ messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+ systemName);
+ return;
+ }
+
+ if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+ {
+ messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+ systemName);
+ return;
+ }
+
+ std::optional<bool> locationIndicatorActive;
+ if (!json_util::readJsonPatch( //
+ req, asyncResp->res, //
+ "LocationIndicatorActive", locationIndicatorActive //
+ ))
+ {
+ return;
+ }
+
+ if (locationIndicatorActive)
+ {
+ getValidDimmPath(asyncResp, dimmId,
+ std::bind_front(handleSetDimmData, asyncResp,
+ *locationIndicatorActive));
+ }
+}
+
+inline void handleMemoryGet(App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& systemName,
+ const std::string& dimmId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+ {
+ // Option currently returns no systems. TBD
+ messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+ systemName);
+ return;
+ }
+
+ if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
+ {
+ messages::resourceNotFound(asyncResp->res, "ComputerSystem",
+ systemName);
+ return;
+ }
+
+ getDimmData(asyncResp, dimmId);
+}
+
inline void requestRoutesMemoryCollection(App& app)
{
/**
@@ -839,31 +1013,12 @@
BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
.privileges(redfish::privileges::getMemory)
.methods(boost::beast::http::verb::get)(
- [&app](const crow::Request& req,
- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
- const std::string& systemName, const std::string& dimmId) {
- if (!redfish::setUpRedfishRoute(app, req, asyncResp))
- {
- return;
- }
+ std::bind_front(handleMemoryGet, std::ref(app)));
- if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
- {
- // Option currently returns no systems. TBD
- messages::resourceNotFound(asyncResp->res, "ComputerSystem",
- systemName);
- return;
- }
-
- if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
- {
- messages::resourceNotFound(asyncResp->res, "ComputerSystem",
- systemName);
- return;
- }
-
- getDimmData(asyncResp, dimmId);
- });
+ BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
+ .privileges(redfish::privileges::patchMemory)
+ .methods(boost::beast::http::verb::patch)(
+ std::bind_front(handleMemoryPatch, std::ref(app)));
}
} // namespace redfish