diff --git a/config/meson.build b/config/meson.build index 172dabab42..c976aeacad 100644 --- a/config/meson.build +++ b/config/meson.build @@ -186,6 +186,25 @@ else work_dir = '/home/root' endif +multi_comp = get_option('experimental-redfish-multi-computer-system') +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 enabled', + ) + endif + if get_option('redfish-dump-log').enabled() + error( + 'redfish-dump-log option not supported with experimental-redfish-multi-computer-system option enabled', + ) + endif + if get_option('redfish-host-logger').enabled() + error( + 'redfish-host-logger option not supported with experimental-redfish-multi-computer-system option enabled', + ) + endif +endif + configure_file( input: 'bmcweb.service.in', output: 'bmcweb.service', diff --git a/docs/Redfish.md b/docs/Redfish.md index 46357b6a9f..939d789c8a 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 @@ -476,6 +478,7 @@ Fields common to all schemas ###### Assembly +- LocationIndicatorActive - Model - PartNumber - SerialNumber diff --git a/include/boost_formatters.hpp b/include/boost_formatters.hpp index bf8397faa6..e675c3b17e 100644 --- a/include/boost_formatters.hpp +++ b/include/boost_formatters.hpp @@ -52,16 +52,4 @@ struct std::formatter } }; -template StringView> -struct std::formatter -{ - constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - auto format(const StringView& msg, auto& ctx) const - { - return std::format_to(ctx.out(), "{}", std::string_view(msg)); - } -}; // NOLINTEND(readability-convert-member-functions-to-static, cert-dcl58-cpp) diff --git a/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/meson.build b/meson.build index 93c26b72c8..f2c34c1dfa 100644 --- a/meson.build +++ b/meson.build @@ -67,7 +67,7 @@ incdir = [ ] # Add compiler arguments -boost_flags = ['-Wno-unused-parameter'] +boost_flags = ['-Wno-pedantic'] nghttp2_flags = [] if (cxx.get_id() == 'clang') if (cxx.version().version_compare('<17.0')) @@ -291,25 +291,13 @@ bmcweb_dependencies += nlohmann_json_dep boost = dependency( 'boost', - modules: ['url'], - version: '>=1.84.0', - required: false, + modules: ['url', 'process'], + version: '>=1.88.0', static: true, + required: false, include_type: 'system', ) -# Boost version is 1.86 or higher to include the 'process' module -if boost.version().version_compare('>=1.86.0') - boost = dependency( - 'boost', - modules: ['url', 'process'], - version: '>=1.86.0', - static: true, - required: false, - include_type: 'system', - ) -endif - if boost.found() bmcweb_dependencies += [boost] else @@ -333,6 +321,7 @@ else 'CMAKE_C_FLAGS': ' '.join(boost_flags), 'BOOST_INCLUDE_LIBRARIES': ';'.join(boost_libs), 'BUILD_SHARED_LIBS': 'OFF', + 'BOOST_PROCESS_USE_STD_FS': 'ON', }, ) diff --git a/redfish-core/include/event_matches_filter.hpp b/redfish-core/include/event_matches_filter.hpp index 66a50a3429..1f327a3ef3 100644 --- a/redfish-core/include/event_matches_filter.hpp +++ b/redfish-core/include/event_matches_filter.hpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp index 566b3ad83b..5f3e99aebd 100644 --- a/redfish-core/include/event_service_manager.hpp +++ b/redfish-core/include/event_service_manager.hpp @@ -20,7 +20,6 @@ #include "utils/time_utils.hpp" #include -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include #include 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/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; diff --git a/redfish-core/include/utils/sensor_utils.hpp b/redfish-core/include/utils/sensor_utils.hpp index f97d93f545..dd8150cff5 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. @@ -460,6 +501,278 @@ inline sensor::ImplementationType dBusSensorImplementationToRedfish( return sensor::ImplementationType::Invalid; } +inline void fillSensorStatus( + const dbus::utility::DBusPropertiesMap& propertiesDict, + nlohmann::json& sensorJson, InventoryItem* inventoryItem) +{ + const bool* checkAvailable = nullptr; + bool available = true; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available", + checkAvailable); + if (!success) + { + messages::internalError(); + } + if (checkAvailable != nullptr) + { + available = *checkAvailable; + } + + sensorJson["Status"]["State"] = getState(inventoryItem, available); + sensorJson["Status"]["Health"] = + getHealth(sensorJson, propertiesDict, inventoryItem); +} + +inline void fillSensorIdentity( + std::string_view sensorName, std::string_view sensorType, + const dbus::utility::DBusPropertiesMap& propertiesDict, + nlohmann::json& sensorJson) +{ + std::string subNodeEscaped = getSensorId(sensorName, sensorType); + // For sensors in SensorCollection we set Id instead of MemberId, + // including power sensors. + sensorJson["Id"] = std::move(subNodeEscaped); + + std::string sensorNameEs(sensorName); + std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); + sensorJson["Name"] = std::move(sensorNameEs); + sensorJson["@odata.type"] = "#Sensor.v1_11_0.Sensor"; + + sensor::ReadingType readingType = sensors::toReadingType(sensorType); + if (readingType == sensor::ReadingType::Invalid) + { + BMCWEB_LOG_ERROR("Redfish cannot map reading type for {}", sensorType); + } + else + { + sensorJson["ReadingType"] = readingType; + } + + std::string_view readingUnits = sensors::toReadingUnits(sensorType); + if (readingUnits.empty()) + { + BMCWEB_LOG_ERROR("Redfish cannot map reading unit for {}", sensorType); + } + else + { + sensorJson["ReadingUnits"] = readingUnits; + } + + std::optional readingBasis; + std::optional implementation; + std::optional statistics; + std::optional readingParameters; + + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesDict, "ReadingBasis", + readingBasis, "Implementation", implementation, "Readings", statistics, + "ReadingParameters", readingParameters); + if (!success) + { + messages::internalError(); + } + + if (readingBasis.has_value()) + { + sensor::ReadingBasisType readingBasisOpt = + dBusSensorReadingBasisToRedfish(*readingBasis); + if (readingBasisOpt != sensor::ReadingBasisType::Invalid) + { + sensorJson["ReadingBasis"] = readingBasisOpt; + } + } + + if (implementation.has_value()) + { + sensor::ImplementationType implementationOpt = + dBusSensorImplementationToRedfish(*implementation); + if (implementationOpt != sensor::ImplementationType::Invalid) + { + sensorJson["Implementation"] = implementationOpt; + } + } + + updateSensorStatistics(sensorJson, statistics, readingParameters); +} + +inline bool fillPowerThermalIdentity( + std::string_view sensorName, std::string_view sensorType, + nlohmann::json& sensorJson, InventoryItem* inventoryItem, + nlohmann::json::json_pointer& unit, bool& forceToInt) +{ + if (sensorType != "power") + { + // Set MemberId and Name for non-power sensors. For PowerSupplies + // and PowerControl, those properties have more general values + // because multiple sensors can be stored in the same JSON object. + std::string sensorNameEs(sensorName); + std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); + sensorJson["Name"] = std::move(sensorNameEs); + } + + if (sensorType == "temperature") + { + unit = "/ReadingCelsius"_json_pointer; + sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature"; + // TODO(ed) Documentation says that path should be type fan_tach, + // implementation seems to implement fan + return true; + } + + if (sensorType == "fan" || sensorType == "fan_tach") + { + unit = "/Reading"_json_pointer; + sensorJson["ReadingUnits"] = thermal::ReadingUnits::RPM; + sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; + if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) + { + setLedState(sensorJson, inventoryItem); + } + forceToInt = true; + return true; + } + + if (sensorType == "fan_pwm") + { + unit = "/Reading"_json_pointer; + sensorJson["ReadingUnits"] = thermal::ReadingUnits::Percent; + sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; + if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) + { + setLedState(sensorJson, inventoryItem); + } + forceToInt = true; + return true; + } + + if (sensorType == "voltage") + { + unit = "/ReadingVolts"_json_pointer; + sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage"; + return true; + } + + if (sensorType == "power") + { + std::string lower; + std::ranges::transform(sensorName, std::back_inserter(lower), + bmcweb::asciiToLower); + if (lower == "total_power") + { + sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl"; + // Put multiple "sensors" into a single PowerControl, so have + // generic names for MemberId and Name. Follows Redfish mockup. + sensorJson["MemberId"] = "0"; + sensorJson["Name"] = "Chassis Power Control"; + unit = "/PowerConsumedWatts"_json_pointer; + } + else if (lower.find("input") != std::string::npos) + { + unit = "/PowerInputWatts"_json_pointer; + } + else + { + unit = "/PowerOutputWatts"_json_pointer; + } + return true; + } + + BMCWEB_LOG_ERROR("Redfish cannot map object type for {}", sensorName); + return false; +} + +// Map of dbus interface name, dbus property name and redfish property_name +using SensorPropertyMap = std::tuple; +using SensorPropertyList = std::vector; + +inline void mapPropertiesBySubnode( + std::string_view sensorType, ChassisSubNode chassisSubNode, + SensorPropertyList& properties, nlohmann::json::json_pointer& unit, + bool isExcerpt) +{ + // unit contains the redfish property_name based on the sensor type/node + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); + + if (isExcerpt) + { + // Excerpts don't have any of these extended properties + return; + } + + if (chassisSubNode == ChassisSubNode::sensorsNode) + { + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", + "/Thresholds/UpperCaution/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", + "/Thresholds/LowerCaution/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", + "/Thresholds/UpperCritical/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", + "/Thresholds/LowerCritical/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.HardShutdown", + "HardShutdownHigh", "/Thresholds/UpperFatal/Reading"_json_pointer); + properties.emplace_back( + "xyz.openbmc_project.Sensor.Threshold.HardShutdown", + "HardShutdownLow", "/Thresholds/LowerFatal/Reading"_json_pointer); + + /* Add additional properties specific to sensorType */ + if (sensorType == "fan_tach") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", + "/SpeedRPM"_json_pointer); + } + } + else if (sensorType != "power") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningHigh", + "/UpperThresholdNonCritical"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", + "WarningLow", + "/LowerThresholdNonCritical"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalHigh", + "/UpperThresholdCritical"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", + "CriticalLow", + "/LowerThresholdCritical"_json_pointer); + } + + // TODO Need to get UpperThresholdFatal and LowerThresholdFatal + + if (chassisSubNode == ChassisSubNode::sensorsNode) + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "/ReadingRangeMin"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "/ReadingRangeMax"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy", + "Accuracy", "/Accuracy"_json_pointer); + } + else if (sensorType == "temperature") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "/MinReadingRangeTemp"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "/MaxReadingRangeTemp"_json_pointer); + } + else if (sensorType != "power") + { + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", + "/MinReadingRange"_json_pointer); + properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", + "/MaxReadingRange"_json_pointer); + } +} + /** * @brief Builds a json sensor representation of a sensor. * @param sensorName The name of the sensor to be built @@ -496,249 +809,31 @@ inline void objectPropertiesToJson( { if (chassisSubNode == ChassisSubNode::sensorsNode) { - std::string subNodeEscaped = getSensorId(sensorName, sensorType); - // For sensors in SensorCollection we set Id instead of MemberId, - // including power sensors. - sensorJson["Id"] = std::move(subNodeEscaped); - - std::string sensorNameEs(sensorName); - std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); - sensorJson["Name"] = std::move(sensorNameEs); - } - else if (sensorType != "power") - { - // Set MemberId and Name for non-power sensors. For PowerSupplies - // and PowerControl, those properties have more general values - // because multiple sensors can be stored in the same JSON object. - std::string sensorNameEs(sensorName); - std::replace(sensorNameEs.begin(), sensorNameEs.end(), '_', ' '); - sensorJson["Name"] = std::move(sensorNameEs); - } - - const bool* checkAvailable = nullptr; - bool available = true; - std::optional readingBasis; - std::optional implementation; - - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesDict, "Available", - checkAvailable, "ReadingBasis", readingBasis, "Implementation", - implementation); - if (!success) - { - messages::internalError(); - } - if (checkAvailable != nullptr) - { - available = *checkAvailable; - } - - sensorJson["Status"]["State"] = getState(inventoryItem, available); - sensorJson["Status"]["Health"] = - getHealth(sensorJson, propertiesDict, inventoryItem); - - if (chassisSubNode == ChassisSubNode::sensorsNode) - { - sensorJson["@odata.type"] = "#Sensor.v1_11_0.Sensor"; - - sensor::ReadingType readingType = - sensors::toReadingType(sensorType); - if (readingType == sensor::ReadingType::Invalid) - { - BMCWEB_LOG_ERROR("Redfish cannot map reading type for {}", - sensorType); - } - else - { - sensorJson["ReadingType"] = readingType; - } - - std::string_view readingUnits = sensors::toReadingUnits(sensorType); - if (readingUnits.empty()) - { - BMCWEB_LOG_ERROR("Redfish cannot map reading unit for {}", - sensorType); - } - else - { - sensorJson["ReadingUnits"] = readingUnits; - } - - if (readingBasis.has_value()) - { - sensor::ReadingBasisType readingBasisOpt = - dBusSensorReadingBasisToRedfish(*readingBasis); - if (readingBasisOpt != sensor::ReadingBasisType::Invalid) - { - sensorJson["ReadingBasis"] = readingBasisOpt; - } - } - - if (implementation.has_value()) - { - sensor::ImplementationType implementationOpt = - dBusSensorImplementationToRedfish(*implementation); - if (implementationOpt != sensor::ImplementationType::Invalid) - { - sensorJson["Implementation"] = implementationOpt; - } - } - } - else if (sensorType == "temperature") - { - unit = "/ReadingCelsius"_json_pointer; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature"; - // TODO(ed) Documentation says that path should be type fan_tach, - // implementation seems to implement fan - } - else if (sensorType == "fan" || sensorType == "fan_tach") - { - unit = "/Reading"_json_pointer; - sensorJson["ReadingUnits"] = thermal::ReadingUnits::RPM; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; - if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) - { - setLedState(sensorJson, inventoryItem); - } - forceToInt = true; - } - else if (sensorType == "fan_pwm") - { - unit = "/Reading"_json_pointer; - sensorJson["ReadingUnits"] = thermal::ReadingUnits::Percent; - sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; - if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) - { - setLedState(sensorJson, inventoryItem); - } - forceToInt = true; - } - else if (sensorType == "voltage") - { - unit = "/ReadingVolts"_json_pointer; - sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage"; + fillSensorIdentity(sensorName, sensorType, propertiesDict, + sensorJson); } - else if (sensorType == "power") + else { - std::string lower; - std::ranges::transform(sensorName, std::back_inserter(lower), - bmcweb::asciiToLower); - if (lower == "total_power") - { - sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl"; - // Put multiple "sensors" into a single PowerControl, so have - // generic names for MemberId and Name. Follows Redfish mockup. - sensorJson["MemberId"] = "0"; - sensorJson["Name"] = "Chassis Power Control"; - unit = "/PowerConsumedWatts"_json_pointer; - } - else if (lower.find("input") != std::string::npos) - { - unit = "/PowerInputWatts"_json_pointer; - } - else + bool filledOk = fillPowerThermalIdentity( + sensorName, sensorType, sensorJson, inventoryItem, unit, + std::ref(forceToInt)); + if (!filledOk) { - unit = "/PowerOutputWatts"_json_pointer; + return; } } - else - { - BMCWEB_LOG_ERROR("Redfish cannot map object type for {}", - sensorName); - return; - } + + fillSensorStatus(propertiesDict, sensorJson, inventoryItem); } // Map of dbus interface name, dbus property name and redfish property_name - std::vector< - std::tuple> - properties; - - properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); - - if (!isExcerpt) - { - if (chassisSubNode == ChassisSubNode::sensorsNode) - { - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", - "/Thresholds/UpperCaution/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", - "/Thresholds/LowerCaution/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", - "/Thresholds/UpperCritical/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", - "/Thresholds/LowerCritical/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.HardShutdown", - "HardShutdownHigh", - "/Thresholds/UpperFatal/Reading"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.HardShutdown", - "HardShutdownLow", - "/Thresholds/LowerFatal/Reading"_json_pointer); - - /* Add additional properties specific to sensorType */ - if (sensorType == "fan_tach") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "Value", "/SpeedRPM"_json_pointer); - } - } - else if (sensorType != "power") - { - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", - "/UpperThresholdNonCritical"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", - "/LowerThresholdNonCritical"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", - "/UpperThresholdCritical"_json_pointer); - properties.emplace_back( - "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", - "/LowerThresholdCritical"_json_pointer); - } + SensorPropertyList properties; - // TODO Need to get UpperThresholdFatal and LowerThresholdFatal - - if (chassisSubNode == ChassisSubNode::sensorsNode) - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MinValue", - "/ReadingRangeMin"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MaxValue", - "/ReadingRangeMax"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Accuracy", - "Accuracy", "/Accuracy"_json_pointer); - } - else if (sensorType == "temperature") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MinValue", - "/MinReadingRangeTemp"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MaxValue", - "/MaxReadingRangeTemp"_json_pointer); - } - else if (sensorType != "power") - { - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MinValue", - "/MinReadingRange"_json_pointer); - properties.emplace_back("xyz.openbmc_project.Sensor.Value", - "MaxValue", - "/MaxReadingRange"_json_pointer); - } - } + // Add additional property mappings based on the sensor type/node + mapPropertiesBySubnode(sensorType, chassisSubNode, properties, unit, + isExcerpt); - for (const std::tuple& p : properties) + for (const SensorPropertyMap& p : properties) { for (const auto& [valueName, valueVariant] : propertiesDict) { diff --git a/redfish-core/include/utils/systems_utils.hpp b/redfish-core/include/utils/systems_utils.hpp index 8e522a0274..618bea12ec 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, @@ -216,48 +219,38 @@ 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 hostStateService; + return std::format("xyz.openbmc_project.State.Host{}", computerSystemIndex); } 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 chassisStateService; + return std::format("xyz.openbmc_project.State.Chassis{}", + computerSystemIndex); } -namespace systems_utils +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( const std::shared_ptr& asyncResp, @@ -312,8 +305,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 +357,5 @@ inline bool indexMatchingSubTreeMapObjectPath( return false; } +} // namespace systems_utils } // namespace redfish diff --git a/redfish-core/lib/assembly.hpp b/redfish-core/lib/assembly.hpp index 4112a7941e..5a29055cb5 100644 --- a/redfish-core/lib/assembly.hpp +++ b/redfish-core/lib/assembly.hpp @@ -10,6 +10,7 @@ #include "generated/enums/resource.hpp" #include "http_request.hpp" #include "http_response.hpp" +#include "led.hpp" #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" @@ -17,6 +18,7 @@ #include "utils/asset_utils.hpp" #include "utils/chassis_utils.hpp" #include "utils/dbus_utils.hpp" +#include "utils/json_utils.hpp" #include #include @@ -27,7 +29,9 @@ #include #include +#include #include +#include #include #include #include @@ -193,7 +197,7 @@ inline void getAssemblyProperties( for (const std::string& assembly : assemblies) { nlohmann::json::object_t item; - item["@odata.type"] = "#Assembly.v1_5_1.AssemblyData"; + item["@odata.type"] = "#Assembly.v1_6_0.AssemblyData"; item["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/Assembly#/Assemblies/{}", chassisId, std::to_string(assemblyIndex)); @@ -210,6 +214,13 @@ inline void getAssemblyProperties( std::bind_front(afterGetDbusObject, asyncResp, assembly, assemblyJsonPtr)); + getLocationIndicatorActive( + asyncResp, assembly, [asyncResp, assemblyJsonPtr](bool asserted) { + asyncResp->res + .jsonValue[assemblyJsonPtr]["LocationIndicatorActive"] = + asserted; + }); + nlohmann::json& assemblyArray = asyncResp->res.jsonValue["Assemblies"]; asyncResp->res.jsonValue["Assemblies@odata.count"] = assemblyArray.size(); @@ -225,7 +236,7 @@ inline void afterHandleChassisAssemblyGet( { if (ec) { - BMCWEB_LOG_WARNING("Chassis {} not found", chassisID); + BMCWEB_LOG_WARNING("Chassis {} not found, ec={}", chassisID, ec); messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); return; } @@ -234,7 +245,7 @@ inline void afterHandleChassisAssemblyGet( boost::beast::http::field::link, "; rel=describedby"); - asyncResp->res.jsonValue["@odata.type"] = "#Assembly.v1_5_1.Assembly"; + asyncResp->res.jsonValue["@odata.type"] = "#Assembly.v1_6_0.Assembly"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}/Assembly", chassisID); asyncResp->res.jsonValue["Name"] = "Assembly Collection"; @@ -289,7 +300,8 @@ inline void handleChassisAssemblyHead( const std::vector& /*assemblyList*/) { if (ec) { - BMCWEB_LOG_WARNING("Chassis {} not found", chassisID); + BMCWEB_LOG_WARNING("Chassis {} not found, ec={}", chassisID, + ec); messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); return; @@ -300,6 +312,79 @@ inline void handleChassisAssemblyHead( }); } +inline void afterHandleChassisAssemblyPatch( + const std::shared_ptr& asyncResp, + const std::string& chassisID, + std::vector& assemblyData, + const boost::system::error_code& ec, + const std::vector& assemblyList) +{ + if (ec) + { + BMCWEB_LOG_WARNING("Chassis {} not found, ec={}", chassisID, ec); + messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); + return; + } + + if (assemblyData.size() > assemblyList.size()) + { + BMCWEB_LOG_WARNING( + "The number of the input Assemblies is larger than the actual number of Assemblies"); + messages::arraySizeTooLong(asyncResp->res, "Assemblies", + assemblyList.size()); + return; + } + if (assemblyData.size() < assemblyList.size()) + { + BMCWEB_LOG_WARNING( + "The number of the input Assemblies is smaller than the actual number of Assemblies"); + messages::arraySizeTooShort(asyncResp->res, "Assemblies", + assemblyList.size()); + return; + } + + std::size_t assemblyIndex = 0; + for (nlohmann::json::object_t& item : assemblyData) + { + std::optional locationIndicatorActive; + if (json_util::readJsonObject(item, asyncResp->res, + "LocationIndicatorActive", + locationIndicatorActive)) + { + if (locationIndicatorActive.has_value()) + { + const auto& assembly = assemblyList[assemblyIndex]; + setLocationIndicatorActive(asyncResp, assembly, + *locationIndicatorActive); + } + } + assemblyIndex++; + } +} + +inline void handleChassisAssemblyPatch( + App& app, const crow::Request& req, + const std::shared_ptr& asyncResp, + const std::string& chassisID) +{ + if (!redfish::setUpRedfishRoute(app, req, asyncResp)) + { + return; + } + + std::vector assemblyData; + if (!redfish::json_util::readJsonPatch(req, asyncResp->res, "Assemblies", + assemblyData)) + { + return; + } + + assembly_utils::getChassisAssembly( + asyncResp, chassisID, + std::bind_front(afterHandleChassisAssemblyPatch, asyncResp, chassisID, + assemblyData)); +} + inline void requestRoutesAssembly(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Chassis//Assembly/") @@ -311,6 +396,11 @@ inline void requestRoutesAssembly(App& app) .privileges(redfish::privileges::getAssembly) .methods(boost::beast::http::verb::get)( std::bind_front(handleChassisAssemblyGet, std::ref(app))); + + BMCWEB_ROUTE(app, "/redfish/v1/Chassis//Assembly/") + .privileges(redfish::privileges::patchAssembly) + .methods(boost::beast::http::verb::patch)( + std::bind_front(handleChassisAssemblyPatch, std::ref(app))); } } // namespace redfish diff --git a/redfish-core/lib/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..90e3b60e83 100644 --- a/redfish-core/lib/chassis.hpp +++ b/redfish-core/lib/chassis.hpp @@ -192,6 +192,24 @@ inline void getChassisState(std::shared_ptr asyncResp) asyncResp->res.jsonValue["Status"]["State"] = resource::State::StandbyOffline; } + else if ( + chassisState == + "xyz.openbmc_project.State.Chassis.PowerState.TransitioningToOff") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOff; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::StandbyOffline; + } + else if ( + chassisState == + "xyz.openbmc_project.State.Chassis.PowerState.TransitioningToOn") + { + asyncResp->res.jsonValue["PowerState"] = + resource::PowerState::PoweringOn; + asyncResp->res.jsonValue["Status"]["State"] = + resource::State::Starting; + } }); } @@ -457,8 +475,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/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) { diff --git a/redfish-core/lib/led.hpp b/redfish-core/lib/led.hpp index b74dc4d5a0..a3dc1c98e7 100644 --- a/redfish-core/lib/led.hpp +++ b/redfish-core/lib/led.hpp @@ -306,6 +306,7 @@ inline void getLedGroupPath( inline void afterGetLedState( const std::shared_ptr& asyncResp, + const std::function& callback, const boost::system::error_code& ec, bool assert) { if (ec) @@ -319,10 +320,11 @@ inline void afterGetLedState( return; } - asyncResp->res.jsonValue["LocationIndicatorActive"] = assert; + callback(assert); } inline void getLedState(const std::shared_ptr& asyncResp, + const std::function& callback, const boost::system::error_code& ec, const std::string& ledGroupPath, const std::string& service) @@ -346,7 +348,7 @@ inline void getLedState(const std::shared_ptr& asyncResp, sdbusplus::asio::getProperty( *crow::connections::systemBus, service, ledGroupPath, "xyz.openbmc_project.Led.Group", "Asserted", - std::bind_front(afterGetLedState, asyncResp)); + std::bind_front(afterGetLedState, asyncResp, callback)); } /** @@ -355,20 +357,31 @@ inline void getLedState(const std::shared_ptr& asyncResp, * @param[in] asyncResp Shared pointer for generating response * message. * @param[in] objPath Object path on PIM - * + * @param[in] callback to pass value * @return None. */ + inline void getLocationIndicatorActive( const std::shared_ptr& asyncResp, - const std::string& objPath) + const std::string& objPath, std::function&& callback) { BMCWEB_LOG_DEBUG("Get LocationIndicatorActive for {}", objPath); - getLedGroupPath(asyncResp, objPath, - [asyncResp](const boost::system::error_code& ec, - const std::string& ledGroupPath, - const std::string& service) { - getLedState(asyncResp, ec, ledGroupPath, service); - }); + getLedGroupPath( + asyncResp, objPath, + [asyncResp, callback = std::move(callback)]( + const boost::system::error_code& ec, + const std::string& ledGroupPath, const std::string& service) { + getLedState(asyncResp, callback, ec, ledGroupPath, service); + }); +} + +inline void getLocationIndicatorActive( + const std::shared_ptr& asyncResp, + const std::string& objPath) +{ + getLocationIndicatorActive(asyncResp, objPath, [asyncResp](bool asserted) { + asyncResp->res.jsonValue["LocationIndicatorActive"] = asserted; + }); } inline void setLedState(const std::shared_ptr& asyncResp, diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp index 9ee2ed6c04..2eb0e64c06 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) @@ -991,9 +984,9 @@ 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", - BMCWEB_REDFISH_SYSTEM_URI_NAME); + systemName); logServiceArrayLocal.emplace_back(std::move(member)); diff --git a/redfish-core/lib/managers.hpp b/redfish-core/lib/managers.hpp index 6f5417c433..0c1f2c8ea9 100644 --- a/redfish-core/lib/managers.hpp +++ b/redfish-core/lib/managers.hpp @@ -27,6 +27,7 @@ #include "utils/manager_utils.hpp" #include "utils/sw_utils.hpp" #include "utils/systemd_utils.hpp" +#include "utils/systems_utils.hpp" #include "utils/time_utils.hpp" #include @@ -682,6 +683,77 @@ inline void getManagerData(const std::shared_ptr& asyncResp, } } +inline void getManagedChassis( + const std::string& chassisId, + const std::shared_ptr& asyncResp) +{ + asyncResp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1; + nlohmann::json::array_t managerForChassis; + nlohmann::json::object_t manager; + manager["@odata.id"] = + boost::urls::format("/redfish/v1/Chassis/{}", chassisId); + managerForChassis.emplace_back(std::move(manager)); + asyncResp->res.jsonValue["Links"]["ManagerForChassis"] = + std::move(managerForChassis); +} + +inline void afterGetValidSystemPaths( + const std::shared_ptr& asyncResp, + nlohmann::json::object_t& manager, + nlohmann::json::array_t& managerForServers, + const boost::system::error_code& ec, + const dbus::utility::MapperGetSubTreePathsResponse& systemPaths) +{ + if (ec) + { + BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value()); + messages::internalError(asyncResp->res); + return; + } + + for (const std::string& system : systemPaths) + { + const sdbusplus::message::object_path systemPath(system); + manager["@odata.id"] = boost::urls::format("/redfish/v1/Systems/{}", + systemPath.filename()); + managerForServers.emplace_back(manager); + } + + asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = + managerForServers.size(); + + asyncResp->res.jsonValue["Links"]["ManagerForServers"] = + std::move(managerForServers); +} + +inline void getManagedServers( + const std::shared_ptr& asyncResp) +{ + nlohmann::json::object_t manager; + nlohmann::json::array_t managerForServers; + + if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) + { + constexpr std::array intf = { + "xyz.openbmc_project.Inventory.Decorator.ManagedHost"}; + dbus::utility::getSubTreePaths( + "/xyz/openbmc_project/inventory", 0, intf, + std::bind_front(afterGetValidSystemPaths, asyncResp, manager, + managerForServers)); + } + else + { + asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; + + manager["@odata.id"] = boost::urls::format( + "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); + managerForServers.emplace_back(std::move(manager)); + + asyncResp->res.jsonValue["Links"]["ManagerForServers"] = + std::move(managerForServers); + } +} + inline void handleManagerGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, @@ -766,19 +838,8 @@ inline void handleManagerGet( asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = nlohmann::json::array_t({"KVMIP"}); } - if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) - { - asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1; - nlohmann::json::array_t managerForServers; - nlohmann::json::object_t manager; - manager["@odata.id"] = std::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 +852,10 @@ 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) + { + getMainChassisId(asyncResp, std::bind_front(getManagedChassis)); + } dbus::utility::getProperty( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 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, 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.hpp b/redfish-core/lib/systems.hpp index 3123f068bd..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 = - getHostStateObjectPath(computerSystemIndex); + dbus::utility::getProperty( - 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 = - getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - 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 = - getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty( - 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 = - getChassisStateObjectPath(computerSystemIndex); + dbus::utility::getProperty( - 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 = - getHostStateObjectPath(computerSystemIndex); + dbus::utility::getAllProperties( - 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) { @@ -1262,8 +1261,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); } @@ -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) { @@ -1397,8 +1396,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 +1493,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); @@ -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); } @@ -2824,7 +2828,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 +2917,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 +2970,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 +3207,7 @@ inline void handleComputerSystemGet( } BMCWEB_LOG_DEBUG("requested system = {}", systemName); - getComputerSystemIndex( + systems_utils::getComputerSystemIndex( asyncResp, systemName, std::bind_front(processComputerSystemGet, asyncResp, systemName)); } @@ -3408,9 +3415,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( @@ -3523,10 +3531,9 @@ inline void getAllowedHostTransitions( const std::shared_ptr& asyncResp, const uint64_t computerSystemIndex) { - sdbusplus::message::object_path path = - getHostStateObjectPath(computerSystemIndex); dbus::utility::getProperty>( - getHostStateServiceName(computerSystemIndex), path, + systems_utils::getHostStateServiceName(computerSystemIndex), + systems_utils::getHostStateObjectPath(computerSystemIndex), "xyz.openbmc_project.State.Host", "AllowedHostTransitions", std::bind_front(afterGetAllowedHostTransitions, asyncResp)); } @@ -3571,7 +3578,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/redfish-core/lib/systems_logservices_hostlogger.hpp b/redfish-core/lib/systems_logservices_hostlogger.hpp index aae0195e1b..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"] = - std::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"] = - std::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"] = - std::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/") diff --git a/redfish-core/lib/systems_logservices_postcodes.hpp b/redfish-core/lib/systems_logservices_postcodes.hpp index 33249dc4be..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,31 +62,26 @@ 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"] = - std::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"; 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", 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"] = - 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", 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) 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; diff --git a/subprojects/boost.wrap b/subprojects/boost.wrap index b6ce680450..7094d06899 100644 --- a/subprojects/boost.wrap +++ b/subprojects/boost.wrap @@ -1,9 +1,9 @@ [wrap-file] -directory = boost-1.87.0 +directory = boost-1.89.0 -source_url = https://github.com/boostorg/boost/releases/download/boost-1.87.0/boost-1.87.0-cmake.tar.gz -source_hash = 78fbf579e3caf0f47517d3fb4d9301852c3154bfecdc5eeebd9b2b0292366f5b -source_filename = 1_87_0.tar.gz +source_url = https://github.com/boostorg/boost/releases/download/boost-1.89.0/boost-1.89.0-cmake.tar.gz +source_hash = 954a01219bf818c7fb850fa610c2c8c71a4fa28fa32a1900056bcb6ff58cf908 +source_filename = 1_89_0.tar.gz [provide] boost = boost_dep 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 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