xref: /openbmc/bmcweb/features/redfish/lib/fabric.hpp (revision 3132dacead062fa63c070fc7f404f8cf93df43a0)
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