// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright OpenBMC Authors #pragma once #include "app.hpp" #include "async_resp.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" #include "fabric.hpp" #include "generated/enums/port.hpp" #include "generated/enums/resource.hpp" #include "http_request.hpp" #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace redfish { inline port::PortType dBusSensorPortTypeToRedfish(const std::string& portType) { if (portType == "xyz.openbmc_project.Inventory.Connector.Port.PortType.DownstreamPort") { return port::PortType::DownstreamPort; } if (portType == "xyz.openbmc_project.Inventory.Connector.Port.PortType.UpstreamPort") { return port::PortType::UpstreamPort; } return port::PortType::Invalid; } inline std::string dBusSensorPortProtocolToRedfish( const std::string& portProtocol) { if (portProtocol == "xyz.openbmc_project.Inventory.Connector.Port.PortProtocol.PCIe") { return "PCIe"; } return "Unknown"; } inline void afterGetFabricSwitchPortInfo( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::DBusPropertiesMap& properties) { if (ec) { BMCWEB_LOG_ERROR("DBus response error on GetAllProperties {}", ec.value()); messages::internalError(asyncResp->res); return; } std::optional speed; std::optional width; std::optional portType; std::optional portProtocol; const bool success = sdbusplus::unpackPropertiesNoThrow( dbus_utils::UnpackErrorPrinter(), properties, "Speed", speed, "Width", width, "PortType", portType, "PortProtocol", portProtocol); if (!success) { messages::internalError(asyncResp->res); return; } if (speed.has_value()) { if (*speed != 0 && *speed != std::numeric_limits::max()) { // Convert from bits per second to Gigabits per second (Gbps) static constexpr int gbpsToBps = 1 << 30; const double convertedSpeed = static_cast(*speed) / gbpsToBps; asyncResp->res.jsonValue["CurrentSpeedGbps"] = convertedSpeed; } } if (width.has_value()) { if (*width != 0 && *width != std::numeric_limits::max()) { asyncResp->res.jsonValue["ActiveWidth"] = *width; } } if (portType.has_value()) { const port::PortType portTypeEnum = dBusSensorPortTypeToRedfish(*portType); if (portTypeEnum != port::PortType::Invalid) { asyncResp->res.jsonValue["PortType"] = portTypeEnum; } } if (portProtocol.has_value()) { const std::string portProtocolStr = dBusSensorPortProtocolToRedfish(*portProtocol); if (portProtocolStr != "Unknown") { asyncResp->res.jsonValue["PortProtocol"] = portProtocolStr; } } } inline void populateMetricsProperty( const std::shared_ptr& asyncResp, const nlohmann::json::json_pointer& jsonPtr, const boost::system::error_code& ec, double value) { if (ec) { BMCWEB_LOG_DEBUG( "DBus response error on GetProperty {} for property {}", ec, jsonPtr.to_string()); return; } if (!std::isfinite(value)) { BMCWEB_LOG_DEBUG("Received non-finite value for property {}", jsonPtr.to_string()); asyncResp->res.jsonValue[jsonPtr] = nullptr; } else { asyncResp->res.jsonValue[jsonPtr] = static_cast(value); } } inline void getMetricProperty( const std::shared_ptr& asyncResp, const std::string& serviceName, const sdbusplus::object_path& objectPath, const nlohmann::json::json_pointer& jsonPtr) { dbus::utility::getProperty( serviceName, objectPath, "xyz.openbmc_project.Metric.Value", "Value", std::bind_front(populateMetricsProperty, asyncResp, jsonPtr)); } inline void handleFabricSwitchPortMetricsPathPortMetricsGet( const std::shared_ptr& asyncResp, const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& object) { if (ec) { BMCWEB_LOG_ERROR("DBus response error on GetAssociatedSubTree{}", ec); messages::internalError(asyncResp->res); return; } for (const auto& [path, service] : object) { if (service.size() != 1) { continue; } sdbusplus::object_path objectPah(path); const std::string metricType = objectPah.parent_path().filename(); const std::string metricName = objectPah.filename(); if (metricType != "pcie") { continue; } const auto& serviceName = service.begin()->first; if (metricName == "correctable_error_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/CorrectableErrorCount"_json_pointer); } else if (metricName == "non_fatal_error_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/NonFatalErrorCount"_json_pointer); } else if (metricName == "fatal_error_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/FatalErrorCount"_json_pointer); } else if (metricName == "l0_to_recovery_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/L0ToRecoveryCount"_json_pointer); } else if (metricName == "replay_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/ReplayCount"_json_pointer); } else if (metricName == "replay_rollover_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/ReplayRolloverCount"_json_pointer); } else if (metricName == "nak_sent_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/NAKSentCount"_json_pointer); } else if (metricName == "nak_received_count") { getMetricProperty(asyncResp, serviceName, path, "/PCIeErrors/NAKReceivedCount"_json_pointer); } else if (metricName == "unsupported_request_count") { getMetricProperty( asyncResp, serviceName, path, "/PCIeErrors/UnsupportedRequestCount"_json_pointer); } } } inline void handleFabricSwitchPortPathPortMetricsGet( const std::shared_ptr& asyncResp, const std::string& fabricId, const std::string& switchId, const std::string& portId, const std::string& portPath, [[maybe_unused]] const std::string& serviceName) { asyncResp->res.jsonValue["@odata.type"] = "#PortMetrics.v1_3_0.PortMetrics"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}/Metrics", fabricId, switchId, portId); asyncResp->res.jsonValue["Id"] = "Metrics"; asyncResp->res.jsonValue["Name"] = std::format("{} {} Port Metrics", switchId, portId); const sdbusplus::object_path associationPath = sdbusplus::object_path(portPath) / "measured_by"; dbus::utility::getAssociatedSubTree( associationPath, sdbusplus::object_path("/xyz/openbmc_project/metric"), 0, std::array{"xyz.openbmc_project.Metric.Value"}, std::bind_front(handleFabricSwitchPortMetricsPathPortMetricsGet, asyncResp)); } inline void handleFabricSwitchPortPathPortGet( const std::shared_ptr& asyncResp, const std::string& fabricId, const std::string& switchId, const std::string& portId, const std::string& portPath, const std::string& serviceName) { asyncResp->res.jsonValue["@odata.type"] = "#Port.v1_4_0.Port"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}", fabricId, switchId, portId); asyncResp->res.jsonValue["Id"] = portId; asyncResp->res.jsonValue["Name"] = std::format("{} {} Port", switchId, portId); nlohmann::json& status = asyncResp->res.jsonValue["Status"]; status["Health"] = resource::Health::OK; status["State"] = resource::State::Enabled; asyncResp->res.jsonValue["Metrics"]["@odata.id"] = boost::urls::format( "/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}/Metrics", fabricId, switchId, portId); dbus::utility::getAllProperties( serviceName, portPath, "xyz.openbmc_project.Inventory.Connector.Port", std::bind_front(afterGetFabricSwitchPortInfo, asyncResp)); } inline void afterHandleFabricSwitchPortPaths( const std::shared_ptr& asyncResp, const std::string& portId, const std::function& callback, const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreeResponse& object) { if (ec) { BMCWEB_LOG_ERROR("DBus response error on GetAssociatedSubTreeById {}", ec); messages::internalError(asyncResp->res); return; } std::string portPath; std::string serviceName; for (const auto& [path, service] : object) { std::string portName = sdbusplus::object_path(path).filename(); if (portName == portId) { portPath = path; if (service.size() != 1) { messages::internalError(asyncResp->res); return; } serviceName = service.begin()->first; break; } } if (portPath.empty()) { messages::resourceNotFound(asyncResp->res, "Port", portId); return; } callback(portPath, serviceName); } inline void getAssociatedPortPath( const std::shared_ptr& asyncResp, const std::string& portId, std::function&& callback, const std::string& switchPath) { std::string associationPath = switchPath + "/connecting"; dbus::utility::getAssociatedSubTree( associationPath, sdbusplus::object_path{"/xyz/openbmc_project/inventory"}, 0, std::array{ "xyz.openbmc_project.Inventory.Connector.Port"}, std::bind_front(afterHandleFabricSwitchPortPaths, asyncResp, portId, std::move(callback))); } inline void handleFabricSwitchPathPortCollection( const std::shared_ptr& asyncResp, const std::string& fabricId, const std::string& switchId, const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& object) { if (ec) { BMCWEB_LOG_ERROR("DBus response error on GetSubTreePaths {}", ec); messages::internalError(asyncResp->res); return; } asyncResp->res.jsonValue["@odata.type"] = "#PortCollection.PortCollection"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( "/redfish/v1/Fabrics/{}/Switches/{}/Ports", fabricId, switchId); asyncResp->res.jsonValue["Name"] = switchId + " Port Collection"; asyncResp->res.jsonValue["Members@odata.count"] = object.size(); nlohmann::json::array_t members; for (const std::string& path : object) { std::string name = sdbusplus::object_path(path).filename(); nlohmann::json::object_t member; member["@odata.id"] = boost::urls::format("/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}", fabricId, switchId, name); members.emplace_back(std::move(member)); } asyncResp->res.jsonValue["Members"] = std::move(members); } inline void getFabricSwitchPortPaths( const std::shared_ptr& asyncResp, const std::string& fabricId, const std::string& switchId, const std::string& switchPath) { std::string associationPath = switchPath + "/connecting"; dbus::utility::getAssociatedSubTreePaths( associationPath, sdbusplus::object_path{"/xyz/openbmc_project/inventory"}, 0, std::array{ "xyz.openbmc_project.Inventory.Connector.Port"}, std::bind_front(handleFabricSwitchPathPortCollection, asyncResp, fabricId, switchId)); } inline void handleFabricSwitchPortPathsSwitchCollection( const std::shared_ptr& asyncResp, const std::string& fabricId, const boost::system::error_code& ec, const dbus::utility::MapperGetSubTreePathsResponse& object) { if (ec) { BMCWEB_LOG_ERROR("DBus response error on GetSubTreePaths {}", ec); messages::internalError(asyncResp->res); return; } asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Fabrics/{}/Switches", fabricId); asyncResp->res.jsonValue["@odata.type"] = "#SwitchCollection.SwitchCollection"; asyncResp->res.jsonValue["Name"] = fabricId + " Switch Collection"; asyncResp->res.jsonValue["Members@odata.count"] = object.size(); nlohmann::json::array_t members; for (const std::string& path : object) { nlohmann::json::object_t member; std::string name = sdbusplus::object_path(path).filename(); member["@odata.id"] = boost::urls::format( "/redfish/v1/Fabrics/{}/Switches/{}", fabricId, name); members.emplace_back(std::move(member)); } asyncResp->res.jsonValue["Members"] = std::move(members); } inline void handleFabricSwitchPortMetricsGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& fabricId, const std::string& switchId, const std::string& portId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } getFabricSwitchPath( asyncResp, fabricId, switchId, std::bind_front( getAssociatedPortPath, asyncResp, portId, std::bind_front(handleFabricSwitchPortPathPortMetricsGet, asyncResp, fabricId, switchId, portId))); } inline void handleFabricSwitchPortGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& fabricId, const std::string& switchId, const std::string& portId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } getFabricSwitchPath( asyncResp, fabricId, switchId, std::bind_front( getAssociatedPortPath, asyncResp, portId, std::bind_front(handleFabricSwitchPortPathPortGet, asyncResp, fabricId, switchId, portId))); } inline void handleFabricSwitchPortsCollectionGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& fabricId, const std::string& switchId) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } getFabricSwitchPath(asyncResp, fabricId, switchId, std::bind_front(getFabricSwitchPortPaths, asyncResp, fabricId, switchId)); } inline void requestRoutesFabricSwitchPort(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Fabrics//Switches//Ports/") .privileges(redfish::privileges::getPortCollection) .methods(boost::beast::http::verb::get)(std::bind_front( handleFabricSwitchPortsCollectionGet, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Fabrics//Switches//Ports//") .privileges(redfish::privileges::getPort) .methods(boost::beast::http::verb::get)( std::bind_front(handleFabricSwitchPortGet, std::ref(app))); BMCWEB_ROUTE( app, "/redfish/v1/Fabrics//Switches//Ports//Metrics/") .privileges(redfish::privileges::getPortMetrics) .methods(boost::beast::http::verb::get)( std::bind_front(handleFabricSwitchPortMetricsGet, std::ref(app))); } } // namespace redfish