blob: ae906ada6b10fdb42ffd801e14176adefe00cb17 [file] [log] [blame]
/*
// 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