From d5a39575a146945e2fbcfe7872aa9b59deaf64ab Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Mon, 24 Nov 2025 14:54:25 -0800 Subject: [PATCH 01/18] Inline globals These globals shouldn't have been added to the redfish namespace, and can cause -Wshadow issues. Inline the variables for now, and we can assess whether we want to promote these to a common API later. Change-Id: I57bf1754cdad6ccd2a37375fad4b26996f0e9e9a Signed-off-by: Ed Tanous --- redfish-core/lib/pcie.hpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp index 36dc920b8e..82c70e9a79 100644 --- a/redfish-core/lib/pcie.hpp +++ b/redfish-core/lib/pcie.hpp @@ -47,12 +47,6 @@ namespace redfish { -static constexpr const char* inventoryPath = "/xyz/openbmc_project/inventory"; -static constexpr std::array pcieDeviceInterface = { - "xyz.openbmc_project.Inventory.Item.PCIeDevice"}; -static constexpr std::array pcieSlotInterface = { - "xyz.openbmc_project.Inventory.Item.PCIeSlot"}; - inline void handlePCIeDevicePath( const std::string& pcieDeviceId, const std::shared_ptr& asyncResp, @@ -69,7 +63,8 @@ inline void handlePCIeDevicePath( { continue; } - + static constexpr std::array pcieDeviceInterface = { + "xyz.openbmc_project.Inventory.Item.PCIeDevice"}; dbus::utility::getDbusObject( pcieDevicePath, pcieDeviceInterface, [pcieDevicePath, asyncResp, @@ -96,8 +91,11 @@ inline void getValidPCIeDevicePath( const std::function& callback) { + static constexpr std::array pcieDeviceInterface = { + "xyz.openbmc_project.Inventory.Item.PCIeDevice"}; + dbus::utility::getSubTreePaths( - inventoryPath, 0, pcieDeviceInterface, + "/xyz/openbmc_project/inventory", 0, pcieDeviceInterface, [pcieDeviceId, asyncResp, callback](const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& @@ -293,9 +291,11 @@ inline void getPCIeDeviceSlotPath( std::function&& callback) { std::string associationPath = pcieDevicePath + "/contained_by"; + sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); + static constexpr std::array pcieSlotInterface = { + "xyz.openbmc_project.Inventory.Item.PCIeSlot"}; dbus::utility::getAssociatedSubTreePaths( - associationPath, sdbusplus::message::object_path(inventoryPath), 0, - pcieSlotInterface, + associationPath, path, 0, pcieSlotInterface, [callback = std::move(callback), asyncResp, pcieDevicePath]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& endpoints) { @@ -361,6 +361,8 @@ inline void afterGetPCIeDeviceSlotPath( const std::shared_ptr& asyncResp, const std::string& pcieDeviceSlot) { + static constexpr std::array pcieSlotInterface = { + "xyz.openbmc_project.Inventory.Item.PCIeSlot"}; dbus::utility::getDbusObject( pcieDeviceSlot, pcieSlotInterface, [asyncResp, From 32ea63d365aeef63a53e4e89d4db56c70cea940e Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Wed, 3 Sep 2025 19:04:19 -0700 Subject: [PATCH 02/18] Use boost::urls::format boost::urls::format is specifically meant for URL construction. It handles encoding like percentage encoding which std::format does not. Change-Id: Ib6e6bc8bcf30b138b93111577d9f2150709dda5c Signed-off-by: Ed Tanous --- redfish-core/lib/bios.hpp | 6 ++-- redfish-core/lib/chassis.hpp | 4 +-- redfish-core/lib/managers.hpp | 4 +-- redfish-core/lib/processor.hpp | 33 +++++++++++++------ redfish-core/lib/storage.hpp | 2 +- .../lib/systems_logservices_hostlogger.hpp | 16 ++++----- .../lib/systems_logservices_postcodes.hpp | 16 ++++----- redfish-core/src/subscription.cpp | 5 ++- 8 files changed, 50 insertions(+), 36 deletions(-) diff --git a/redfish-core/lib/bios.hpp b/redfish-core/lib/bios.hpp index 560fe7fbc5..ddbd77cc72 100644 --- a/redfish-core/lib/bios.hpp +++ b/redfish-core/lib/bios.hpp @@ -15,6 +15,7 @@ #include "utils/sw_utils.hpp" #include +#include #include #include @@ -55,8 +56,9 @@ inline void handleBiosServiceGet( asyncResp->res.jsonValue["Description"] = "BIOS Configuration Service"; asyncResp->res.jsonValue["Id"] = "BIOS"; asyncResp->res.jsonValue["Actions"]["#Bios.ResetBios"]["target"] = - std::format("/redfish/v1/Systems/{}/Bios/Actions/Bios.ResetBios", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + boost::urls::format( + "/redfish/v1/Systems/{}/Bios/Actions/Bios.ResetBios", + BMCWEB_REDFISH_SYSTEM_URI_NAME); // Get the ActiveSoftwareImage and SoftwareImages sw_util::populateSoftwareInformation(asyncResp, sw_util::biosPurpose, "", diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp index 5467f58296..a35e2cdb7c 100644 --- a/redfish-core/lib/chassis.hpp +++ b/redfish-core/lib/chassis.hpp @@ -457,8 +457,8 @@ inline void handleDecoratorAssetProperties( nlohmann::json::array_t computerSystems; nlohmann::json::object_t system; - system["@odata.id"] = - std::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); + system["@odata.id"] = boost::urls::format("/redfish/v1/Systems/{}", + BMCWEB_REDFISH_SYSTEM_URI_NAME); computerSystems.emplace_back(std::move(system)); asyncResp->res.jsonValue["Links"]["ComputerSystems"] = std::move(computerSystems); diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 6f5417c433..3c5c53f723 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -772,8 +772,8 @@ inline void handleManagerGet( nlohmann::json::array_t managerForServers; nlohmann::json::object_t manager; - manager["@odata.id"] = std::format("/redfish/v1/Systems/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + manager["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); managerForServers.emplace_back(std::move(manager)); asyncResp->res.jsonValue["Links"]["ManagerForServers"] = diff --git a/redfish-core/lib/processor.hpp b/redfish-core/lib/processor.hpp index 882be4bdf9..5b4ec99150 100644 --- a/redfish-core/lib/processor.hpp +++ b/redfish-core/lib/processor.hpp @@ -901,7 +901,7 @@ inline void getProcessorData( */ inline void patchAppliedOperatingConfig( const std::shared_ptr& resp, - const std::string& processorId, const std::string& appliedConfigUri, + const std::string& processorId, const boost::urls::url& appliedConfigUri, const std::string& cpuObjectPath, const dbus::utility::MapperServiceMap& serviceMap) { @@ -925,11 +925,10 @@ inline void patchAppliedOperatingConfig( } // Check that the config URI is a child of the cpu URI being patched. - std::string expectedPrefix(std::format("/redfish/v1/Systems/{}/Processors/", - BMCWEB_REDFISH_SYSTEM_URI_NAME)); - expectedPrefix += processorId; - expectedPrefix += "/OperatingConfigs/"; - if (!appliedConfigUri.starts_with(expectedPrefix) || + boost::urls::url expectedPrefix = boost::urls::format( + "/redfish/v1/Systems/{}/Processors/{}/OperatingConfigs/", + BMCWEB_REDFISH_SYSTEM_URI_NAME, processorId); + if (!appliedConfigUri.buffer().starts_with(expectedPrefix.buffer()) || expectedPrefix.size() == appliedConfigUri.size()) { messages::propertyValueIncorrect(resp->res, "AppliedOperatingConfig", @@ -941,7 +940,8 @@ inline void patchAppliedOperatingConfig( // direct child of the CPU object. // Strip the expectedPrefix from the config URI to get the "filename", and // append to the CPU's path. - std::string configBaseName = appliedConfigUri.substr(expectedPrefix.size()); + std::string configBaseName = + appliedConfigUri.buffer().substr(expectedPrefix.buffer().size()); sdbusplus::message::object_path configPath(cpuObjectPath); configPath /= configBaseName; @@ -1013,7 +1013,7 @@ inline void handleProcessorGet( inline void doPatchProcessor( const std::shared_ptr& asyncResp, const std::string& processorId, - const std::optional& appliedConfigUri, + const std::optional& appliedConfigUri, std::optional locationIndicatorActive, const std::string& objectPath, const dbus::utility::MapperServiceMap& serviceMap) { @@ -1054,16 +1054,29 @@ inline void handleProcessorPatch( return; } - std::optional appliedConfigUri; + std::optional appliedConfigStr; std::optional locationIndicatorActive; if (!json_util::readJsonPatch( req, asyncResp->res, // - "AppliedOperatingConfig/@odata.id", appliedConfigUri, // + "AppliedOperatingConfig/@odata.id", appliedConfigStr, // "LocationIndicatorActive", locationIndicatorActive // )) { return; } + std::optional appliedConfigUri; + if (appliedConfigStr) + { + boost::system::result parsed = + boost::urls::parse_relative_ref(*appliedConfigStr); + if (!parsed) + { + messages::propertyValueFormatError( + asyncResp->res, "AppliedOperatingConfig", *appliedConfigStr); + return; + } + appliedConfigUri = std::move(*parsed); + } // Check for 404 and find matching D-Bus object, then run // property patch handlers if that all succeeds. diff --git a/redfish-core/lib/storage.hpp b/redfish-core/lib/storage.hpp index 737867f683..4a08d3404f 100644 --- a/redfish-core/lib/storage.hpp +++ b/redfish-core/lib/storage.hpp @@ -53,7 +53,7 @@ inline void handleSystemsStorageCollectionGet( asyncResp->res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection"; - asyncResp->res.jsonValue["@odata.id"] = std::format( + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/Storage", BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Name"] = "Storage Collection"; diff --git a/redfish-core/lib/systems_logservices_hostlogger.hpp b/redfish-core/lib/systems_logservices_hostlogger.hpp index aae0195e1b..bb3ebd365e 100644 --- a/redfish-core/lib/systems_logservices_hostlogger.hpp +++ b/redfish-core/lib/systems_logservices_hostlogger.hpp @@ -136,15 +136,15 @@ inline void handleSystemsLogServicesHostloggerGet( return; } asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + boost::urls::format("/redfish/v1/Systems/{}/LogServices/HostLogger", + BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; asyncResp->res.jsonValue["Name"] = "Host Logger Service"; asyncResp->res.jsonValue["Description"] = "Host Logger Service"; asyncResp->res.jsonValue["Id"] = "HostLogger"; - asyncResp->res.jsonValue["Entries"]["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); } inline void handleSystemsLogServicesHostloggerEntriesGet( @@ -175,9 +175,9 @@ inline void handleSystemsLogServicesHostloggerEntriesGet( systemName); return; } - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; asyncResp->res.jsonValue["Name"] = "HostLogger Entries"; diff --git a/redfish-core/lib/systems_logservices_postcodes.hpp b/redfish-core/lib/systems_logservices_postcodes.hpp index 33249dc4be..ecd52025b7 100644 --- a/redfish-core/lib/systems_logservices_postcodes.hpp +++ b/redfish-core/lib/systems_logservices_postcodes.hpp @@ -74,17 +74,17 @@ inline void handleSystemsLogServicesPostCodesGet( return; } asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/PostCodes", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + boost::urls::format("/redfish/v1/Systems/{}/LogServices/PostCodes", + BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; asyncResp->res.jsonValue["Name"] = "POST Code Log Service"; asyncResp->res.jsonValue["Description"] = "POST Code Log Service"; asyncResp->res.jsonValue["Id"] = "PostCodes"; asyncResp->res.jsonValue["OverWritePolicy"] = log_service::OverWritePolicy::WrapsWhenFull; - asyncResp->res.jsonValue["Entries"]["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); std::pair redfishDateTimeOffset = redfish::time_utils::getDateTimeOffsetNow(); @@ -488,9 +488,9 @@ inline void handleSystemsLogServicesPostCodesEntriesGet( } asyncResp->res.jsonValue["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; - asyncResp->res.jsonValue["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", + BMCWEB_REDFISH_SYSTEM_URI_NAME); asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; asyncResp->res.jsonValue["Description"] = "Collection of POST Code Log Entries"; diff --git a/redfish-core/src/subscription.cpp b/redfish-core/src/subscription.cpp index 5d6d629ea8..0fcdefc4ab 100644 --- a/redfish-core/src/subscription.cpp +++ b/redfish-core/src/subscription.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -94,8 +93,8 @@ void Subscription::sendHeartbeatEvent() // send the heartbeat message nlohmann::json eventMessage = messages::redfishServiceFunctional(); eventMessage["EventTimestamp"] = time_utils::getDateTimeOffsetNow().first; - eventMessage["OriginOfCondition"] = - std::format("/redfish/v1/EventService/Subscriptions/{}", userSub->id); + eventMessage["OriginOfCondition"] = boost::urls::format( + "/redfish/v1/EventService/Subscriptions/{}", userSub->id); eventMessage["MemberId"] = "0"; nlohmann::json::array_t eventRecord; From c2f428f514c84f7cefa46c3e6ee4d3dd2fe2dd52 Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Fri, 17 Oct 2025 17:25:22 +0200 Subject: [PATCH 03/18] Add remaining functions to systems_utils namespace Initially, systems_utils.hpp has not defined its own namespace, all functions were under the redfish namespace. Patch 82078 [1] introduced systems_utils namespace to the header for new functionality. This patch adds all functions, that haven't been part of the new namespace to systems_utils namespace and updates all call sites accordingly. [1] https://gerrit.openbmc.org/c/openbmc/bmcweb/+/82078 Tested: Code compiles. Change-Id: Ia6ac3edd36aacc22cbf1a6adae4e2b264932b43c Signed-off-by: Oliver Brewka --- redfish-core/include/utils/systems_utils.hpp | 9 +-- redfish-core/lib/systems.hpp | 78 ++++++++++--------- .../include/utils/systems_utils_test.cpp | 40 +++++----- 3 files changed, 65 insertions(+), 62 deletions(-) diff --git a/redfish-core/include/utils/systems_utils.hpp b/redfish-core/include/utils/systems_utils.hpp index 8e522a0274..288364798e 100644 --- a/redfish-core/include/utils/systems_utils.hpp +++ b/redfish-core/include/utils/systems_utils.hpp @@ -26,6 +26,9 @@ namespace redfish { +namespace systems_utils +{ + inline void handleSystemCollectionMembers( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, @@ -256,9 +259,6 @@ inline std::string getChassisStateServiceName( return chassisStateService; } -namespace systems_utils -{ - inline void afterGetValidSystemsPath( const std::shared_ptr& asyncResp, const std::string& systemId, @@ -312,8 +312,6 @@ inline void getValidSystemsPath( }); } -} // namespace systems_utils - /** * @brief Match computerSystemIndex with index contained by an object path * i.e 1 in /xyz/openbmc/project/control/host1/policy/TPMEnable @@ -366,4 +364,5 @@ inline bool indexMatchingSubTreeMapObjectPath( return false; } +} // namespace systems_utils } // namespace redfish diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp index 3123f068bd..345a47f85f 100644 --- a/redfish-core/lib/systems.hpp +++ b/redfish-core/lib/systems.hpp @@ -454,9 +454,9 @@ inline void getHostState(const std::shared_ptr& asyncResp, { BMCWEB_LOG_DEBUG("Get host information."); sdbusplus::message::object_path path = - getHostStateObjectPath(computerSystemIndex); + systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), path, "xyz.openbmc_project.State.Host", "CurrentHostState", [asyncResp](const boost::system::error_code& ec, const std::string& hostState) { @@ -758,9 +758,9 @@ inline void getBootProgress(const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { sdbusplus::message::object_path path = - getHostStateObjectPath(computerSystemIndex); + systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), path, "xyz.openbmc_project.State.Boot.Progress", "BootProgress", [asyncResp](const boost::system::error_code ec, const std::string& bootProgressStr) { @@ -791,9 +791,9 @@ inline void getBootProgressLastStateTime( const uint64_t computerSystemIndex) { sdbusplus::message::object_path path = - getHostStateObjectPath(computerSystemIndex); + systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), path, "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate", [asyncResp](const boost::system::error_code& ec, const uint64_t lastStateTime) { @@ -1094,9 +1094,9 @@ inline void getLastResetTime( { BMCWEB_LOG_DEBUG("Getting System Last Reset Time"); sdbusplus::message::object_path path = - getChassisStateObjectPath(computerSystemIndex); + systems_utils::getChassisStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - getChassisStateServiceName(computerSystemIndex), path, + systems_utils::getChassisStateServiceName(computerSystemIndex), path, "xyz.openbmc_project.State.Chassis", "LastStateChangeTime", [asyncResp](const boost::system::error_code& ec, uint64_t lastResetTime) { @@ -1135,9 +1135,9 @@ inline void getAutomaticRebootAttempts( { BMCWEB_LOG_DEBUG("Get Automatic Retry policy"); sdbusplus::message::object_path path = - getHostStateObjectPath(computerSystemIndex); + systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getAllProperties( - getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), path, "xyz.openbmc_project.Control.Boot.RebootAttempts", [asyncResp{asyncResp}]( const boost::system::error_code& ec, @@ -1262,8 +1262,8 @@ inline void setAutomaticRetryAttempts( BMCWEB_LOG_DEBUG("Set Automatic Retry Attempts."); setDbusProperty(asyncResp, "Boot/AutomaticRetryAttempts", - getHostStateServiceName(computerSystemIndex), - getHostStateObjectPath(computerSystemIndex), + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), "xyz.openbmc_project.Control.Boot.RebootAttempts", "RetryAttempts", retryAttempts); } @@ -1397,8 +1397,8 @@ inline void getTrustedModuleRequiredToBootCallback( if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - if (!indexMatchingSubTreeMapObjectPath(asyncResp, computerSystemIndex, - subtree, path, service)) + if (!systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, computerSystemIndex, subtree, path, service)) { return; } @@ -1494,8 +1494,8 @@ inline void setTrustedModuleRequiredToBootCallback( if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - if (!indexMatchingSubTreeMapObjectPath(asyncResp, computerSystemIndex, - subtree, path, serv)) + if (!systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, computerSystemIndex, subtree, path, serv)) { BMCWEB_LOG_DEBUG("TPM.Policy mapper error!"); messages::internalError(asyncResp->res); @@ -2824,7 +2824,7 @@ inline void handleComputerSystemCollectionGet( asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; asyncResp->res.jsonValue["Name"] = "Computer System Collection"; - getSystemCollectionMembers(asyncResp); + systems_utils::getSystemCollectionMembers(asyncResp); } /** @@ -2913,19 +2913,21 @@ inline void processComputerSystemResetActionPost( if (hostCommand) { - setDbusProperty(asyncResp, "Reset", - getHostStateServiceName(computerSystemIndex), - getHostStateObjectPath(computerSystemIndex), - "xyz.openbmc_project.State.Host", - "RequestedHostTransition", command); + setDbusProperty( + asyncResp, "Reset", + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), + "xyz.openbmc_project.State.Host", "RequestedHostTransition", + command); } else { - setDbusProperty(asyncResp, "Reset", - getChassisStateServiceName(computerSystemIndex), - getChassisStateObjectPath(computerSystemIndex), - "xyz.openbmc_project.State.Chassis", - "RequestedPowerTransition", command); + setDbusProperty( + asyncResp, "Reset", + systems_utils::getChassisStateServiceName(computerSystemIndex), + systems_utils::getChassisStateObjectPath(computerSystemIndex), + "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", + command); } } @@ -2964,9 +2966,10 @@ inline void handleComputerSystemResetActionPost( return; } - getComputerSystemIndex(asyncResp, systemName, - std::bind_front(processComputerSystemResetActionPost, - asyncResp, resetType)); + systems_utils::getComputerSystemIndex( + asyncResp, systemName, + std::bind_front(processComputerSystemResetActionPost, asyncResp, + resetType)); } inline void handleComputerSystemHead( @@ -3200,7 +3203,7 @@ inline void handleComputerSystemGet( } BMCWEB_LOG_DEBUG("requested system = {}", systemName); - getComputerSystemIndex( + systems_utils::getComputerSystemIndex( asyncResp, systemName, std::bind_front(processComputerSystemGet, asyncResp, systemName)); } @@ -3408,9 +3411,10 @@ inline void handleComputerSystemPatch( return; } - getComputerSystemIndex(asyncResp, systemName, - std::bind_front(processComputerSystemPatch, - asyncResp, systemName, patchParams)); + systems_utils::getComputerSystemIndex( + asyncResp, systemName, + std::bind_front(processComputerSystemPatch, asyncResp, systemName, + patchParams)); } inline void handleSystemCollectionResetActionHead( @@ -3524,9 +3528,9 @@ inline void getAllowedHostTransitions( const uint64_t computerSystemIndex) { sdbusplus::message::object_path path = - getHostStateObjectPath(computerSystemIndex); + systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty>( - getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), path, "xyz.openbmc_project.State.Host", "AllowedHostTransitions", std::bind_front(afterGetAllowedHostTransitions, asyncResp)); } @@ -3571,7 +3575,7 @@ inline void handleSystemCollectionResetActionGet( asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; // Look to see if system defines AllowedHostTransitions - getComputerSystemIndex( + systems_utils::getComputerSystemIndex( asyncResp, systemName, std::bind_front(getAllowedHostTransitions, asyncResp)); } diff --git a/test/redfish-core/include/utils/systems_utils_test.cpp b/test/redfish-core/include/utils/systems_utils_test.cpp index df16f1c68f..9719b78a4e 100644 --- a/test/redfish-core/include/utils/systems_utils_test.cpp +++ b/test/redfish-core/include/utils/systems_utils_test.cpp @@ -34,33 +34,33 @@ TEST(SystemsUtils, IndexMatchingObjectPath) {"/xyz/openbmc_project/control/host999/", {{"xyz.openbmc_project.Settings", {}}}}}; - EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 1, subtree, - objectPath, service)); - EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 2, subtree, - objectPath, service)); - EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 10, subtree, - objectPath, service)); - EXPECT_TRUE(indexMatchingSubTreeMapObjectPath(asyncResp, 999, subtree, - objectPath, service)); - EXPECT_FALSE(indexMatchingSubTreeMapObjectPath(asyncResp, 100, subtree, - objectPath, service)); - EXPECT_FALSE(indexMatchingSubTreeMapObjectPath(asyncResp, 11, subtree, - objectPath, service)); - EXPECT_FALSE(indexMatchingSubTreeMapObjectPath(asyncResp, 0, subtree, - objectPath, service)); + EXPECT_TRUE(systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, 1, subtree, objectPath, service)); + EXPECT_TRUE(systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, 2, subtree, objectPath, service)); + EXPECT_TRUE(systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, 10, subtree, objectPath, service)); + EXPECT_TRUE(systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, 999, subtree, objectPath, service)); + EXPECT_FALSE(systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, 100, subtree, objectPath, service)); + EXPECT_FALSE(systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, 11, subtree, objectPath, service)); + EXPECT_FALSE(systems_utils::indexMatchingSubTreeMapObjectPath( + asyncResp, 0, subtree, objectPath, service)); - indexMatchingSubTreeMapObjectPath(asyncResp, 1, subtree, objectPath, - service); + systems_utils::indexMatchingSubTreeMapObjectPath(asyncResp, 1, subtree, + objectPath, service); EXPECT_EQ(objectPath, "/xyz/openbmc_project/control/host1"); EXPECT_EQ(service, "xyz.openbmc_project.Settings"); - indexMatchingSubTreeMapObjectPath(asyncResp, 10, subtree, objectPath, - service); + systems_utils::indexMatchingSubTreeMapObjectPath(asyncResp, 10, subtree, + objectPath, service); EXPECT_EQ(objectPath, "/xyz/openbmc_project/control/host10/policy/TPMEnable"); - indexMatchingSubTreeMapObjectPath(asyncResp, 999, subtree, objectPath, - service); + systems_utils::indexMatchingSubTreeMapObjectPath(asyncResp, 999, subtree, + objectPath, service); EXPECT_EQ(objectPath, "/xyz/openbmc_project/control/host999/"); } } // namespace From 456512bc3fa1cd6318da38c7f1d162c8c4db9336 Mon Sep 17 00:00:00 2001 From: Chandramohan Harkude Date: Wed, 3 Dec 2025 00:08:53 +0530 Subject: [PATCH 04/18] Fix RFA aggregation issue Aggregation was failing because of method and target URI missing in HTTP request This was a regression from 897e4c80f35b5bd963923f5794a7d3b229dba306 HTTP/1.1 Accept: application/json, application/octet-stream Host: 172.31.13.251 Fix : Added the Target URI and method from original request Testing : ``` curl -k -u root:0penBmc -X GET https://${BMC_IP}/redfish/v1/Chassis Returned items from both BMC and Satellite BMC ``` Change-Id: Ib446adfeb94d0cce3a272fc39ee5cc60ce3df7f8 Signed-off-by: Chandramohan Harkude Signed-off-by: Ed Tanous --- .../include/utils/redfish_aggregator_utils.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/redfish-core/include/utils/redfish_aggregator_utils.hpp b/redfish-core/include/utils/redfish_aggregator_utils.hpp index 696edf76f4..34148a5ad3 100644 --- a/redfish-core/include/utils/redfish_aggregator_utils.hpp +++ b/redfish-core/include/utils/redfish_aggregator_utils.hpp @@ -23,7 +23,14 @@ inline crow::Request createNewRequest(const crow::Request& localReq) BMCWEB_LOG_ERROR("Failed to set body. Continuing"); } - for (const auto& field : req.fields()) + // Preserve method and target (URI) from the original request + req.method(localReq.method()); + if (!req.target(localReq.target())) + { + BMCWEB_LOG_ERROR("Failed to set target on aggregated request"); + } + + for (const auto& field : localReq.fields()) { // Drop any incoming x-auth-token headers and keep Host and // Content-Type. Set Accept. @@ -34,6 +41,7 @@ inline crow::Request createNewRequest(const crow::Request& localReq) req.addHeader(headerName, field.value()); } } + // Set Accept header to application/json, application/octet-stream req.addHeader(boost::beast::http::field::accept, "application/json, application/octet-stream"); return req; From 57d41fed30c140c308155076c0642cca7c418201 Mon Sep 17 00:00:00 2001 From: Janet Adkins Date: Thu, 13 Nov 2025 16:55:34 -0600 Subject: [PATCH 05/18] Fans: Create utility class for fans Support for fans from separate routes of ThermalSubsystem and EnvironmentMetrics will need the same utility functions. By moving these shared functions into single file will help with build time by not needing to included full fan support in all of the related locations. Initially only the getFanPaths() is moved. Expectation is more functions will be added as the expanded support is added. Tested: - Compiles - Verified ThermalSubsystem/Fans output unchanged Change-Id: I22c22bdf38155e93aa13e259dd8c904a977f8a07 Signed-off-by: Janet Adkins --- redfish-core/include/utils/fan_utils.hpp | 60 ++++++++++++++++++++++++ redfish-core/lib/fan.hpp | 45 ++---------------- 2 files changed, 65 insertions(+), 40 deletions(-) create mode 100644 redfish-core/include/utils/fan_utils.hpp diff --git a/redfish-core/include/utils/fan_utils.hpp b/redfish-core/include/utils/fan_utils.hpp new file mode 100644 index 0000000000..9d6ef0bfdb --- /dev/null +++ b/redfish-core/include/utils/fan_utils.hpp @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright OpenBMC Authors +#pragma once + +#include "async_resp.hpp" +#include "dbus_utility.hpp" +#include "error_messages.hpp" +#include "logging.hpp" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace redfish +{ +constexpr std::array fanInterface = { + "xyz.openbmc_project.Inventory.Item.Fan"}; + +namespace fan_utils +{ +inline void getFanPaths( + const std::shared_ptr& asyncResp, + const std::string& validChassisPath, + const std::function& callback) +{ + sdbusplus::message::object_path endpointPath{validChassisPath}; + endpointPath /= "cooled_by"; + + dbus::utility::getAssociatedSubTreePaths( + endpointPath, + sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, + fanInterface, + [asyncResp, callback]( + const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { + if (ec) + { + if (ec.value() != EBADR) + { + BMCWEB_LOG_ERROR( + "DBUS response error for getAssociatedSubTreePaths {}", + ec.value()); + messages::internalError(asyncResp->res); + } + return; + } + callback(subtreePaths); + }); +} + +} // namespace fan_utils +} // namespace redfish diff --git a/redfish-core/lib/fan.hpp b/redfish-core/lib/fan.hpp index 40368e7199..f79ec85301 100644 --- a/redfish-core/lib/fan.hpp +++ b/redfish-core/lib/fan.hpp @@ -14,7 +14,7 @@ #include "registries/privilege_registry.hpp" #include "utils/asset_utils.hpp" #include "utils/chassis_utils.hpp" -#include "utils/dbus_utils.hpp" +#include "utils/fan_utils.hpp" #include "utils/json_utils.hpp" #include @@ -24,9 +24,7 @@ #include #include #include -#include -#include #include #include #include @@ -36,9 +34,6 @@ namespace redfish { -constexpr std::array fanInterface = { - "xyz.openbmc_project.Inventory.Item.Fan"}; - inline void updateFanList( const std::shared_ptr& asyncResp, const std::string& chassisId, @@ -64,37 +59,6 @@ inline void updateFanList( asyncResp->res.jsonValue["Members@odata.count"] = fanList.size(); } -inline void getFanPaths( - const std::shared_ptr& asyncResp, - const std::string& validChassisPath, - const std::function& callback) -{ - sdbusplus::message::object_path endpointPath{validChassisPath}; - endpointPath /= "cooled_by"; - - dbus::utility::getAssociatedSubTreePaths( - endpointPath, - sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, - fanInterface, - [asyncResp, callback]( - const boost::system::error_code& ec, - const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths) { - if (ec) - { - if (ec.value() != EBADR) - { - BMCWEB_LOG_ERROR( - "DBUS response error for getAssociatedSubTreePaths {}", - ec.value()); - messages::internalError(asyncResp->res); - } - return; - } - callback(subtreePaths); - }); -} - inline void doFanCollection(const std::shared_ptr& asyncResp, const std::string& chassisId, const std::optional& validChassisPath) @@ -117,8 +81,9 @@ inline void doFanCollection(const std::shared_ptr& asyncResp, asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); asyncResp->res.jsonValue["Members@odata.count"] = 0; - getFanPaths(asyncResp, *validChassisPath, - std::bind_front(updateFanList, asyncResp, chassisId)); + fan_utils::getFanPaths( + asyncResp, *validChassisPath, + std::bind_front(updateFanList, asyncResp, chassisId)); } inline void handleFanCollectionHead( @@ -209,7 +174,7 @@ inline void getValidFanObject( const std::function& callback) { - getFanPaths( + fan_utils::getFanPaths( asyncResp, validChassisPath, [fanId, asyncResp, callback]( const dbus::utility::MapperGetSubTreePathsResponse& fanPaths) { From 1aa94df4cb3b7ab864a4c5fb009538c259c2801e Mon Sep 17 00:00:00 2001 From: Harshit Aghera Date: Thu, 31 Jul 2025 15:24:55 +0530 Subject: [PATCH 06/18] sensor_utils: Add PeakReading property Add support for PeakReading and PeakReadingTime for sensors. This enhancement allows sensor readings to include max observed value information in the Redfish API, along with timestamp. It uses PDI xyz.openbmc_project.Telemetry.Report. Property PeakReading is added if OperationType in PDI property ReadingParameters is set to Maximum. Current Limitation - The ResetMetrics action is currently not supported for sensor URIs. As a result, the ability to clear PeakReading values for GPU Power Sensors has not been implemented. Future Consideration - If ResetMetrics action support is added in the future, the corresponding functionality will also need to be implemented in the dbus-sensor application to ensure full compatibility. Schema: https://redfish.dmtf.org/schemas/v1/Sensor.v1_2_0.yaml (PeakReading) Backend implementation for reference: https://gerrit.openbmc.org/c/openbmc/dbus-sensors/+/82479 Tested: Build an image for nvl32-obmc machine with the following patches cherry picked. https://gerrit.openbmc.org/c/openbmc/openbmc/+/85490 https://gerrit.openbmc.org/c/openbmc/bmcweb/+/82449. The patch cherry-picks the following patches that are currently under review. ``` 1. device tree https://lore.kernel.org/all/aRbLqH8pLWCQryhu@molberding.nvidia.com/ 2. mctpd patches https://github.com/CodeConstruct/mctp/pull/85 3. u-boot changes https://lore.kernel.org/openbmc/20251121-msx4-v1-0-fc0118b666c1@nvidia.com/T/#t 4. kernel changes as specified in the openbmc patch (for espi) 5. entity-manager changes https://gerrit.openbmc.org/c/openbmc/entity-manager/+/85455 6. platform-init changes https://gerrit.openbmc.org/c/openbmc/platform-init/+/85456 7. spi changes https://lore.kernel.org/all/20251121-w25q01jv_fixup-v1-1-3d175050db73@nvidia.com/ ``` ``` > curl -s -k -u 'root:0penBmc' https://10.137.203.137/redfish/v1/Chassis/NVIDIA_GB200_1/Sensors/power_NVIDIA_GB200_GPU_0_Power_0 { "@odata.id": "/redfish/v1/Chassis/NVIDIA_GB200_1/Sensors/power_NVIDIA_GB200_GPU_0_Power_0", "@odata.type": "#Sensor.v1_2_0.Sensor", "Id": "power_NVIDIA_GB200_GPU_0_Power_0", "Name": "NVIDIA GB200 GPU 0 Power 0", "PeakReading": 52.671, "PeakReadingTime": 0, "Reading": 27.214, "ReadingRangeMax": 5000.0, "ReadingRangeMin": 0.0, "ReadingType": "Power", "ReadingUnits": "W", "Status": { "Health": "OK", "State": "Enabled" } }% ```` Change-Id: I8c1ab6ce85f31419db4a1d931bf99722d24afbd7 Signed-off-by: Harshit Aghera --- docs/Redfish.md | 2 + include/dbus_utility.hpp | 1 + redfish-core/include/utils/sensor_utils.hpp | 48 ++++++++++++- .../include/utils/sensor_utils_test.cpp | 69 +++++++++++++++++++ 4 files changed, 119 insertions(+), 1 deletion(-) diff --git a/docs/Redfish.md b/docs/Redfish.md index 46357b6a9f..3f0a9ef89c 100644 --- a/docs/Redfish.md +++ b/docs/Redfish.md @@ -291,6 +291,8 @@ Fields common to all schemas #### Sensor - Implementation +- PeakReading +- PeakReadingTime - Reading - ReadingBasis - ReadingRangeMax diff --git a/include/dbus_utility.hpp b/include/dbus_utility.hpp index 407909a181..17589c49d1 100644 --- a/include/dbus_utility.hpp +++ b/include/dbus_utility.hpp @@ -49,6 +49,7 @@ using DbusVariantType = std::variant< std::vector, sdbusplus::message::object_path, std::tuple>>, + std::tuple>>, std::vector, std::vector>, std::vector>>, diff --git a/redfish-core/include/utils/sensor_utils.hpp b/redfish-core/include/utils/sensor_utils.hpp index f97d93f545..0e3c220d77 100644 --- a/redfish-core/include/utils/sensor_utils.hpp +++ b/redfish-core/include/utils/sensor_utils.hpp @@ -288,6 +288,47 @@ inline sensor::ReadingType toReadingType(std::string_view sensorType) } // namespace sensors +// represents metric Id, metadata, reading value and timestamp of single +// reading update in milliseconds +using Reading = std::tuple; +// represents multiple independent readings +using Readings = std::vector; +// represents a timestamp and multiple independent readings +using Statistics = std::tuple; +// represents sensor's path, its metadata +using SensorPaths = + std::vector>; +// represents reading parameters for statistics readings +using ReadingParameters = + std::vector>; + +inline void updateSensorStatistics( + nlohmann::json& sensorJson, const std::optional& statistics, + const std::optional& readingParameters) +{ + if (statistics.has_value() && readingParameters.has_value()) + { + Readings metrics = std::get<1>(*statistics); + for (const auto& [sensorPaths, operationType, metricId, duration] : + *readingParameters) + { + if (operationType == + "xyz.openbmc_project.Telemetry.Report.OperationType.Maximum") + { + if (metrics.size() == 1) + { + const auto& [id, metadata, value, timestamp] = metrics[0]; + sensorJson["PeakReading"] = value; + if (timestamp != 0) + { + sensorJson["PeakReadingTime"] = timestamp; + } + } + } + } + } +} + /** * @brief Returns the Redfish State value for the specified inventory item. * @param inventoryItem D-Bus inventory item associated with a sensor. @@ -519,11 +560,14 @@ inline void objectPropertiesToJson( bool available = true; std::optional readingBasis; std::optional implementation; + std::optional statistics; + std::optional readingParameters; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available", checkAvailable, "ReadingBasis", readingBasis, "Implementation", - implementation); + implementation, "Readings", statistics, "ReadingParameters", + readingParameters); if (!success) { messages::internalError(); @@ -583,6 +627,8 @@ inline void objectPropertiesToJson( sensorJson["Implementation"] = implementationOpt; } } + + updateSensorStatistics(sensorJson, statistics, readingParameters); } else if (sensorType == "temperature") { diff --git a/test/redfish-core/include/utils/sensor_utils_test.cpp b/test/redfish-core/include/utils/sensor_utils_test.cpp index 9cda386c73..029799e059 100644 --- a/test/redfish-core/include/utils/sensor_utils_test.cpp +++ b/test/redfish-core/include/utils/sensor_utils_test.cpp @@ -2,7 +2,9 @@ // SPDX-FileCopyrightText: Copyright OpenBMC Authors #include "utils/sensor_utils.hpp" +#include #include +#include #include @@ -112,5 +114,72 @@ TEST(IsExcerptNode, False) EXPECT_FALSE(isExcerptNode(ChassisSubNode::unknownNode)); } +TEST(UpdateSensorStatistics, ParametersValid) +{ + nlohmann::json sensorJson; + Reading reading = + std::make_tuple("metricId1", "metadata1", 42.5, 1234567890); + Readings metrics = {reading}; + Statistics statistics = std::make_tuple(1234567890, metrics); + SensorPaths sensorPaths; + ReadingParameters readingParams = {std::make_tuple( + sensorPaths, + "xyz.openbmc_project.Telemetry.Report.OperationType.Maximum", + "metricId1", 60)}; + + updateSensorStatistics(sensorJson, statistics, readingParams); + + EXPECT_EQ(sensorJson["PeakReading"], 42.5); + EXPECT_EQ(sensorJson["PeakReadingTime"], 1234567890); +} + +TEST(UpdateSensorStatistics, EmptyMetrics) +{ + nlohmann::json sensorJson; + Readings metrics; + Statistics statistics = std::make_tuple(1234567890, metrics); + SensorPaths sensorPaths; + ReadingParameters readingParams = {std::make_tuple( + sensorPaths, + "xyz.openbmc_project.Telemetry.Report.OperationType.Maximum", + "metricId1", 60)}; + + updateSensorStatistics(sensorJson, statistics, readingParams); + + EXPECT_FALSE(sensorJson.contains("PeakReading")); + EXPECT_FALSE(sensorJson.contains("PeakReadingTime")); +} + +TEST(UpdateSensorStatistics, NonMaximumOperationType) +{ + nlohmann::json sensorJson; + Reading reading = + std::make_tuple("metricId1", "metadata1", 42.5, 1234567890); + Readings metrics = {reading}; + Statistics statistics = std::make_tuple(1234567890, metrics); + SensorPaths sensorPaths; + ReadingParameters readingParams = {std::make_tuple( + sensorPaths, + "xyz.openbmc_project.Telemetry.Report.OperationType.Minimum", + "metricId1", 60)}; + + updateSensorStatistics(sensorJson, statistics, readingParams); + + EXPECT_FALSE(sensorJson.contains("PeakReading")); + EXPECT_FALSE(sensorJson.contains("PeakReadingTime")); +} + +TEST(UpdateSensorStatistics, ParamsNullopt) +{ + nlohmann::json sensorJson; + std::optional statistics = std::nullopt; + std::optional readingParams = std::nullopt; + + updateSensorStatistics(sensorJson, statistics, readingParams); + + EXPECT_FALSE(sensorJson.contains("PeakReading")); + EXPECT_FALSE(sensorJson.contains("PeakReadingTime")); +} + } // namespace } // namespace redfish::sensor_utils From d32aff8d085db882f222a0de019385d37749b8b1 Mon Sep 17 00:00:00 2001 From: Myung Bae Date: Tue, 23 Sep 2025 10:33:55 -0700 Subject: [PATCH 07/18] Add LocationIndicatorActive for Assembly Implement LocationIndicatorActive for Assembly schema to set and get the status of the location LED. A client uses the `LocationIndicatorActive` property to physically identify or locate the assembly. The assembly is an array of AssemblyData [1], and the element of the array can be patched as explained in [2]. ``` { "Assemblies": [ {}, {}, { "LocationIndicatorActive": true }, {} ] } ``` Tested: - Validator passes. - 1. Get LocationIndicatorActive ``` curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Chassis/chassis/Assembly { "@odata.id": "/redfish/v1/Chassis/chassis/Assembly", "@odata.type": "#Assembly.v1_6_0.Assembly", "Assemblies": [ { "@odata.id": "/redfish/v1/Chassis/chassis/Assembly#/Assemblies/0", "@odata.type": "#Assembly.v1_6_0.AssemblyData", "LocationIndicatorActive": false, "MemberId": "0", ... }, { "@odata.id": "/redfish/v1/Chassis/chassis/Assembly#/Assemblies/1", "@odata.type": "#Assembly.v1_6_0.AssemblyData", "LocationIndicatorActive": false, "MemberId": "1", ... } ], "Assemblies@odata.count": 2, "Id": "Assembly", "Name": "Assembly Collection" } ``` 2. Set LocationIndicatorActive to true ``` curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" \ -X PATCH -d '{"Assemblies":[{"LocationIndicatorActive":true},{}]}' \ https://${bmc}/redfish/v1/Chassis/chassis/Assembly ``` Then we will see the location LED lit up, and the LocationIndicatorActive value becomes true. ``` curl -k -H "X-Auth-Token: $token" -X GET https://${bmc}/redfish/v1/Chassis/chassis/Assembly { "@odata.id": "/redfish/v1/Chassis/chassis/Assembly", "@odata.type": "#Assembly.v1_6_0.Assembly", "Assemblies": [ { "@odata.id": "/redfish/v1/Chassis/chassis/Assembly#/Assemblies/0", "@odata.type": "#Assembly.v1_6_0.AssemblyData", "LocationIndicatorActive": true, "MemberId": "0", ... }, { "@odata.id": "/redfish/v1/Chassis/chassis/Assembly#/Assemblies/1", "@odata.type": "#Assembly.v1_6_0.AssemblyData", "LocationIndicatorActive": false, "MemberId": "1", ... } ], "Assemblies@odata.count": 2, "Id": "Assembly", "Name": "Assembly Collection" } ``` If the input array size is different from the existing assemblies, it will cause an error like ``` curl -k -H "X-Auth-Token: $token" -H "Content-Type: application/json" \ -X PATCH -d '{"Assemblies":[{},{"LocationIndicatorActive":true},{}]}' \ https://${bmc}/redfish/v1/Chassis/chassis/Assembly { "error": { "@Message.ExtendedInfo": [ { "@odata.type": "#Message.v1_1_1.Message", "Message": "The array provided for property Assemblies exceeds the size limit 2.", "MessageArgs": [ "Assemblies", "2" ], "MessageId": "Base.1.19.ArraySizeTooLong", "MessageSeverity": "Warning", "Resolution": "Resubmit the request with an appropriate array size." } ], "code": "Base.1.19.ArraySizeTooLong", "message": "The array provided for property Assemblies exceeds the size limit 2." } }% ``` [1] https://redfish.dmtf.org/schemas/v1/Assembly.v1_6_0.json [2] https://redfishforum.com/thread/437/patch-individual-items-array-objects Change-Id: Ic2e87f5daeb7ebed161654bb54ac29e7d5daa482 Signed-off-by: Myung Bae --- docs/Redfish.md | 1 + redfish-core/lib/assembly.hpp | 98 +++++++++++++++++++++++++++++++++-- redfish-core/lib/led.hpp | 33 ++++++++---- 3 files changed, 118 insertions(+), 14 deletions(-) diff --git a/docs/Redfish.md b/docs/Redfish.md index 3f0a9ef89c..939d789c8a 100644 --- a/docs/Redfish.md +++ b/docs/Redfish.md @@ -478,6 +478,7 @@ Fields common to all schemas ###### Assembly +- LocationIndicatorActive - Model - PartNumber - SerialNumber diff --git a/redfish-core/lib/assembly.hpp b/redfish-core/lib/assembly.hpp index 4112a7941e..5a29055cb5 100644 --- a/redfish-core/lib/assembly.hpp +++ b/redfish-core/lib/assembly.hpp @@ -10,6 +10,7 @@ #include "generated/enums/resource.hpp" #include "http_request.hpp" #include "http_response.hpp" +#include "led.hpp" #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" @@ -17,6 +18,7 @@ #include "utils/asset_utils.hpp" #include "utils/chassis_utils.hpp" #include "utils/dbus_utils.hpp" +#include "utils/json_utils.hpp" #include #include @@ -27,7 +29,9 @@ #include #include +#include #include +#include #include #include #include @@ -193,7 +197,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)); @@ -210,6 +214,13 @@ inline void getAssemblyProperties( std::bind_front(afterGetDbusObject, asyncResp, assembly, assemblyJsonPtr)); + getLocationIndicatorActive( + asyncResp, assembly, [asyncResp, assemblyJsonPtr](bool asserted) { + asyncResp->res + .jsonValue[assemblyJsonPtr]["LocationIndicatorActive"] = + asserted; + }); + nlohmann::json& assemblyArray = asyncResp->res.jsonValue["Assemblies"]; asyncResp->res.jsonValue["Assemblies@odata.count"] = assemblyArray.size(); @@ -225,7 +236,7 @@ inline void afterHandleChassisAssemblyGet( { if (ec) { - BMCWEB_LOG_WARNING("Chassis {} not found", chassisID); + BMCWEB_LOG_WARNING("Chassis {} not found, ec={}", chassisID, ec); messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); return; } @@ -234,7 +245,7 @@ inline void afterHandleChassisAssemblyGet( boost::beast::http::field::link, "; rel=describedby"); - asyncResp->res.jsonValue["@odata.type"] = "#Assembly.v1_5_1.Assembly"; + asyncResp->res.jsonValue["@odata.type"] = "#Assembly.v1_6_0.Assembly"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}/Assembly", chassisID); asyncResp->res.jsonValue["Name"] = "Assembly Collection"; @@ -289,7 +300,8 @@ inline void handleChassisAssemblyHead( const std::vector& /*assemblyList*/) { if (ec) { - BMCWEB_LOG_WARNING("Chassis {} not found", chassisID); + BMCWEB_LOG_WARNING("Chassis {} not found, ec={}", chassisID, + ec); messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); return; @@ -300,6 +312,79 @@ inline void handleChassisAssemblyHead( }); } +inline void afterHandleChassisAssemblyPatch( + const std::shared_ptr& asyncResp, + const std::string& chassisID, + std::vector& assemblyData, + const boost::system::error_code& ec, + const std::vector& assemblyList) +{ + if (ec) + { + BMCWEB_LOG_WARNING("Chassis {} not found, ec={}", chassisID, ec); + messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); + return; + } + + if (assemblyData.size() > assemblyList.size()) + { + BMCWEB_LOG_WARNING( + "The number of the input Assemblies is larger than the actual number of Assemblies"); + messages::arraySizeTooLong(asyncResp->res, "Assemblies", + assemblyList.size()); + return; + } + if (assemblyData.size() < assemblyList.size()) + { + BMCWEB_LOG_WARNING( + "The number of the input Assemblies is smaller than the actual number of Assemblies"); + messages::arraySizeTooShort(asyncResp->res, "Assemblies", + assemblyList.size()); + return; + } + + std::size_t assemblyIndex = 0; + for (nlohmann::json::object_t& item : assemblyData) + { + std::optional locationIndicatorActive; + if (json_util::readJsonObject(item, asyncResp->res, + "LocationIndicatorActive", + locationIndicatorActive)) + { + if (locationIndicatorActive.has_value()) + { + const auto& assembly = assemblyList[assemblyIndex]; + setLocationIndicatorActive(asyncResp, assembly, + *locationIndicatorActive); + } + } + assemblyIndex++; + } +} + +inline void handleChassisAssemblyPatch( + App& app, const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& chassisID) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + + std::vector assemblyData; + if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Assemblies", + assemblyData)) + { + return; + } + + assembly_utils::getChassisAssembly( + asyncResp, chassisID, + std::bind_front(afterHandleChassisAssemblyPatch, asyncResp, chassisID, + assemblyData)); +} + inline void requestRoutesAssembly(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Chassis//Assembly/") @@ -311,6 +396,11 @@ inline void requestRoutesAssembly(App& app) .privileges(redfish::privileges::getAssembly) .methods(boost::beast::http::verb::get)( std::bind_front(handleChassisAssemblyGet, std::ref(app))); + + BMCWEB_ROUTE(app, "/redfish/v1/Chassis//Assembly/") + .privileges(redfish::privileges::patchAssembly) + .methods(boost::beast::http::verb::patch)( + std::bind_front(handleChassisAssemblyPatch, std::ref(app))); } } // namespace redfish diff --git a/redfish-core/lib/led.hpp b/redfish-core/lib/led.hpp index b74dc4d5a0..a3dc1c98e7 100644 --- a/redfish-core/lib/led.hpp +++ b/redfish-core/lib/led.hpp @@ -306,6 +306,7 @@ inline void getLedGroupPath( inline void afterGetLedState( const std::shared_ptr& asyncResp, + const std::function& callback, const boost::system::error_code& ec, bool assert) { if (ec) @@ -319,10 +320,11 @@ inline void afterGetLedState( return; } - asyncResp->res.jsonValue["LocationIndicatorActive"] = assert; + callback(assert); } inline void getLedState(const std::shared_ptr& asyncResp, + const std::function& callback, const boost::system::error_code& ec, const std::string& ledGroupPath, const std::string& service) @@ -346,7 +348,7 @@ inline void getLedState(const std::shared_ptr& asyncResp, sdbusplus::asio::getProperty( *crow::connections::systemBus, service, ledGroupPath, "xyz.openbmc_project.Led.Group", "Asserted", - std::bind_front(afterGetLedState, asyncResp)); + std::bind_front(afterGetLedState, asyncResp, callback)); } /** @@ -355,20 +357,31 @@ inline void getLedState(const std::shared_ptr& asyncResp, * @param[in] asyncResp Shared pointer for generating response * message. * @param[in] objPath Object path on PIM - * + * @param[in] callback to pass value * @return None. */ + inline void getLocationIndicatorActive( const std::shared_ptr& asyncResp, - const std::string& objPath) + const std::string& objPath, std::function&& callback) { 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); - }); + getLedGroupPath( + asyncResp, objPath, + [asyncResp, callback = std::move(callback)]( + const boost::system::error_code& ec, + const std::string& ledGroupPath, const std::string& service) { + getLedState(asyncResp, callback, ec, ledGroupPath, service); + }); +} + +inline void getLocationIndicatorActive( + const std::shared_ptr& asyncResp, + const std::string& objPath) +{ + getLocationIndicatorActive(asyncResp, objPath, [asyncResp](bool asserted) { + asyncResp->res.jsonValue["LocationIndicatorActive"] = asserted; + }); } inline void setLedState(const std::shared_ptr& asyncResp, From c583f9000076a8bdc1acff511e4c7db5b4b3109b Mon Sep 17 00:00:00 2001 From: Igor Kanyuka Date: Mon, 1 Dec 2025 12:28:05 +0000 Subject: [PATCH 08/18] Expose Chassis state on Transitioning If chassis is in one of transitioning states, Redfish interface does not expose PowerState and State.Status. This causes tests failure and users cannot determine the actual state using Redfish. Expose PowerState and State.Status for TransitioningToOff and TransitioningToOn to fix this. Tested: Robot tests testing power operations now pass, before when the D-Bus state was TransitioningToOff or TransitioningToOn they would error because they could not find attributes they need. Also, ran rf_service_validator against BMC with D-Bus state set to TransitioningToOff and made sure it succeeded. Change-Id: I9c446a107bac6a69e962ef81de82920c9cce21ae Signed-off-by: Igor Kanyuka --- redfish-core/lib/chassis.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp index a35e2cdb7c..90e3b60e83 100644 --- a/redfish-core/lib/chassis.hpp +++ b/redfish-core/lib/chassis.hpp @@ -192,6 +192,24 @@ inline void getChassisState(std::shared_ptr asyncResp) asyncResp->res.jsonValue["Status"]["State"] = resource::State::StandbyOffline; } + else if ( + chassisState == + "xyz.openbmc_project.State.Chassis.PowerState.TransitioningToOff") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOff; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::StandbyOffline; + } + else if ( + chassisState == + "xyz.openbmc_project.State.Chassis.PowerState.TransitioningToOn") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOn; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Starting; + } }); } From aa0368aeaaf730950af6f94b554db0d0bc7d489e Mon Sep 17 00:00:00 2001 From: Ramya Sivakumar Date: Fri, 5 Dec 2025 18:23:24 +0530 Subject: [PATCH 09/18] Removed unused header in redfish-core/include Remove unused includes and include boost/circular_buffer/base.hpp in event_service_manager and event_matches_filter Tested: Code compiles. Change-Id: Ib90374b822560c063f3047037bdb87f36685f48c Signed-off-by: Ramya Sivakumar --- redfish-core/include/event_matches_filter.hpp | 1 - redfish-core/include/event_service_manager.hpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/redfish-core/include/event_matches_filter.hpp b/redfish-core/include/event_matches_filter.hpp index 66a50a3429..1f327a3ef3 100644 --- a/redfish-core/include/event_matches_filter.hpp +++ b/redfish-core/include/event_matches_filter.hpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp index 566b3ad83b..5f3e99aebd 100644 --- a/redfish-core/include/event_service_manager.hpp +++ b/redfish-core/include/event_service_manager.hpp @@ -20,7 +20,6 @@ #include "utils/time_utils.hpp" #include -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include #include From e32edd47e374ceb93d89ac1d2c3158c39ff6e5cf Mon Sep 17 00:00:00 2001 From: Janet Adkins Date: Thu, 20 Nov 2025 15:52:19 -0600 Subject: [PATCH 10/18] Sensors: Reorganize objectPropertiesToJson for clarity A review comment for a different commit noted the length of the objectPropertiesToJson() method. [1] Here I am restructuring the method to reduce its complexity. The restructure factors out different sections of the work: - Filling in the basic identity of the sensor. This is split into two different methods. One for handling through the Redfish Sensors path and the others for the deprecated Redfish Power/Thermal paths. (Identity includes the Name/Id and type information for the sensor. - Common function for filling in Sensor status. - Mapping other available D-Bus properties to their Redfish property name. [1] https://gerrit.openbmc.org/c/openbmc/bmcweb/+/85103/comment/621fe109_9157ec50/ Tested: Using hardware simulator - Enabled redfish-allow-deprecated-power-thermal and confirmed response same before and after change for: - /redfish/v1/Chassis/chassis/Sensors - /redfish/v1/Chassis/chassis/Thermal - /redfish/v1/Chassis/chassis/Power - /redfish/v1/Chassis/chassis/ThermalSubsystem/ThermalMetrics - /redfish/v1/Chassis/chassis/EnvironmentMetrics - Each Member under /redfish/v1/Chassis/chassis/Sensors/ (i.e. /redfish/v1/Chassis/chassis/Sensors/{}) Change-Id: I1524c5c3b1f98a95dc2ed82d395897cd5f8af7d2 Signed-off-by: Janet Adkins --- redfish-core/include/utils/sensor_utils.hpp | 525 +++++++++++--------- 1 file changed, 287 insertions(+), 238 deletions(-) diff --git a/redfish-core/include/utils/sensor_utils.hpp b/redfish-core/include/utils/sensor_utils.hpp index 0e3c220d77..dd8150cff5 100644 --- a/redfish-core/include/utils/sensor_utils.hpp +++ b/redfish-core/include/utils/sensor_utils.hpp @@ -501,6 +501,278 @@ inline sensor::ImplementationType dBusSensorImplementationToRedfish( return sensor::ImplementationType::Invalid; } +inline void fillSensorStatus( + const dbus::utility::DBusPropertiesMap& propertiesDict, + nlohmann::json& sensorJson, InventoryItem* inventoryItem) +{ + const bool* checkAvailable = nullptr; + bool available = true; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available", + checkAvailable); + if (!success) + { + messages::internalError(); + } + if (checkAvailable != nullptr) + { + available = *checkAvailable; + } + + sensorJson["Status"]["State"] = getState(inventoryItem, available); + sensorJson["Status"]["Health"] = + getHealth(sensorJson, propertiesDict, inventoryItem); +} + +inline void fillSensorIdentity( + std::string_view sensorName, std::string_view sensorType, + const dbus::utility::DBusPropertiesMap& propertiesDict, + nlohmann::json& sensorJson) +{ + std::string subNodeEscaped = getSensorId(sensorName, sensorType); + // For sensors in SensorCollection we set Id instead of MemberId, + // including power sensors. + sensorJson["Id"] = std::move(subNodeEscaped); + + std::string sensorNameEs(sensorName); + std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); + sensorJson["Name"] = std::move(sensorNameEs); + sensorJson["@odata.type"] = "#Sensor.v1_11_0.Sensor"; + + sensor::ReadingType readingType = sensors::toReadingType(sensorType); + if (readingType == sensor::ReadingType::Invalid) + { + BMCWEB_LOG_ERROR("Redfish cannot map reading type for {}", sensorType); + } + else + { + sensorJson["ReadingType"] = readingType; + } + + std::string_view readingUnits = sensors::toReadingUnits(sensorType); + if (readingUnits.empty()) + { + BMCWEB_LOG_ERROR("Redfish cannot map reading unit for {}", sensorType); + } + else + { + sensorJson["ReadingUnits"] = readingUnits; + } + + std::optional readingBasis; + std::optional implementation; + std::optional statistics; + std::optional readingParameters; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesDict, "ReadingBasis", + readingBasis, "Implementation", implementation, "Readings", statistics, + "ReadingParameters", readingParameters); + if (!success) + { + messages::internalError(); + } + + if (readingBasis.has_value()) + { + sensor::ReadingBasisType readingBasisOpt = + dBusSensorReadingBasisToRedfish(*readingBasis); + if (readingBasisOpt != sensor::ReadingBasisType::Invalid) + { + sensorJson["ReadingBasis"] = readingBasisOpt; + } + } + + if (implementation.has_value()) + { + sensor::ImplementationType implementationOpt = + dBusSensorImplementationToRedfish(*implementation); + if (implementationOpt != sensor::ImplementationType::Invalid) + { + sensorJson["Implementation"] = implementationOpt; + } + } + + updateSensorStatistics(sensorJson, statistics, readingParameters); +} + +inline bool fillPowerThermalIdentity( + std::string_view sensorName, std::string_view sensorType, + nlohmann::json& sensorJson, InventoryItem* inventoryItem, + nlohmann::json::json_pointer& unit, bool& forceToInt) +{ + if (sensorType != "power") + { + // Set MemberId and Name for non-power sensors. For PowerSupplies + // and PowerControl, those properties have more general values + // because multiple sensors can be stored in the same JSON object. + std::string sensorNameEs(sensorName); + std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); + sensorJson["Name"] = std::move(sensorNameEs); + } + + if (sensorType == "temperature") + { + unit = "/ReadingCelsius"_json_pointer; + sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature"; + // TODO(ed) Documentation says that path should be type fan_tach, + // implementation seems to implement fan + return true; + } + + if (sensorType == "fan" || sensorType == "fan_tach") + { + unit = "/Reading"_json_pointer; + sensorJson["ReadingUnits"] = thermal::ReadingUnits::RPM; + sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; + if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) + { + setLedState(sensorJson, inventoryItem); + } + forceToInt = true; + return true; + } + + if (sensorType == "fan_pwm") + { + unit = "/Reading"_json_pointer; + sensorJson["ReadingUnits"] = thermal::ReadingUnits::Percent; + sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; + if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) + { + setLedState(sensorJson, inventoryItem); + } + forceToInt = true; + return true; + } + + if (sensorType == "voltage") + { + unit = "/ReadingVolts"_json_pointer; + sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage"; + return true; + } + + if (sensorType == "power") + { + std::string lower; + std::ranges::transform(sensorName, std::back_inserter(lower), + bmcweb::asciiToLower); + if (lower == "total_power") + { + sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl"; + // Put multiple "sensors" into a single PowerControl, so have + // generic names for MemberId and Name. Follows Redfish mockup. + sensorJson["MemberId"] = "0"; + sensorJson["Name"] = "Chassis Power Control"; + unit = "/PowerConsumedWatts"_json_pointer; + } + else if (lower.find("input") != std::string::npos) + { + unit = "/PowerInputWatts"_json_pointer; + } + else + { + unit = "/PowerOutputWatts"_json_pointer; + } + return true; + } + + BMCWEB_LOG_ERROR("Redfish cannot map object type for {}", sensorName); + return false; +} + +// Map of dbus interface name, dbus property name and redfish property_name +using SensorPropertyMap = std::tuple; +using SensorPropertyList = std::vector; + +inline void mapPropertiesBySubnode( + std::string_view sensorType, ChassisSubNode chassisSubNode, + SensorPropertyList& properties, nlohmann::json::json_pointer& unit, + bool isExcerpt) +{ + // unit contains the redfish property_name based on the sensor type/node + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); + + if (isExcerpt) + { + // Excerpts don't have any of these extended properties + return; + } + + if (chassisSubNode == ChassisSubNode::sensorsNode) + { + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", + "/Thresholds/UpperCaution/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", + "/Thresholds/LowerCaution/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", + "/Thresholds/UpperCritical/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", + "/Thresholds/LowerCritical/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.HardShutdown", + "HardShutdownHigh", "/Thresholds/UpperFatal/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.HardShutdown", + "HardShutdownLow", "/Thresholds/LowerFatal/Reading"_json_pointer); + + /* Add additional properties specific to sensorType */ + if (sensorType == "fan_tach") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", + "/SpeedRPM"_json_pointer); + } + } + else if (sensorType != "power") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningHigh", + "/UpperThresholdNonCritical"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningLow", + "/LowerThresholdNonCritical"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalHigh", + "/UpperThresholdCritical"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalLow", + "/LowerThresholdCritical"_json_pointer); + } + + // TODO Need to get UpperThresholdFatal and LowerThresholdFatal + + if (chassisSubNode == ChassisSubNode::sensorsNode) + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "/ReadingRangeMin"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "/ReadingRangeMax"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy", + "Accuracy", "/Accuracy"_json_pointer); + } + else if (sensorType == "temperature") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "/MinReadingRangeTemp"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "/MaxReadingRangeTemp"_json_pointer); + } + else if (sensorType != "power") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "/MinReadingRange"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "/MaxReadingRange"_json_pointer); + } +} + /** * @brief Builds a json sensor representation of a sensor. * @param sensorName The name of the sensor to be built @@ -537,254 +809,31 @@ inline void objectPropertiesToJson( { if (chassisSubNode == ChassisSubNode::sensorsNode) { - std::string subNodeEscaped = getSensorId(sensorName, sensorType); - // For sensors in SensorCollection we set Id instead of MemberId, - // including power sensors. - sensorJson["Id"] = std::move(subNodeEscaped); - - std::string sensorNameEs(sensorName); - std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); - sensorJson["Name"] = std::move(sensorNameEs); - } - else if (sensorType != "power") - { - // Set MemberId and Name for non-power sensors. For PowerSupplies - // and PowerControl, those properties have more general values - // because multiple sensors can be stored in the same JSON object. - std::string sensorNameEs(sensorName); - std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); - sensorJson["Name"] = std::move(sensorNameEs); - } - - const bool* checkAvailable = nullptr; - bool available = true; - std::optional readingBasis; - std::optional implementation; - std::optional statistics; - std::optional readingParameters; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available", - checkAvailable, "ReadingBasis", readingBasis, "Implementation", - implementation, "Readings", statistics, "ReadingParameters", - readingParameters); - if (!success) - { - messages::internalError(); - } - if (checkAvailable != nullptr) - { - available = *checkAvailable; - } - - sensorJson["Status"]["State"] = getState(inventoryItem, available); - sensorJson["Status"]["Health"] = - getHealth(sensorJson, propertiesDict, inventoryItem); - - if (chassisSubNode == ChassisSubNode::sensorsNode) - { - sensorJson["@odata.type"] = "#Sensor.v1_11_0.Sensor"; - - sensor::ReadingType readingType = - sensors::toReadingType(sensorType); - if (readingType == sensor::ReadingType::Invalid) - { - BMCWEB_LOG_ERROR("Redfish cannot map reading type for {}", - sensorType); - } - else - { - sensorJson["ReadingType"] = readingType; - } - - std::string_view readingUnits = sensors::toReadingUnits(sensorType); - if (readingUnits.empty()) - { - BMCWEB_LOG_ERROR("Redfish cannot map reading unit for {}", - sensorType); - } - else - { - sensorJson["ReadingUnits"] = readingUnits; - } - - if (readingBasis.has_value()) - { - sensor::ReadingBasisType readingBasisOpt = - dBusSensorReadingBasisToRedfish(*readingBasis); - if (readingBasisOpt != sensor::ReadingBasisType::Invalid) - { - sensorJson["ReadingBasis"] = readingBasisOpt; - } - } - - if (implementation.has_value()) - { - sensor::ImplementationType implementationOpt = - dBusSensorImplementationToRedfish(*implementation); - if (implementationOpt != sensor::ImplementationType::Invalid) - { - sensorJson["Implementation"] = implementationOpt; - } - } - - updateSensorStatistics(sensorJson, statistics, readingParameters); - } - else if (sensorType == "temperature") - { - unit = "/ReadingCelsius"_json_pointer; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature"; - // TODO(ed) Documentation says that path should be type fan_tach, - // implementation seems to implement fan - } - else if (sensorType == "fan" || sensorType == "fan_tach") - { - unit = "/Reading"_json_pointer; - sensorJson["ReadingUnits"] = thermal::ReadingUnits::RPM; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; - if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) - { - setLedState(sensorJson, inventoryItem); - } - forceToInt = true; - } - else if (sensorType == "fan_pwm") - { - unit = "/Reading"_json_pointer; - sensorJson["ReadingUnits"] = thermal::ReadingUnits::Percent; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; - if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) - { - setLedState(sensorJson, inventoryItem); - } - forceToInt = true; + fillSensorIdentity(sensorName, sensorType, propertiesDict, + sensorJson); } - else if (sensorType == "voltage") - { - unit = "/ReadingVolts"_json_pointer; - sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage"; - } - else if (sensorType == "power") + else { - std::string lower; - std::ranges::transform(sensorName, std::back_inserter(lower), - bmcweb::asciiToLower); - if (lower == "total_power") - { - sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl"; - // Put multiple "sensors" into a single PowerControl, so have - // generic names for MemberId and Name. Follows Redfish mockup. - sensorJson["MemberId"] = "0"; - sensorJson["Name"] = "Chassis Power Control"; - unit = "/PowerConsumedWatts"_json_pointer; - } - else if (lower.find("input") != std::string::npos) + bool filledOk = fillPowerThermalIdentity( + sensorName, sensorType, sensorJson, inventoryItem, unit, + std::ref(forceToInt)); + if (!filledOk) { - unit = "/PowerInputWatts"_json_pointer; - } - else - { - unit = "/PowerOutputWatts"_json_pointer; + return; } } - else - { - BMCWEB_LOG_ERROR("Redfish cannot map object type for {}", - sensorName); - return; - } + + fillSensorStatus(propertiesDict, sensorJson, inventoryItem); } // Map of dbus interface name, dbus property name and redfish property_name - std::vector< - std::tuple> - properties; - - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); - - if (!isExcerpt) - { - if (chassisSubNode == ChassisSubNode::sensorsNode) - { - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", - "/Thresholds/UpperCaution/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", - "/Thresholds/LowerCaution/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", - "/Thresholds/UpperCritical/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", - "/Thresholds/LowerCritical/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.HardShutdown", - "HardShutdownHigh", - "/Thresholds/UpperFatal/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.HardShutdown", - "HardShutdownLow", - "/Thresholds/LowerFatal/Reading"_json_pointer); - - /* Add additional properties specific to sensorType */ - if (sensorType == "fan_tach") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "Value", "/SpeedRPM"_json_pointer); - } - } - else if (sensorType != "power") - { - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", - "/UpperThresholdNonCritical"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", - "/LowerThresholdNonCritical"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", - "/UpperThresholdCritical"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", - "/LowerThresholdCritical"_json_pointer); - } - - // TODO Need to get UpperThresholdFatal and LowerThresholdFatal + SensorPropertyList properties; - if (chassisSubNode == ChassisSubNode::sensorsNode) - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MinValue", - "/ReadingRangeMin"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MaxValue", - "/ReadingRangeMax"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy", - "Accuracy", "/Accuracy"_json_pointer); - } - else if (sensorType == "temperature") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MinValue", - "/MinReadingRangeTemp"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MaxValue", - "/MaxReadingRangeTemp"_json_pointer); - } - else if (sensorType != "power") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MinValue", - "/MinReadingRange"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MaxValue", - "/MaxReadingRange"_json_pointer); - } - } + // Add additional property mappings based on the sensor type/node + mapPropertiesBySubnode(sensorType, chassisSubNode, properties, unit, + isExcerpt); - for (const std::tuple& p : properties) + for (const SensorPropertyMap& p : properties) { for (const auto& [valueName, valueVariant] : propertiesDict) { From 9ad2c6f422bc0860314b853c287283dcbd29430d Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Wed, 22 Oct 2025 16:02:35 -0700 Subject: [PATCH 11/18] Update to boost 1.89 Update the subtree boost to 1.89; Keep the "required" version at 1.88 because that's currently what yocto uses. Now that we don't need old versions, the branch for boost 1.84 support is removed, and we can universally pull in boost::process as a library. Additionally boost::core::string_view has defined a std::formatter nearly identical to what was done there, so there's now a conflict. Add a version check and shift to the boost provided formatter when it's available. Tested: Code builds out of tree correctly. Change-Id: I15a10da084da8f9d9460781b16a0fdc92987fc9a Signed-off-by: Ed Tanous --- include/boost_formatters.hpp | 12 ------------ meson.build | 21 +++++---------------- subprojects/boost.wrap | 8 ++++---- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/include/boost_formatters.hpp b/include/boost_formatters.hpp index bf8397faa6..e675c3b17e 100644 --- a/include/boost_formatters.hpp +++ b/include/boost_formatters.hpp @@ -52,16 +52,4 @@ struct std::formatter } }; -template StringView> -struct std::formatter -{ - constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - auto format(const StringView& msg, auto& ctx) const - { - return std::format_to(ctx.out(), "{}", std::string_view(msg)); - } -}; // NOLINTEND(readability-convert-member-functions-to-static, cert-dcl58-cpp) diff --git a/meson.build b/meson.build index 93c26b72c8..f2c34c1dfa 100644 --- a/meson.build +++ b/meson.build @@ -67,7 +67,7 @@ incdir = [ ] # Add compiler arguments -boost_flags = ['-Wno-unused-parameter'] +boost_flags = ['-Wno-pedantic'] nghttp2_flags = [] if (cxx.get_id() == 'clang') if (cxx.version().version_compare('<17.0')) @@ -291,25 +291,13 @@ bmcweb_dependencies += nlohmann_json_dep boost = dependency( 'boost', - modules: ['url'], - version: '>=1.84.0', - required: false, + modules: ['url', 'process'], + version: '>=1.88.0', static: true, + required: false, include_type: 'system', ) -# Boost version is 1.86 or higher to include the 'process' module -if boost.version().version_compare('>=1.86.0') - boost = dependency( - 'boost', - modules: ['url', 'process'], - version: '>=1.86.0', - static: true, - required: false, - include_type: 'system', - ) -endif - if boost.found() bmcweb_dependencies += [boost] else @@ -333,6 +321,7 @@ else 'CMAKE_C_FLAGS': ' '.join(boost_flags), 'BOOST_INCLUDE_LIBRARIES': ';'.join(boost_libs), 'BUILD_SHARED_LIBS': 'OFF', + 'BOOST_PROCESS_USE_STD_FS': 'ON', }, ) diff --git a/subprojects/boost.wrap b/subprojects/boost.wrap index b6ce680450..7094d06899 100644 --- a/subprojects/boost.wrap +++ b/subprojects/boost.wrap @@ -1,9 +1,9 @@ [wrap-file] -directory = boost-1.87.0 +directory = boost-1.89.0 -source_url = https://github.com/boostorg/boost/releases/download/boost-1.87.0/boost-1.87.0-cmake.tar.gz -source_hash = 78fbf579e3caf0f47517d3fb4d9301852c3154bfecdc5eeebd9b2b0292366f5b -source_filename = 1_87_0.tar.gz +source_url = https://github.com/boostorg/boost/releases/download/boost-1.89.0/boost-1.89.0-cmake.tar.gz +source_hash = 954a01219bf818c7fb850fa610c2c8c71a4fa28fa32a1900056bcb6ff58cf908 +source_filename = 1_89_0.tar.gz [provide] boost = boost_dep From 5261ea9632ed6f3fe85e0d66b1f4258de56bbaea Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Sun, 23 Nov 2025 18:17:12 +0100 Subject: [PATCH 12/18] Improvements for object path creation under ComputerSystem Generally we use std::format for string mutations. Update systems_utils.hpp and systems.hpp object path creation accordingly. This also removes the call to std::to_string for computerSystemIndex. Tested: Code compiles. Change-Id: I9bb7465a304c5bfc114cb2b2653cb7416b359f40 Signed-off-by: Oliver Brewka --- redfish-core/include/utils/systems_utils.hpp | 36 ++--- redfish-core/lib/systems.hpp | 143 ++++++++++--------- 2 files changed, 93 insertions(+), 86 deletions(-) diff --git a/redfish-core/include/utils/systems_utils.hpp b/redfish-core/include/utils/systems_utils.hpp index 288364798e..85d25aeda1 100644 --- a/redfish-core/include/utils/systems_utils.hpp +++ b/redfish-core/include/utils/systems_utils.hpp @@ -219,44 +219,48 @@ inline void getComputerSystemIndex( inline sdbusplus::message::object_path getHostStateObjectPath( const uint64_t computerSystemIndex) { - const sdbusplus::message::object_path hostStatePath( - "/xyz/openbmc_project/state/host" + - std::to_string(computerSystemIndex)); - - return hostStatePath; + sdbusplus::message::object_path hostPath("/xyz/openbmc_project/state"); + hostPath /= std::format("host{}", computerSystemIndex); + return hostPath; } inline std::string getHostStateServiceName(const uint64_t computerSystemIndex) { - std::string hostStateService = "xyz.openbmc_project.State.Host"; if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - hostStateService += std::to_string(computerSystemIndex); + return std::format("xyz.openbmc_project.State.Host{}", + computerSystemIndex); } - return hostStateService; + return "xyz.openbmc_project.State.Host"; } inline sdbusplus::message::object_path getChassisStateObjectPath( const uint64_t computerSystemIndex) { - const sdbusplus::message::object_path chassisStatePath( - "/xyz/openbmc_project/state/chassis" + - std::to_string(computerSystemIndex)); - - return chassisStatePath; + sdbusplus::message::object_path chassisPath("/xyz/openbmc_project/state"); + chassisPath /= std::format("chassis{}", computerSystemIndex); + return chassisPath; } inline std::string getChassisStateServiceName( const uint64_t computerSystemIndex) { - std::string chassisStateService = "xyz.openbmc_project.State.Chassis"; if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - chassisStateService += std::to_string(computerSystemIndex); + return std::format("xyz.openbmc_project.State.Chassis{}", + computerSystemIndex); } - return chassisStateService; + return "xyz.openbmc_project.State.Chassis"; +} + +inline sdbusplus::message::object_path getControlObjectPath( + const uint64_t computerSystemIndex) +{ + sdbusplus::message::object_path controlPath("/xyz/openbmc_project/control"); + controlPath /= std::format("host{}", computerSystemIndex); + return controlPath; } inline void afterGetValidSystemsPath( diff --git a/redfish-core/lib/systems.hpp b/redfish-core/lib/systems.hpp index 345a47f85f..2a43f4809e 100644 --- a/redfish-core/lib/systems.hpp +++ b/redfish-core/lib/systems.hpp @@ -453,10 +453,10 @@ inline void getHostState(const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { BMCWEB_LOG_DEBUG("Get host information."); - sdbusplus::message::object_path path = - systems_utils::getHostStateObjectPath(computerSystemIndex); + dbus::utility::getProperty( - systems_utils::getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Host", "CurrentHostState", [asyncResp](const boost::system::error_code& ec, const std::string& hostState) { @@ -757,10 +757,9 @@ inline int assignBootParameters( inline void getBootProgress(const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path = - systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - systems_utils::getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Boot.Progress", "BootProgress", [asyncResp](const boost::system::error_code ec, const std::string& bootProgressStr) { @@ -790,10 +789,9 @@ inline void getBootProgressLastStateTime( const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path = - systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - systems_utils::getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate", [asyncResp](const boost::system::error_code& ec, const uint64_t lastStateTime) { @@ -827,12 +825,12 @@ inline void getBootOverrideType( const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; dbus::utility::getProperty( - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.Type", "BootType", [asyncResp](const boost::system::error_code& ec, const std::string& bootType) { @@ -872,11 +870,12 @@ inline void getBootOverrideMode( const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; + dbus::utility::getProperty( - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.Mode", "BootMode", [asyncResp](const boost::system::error_code& ec, const std::string& bootModeStr) { @@ -927,12 +926,12 @@ inline void getBootOverrideSource( const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; dbus::utility::getProperty( - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.Source", "BootSource", [asyncResp, computerSystemIndex](const boost::system::error_code& ec, const std::string& bootSourceStr) { @@ -987,15 +986,15 @@ inline void processBootOverrideEnable( return; } - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; - path /= "one_time"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; + controlPath /= "one_time"; // If boot source override is enabled, we need to check 'one_time' // property to set a correct value for the "BootSourceOverrideEnabled" dbus::utility::getProperty( - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Object.Enable", "Enabled", [asyncResp](const boost::system::error_code& ec, bool oneTimeSetting) { if (ec) @@ -1030,12 +1029,12 @@ inline void getBootOverrideEnable( const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; dbus::utility::getProperty( - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Object.Enable", "Enabled", [asyncResp, computerSystemIndex](const boost::system::error_code& ec, const bool bootOverrideEnable) { @@ -1093,10 +1092,10 @@ inline void getLastResetTime( const uint64_t computerSystemIndex) { BMCWEB_LOG_DEBUG("Getting System Last Reset Time"); - sdbusplus::message::object_path path = - systems_utils::getChassisStateObjectPath(computerSystemIndex); + dbus::utility::getProperty( - systems_utils::getChassisStateServiceName(computerSystemIndex), path, + systems_utils::getChassisStateServiceName(computerSystemIndex), + systems_utils::getChassisStateObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Chassis", "LastStateChangeTime", [asyncResp](const boost::system::error_code& ec, uint64_t lastResetTime) { @@ -1134,10 +1133,10 @@ inline void getAutomaticRebootAttempts( const uint64_t computerSystemIndex) { BMCWEB_LOG_DEBUG("Get Automatic Retry policy"); - sdbusplus::message::object_path path = - systems_utils::getHostStateObjectPath(computerSystemIndex); + dbus::utility::getAllProperties( - systems_utils::getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), "xyz.openbmc_project.Control.Boot.RebootAttempts", [asyncResp{asyncResp}]( const boost::system::error_code& ec, @@ -1197,12 +1196,12 @@ inline void getAutomaticRetryPolicy( { BMCWEB_LOG_DEBUG("Get Automatic Retry policy"); - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "auto_reboot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "auto_reboot"; dbus::utility::getProperty( - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", [asyncResp, computerSystemIndex](const boost::system::error_code& ec, bool autoRebootEnabled) { @@ -1305,12 +1304,12 @@ inline void getPowerRestorePolicy( { BMCWEB_LOG_DEBUG("Get power restore policy"); - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "power_restore_policy"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "power_restore_policy"; dbus::utility::getProperty( - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", [asyncResp](const boost::system::error_code& ec, const std::string& policy) { @@ -1596,11 +1595,12 @@ inline void setBootType(const std::shared_ptr& asyncResp, // Act on validated parameters BMCWEB_LOG_DEBUG("DBUS boot type: {}", bootTypeStr); - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; + setDbusProperty(asyncResp, "Boot/BootSourceOverrideMode", - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.Type", "BootType", bootTypeStr); } @@ -1655,11 +1655,12 @@ inline void setBootEnable(const std::shared_ptr& asyncResp, // Act on validated parameters BMCWEB_LOG_DEBUG("DBUS boot override enable: {}", bootOverrideEnable); - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; + setDbusProperty(asyncResp, "Boot/BootSourceOverrideEnabled", - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Object.Enable", "Enabled", bootOverrideEnable); @@ -1673,9 +1674,9 @@ inline void setBootEnable(const std::shared_ptr& asyncResp, BMCWEB_LOG_DEBUG("DBUS boot override persistent: {}", bootOverridePersistent); - path /= "one_time"; + controlPath /= "one_time"; setDbusProperty(asyncResp, "Boot/BootSourceOverrideEnabled", - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Object.Enable", "Enabled", !bootOverridePersistent); } @@ -1720,15 +1721,16 @@ inline void setBootModeOrSource( BMCWEB_LOG_DEBUG("DBUS boot source: {}", bootSourceStr); BMCWEB_LOG_DEBUG("DBUS boot mode: {}", bootModeStr); - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "boot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "boot"; + setDbusProperty(asyncResp, "Boot/BootSourceOverrideTarget", - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.Source", "BootSource", bootSourceStr); setDbusProperty(asyncResp, "Boot/BootSourceOverrideTarget", - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.Mode", "BootMode", bootModeStr); } @@ -1912,11 +1914,12 @@ inline void setAutomaticRetry( return; } - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "auto_reboot"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "auto_reboot"; + setDbusProperty(asyncResp, "Boot/AutomaticRetryConfig", - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", autoRebootEnabled); } @@ -1962,11 +1965,12 @@ inline void setPowerRestorePolicy( return; } - sdbusplus::message::object_path path("/xyz/openbmc_project/control/host" + - std::to_string(computerSystemIndex)); - path /= "power_restore_policy"; + sdbusplus::message::object_path controlPath = + systems_utils::getControlObjectPath(computerSystemIndex); + controlPath /= "power_restore_policy"; + setDbusProperty(asyncResp, "PowerRestorePolicy", - "xyz.openbmc_project.Settings", path, + "xyz.openbmc_project.Settings", controlPath, "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", powerRestorePolicy); } @@ -3527,10 +3531,9 @@ inline void getAllowedHostTransitions( const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path = - systems_utils::getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty>( - systems_utils::getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Host", "AllowedHostTransitions", std::bind_front(afterGetAllowedHostTransitions, asyncResp)); } From 2ff8e600829a5ea88886677ff33da44f15d48951 Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Mon, 15 Dec 2025 16:46:17 +0100 Subject: [PATCH 13/18] Refactor ManagerForChassis handler Extract the lambda into its own function. Tested: Code compiles. Change-Id: Ie6f2c9097d0eea41b650a93e05e49c2af4a24530 Signed-off-by: Oliver Brewka --- redfish-core/lib/managers.hpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 3c5c53f723..ebf90d1fc7 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -682,6 +682,20 @@ inline void getManagerData(const std::shared_ptr& asyncResp, } } +inline void getManagedChassis( + const std::string& chassisId, + const std::shared_ptr& asyncResp) +{ + asyncResp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; + nlohmann::json::array_t managerForChassis; + nlohmann::json::object_t manager; + manager["@odata.id"] = + boost::urls::format("/redfish/v1/Chassis/{}", chassisId); + managerForChassis.emplace_back(std::move(manager)); + asyncResp->res.jsonValue["Links"]["ManagerForChassis"] = + std::move(managerForChassis); +} + inline void handleManagerGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, @@ -791,21 +805,7 @@ inline void handleManagerGet( managerDiagnosticData["@odata.id"] = boost::urls::format( "/redfish/v1/Managers/{}/ManagerDiagnosticData", managerId); - getMainChassisId( - asyncResp, [](const std::string& chassisId, - const std::shared_ptr& aRsp) { - aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; - nlohmann::json::array_t managerForChassis; - nlohmann::json::object_t managerObj; - boost::urls::url chassiUrl = - boost::urls::format("/redfish/v1/Chassis/{}", chassisId); - managerObj["@odata.id"] = chassiUrl; - managerForChassis.emplace_back(std::move(managerObj)); - aRsp->res.jsonValue["Links"]["ManagerForChassis"] = - std::move(managerForChassis); - aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] = - chassiUrl; - }); + getMainChassisId(asyncResp, std::bind_front(getManagedChassis)); dbus::utility::getProperty( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", From c12ef7e85298007bba543820347894eebe5e67ea Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Sun, 23 Nov 2025 19:42:54 +0100 Subject: [PATCH 14/18] Multi-host support for Managers ManagerForServers on multi-host should contain all ComputerSystem resources, that implement the ManagedHost interface as of now. Add a proper handle for multi-host. ManagerForChassis has been disabled for the time being with the multi- host option enabled. Currently there is no viable solution for that. Tested: Unit test pass. Validation succeeded. ``` Sample output $ curl -k 'https://'"${BMC}"':'"${BMC_WEBPORT}"'/redfish/v1/Managers/bmc' \ -H 'X-Auth-Token: '"$BMCWEB_SESSION_TOKEN"'' \ -H "Content-Type: application/json" { "Links": [ ... "ManagerForServers": [ { "@odata.id": "/redfish/v1/Systems/System_1" }, { "@odata.id": "/redfish/v1/Systems/System_2" }, { "@odata.id": "/redfish/v1/Systems/System_3" }, { "@odata.id": "/redfish/v1/Systems/System_4" }, { "@odata.id": "/redfish/v1/Systems/System_5" }, { "@odata.id": "/redfish/v1/Systems/System_6" }, { "@odata.id": "/redfish/v1/Systems/System_7" }, { "@odata.id": "/redfish/v1/Systems/System_8" } ], "ManagerForServers@odata.count": 8, ... ] ... } ``` [1] https://gerrit.openbmc.org/c/openbmc/bmcweb/+/60793 Change-Id: I4b164d6df4882bcb86e55fd89213d840abf7791e Signed-off-by: Oliver Brewka --- redfish-core/lib/managers.hpp | 76 +++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index ebf90d1fc7..0c1f2c8ea9 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -27,6 +27,7 @@ #include "utils/manager_utils.hpp" #include "utils/sw_utils.hpp" #include "utils/systemd_utils.hpp" +#include "utils/systems_utils.hpp" #include "utils/time_utils.hpp" #include @@ -696,6 +697,63 @@ inline void getManagedChassis( std::move(managerForChassis); } +inline void afterGetValidSystemPaths( + const std::shared_ptr& asyncResp, + nlohmann::json::object_t& manager, + nlohmann::json::array_t& managerForServers, + const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& systemPaths) +{ + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value()); + messages::internalError(asyncResp->res); + return; + } + + for (const std::string& system : systemPaths) + { + const sdbusplus::message::object_path systemPath(system); + manager["@odata.id"] = boost::urls::format("/redfish/v1/Systems/{}", + systemPath.filename()); + managerForServers.emplace_back(manager); + } + + asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = + managerForServers.size(); + + asyncResp->res.jsonValue["Links"]["ManagerForServers"] = + std::move(managerForServers); +} + +inline void getManagedServers( + const std::shared_ptr& asyncResp) +{ + nlohmann::json::object_t manager; + nlohmann::json::array_t managerForServers; + + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + constexpr std::array intf = { + "xyz.openbmc_project.Inventory.Decorator.ManagedHost"}; + dbus::utility::getSubTreePaths( + "/xyz/openbmc_project/inventory", 0, intf, + std::bind_front(afterGetValidSystemPaths, asyncResp, manager, + managerForServers)); + } + else + { + asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; + + manager["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); + managerForServers.emplace_back(std::move(manager)); + + asyncResp->res.jsonValue["Links"]["ManagerForServers"] = + std::move(managerForServers); + } +} + inline void handleManagerGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, @@ -780,19 +838,8 @@ inline void handleManagerGet( asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = nlohmann::json::array_t({"KVMIP"}); } - if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; - nlohmann::json::array_t managerForServers; - nlohmann::json::object_t manager; - manager["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); - managerForServers.emplace_back(std::move(manager)); - - asyncResp->res.jsonValue["Links"]["ManagerForServers"] = - std::move(managerForServers); - } + getManagedServers(asyncResp); sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose, "FirmwareVersion", true); @@ -805,7 +852,10 @@ inline void handleManagerGet( managerDiagnosticData["@odata.id"] = boost::urls::format( "/redfish/v1/Managers/{}/ManagerDiagnosticData", managerId); - getMainChassisId(asyncResp, std::bind_front(getManagedChassis)); + if (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + getMainChassisId(asyncResp, std::bind_front(getManagedChassis)); + } dbus::utility::getProperty( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", From 8642dabbc13a3db2f07d398f15c1ce702d36fa87 Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Fri, 14 Nov 2025 17:27:35 +0100 Subject: [PATCH 15/18] Multi-host support for LogServiceCollection As a first step to introduce multi-host support for LogServices, update the LogServiceCollection accordingly to allow for request handling when redfish-experimental-multi-computer-system is enabled. Disallow all LogServices not supported for multi-host via meson. Tested: Validator succeeded. Change-Id: I619a96bcab6a0c240d93c3c6586d813ccfb43cad Signed-off-by: Oliver Brewka --- config/meson.build | 14 +++++++++++ redfish-core/lib/log_services.hpp | 39 +++++++++++++------------------ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/config/meson.build b/config/meson.build index 172dabab42..44ef635779 100644 --- a/config/meson.build +++ b/config/meson.build @@ -186,6 +186,20 @@ else work_dir = '/home/root' endif +multi_comp = get_option('experimental-redfish-multi-computer-system').enabled() +cpuLog = get_option('redfish-cpu-log').disable_if( + multi_comp, + error_message: 'not supported with experimental-redfish-multi-computer-system option enabled', +) +dumpLog = get_option('redfish-dump-log').disable_if( + multi_comp, + error_message: 'not supported with experimental-redfish-multi-computer-system option enabled', +) +hostLog = get_option('redfish-host-logger').disable_if( + multi_comp, + error_message: 'not supported with experimental-redfish-multi-computer-system option enabled', +) + configure_file( input: 'bmcweb.service.in', output: 'bmcweb.service', diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index 9ee2ed6c04..a01ddb8cda 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -907,26 +907,22 @@ inline void handleSystemsLogServiceCollectionGet( { return; } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + 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; + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } } // Collections don't include the static data added by SubRoute // because it has a duplicate entry for members asyncResp->res.jsonValue["@odata.type"] = "#LogServiceCollection.LogServiceCollection"; - asyncResp->res.jsonValue["@odata.id"] = std::format( - "/redfish/v1/Systems/{}/LogServices", BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.id"] = + std::format("/redfish/v1/Systems/{}/LogServices", systemName); asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; asyncResp->res.jsonValue["Description"] = "Collection of LogServices for this Computer System"; @@ -947,26 +943,23 @@ inline void handleSystemsLogServiceCollectionGet( { nlohmann::json::object_t dumpLog; dumpLog["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/Dump", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + std::format("/redfish/v1/Systems/{}/LogServices/Dump", systemName); logServiceArray.emplace_back(std::move(dumpLog)); } if constexpr (BMCWEB_REDFISH_CPU_LOG) { nlohmann::json::object_t crashdump; - crashdump["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/Crashdump", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + crashdump["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/Crashdump", systemName); logServiceArray.emplace_back(std::move(crashdump)); } if constexpr (BMCWEB_REDFISH_HOST_LOGGER) { nlohmann::json::object_t hostlogger; - hostlogger["@odata.id"] = - std::format("/redfish/v1/Systems/{}/LogServices/HostLogger", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + hostlogger["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger", systemName); logServiceArray.emplace_back(std::move(hostlogger)); } asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size(); @@ -975,7 +968,7 @@ inline void handleSystemsLogServiceCollectionGet( "xyz.openbmc_project.State.Boot.PostCode"}; dbus::utility::getSubTreePaths( "/", 0, interfaces, - [asyncResp]( + [asyncResp, systemName]( const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& subtreePath) { if (ec) @@ -993,7 +986,7 @@ inline void handleSystemsLogServiceCollectionGet( nlohmann::json::object_t member; member["@odata.id"] = std::format( "/redfish/v1/Systems/{}/LogServices/PostCodes", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + systemName); logServiceArrayLocal.emplace_back(std::move(member)); From 8e678ec865ffc33a3ecdbf48a5aabc14eb3a0fa5 Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Mon, 2 Dec 2024 17:52:17 +0100 Subject: [PATCH 16/18] Multi-host support for PostCodes LogService Add support for multi-host for all GET and POST method requests under /redfish/v1/Systems/{computerSystemId}/LogServices/PostCodes/ redfish resource. Multi-host meson options needed: -Dexperimental-redfish-multi-computer-system=enabled Tested: Redfish-service-validation passes on single-host machine as well as on yv4 in qemu. Every postcode entry is displayed in web-ui on single-host and shown in the curl output when requested on yv4 qemu. ``` curl -w "@curl-format.txt" -c cjar -b cjar -k -X GET 'https://'"${BMC}"':4443/redfish/v1/Systems/Yosemite_4_Sentinel_Dome_Slot_1_Chassis/LogServices/PostCodes/Entries' \ -H 'X-Auth-Token: '"$BMCWEB_SESSION_TOKEN"'' { "@odata.id": "/redfish/v1/Systems/Yosemite_4_Sentinel_Dome_Slot_1_Chassis/LogServices/PostCodes/Entries", "@odata.type": "#LogEntryCollection.LogEntryCollection", "Description": "Collection of POST Code Log Entries", "Members": [ { "@odata.id": "/redfish/v1/Systems/Yosemite_4_Sentinel_Dome_Slot_1_Chassis/LogServices/PostCodes/Entries/B1-1", "@odata.type": "#LogEntry.v1_9_0.LogEntry", "Created": "2025-04-22T18:30:51.528798+00:00", "EntryType": "Event", "Id": "B1-1", "Message": "Boot Count: 1; Time Stamp Offset: 0.0000 seconds; POST Code: 0xA1", "MessageArgs": [ "1", "0.0000", "0xA1" ], "MessageId": "OpenBMC.0.2.BIOSPOSTCode", "Name": "POST Code Log Entry", "Severity": "OK" }, ... "Members@odata.count": 819, "Name": "BIOS POST Code Log Entries" } curl -w "@curl-format.txt" -c cjar -b cjar -k -X GET 'https://'"${BMC}"':4443/redfish/v1/Systems/Yosemite_4_Sentinel_Dome_Slot_1_Chassis/LogServices/PostCodes/Entries/B1-1' \ -H 'X-Auth-Token: '"$BMCWEB_SESSION_TOKEN"'' { "@odata.id": "/redfish/v1/Systems/Yosemite_4_Sentinel_Dome_Slot_1_Chassis/LogServices/PostCodes/Entries/B1-1", "@odata.type": "#LogEntry.v1_9_0.LogEntry", "Created": "2025-04-22T18:30:51.528798+00:00", "EntryType": "Event", "Id": "B1-1", "Message": "Boot Count: 1; Time Stamp Offset: 0.0000 seconds; POST Code: 0xA1", "MessageArgs": [ "1", "0.0000", "0xA1" ], "MessageId": "OpenBMC.0.2.BIOSPOSTCode", "Name": "POST Code Log Entry", "Severity": "OK" } ``` POST has been tested on single-host hardware inside web-ui and yv4 qemu machine with curl. (Postcodes have been copied over to the qemu machine manually from a single-host machine). No regressions observed. The postcode dir for the specific host is cleared successfully on both single- and multi-host machine after the POST. Change-Id: Ie04cb160a1f2756a04be68e6675a6cecc5f09117 Signed-off-by: Oliver Brewka --- redfish-core/lib/log_services.hpp | 2 +- .../lib/systems_logservices_postcodes.hpp | 291 ++++++++++-------- 2 files changed, 159 insertions(+), 134 deletions(-) diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index a01ddb8cda..2eb0e64c06 100644 --- a/redfish-core/lib/log_services.hpp +++ b/redfish-core/lib/log_services.hpp @@ -984,7 +984,7 @@ inline void handleSystemsLogServiceCollectionGet( nlohmann::json& logServiceArrayLocal = asyncResp->res.jsonValue["Members"]; nlohmann::json::object_t member; - member["@odata.id"] = std::format( + member["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/LogServices/PostCodes", systemName); diff --git a/redfish-core/lib/systems_logservices_postcodes.hpp b/redfish-core/lib/systems_logservices_postcodes.hpp index ecd52025b7..0b1fc69c33 100644 --- a/redfish-core/lib/systems_logservices_postcodes.hpp +++ b/redfish-core/lib/systems_logservices_postcodes.hpp @@ -20,6 +20,7 @@ #include "utils/etag_utils.hpp" #include "utils/hex_utils.hpp" #include "utils/query_param.hpp" +#include "utils/systems_utils.hpp" #include "utils/time_utils.hpp" #include @@ -29,6 +30,7 @@ #include #include #include +#include #include #include @@ -60,22 +62,18 @@ inline void handleSystemsLogServicesPostCodesGet( { return; } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + 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; + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } } - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Systems/{}/LogServices/PostCodes", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + + asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes", systemName); asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; asyncResp->res.jsonValue["Name"] = "POST Code Log Service"; asyncResp->res.jsonValue["Description"] = "POST Code Log Service"; @@ -83,8 +81,7 @@ inline void handleSystemsLogServicesPostCodesGet( asyncResp->res.jsonValue["OverWritePolicy"] = log_service::OverWritePolicy::WrapsWhenFull; asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", systemName); std::pair redfishDateTimeOffset = redfish::time_utils::getDateTimeOffsetNow(); @@ -92,38 +89,31 @@ inline void handleSystemsLogServicesPostCodesGet( asyncResp->res.jsonValue["DateTimeLocalOffset"] = redfishDateTimeOffset.second; - asyncResp->res - .jsonValue["Actions"]["#LogService.ClearLog"]["target"] = std::format( + asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] + ["target"] = boost::urls::format( "/redfish/v1/Systems/{}/LogServices/PostCodes/Actions/LogService.ClearLog", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + systemName); etag_utils::setEtagOmitDateTimeHandler(asyncResp); } -inline void handleSystemsLogServicesPostCodesPost( - App& app, const crow::Request& req, - const std::shared_ptr& asyncResp, - const std::string& systemName) +inline sdbusplus::message::object_path getPostCodeObjectPath( + const uint64_t computerSystemIndex) { - 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; - } - BMCWEB_LOG_DEBUG("Do delete all postcodes entries."); + sdbusplus::message::object_path path("/xyz/openbmc_project/State/Boot"); + path /= std::format("PostCode{}", computerSystemIndex); + return path; +} + +inline std::string getPostCodeService(const uint64_t computerSystemIndex) +{ + return std::format("xyz.openbmc_project.State.Boot.PostCode{}", + computerSystemIndex); +} +inline void doClearPostCodes(std::shared_ptr& asyncResp, + const uint64_t computerSystemIndex) +{ // Make call to post-code service to request clear all dbus::utility::async_method_call( asyncResp, @@ -140,11 +130,35 @@ inline void handleSystemsLogServicesPostCodesPost( } messages::success(asyncResp->res); }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", + getPostCodeService(computerSystemIndex), + getPostCodeObjectPath(computerSystemIndex), "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); } +inline void handleSystemsLogServicesPostCodesPost( + App& app, const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& systemName) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + } + BMCWEB_LOG_DEBUG("Do delete all postcodes entries."); + + systems_utils::getComputerSystemIndex( + asyncResp, systemName, std::bind_front(doClearPostCodes, asyncResp)); +} + /** * @brief Parse post code ID and get the current value and index value * eg: postCodeID=B1-2, currentValue=1, index=2 @@ -191,6 +205,7 @@ inline bool parsePostCode(std::string_view postCodeID, uint64_t& currentValue, static bool fillPostCodeEntry( const std::shared_ptr& asyncResp, + const std::string& systemName, const boost::container::flat_map< uint64_t, std::tuple, std::vector>>& postcode, @@ -290,7 +305,7 @@ static bool fillPostCodeEntry( bmcLogEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; bmcLogEntry["@odata.id"] = boost::urls::format( "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, postcodeEntryID); + systemName, postcodeEntryID); bmcLogEntry["Name"] = "POST Code Log Entry"; bmcLogEntry["Id"] = postcodeEntryID; bmcLogEntry["Message"] = std::move(msg); @@ -301,11 +316,9 @@ static bool fillPostCodeEntry( bmcLogEntry["Created"] = entryTimeStr; if (!std::get<1>(code.second).empty()) { - bmcLogEntry["AdditionalDataURI"] = - std::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - postcodeEntryID + "/attachment"; + bmcLogEntry["AdditionalDataURI"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries/{}/attachment", + systemName, postcodeEntryID); } // codeIndex is only specified when querying single entry, return only @@ -326,7 +339,8 @@ static bool fillPostCodeEntry( inline void getPostCodeForEntry( const std::shared_ptr& asyncResp, - const std::string& entryId) + const std::string& systemName, const std::string& entryId, + const uint64_t computerSystemIndex) { uint16_t bootIndex = 0; uint64_t codeIndex = 0; @@ -346,7 +360,7 @@ inline void getPostCodeForEntry( dbus::utility::async_method_call( asyncResp, - [asyncResp, entryId, bootIndex, + [asyncResp, systemName, entryId, bootIndex, codeIndex](const boost::system::error_code& ec, const boost::container::flat_map< uint64_t, std::tuple, @@ -364,26 +378,29 @@ inline void getPostCodeForEntry( return; } - if (!fillPostCodeEntry(asyncResp, postcode, bootIndex, codeIndex)) + if (!fillPostCodeEntry(asyncResp, systemName, postcode, bootIndex, + codeIndex)) { messages::resourceNotFound(asyncResp->res, "LogEntry", entryId); return; } }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", + getPostCodeService(computerSystemIndex), + getPostCodeObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", bootIndex); } inline void getPostCodeForBoot( const std::shared_ptr& asyncResp, + const std::string& systemName, const uint64_t computerSystemIndex, const uint16_t bootIndex, const uint16_t bootCount, const uint64_t entryCount, size_t skip, size_t top) { dbus::utility::async_method_call( asyncResp, - [asyncResp, bootIndex, bootCount, entryCount, skip, + [asyncResp, systemName, computerSystemIndex, bootIndex, bootCount, + entryCount, skip, top](const boost::system::error_code& ec, const boost::container::flat_map< uint64_t, std::tuple, @@ -408,8 +425,8 @@ inline void getPostCodeForBoot( std::min(static_cast(top + skip), endCount) - entryCount; - fillPostCodeEntry(asyncResp, postcode, bootIndex, 0, - thisBootSkip, thisBootTop); + fillPostCodeEntry(asyncResp, systemName, postcode, + bootIndex, 0, thisBootSkip, thisBootTop); } asyncResp->res.jsonValue["Members@odata.count"] = endCount; } @@ -417,35 +434,36 @@ inline void getPostCodeForBoot( // continue to previous bootIndex if (bootIndex < bootCount) { - getPostCodeForBoot(asyncResp, + getPostCodeForBoot(asyncResp, systemName, computerSystemIndex, static_cast(bootIndex + 1), bootCount, endCount, skip, top); } else if (skip + top < endCount) { - asyncResp->res.jsonValue["Members@odata.nextLink"] = - std::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip=", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + - std::to_string(skip + top); + asyncResp->res + .jsonValue["Members@odata.nextLink"] = boost::urls::format( + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries?$skip={}", + systemName, std::to_string(skip + top)); } }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", + getPostCodeService(computerSystemIndex), + getPostCodeObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", bootIndex); } inline void getCurrentBootNumber( - const std::shared_ptr& asyncResp, size_t skip, - size_t top) + const std::shared_ptr& asyncResp, + const std::string& systemName, size_t skip, size_t top, + const uint64_t computerSystemIndex) { uint64_t entryCount = 0; + dbus::utility::getProperty( - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", + getPostCodeService(computerSystemIndex), + getPostCodeObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount", - [asyncResp, entryCount, skip, + [asyncResp, systemName, computerSystemIndex, entryCount, skip, top](const boost::system::error_code& ec, const uint16_t bootCount) { if (ec) { @@ -453,7 +471,8 @@ inline void getCurrentBootNumber( messages::internalError(asyncResp->res); return; } - getPostCodeForBoot(asyncResp, 1, bootCount, entryCount, skip, top); + getPostCodeForBoot(asyncResp, systemName, computerSystemIndex, 1, + bootCount, entryCount, skip, top); }); } @@ -472,25 +491,21 @@ inline void handleSystemsLogServicesPostCodesEntriesGet( { 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) + if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } } + asyncResp->res.jsonValue["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + "/redfish/v1/Systems/{}/LogServices/PostCodes/Entries", systemName); asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; asyncResp->res.jsonValue["Description"] = "Collection of POST Code Log Entries"; @@ -498,47 +513,18 @@ inline void handleSystemsLogServicesPostCodesEntriesGet( asyncResp->res.jsonValue["Members@odata.count"] = 0; size_t skip = delegatedQuery.skip.value_or(0); size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop); - getCurrentBootNumber(asyncResp, skip, top); + + systems_utils::getComputerSystemIndex( + asyncResp, systemName, + std::bind_front(getCurrentBootNumber, asyncResp, systemName, skip, + top)); } -inline void handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet( - App& app, const crow::Request& req, +inline void processSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet( const std::shared_ptr& asyncResp, - const std::string& systemName, const std::string& postCodeID) + const std::string& postCodeID, const uint64_t currentValue, + const uint16_t index, const uint64_t computerSystemIndex) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if (!http_helpers::isContentTypeAllowed( - req.getHeaderValue("Accept"), - http_helpers::ContentType::OctetStream, true)) - { - asyncResp->res.result(boost::beast::http::status::bad_request); - 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; - } - - uint64_t currentValue = 0; - uint16_t index = 0; - if (!parsePostCode(postCodeID, currentValue, index)) - { - messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID); - return; - } - dbus::utility::async_method_call( asyncResp, [asyncResp, postCodeID, currentValue]( @@ -585,35 +571,74 @@ inline void handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet( boost::beast::http::field::content_transfer_encoding, "Base64"); asyncResp->res.write(crow::utility::base64encode(strData)); }, - "xyz.openbmc_project.State.Boot.PostCode0", - "/xyz/openbmc_project/State/Boot/PostCode0", + getPostCodeService(computerSystemIndex), + getPostCodeObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index); } -inline void handleSystemsLogServicesPostCodesEntriesEntryGet( +inline void handleSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, - const std::string& systemName, const std::string& targetID) + const std::string& systemName, const std::string& postCodeID) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + if (!http_helpers::isContentTypeAllowed( + req.getHeaderValue("Accept"), + http_helpers::ContentType::OctetStream, true)) { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); + asyncResp->res.result(boost::beast::http::status::bad_request); return; } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + } + + uint64_t currentValue = 0; + uint16_t index = 0; + if (!parsePostCode(postCodeID, currentValue, index)) + { + messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID); return; } - getPostCodeForEntry(asyncResp, targetID); + systems_utils::getComputerSystemIndex( + asyncResp, systemName, + std::bind_front( + processSystemsLogServicesPostCodesEntriesEntryAdditionalDataGet, + asyncResp, postCodeID, currentValue, index)); +} + +inline void handleSystemsLogServicesPostCodesEntriesEntryGet( + App& app, const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& systemName, const std::string& targetID) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + } + + systems_utils::getComputerSystemIndex( + asyncResp, systemName, + std::bind_front(getPostCodeForEntry, asyncResp, systemName, targetID)); } inline void requestRoutesSystemsLogServicesPostCode(App& app) From e40d947dd711d8a4e2ebcec7493b0bb3fb1794ec Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Fri, 12 Dec 2025 19:15:19 +0100 Subject: [PATCH 17/18] WIP: Simplify dbus service name creation Non-indexed dbus service names for instance based daemons got deprecated. It is expected, that xyz.openbmc_project.State.Host0 and xyz.openbmc_project.State.Chassis0 exist on every single-host platform. Remove the multi-host check, and directly return an index based dbus service name. Tested: Manually checking if dbus service is picking up the requested ResetType / state transition interface. Before POST: ``` $ busctl introspect xyz.openbmc_project.State.Host0 \ /xyz/openbmc_project/state/host0 xyz.openbmc_project.State.Host interface - - ... .RequestedHostTransition property s "xyz.openbmc_project.State.Host.Transition.Off" ... ``` POST request: ``` $ curl -v -k POST 'https://'"${BMC}"':'"${BMC_WEBPORT}"'/redfish/v1/Systems/system/Actions/ComputerSystem.Reset' \ -H 'X-Auth-Token: '"$BMCWEB_SESSION_TOKEN"'' \ -H "Content-Type: application/json" -d {"ResetType":"On"} ``` After POST: ``` $ busctl introspect xyz.openbmc_project.State.Host0 \ /xyz/openbmc_project/state/host0 .RequestedHostTransition property s "xyz.openbmc_project.State.Host.Transition.On" ``` Change-Id: I5b2c37f9d6a622af0c056504e1bb6dd839fd7d14 Signed-off-by: Oliver Brewka --- redfish-core/include/utils/systems_utils.hpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/redfish-core/include/utils/systems_utils.hpp b/redfish-core/include/utils/systems_utils.hpp index 85d25aeda1..618bea12ec 100644 --- a/redfish-core/include/utils/systems_utils.hpp +++ b/redfish-core/include/utils/systems_utils.hpp @@ -226,13 +226,7 @@ inline sdbusplus::message::object_path getHostStateObjectPath( inline std::string getHostStateServiceName(const uint64_t computerSystemIndex) { - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - return std::format("xyz.openbmc_project.State.Host{}", - computerSystemIndex); - } - - return "xyz.openbmc_project.State.Host"; + return std::format("xyz.openbmc_project.State.Host{}", computerSystemIndex); } inline sdbusplus::message::object_path getChassisStateObjectPath( @@ -246,13 +240,8 @@ inline sdbusplus::message::object_path getChassisStateObjectPath( inline std::string getChassisStateServiceName( const uint64_t computerSystemIndex) { - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - return std::format("xyz.openbmc_project.State.Chassis{}", - computerSystemIndex); - } - - return "xyz.openbmc_project.State.Chassis"; + return std::format("xyz.openbmc_project.State.Chassis{}", + computerSystemIndex); } inline sdbusplus::message::object_path getControlObjectPath( From b695aab8a2e4e0a1961920721c34a2e8436eeb0d Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Fri, 7 Mar 2025 09:16:56 +0100 Subject: [PATCH 18/18] WIP: Multi-host support for hostlogger service Add support for multi-host for all GET and POST method requests under /redfish/v1/Systems/{computerSystemId}/LogServices/HostLogger/ redfish resource. Testing: TBD Change-Id: I026be8106f2accbb77d8d40749f502f3162ad04b Signed-off-by: Oliver Brewka --- .../lib/systems_logservices_hostlogger.hpp | 189 +++++++++++------- 1 file changed, 115 insertions(+), 74 deletions(-) diff --git a/redfish-core/lib/systems_logservices_hostlogger.hpp b/redfish-core/lib/systems_logservices_hostlogger.hpp index bb3ebd365e..f99784242d 100644 --- a/redfish-core/lib/systems_logservices_hostlogger.hpp +++ b/redfish-core/lib/systems_logservices_hostlogger.hpp @@ -15,6 +15,7 @@ #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/query_param.hpp" +#include "utils/systems_utils.hpp" #include #include @@ -38,10 +39,38 @@ namespace redfish { constexpr const char* hostLoggerFolderPath = "/var/log/console"; +// default output dir for phosphor-hostlogger in buffer mode +constexpr const char* multiHostLoggerFolderPath = "/var/lib/obmc/hostlogs"; + inline bool getHostLoggerFiles( const std::string& hostLoggerFilePath, - std::vector& hostLoggerFiles) + std::vector& hostLoggerFiles, + const uint64_t computerSystemIndex) { + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + std::string logFilesPath = multiHostLoggerFolderPath; + logFilesPath.append("/host" + std::to_string(computerSystemIndex)); + + BMCWEB_LOG_DEBUG("LogFilesPath: {}", logFilesPath); + + std::error_code ec; + std::filesystem::directory_iterator logPath(logFilesPath, ec); + + if (ec) + { + BMCWEB_LOG_WARNING("{}", ec.message()); + return false; + } + for (const std::filesystem::directory_entry& it : logPath) + { + BMCWEB_LOG_DEBUG("Logfile: {}", it.path().filename().string()); + hostLoggerFiles.emplace_back(it.path()); + } + // TODO 07/13/25-19:58 olek: need to sort vector by timestamps + return true; + } + std::error_code ec; std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec); if (ec) @@ -96,15 +125,15 @@ inline bool getHostLoggerEntries( return true; } -inline void fillHostLoggerEntryJson(std::string_view logEntryID, - std::string_view msg, - nlohmann::json::object_t& logEntryJson) +inline void fillHostLoggerEntryJson( + const std::string& systemName, std::string_view logEntryID, + std::string_view msg, nlohmann::json::object_t& logEntryJson) { // Fill in the log entry with the gathered data. logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry"; logEntryJson["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries/{}", - BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID); + "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries/{}", systemName, + logEntryID); logEntryJson["Name"] = "Host Logger Entry"; logEntryJson["Id"] = logEntryID; logEntryJson["Message"] = msg; @@ -122,62 +151,33 @@ inline void handleSystemsLogServicesHostloggerGet( { 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) + + if (!BMCWEB_REDFISH_SYSTEM_URI_NAME.empty()) { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } } - asyncResp->res.jsonValue["@odata.id"] = - boost::urls::format("/redfish/v1/Systems/{}/LogServices/HostLogger", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger", systemName); asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService"; asyncResp->res.jsonValue["Name"] = "Host Logger Service"; asyncResp->res.jsonValue["Description"] = "Host Logger Service"; asyncResp->res.jsonValue["Id"] = "HostLogger"; - asyncResp->res.jsonValue["Entries"]["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", systemName); } -inline void handleSystemsLogServicesHostloggerEntriesGet( - App& app, const crow::Request& req, +inline void processSystemsLogServicesHostloggerEntriesGet( const std::shared_ptr& asyncResp, - const std::string& systemName) + const std::string& systemName, query_param::Query& delegatedQuery, + const uint64_t computerSystemIndex) { - query_param::QueryCapabilities capabilities = { - .canDelegateTop = true, - .canDelegateSkip = true, - }; - query_param::Query delegatedQuery; - if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, - delegatedQuery, capabilities)) - { - 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; - } - asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( - "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + asyncResp->res.jsonValue["@odata.id"] = std::format( + "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries", systemName); asyncResp->res.jsonValue["@odata.type"] = "#LogEntryCollection.LogEntryCollection"; asyncResp->res.jsonValue["Name"] = "HostLogger Entries"; @@ -188,7 +188,9 @@ inline void handleSystemsLogServicesHostloggerEntriesGet( asyncResp->res.jsonValue["Members@odata.count"] = 0; std::vector hostLoggerFiles; - if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) + + if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles, + computerSystemIndex)) { BMCWEB_LOG_DEBUG("Failed to get host log file path"); return; @@ -217,8 +219,8 @@ inline void handleSystemsLogServicesHostloggerEntriesGet( for (size_t i = 0; i < logEntries.size(); i++) { nlohmann::json::object_t hostLogEntry; - fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i], - hostLogEntry); + fillHostLoggerEntryJson(systemName, std::to_string(skip + i), + logEntries[i], hostLogEntry); logEntryArray.emplace_back(std::move(hostLogEntry)); } @@ -228,36 +230,48 @@ inline void handleSystemsLogServicesHostloggerEntriesGet( asyncResp->res.jsonValue["Members@odata.nextLink"] = std::format( "/redfish/v1/Systems/{}/LogServices/HostLogger/Entries?$skip=", - BMCWEB_REDFISH_SYSTEM_URI_NAME) + + systemName) + std::to_string(skip + top); } } } - -inline void handleSystemsLogServicesHostloggerEntriesEntryGet( +inline void handleSystemsLogServicesHostloggerEntriesGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, - const std::string& systemName, const std::string& param) + const std::string& systemName) { - if (!redfish::setUpRedfishRoute(app, req, asyncResp)) - { - return; - } - if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + query_param::QueryCapabilities capabilities = { + .canDelegateTop = true, + .canDelegateSkip = true, + }; + query_param::Query delegatedQuery; + if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, + delegatedQuery, capabilities)) { - // Option currently returns no systems. TBD - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); return; } - if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + if (!BMCWEB_REDFISH_SYSTEM_URI_NAME.empty()) { - messages::resourceNotFound(asyncResp->res, "ComputerSystem", - systemName); - return; + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } } - std::string_view targetID = param; + systems_utils::getComputerSystemIndex( + asyncResp, systemName, + std::bind_front(processSystemsLogServicesHostloggerEntriesGet, + asyncResp, systemName, delegatedQuery)); +} + +inline void processSystemsLogServicesHostloggerEntriesEntryGet( + const std::shared_ptr& asyncResp, + const std::string& systemName, const std::string& param, + const uint64_t computerSystemIndex) +{ + std::string_view targetID = param; uint64_t idInt = 0; auto [ptr, ec] = std::from_chars(targetID.begin(), targetID.end(), idInt); @@ -268,7 +282,8 @@ inline void handleSystemsLogServicesHostloggerEntriesEntryGet( } std::vector hostLoggerFiles; - if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles)) + if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles, + computerSystemIndex)) { BMCWEB_LOG_DEBUG("Failed to get host log file path"); return; @@ -290,7 +305,8 @@ inline void handleSystemsLogServicesHostloggerEntriesEntryGet( if (!logEntries.empty()) { nlohmann::json::object_t hostLogEntry; - fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry); + fillHostLoggerEntryJson(systemName, targetID, logEntries[0], + hostLogEntry); asyncResp->res.jsonValue.update(hostLogEntry); return; } @@ -299,6 +315,31 @@ inline void handleSystemsLogServicesHostloggerEntriesEntryGet( messages::resourceNotFound(asyncResp->res, "LogEntry", param); } +inline void handleSystemsLogServicesHostloggerEntriesEntryGet( + App& app, const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& systemName, const std::string& param) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + if (!BMCWEB_REDFISH_SYSTEM_URI_NAME.empty()) + { + if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) + { + messages::resourceNotFound(asyncResp->res, "ComputerSystem", + systemName); + return; + } + } + + systems_utils::getComputerSystemIndex( + asyncResp, systemName, + std::bind_front(processSystemsLogServicesHostloggerEntriesEntryGet, + asyncResp, systemName, param)); +} + inline void requestRoutesSystemsLogServiceHostlogger(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Systems//LogServices/HostLogger/")