From e860b1b6ed7b2a6797660ed4aa0e4ff75a675c4f Mon Sep 17 00:00:00 2001 From: Rahul D S Date: Fri, 10 Jul 2026 10:22:02 +0000 Subject: [PATCH] Support GET and PATCH for ReadyToRemove on assemblies Add GET handling for Assembly ReadyToRemove by reading the xyz.openbmc_project.State.Decorator.ReadyToRemove interface from the inventory. Update PATCH handling so non-special assemblies set the D-Bus ReadyToRemove. Keep existing special handling for tod_battery and panel CM. ReadyToRemove is now part of the assemblyproperty and not under oem schema Tested-By: 1. GET returns the correct property under the inventory d-bus 2. PATCH sets the readytoremove on the inventory dbus Signed-off-by: Rahul D S --- redfish-core/lib/assembly.hpp | 107 ++++++++++++++++++++++++++++++++-- res, | 0 2 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 res, diff --git a/redfish-core/lib/assembly.hpp b/redfish-core/lib/assembly.hpp index 3e88ef63c..3363251bb 100644 --- a/redfish-core/lib/assembly.hpp +++ b/redfish-core/lib/assembly.hpp @@ -40,6 +40,8 @@ #include #include +static constexpr const char* readyToRemoveIface = + "xyz.openbmc_project.State.ReadyToRemove"; namespace redfish { @@ -152,6 +154,32 @@ void getAssemblyHealth(const std::shared_ptr& asyncResp, }); } +/** + * @brief Populate ReadyToRemove for assemblies that implement + * xyz.openbmc_project.State.ReadyToRemove. + */ +inline void getAssemblyReadyToRemove( + const std::shared_ptr& asyncResp, + const std::string& serviceName, const std::string& assembly, + const nlohmann::json::json_pointer& assemblyJsonPtr) +{ + dbus::utility::getProperty( + serviceName, assembly, readyToRemoveIface, "ReadyToRemove", + [asyncResp, + assemblyJsonPtr](const boost::system::error_code& ec, bool value) { + if (ec) + { + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value()); + messages::internalError(asyncResp->res); + } + return; + } + asyncResp->res.jsonValue[assemblyJsonPtr]["ReadyToRemove"] = value; + }); +} + inline void afterGetDbusObject( const std::shared_ptr& asyncResp, const std::string& assembly, @@ -197,6 +225,11 @@ inline void afterGetDbusObject( getAssemblyHealth(asyncResp, serviceName, assembly, assemblyJsonPtr); } + else if (interface == readyToRemoveIface) + { + getAssemblyReadyToRemove(asyncResp, serviceName, assembly, + assemblyJsonPtr); + } } } } @@ -219,7 +252,7 @@ inline void getAssemblyProperties( for (const std::string& assembly : assemblies) { nlohmann::json::object_t item; - item["@odata.type"] = "#Assembly.v1_5_1.AssemblyData"; + item["@odata.type"] = "#Assembly.v1_6_0.AssemblyData"; item["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/Assembly#/Assemblies/{}", chassisId, std::to_string(assemblyIndex)); @@ -358,16 +391,19 @@ inline void afterHandleChassisAssemblyPatch( } std::map locationIndicatorActiveMap; + std::map readyToRemoveMap; std::map oemIndicatorMap; for (nlohmann::json::object_t& item : assemblyData) { std::optional memberId; std::optional locationIndicatorActive; + std::optional readyToRemove; std::optional oem; - if (!json_util::readJsonObject(item, asyncResp->res, "MemberId", - memberId, "LocationIndicatorActive", - locationIndicatorActive, "Oem", oem)) + if (!json_util::readJsonObject( + item, asyncResp->res, "MemberId", memberId, + "LocationIndicatorActive", locationIndicatorActive, + "ReadyToRemove", readyToRemove, "Oem", oem)) { return; } @@ -386,6 +422,20 @@ inline void afterHandleChassisAssemblyPatch( return; } } + if (readyToRemove) + { + if (memberId) + { + readyToRemoveMap[*memberId] = *readyToRemove; + } + else + { + BMCWEB_LOG_WARNING( + "Property Missing - MemberId must be included with ReadyToRemove"); + messages::propertyMissing(asyncResp->res, "MemberId"); + return; + } + } if (oem) { if (memberId) @@ -413,6 +463,7 @@ inline void afterHandleChassisAssemblyPatch( setLocationIndicatorActive(asyncResp, assembly, iter->second); } + // Handle Oem/OpenBMC/ReadyToRemove auto iter2 = oemIndicatorMap.find(std::to_string(assemblyIndex)); if (iter2 != oemIndicatorMap.end()) @@ -492,6 +543,46 @@ inline void afterHandleChassisAssemblyPatch( return; } } + + // Handle top-level ReadyToRemove (standard Assembly schema v1_6_0) + auto iter3 = readyToRemoveMap.find(std::to_string(assemblyIndex)); + + if (iter3 != readyToRemoveMap.end()) + { + bool readytoremove = iter3->second; + + constexpr std::array rtrIface = { + readyToRemoveIface}; + dbus::utility::getDbusObject( + assembly, rtrIface, + [asyncResp, assembly, + readytoremove](const boost::system::error_code& ec1, + const dbus::utility::MapperGetObject& object) { + if (ec1 || object.empty()) + { + BMCWEB_LOG_ERROR( + "getDbusObject failed for ReadyToRemove on {}: {}", + assembly, ec1.message()); + messages::internalError(asyncResp->res); + return; + } + + const std::string& service = object.begin()->first; + sdbusplus::asio::setProperty( + *crow::connections::systemBus, service, assembly, + readyToRemoveIface, "ReadyToRemove", readytoremove, + [asyncResp](const boost::system::error_code& ec2) { + if (ec2) + { + BMCWEB_LOG_ERROR( + "Failed to set ReadyToRemove: {}", + ec2.message()); + messages::internalError(asyncResp->res); + } + }); + }); + } + assemblyIndex++; } } @@ -515,8 +606,12 @@ inline void handleChassisAssemblyPatch( assembly_utils::getChassisAssembly( asyncResp, chassisID, - std::bind_front(afterHandleChassisAssemblyPatch, asyncResp, chassisID, - assemblyData)); + [asyncResp, chassisID, assemblyData = std::move(assemblyData)]( + const boost::system::error_code& ec, + const std::vector& assemblyList) mutable { + afterHandleChassisAssemblyPatch(asyncResp, chassisID, assemblyData, + ec, assemblyList); + }); } /** diff --git a/res, b/res, new file mode 100644 index 000000000..e69de29bb