blob: d618096dfac9a95e629d4dc3732c3b8b946692fe [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 "health.hpp"
#include "http_utility.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 <boost/system/error_code.hpp>
#include <nlohmann/json.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <algorithm>
#include <array>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
namespace redfish
{
inline std::string translateMemoryTypeToRedfish(const std::string& memoryType)
{
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR")
{
return "DDR";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2")
{
return "DDR2";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR3")
{
return "DDR3";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4")
{
return "DDR4";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4E_SDRAM")
{
return "DDR4E_SDRAM";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR5")
{
return "DDR5";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR4_SDRAM")
{
return "LPDDR4_SDRAM";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR3_SDRAM")
{
return "LPDDR3_SDRAM";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM")
{
return "DDR2_SDRAM_FB_DIMM";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM_PROB")
{
return "DDR2_SDRAM_FB_DIMM_PROBE";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR_SGRAM")
{
return "DDR_SGRAM";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.ROM")
{
return "ROM";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.SDRAM")
{
return "SDRAM";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.EDO")
{
return "EDO";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.FastPageMode")
{
return "FastPageMode";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.PipelinedNibble")
{
return "PipelinedNibble";
}
if (memoryType ==
"xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.Logical")
{
return "Logical";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM")
{
return "HBM";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM2")
{
return "HBM2";
}
if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM3")
{
return "HBM3";
}
// This is values like Other or Unknown
// Also D-Bus values:
// DRAM
// EDRAM
// VRAM
// SRAM
// RAM
// FLASH
// EEPROM
// FEPROM
// EPROM
// CDRAM
// ThreeDRAM
// RDRAM
// FBD2
// LPDDR_SDRAM
// LPDDR2_SDRAM
// LPDDR5_SDRAM
return "";
}
inline void dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const char* key, const uint16_t* value,
const nlohmann::json::json_pointer& jsonPtr)
{
if (value == nullptr)
{
return;
}
aResp->res.jsonValue[jsonPtr][key] = "0x" + intToHexString(*value, 4);
}
inline void getPersistentMemoryProperties(
const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const dbus::utility::DBusPropertiesMap& properties,
const nlohmann::json::json_pointer& jsonPtr)
{
const uint16_t* moduleManufacturerID = nullptr;
const uint16_t* moduleProductID = nullptr;
const uint16_t* subsystemVendorID = nullptr;
const uint16_t* subsystemDeviceID = nullptr;
const uint64_t* volatileRegionSizeLimitInKiB = nullptr;
const uint64_t* pmRegionSizeLimitInKiB = nullptr;
const uint64_t* volatileSizeInKiB = nullptr;
const uint64_t* pmSizeInKiB = nullptr;
const uint64_t* cacheSizeInKB = nullptr;
const uint64_t* voltaileRegionMaxSizeInKib = nullptr;
const uint64_t* pmRegionMaxSizeInKiB = nullptr;
const uint64_t* allocationIncrementInKiB = nullptr;
const uint64_t* allocationAlignmentInKiB = nullptr;
const uint64_t* volatileRegionNumberLimit = nullptr;
const uint64_t* pmRegionNumberLimit = nullptr;
const uint64_t* spareDeviceCount = nullptr;
const bool* isSpareDeviceInUse = nullptr;
const bool* isRankSpareEnabled = nullptr;
const std::vector<uint32_t>* maxAveragePowerLimitmW = nullptr;
const bool* configurationLocked = nullptr;
const std::string* allowedMemoryModes = nullptr;
const std::string* memoryMedia = nullptr;
const bool* configurationLockCapable = nullptr;
const bool* dataLockCapable = nullptr;
const bool* passphraseCapable = nullptr;
const uint64_t* maxPassphraseCount = nullptr;
const uint64_t* passphraseLockLimit = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), properties, "ModuleManufacturerID",
moduleManufacturerID, "ModuleProductID", moduleProductID,
"SubsystemVendorID", subsystemVendorID, "SubsystemDeviceID",
subsystemDeviceID, "VolatileRegionSizeLimitInKiB",
volatileRegionSizeLimitInKiB, "PmRegionSizeLimitInKiB",
pmRegionSizeLimitInKiB, "VolatileSizeInKiB", volatileSizeInKiB,
"PmSizeInKiB", pmSizeInKiB, "CacheSizeInKB", cacheSizeInKB,
"VoltaileRegionMaxSizeInKib", voltaileRegionMaxSizeInKib,
"PmRegionMaxSizeInKiB", pmRegionMaxSizeInKiB,
"AllocationIncrementInKiB", allocationIncrementInKiB,
"AllocationAlignmentInKiB", allocationAlignmentInKiB,
"VolatileRegionNumberLimit", volatileRegionNumberLimit,
"PmRegionNumberLimit", pmRegionNumberLimit, "SpareDeviceCount",
spareDeviceCount, "IsSpareDeviceInUse", isSpareDeviceInUse,
"IsRankSpareEnabled", isRankSpareEnabled, "MaxAveragePowerLimitmW",
maxAveragePowerLimitmW, "ConfigurationLocked", configurationLocked,
"AllowedMemoryModes", allowedMemoryModes, "MemoryMedia", memoryMedia,
"ConfigurationLockCapable", configurationLockCapable, "DataLockCapable",
dataLockCapable, "PassphraseCapable", passphraseCapable,
"MaxPassphraseCount", maxPassphraseCount, "PassphraseLockLimit",
passphraseLockLimit);
if (!success)
{
messages::internalError(aResp->res);
return;
}
dimmPropToHex(aResp, "ModuleManufacturerID", moduleManufacturerID, jsonPtr);
dimmPropToHex(aResp, "ModuleProductID", moduleProductID, jsonPtr);
dimmPropToHex(aResp, "MemorySubsystemControllerManufacturerID",
subsystemVendorID, jsonPtr);
dimmPropToHex(aResp, "MemorySubsystemControllerProductID",
subsystemDeviceID, jsonPtr);
if (volatileRegionSizeLimitInKiB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["VolatileRegionSizeLimitMiB"] =
(*volatileRegionSizeLimitInKiB) >> 10;
}
if (pmRegionSizeLimitInKiB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["PersistentRegionSizeLimitMiB"] =
(*pmRegionSizeLimitInKiB) >> 10;
}
if (volatileSizeInKiB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["VolatileSizeMiB"] =
(*volatileSizeInKiB) >> 10;
}
if (pmSizeInKiB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["NonVolatileSizeMiB"] =
(*pmSizeInKiB) >> 10;
}
if (cacheSizeInKB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["CacheSizeMiB"] = (*cacheSizeInKB >> 10);
}
if (voltaileRegionMaxSizeInKib != nullptr)
{
aResp->res.jsonValue[jsonPtr]["VolatileRegionSizeMaxMiB"] =
(*voltaileRegionMaxSizeInKib) >> 10;
}
if (pmRegionMaxSizeInKiB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["PersistentRegionSizeMaxMiB"] =
(*pmRegionMaxSizeInKiB) >> 10;
}
if (allocationIncrementInKiB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["AllocationIncrementMiB"] =
(*allocationIncrementInKiB) >> 10;
}
if (allocationAlignmentInKiB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["AllocationAlignmentMiB"] =
(*allocationAlignmentInKiB) >> 10;
}
if (volatileRegionNumberLimit != nullptr)
{
aResp->res.jsonValue[jsonPtr]["VolatileRegionNumberLimit"] =
*volatileRegionNumberLimit;
}
if (pmRegionNumberLimit != nullptr)
{
aResp->res.jsonValue[jsonPtr]["PersistentRegionNumberLimit"] =
*pmRegionNumberLimit;
}
if (spareDeviceCount != nullptr)
{
aResp->res.jsonValue[jsonPtr]["SpareDeviceCount"] = *spareDeviceCount;
}
if (isSpareDeviceInUse != nullptr)
{
aResp->res.jsonValue[jsonPtr]["IsSpareDeviceEnabled"] =
*isSpareDeviceInUse;
}
if (isRankSpareEnabled != nullptr)
{
aResp->res.jsonValue[jsonPtr]["IsRankSpareEnabled"] =
*isRankSpareEnabled;
}
if (maxAveragePowerLimitmW != nullptr)
{
aResp->res.jsonValue[jsonPtr]["MaxTDPMilliWatts"] =
*maxAveragePowerLimitmW;
}
if (configurationLocked != nullptr)
{
aResp->res.jsonValue[jsonPtr]["ConfigurationLocked"] =
*configurationLocked;
}
if (allowedMemoryModes != nullptr)
{
constexpr const std::array<const char*, 3> values{"Volatile", "PMEM",
"Block"};
for (const char* v : values)
{
if (allowedMemoryModes->ends_with(v))
{
aResp->res.jsonValue[jsonPtr]["OperatingMemoryModes"].push_back(
v);
break;
}
}
}
if (memoryMedia != nullptr)
{
constexpr const std::array<const char*, 3> values{"DRAM", "NAND",
"Intel3DXPoint"};
for (const char* v : values)
{
if (memoryMedia->ends_with(v))
{
aResp->res.jsonValue[jsonPtr]["MemoryMedia"].push_back(v);
break;
}
}
}
if (configurationLockCapable != nullptr)
{
aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
["ConfigurationLockCapable"] =
*configurationLockCapable;
}
if (dataLockCapable != nullptr)
{
aResp->res
.jsonValue[jsonPtr]["SecurityCapabilities"]["DataLockCapable"] =
*dataLockCapable;
}
if (passphraseCapable != nullptr)
{
aResp->res
.jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseCapable"] =
*passphraseCapable;
}
if (maxPassphraseCount != nullptr)
{
aResp->res
.jsonValue[jsonPtr]["SecurityCapabilities"]["MaxPassphraseCount"] =
*maxPassphraseCount;
}
if (passphraseLockLimit != nullptr)
{
aResp->res
.jsonValue[jsonPtr]["SecurityCapabilities"]["PassphraseLockLimit"] =
*passphraseLockLimit;
}
}
inline void
getProcessorLinks(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& objectPath,
const nlohmann::json::json_pointer& jsonPtr)
{
BMCWEB_LOG_DEBUG << "Get Memory -- Processor association";
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
dbus_utils::getProperty<std::vector<std::string>>(
"xyz.openbmc_project.ObjectMapper", objectPath + "/processor",
"xyz.openbmc_project.Association", "endpoints", requestContext,
[asyncResp, jsonPtr](const boost::system::error_code ec,
const std::vector<std::string>& processorList) {
if (ec)
{
return;
}
if (processorList.empty())
{
return;
}
nlohmann::json::array_t processorLinks;
for (std::string_view processor : processorList)
{
sdbusplus::message::object_path processorPath(processor.data());
// Define as `std::string` for easier concatenation later.
const std::string processorName = processorPath.filename();
if (processorName.empty())
{
BMCWEB_LOG_ERROR << "filename() is empty in "
<< processorPath.str;
return;
}
nlohmann::json::object_t processorObject;
processorObject["@odata.id"] =
"/redfish/v1/Systems/system/Processor/" + processorName;
processorLinks.push_back(processorObject);
}
asyncResp->res.jsonValue[jsonPtr]["Links"]["Processors"] =
std::move(processorLinks);
});
}
inline void
assembleDimmProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const dbus::utility::DBusPropertiesMap& properties,
const nlohmann::json::json_pointer& jsonPtr)
{
const uint16_t* memoryDataWidth = nullptr;
const uint32_t* memorySizeInKB = nullptr;
const std::string* partNumber = nullptr;
const std::string* serialNumber = nullptr;
const std::string* manufacturer = nullptr;
const uint16_t* revisionCode = nullptr;
const bool* present = nullptr;
const uint16_t* memoryTotalWidth = nullptr;
const std::string* ecc = nullptr;
const std::string* formFactor = nullptr;
const std::vector<uint16_t>* allowedSpeedsMT = nullptr;
const uint32_t* memoryAttributes = nullptr;
const uint16_t* memoryConfiguredSpeedInMhz = nullptr;
const std::string* memoryType = nullptr;
const uint8_t* channel = nullptr;
const uint8_t* memoryController = nullptr;
const uint8_t* slot = nullptr;
const uint8_t* socket = nullptr;
const std::string* sparePartNumber = nullptr;
const std::string* model = nullptr;
const std::string* locationCode = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth",
memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber",
partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
"RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth",
memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor,
"AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes",
memoryAttributes, "MemoryConfiguredSpeedInMhz",
memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel",
channel, "MemoryController", memoryController, "Slot", slot, "Socket",
socket, "SparePartNumber", sparePartNumber, "Model", model,
"LocationCode", locationCode);
if (!success)
{
messages::internalError(aResp->res);
return;
}
if (memoryDataWidth != nullptr)
{
aResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth;
}
if (memorySizeInKB != nullptr)
{
aResp->res.jsonValue[jsonPtr]["CapacityMiB"] = (*memorySizeInKB >> 10);
}
if (partNumber != nullptr)
{
aResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber;
}
if (serialNumber != nullptr)
{
aResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber;
}
if (manufacturer != nullptr)
{
aResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer;
}
if (revisionCode != nullptr)
{
aResp->res.jsonValue[jsonPtr]["FirmwareRevision"] =
std::to_string(*revisionCode);
}
if (present != nullptr && !*present)
{
aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent";
}
if (memoryTotalWidth != nullptr)
{
aResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth;
}
if (ecc != nullptr)
{
constexpr const std::array<const char*, 4> values{
"NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"};
for (const char* v : values)
{
if (ecc->ends_with(v))
{
aResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v;
break;
}
}
}
if (formFactor != nullptr)
{
constexpr const std::array<const char*, 11> values{
"RDIMM", "UDIMM", "SO_DIMM", "LRDIMM",
"Mini_RDIMM", "Mini_UDIMM", "SO_RDIMM_72b", "SO_UDIMM_72b",
"SO_DIMM_16b", "SO_DIMM_32b", "Die"};
for (const char* v : values)
{
if (formFactor->ends_with(v))
{
aResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v;
break;
}
}
}
if (allowedSpeedsMT != nullptr)
{
nlohmann::json& jValue =
aResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"];
jValue = nlohmann::json::array();
for (uint16_t subVal : *allowedSpeedsMT)
{
jValue.push_back(subVal);
}
}
if (memoryAttributes != nullptr)
{
aResp->res.jsonValue[jsonPtr]["RankCount"] =
static_cast<uint64_t>(*memoryAttributes);
}
if (memoryConfiguredSpeedInMhz != nullptr)
{
aResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] =
*memoryConfiguredSpeedInMhz;
}
if (memoryType != nullptr)
{
std::string memoryDeviceType =
translateMemoryTypeToRedfish(*memoryType);
// Values like "Unknown" or "Other" will return empty
// so just leave off
if (!memoryDeviceType.empty())
{
aResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] =
memoryDeviceType;
}
if (memoryType->find("DDR") != std::string::npos)
{
aResp->res.jsonValue[jsonPtr]["MemoryType"] = "DRAM";
}
else if (memoryType->ends_with("Logical"))
{
aResp->res.jsonValue[jsonPtr]["MemoryType"] = "IntelOptane";
}
}
if (channel != nullptr)
{
aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] = *channel;
}
if (memoryController != nullptr)
{
aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] =
*memoryController;
}
if (slot != nullptr)
{
aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot;
}
if (socket != nullptr)
{
aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket;
}
if (sparePartNumber != nullptr)
{
aResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber;
}
if (model != nullptr)
{
aResp->res.jsonValue[jsonPtr]["Model"] = *model;
}
if (locationCode != nullptr)
{
aResp->res
.jsonValue[jsonPtr]["Location"]["PartLocation"]["ServiceLabel"] =
*locationCode;
}
getPersistentMemoryProperties(aResp, properties, jsonPtr);
}
inline void assembleDimmPartitionData(
const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const dbus::utility::DBusPropertiesMap& properties,
const nlohmann::json::json_pointer& regionPtr)
{
const std::string* memoryClassification = nullptr;
const uint64_t* offsetInKiB = nullptr;
const std::string* partitionId = nullptr;
const bool* passphraseState = nullptr;
const uint64_t* sizeInKiB = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification",
memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId",
partitionId, "PassphraseState", passphraseState, "SizeInKiB",
sizeInKiB);
if (!success)
{
messages::internalError(aResp->res);
return;
}
nlohmann::json::object_t partition;
if (memoryClassification != nullptr)
{
partition["MemoryClassification"] = *memoryClassification;
}
if (offsetInKiB != nullptr)
{
partition["OffsetMiB"] = (*offsetInKiB >> 10);
}
if (partitionId != nullptr)
{
partition["RegionId"] = *partitionId;
}
if (passphraseState != nullptr)
{
partition["PassphraseEnabled"] = *passphraseState;
}
if (sizeInKiB != nullptr)
{
partition["SizeMiB"] = (*sizeInKiB >> 10);
}
aResp->res.jsonValue[regionPtr].emplace_back(std::move(partition));
}
namespace memory
{
// An RAII wrapper such that we can populate partitions and health data
// efficiently in the specialized expand handler.
class HealthAndPartition :
public std::enable_shared_from_this<HealthAndPartition>
{
public:
HealthAndPartition(const std::shared_ptr<bmcweb::AsyncResp>& responseIn,
std::unordered_map<std::string, std::string>&& mapIn) :
asyncResponse(responseIn),
partitionServiceToObjectManagerPath(mapIn)
{}
// HealthAndPartition is move-only because we don't want to fetch
// health or partition data for the same AsyncReponse twice.
HealthAndPartition(HealthAndPartition&& other) = default;
HealthAndPartition& operator=(HealthAndPartition&& other) = default;
HealthAndPartition(const HealthAndPartition&) = delete;
HealthAndPartition& operator=(const HealthAndPartition&) = delete;
~HealthAndPartition()
{
BMCWEB_LOG_DEBUG << "HealthAndPartition destructs";
populatePartitions();
}
void getAllPartitions()
{
BMCWEB_LOG_DEBUG << "getAllPartitions entered";
managedStore::ManagedObjectStoreContext context(asyncResponse);
for (const auto& [service, objectManagerPath] :
partitionServiceToObjectManagerPath)
{
managedStore::managedObjectStore->getManagedObjectsWithContext(
service, {objectManagerPath}, context,
[self{shared_from_this()}](
const boost::system::error_code ec,
const dbus::utility::ManagedObjectType& objects) {
if (ec)
{
BMCWEB_LOG_DEBUG << "DBUS response error";
messages::internalError(self->asyncResponse->res);
return;
}
BMCWEB_LOG_DEBUG << "Partition objects stored";
self->objectsForPartition.insert(
self->objectsForPartition.end(),
std::make_move_iterator(objects.begin()),
std::make_move_iterator(objects.end()));
});
}
}
std::shared_ptr<bmcweb::AsyncResp> asyncResponse;
std::shared_ptr<HealthPopulate> health;
std::unordered_map<std::string, nlohmann::json::json_pointer> dimmToPtr;
private:
void populatePartitions()
{
BMCWEB_LOG_DEBUG << "populatePartitions entered";
for (const auto& [objectPath, interfaces] : objectsForPartition)
{
for (const auto& [interface, properties] : interfaces)
{
if (interface ==
"xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition")
{
BMCWEB_LOG_DEBUG << "Found a partition; objectPath="
<< objectPath.str;
// Example objectPath:
// /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
const std::string& dimm =
objectPath.parent_path().filename();
if (dimm.empty() || !dimmToPtr.contains(dimm))
{
continue;
}
assembleDimmPartitionData(asyncResponse, properties,
dimmToPtr[dimm] / "Regions");
}
}
}
auto it = asyncResponse->res.jsonValue.find("Members");
if (it == asyncResponse->res.jsonValue.end())
{
return;
}
auto* members = it->get_ptr<nlohmann::json::array_t*>();
if (members == nullptr)
{
BMCWEB_LOG_ERROR << "Members is not array?!";
messages::internalError(asyncResponse->res);
}
if (!json_util::sortJsonArrayByKey("@odata.id", *members))
{
BMCWEB_LOG_ERROR << "Unable to sort the DIMM collection";
messages::internalError(asyncResponse->res);
}
}
std::unordered_map<std::string, std::string>
partitionServiceToObjectManagerPath;
dbus::utility::ManagedObjectType objectsForPartition;
};
inline void getDimmChassisAssociation(
const std::shared_ptr<memory::HealthAndPartition>& healthAndPartition,
const nlohmann::json::json_pointer& jsonPtr, const std::string& dimmId,
const std::string& path)
{
BMCWEB_LOG_DEBUG << "Get DIMM -- Chassis association";
managedStore::ManagedObjectStoreContext requestContext(
healthAndPartition->asyncResponse);
dbus_utils::getProperty<std::vector<std::string>>(
"xyz.openbmc_project.ObjectMapper", path + "/chassis",
"xyz.openbmc_project.Association", "endpoints", requestContext,
[healthAndPartition, dimmId,
jsonPtr](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 << dimmId
<< " 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;
}
healthAndPartition->asyncResponse->res
.jsonValue[jsonPtr]["Links"]["Chassis"]["@odata.id"] =
"/redfish/v1/Chassis/" + chassisName;
});
}
} // namespace memory
inline void getAllDimmsCallback(
const std::shared_ptr<memory::HealthAndPartition>& healthAndPartition,
const std::optional<std::string>& dimmId,
const boost::system::error_code ec,
const dbus::utility::ManagedObjectType& objects,
const std::string& systemPath)
{
BMCWEB_LOG_DEBUG << "getAllDimmsCallback entered";
if (ec)
{
BMCWEB_LOG_DEBUG << "DBUS response error";
messages::internalError(healthAndPartition->asyncResponse->res);
return;
}
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();
}
for (const auto& [objectPath, interfaces] : objects)
{
// For multi-host systems, the object path of the Dimm must be a descendant
// of the systemPath. (e.g. if systemPath = /a/b, then objectPath must be
// some pattern like /a/b/..)
if (multiHost && !std::string(objectPath).starts_with(systemPath))
{
continue;
}
std::string thisDimmID = objectPath.filename();
if (thisDimmID.empty())
{
continue;
}
if (dimmId && *dimmId != thisDimmID)
{
continue;
}
bool hasDimmInterface = false;
for (const auto& [interface, _] : interfaces)
{
if (interface == "xyz.openbmc_project.Inventory.Item.Dimm")
{
hasDimmInterface = true;
break;
}
}
if (!hasDimmInterface)
{
continue;
}
BMCWEB_LOG_DEBUG << "Found a dimm; objectPath=" << objectPath.str;
nlohmann::json::json_pointer healthPtr;
if (!dimmId)
{
size_t index =
healthAndPartition->asyncResponse->res.jsonValue["Members"]
.size();
healthPtr = "/Members"_json_pointer / index / "Status";
healthAndPartition->dimmToPtr[thisDimmID] =
"/Members"_json_pointer / index;
}
else
{
healthPtr = "/Status"_json_pointer;
healthAndPartition->dimmToPtr[thisDimmID] = ""_json_pointer;
}
#ifdef HEALTH_POPULATE
auto dimmHealth = std::make_shared<HealthPopulate>(
healthAndPartition->asyncResponse, healthPtr);
dimmHealth->selfPath = objectPath;
memory::getDimmChassisAssociation(
healthAndPartition, healthAndPartition->dimmToPtr[thisDimmID],
thisDimmID, objectPath);
if (healthAndPartition->health == nullptr)
{
dimmHealth->populate();
healthAndPartition->health = std::move(dimmHealth);
}
else
{
// if there is already a health object, append other
// objects to its children to avoid duplicate DBus
// queries
healthAndPartition->health->children.push_back(
std::move(dimmHealth));
}
#endif
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]["Id"] =
thisDimmID;
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]["Name"] =
"DIMM Slot";
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]["Status"]
["State"] = "Enabled";
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]["Status"]
["Health"] = "OK";
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Systems", systemName,
"Memory", thisDimmID);
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]
["@odata.type"] = "#Memory.v1_11_0.Memory";
// Memory Metrics are only implemented in single-host systems as of now
if (systemName == "system")
{
// Inserting the "Metrics" JSON stanza
std::string metricsUrl =
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]
["@odata.id"];
metricsUrl += "/MemoryMetrics";
nlohmann::json::object_t metricsObj;
metricsObj["@odata.id"] = std::move(metricsUrl);
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]
["Metrics"] = std::move(metricsObj);
}
for (const auto& [interface, properties] : interfaces)
{
assembleDimmProperties(healthAndPartition->asyncResponse,
properties,
healthAndPartition->dimmToPtr[thisDimmID]);
if (interface == "xyz.openbmc_project.Inventory.Connector.Slot")
{
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]
["Location"]["PartLocation"]["LocationType"] =
"Slot";
}
if (interface == "xyz.openbmc_project.Inventory.Connector.Embedded")
{
healthAndPartition->asyncResponse->res
.jsonValue[healthAndPartition->dimmToPtr[thisDimmID]]
["Location"]["PartLocation"]["LocationType"] =
"Embeded";
}
}
getProcessorLinks(healthAndPartition->asyncResponse, objectPath,
healthAndPartition->dimmToPtr[thisDimmID]);
}
if (!dimmId)
{
healthAndPartition->asyncResponse->res
.jsonValue["Members@odata.count"] =
healthAndPartition->asyncResponse->res.jsonValue["Members"].size();
return;
}
}
inline void getAllDimms(
const std::shared_ptr<memory::HealthAndPartition>& healthAndPartition,
const std::optional<std::string>& dimmId,
const managedStore::ManagedObjectStoreContext& context,
const std::unordered_map<std::string, std::string>&
serviceToObjectManagerPath,
const std::string& systemPath)
{
if (!dimmId)
{
healthAndPartition->asyncResponse->res.jsonValue["Members"] =
nlohmann::json::array();
healthAndPartition->asyncResponse->res
.jsonValue["Members@odata.count"] = 0;
}
for (const auto& [service, objectManagerPath] : serviceToObjectManagerPath)
{
managedStore::managedObjectStore->getManagedObjectsWithContext(
service, objectManagerPath, context,
[healthAndPartition, dimmId{dimmId},
systemPath](const boost::system::error_code ec,
const dbus::utility::ManagedObjectType& objects) {
getAllDimmsCallback(healthAndPartition, dimmId, ec, objects,
systemPath);
});
}
}
inline void getObjectManagerPathsGivenServicesCallBack(
const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const std::optional<std::string>& dimmId,
const std::unordered_set<std::string>& dimmServices,
const std::unordered_set<std::string>& partitionServices,
const boost::system::error_code ec,
const dbus::utility::MapperGetSubTreeResponse& subtree,
const std::string& systemPath)
{
if (ec)
{
BMCWEB_LOG_DEBUG << "DBUS response error";
messages::internalError(aResp->res);
return;
}
BMCWEB_LOG_DEBUG << "There are " << dimmServices.size()
<< " services which implement the DIMM interface";
BMCWEB_LOG_DEBUG << "There are " << partitionServices.size()
<< " services which implement the Partition interface";
std::unordered_map<std::string, std::string> dimmServiceToObjectManagerPath;
std::unordered_map<std::string, std::string>
partitionServiceToObjectManagerPath;
for (const auto& [objectManagerPath, mapperServiceMap] : subtree)
{
for (const auto& [service, _] : mapperServiceMap)
{
if (dimmServices.contains(service))
{
dimmServiceToObjectManagerPath[service] = objectManagerPath;
}
if (partitionServices.contains(service))
{
partitionServiceToObjectManagerPath[service] =
objectManagerPath;
}
}
}
auto healthAndPartition = std::make_shared<memory::HealthAndPartition>(
aResp, std::move(partitionServiceToObjectManagerPath));
healthAndPartition->getAllPartitions();
managedStore::ManagedObjectStoreContext context(aResp);
getAllDimms(healthAndPartition, dimmId, context,
dimmServiceToObjectManagerPath, systemPath);
}
inline void getObjectManagerPathsGivenServices(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::optional<std::string>& dimmId,
std::unordered_set<std::string>&& dimmServices,
std::unordered_set<std::string>&& partitionServices,
const std::string& systemPath)
{
std::array<std::string_view, 1> interfaces{
"org.freedesktop.DBus.ObjectManager"};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::managedObjectStore->getSubTree(
"/", 0, interfaces, requestContext,
[asyncResp{asyncResp}, dimmId{dimmId},
dimmServices{std::move(dimmServices)},
partitionServices{std::move(partitionServices)},
systemPath](const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
getObjectManagerPathsGivenServicesCallBack(
asyncResp, dimmId, dimmServices, partitionServices, ec, subtree,
systemPath);
});
}
// If |dimmId| is set, will only keep the first matched dimmId.
inline void getDimmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::optional<std::string>& dimmId,
const std::string& subtreeRoot,
const std::string& systemPath)
{
constexpr std::array<std::string_view, 2> dimmInterfaces = {
"xyz.openbmc_project.Inventory.Item.Dimm",
"xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::managedObjectStore->getSubTree(
subtreeRoot, 0, dimmInterfaces, requestContext,
[asyncResp{asyncResp}, dimmId,
systemPath](const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec)
{
BMCWEB_LOG_DEBUG << "DBUS response error";
messages::internalError(asyncResp->res);
return;
}
BMCWEB_LOG_DEBUG
<< "Collect services that implement DIMM/partition interface for "
<< dimmId.value_or("all DIMMs");
bool foundGivenDimm = false;
std::unordered_set<std::string> dimmServices;
std::unordered_set<std::string> partitionServices;
for (const auto& [path, object] : subtree)
{
BMCWEB_LOG_DEBUG << "Object path=" << path;
sdbusplus::message::object_path objectPath(path);
for (const auto& [service, interfaces] : object)
{
for (const std::string& interface : interfaces)
{
if (interface == "xyz.openbmc_project.Inventory.Item.Dimm")
{
if (dimmId && objectPath.filename() != *dimmId)
{
continue;
}
BMCWEB_LOG_DEBUG << "Added DIMM services " << service;
dimmServices.insert(service);
foundGivenDimm = true;
}
// partitions are separate as there can be multiple
// per device, i.e.
// /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
// /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
if (interface ==
"xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition")
{
if (dimmId &&
objectPath.parent_path().filename() != *dimmId)
{
continue;
}
BMCWEB_LOG_DEBUG << "Added Partition services "
<< service;
partitionServices.insert(service);
}
}
}
// Fetch the first matched DIMM
if (dimmId && foundGivenDimm)
{
break;
}
}
if (dimmId && !foundGivenDimm)
{
messages::resourceNotFound(asyncResp->res, "Memory", *dimmId);
return;
}
getObjectManagerPathsGivenServices(
asyncResp, dimmId, std::move(dimmServices),
std::move(partitionServices), systemPath);
});
}
inline void
handleMemoryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemPath,
const query_param::Query& delegated)
{
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.jsonValue["@odata.type"] =
"#MemoryCollection.MemoryCollection";
asyncResp->res.jsonValue["Name"] = "Memory Module Collection";
asyncResp->res.jsonValue["@odata.id"] =
"/redfish/v1/Systems/" + systemName + "/Memory";
constexpr std::array<std::string_view, 1> interfaces{
"xyz.openbmc_project.Inventory.Item.Dimm"};
if (delegated.expandLevel > 0 &&
delegated.expandType != query_param::ExpandType::None)
{
BMCWEB_LOG_DEBUG << "Use efficient expand handler";
getDimmData(asyncResp, std::nullopt, subtreeRoot, systemPath);
}
else
{
BMCWEB_LOG_DEBUG << "Use default expand handler";
collection_util::getCollectionMembers(
asyncResp,
boost::urls::url("/redfish/v1/Systems/" + systemName + "/Memory"),
interfaces, subtreeRoot.c_str());
}
}
inline void requestRoutesMemoryCollection(App& app)
{
/**
* Functions triggers appropriate requests on DBus
*/
BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/")
.privileges(redfish::privileges::getMemoryCollection)
.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 = {
.canDelegateExpandLevel = 1,
};
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) {
handleMemoryCollection(asyncResp, systemPath, delegated);
});
});
}
// Constructs hook with known-good settings for use with MemoryMetrics
inline external_storer::Hook makeDimmHook()
{
const std::string pathBase{"Systems/system/Memory"};
const std::string emptyString;
const std::vector<std::string> emptyList;
return {pathBase, emptyString, emptyList, emptyList};
}
// Remembers the ExternalStorer hook and one instance per DIMM in this system
inline std::shared_ptr<external_storer::Hook>
rememberDimmHook(const std::string& dimmId)
{
static std::shared_ptr<external_storer::Hook> hookMemory = nullptr;
if (!hookMemory)
{
// If not already remembered by static variable, create hook
hookMemory = std::make_shared<external_storer::Hook>(makeDimmHook());
}
auto respGet = std::make_shared<bmcweb::AsyncResp>();
hookMemory->handleGetInstance(respGet, dimmId);
if (respGet->res.result() == boost::beast::http::status::ok)
{
// Already exists, good, nothing more needs to be done
return hookMemory;
}
boost::beast::http::request<boost::beast::http::string_body> upBody;
std::error_code ec;
// Create instance, with name of DIMM, and no further customizations
auto upJson = nlohmann::json::object();
upJson["Id"] = dimmId;
// 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};
auto respCreate = std::make_shared<bmcweb::AsyncResp>();
hookMemory->handleCreateInstance(reqCreate, respCreate);
if (respCreate->res.result() != boost::beast::http::status::created)
{
BMCWEB_LOG_ERROR << "Problem creating instance for " << dimmId;
return nullptr;
}
// The instance should now be usable
return hookMemory;
}
inline nlohmann::json fakeMemoryMetrics(const std::string& dimm)
{
nlohmann::json::object_t metricsObj;
metricsObj["Id"] = "Metrics";
metricsObj["Name"] = "Memory Metrics";
nlohmann::json::object_t currentPeriod;
currentPeriod["CorrectableECCErrorCount"] = 0;
currentPeriod["UncorrectableECCErrorCount"] = 0;
currentPeriod["IndeterminateCorrectableErrorCount"] = 0;
currentPeriod["IndeterminateUncorrectableErrorCount"] = 0;
metricsObj["CurrentPeriod"] = std::move(currentPeriod);
// 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/Memory/";
url += dimm;
url += "/MemoryMetrics";
metricsObj["@odata.id"] = std::move(url);
return metricsObj;
}
// TODO(): This is cut-and-paste code with the ProcessorMetrics patch
// This function is only renamed because of compiler limitation
// Merge into a common utility function, once known where to keep it
inline int prefixStrToInt2(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;
long 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 countDimm()
{
// TODO(): This is a STUB
return 256;
}
inline bool checkDimmIndex(const std::string& dimmId)
{
int bound = countDimm();
int num = prefixStrToInt2("dimm", dimmId);
if (num < 0)
{
BMCWEB_LOG_ERROR << "DIMM index not parseable";
return false;
}
if (num >= bound)
{
BMCWEB_LOG_ERROR << "DIMM index out of range";
return false;
}
return true;
}
inline bool getExternalStorerMemoryMetrics(
const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& dimmId)
{
if (!checkDimmIndex(dimmId))
{
BMCWEB_LOG_ERROR << "Problem checking DIMM index for " << dimmId;
return false;
}
std::shared_ptr<external_storer::Hook> hook = rememberDimmHook(dimmId);
if (!hook)
{
BMCWEB_LOG_ERROR << "Problem getting hook for " << dimmId;
return false;
}
std::string emptyString;
hook->handleGetEntry(aResp, ""_json_pointer, dimmId, emptyString,
"MemoryMetrics");
if (aResp->res.result() == boost::beast::http::status::not_found)
{
// Fake up a synthetic response to replace only the 404 error
aResp->res.jsonValue = fakeMemoryMetrics(dimmId);
aResp->res.result(boost::beast::http::status::ok);
}
if (aResp->res.result() != boost::beast::http::status::ok)
{
BMCWEB_LOG_ERROR << "Problem getting file for " << dimmId;
return false;
}
// Apply invariants, to override whatever might have been in the file
aResp->res.jsonValue["@odata.type"] = "#MemoryMetrics.v1_5_0.MemoryMetrics";
return true;
}
inline void getMemoryMetrics(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const std::string& dimmId)
{
BMCWEB_LOG_DEBUG << "Get available memory metrics for DIMM.";
// This is the integration point for ExternalStorer with MemoryMetrics.
// If locally-existing file exists, use ExternalStorer to return it.
if (getExternalStorerMemoryMetrics(aResp, dimmId))
{
BMCWEB_LOG_DEBUG << "Successful local file read for " << dimmId;
return;
}
// No more falling back to D-Bus query, instead, simply give user 404
BMCWEB_LOG_WARNING << "Memory metrics not found for " << dimmId;
messages::resourceNotFound(aResp->res, "MemoryMetrics", dimmId);
}
inline void handleMemory(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemPath,
const std::string& dimmId)
{
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;
}
getDimmData(asyncResp, dimmId, subtreeRoot, systemPath);
}
inline void requestRoutesMemory(App& app)
{
/**
* Functions triggers appropriate requests on DBus
*/
BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
.privileges(redfish::privileges::getMemory)
.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& dimmId) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp))
{
return;
}
redfish::system_utils::getSystemInformation(
asyncResp, systemName,
[dimmId](const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemPath) {
handleMemory(asyncResp, systemPath, dimmId);
});
});
}
inline void requestRoutesMemoryMetrics(App& app)
{
/**
* Functions triggers appropriate requests on DBus
*/
BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/Memory/<str>/MemoryMetrics/")
.privileges(redfish::privileges::getMemoryMetrics)
.methods(boost::beast::http::verb::get)(
[&app](const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& dimmId) {
if (!redfish::setUpRedfishRoute(app, req, asyncResp))
{
return;
}
asyncResp->res.jsonValue["@odata.type"] =
"#MemoryMetrics.v1_5_0.MemoryMetrics";
asyncResp->res.jsonValue["@odata.id"] =
"/redfish/v1/Systems/system/Memory/" + dimmId + "/MemoryMetrics";
getMemoryMetrics(asyncResp, dimmId);
});
}
} // namespace redfish