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 "fabric.hpp"
10 #include "generated/enums/port.hpp"
11 #include "generated/enums/resource.hpp"
12 #include "http_request.hpp"
13 #include "logging.hpp"
14 #include "query.hpp"
15 #include "registries/privilege_registry.hpp"
16
17 #include <boost/beast/http/verb.hpp>
18 #include <boost/system/error_code.hpp>
19 #include <nlohmann/json.hpp>
20 #include <sdbusplus/message/native_types.hpp>
21 #include <sdbusplus/unpack_properties.hpp>
22
23 #include <array>
24 #include <cstddef>
25 #include <format>
26 #include <functional>
27 #include <memory>
28 #include <optional>
29 #include <string>
30 #include <string_view>
31 #include <utility>
32
33 static constexpr const char* inventoryPath = "/xyz/openbmc_project/inventory";
34 static constexpr std::array<std::string_view, 1> portInterface = {
35 "xyz.openbmc_project.Inventory.Connector.Port"};
36
37 namespace redfish
38 {
dBusSensorPortTypeToRedfish(const std::string & portType)39 inline port::PortType dBusSensorPortTypeToRedfish(const std::string& portType)
40 {
41 if (portType ==
42 "xyz.openbmc_project.Inventory.Connector.Port.PortType.DownstreamPort")
43 {
44 return port::PortType::DownstreamPort;
45 }
46
47 if (portType ==
48 "xyz.openbmc_project.Inventory.Connector.Port.PortType.UpstreamPort")
49 {
50 return port::PortType::UpstreamPort;
51 }
52
53 return port::PortType::Invalid;
54 }
55
dBusSensorPortProtocolToRedfish(const std::string & portProtocol)56 inline std::string dBusSensorPortProtocolToRedfish(
57 const std::string& portProtocol)
58 {
59 if (portProtocol ==
60 "xyz.openbmc_project.Inventory.Connector.Port.PortProtocol.PCIe")
61 {
62 return "PCIe";
63 }
64
65 return "Unknown";
66 }
67
afterGetFabricSwitchPortInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & properties)68 inline void afterGetFabricSwitchPortInfo(
69 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
70 const boost::system::error_code& ec,
71 const dbus::utility::DBusPropertiesMap& properties)
72 {
73 if (ec)
74 {
75 BMCWEB_LOG_ERROR("DBus response error on GetAllProperties {}",
76 ec.value());
77 messages::internalError(asyncResp->res);
78 return;
79 }
80
81 std::optional<uint64_t> speed;
82 std::optional<size_t> width;
83 std::optional<std::string> portType;
84 std::optional<std::string> portProtocol;
85
86 const bool success = sdbusplus::unpackPropertiesNoThrow(
87 dbus_utils::UnpackErrorPrinter(), properties, "Speed", speed, "Width",
88 width, "PortType", portType, "PortProtocol", portProtocol);
89
90 if (!success)
91 {
92 messages::internalError(asyncResp->res);
93 return;
94 }
95
96 if (speed.has_value())
97 {
98 if (*speed != 0 && *speed != std::numeric_limits<uint64_t>::max())
99 {
100 // Convert from bits per second to Gigabits per second (Gbps)
101 static constexpr int gbpsToBps = 1 << 30;
102 const double convertedSpeed =
103 static_cast<double>(*speed) / gbpsToBps;
104
105 asyncResp->res.jsonValue["CurrentSpeedGbps"] = convertedSpeed;
106 }
107 }
108 if (width.has_value())
109 {
110 if (*width != 0 && *width != std::numeric_limits<size_t>::max())
111 {
112 asyncResp->res.jsonValue["ActiveWidth"] = *width;
113 }
114 }
115 if (portType.has_value())
116 {
117 const port::PortType portTypeEnum =
118 dBusSensorPortTypeToRedfish(*portType);
119 if (portTypeEnum != port::PortType::Invalid)
120 {
121 asyncResp->res.jsonValue["PortType"] = portTypeEnum;
122 }
123 }
124 if (portProtocol.has_value())
125 {
126 const std::string portProtocolStr =
127 dBusSensorPortProtocolToRedfish(*portProtocol);
128 if (portProtocolStr != "Unknown")
129 {
130 asyncResp->res.jsonValue["PortProtocol"] = portProtocolStr;
131 }
132 }
133 }
134
handleFabricSwitchPortPathPortMetricsGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,const std::string & portId,const std::string & portPath,const std::string & serviceName)135 inline void handleFabricSwitchPortPathPortMetricsGet(
136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
137 const std::string& fabricId, const std::string& switchId,
138 const std::string& portId, [[maybe_unused]] const std::string& portPath,
139 [[maybe_unused]] const std::string& serviceName)
140 {
141 asyncResp->res.jsonValue["@odata.type"] = "#PortMetrics.v1_3_0.PortMetrics";
142 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
143 "/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}/Metrics", fabricId,
144 switchId, portId);
145 asyncResp->res.jsonValue["Id"] = "Metrics";
146 asyncResp->res.jsonValue["Name"] =
147 std::format("{} {} Port Metrics", switchId, portId);
148 }
149
handleFabricSwitchPortPathPortGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,const std::string & portId,const std::string & portPath,const std::string & serviceName)150 inline void handleFabricSwitchPortPathPortGet(
151 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
152 const std::string& fabricId, const std::string& switchId,
153 const std::string& portId, const std::string& portPath,
154 const std::string& serviceName)
155 {
156 asyncResp->res.jsonValue["@odata.type"] = "#Port.v1_4_0.Port";
157 asyncResp->res.jsonValue["@odata.id"] =
158 boost::urls::format("/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}",
159 fabricId, switchId, portId);
160 asyncResp->res.jsonValue["Id"] = portId;
161 asyncResp->res.jsonValue["Name"] =
162 std::format("{} {} Port", switchId, portId);
163
164 nlohmann::json& status = asyncResp->res.jsonValue["Status"];
165 status["Health"] = resource::Health::OK;
166 status["State"] = resource::State::Enabled;
167
168 asyncResp->res.jsonValue["Metrics"]["@odata.id"] = boost::urls::format(
169 "/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}/Metrics", fabricId,
170 switchId, portId);
171
172 dbus::utility::getAllProperties(
173 serviceName, portPath, std::string{portInterface[0]},
174 std::bind_front(afterGetFabricSwitchPortInfo, asyncResp));
175 }
176
afterHandleFabricSwitchPortPaths(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & portId,const std::function<void (const std::string & portPath,const std::string & serviceName)> & callback,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & object)177 inline void afterHandleFabricSwitchPortPaths(
178 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
179 const std::string& portId,
180 const std::function<void(const std::string& portPath,
181 const std::string& serviceName)>& callback,
182 const boost::system::error_code& ec,
183 const dbus::utility::MapperGetSubTreeResponse& object)
184 {
185 if (ec)
186 {
187 BMCWEB_LOG_ERROR("DBus response error on GetAssociatedSubTreeById {}",
188 ec);
189 messages::internalError(asyncResp->res);
190 return;
191 }
192
193 std::string portPath;
194 std::string serviceName;
195 for (const auto& [path, service] : object)
196 {
197 std::string portName = sdbusplus::message::object_path(path).filename();
198 if (portName == portId)
199 {
200 portPath = path;
201 if (service.size() != 1)
202 {
203 messages::internalError(asyncResp->res);
204 return;
205 }
206 serviceName = service.begin()->first;
207 break;
208 }
209 }
210
211 if (portPath.empty())
212 {
213 messages::resourceNotFound(asyncResp->res, "Port", portId);
214 return;
215 }
216
217 callback(portPath, serviceName);
218 }
219
getAssociatedPortPath(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & portId,std::function<void (const std::string & portPath,const std::string & serviceName)> && callback,const std::string & switchPath)220 inline void getAssociatedPortPath(
221 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
222 const std::string& portId,
223 std::function<void(const std::string& portPath,
224 const std::string& serviceName)>&& callback,
225 const std::string& switchPath)
226 {
227 std::string associationPath = switchPath + "/connecting";
228 dbus::utility::getAssociatedSubTree(
229 associationPath, sdbusplus::message::object_path{inventoryPath}, 0,
230 portInterface,
231 std::bind_front(afterHandleFabricSwitchPortPaths, asyncResp, portId,
232 std::move(callback)));
233 }
234
handleFabricSwitchPathPortCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & object)235 inline void handleFabricSwitchPathPortCollection(
236 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
237 const std::string& fabricId, const std::string& switchId,
238 const boost::system::error_code& ec,
239 const dbus::utility::MapperGetSubTreePathsResponse& object)
240 {
241 if (ec)
242 {
243 BMCWEB_LOG_ERROR("DBus response error on GetSubTreePaths {}", ec);
244 messages::internalError(asyncResp->res);
245 return;
246 }
247
248 asyncResp->res.jsonValue["@odata.type"] = "#PortCollection.PortCollection";
249 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
250 "/redfish/v1/Fabrics/{}/Switches/{}/Ports", fabricId, switchId);
251 asyncResp->res.jsonValue["Name"] = switchId + " Port Collection";
252
253 asyncResp->res.jsonValue["Members@odata.count"] = object.size();
254
255 nlohmann::json::array_t members;
256 for (const std::string& path : object)
257 {
258 std::string name = sdbusplus::message::object_path(path).filename();
259 nlohmann::json::object_t member;
260 member["@odata.id"] =
261 boost::urls::format("/redfish/v1/Fabrics/{}/Switches/{}/Ports/{}",
262 fabricId, switchId, name);
263 members.emplace_back(std::move(member));
264 }
265
266 asyncResp->res.jsonValue["Members"] = std::move(members);
267 }
268
getFabricSwitchPortPaths(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,const std::string & switchPath)269 inline void getFabricSwitchPortPaths(
270 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
271 const std::string& fabricId, const std::string& switchId,
272 const std::string& switchPath)
273 {
274 std::string associationPath = switchPath + "/connecting";
275 dbus::utility::getAssociatedSubTreePaths(
276 associationPath, sdbusplus::message::object_path{inventoryPath}, 0,
277 portInterface,
278 std::bind_front(handleFabricSwitchPathPortCollection, asyncResp,
279 fabricId, switchId));
280 }
281
handleFabricSwitchPortPathsSwitchCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreePathsResponse & object)282 inline void handleFabricSwitchPortPathsSwitchCollection(
283 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
284 const std::string& fabricId, const boost::system::error_code& ec,
285 const dbus::utility::MapperGetSubTreePathsResponse& object)
286 {
287 if (ec)
288 {
289 BMCWEB_LOG_ERROR("DBus response error on GetSubTreePaths {}", ec);
290 messages::internalError(asyncResp->res);
291 return;
292 }
293
294 asyncResp->res.jsonValue["@odata.id"] =
295 boost::urls::format("/redfish/v1/Fabrics/{}/Switches", fabricId);
296 asyncResp->res.jsonValue["@odata.type"] =
297 "#SwitchCollection.SwitchCollection";
298 asyncResp->res.jsonValue["Name"] = fabricId + " Switch Collection";
299
300 asyncResp->res.jsonValue["Members@odata.count"] = object.size();
301
302 nlohmann::json::array_t members;
303 for (const std::string& path : object)
304 {
305 nlohmann::json::object_t member;
306 std::string name = sdbusplus::message::object_path(path).filename();
307 member["@odata.id"] = boost::urls::format(
308 "/redfish/v1/Fabrics/{}/Switches/{}", fabricId, name);
309 members.emplace_back(std::move(member));
310 }
311
312 asyncResp->res.jsonValue["Members"] = std::move(members);
313 }
314
handleFabricSwitchPortMetricsGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,const std::string & portId)315 inline void handleFabricSwitchPortMetricsGet(
316 App& app, const crow::Request& req,
317 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
318 const std::string& fabricId, const std::string& switchId,
319 const std::string& portId)
320 {
321 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
322 {
323 return;
324 }
325
326 getFabricSwitchPath(
327 asyncResp, fabricId, switchId,
328 std::bind_front(
329 getAssociatedPortPath, asyncResp, portId,
330 std::bind_front(handleFabricSwitchPortPathPortMetricsGet, asyncResp,
331 fabricId, switchId, portId)));
332 }
333
handleFabricSwitchPortGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId,const std::string & portId)334 inline void handleFabricSwitchPortGet(
335 App& app, const crow::Request& req,
336 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
337 const std::string& fabricId, const std::string& switchId,
338 const std::string& portId)
339 {
340 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
341 {
342 return;
343 }
344
345 getFabricSwitchPath(
346 asyncResp, fabricId, switchId,
347 std::bind_front(
348 getAssociatedPortPath, asyncResp, portId,
349 std::bind_front(handleFabricSwitchPortPathPortGet, asyncResp,
350 fabricId, switchId, portId)));
351 }
352
handleFabricSwitchPortsCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & fabricId,const std::string & switchId)353 inline void handleFabricSwitchPortsCollectionGet(
354 App& app, const crow::Request& req,
355 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
356 const std::string& fabricId, const std::string& switchId)
357 {
358 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
359 {
360 return;
361 }
362
363 getFabricSwitchPath(asyncResp, fabricId, switchId,
364 std::bind_front(getFabricSwitchPortPaths, asyncResp,
365 fabricId, switchId));
366 }
367
requestRoutesFabricSwitchPort(App & app)368 inline void requestRoutesFabricSwitchPort(App& app)
369 {
370 BMCWEB_ROUTE(app, "/redfish/v1/Fabrics/<str>/Switches/<str>/Ports/")
371 .privileges(redfish::privileges::getPortCollection)
372 .methods(boost::beast::http::verb::get)(std::bind_front(
373 handleFabricSwitchPortsCollectionGet, std::ref(app)));
374
375 BMCWEB_ROUTE(app, "/redfish/v1/Fabrics/<str>/Switches/<str>/Ports/<str>/")
376 .privileges(redfish::privileges::getPort)
377 .methods(boost::beast::http::verb::get)(
378 std::bind_front(handleFabricSwitchPortGet, std::ref(app)));
379
380 BMCWEB_ROUTE(
381 app, "/redfish/v1/Fabrics/<str>/Switches/<str>/Ports/<str>/Metrics/")
382 .privileges(redfish::privileges::getPortMetrics)
383 .methods(boost::beast::http::verb::get)(
384 std::bind_front(handleFabricSwitchPortMetricsGet, std::ref(app)));
385 }
386 } // namespace redfish
387