blob: 365abd25eb6174c672c8f0040763589101064764 [file] [log] [blame]
#pragma once
#include "app.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "generated/enums/pcie_slots.hpp"
#include "pcie.hpp"
#include "registries/privilege_registry.hpp"
#include "utility.hpp"
#include "utils/dbus_utils.hpp"
#include "utils/json_utils.hpp"
#include "utils/location_utils.hpp"
#include <boost/system/error_code.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <array>
#include <string_view>
#include "managed_store.hpp"
#ifdef UNIT_TEST_BUILD
#include "test/g3/mock_managed_store.hpp" // NOLINT
#endif
namespace redfish
{
inline void
addPresenceStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& connectionName,
const std::string& pcieSlotPath, size_t index)
{
managedStore::ManagedObjectStoreContext context(asyncResp);
redfish::dbus_utils::getProperty<bool>(
connectionName, pcieSlotPath,
"xyz.openbmc_project.Inventory.Item", "Present",
context,
[asyncResp, index](boost::system::error_code ec,
bool value) {
if (ec)
{
BMCWEB_LOG_DEBUG << "DBUS response error";
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue["Slots"][index]["Status"]["State"] =
value ? "Enabled" : "Absent";
});
}
inline void addLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& connectionName,
const std::string& pcieSlotPath, size_t index)
{
location_util::getPartLocationContext(
asyncResp, "/Slots"_json_pointer / index / "Location",
pcieSlotPath + "/chassis");
managedStore::ManagedObjectStoreContext context(asyncResp);
redfish::dbus_utils::getProperty<std::string>(
connectionName, pcieSlotPath,
"xyz.openbmc_project.Inventory.Decorator.LocationCode",
"LocationCode", context,
[asyncResp, index](const boost::system::error_code ec,
const std::string& value) {
if (ec)
{
BMCWEB_LOG_DEBUG << "DBUS response error";
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue["Slots"][index]["Location"]["PartLocation"]
["ServiceLabel"] = value;
});
}
inline pcie_slots::SlotTypes dbusSlotTypeToRf(const std::string& slotType)
{
if (slotType ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.FullLength")
{
return pcie_slots::SlotTypes::FullLength;
}
if (slotType ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.HalfLength")
{
return pcie_slots::SlotTypes::HalfLength;
}
if (slotType ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.LowProfile")
{
return pcie_slots::SlotTypes::LowProfile;
}
if (slotType ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.Mini")
{
return pcie_slots::SlotTypes::Mini;
}
if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.M_2")
{
return pcie_slots::SlotTypes::M2;
}
if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OEM")
{
return pcie_slots::SlotTypes::OEM;
}
if (slotType ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OCP3Small")
{
return pcie_slots::SlotTypes::OCP3Small;
}
if (slotType ==
"xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OCP3Large")
{
return pcie_slots::SlotTypes::OCP3Large;
}
if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.U_2")
{
return pcie_slots::SlotTypes::U2;
}
// Unknown or others
return pcie_slots::SlotTypes::Invalid;
}
inline void
onPcieSlotGetAllDone(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& propertiesList,
const std::function<void(size_t)>& callback)
{
if (ec)
{
BMCWEB_LOG_ERROR << "Can't get PCIeSlot properties!";
messages::internalError(asyncResp->res);
return;
}
nlohmann::json& slots = asyncResp->res.jsonValue["Slots"];
nlohmann::json::array_t* slotsPtr =
slots.get_ptr<nlohmann::json::array_t*>();
if (slotsPtr == nullptr)
{
BMCWEB_LOG_ERROR << "Slots key isn't an array???";
messages::internalError(asyncResp->res);
return;
}
nlohmann::json::object_t slot;
const std::string* generation = nullptr;
const size_t* lanes = nullptr;
const std::string* slotType = nullptr;
const bool* hotPluggable = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), propertiesList, "Generation",
generation, "Lanes", lanes, "SlotType", slotType, "HotPluggable",
hotPluggable);
if (!success)
{
messages::internalError(asyncResp->res);
return;
}
if (generation != nullptr)
{
std::optional<pcie_device::PCIeTypes> pcieType =
redfishPcieGenerationFromDbus(*generation);
if (!pcieType)
{
messages::internalError(asyncResp->res);
return;
}
slot["PCIeType"] = *pcieType;
}
if (lanes != nullptr && *lanes != 0)
{
slot["Lanes"] = *lanes;
}
if (slotType != nullptr)
{
pcie_slots::SlotTypes redfishSlotType = dbusSlotTypeToRf(*slotType);
if (redfishSlotType == pcie_slots::SlotTypes::Invalid)
{
return;
}
slot["SlotType"] = redfishSlotType;
}
if (hotPluggable != nullptr)
{
slot["HotPluggable"] = *hotPluggable;
}
// Pass the slot index to the callback.
size_t slotIndex = slots.size();
slots.emplace_back(std::move(slot));
callback(slotIndex);
}
inline void onMapperAssociationDone(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisID, const std::string& pcieSlotPath,
const std::string& connectionName, const boost::system::error_code& ec,
const dbus::utility::MapperEndPoints& pcieSlotChassis, bool findLocation,
bool findPresence)
{
if (ec)
{
if (ec.value() == EBADR)
{
// This PCIeSlot have no chassis association.
return;
}
BMCWEB_LOG_ERROR << "DBUS response error";
messages::internalError(asyncResp->res);
return;
}
if (pcieSlotChassis.size() != 1)
{
BMCWEB_LOG_ERROR << "PCIe Slot association error! ";
messages::internalError(asyncResp->res);
return;
}
sdbusplus::message::object_path path(pcieSlotChassis[0]);
std::string chassisName = path.filename();
if (chassisName != chassisID)
{
// The pcie slot doesn't belong to the chassisID
return;
}
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getAllProperties(
connectionName, pcieSlotPath,
"xyz.openbmc_project.Inventory.Item.PCIeSlot", context,
[asyncResp, connectionName, pcieSlotPath, findLocation,
findPresence](const boost::system::error_code& ec2,
const dbus::utility::DBusPropertiesMap& propertiesList) {
onPcieSlotGetAllDone(asyncResp, ec2, propertiesList,
[asyncResp, connectionName, pcieSlotPath,
findLocation, findPresence](size_t index) {
if (findLocation)
{
addLocation(asyncResp, connectionName, pcieSlotPath, index);
}
if (findPresence)
{
addPresenceStatus(asyncResp, connectionName, pcieSlotPath,
index);
}
});
});
}
inline void
onMapperSubtreeDone(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisID,
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree)
{
if (ec)
{
BMCWEB_LOG_ERROR << "D-Bus response error on GetSubTree " << ec;
messages::internalError(asyncResp->res);
return;
}
BMCWEB_LOG_DEBUG << "Get properties for PCIeSlots associated to chassis = "
<< chassisID;
asyncResp->res.jsonValue["@odata.type"] = "#PCIeSlots.v1_4_1.PCIeSlots";
asyncResp->res.jsonValue["Name"] = "PCIe Slot Information";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisID, "PCIeSlots");
asyncResp->res.jsonValue["Id"] = "1";
asyncResp->res.jsonValue["Slots"] = nlohmann::json::array();
if (subtree.empty())
{
return;
}
const std::string locationInterface =
"xyz.openbmc_project.Inventory.Decorator."
"LocationCode";
const std::string itemInterface = "xyz.openbmc_project.Inventory.Item";
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
for (const auto& pathServicePair : subtree)
{
const std::string& pcieSlotPath = pathServicePair.first;
for (const auto& connectionInterfacePair : pathServicePair.second)
{
const std::string& connectionName = connectionInterfacePair.first;
const std::vector<std::string>& interfaceList =
connectionInterfacePair.second;
sdbusplus::message::object_path pcieSlotAssociationPath(
pcieSlotPath);
pcieSlotAssociationPath /= "chassis";
// The association of this PCIeSlot is used to determine whether
// it belongs to this ChassisID
dbus_utils::getAssociationEndPoints(
std::string{pcieSlotAssociationPath}, requestContext,
[asyncResp, chassisID, pcieSlotPath, connectionName,
interfaceList, locationInterface, itemInterface](
const boost::system::error_code& ec2,
const dbus::utility::MapperEndPoints& endpoints) {
bool findPresence =
std::find(interfaceList.begin(), interfaceList.end(),
itemInterface) != interfaceList.end();
bool findLocation =
std::find(interfaceList.begin(), interfaceList.end(),
locationInterface) != interfaceList.end();
onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath,
connectionName, ec2, endpoints,
findLocation, findPresence);
});
}
}
}
inline void handlePCIeSlotCollectionGet(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisID)
{
if (!redfish::setUpRedfishRoute(app, req, asyncResp))
{
return;
}
constexpr std::array<std::string_view, 1> interfaces = {
"xyz.openbmc_project.Inventory.Item.PCIeSlot"};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTree(
"/xyz/openbmc_project/inventory", 0, interfaces, requestContext,
[asyncResp,
chassisID](const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
onMapperSubtreeDone(asyncResp, chassisID, ec, subtree);
});
}
inline void requestRoutesPCIeSlots(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PCIeSlots/")
.privileges(redfish::privileges::getPCIeSlots)
.methods(boost::beast::http::verb::get)(
std::bind_front(handlePCIeSlotCollectionGet, std::ref(app)));
}
} // namespace redfish