From d5a39575a146945e2fbcfe7872aa9b59deaf64ab Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Mon, 24 Nov 2025 14:54:25 -0800 Subject: [PATCH 01/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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 37e23ab2679f76af6aed12e3605637fdfc630571 Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Sun, 23 Nov 2025 18:17:12 +0100 Subject: [PATCH 07/12] 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. In addition make strings used for path creation and the path itself const, to improve readability and overall quality. Tested: Code compiles. Validation succeeded. 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 7e37d3d3e3f092f3cb9ae740bffd1466ba08c4ae Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Sun, 23 Nov 2025 19:42:54 +0100 Subject: [PATCH 08/12] Multi-host support in managers.hpp ManagerForServers should contain all ComputerSystem resources, that also show inside the Systems collection. Add a proper handle for multi-host. ManagerForChassis has been disabled for the time being when on multi- host, will be enabled once the getMainChassisId rework has been completed [1]. 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 | 107 +++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 27 deletions(-) diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 3c5c53f723..eaebffb944 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 @@ -682,6 +683,63 @@ inline void getManagerData(const std::shared_ptr& asyncResp, } } +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/", 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, @@ -766,19 +824,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); @@ -791,21 +838,27 @@ 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; - }); + if (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + // Todo: chassis matching could be handled by patch + // https://gerrit.openbmc.org/c/openbmc/bmcweb/+/60793 + 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; + }); + } dbus::utility::getProperty( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", From f7521ef1020c668dd194531bad31f328de64004b Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Fri, 14 Nov 2025 17:27:35 +0100 Subject: [PATCH 09/12] 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. 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..ed04b3f492 100644 --- a/config/meson.build +++ b/config/meson.build @@ -186,6 +186,20 @@ else work_dir = '/home/root' endif +# Multi-host feature handling +multi_comp = get_option('experimental-redfish-multi-computer-system') +if multi_comp == 'enabled' + cpu_log = get_option('redfish-cpu-log') + cpu_log = 'disabled' + warning('redfish-cpu-log option not supported with experimental-redfish-multi-computer-system option') + dump_log = get_option('redfish-dump-log') + dump_log = 'disabled' + warning('redfish-dump-log option not supported with experimental-redfish-multi-computer-system option') + host_logger = get_option('redfish-host-logger') + host_logger = 'disabled' + warning('redfish-host-logger option not supported with experimental-redfish-multi-computer-system option') +endif + 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 bbc79d0a9dbae097cbf42a998b1eb94fa78aa30a Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Mon, 2 Dec 2024 17:52:17 +0100 Subject: [PATCH 10/12] 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 0441b3cbbb2365d0899722e71dd9e10630aa2d40 Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Fri, 7 Mar 2025 09:16:56 +0100 Subject: [PATCH 11/12] 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/") From 2785da463d2461c7e0216b1288ab01e854d5b6aa Mon Sep 17 00:00:00 2001 From: Oliver Brewka Date: Mon, 8 Dec 2025 19:38:43 +0100 Subject: [PATCH 12/12] fixup! Multi-host support for LogServiceCollection Change-Id: I44b1c2e192c9e86aaacb5bf0f075d3f31529c1e8 --- config/meson.build | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/config/meson.build b/config/meson.build index ed04b3f492..088c032f1e 100644 --- a/config/meson.build +++ b/config/meson.build @@ -186,18 +186,17 @@ else work_dir = '/home/root' endif -# Multi-host feature handling multi_comp = get_option('experimental-redfish-multi-computer-system') -if multi_comp == 'enabled' - cpu_log = get_option('redfish-cpu-log') - cpu_log = 'disabled' - warning('redfish-cpu-log option not supported with experimental-redfish-multi-computer-system option') - dump_log = get_option('redfish-dump-log') - dump_log = 'disabled' - warning('redfish-dump-log option not supported with experimental-redfish-multi-computer-system option') - host_logger = get_option('redfish-host-logger') - host_logger = 'disabled' - warning('redfish-host-logger option not supported with experimental-redfish-multi-computer-system option') +if multi_comp.enabled() + if get_option('redfish-cpu-log').enabled() + error('redfish-cpu-log option not supported with experimental-redfish-multi-computer-system option') + endif + if get_option('redfish-dump-log').enabled() + error('redfish-dump-log option not supported with experimental-redfish-multi-computer-system option') + endif + if get_option('redfish-host-logger').enabled() + error('redfish-host-logger option not supported with experimental-redfish-multi-computer-system option') + endif endif configure_file(