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
handleFabricSwitchPathSwitchGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,const std::string & switchPath)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
handleFabricSwitchPaths(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & switchId,const std::function<void (const std::string & switchPath)> & callback,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & object)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
getFabricSwitchPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,std::function<void (const std::string & switchPath)> && callback)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
handleFabricSwitchGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId)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
handleFabricSwitchCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId)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
handleFabricGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId)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
handleFabricCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
requestRoutesFabrics(App & app)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