| /* |
| // Copyright (c) 2018 Intel Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| */ |
| #pragma once |
| |
| #include "app.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "external_storer.hpp" |
| #include "health.hpp" |
| #include "managed_store.hpp" |
| #include "managed_store_types.hpp" |
| #include "query.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "utils/collection.hpp" |
| #include "utils/dbus_utils.hpp" |
| #include "utils/hex_utils.hpp" |
| #include "utils/json_utils.hpp" |
| #include "utils/location_utils.hpp" |
| #include "utils/system_utils.hpp" |
| |
| #include <boost/container/flat_map.hpp> |
| #include <boost/system/error_code.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <sdbusplus/message/native_types.hpp> |
| #include <sdbusplus/unpack_properties.hpp> |
| #include <sdbusplus/utility/dedup_variant.hpp> |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstdint> |
| #include <limits> |
| #include <string_view> |
| #include <unordered_map> |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| namespace redfish |
| { |
| |
| using resourceIdToSubtreeRespMapType = |
| std::unordered_map<std::string, |
| std::pair<std::string, dbus::utility::MapperServiceMap>>; |
| using ManagedObjectByServiceMap = |
| std::unordered_map<std::string, dbus::utility::ManagedObjectType>; |
| |
| constexpr std::array<std::string_view, 2> processorInterfaces = { |
| "xyz.openbmc_project.Inventory.Item.Cpu", |
| "xyz.openbmc_project.Inventory.Item.Accelerator"}; |
| |
| constexpr std::array<std::string_view, 1> subProcessorCoreInterfaces = { |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; |
| |
| constexpr std::array<std::string_view, 1> subProcessorThreadInterfaces = { |
| "xyz.openbmc_project.Inventory.Item.CpuThread"}; |
| |
| inline void getSubProcessorThreadCollectionWithExpand( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, |
| const std::string& processorId, const std::string& coreId, |
| const std::string& corePath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap); |
| |
| inline void getSubProcessorCoreCollectionWithExpand( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, |
| const std::string& processorId, const std::string& cpuPath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap); |
| |
| inline void getProcessorCollectionWithExpand( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap, uint8_t expandLevel, |
| const std::string& systempath); |
| |
| inline void handleGetCpuCoreThreadMetrics( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& cpuArg, |
| const std::string& coreArg, const std::string& threadArg); |
| |
| inline void |
| handleGetCpuMetrics(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& cpuArg); |
| |
| /** |
| * @brief Returns a collection of dbus interfaces applicable to SubProcessor's |
| * subtree. |
| * |
| * @param[in] expandLevel Requested expand level. |
| * |
| * @return Vector of strings capturing dbus interface names. |
| */ |
| inline std::vector<std::string> |
| getInterfacesForSubProcessorSubtree(uint8_t expandLevel) |
| { |
| std::vector<std::string> subProcessorSubtreeInterfaces = { |
| subProcessorCoreInterfaces.begin(), subProcessorCoreInterfaces.end()}; |
| if (expandLevel > 0) |
| { |
| subProcessorSubtreeInterfaces.insert( |
| subProcessorSubtreeInterfaces.end(), |
| subProcessorThreadInterfaces.begin(), |
| subProcessorThreadInterfaces.end()); |
| return subProcessorSubtreeInterfaces; |
| } |
| return subProcessorSubtreeInterfaces; |
| } |
| |
| /** |
| * @brief Returns a collection of dbus interfaces applicable to Processor's |
| * subtree. |
| * |
| * @param[in] expandLevel Requested expand level. |
| * |
| * @return Vector of strings capturing dbus interface names. |
| */ |
| inline std::vector<std::string> |
| getInterfacesForProcessorSubtree(uint8_t expandLevel) |
| { |
| if (expandLevel > 0) |
| { |
| std::vector<std::string> processorSubtreeInterfaces = |
| getInterfacesForSubProcessorSubtree(expandLevel - 1); |
| processorSubtreeInterfaces.insert(processorSubtreeInterfaces.end(), |
| processorInterfaces.begin(), |
| processorInterfaces.end()); |
| return processorSubtreeInterfaces; |
| } |
| return {processorInterfaces.begin(), processorInterfaces.end()}; |
| } |
| |
| /** |
| * @brief Fill out uuid info of a processor by |
| * requesting data from the given D-Bus object. |
| * |
| * @param[in,out] aResp Async HTTP response. |
| * @param[in] objPath D-Bus object to query. |
| * @param[in] interface D-Bus interface to query. |
| * @param[in] jsonPtr json pointer to index the response fields |
| * @param[in] managedObjects Dbus objects with interfaces and properties. |
| */ |
| inline void |
| getProcessorUUID(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& objPath, const std::string& interface, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& managedObjects) |
| { |
| BMCWEB_LOG_DEBUG << "Get Processor UUID"; |
| dbus::utility::DBusPropertiesMap properties; |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, |
| interface, properties); |
| |
| const std::string* property = nullptr; |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "UUID", property); |
| |
| if (!success) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (property != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["UUID"] = *property; |
| } |
| } |
| |
| /** |
| * @brief Populates Cpu information using given dbus properties. |
| * |
| * @param[in] aResp Async HTTP response. |
| * @param[in] jsonPtr json pointer to index the response fields. |
| * @param[in] dbusProperties DBusPropertiesMap to parse for Cpu data. |
| * |
| * @return void |
| */ |
| inline void getCpuDataFromDbusProperties( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::DBusPropertiesMap& dbusProperties) |
| { |
| BMCWEB_LOG_DEBUG << "Get CPU resources from dbus properties."; |
| |
| // Set the default value of state |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled"; |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; |
| |
| for (const auto& property : dbusProperties) |
| { |
| if (property.first == "Present") |
| { |
| const bool* cpuPresent = std::get_if<bool>(&property.second); |
| if (cpuPresent == nullptr) |
| { |
| // Important property not in desired type |
| messages::internalError(aResp->res); |
| return; |
| } |
| if (!*cpuPresent) |
| { |
| // Slot is not populated |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; |
| } |
| } |
| else if (property.first == "Functional") |
| { |
| const bool* cpuFunctional = std::get_if<bool>(&property.second); |
| if (cpuFunctional == nullptr) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| if (!*cpuFunctional) |
| { |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "Critical"; |
| } |
| } |
| else if (property.first == "CoreCount") |
| { |
| const uint16_t* coresCount = |
| std::get_if<uint16_t>(&property.second); |
| if (coresCount == nullptr) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| aResp->res.jsonValue[jsonPtr]["TotalCores"] = *coresCount; |
| } |
| else if (property.first == "MaxSpeedInMhz") |
| { |
| const uint32_t* value = std::get_if<uint32_t>(&property.second); |
| if (value != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["MaxSpeedMHz"] = *value; |
| } |
| } |
| else if (property.first == "Socket") |
| { |
| const std::string* value = |
| std::get_if<std::string>(&property.second); |
| if (value != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["Socket"] = *value; |
| } |
| } |
| else if (property.first == "ThreadCount") |
| { |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); |
| if (value != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["TotalThreads"] = *value; |
| } |
| } |
| else if (property.first == "CoreEnable") |
| { |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); |
| if (value != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["TotalEnabledCores"] = *value; |
| } |
| } |
| else if (property.first == "EffectiveFamily") |
| { |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); |
| if (value != nullptr && *value != 2) |
| { |
| aResp->res |
| .jsonValue[jsonPtr]["ProcessorId"]["EffectiveFamily"] = |
| "0x" + intToHexString(*value, 4); |
| } |
| } |
| else if (property.first == "EffectiveModel") |
| { |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); |
| if (value == nullptr) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| if (*value != 0) |
| { |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["EffectiveModel"] = |
| "0x" + intToHexString(*value, 4); |
| } |
| } |
| else if (property.first == "Id") |
| { |
| const uint64_t* value = std::get_if<uint64_t>(&property.second); |
| if (value != nullptr && *value != 0) |
| { |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"] |
| ["IdentificationRegisters"] = |
| "0x" + intToHexString(*value, 16); |
| } |
| } |
| else if (property.first == "Microcode") |
| { |
| const uint32_t* value = std::get_if<uint32_t>(&property.second); |
| if (value == nullptr) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| if (*value != 0) |
| { |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["MicrocodeInfo"] = |
| "0x" + intToHexString(*value, 8); |
| } |
| } |
| else if (property.first == "Step") |
| { |
| const uint16_t* value = std::get_if<uint16_t>(&property.second); |
| if (value == nullptr) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| if (*value != std::numeric_limits<uint16_t>::max()) |
| { |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"]["Step"] = |
| "0x" + intToHexString(*value, 4); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @brief Populates Cpu information using given dbus Managed Objects. |
| * |
| * @param[in] aResp Async HTTP response. |
| * @param[in] cpuId Cpu identifier. |
| * @param[in] objPath Cpu dbus object path. |
| * @param[in] interface Cpu dbus interface name. |
| * @param[in] jsonPtr json pointer to index the response fields. |
| * @param[in] dbusData Dbus Managed Objects to parse for Cpu data. |
| * |
| * @return void |
| */ |
| inline void getCpuData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& cpuId, const std::string& objPath, |
| const std::string& interface, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& dbusData) |
| { |
| BMCWEB_LOG_DEBUG << "Get Cpu data from managed objects for object " |
| << objPath; |
| aResp->res.jsonValue[jsonPtr]["Id"] = cpuId; |
| aResp->res.jsonValue[jsonPtr]["Name"] = "Processor"; |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "CPU"; |
| |
| bool slotPresent = false; |
| std::string corePath = objPath + "/core"; |
| size_t totalCores = 0; |
| for (const auto& object : dbusData) |
| { |
| dbus::utility::DBusPropertiesMap properties; |
| if (object.first.str == objPath) |
| { |
| dbus_utils::getPropertiesFromManagedObjects(dbusData, objPath, |
| interface, properties); |
| getCpuDataFromDbusProperties(aResp, jsonPtr, properties); |
| } |
| else if (object.first.str.starts_with(corePath)) |
| { |
| dbus_utils::getPropertiesFromManagedObjects( |
| dbusData, object.first.str, |
| "xyz.openbmc_project.Inventory.Item", properties); |
| |
| for (const auto& property : properties) |
| { |
| if (property.first != "Present") |
| { |
| continue; |
| } |
| const bool* present = std::get_if<bool>(&property.second); |
| if (present != nullptr && *present) |
| { |
| slotPresent = true; |
| totalCores++; |
| } |
| } |
| } |
| } |
| // In getCpuDataFromDbusProperties(), state and health are set based on the |
| // present and functional status. If core count is zero, then it has a |
| // higher precedence. |
| if (slotPresent) |
| { |
| if (totalCores == 0) |
| { |
| // Slot is not populated, set status end return |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; |
| } |
| aResp->res.jsonValue[jsonPtr]["TotalCores"] = totalCores; |
| } |
| } |
| |
| /** |
| * @brief Populates Cpu asset information using given dbus Managed Objects. |
| * |
| * @param[in] aResp Async HTTP response. |
| * @param[in] objPath Cpu dbus object path. |
| * @param[in] interface Cpu dbus interface name. |
| * @param[in] jsonPtr json pointer to index the response fields. |
| * @param[in] managedObjects Dbus Managed Objects to parse for Cpu data. |
| * |
| * @return void |
| */ |
| inline void |
| getCpuAssetData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& objPath, const std::string& interface, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& managedObjects) |
| { |
| BMCWEB_LOG_DEBUG << "Get Cpu Asset Data"; |
| const std::string* serialNumber = nullptr; |
| const std::string* model = nullptr; |
| const std::string* manufacturer = nullptr; |
| const std::string* partNumber = nullptr; |
| const std::string* sparePartNumber = nullptr; |
| |
| dbus::utility::DBusPropertiesMap properties; |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, |
| interface, properties); |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "SerialNumber", |
| serialNumber, "Model", model, "Manufacturer", manufacturer, |
| "PartNumber", partNumber, "SparePartNumber", sparePartNumber); |
| |
| if (!success) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (serialNumber != nullptr && !serialNumber->empty()) |
| { |
| aResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber; |
| } |
| |
| if ((model != nullptr) && !model->empty()) |
| { |
| aResp->res.jsonValue[jsonPtr]["Model"] = *model; |
| } |
| |
| if (manufacturer != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer; |
| |
| // Otherwise would be unexpected. |
| if (manufacturer->find("Intel") != std::string::npos) |
| { |
| aResp->res.jsonValue[jsonPtr]["ProcessorArchitecture"] = "x86"; |
| aResp->res.jsonValue[jsonPtr]["InstructionSet"] = "x86-64"; |
| } |
| else if (manufacturer->find("IBM") != std::string::npos) |
| { |
| aResp->res.jsonValue[jsonPtr]["ProcessorArchitecture"] = "Power"; |
| aResp->res.jsonValue[jsonPtr]["InstructionSet"] = "PowerISA"; |
| } |
| } |
| |
| if (partNumber != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber; |
| } |
| |
| if (sparePartNumber != nullptr && !sparePartNumber->empty()) |
| { |
| aResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber; |
| } |
| } |
| |
| /** |
| * @brief Populates Cpu revision data using given dbus Managed Objects. |
| * |
| * @param[in] aResp Async HTTP response. |
| * @param[in] objPath Cpu dbus object path. |
| * @param[in] interface Cpu dbus interface name. |
| * @param[in] jsonPtr json pointer to index the response fields. |
| * @param[in] managedObjects Dbus Managed Objects to parse for Cpu data. |
| * |
| * @return void |
| */ |
| inline void |
| getCpuRevisionData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& objPath, const std::string& interface, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& managedObjects) |
| { |
| BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; |
| dbus::utility::DBusPropertiesMap properties; |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, |
| interface, properties); |
| |
| const std::string* version = nullptr; |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Version", version); |
| |
| if (!success) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (version != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["Version"] = *version; |
| } |
| } |
| |
| /** |
| * @brief Populates accelerator data using given dbus Managed Objects. |
| * |
| * @param[in] aResp Async HTTP response. |
| * @param[in] acclrtrId Accelerator identifier. |
| * @param[in] objPath Accelerator dbus object path. |
| * @param[in] interface Accelerator dbus interface name. |
| * @param[in] jsonPtr json pointer to index the response fields. |
| * @param[in] managedObjects Dbus Managed Objects to parse. |
| * |
| * @return void |
| */ |
| inline void |
| getAcceleratorData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& acclrtrId, const std::string& objPath, |
| const std::string& interface, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& managedObjects) |
| { |
| BMCWEB_LOG_DEBUG |
| << "Get available system Accelerator resources by service."; |
| |
| const bool* functional = nullptr; |
| const bool* present = nullptr; |
| |
| dbus::utility::DBusPropertiesMap properties; |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, |
| interface, properties); |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Functional", functional, |
| "Present", present); |
| |
| if (!success) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| std::string state = "Enabled"; |
| std::string health = "OK"; |
| |
| if (present != nullptr && !*present) |
| { |
| state = "Absent"; |
| } |
| |
| if (functional != nullptr && !*functional) |
| { |
| if (state == "Enabled") |
| { |
| health = "Critical"; |
| } |
| } |
| |
| aResp->res.jsonValue[jsonPtr]["Id"] = acclrtrId; |
| aResp->res.jsonValue[jsonPtr]["Name"] = "Processor"; |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = state; |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = health; |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "Accelerator"; |
| } |
| |
| // OperatingConfig D-Bus Types |
| using TurboProfileProperty = std::vector<std::tuple<uint32_t, size_t>>; |
| using BaseSpeedPrioritySettingsProperty = |
| std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>; |
| // uint32_t and size_t may or may not be the same type, requiring a dedup'd |
| // variant |
| |
| /** |
| * Fill out the HighSpeedCoreIDs in a Processor resource from the given |
| * OperatingConfig D-Bus property. |
| * |
| * @param[in,out] aResp Async HTTP response. |
| * @param[in] jsonPtr json pointer to index the response fields |
| * @param[in] baseSpeedSettings Full list of base speed priority groups, |
| * to use to determine the list of high |
| * speed cores. |
| */ |
| inline void highSpeedCoreIdsHandler( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const BaseSpeedPrioritySettingsProperty& baseSpeedSettings) |
| { |
| // The D-Bus property does not indicate which bucket is the "high |
| // priority" group, so let's discern that by looking for the one with |
| // highest base frequency. |
| auto highPriorityGroup = baseSpeedSettings.cend(); |
| uint32_t highestBaseSpeed = 0; |
| for (auto it = baseSpeedSettings.cbegin(); it != baseSpeedSettings.cend(); |
| ++it) |
| { |
| const uint32_t baseFreq = std::get<uint32_t>(*it); |
| if (baseFreq > highestBaseSpeed) |
| { |
| highestBaseSpeed = baseFreq; |
| highPriorityGroup = it; |
| } |
| } |
| |
| nlohmann::json& jsonCoreIds = |
| aResp->res.jsonValue[jsonPtr]["HighSpeedCoreIDs"]; |
| jsonCoreIds = nlohmann::json::array(); |
| |
| // There may not be any entries in the D-Bus property, so only populate |
| // if there was actually something there. |
| if (highPriorityGroup != baseSpeedSettings.cend()) |
| { |
| jsonCoreIds = std::get<std::vector<uint32_t>>(*highPriorityGroup); |
| } |
| } |
| |
| /** |
| * Fill out OperatingConfig related items in a Processor resource by requesting |
| * data from the given D-Bus object. |
| * |
| * @param[in,out] aResp Async HTTP response. |
| * @param[in] cpuId CPU D-Bus name. |
| * @param[in] objPath D-Bus object to query. |
| * @param[in] interface D-Bus interface to query. |
| * @param[in] jsonPtr json pointer to index the response fields |
| * @param[in] managedObjects Dbus objects with interfaces and properties. |
| */ |
| inline void |
| getCpuConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& cpuId, const std::string& objPath, |
| const std::string& interface, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& managedObjects) |
| { |
| BMCWEB_LOG_INFO << "Getting CPU operating configs for " << cpuId; |
| |
| nlohmann::json& json = aResp->res.jsonValue; |
| |
| const sdbusplus::message::object_path* appliedConfig = nullptr; |
| const bool* baseSpeedPriorityEnabled = nullptr; |
| const BaseSpeedPrioritySettingsProperty* baseSpeedList = nullptr; |
| |
| dbus::utility::DBusPropertiesMap properties; |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, |
| interface, properties); |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "AppliedConfig", |
| appliedConfig, "BaseSpeedPriorityEnabled", baseSpeedPriorityEnabled); |
| |
| if (!success) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (appliedConfig != nullptr) |
| { |
| const std::string& dbusPath = appliedConfig->str; |
| std::string uri = "/redfish/v1/Systems/system/Processors/" + cpuId + |
| "/OperatingConfigs"; |
| nlohmann::json::object_t operatingConfig; |
| operatingConfig["@odata.id"] = uri; |
| json[jsonPtr]["OperatingConfigs"] = std::move(operatingConfig); |
| |
| // Reuse the D-Bus config object name for the Redfish |
| // URI |
| size_t baseNamePos = dbusPath.rfind('/'); |
| if (baseNamePos == std::string::npos || |
| baseNamePos == (dbusPath.size() - 1)) |
| { |
| // If the AppliedConfig was somehow not a valid path, |
| // skip adding any more properties, since everything |
| // else is tied to this applied config. |
| messages::internalError(aResp->res); |
| return; |
| } |
| uri += '/'; |
| uri += dbusPath.substr(baseNamePos + 1); |
| nlohmann::json::object_t appliedOperatingConfig; |
| appliedOperatingConfig["@odata.id"] = uri; |
| json[jsonPtr]["AppliedOperatingConfig"] = |
| std::move(appliedOperatingConfig); |
| |
| // Once we found the current applied config, read the base freq core ids |
| // out of Operating config. |
| |
| dbus::utility::DBusPropertiesMap config; |
| dbus_utils::getPropertiesFromManagedObjects( |
| managedObjects, dbusPath, |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", config); |
| |
| const bool parseConfigSuccess = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), config, |
| "BaseSpeedPrioritySettings", baseSpeedList); |
| |
| if (!parseConfigSuccess) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (baseSpeedList != nullptr) |
| { |
| highSpeedCoreIdsHandler(aResp, jsonPtr, *baseSpeedList); |
| } |
| } |
| |
| if (baseSpeedPriorityEnabled != nullptr) |
| { |
| json[jsonPtr]["BaseSpeedPriorityState"] = |
| *baseSpeedPriorityEnabled ? "Enabled" : "Disabled"; |
| } |
| } |
| |
| /** |
| * Populate the unique identifier in a Processor resource by requesting data |
| * from the given D-Bus object. |
| * |
| * @param[in,out] aResp Async HTTP response. |
| * @param[in] objPath D-Bus object to query. |
| * @param[in] interface D-Bus interface to query. |
| * @param[in] jsonPtr json pointer to index the response fields |
| * @param[in] managedObjects Dbus objects with interfaces and properties. |
| */ |
| inline void |
| getCpuUniqueId(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& objectPath, const std::string& interface, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& managedObjects) |
| { |
| BMCWEB_LOG_DEBUG << "Get CPU UniqueIdentifier"; |
| dbus::utility::DBusPropertiesMap properties; |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objectPath, |
| interface, properties); |
| |
| const std::string* id = nullptr; |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "UniqueIdentifier", id); |
| |
| if (!success) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (id != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["ProcessorId"] |
| ["ProtectedIdentificationNumber"] = *id; |
| } |
| } |
| |
| inline void getCpuChassisAssociation( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& processorId, const std::string& objectPath, |
| const nlohmann::json::json_pointer& jsonPtr) |
| { |
| BMCWEB_LOG_DEBUG << "Get CPU -- Chassis association"; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", objectPath + "/chassis", |
| "xyz.openbmc_project.Association", "endpoints", requestContext, |
| [asyncResp{asyncResp}, jsonPtr, |
| processorId](const boost::system::error_code ec, |
| const std::vector<std::string>& chassisList) { |
| if (ec) |
| { |
| return; |
| } |
| if (chassisList.empty()) |
| { |
| return; |
| } |
| if (chassisList.size() > 1) |
| { |
| BMCWEB_LOG_DEBUG << processorId |
| << " is associated with mutliple chassis"; |
| return; |
| } |
| |
| sdbusplus::message::object_path chassisPath(chassisList[0]); |
| std::string chassisName = chassisPath.filename(); |
| if (chassisName.empty()) |
| { |
| BMCWEB_LOG_ERROR << "filename() is empty in " << chassisPath.str; |
| return; |
| } |
| asyncResp->res.jsonValue[jsonPtr]["Links"]["Chassis"] = { |
| {"@odata.id", "/redfish/v1/Chassis/" + chassisName}}; |
| }); |
| } |
| |
| /** |
| * @brief Fill out the links to associated memory of a processor |
| * by looking up its D-Bus association. |
| * |
| * @param[in,out] aResp Async HTTP response. |
| * @param[in] objPath D-Bus object to query. |
| */ |
| inline void getMemoryLinks(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& objectPath, |
| const nlohmann::json::json_pointer& jsonPtr) |
| { |
| BMCWEB_LOG_DEBUG << "Get Processor -- Memory association"; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", objectPath + "/memory", |
| "xyz.openbmc_project.Association", "endpoints", requestContext, |
| [asyncResp, jsonPtr](const boost::system::error_code ec, |
| const std::vector<std::string>& memoryList) { |
| if (ec) |
| { |
| return; |
| } |
| if (memoryList.empty()) |
| { |
| return; |
| } |
| nlohmann::json::array_t memoryLinks; |
| for (const std::string& memory : memoryList) |
| { |
| sdbusplus::message::object_path memoryPath(memory); |
| const std::string memoryName = memoryPath.filename(); |
| if (memoryName.empty()) |
| { |
| BMCWEB_LOG_ERROR << "filename() is empty in " << memoryPath.str; |
| return; |
| } |
| nlohmann::json::object_t memoryObject; |
| memoryObject["@odata.id"] = |
| "/redfish/v1/Systems/system/Memory/" + memoryName; |
| memoryLinks.emplace_back(memoryObject); |
| } |
| asyncResp->res.jsonValue[jsonPtr]["Links"]["Memory"] = |
| std::move(memoryLinks); |
| }); |
| } |
| |
| /** |
| * Find the D-Bus object representing the requested Processor, and call the |
| * handler with the results. If matching object is not found, add 404 error to |
| * response and don't call the handler. |
| * |
| * @param[in,out] resp Async HTTP response. |
| * @param[in] processorId Redfish Processor Id. |
| * @param[in] handler Callback to continue processing request upon |
| * successfully finding object. |
| */ |
| template <typename Handler> |
| inline void getProcessorObject(const std::shared_ptr<bmcweb::AsyncResp>& resp, |
| const std::string& processorId, |
| const std::string& systemPath, Handler&& handler) |
| { |
| BMCWEB_LOG_DEBUG << "Get available system processor resources."; |
| |
| const bool multiHost = !systemPath.empty(); |
| |
| // Default systemName is system and change if system is multi-host |
| std::string systemName = "system"; |
| // The subtree to query for Dimms changes if single or multi-host |
| std::string subtreeRoot = "/xyz/openbmc_project/inventory"; |
| |
| if (multiHost) |
| { |
| systemName = sdbusplus::message::object_path(systemPath).filename(); |
| subtreeRoot = systemPath; |
| } |
| |
| managedStore::ManagedObjectStoreContext context(resp); |
| managedStore::GetManagedObjectStore()->getSubTree( |
| subtreeRoot, 0, processorInterfaces, context, |
| [resp{resp}, processorId, multiHost, systemPath, |
| handler = std::forward<Handler>(handler), context]( |
| const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) mutable { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error: " << ec.value(); |
| messages::internalError(resp->res); |
| return; |
| } |
| |
| std::vector<std::string> serviceNames = |
| dbus_utils::getServiceNamesFromSubtree(subtree); |
| if (serviceNames.empty()) |
| { |
| BMCWEB_LOG_ERROR << "Service list is empty"; |
| messages::internalError(resp->res); |
| return; |
| } |
| |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); |
| dbus_utils::getManagedObjectsInEachService( |
| std::move(serviceNames), path, context, |
| [resp{std::move(resp)}, subtree, processorId, multiHost, systemPath, |
| handler = std::forward<Handler>(handler)]( |
| const ManagedObjectByServiceMap& managedObjectsByService) { |
| if (managedObjectsByService.empty()) |
| { |
| BMCWEB_LOG_ERROR |
| << "GetManagedObjects does not return any object."; |
| messages::internalError(resp->res); |
| return; |
| } |
| BMCWEB_LOG_DEBUG << "Looping through subtree"; |
| |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| BMCWEB_LOG_DEBUG << objectPath; |
| if (multiHost && !objectPath.starts_with(systemPath)) |
| { |
| continue; |
| } |
| |
| // Ignore any objects which don't end with our desired cpu name |
| if (!objectPath.ends_with(processorId)) |
| { |
| continue; |
| } |
| |
| // Filter out objects that don't have the CPU-specific |
| // interfaces to make sure we can return 404 on non-CPUs |
| // (e.g. /redfish/../Processors/dimm0) |
| if (!dbus_utils::findInterfacesInServiceMap( |
| serviceMap, processorInterfaces)) |
| { |
| continue; |
| } |
| handler(objectPath, subtree, managedObjectsByService); |
| return; |
| } |
| messages::resourceNotFound(resp->res, "Processor", processorId); |
| }); |
| }); |
| } |
| |
| inline void |
| getProcessorState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& service, const std::string& objPath, |
| const nlohmann::json::json_pointer& jsonPtr) |
| { |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<bool>( |
| service, objPath, "xyz.openbmc_project.Inventory.Item", "Present", |
| requestContext, |
| [asyncResp, objPath, jsonPtr](const boost::system::error_code& ec, |
| bool present) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "get presence failed for Processor State " |
| << objPath << " with error " << ec; |
| |
| if (ec.value() != EBADR) |
| { |
| messages::internalError(asyncResp->res); |
| } |
| return; |
| } |
| asyncResp->res.jsonValue[jsonPtr]["Status"]["State"] = |
| present ? "Enabled" : "Absent"; |
| }); |
| } |
| |
| inline void getProcessorStateData( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, const std::string& processorId, |
| const std::string& procStateId, const nlohmann::json::json_pointer& jsonPtr) |
| { |
| BMCWEB_LOG_DEBUG << "Processor State Id: " << procStateId; |
| asyncResp->res.jsonValue[jsonPtr]["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, |
| "Processors", processorId, "Oem", "Google", |
| "ProcessorStates", procStateId); |
| asyncResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#GoogleProcessorState.GoogleProcessorState"; |
| asyncResp->res.jsonValue[jsonPtr]["Id"] = procStateId; |
| asyncResp->res.jsonValue[jsonPtr]["Name"] = "Processor"; |
| getProcessorState(asyncResp, "xyz.openbmc_project.EntityManager", |
| absl::StrCat("/xyz/openbmc_project/inventory/", |
| systemName, "/processorstate/", procStateId), |
| jsonPtr); |
| } |
| |
| void handleGetProcessorState( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, const std::string& processorId, |
| const std::string& procStateId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| getProcessorStateData(asyncResp, systemName, processorId, procStateId, |
| ""_json_pointer); |
| } |
| |
| using Mapper = dbus::utility::MapperGetSubTreePathsResponse; |
| inline void getProcessorStateLinksOrExpand( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& processorId, const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& systemName, uint8_t expandLevel, |
| const boost::system::error_code& ec, const Mapper& procStatePaths) |
| { |
| BMCWEB_LOG_DEBUG << "Get Processor State URL"; |
| |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR |
| << "ProcessorState Collection GetSubTreePaths handler Dbus error " |
| << ec; |
| return; |
| } |
| nlohmann::json::array_t procStateLinks; |
| size_t stateCount = 0; |
| for (const std::string& procState : procStatePaths) |
| { |
| std::string procStateId = |
| sdbusplus::message::object_path(procState).filename(); |
| nlohmann::json::json_pointer processorStatePtr = |
| jsonPtr / "Oem" / "Google" / "ProcessorStates" / stateCount++; |
| if (expandLevel > 0) |
| { |
| getProcessorStateData(aResp, systemName, processorId, procStateId, |
| processorStatePtr); |
| } |
| else |
| { |
| nlohmann::json::object_t procStateObj; |
| aResp->res.jsonValue[processorStatePtr]["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", |
| systemName, "Processors", |
| processorId, "Oem", "Google", |
| "ProcessorStates", procStateId); |
| } |
| } |
| } |
| |
| inline void |
| getProcessorData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| uint8_t expandLevel, const std::string& processorId, |
| const std::string& systemPath, |
| const std::string& objectPath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap) |
| { |
| BMCWEB_LOG_DEBUG << "Get processor data."; |
| |
| const bool multiHost = !systemPath.empty(); |
| |
| // Default systemName is system and change if system is multi-host |
| std::string systemName = "system"; |
| |
| if (multiHost) |
| { |
| systemName = sdbusplus::message::object_path(systemPath).filename(); |
| } |
| |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#Processor.v1_11_0.Processor"; |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", systemName, "Processors", processorId); |
| |
| // Get service to interface map for cpu |
| dbus::utility::MapperServiceMap serviceMap; |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) |
| { |
| if (objectPath == parsedObjectPath) |
| { |
| serviceMap = parsedServiceMap; |
| break; |
| } |
| } |
| |
| for (const auto& [serviceName, interfaceList] : serviceMap) |
| { |
| auto iter = managedObjectsMap.find(serviceName); |
| if (iter == managedObjectsMap.end()) |
| { |
| BMCWEB_LOG_DEBUG << "Cpu data not available for serviceName " |
| << serviceName; |
| continue; |
| } |
| |
| BMCWEB_LOG_DEBUG << "Populate processor data from dbus interfaces."; |
| for (const auto& interface : interfaceList) |
| { |
| if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") |
| { |
| getCpuAssetData(aResp, objectPath, interface, jsonPtr, |
| iter->second); |
| } |
| else if (interface == |
| "xyz.openbmc_project.Inventory.Decorator.Revision") |
| { |
| getCpuRevisionData(aResp, objectPath, interface, jsonPtr, |
| iter->second); |
| } |
| else if (interface == "xyz.openbmc_project.Inventory.Item.Cpu") |
| { |
| getCpuData(aResp, processorId, objectPath, interface, jsonPtr, |
| iter->second); |
| } |
| else if (interface == |
| "xyz.openbmc_project.Inventory.Item.Accelerator") |
| { |
| getAcceleratorData(aResp, processorId, objectPath, interface, |
| jsonPtr, iter->second); |
| } |
| else if ( |
| interface == |
| "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig") |
| { |
| getCpuConfigData(aResp, processorId, objectPath, interface, |
| jsonPtr, iter->second); |
| } |
| else if (interface == |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode") |
| { |
| location_util::getLocationCodeFromManagedObject( |
| aResp, objectPath, interface, jsonPtr / "Location", |
| iter->second); |
| location_util::getPartLocationContext( |
| aResp, jsonPtr / "Location", objectPath + "/chassis"); |
| } |
| else if (interface == "xyz.openbmc_project.Common.UUID") |
| { |
| getProcessorUUID(aResp, objectPath, interface, jsonPtr, |
| iter->second); |
| } |
| else if (interface == |
| "xyz.openbmc_project.Inventory.Decorator.UniqueIdentifier") |
| { |
| getCpuUniqueId(aResp, objectPath, interface, jsonPtr, |
| iter->second); |
| } |
| else |
| { |
| std::optional<std::string> locationType = |
| location_util::getLocationType(interface); |
| if (locationType == std::nullopt) |
| { |
| BMCWEB_LOG_DEBUG |
| << "getLocationType for Processor failed for " |
| << interface; |
| continue; |
| } |
| |
| aResp->res.jsonValue[jsonPtr]["Location"]["PartLocation"] |
| ["LocationType"] = *locationType; |
| } |
| } |
| } |
| getCpuChassisAssociation(aResp, processorId, objectPath, jsonPtr); |
| getMemoryLinks(aResp, objectPath, jsonPtr); |
| |
| managedStore::ManagedObjectStoreContext context(aResp); |
| constexpr std::array<std::string_view, 1> procStateIntf = { |
| "xyz.openbmc_project.Inventory.Item.ProcessorState"}; |
| dbus_utils::getSubTreePaths( |
| "/xyz/openbmc_project/inventory", 0, procStateIntf, context, |
| std::bind_front(getProcessorStateLinksOrExpand, aResp, processorId, |
| jsonPtr, systemName, expandLevel)); |
| |
| // TODO(edward): Support SubProcessors and ProcessorMetrics for Multi-Host |
| // as use-cases arise |
| if (!multiHost) |
| { |
| if (expandLevel > 0) |
| { |
| nlohmann::json::json_pointer subProcessorPtr = |
| jsonPtr / "SubProcessors"; |
| getSubProcessorCoreCollectionWithExpand( |
| aResp, subProcessorPtr, expandLevel - 1, processorId, |
| objectPath, subtree, managedObjectsMap); |
| } |
| else |
| { |
| aResp->res.jsonValue[jsonPtr]["SubProcessors"]["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", |
| "system", "Processors", |
| processorId, "SubProcessors"); |
| } |
| |
| if (expandLevel > 0) |
| { |
| nlohmann::json::json_pointer metricsPtr = jsonPtr / "Metrics"; |
| handleGetCpuMetrics(aResp, metricsPtr, processorId); |
| } |
| else |
| { |
| // Inserting the "Metrics" JSON stanza |
| std::string metricsUrl = aResp->res.jsonValue[jsonPtr]["@odata.id"]; |
| metricsUrl += "/ProcessorMetrics"; |
| nlohmann::json::object_t metricsObj; |
| metricsObj["@odata.id"] = std::move(metricsUrl); |
| aResp->res.jsonValue[jsonPtr]["Metrics"] = std::move(metricsObj); |
| } |
| } |
| } |
| |
| template <typename Handler> |
| inline void getProcessorPaths(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& processorId, Handler&& handler) |
| { |
| std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.Cpu"}; |
| |
| managedStore::ManagedObjectStoreContext requestContext(aResp); |
| managedStore::GetManagedObjectStore()->getSubTreePaths( |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, |
| [processorId, aResp{aResp}, |
| handler{std::forward<Handler>(handler)}]( |
| const boost::system::error_code ec, |
| const std::vector<std::string>& subTreePaths) { |
| if (ec) |
| { |
| handler(ec, ""); |
| return; |
| } |
| |
| for (const std::string& cpuPath : subTreePaths) |
| { |
| if (sdbusplus::message::object_path(cpuPath).filename() != |
| processorId) |
| { |
| continue; |
| } |
| |
| handler(ec, cpuPath); |
| return; |
| } |
| |
| // Set an error code since valid cpu path is not found |
| handler(boost::system::errc::make_error_code( |
| boost::system::errc::no_such_file_or_directory), |
| ""); |
| }); |
| } |
| |
| template <typename Handler> |
| inline void |
| getSubProcessorCorePaths(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& processorId, |
| const std::string& coreId, Handler&& handler) |
| { |
| |
| std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.Cpu"}; |
| managedStore::ManagedObjectStoreContext requestContext(aResp); |
| managedStore::GetManagedObjectStore()->getSubTreePaths( |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, |
| [processorId, coreId, aResp{aResp}, |
| handler{std::forward<Handler>(handler)}, |
| requestContext](const boost::system::error_code ec, |
| const std::vector<std::string>& subTreeCpuPaths) { |
| if (ec) |
| { |
| handler(ec, ""); |
| return; |
| } |
| |
| for (const std::string& cpuPath : subTreeCpuPaths) |
| { |
| if (sdbusplus::message::object_path(cpuPath).filename() != |
| processorId) |
| { |
| continue; |
| } |
| |
| std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; |
| managedStore::GetManagedObjectStore()->getSubTreePaths( |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, |
| [processorId, coreId, aResp{aResp}, |
| handler](const boost::system::error_code ec2, |
| const std::vector<std::string>& subTreeCorePaths) { |
| if (ec2) |
| { |
| handler(ec2, ""); |
| return; |
| } |
| |
| for (const std::string& corePath : subTreeCorePaths) |
| { |
| if (sdbusplus::message::object_path(corePath).filename() != |
| coreId) |
| { |
| continue; |
| } |
| handler(ec2, corePath); |
| return; |
| } |
| // Set an error code since valid processor core path is not |
| // found |
| handler(boost::system::errc::make_error_code( |
| boost::system::errc::no_such_file_or_directory), |
| ""); |
| }); |
| return; |
| } |
| |
| // Set an error code since valid processor cpu path is not found |
| handler(boost::system::errc::make_error_code( |
| boost::system::errc::no_such_file_or_directory), |
| ""); |
| }); |
| } |
| |
| inline void |
| getCoreThreadData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| [[maybe_unused]] const uint8_t expandLevel, |
| const std::string& processorId, const std::string& coreId, |
| const std::string& threadId, |
| const std::string& threadPath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap) |
| { |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#Processor.v1_11_0.Processor"; |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors", coreId, "SubProcessors", threadId); |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor"; |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "Thread"; |
| aResp->res.jsonValue[jsonPtr]["Id"] = threadId; |
| |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled"; |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; |
| |
| bool present = false; |
| bool functional = false; |
| |
| dbus::utility::MapperServiceMap serviceMap; |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) |
| { |
| if (threadPath == parsedObjectPath) |
| { |
| serviceMap = parsedServiceMap; |
| break; |
| } |
| } |
| |
| for (const auto& [serviceName, interfaceList] : serviceMap) |
| { |
| auto iter = managedObjectsMap.find(serviceName); |
| if (iter == managedObjectsMap.end()) |
| { |
| BMCWEB_LOG_DEBUG << "Thread data not available for serviceName " |
| << serviceName; |
| continue; |
| } |
| |
| for (const auto& interface : interfaceList) |
| { |
| dbus::utility::DBusPropertiesMap properties; |
| if (interface == "xyz.openbmc_project.State." |
| "Decorator.OperationalStatus") |
| { |
| const bool* propertyFunctional = nullptr; |
| dbus_utils::getPropertiesFromManagedObjects( |
| iter->second, threadPath, interface, properties); |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Functional", |
| propertyFunctional); |
| if (!success || propertyFunctional == nullptr) |
| { |
| continue; |
| } |
| functional = *propertyFunctional; |
| } |
| else if (interface == "xyz.openbmc_project.Inventory.Item") |
| { |
| const bool* propertyPresent = nullptr; |
| const std::string* propertyPrettyName = nullptr; |
| dbus_utils::getPropertiesFromManagedObjects( |
| iter->second, threadPath, interface, properties); |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Present", |
| propertyPresent, "PrettyName", propertyPrettyName); |
| if (!success) |
| { |
| continue; |
| } |
| |
| if (propertyPresent != nullptr) |
| { |
| present = *propertyPresent; |
| } |
| |
| if (propertyPrettyName != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["Name"] = *propertyPrettyName; |
| } |
| } |
| else if (interface == |
| "xyz.openbmc_project.Inventory.Item.CpuThread") |
| { |
| const uint32_t* propertyMicrocode = nullptr; |
| dbus_utils::getPropertiesFromManagedObjects( |
| iter->second, threadPath, interface, properties); |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Microcode", |
| propertyMicrocode); |
| if (!success) |
| { |
| continue; |
| } |
| |
| if (propertyMicrocode != nullptr) |
| { |
| aResp->res |
| .jsonValue[jsonPtr]["ProcessorId"]["MicrocodeInfo"] = |
| "0x" + intToHexString(*propertyMicrocode, 8); |
| } |
| } |
| } |
| } |
| |
| if (!present) |
| { |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; |
| } |
| |
| if (!functional) |
| { |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "Critical"; |
| } |
| |
| if (expandLevel > 0) |
| { |
| nlohmann::json::json_pointer metricsPtr = jsonPtr / "Metrics"; |
| handleGetCpuCoreThreadMetrics(aResp, metricsPtr, processorId, coreId, |
| threadId); |
| } |
| else |
| { |
| // Inserting the "Metrics" JSON stanza |
| std::string metricsUrl = aResp->res.jsonValue[jsonPtr]["@odata.id"]; |
| metricsUrl += "/ProcessorMetrics"; |
| nlohmann::json::object_t metricsObj; |
| metricsObj["@odata.id"] = std::move(metricsUrl); |
| aResp->res.jsonValue[jsonPtr]["Metrics"] = std::move(metricsObj); |
| } |
| } |
| |
| inline void getSubProcessorThreadData( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& processorId, const std::string& coreId, |
| const std::string& threadId, const boost::system::error_code ec, |
| const std::string& corePath) |
| { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); |
| // No sub processor thread objects found by mapper |
| if (ec.value() == boost::system::errc::io_error) |
| { |
| messages::resourceNotFound(aResp->res, |
| "#Processor.v1_11_0.Processor", coreId); |
| return; |
| } |
| |
| messages::internalError(aResp->res); |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); |
| |
| managedStore::ManagedObjectStoreContext context(aResp); |
| managedStore::GetManagedObjectStore()->getSubTree( |
| corePath, 0, subProcessorThreadInterfaces, context, |
| [aResp{aResp}, processorId, coreId, threadId, |
| context](const boost::system::error_code& ec2, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec2) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec2: " << ec2.value(); |
| // No sub processor core objects found by mapper |
| if (ec2.value() == boost::system::errc::io_error) |
| { |
| messages::resourceNotFound( |
| aResp->res, "#Processor.v1_11_0.Processor", coreId); |
| return; |
| } |
| |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| std::vector<std::string> serviceNames = |
| dbus_utils::getServiceNamesFromSubtree(subtree); |
| |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); |
| dbus_utils::getManagedObjectsInEachService( |
| std::move(serviceNames), path, context, |
| [aResp{aResp}, subtree, processorId, coreId, threadId]( |
| const ManagedObjectByServiceMap& managedObjectsByService) { |
| if (managedObjectsByService.empty()) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| if (sdbusplus::message::object_path(objectPath).filename() != |
| threadId) |
| { |
| continue; |
| } |
| |
| getCoreThreadData(aResp, ""_json_pointer, 0, processorId, |
| coreId, threadId, objectPath, subtree, |
| managedObjectsByService); |
| return; |
| } |
| |
| // Object not found |
| messages::resourceNotFound( |
| aResp->res, "#Processor.v1_11_0.Processor", threadId); |
| }); |
| }); |
| } |
| |
| inline void getSubProcessorThreadMembers( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& processorId, const std::string& coreId, |
| const boost::system::error_code ec, const std::string& corePath) |
| { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); |
| // No sub processor core objects found by mapper |
| if (ec.value() == boost::system::errc::io_error) |
| { |
| messages::resourceNotFound(aResp->res, |
| "#Processor.v1_11_0.Processor", coreId); |
| return; |
| } |
| |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); |
| aResp->res.jsonValue["@odata.type"] = |
| "#ProcessorCollection.ProcessorCollection"; |
| aResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors", coreId, "SubProcessors"); |
| aResp->res.jsonValue["Name"] = "SubProcessor Collection"; |
| |
| const boost::urls::url& subProcessorsPath = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors", coreId, "SubProcessors"); |
| |
| constexpr std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.CpuThread"}; |
| |
| collection_util::getCollectionMembers(aResp, subProcessorsPath, interfaces, |
| corePath.c_str()); |
| } |
| |
| inline void |
| getCpuCoreData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| uint8_t expandLevel, const std::string& processorId, |
| const std::string& coreId, const std::string& corePath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap) |
| { |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#Processor.v1_11_0.Processor"; |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors", coreId); |
| |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor"; |
| aResp->res.jsonValue[jsonPtr]["ProcessorType"] = "Core"; |
| aResp->res.jsonValue[jsonPtr]["Id"] = coreId; |
| |
| if (expandLevel > 0) |
| { |
| nlohmann::json::json_pointer subProcessorPtr = |
| jsonPtr / "SubProcessors"; |
| getSubProcessorThreadCollectionWithExpand( |
| aResp, subProcessorPtr, expandLevel - 1, processorId, coreId, |
| corePath, subtree, managedObjectsMap); |
| } |
| else |
| { |
| aResp->res.jsonValue[jsonPtr]["SubProcessors"]["@odata.id"] = |
| crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors", coreId, "SubProcessors"); |
| } |
| |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled"; |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK"; |
| |
| bool present = false; |
| bool functional = false; |
| |
| dbus::utility::MapperServiceMap serviceMap; |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) |
| { |
| if (corePath == parsedObjectPath) |
| { |
| serviceMap = parsedServiceMap; |
| break; |
| } |
| } |
| |
| for (const auto& [serviceName, interfaceList] : serviceMap) |
| { |
| auto iter = managedObjectsMap.find(serviceName); |
| if (iter == managedObjectsMap.end()) |
| { |
| BMCWEB_LOG_DEBUG << "Cpu data not available for serviceName " |
| << serviceName; |
| continue; |
| } |
| |
| for (const auto& interface : interfaceList) |
| { |
| dbus::utility::DBusPropertiesMap properties; |
| if (interface == "xyz.openbmc_project.State." |
| "Decorator.OperationalStatus") |
| { |
| const bool* propertyFunctional = nullptr; |
| dbus_utils::getPropertiesFromManagedObjects( |
| iter->second, corePath, interface, properties); |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Functional", |
| propertyFunctional); |
| |
| if (!success) |
| { |
| continue; |
| } |
| |
| if (propertyFunctional != nullptr) |
| { |
| functional = *propertyFunctional; |
| } |
| } |
| else if (interface == "xyz.openbmc_project.Inventory.Item") |
| { |
| const bool* propertyPresent = nullptr; |
| const std::string* propertyPrettyName = nullptr; |
| dbus_utils::getPropertiesFromManagedObjects( |
| iter->second, corePath, interface, properties); |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Present", |
| propertyPresent, "PrettyName", propertyPrettyName); |
| |
| if (!success) |
| { |
| continue; |
| } |
| |
| if (propertyPresent != nullptr) |
| { |
| present = *propertyPresent; |
| } |
| |
| if (propertyPrettyName != nullptr) |
| { |
| aResp->res.jsonValue[jsonPtr]["Name"] = *propertyPrettyName; |
| } |
| } |
| else if (interface == "xyz.openbmc_project.Inventory.Item.CpuCore") |
| { |
| const uint32_t* propertyMicrocode = nullptr; |
| dbus_utils::getPropertiesFromManagedObjects( |
| iter->second, corePath, interface, properties); |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "Microcode", |
| propertyMicrocode); |
| if (!success) |
| { |
| continue; |
| } |
| |
| if (propertyMicrocode != nullptr) |
| { |
| aResp->res |
| .jsonValue[jsonPtr]["ProcessorId"]["MicrocodeInfo"] = |
| "0x" + intToHexString(*propertyMicrocode, 8); |
| } |
| } |
| } |
| } |
| |
| if (!present) |
| { |
| aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent"; |
| } |
| |
| if (!functional) |
| { |
| aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "Critical"; |
| } |
| |
| // The "Metrics" stanza is deliberately omitted at the Core layer |
| } |
| |
| inline void getSubProcessorCoreData( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& processorId, const std::string& coreId, |
| const boost::system::error_code ec, const std::string& cpuPath) |
| { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); |
| // No processor objects found by mapper |
| if (ec.value() == boost::system::errc::io_error) |
| { |
| messages::resourceNotFound( |
| aResp->res, "#Processor.v1_11_0.Processor", processorId); |
| return; |
| } |
| |
| messages::internalError(aResp->res); |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); |
| |
| std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; |
| managedStore::ManagedObjectStoreContext context(aResp); |
| managedStore::GetManagedObjectStore()->getSubTree( |
| cpuPath, 0, interfaces, context, |
| [aResp{aResp}, processorId, coreId, |
| context](const boost::system::error_code& ec2, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec2) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec2: " << ec2.value(); |
| // No processor objects found by mapper |
| if (ec2.value() == boost::system::errc::io_error) |
| { |
| messages::resourceNotFound( |
| aResp->res, "#Processor.v1_11_0.Processor", processorId); |
| return; |
| } |
| |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| std::vector<std::string> serviceNames = |
| dbus_utils::getServiceNamesFromSubtree(subtree); |
| |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); |
| dbus_utils::getManagedObjectsInEachService( |
| std::move(serviceNames), path, context, |
| [aResp{aResp}, subtree, processorId, |
| coreId](const ManagedObjectByServiceMap& managedObjectsByService) { |
| if (managedObjectsByService.empty()) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| if (sdbusplus::message::object_path(objectPath).filename() != |
| coreId) |
| { |
| continue; |
| } |
| getCpuCoreData(aResp, ""_json_pointer, 0, processorId, coreId, |
| objectPath, subtree, managedObjectsByService); |
| return; |
| } |
| |
| // Object not found |
| messages::resourceNotFound(aResp->res, |
| "#Processor.v1_11_0.Processor", coreId); |
| }); |
| }); |
| } |
| |
| inline void |
| getSubProcessorCoreMembers(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& processorId, |
| const boost::system::error_code ec, |
| const std::string& cpuPath) |
| { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error, ec: " << ec.value(); |
| // No processor objects found by mapper |
| if (ec.value() == boost::system::errc::io_error) |
| { |
| messages::resourceNotFound( |
| aResp->res, "#Processor.v1_11_0.Processor", processorId); |
| return; |
| } |
| |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); |
| aResp->res.jsonValue["@odata.type"] = |
| "#ProcessorCollection.ProcessorCollection"; |
| aResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors"); |
| aResp->res.jsonValue["Name"] = "SubProcessor Collection"; |
| |
| const boost::urls::url& subProcessorsPath = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors"); |
| |
| constexpr std::array<std::string_view, 1> interfaces{ |
| "xyz.openbmc_project.Inventory.Item.CpuCore"}; |
| |
| collection_util::getCollectionMembers(aResp, subProcessorsPath, interfaces, |
| cpuPath.c_str()); |
| } |
| |
| inline void getSubProcessorThreadCollectionWithExpand( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, |
| const std::string& processorId, const std::string& coreId, |
| const std::string& corePath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap) |
| { |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor Collection"; |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#ProcessorCollection.ProcessorCollection"; |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors", coreId, "SubProcessors"); |
| |
| // Maps thread ID to threadObjectPath |
| std::map<std::string, std::string, AlphanumLess<std::string>> |
| threadIdToObjectPath; |
| |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| if (!objectPath.starts_with(corePath) || |
| !dbus_utils::findInterfacesInServiceMap( |
| serviceMap, subProcessorThreadInterfaces)) |
| { |
| continue; |
| } |
| sdbusplus::message::object_path threadPath(objectPath); |
| const std::string& threadId = threadPath.filename(); |
| threadIdToObjectPath.insert({threadId, threadPath}); |
| } |
| |
| BMCWEB_LOG_DEBUG << "Core path: " << corePath |
| << " thread count: " << threadIdToObjectPath.size(); |
| |
| if (threadIdToObjectPath.empty()) |
| { |
| BMCWEB_LOG_WARNING << "D-Bus CPU thread count was somehow empty"; |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = 0; |
| aResp->res.jsonValue[jsonPtr]["Members"] = nlohmann::json::array(); |
| return; |
| } |
| |
| size_t threadMemberCount = 0; |
| for (const auto& [threadId, _] : threadIdToObjectPath) |
| { |
| nlohmann::json::json_pointer threadMemberPtr = |
| jsonPtr / "Members" / threadMemberCount; |
| if (expandLevel > 0) |
| { |
| getCoreThreadData(aResp, threadMemberPtr, expandLevel - 1, |
| processorId, coreId, threadId, |
| threadIdToObjectPath[threadId], subtree, |
| managedObjectsMap); |
| } |
| else |
| { |
| aResp->res.jsonValue[threadMemberPtr]["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", |
| "system", "Processors", |
| processorId, "SubProcessors", |
| coreId, "SubProcessors", threadId); |
| } |
| threadMemberCount++; |
| } |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = threadMemberCount; |
| } |
| |
| inline void getSubProcessorCoreCollectionWithExpand( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const nlohmann::json::json_pointer& jsonPtr, uint8_t expandLevel, |
| const std::string& processorId, const std::string& cpuPath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap) |
| { |
| aResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#ProcessorCollection.ProcessorCollection"; |
| aResp->res.jsonValue[jsonPtr]["Name"] = "SubProcessor Collection"; |
| aResp->res.jsonValue[jsonPtr]["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", processorId, |
| "SubProcessors"); |
| |
| // Maps core ID to coreObjectPath |
| std::map<std::string, std::string, AlphanumLess<std::string>> |
| coreIdToObjectPath; |
| |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| if (!objectPath.starts_with(cpuPath) || |
| !dbus_utils::findInterfacesInServiceMap(serviceMap, |
| subProcessorCoreInterfaces)) |
| { |
| continue; |
| } |
| sdbusplus::message::object_path corePath(objectPath); |
| const std::string& coreId = corePath.filename(); |
| coreIdToObjectPath.insert({coreId, objectPath}); |
| } |
| |
| if (coreIdToObjectPath.empty()) |
| { |
| BMCWEB_LOG_WARNING << "D-Bus CPU core count was somehow empty"; |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = 0; |
| aResp->res.jsonValue[jsonPtr]["Members"] = nlohmann::json::array(); |
| return; |
| } |
| |
| size_t coreMemberCount = 0; |
| for (const auto& [coreId, _] : coreIdToObjectPath) |
| { |
| nlohmann::json::json_pointer coreMemberPtr = |
| jsonPtr / "Members" / coreMemberCount; |
| if (expandLevel > 0) |
| { |
| getCpuCoreData(aResp, coreMemberPtr, expandLevel - 1, processorId, |
| coreId, coreIdToObjectPath[coreId], subtree, |
| managedObjectsMap); |
| } |
| else |
| { |
| aResp->res.jsonValue[coreMemberPtr]["@odata.id"] = |
| crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", |
| processorId, "SubProcessors", coreId); |
| } |
| coreMemberCount++; |
| } |
| |
| aResp->res.jsonValue[jsonPtr]["Members@odata.count"] = coreMemberCount; |
| } |
| |
| inline void getProcessorCollectionWithExpand( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const ManagedObjectByServiceMap& managedObjectsMap, uint8_t expandLevel, |
| const std::string& systemPath) |
| { |
| BMCWEB_LOG_DEBUG << "Get processor collection with expand."; |
| |
| const bool multiHost = !systemPath.empty(); |
| |
| // Maps cpu ID to cpuObjectPath. |
| std::map<std::string, std::string, AlphanumLess<std::string>> |
| cpuNameToObjectPath; |
| |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| |
| if (!dbus_utils::findInterfacesInServiceMap(serviceMap, |
| processorInterfaces)) |
| { |
| continue; |
| } |
| sdbusplus::message::object_path cpuPath(objectPath); |
| |
| // For multi-host systems, the object path of the cpu must be a |
| // descendant of the systemPath. (e.g. if systemPath = /a/b, then |
| // objectPath must be some pattern like /a/b/..) |
| if (multiHost && !objectPath.starts_with(systemPath)) |
| { |
| continue; |
| } |
| |
| const std::string& cpuId = cpuPath.filename(); |
| cpuNameToObjectPath.insert({cpuId, cpuPath}); |
| } |
| |
| // If no cpus found, return |
| if (cpuNameToObjectPath.empty()) |
| { |
| BMCWEB_LOG_WARNING << "D-Bus CPU count was somehow empty"; |
| aResp->res.jsonValue["Members@odata.count"] = 0; |
| aResp->res.jsonValue["Members"] = nlohmann::json::array(); |
| return; |
| } |
| |
| size_t cpuMemberCount = 0; |
| for (const auto& [cpu, _] : cpuNameToObjectPath) |
| { |
| nlohmann::json::json_pointer cpuMemberPtr = |
| "/Members"_json_pointer / cpuMemberCount; |
| getProcessorData(aResp, cpuMemberPtr, expandLevel - 1, cpu, systemPath, |
| cpuNameToObjectPath[cpu], subtree, managedObjectsMap); |
| cpuMemberCount++; |
| } |
| |
| aResp->res.jsonValue["Members@odata.count"] = cpuMemberCount; |
| aResp->res.jsonValue["Name"] = "Processor Collection"; |
| } |
| |
| /** |
| * Request all the properties for the given D-Bus object and fill out the |
| * related entries in the Redfish OperatingConfig response. |
| * |
| * @param[in,out] aResp Async HTTP response. |
| * @param[in] service D-Bus service name to query. |
| * @param[in] objPath D-Bus object to query. |
| */ |
| inline void |
| getOperatingConfigData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& service, |
| const std::string& objPath) |
| { |
| managedStore::ManagedObjectStoreContext context(aResp); |
| managedStore::GetManagedObjectStore()->getAllProperties( |
| service, objPath, |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig", context, |
| [aResp](const boost::system::error_code& ec, |
| const dbus::utility::DBusPropertiesMap& properties) { |
| if (ec) |
| { |
| BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " << ec.message(); |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| const size_t* availableCoreCount = nullptr; |
| const uint32_t* baseSpeed = nullptr; |
| const uint32_t* maxJunctionTemperature = nullptr; |
| const uint32_t* maxSpeed = nullptr; |
| const uint32_t* powerLimit = nullptr; |
| const TurboProfileProperty* turboProfile = nullptr; |
| const BaseSpeedPrioritySettingsProperty* baseSpeedPrioritySettings = |
| nullptr; |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "AvailableCoreCount", |
| availableCoreCount, "BaseSpeed", baseSpeed, |
| "MaxJunctionTemperature", maxJunctionTemperature, "MaxSpeed", |
| maxSpeed, "PowerLimit", powerLimit, "TurboProfile", turboProfile, |
| "BaseSpeedPrioritySettings", baseSpeedPrioritySettings); |
| |
| if (!success) |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| nlohmann::json& json = aResp->res.jsonValue; |
| |
| if (availableCoreCount != nullptr) |
| { |
| json["TotalAvailableCoreCount"] = *availableCoreCount; |
| } |
| |
| if (baseSpeed != nullptr) |
| { |
| json["BaseSpeedMHz"] = *baseSpeed; |
| } |
| |
| if (maxJunctionTemperature != nullptr) |
| { |
| json["MaxJunctionTemperatureCelsius"] = *maxJunctionTemperature; |
| } |
| |
| if (maxSpeed != nullptr) |
| { |
| json["MaxSpeedMHz"] = *maxSpeed; |
| } |
| |
| if (powerLimit != nullptr) |
| { |
| json["TDPWatts"] = *powerLimit; |
| } |
| |
| if (turboProfile != nullptr) |
| { |
| nlohmann::json& turboArray = json["TurboProfile"]; |
| turboArray = nlohmann::json::array(); |
| for (const auto& [turboSpeed, coreCount] : *turboProfile) |
| { |
| nlohmann::json::object_t turbo; |
| turbo["ActiveCoreCount"] = coreCount; |
| turbo["MaxSpeedMHz"] = turboSpeed; |
| turboArray.emplace_back(std::move(turbo)); |
| } |
| } |
| |
| if (baseSpeedPrioritySettings != nullptr) |
| { |
| nlohmann::json& baseSpeedArray = json["BaseSpeedPrioritySettings"]; |
| baseSpeedArray = nlohmann::json::array(); |
| for (const auto& [baseSpeedMhz, coreList] : |
| *baseSpeedPrioritySettings) |
| { |
| nlohmann::json::object_t speed; |
| speed["CoreCount"] = coreList.size(); |
| speed["CoreIDs"] = coreList; |
| speed["BaseSpeedMHz"] = baseSpeedMhz; |
| baseSpeedArray.emplace_back(std::move(speed)); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Handle the D-Bus response from attempting to set the CPU's AppliedConfig |
| * property. Main task is to translate error messages into Redfish errors. |
| * |
| * @param[in,out] resp HTTP response. |
| * @param[in] setPropVal Value which we attempted to set. |
| * @param[in] ec D-Bus response error code. |
| * @param[in] msg D-Bus response message. |
| */ |
| inline void |
| handleAppliedConfigResponse(const std::shared_ptr<bmcweb::AsyncResp>& resp, |
| const std::string& setPropVal, |
| const boost::system::error_code& ec, |
| const sdbusplus::message_t& msg) |
| { |
| if (!ec) |
| { |
| BMCWEB_LOG_DEBUG << "Set Property succeeded"; |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG << "Set Property failed: " << ec; |
| |
| const sd_bus_error* dbusError = msg.get_error(); |
| if (dbusError == nullptr) |
| { |
| messages::internalError(resp->res); |
| return; |
| } |
| |
| // The asio error code doesn't know about our custom errors, so we have to |
| // parse the error string. Some of these D-Bus -> Redfish translations are a |
| // stretch, but it's good to try to communicate something vaguely useful. |
| if (strcmp(dbusError->name, |
| "xyz.openbmc_project.Common.Error.InvalidArgument") == 0) |
| { |
| // Service did not like the object_path we tried to set. |
| messages::propertyValueIncorrect( |
| resp->res, "AppliedOperatingConfig/@odata.id", setPropVal); |
| } |
| else if (strcmp(dbusError->name, |
| "xyz.openbmc_project.Common.Error.NotAllowed") == 0) |
| { |
| // Service indicates we can never change the config for this processor. |
| messages::propertyNotWritable(resp->res, "AppliedOperatingConfig"); |
| } |
| else if (strcmp(dbusError->name, |
| "xyz.openbmc_project.Common.Error.Unavailable") == 0) |
| { |
| // Service indicates the config cannot be changed right now, but maybe |
| // in a different system state. |
| messages::resourceInStandby(resp->res); |
| } |
| else |
| { |
| messages::internalError(resp->res); |
| } |
| } |
| |
| /** |
| * Handle the PATCH operation of the AppliedOperatingConfig property. Do basic |
| * validation of the input data, and then set the D-Bus property. |
| * |
| * @param[in,out] resp Async HTTP response. |
| * @param[in] processorId Processor's Id. |
| * @param[in] appliedConfigUri New property value to apply. |
| * @param[in] cpuObjectPath Path of CPU object to modify. |
| * @param[in] serviceMap Service map for CPU object. |
| */ |
| inline void patchAppliedOperatingConfig( |
| const std::shared_ptr<bmcweb::AsyncResp>& resp, |
| const std::string& processorId, const std::string& appliedConfigUri, |
| const std::string& cpuObjectPath, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| [[maybe_unused]] const ManagedObjectByServiceMap& managedObjectsMap) |
| { |
| // Get Service map for the object path. |
| dbus::utility::MapperServiceMap serviceMap; |
| for (const auto& [parsedObjectPath, parsedServiceMap] : subtree) |
| { |
| if (cpuObjectPath == parsedObjectPath) |
| { |
| serviceMap = parsedServiceMap; |
| break; |
| } |
| } |
| |
| // Check that the property even exists by checking for the interface |
| const std::string* controlService = nullptr; |
| for (const auto& [serviceName, interfaceList] : serviceMap) |
| { |
| if (std::find(interfaceList.begin(), interfaceList.end(), |
| "xyz.openbmc_project.Control.Processor." |
| "CurrentOperatingConfig") != interfaceList.end()) |
| { |
| controlService = &serviceName; |
| break; |
| } |
| } |
| |
| if (controlService == nullptr) |
| { |
| messages::internalError(resp->res); |
| return; |
| } |
| |
| // Check that the config URI is a child of the cpu URI being patched. |
| std::string expectedPrefix("/redfish/v1/Systems/system/Processors/"); |
| expectedPrefix += processorId; |
| expectedPrefix += "/OperatingConfigs/"; |
| if (!appliedConfigUri.starts_with(expectedPrefix) || |
| expectedPrefix.size() == appliedConfigUri.size()) |
| { |
| messages::propertyValueIncorrect( |
| resp->res, "AppliedOperatingConfig/@odata.id", appliedConfigUri); |
| return; |
| } |
| |
| // Generate the D-Bus path of the OperatingConfig object, by assuming it's a |
| // 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()); |
| sdbusplus::message::object_path configPath(cpuObjectPath); |
| configPath /= configBaseName; |
| |
| BMCWEB_LOG_INFO << "Setting config to " << configPath.str; |
| |
| // Set the property, with handler to check error responses |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| resp->strand_, |
| [resp, appliedConfigUri](const boost::system::error_code& ec, |
| const sdbusplus::message_t& msg) { |
| handleAppliedConfigResponse(resp, appliedConfigUri, ec, msg); |
| }, |
| *controlService, cpuObjectPath, "org.freedesktop.DBus.Properties", |
| "Set", "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig", |
| "AppliedConfig", dbus::utility::DbusVariantType(std::move(configPath))); |
| } |
| |
| inline void handleSubProcessorThreadHead( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& /* systemName */, const std::string& /* processorId */, |
| const std::string& /* coreId */, const std::string& /* threadId */) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) |
| { |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); |
| } |
| |
| inline void handleSubProcessorThreadCollectionHead( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& /* systemName */, const std::string& /* processorId */, |
| const std::string& /* coreId */) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) |
| { |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); |
| } |
| |
| inline void |
| handleSubProcessorCoreHead(crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& /* systemName */, |
| const std::string& /* processorId */, |
| const std::string& /* coreId */) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) |
| { |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); |
| } |
| |
| inline void handleSubProcessorCoreCollectionHead( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& /* systemName */, const std::string& /* processorId */) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) |
| { |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); |
| } |
| |
| inline void handleProcessorHead(crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& /* systemName */, |
| const std::string& /* processorId */) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) |
| { |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/Processor/Processor.json>; rel=describedby"); |
| } |
| |
| inline void handleProcessorCollectionHead( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& /* systemName */) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, aResp)) |
| { |
| return; |
| } |
| aResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); |
| } |
| |
| inline void parseSubtreeForProcessorCollection( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree, |
| const query_param::Query& delegated, const std::string& systemPath) |
| { |
| const bool multiHost = !systemPath.empty(); |
| |
| // Default systemName is system and change if system is multi-host |
| std::string systemName = "system"; |
| |
| if (multiHost) |
| { |
| systemName = sdbusplus::message::object_path(systemPath).filename(); |
| } |
| |
| if (delegated.expandLevel == 0 || |
| delegated.expandType == query_param::ExpandType::None) |
| { |
| BMCWEB_LOG_DEBUG << "Use default processor expand handler"; |
| collection_util::getCollectionMembersFromSubtree( |
| asyncResp, |
| boost::urls::url("/redfish/v1/Systems/" + systemName + |
| "/Processors"), |
| processorInterfaces, subtree); |
| return; |
| } |
| BMCWEB_LOG_DEBUG << "Use efficient processor expand handler"; |
| |
| std::vector<std::string> serviceNames = |
| dbus_utils::getServiceNamesFromSubtree(subtree); |
| if (serviceNames.empty()) |
| { |
| BMCWEB_LOG_DEBUG << "Can't retrieve service names from subtree"; |
| asyncResp->res.jsonValue["Members@odata.count"] = 0; |
| asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); |
| return; |
| } |
| |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| // Get managed objects from each service in subtree to proactively |
| // fetch all dbus data needed to serve requests for subordinate |
| // resources. |
| sdbusplus::message::object_path path("/xyz/openbmc_project/inventory"); |
| dbus_utils::getManagedObjectsInEachService( |
| std::move(serviceNames), path, context, |
| [asyncResp{asyncResp}, subtree, delegated, |
| systemPath](const ManagedObjectByServiceMap& managedObjectsByService) { |
| if (managedObjectsByService.empty()) |
| { |
| BMCWEB_LOG_DEBUG << "Cannot get managed objects."; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| getProcessorCollectionWithExpand(asyncResp, subtree, |
| managedObjectsByService, |
| delegated.expandLevel, systemPath); |
| }); |
| } |
| |
| inline void handleProcessorCollection( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const query_param::Query& delegated, const std::string& systemPath) |
| { |
| const bool multiHost = !systemPath.empty(); |
| |
| // Default systemName is system and change if system is multi-host |
| std::string systemName = "system"; |
| // The subtree to query for Dimms changes if single or multi-host |
| std::string subtreeRoot = "/xyz/openbmc_project/inventory"; |
| |
| if (multiHost) |
| { |
| systemName = sdbusplus::message::object_path(systemPath).filename(); |
| subtreeRoot = systemPath; |
| } |
| |
| asyncResp->res.addHeader( |
| boost::beast::http::field::link, |
| "</redfish/v1/JsonSchemas/ProcessorCollection/ProcessorCollection.json>; rel=describedby"); |
| |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#ProcessorCollection.ProcessorCollection"; |
| asyncResp->res.jsonValue["Name"] = "Processor Collection"; |
| |
| asyncResp->res.jsonValue["@odata.id"] = |
| "/redfish/v1/Systems/" + systemName + "/Processors"; |
| |
| // Get dbus interfaces based on expand level. |
| auto interfaces = getInterfacesForProcessorSubtree(delegated.expandLevel); |
| |
| if (interfaces.empty()) |
| { |
| BMCWEB_LOG_ERROR << "Can't get interface list for expand level " |
| << delegated.expandLevel; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| // Get subtree information to serve requests for both processor |
| // collection and, if expand is requested, for all requested subordinate |
| // resources. |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getSubTree( |
| subtreeRoot, 0, interfaces, requestContext, |
| [asyncResp{asyncResp}, delegated, |
| systemPath](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec == boost::system::errc::io_error) |
| { |
| asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); |
| asyncResp->res.jsonValue["Members@odata.count"] = 0; |
| return; |
| } |
| |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec.value(); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| parseSubtreeForProcessorCollection(asyncResp, subtree, delegated, |
| systemPath); |
| }); |
| } |
| |
| inline void handleOperatingConfigCollectionGet( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& cpuName) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#OperatingConfigCollection.OperatingConfigCollection"; |
| asyncResp->res.jsonValue["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", "system", |
| "Processors", cpuName, "OperatingConfigs"); |
| asyncResp->res.jsonValue["Name"] = "Operating Config Collection"; |
| |
| // First find the matching CPU object so we know how to |
| // constrain our search for related Config objects. |
| const std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Control.Processor.CurrentOperatingConfig"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getSubTreePaths( |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, |
| [asyncResp, |
| cpuName](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreePathsResponse& objects) { |
| if (ec) |
| { |
| BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " << ec.message(); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| for (const std::string& object : objects) |
| { |
| if (!object.ends_with(cpuName)) |
| { |
| continue; |
| } |
| |
| // Not expected that there will be multiple matching |
| // CPU objects, but if there are just use the first |
| // one. |
| |
| // Use the common search routine to construct the |
| // Collection of all Config objects under this CPU. |
| constexpr std::array<std::string_view, 1> interface{ |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; |
| collection_util::getCollectionMembers( |
| asyncResp, |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", |
| "system", "Processors", cpuName, |
| "OperatingConfigs"), |
| interface, object.c_str()); |
| return; |
| } |
| }); |
| } |
| |
| inline void requestRoutesOperatingConfigCollection(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/") |
| .privileges(redfish::privileges::getOperatingConfigCollection) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleOperatingConfigCollectionGet, std::ref(app))); |
| } |
| |
| inline void handleOperatingConfigGet( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& cpuName, const std::string& configName) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| // Ask for all objects implementing OperatingConfig so we can search |
| // for one with a matching name |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Inventory.Item.Cpu.OperatingConfig"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getSubTree( |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, |
| [asyncResp, cpuName, |
| configName](const boost::system::error_code& ec, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_WARNING << "D-Bus error: " << ec << ", " << ec.message(); |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| const std::string expectedEnding = cpuName + '/' + configName; |
| for (const auto& [objectPath, serviceMap] : subtree) |
| { |
| // Ignore any configs without matching cpuX/configY |
| if (!objectPath.ends_with(expectedEnding) || serviceMap.empty()) |
| { |
| continue; |
| } |
| |
| nlohmann::json& json = asyncResp->res.jsonValue; |
| json["@odata.type"] = "#OperatingConfig.v1_0_0.OperatingConfig"; |
| json["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "system", "Processors", cpuName, |
| "OperatingConfigs", configName); |
| json["Name"] = "Processor Profile"; |
| json["Id"] = configName; |
| |
| // Just use the first implementation of the object - not |
| // expected that there would be multiple matching |
| // services |
| getOperatingConfigData(asyncResp, serviceMap.begin()->first, |
| objectPath); |
| return; |
| } |
| messages::resourceNotFound(asyncResp->res, "OperatingConfig", |
| configName); |
| }); |
| } |
| |
| inline void requestRoutesOperatingConfig(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/system/Processors/<str>/OperatingConfigs/<str>/") |
| .privileges(redfish::privileges::getOperatingConfig) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleOperatingConfigGet, std::ref(app))); |
| } |
| |
| inline int countProcessors() |
| { |
| // TODO(): This is a STUB |
| // For now, this is OK, since it is only used for syntax validation. |
| // However, when and if writing (POST, PATCH, etc.) is allowed, |
| // we will need to have better bounds-checking here, |
| // otherwise it will be trivial for a writer to DoS all the storage. |
| return 16; |
| } |
| |
| inline int countCores(int cpuIndex) |
| { |
| // TODO(): This is a STUB, see above comment |
| (void)cpuIndex; |
| return 256; |
| } |
| |
| inline int countThreads(int cpuIndex, int coreIndex) |
| { |
| // TODO(): This is a STUB, see above comment |
| (void)cpuIndex; |
| (void)coreIndex; |
| return 16; |
| } |
| |
| inline int prefixStrToInt(const std::string& prefix, const std::string& str) |
| { |
| size_t lenPrefix = prefix.size(); |
| size_t lenStr = str.size(); |
| |
| // Content following prefix must be within 1 to 9 characters in length |
| if (lenStr < (lenPrefix + 1)) |
| { |
| BMCWEB_LOG_ERROR << "Index number too short"; |
| return -1; |
| } |
| if (lenStr > (lenPrefix + 9)) |
| { |
| // This length limit prevents 32-bit rollover attacks |
| BMCWEB_LOG_ERROR << "Index number too long"; |
| return -1; |
| } |
| |
| if (str.substr(0, lenPrefix) != prefix) |
| { |
| // Prefix does not match |
| BMCWEB_LOG_ERROR << "Index number wrong prefix"; |
| return -1; |
| } |
| |
| std::string numText = str.substr(lenPrefix); |
| size_t lenText = numText.size(); |
| size_t lenCheck = std::strlen(numText.c_str()); |
| if (lenText != lenCheck) |
| { |
| // This comparison prevents 0x00 string truncation attacks |
| BMCWEB_LOG_ERROR << "Index number wrong length"; |
| return -1; |
| } |
| |
| // Cannot use std::stoi because stoi throws |
| char* endptr = nullptr; |
| int64_t value = std::strtol(numText.c_str(), &endptr, 10); |
| if (*endptr != '\0') |
| { |
| // The number was followed by extra content |
| BMCWEB_LOG_ERROR << "Index number wrong content"; |
| return -1; |
| } |
| |
| int result = static_cast<int>(value); |
| return result; |
| } |
| |
| inline int extractIndexFromArg(const std::string& arg) |
| { |
| const std::string index = |
| arg.substr(arg.find_last_not_of("0123456789") + 1); |
| if (index.empty()) |
| { |
| BMCWEB_LOG_ERROR << "Unable to extract index from arg"; |
| return -1; |
| } |
| // use std::strtol since std::stoi can throw |
| char* enptr = nullptr; |
| int64_t value = std::strtol(index.c_str(), &enptr, 10); |
| return static_cast<int>(value); |
| } |
| |
| inline int cpuArgToIndex(const std::string& cpuArg) |
| { |
| int bound = countProcessors(); |
| int num = extractIndexFromArg(cpuArg); |
| if (num >= bound) |
| { |
| BMCWEB_LOG_ERROR << "Processor index out of range"; |
| return -1; |
| } |
| return num; |
| } |
| |
| inline int coreArgToIndex(const std::string& coreArg, int cpuIndex) |
| { |
| |
| int bound = countCores(cpuIndex); |
| int num = prefixStrToInt("core", coreArg); |
| if (num >= bound) |
| { |
| BMCWEB_LOG_ERROR << "Subprocessor core index out of range"; |
| return -1; |
| } |
| return num; |
| } |
| |
| inline int threadArgToIndex(const std::string& threadArg, int cpuIndex, |
| int coreIndex) |
| { |
| |
| int bound = countThreads(cpuIndex, coreIndex); |
| int num = prefixStrToInt("thread", threadArg); |
| if (num >= bound) |
| { |
| BMCWEB_LOG_ERROR << "Subprocessor thread index out of range"; |
| return -1; |
| } |
| return num; |
| } |
| |
| inline int validateCpu(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& cpuArg) |
| { |
| int cpuIndex = cpuArgToIndex(cpuArg); |
| if (cpuIndex < 0) |
| { |
| messages::resourceNotFound(asyncResp->res, "Processor", cpuArg); |
| } |
| return cpuIndex; |
| } |
| |
| inline int validateCpuCore(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& cpuArg, |
| const std::string& coreArg) |
| { |
| int cpuIndex = validateCpu(asyncResp, cpuArg); |
| if (cpuIndex < 0) |
| { |
| return cpuIndex; |
| } |
| int coreIndex = coreArgToIndex(coreArg, cpuIndex); |
| if (coreIndex < 0) |
| { |
| messages::resourceNotFound(asyncResp->res, "Core", coreArg); |
| } |
| return coreIndex; |
| } |
| |
| inline int |
| validateCpuCoreThread(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& cpuArg, const std::string& coreArg, |
| const std::string& threadArg) |
| { |
| int cpuIndex = validateCpu(asyncResp, cpuArg); |
| if (cpuIndex < 0) |
| { |
| return cpuIndex; |
| } |
| int coreIndex = validateCpuCore(asyncResp, cpuArg, coreArg); |
| if (coreIndex < 0) |
| { |
| return coreIndex; |
| } |
| int threadIndex = threadArgToIndex(threadArg, cpuIndex, coreIndex); |
| if (threadIndex < 0) |
| { |
| messages::resourceNotFound(asyncResp->res, "Thread", threadArg); |
| } |
| return threadIndex; |
| } |
| |
| // Creates a generic ExternalStorer hook, should be 2 directories above |
| // Each hook contains instances, each instance contains entries |
| // Omit the leading "/redfish/v1" from pathBase, and no trailing slash |
| inline external_storer::Hook makeExternalStorerHook(const std::string& pathBase) |
| { |
| const std::string emptyString; |
| const std::vector<std::string> emptyList; |
| |
| return {pathBase, emptyString, emptyList, emptyList}; |
| } |
| |
| // Creates a generic ExternalStorer instance within the given hook |
| // The content will be minimal, just enough to create filename on disk |
| // Returns true if successful |
| inline bool makeExternalStorerInstance( |
| const std::shared_ptr<external_storer::Hook>& hook, const std::string& id) |
| { |
| // External storer NEVER sends dbus calls, so you will never leave this thread. |
| auto respGet = std::make_shared<bmcweb::AsyncResp>(nullptr); |
| |
| hook->handleGetInstance(respGet, id); |
| if (respGet->res.result() == boost::beast::http::status::ok) |
| { |
| // Already exists, nothing more needs to be done |
| return true; |
| } |
| |
| boost::beast::http::request<boost::beast::http::string_body> upBody; |
| std::error_code ec; |
| |
| // Create instance, with given ID, which will become filename on disk |
| auto upJson = nlohmann::json::object(); |
| upJson["Id"] = id; |
| |
| // Must supply 4th argument to avoid throwing exceptions |
| upBody.body() = |
| upJson.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace); |
| crow::Request reqCreate{upBody, ec}; |
| |
| // External storer NEVER sends dbus calls, so you will never leave this thread. |
| auto respCreate = std::make_shared<bmcweb::AsyncResp>(nullptr); |
| |
| hook->handleCreateInstance(reqCreate, respCreate); |
| if (respCreate->res.result() != boost::beast::http::status::created) |
| { |
| BMCWEB_LOG_ERROR << "Problem creating ExternalStorer instance " << id; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Remembers toplevel Processors hook, there is only one |
| // Each instance in it represents a CPU and is named like cpuArg |
| // Each instance contains only one entry, "ProcessorMetrics" |
| inline std::shared_ptr<external_storer::Hook> |
| rememberProcessorHook(const std::string& cpuArg) |
| { |
| static std::shared_ptr<external_storer::Hook> hookProcessor = nullptr; |
| |
| if (!hookProcessor) |
| { |
| // If not already remembered by static variable, create hook |
| hookProcessor = std::make_shared<external_storer::Hook>( |
| makeExternalStorerHook("Systems/system/Processors")); |
| } |
| |
| if (!(makeExternalStorerInstance(hookProcessor, cpuArg))) |
| { |
| BMCWEB_LOG_ERROR << "Problem remembering hook for " << cpuArg; |
| return nullptr; |
| } |
| |
| return hookProcessor; |
| } |
| |
| // Remembers lowest-level SubProcessors hook |
| // There is one hook for each unique (CPU, thread) tuple |
| // Each instance in it represents a thread and is named like threadArg |
| // Each instance contains only one entry, "ProcessorMetrics" |
| inline std::shared_ptr<external_storer::Hook> |
| rememberThreadHook(const std::string& cpuArg, const std::string& coreArg, |
| const std::string& threadArg) |
| { |
| static std::map< |
| std::string, |
| std::map<std::string, std::shared_ptr<external_storer::Hook>>> |
| hookMaps; |
| |
| auto findCpu = hookMaps.find(cpuArg); |
| if (findCpu == hookMaps.end()) |
| { |
| hookMaps[cpuArg] = {}; |
| findCpu = hookMaps.find(cpuArg); |
| } |
| |
| auto findCore = findCpu->second.find(coreArg); |
| if (findCore == findCpu->second.end()) |
| { |
| (findCpu->second)[coreArg] = {}; |
| findCore = findCpu->second.find(coreArg); |
| } |
| |
| std::shared_ptr<external_storer::Hook> hookThread = findCore->second; |
| |
| if (!hookThread) |
| { |
| std::string path = "Systems/system/Processors/" + cpuArg + |
| "/SubProcessors/" + coreArg + "/SubProcessors"; |
| hookThread = std::make_shared<external_storer::Hook>( |
| makeExternalStorerHook(path)); |
| findCore->second = hookThread; |
| } |
| |
| if (!(makeExternalStorerInstance(hookThread, threadArg))) |
| { |
| BMCWEB_LOG_ERROR << "Problem remembering hook for " << threadArg; |
| return nullptr; |
| } |
| |
| return hookThread; |
| } |
| |
| inline nlohmann::json fakeProcessorMetrics(const std::string& cpu, |
| const std::string& core, |
| const std::string& thread) |
| { |
| nlohmann::json::object_t metricsObj; |
| |
| metricsObj["Id"] = "Metrics"; |
| metricsObj["Name"] = "Processor Metrics"; |
| |
| // The different levels have slightly different default content |
| if (!thread.empty()) |
| { |
| // Thread level |
| metricsObj["CorrectableCoreErrorCount"] = 0; |
| metricsObj["UncorrectableCoreErrorCount"] = 0; |
| } |
| metricsObj["CorrectableOtherErrorCount"] = 0; |
| metricsObj["UncorrectableOtherErrorCount"] = 0; |
| |
| // ExternalStorer would normally apply this as invariant if successful |
| // The caller already applies "@odata.type" as invariant no matter what |
| std::string url = "/redfish/v1/Systems/system/Processors/"; |
| url += cpu; |
| if (!core.empty()) |
| { |
| url += "/SubProcessors/"; |
| url += core; |
| } |
| if (!thread.empty()) |
| { |
| url += "/SubProcessors/"; |
| url += thread; |
| } |
| url += "/ProcessorMetrics"; |
| |
| metricsObj["@odata.id"] = std::move(url); |
| |
| return metricsObj; |
| } |
| |
| inline void |
| handleGetCpuMetrics(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& cpuArg) |
| { |
| if (validateCpu(asyncResp, cpuArg) < 0) |
| { |
| // Error reply already composed |
| return; |
| } |
| |
| std::shared_ptr<external_storer::Hook> hook = rememberProcessorHook(cpuArg); |
| if (!hook) |
| { |
| messages::resourceNotFound(asyncResp->res, "Processor", cpuArg); |
| return; |
| } |
| |
| std::string emptyString; |
| hook->handleGetEntry(asyncResp, jsonPtr, cpuArg, emptyString, |
| "ProcessorMetrics"); |
| if (asyncResp->res.result() == boost::beast::http::status::not_found) |
| { |
| // Fake up a synthetic response to replace only the 404 error |
| asyncResp->res.jsonValue[jsonPtr] = |
| fakeProcessorMetrics(cpuArg, "", ""); |
| asyncResp->res.result(boost::beast::http::status::ok); |
| } |
| if (asyncResp->res.result() != boost::beast::http::status::ok) |
| { |
| // Error reply already composed |
| return; |
| } |
| |
| // Apply invariants, to override whatever might have been in the file |
| asyncResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#ProcessorMetrics.v1_5_0.ProcessorMetrics"; |
| } |
| |
| inline void handleGetCpuCoreThreadMetrics( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& cpuArg, |
| const std::string& coreArg, const std::string& threadArg) |
| { |
| if (validateCpuCoreThread(asyncResp, cpuArg, coreArg, threadArg) < 0) |
| { |
| return; |
| } |
| |
| std::shared_ptr<external_storer::Hook> hook = |
| rememberThreadHook(cpuArg, coreArg, threadArg); |
| if (!hook) |
| { |
| messages::resourceNotFound(asyncResp->res, "Thread", threadArg); |
| return; |
| } |
| |
| std::string emptyString; |
| hook->handleGetEntry(asyncResp, jsonPtr, threadArg, emptyString, |
| "ProcessorMetrics"); |
| if (asyncResp->res.result() == boost::beast::http::status::not_found) |
| { |
| // Fake up a synthetic response to replace only the 404 error |
| asyncResp->res.jsonValue[jsonPtr] = |
| fakeProcessorMetrics(cpuArg, coreArg, threadArg); |
| asyncResp->res.result(boost::beast::http::status::ok); |
| } |
| if (asyncResp->res.result() != boost::beast::http::status::ok) |
| { |
| // Error reply already composed |
| return; |
| } |
| |
| // Apply invariants, to override whatever might have been in the file |
| asyncResp->res.jsonValue[jsonPtr]["@odata.type"] = |
| "#ProcessorMetrics.v1_5_0.ProcessorMetrics"; |
| } |
| |
| // The glue layers are already handled by these functions elsewhere: |
| // Processors = requestRoutesProcessorCollection() |
| // Processors/cpuX = requestRoutesProcessor() |
| // Processors/cpuX/SubProcessors = requestRoutesSubProcessorCoreCollection() |
| // Processors/cpuX/SubProcessors/coreY = requestRoutesSubProcessorCore() |
| // Processors/cpuX/SubProcessors/coreY/SubProcessors = |
| // requestRoutesSubProcessorThreadCollection() |
| // Processors/cpuX/SubProcessors/coreY/SubProcessors/threadZ = |
| // requestRoutesSubProcessorThread() |
| |
| // The "ProcessorMetrics" endpoints are below those glue layers: |
| // Processors/cpuX/ProcessorMetrics = handled here |
| // Processors/cpuX/SubProcessors/coreY/ProcessorMetrics = intentionally |
| // omitted |
| // Processors/cpuX/SubProcessors/coreY/SubProcessors/threadZ/ProcessorMetrics |
| // = handled here |
| inline void requestRoutesProcessorMetricsViaExternalStorer(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, "/redfish/v1/Systems/system/Processors/<str>/ProcessorMetrics/") |
| .privileges(redfish::privileges::getProcessor) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& cpuArg) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| handleGetCpuMetrics(asyncResp, ""_json_pointer, cpuArg); |
| }); |
| |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/system/Processors/<str>/SubProcessors/<str>/SubProcessors/<str>/ProcessorMetrics/") |
| .privileges(redfish::privileges::getProcessor) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& cpuArg, const std::string& coreArg, |
| const std::string& threadArg) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| handleGetCpuCoreThreadMetrics(asyncResp, ""_json_pointer, cpuArg, |
| coreArg, threadArg); |
| }); |
| |
| // TODO(): Implement corresponding PATCH methods |
| // The POST method is not correct to use here, |
| // as the result location is already well-known name "ProcessorMetrics". |
| // and from the user point of view it always exists, |
| // so the creation operation is not the correct operation. |
| // The PUT method also might not be correct to use here, |
| // as it would fully replace all content at the result location, |
| // but the expectation is to only selectively replace some content. |
| } |
| |
| inline void requestRoutesProcessorCollection(App& app) |
| { |
| /** |
| * Functions triggers appropriate requests on DBus |
| */ |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") |
| .privileges(redfish::privileges::headProcessorCollection) |
| .methods(boost::beast::http::verb::head)( |
| std::bind_front(handleProcessorCollectionHead, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/") |
| .privileges(redfish::privileges::getProcessorCollection) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName) { |
| query_param::Query delegated; |
| query_param::QueryCapabilities capabilities = { |
| #ifdef EFFICIENT_EXPAND_PROCESSORS_ENABLED |
| .canDelegateExpandLevel = 6, |
| #else |
| .canDelegateExpandLevel = 0, |
| #endif |
| }; |
| if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp, |
| delegated, capabilities)) |
| { |
| return; |
| } |
| |
| redfish::system_utils::getSystemInformation( |
| asyncResp, systemName, |
| [delegated](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemPath) { |
| handleProcessorCollection(asyncResp, delegated, systemPath); |
| }); |
| }); |
| } |
| |
| inline void getProcessorStateCollection( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, const std::string& processorId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| constexpr std::array<std::string_view, 1> procStateIntf = { |
| "xyz.openbmc_project.Inventory.Item.ProcessorState"}; |
| collection_util::getCollectionMembers( |
| asyncResp, |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName, |
| "Processors", processorId, "Oem", "Google", |
| "ProcessorStates"), |
| procStateIntf); |
| } |
| |
| inline void |
| handleProcessorGet(App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, |
| const std::string& processorId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| |
| redfish::system_utils::getSystemInformation( |
| asyncResp, systemName, |
| [processorId](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemPath) { |
| getProcessorObject(asyncResp, processorId, systemPath, |
| std::bind_front(getProcessorData, asyncResp, |
| ""_json_pointer, 0, processorId, |
| systemPath)); |
| }); |
| } |
| |
| inline void |
| handleProcessorPatch(App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, |
| const std::string& processorId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (systemName != "system") |
| { |
| messages::resourceNotFound(asyncResp->res, "ComputerSystem", |
| systemName); |
| return; |
| } |
| |
| std::optional<nlohmann::json> appliedConfigJson; |
| if (!json_util::readJsonPatch(req, asyncResp->res, "AppliedOperatingConfig", |
| appliedConfigJson)) |
| { |
| return; |
| } |
| |
| if (appliedConfigJson) |
| { |
| std::string appliedConfigUri; |
| if (!json_util::readJson(*appliedConfigJson, asyncResp->res, |
| "@odata.id", appliedConfigUri)) |
| { |
| return; |
| } |
| |
| redfish::system_utils::getSystemInformation( |
| asyncResp, systemName, |
| [processorId, appliedConfigUri]( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemPath) { |
| // Check for 404 and find matching D-Bus object, then run |
| // property patch handlers if that all succeeds. |
| getProcessorObject(asyncResp, processorId, systemPath, |
| std::bind_front(patchAppliedOperatingConfig, |
| asyncResp, processorId, |
| appliedConfigUri)); |
| }); |
| } |
| } |
| |
| inline void requestRoutesProcessor(App& app) |
| { |
| /** |
| * Functions triggers appropriate requests on DBus |
| */ |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") |
| .privileges(redfish::privileges::headProcessor) |
| .methods(boost::beast::http::verb::head)( |
| std::bind_front(handleProcessorHead, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") |
| .privileges(redfish::privileges::getProcessor) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleProcessorGet, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Processors/<str>/") |
| .privileges(redfish::privileges::patchProcessor) |
| .methods(boost::beast::http::verb::patch)( |
| std::bind_front(handleProcessorPatch, std::ref(app))); |
| |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/Oem/Google/ProcessorStates/") |
| .privileges(redfish::privileges::getProcessor) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(getProcessorStateCollection, std::ref(app))); |
| |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/Oem/Google/ProcessorStates/<str>/") |
| .privileges(redfish::privileges::getProcessor) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleGetProcessorState, std::ref(app))); |
| } |
| |
| inline void requestRoutesSubProcessorCoreCollection(App& app) |
| { |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/") |
| .privileges(redfish::privileges::headProcessorCollection) |
| .methods(boost::beast::http::verb::head)(std::bind_front( |
| handleSubProcessorCoreCollectionHead, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/") |
| .privileges(redfish::privileges::getProcessorCollection) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, |
| const std::string& processorId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (systemName != "system") |
| { |
| messages::resourceNotFound(asyncResp->res, "ComputerSystem", |
| systemName); |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG << "Get available system sub processor core members."; |
| |
| getProcessorPaths(asyncResp, processorId, |
| std::bind_front(getSubProcessorCoreMembers, asyncResp, |
| processorId)); |
| }); |
| } |
| |
| inline void requestRoutesSubProcessorCore(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/") |
| .privileges(redfish::privileges::headProcessor) |
| .methods(boost::beast::http::verb::head)( |
| std::bind_front(handleSubProcessorCoreHead, std::ref(app))); |
| |
| BMCWEB_ROUTE( |
| app, "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/") |
| .privileges(redfish::privileges::getProcessor) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, |
| const std::string& processorId, const std::string& coreId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (systemName != "system") |
| { |
| messages::resourceNotFound(asyncResp->res, "ComputerSystem", |
| systemName); |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG |
| << "Get available system sub processor core resources."; |
| |
| getProcessorPaths(asyncResp, processorId, |
| std::bind_front(getSubProcessorCoreData, asyncResp, |
| processorId, coreId)); |
| }); |
| } |
| |
| inline void requestRoutesSubProcessorThreadCollection(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/SubProcessors/") |
| .privileges(redfish::privileges::headProcessorCollection) |
| .methods(boost::beast::http::verb::head)(std::bind_front( |
| handleSubProcessorThreadCollectionHead, std::ref(app))); |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/SubProcessors/") |
| .privileges(redfish::privileges::getProcessorCollection) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, |
| const std::string& processorId, const std::string& coreId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (systemName != "system") |
| { |
| messages::resourceNotFound(asyncResp->res, "ComputerSystem", |
| systemName); |
| return; |
| } |
| |
| getSubProcessorCorePaths(asyncResp, processorId, coreId, |
| std::bind_front(getSubProcessorThreadMembers, |
| asyncResp, processorId, |
| coreId)); |
| }); |
| } |
| |
| inline void requestRoutesSubProcessorThread(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/SubProcessors/<str>/") |
| .privileges(redfish::privileges::headProcessor) |
| .methods(boost::beast::http::verb::head)( |
| std::bind_front(handleSubProcessorThreadHead, std::ref(app))); |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Systems/<str>/Processors/<str>/SubProcessors/<str>/SubProcessors/<str>/") |
| .privileges(redfish::privileges::getProcessor) |
| .methods(boost::beast::http::verb::get)( |
| [&app](const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& systemName, |
| const std::string& processorId, const std::string& coreId, |
| const std::string& threadId) { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (systemName != "system") |
| { |
| messages::resourceNotFound(asyncResp->res, "ComputerSystem", |
| systemName); |
| return; |
| } |
| |
| getSubProcessorCorePaths(asyncResp, processorId, coreId, |
| std::bind_front(getSubProcessorThreadData, |
| asyncResp, processorId, coreId, |
| threadId)); |
| }); |
| } |
| |
| } // namespace redfish |