Implement Redfish PCIeSlots schema
PCIeSlotCollection, and PCIeSlot schemas are used for determining
and inspecting the PCIe physical topology of a system. It is used to
determine what a particular physical slots formfactor is.
This commit supports the as documented in Redfish.md.
https://redfish.dmtf.org/schemas/PCIeSlots_v1.xml
Tested: Validator passes (on previous patchset)
1、Get PCIe slots
curl -k -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PCIeSlots
{
"@odata.id": "/redfish/v1/Chassis/chassis/PCIeSlots",
"@odata.type": "#PCIeSlots.v1_4_1.PCIeSlots",
"Id": "PCIeSlots",
"Name": "PCIe Slot Information",
"Slots": [
{
"HotPluggable": false,
"Lanes": 16,
"PCIeType": "Gen1",
"SlotType": "FullLength"
},
{
"HotPluggable": false,
"Lanes": 16,
"PCIeType": "Gen2",
"SlotType": "OEM"
}
]
}
2、No PCIeSlots
curl -k -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/chassis/PCIeSlots
{
"@odata.id": "/redfish/v1/Chassis/chassis/PCIeSlots",
"@odata.type": "#PCIeSlots.v1_4_1.PCIeSlots",
"Id": "PCIeSlots",
"Name": "PCIe Slot Information",
"Slots": []
}
3、Bad chassis ID return 404
curl -k -H "X-Auth-Token: $token" -X GET
https://${bmc}/redfish/v1/Chassis/badChassisID/PCIeSlots
Returns 404 and ResourceNotFound
Signed-off-by: Chicago Duan <duanzhijia01@inspur.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I11e1bf94b3865986cbd580293ea906fe96067912
diff --git a/Redfish.md b/Redfish.md
index b7d7f4c..ab4f7db 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -274,6 +274,15 @@
- MinNumNeeded
- MaxNumSupported
+#### /redfish/v1/Chassis/{ChassisId}/PCIeSlots/
+- Members
+
+#### /redfish/v1/Chassis/{ChassisId}/PCIeSlots/{SlotName}
+- HotPluggable
+- Lanes
+- PCIeType
+- SlotType
+
#### /redfish/v1/EventService/
##### EventService
- Actions
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index b7dd3b3..55c00f6 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -31,6 +31,7 @@
#include "../lib/metric_report_definition.hpp"
#include "../lib/network_protocol.hpp"
#include "../lib/pcie.hpp"
+#include "../lib/pcie_slots.hpp"
#include "../lib/power.hpp"
#include "../lib/processor.hpp"
#include "../lib/redfish_sessions.hpp"
@@ -80,6 +81,7 @@
requestRoutesManagerResetAction(app);
requestRoutesManagerResetActionInfo(app);
requestRoutesManagerResetToDefaultsAction(app);
+ requestRoutesPCIeSlots(app);
requestRoutesChassisCollection(app);
requestRoutesChassis(app);
requestRoutesChassisResetAction(app);
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index 724d5f3..11c04b4 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -397,6 +397,10 @@
"/redfish/v1/Chassis/" + chassisId + "/Sensors";
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
+ asyncResp->res.jsonValue["PCIeSlots"]["@odata.id"] =
+ crow::utility::urlFromPieces("redfish", "v1", "Chassis",
+ chassisId, "PCIeSlots");
+
nlohmann::json::array_t computerSystems;
nlohmann::json::object_t system;
system["@odata.id"] = "/redfish/v1/Systems/system";
diff --git a/redfish-core/lib/pcie.hpp b/redfish-core/lib/pcie.hpp
index b4d6433..8437f50 100644
--- a/redfish-core/lib/pcie.hpp
+++ b/redfish-core/lib/pcie.hpp
@@ -129,9 +129,8 @@
{
return "Gen5";
}
- if (generationInUse.empty() ||
- generationInUse ==
- "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown")
+ if (generationInUse ==
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown")
{
return "";
}
diff --git a/redfish-core/lib/pcie_slots.hpp b/redfish-core/lib/pcie_slots.hpp
new file mode 100644
index 0000000..b340df2
--- /dev/null
+++ b/redfish-core/lib/pcie_slots.hpp
@@ -0,0 +1,290 @@
+#pragma once
+
+#include "error_messages.hpp"
+#include "utility.hpp"
+
+#include <app.hpp>
+#include <pcie.hpp>
+#include <registries/privilege_registry.hpp>
+#include <utils/json_utils.hpp>
+
+namespace redfish
+{
+
+inline std::string dbusSlotTypeToRf(const std::string& slotType)
+{
+ if (slotType ==
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.FullLength")
+ {
+ return "FullLength";
+ }
+ if (slotType ==
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.HalfLength")
+ {
+ return "HalfLength";
+ }
+ if (slotType ==
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.LowProfile")
+ {
+ return "LowProfile";
+ }
+ if (slotType ==
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.Mini")
+ {
+ return "Mini";
+ }
+ if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.M_2")
+ {
+ return "M2";
+ }
+ if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OEM")
+ {
+ return "OEM";
+ }
+ if (slotType ==
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OCP3Small")
+ {
+ return "OCP3Small";
+ }
+ if (slotType ==
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.OCP3Large")
+ {
+ return "OCP3Large";
+ }
+ if (slotType == "xyz.openbmc_project.Inventory.Item.PCIeSlot.SlotTypes.U_2")
+ {
+ return "U2";
+ }
+
+ // Unknown or others
+ return "";
+}
+
+inline void
+ onPcieSlotGetAllDone(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const boost::system::error_code ec,
+ const dbus::utility::DBusPropertiesMap& propertiesList)
+{
+ 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;
+
+ for (const auto& property : propertiesList)
+ {
+ const std::string& propertyName = property.first;
+
+ if (propertyName == "Generation")
+ {
+ const std::string* value =
+ std::get_if<std::string>(&property.second);
+ if (value == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ std::optional<std::string> pcieType =
+ redfishPcieGenerationFromDbus(*value);
+ if (!pcieType)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ slot["PCIeType"] = !pcieType;
+ }
+ else if (propertyName == "Lanes")
+ {
+ const size_t* value = std::get_if<size_t>(&property.second);
+ if (value == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ slot["Lanes"] = *value;
+ }
+ else if (propertyName == "SlotType")
+ {
+ const std::string* value =
+ std::get_if<std::string>(&property.second);
+ if (value == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ std::string slotType = dbusSlotTypeToRf(*value);
+ if (!slotType.empty())
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ slot["SlotType"] = slotType;
+ }
+ else if (propertyName == "HotPluggable")
+ {
+ const bool* value = std::get_if<bool>(&property.second);
+ if (value == nullptr)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ slot["HotPluggable"] = *value;
+ }
+ }
+ slots.emplace_back(std::move(slot));
+}
+
+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 std::variant<std::vector<std::string>>& endpoints)
+{
+ if (ec)
+ {
+ if (ec.value() == EBADR)
+ {
+ // This PCIeSlot have no chassis association.
+ return;
+ }
+ BMCWEB_LOG_ERROR << "DBUS response error";
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ const std::vector<std::string>* pcieSlotChassis =
+ std::get_if<std::vector<std::string>>(&(endpoints));
+
+ if (pcieSlotChassis == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Error getting PCIe Slot association!";
+ 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;
+ }
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec,
+ const dbus::utility::DBusPropertiesMap& propertiesList) {
+ onPcieSlotGetAllDone(asyncResp, ec, propertiesList);
+ },
+ connectionName, pcieSlotPath, "org.freedesktop.DBus.Properties",
+ "GetAll", "xyz.openbmc_project.Inventory.Item.PCIeSlot");
+}
+
+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;
+ }
+ if (subtree.empty())
+ {
+ messages::resourceNotFound(asyncResp->res, "#Chassis", chassisID);
+ 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();
+
+ for (const auto& pathServicePair : subtree)
+ {
+ const std::string& pcieSlotPath = pathServicePair.first;
+ for (const auto& connectionInterfacePair : pathServicePair.second)
+ {
+ const std::string& connectionName = connectionInterfacePair.first;
+ sdbusplus::message::object_path pcieSlotAssociationPath(
+ pcieSlotPath);
+ pcieSlotAssociationPath /= "chassis";
+
+ // The association of this PCIeSlot is used to determine whether
+ // it belongs to this ChassisID
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, chassisID, pcieSlotPath, connectionName](
+ const boost::system::error_code ec,
+ const std::variant<std::vector<std::string>>& endpoints) {
+ onMapperAssociationDone(asyncResp, chassisID, pcieSlotPath,
+ connectionName, ec, endpoints);
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ std::string{pcieSlotAssociationPath},
+ "org.freedesktop.DBus.Properties", "Get",
+ "xyz.openbmc_project.Association", "endpoints");
+ }
+ }
+}
+
+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->res))
+ {
+ return;
+ }
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp,
+ chassisID](const boost::system::error_code ec,
+ const dbus::utility::MapperGetSubTreeResponse& subtree) {
+ onMapperSubtreeDone(asyncResp, chassisID, ec, subtree);
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+ "/xyz/openbmc_project/inventory", int32_t(0),
+ std::array<const char*, 1>{
+ "xyz.openbmc_project.Inventory.Item.PCIeSlot"});
+}
+
+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