1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "app.hpp" 6 #include "async_resp.hpp" 7 #include "dbus_utility.hpp" 8 #include "error_messages.hpp" 9 #include "generated/enums/resource.hpp" 10 #include "http_request.hpp" 11 #include "logging.hpp" 12 #include "query.hpp" 13 #include "registries/privilege_registry.hpp" 14 #include "utils/collection.hpp" 15 16 #include <boost/beast/http/verb.hpp> 17 #include <boost/system/error_code.hpp> 18 #include <boost/url/url.hpp> 19 20 #include <array> 21 #include <format> 22 #include <functional> 23 #include <memory> 24 #include <string> 25 #include <string_view> 26 #include <utility> 27 28 static constexpr std::array<std::string_view, 1> switchInterfaces = { 29 "xyz.openbmc_project.Inventory.Item.PCIeSwitch"}; 30 31 namespace redfish 32 { 33 34 inline void handleFabricSwitchPathSwitchGet( 35 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 36 const std::string& fabricId, const std::string& switchId, 37 [[maybe_unused]] const std::string& switchPath) 38 { 39 asyncResp->res.jsonValue["@odata.type"] = "#Switch.v1_7_0.Switch"; 40 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 41 "/redfish/v1/Fabrics/{}/Switches/{}", fabricId, switchId); 42 asyncResp->res.jsonValue["Id"] = switchId; 43 asyncResp->res.jsonValue["Name"] = switchId; 44 45 nlohmann::json& status = asyncResp->res.jsonValue["Status"]; 46 status["Health"] = resource::Health::OK; 47 status["State"] = resource::State::Enabled; 48 49 asyncResp->res.jsonValue["Ports"]["@odata.id"] = boost::urls::format( 50 "/redfish/v1/Fabrics/{}/Switches/{}/Ports", fabricId, switchId); 51 } 52 53 inline void handleFabricSwitchPaths( 54 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 55 const std::string& switchId, 56 const std::function<void(const std::string& switchPath)>& callback, 57 const boost::system::error_code& ec, 58 const dbus::utility::MapperGetSubTreePathsResponse& object) 59 { 60 if (ec) 61 { 62 if (ec.value() == boost::system::errc::io_error || ec.value() == EBADR) 63 { 64 BMCWEB_LOG_DEBUG("Switch resource {} not found", switchId); 65 messages::resourceNotFound(asyncResp->res, "Switch", switchId); 66 return; 67 } 68 69 BMCWEB_LOG_ERROR("DBus response error on GetSubTreePaths {}", ec); 70 messages::internalError(asyncResp->res); 71 return; 72 } 73 74 std::string switchPath; 75 76 for (const auto& path : object) 77 { 78 std::string switchName = 79 sdbusplus::message::object_path(path).filename(); 80 if (switchName == switchId) 81 { 82 if (!switchPath.empty()) 83 { 84 BMCWEB_LOG_ERROR("Multiple Switch resources found for {}", 85 switchId); 86 messages::internalError(asyncResp->res); 87 return; 88 } 89 90 switchPath = path; 91 } 92 } 93 94 if (!switchPath.empty()) 95 { 96 callback(switchPath); 97 return; 98 } 99 100 messages::resourceNotFound(asyncResp->res, "Switch", switchId); 101 } 102 103 inline void getFabricSwitchPath( 104 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 105 const std::string& fabricId, const std::string& switchId, 106 std::function<void(const std::string& switchPath)>&& callback) 107 { 108 if (fabricId != BMCWEB_REDFISH_FABRIC_URI_NAME) 109 { 110 messages::resourceNotFound(asyncResp->res, "Fabric", fabricId); 111 return; 112 } 113 114 dbus::utility::getSubTreePaths( 115 "/xyz/openbmc_project/inventory", 0, switchInterfaces, 116 std::bind_front(handleFabricSwitchPaths, asyncResp, switchId, 117 std::move(callback))); 118 } 119 120 inline void handleFabricSwitchGet( 121 crow::App& app, const crow::Request& req, 122 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 123 const std::string& fabricId, const std::string& switchId) 124 { 125 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 126 { 127 return; 128 } 129 130 getFabricSwitchPath(asyncResp, fabricId, switchId, 131 std::bind_front(handleFabricSwitchPathSwitchGet, 132 asyncResp, fabricId, switchId)); 133 } 134 135 inline void handleFabricSwitchCollectionGet( 136 crow::App& app, const crow::Request& req, 137 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 138 const std::string& fabricId) 139 { 140 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 141 { 142 return; 143 } 144 145 if (fabricId != BMCWEB_REDFISH_FABRIC_URI_NAME) 146 { 147 messages::resourceNotFound(asyncResp->res, "Fabric", fabricId); 148 return; 149 } 150 151 const auto switchUrl = 152 boost::urls::format("/redfish/v1/Fabrics/{}/Switches", fabricId); 153 154 asyncResp->res.jsonValue["@odata.id"] = switchUrl; 155 asyncResp->res.jsonValue["@odata.type"] = 156 "#SwitchCollection.SwitchCollection"; 157 asyncResp->res.jsonValue["Name"] = fabricId + " Switch Collection"; 158 159 asyncResp->res.jsonValue["@odata.id"] = 160 boost::urls::format("/redfish/v1/Fabrics/{}/Switches", fabricId); 161 asyncResp->res.jsonValue["@odata.type"] = 162 "#SwitchCollection.SwitchCollection"; 163 asyncResp->res.jsonValue["Name"] = fabricId + " Switch Collection"; 164 165 collection_util::getCollectionMembers( 166 asyncResp, switchUrl, switchInterfaces, 167 "/xyz/openbmc_project/inventory"); 168 } 169 170 inline void handleFabricGet(crow::App& app, const crow::Request& req, 171 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 172 const std::string& fabricId) 173 { 174 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 175 { 176 return; 177 } 178 179 if (fabricId != BMCWEB_REDFISH_FABRIC_URI_NAME) 180 { 181 messages::resourceNotFound(asyncResp->res, "Fabric", fabricId); 182 return; 183 } 184 185 asyncResp->res.jsonValue["@odata.type"] = "#Fabric.v1_2_0.Fabric"; 186 asyncResp->res.jsonValue["@odata.id"] = 187 boost::urls::format("/redfish/v1/Fabrics/{}", fabricId); 188 asyncResp->res.jsonValue["Id"] = fabricId; 189 asyncResp->res.jsonValue["Name"] = fabricId + " Fabric"; 190 191 asyncResp->res.jsonValue["Switches"]["@odata.id"] = 192 boost::urls::format("/redfish/v1/Fabrics/{}/Switches", fabricId); 193 } 194 195 inline void handleFabricCollectionGet( 196 crow::App& app, const crow::Request& req, 197 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 198 { 199 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 200 { 201 return; 202 } 203 204 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Fabrics"; 205 asyncResp->res.jsonValue["@odata.type"] = 206 "#FabricCollection.FabricCollection"; 207 208 asyncResp->res.jsonValue["Name"] = "Fabric Collection"; 209 210 asyncResp->res.jsonValue["Members@odata.count"] = 1; 211 212 nlohmann::json::array_t members; 213 nlohmann::json::object_t member; 214 member["@odata.id"] = boost::urls::format("/redfish/v1/Fabrics/{}", 215 BMCWEB_REDFISH_FABRIC_URI_NAME); 216 members.emplace_back(std::move(member)); 217 218 asyncResp->res.jsonValue["Members"] = std::move(members); 219 } 220 221 inline void requestRoutesFabrics(App& app) 222 { 223 // The Fabric resource is designed so that each BMC will have only one 224 // Fabric resource. 225 BMCWEB_ROUTE(app, "/redfish/v1/Fabrics/") 226 .privileges(redfish::privileges::getFabricCollection) 227 .methods(boost::beast::http::verb::get)( 228 std::bind_front(handleFabricCollectionGet, std::ref(app))); 229 230 BMCWEB_ROUTE(app, "/redfish/v1/Fabrics/<str>/") 231 .privileges(redfish::privileges::getFabric) 232 .methods(boost::beast::http::verb::get)( 233 std::bind_front(handleFabricGet, std::ref(app))); 234 235 BMCWEB_ROUTE(app, "/redfish/v1/Fabrics/<str>/Switches/") 236 .privileges(redfish::privileges::getSwitchCollection) 237 .methods(boost::beast::http::verb::get)( 238 std::bind_front(handleFabricSwitchCollectionGet, std::ref(app))); 239 240 BMCWEB_ROUTE(app, "/redfish/v1/Fabrics/<str>/Switches/<str>/") 241 .privileges(redfish::privileges::getSwitch) 242 .methods(boost::beast::http::verb::get)( 243 std::bind_front(handleFabricSwitchGet, std::ref(app))); 244 } 245 } // namespace redfish 246