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_singleton.hpp"
8 #include "dbus_utility.hpp"
9 #include "error_messages.hpp"
10 #include "generated/enums/resource.hpp"
11 #include "http_request.hpp"
12 #include "http_response.hpp"
13 #include "logging.hpp"
14 #include "query.hpp"
15 #include "registries/privilege_registry.hpp"
16 #include "utils/assembly_utils.hpp"
17 #include "utils/asset_utils.hpp"
18 #include "utils/chassis_utils.hpp"
19 #include "utils/dbus_utils.hpp"
20
21 #include <boost/beast/http/verb.hpp>
22 #include <boost/system/error_code.hpp>
23 #include <boost/url/format.hpp>
24 #include <nlohmann/json.hpp>
25 #include <sdbusplus/asio/property.hpp>
26 #include <sdbusplus/unpack_properties.hpp>
27
28 #include <cstddef>
29 #include <functional>
30 #include <memory>
31 #include <ranges>
32 #include <string>
33 #include <string_view>
34 #include <vector>
35
36 namespace redfish
37 {
38
39 /**
40 * @brief Get Location code for the given assembly.
41 * @param[in] asyncResp - Shared pointer for asynchronous calls.
42 * @param[in] serviceName - Service in which the assembly is hosted.
43 * @param[in] assembly - Assembly object.
44 * @param[in] assemblyJsonPtr - json-keyname on the assembly list output.
45 * @return None.
46 */
getAssemblyLocationCode(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & serviceName,const std::string & assembly,const nlohmann::json::json_pointer & assemblyJsonPtr)47 inline void getAssemblyLocationCode(
48 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
49 const std::string& serviceName, const std::string& assembly,
50 const nlohmann::json::json_pointer& assemblyJsonPtr)
51 {
52 sdbusplus::asio::getProperty<std::string>(
53 *crow::connections::systemBus, serviceName, assembly,
54 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
55 [asyncResp, assembly, assemblyJsonPtr](
56 const boost::system::error_code& ec, const std::string& value) {
57 if (ec)
58 {
59 if (ec.value() != EBADR)
60 {
61 BMCWEB_LOG_ERROR("DBUS response error: {} for assembly {}",
62 ec.value(), assembly);
63 messages::internalError(asyncResp->res);
64 }
65 return;
66 }
67
68 asyncResp->res.jsonValue[assemblyJsonPtr]["Location"]
69 ["PartLocation"]["ServiceLabel"] = value;
70 });
71 }
72
getAssemblyState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const auto & serviceName,const auto & assembly,const nlohmann::json::json_pointer & assemblyJsonPtr)73 inline void getAssemblyState(
74 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
75 const auto& serviceName, const auto& assembly,
76 const nlohmann::json::json_pointer& assemblyJsonPtr)
77 {
78 asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["State"] =
79 resource::State::Enabled;
80
81 dbus::utility::getProperty<bool>(
82 serviceName, assembly, "xyz.openbmc_project.Inventory.Item", "Present",
83 [asyncResp, assemblyJsonPtr,
84 assembly](const boost::system::error_code& ec, const bool value) {
85 if (ec)
86 {
87 if (ec.value() != EBADR)
88 {
89 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value());
90 messages::internalError(asyncResp->res);
91 }
92 return;
93 }
94
95 if (!value)
96 {
97 asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["State"] =
98 resource::State::Absent;
99 }
100 });
101 }
102
getAssemblyHealth(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const auto & serviceName,const auto & assembly,const nlohmann::json::json_pointer & assemblyJsonPtr)103 void getAssemblyHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
104 const auto& serviceName, const auto& assembly,
105 const nlohmann::json::json_pointer& assemblyJsonPtr)
106 {
107 asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["Health"] =
108 resource::Health::OK;
109
110 dbus::utility::getProperty<bool>(
111 serviceName, assembly,
112 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional",
113 [asyncResp, assemblyJsonPtr](const boost::system::error_code& ec,
114 bool functional) {
115 if (ec)
116 {
117 if (ec.value() != EBADR)
118 {
119 BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
120 messages::internalError(asyncResp->res);
121 }
122 return;
123 }
124
125 if (!functional)
126 {
127 asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["Health"] =
128 resource::Health::Critical;
129 }
130 });
131 }
132
afterGetDbusObject(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & assembly,const nlohmann::json::json_pointer & assemblyJsonPtr,const boost::system::error_code & ec,const dbus::utility::MapperGetObject & object)133 inline void afterGetDbusObject(
134 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
135 const std::string& assembly,
136 const nlohmann::json::json_pointer& assemblyJsonPtr,
137 const boost::system::error_code& ec,
138 const dbus::utility::MapperGetObject& object)
139 {
140 if (ec)
141 {
142 BMCWEB_LOG_ERROR("DBUS response error : {} for assembly {}", ec.value(),
143 assembly);
144 messages::internalError(asyncResp->res);
145 return;
146 }
147
148 for (const auto& [serviceName, interfaceList] : object)
149 {
150 for (const auto& interface : interfaceList)
151 {
152 if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
153 {
154 asset_utils::getAssetInfo(asyncResp, serviceName, assembly,
155 assemblyJsonPtr, true, false);
156 }
157 else if (interface ==
158 "xyz.openbmc_project.Inventory.Decorator.LocationCode")
159 {
160 getAssemblyLocationCode(asyncResp, serviceName, assembly,
161 assemblyJsonPtr);
162 }
163 else if (interface == "xyz.openbmc_project.Inventory.Item")
164 {
165 getAssemblyState(asyncResp, serviceName, assembly,
166 assemblyJsonPtr);
167 }
168 else if (interface ==
169 "xyz.openbmc_project.State.Decorator.OperationalStatus")
170 {
171 getAssemblyHealth(asyncResp, serviceName, assembly,
172 assemblyJsonPtr);
173 }
174 }
175 }
176 }
177
178 /**
179 * @brief Get properties for the assemblies associated to given chassis
180 * @param[in] asyncResp - Shared pointer for asynchronous calls.
181 * @param[in] chassisId - Chassis the assemblies are associated with.
182 * @param[in] assemblies - list of all the assemblies associated with the
183 * chassis.
184 * @return None.
185 */
getAssemblyProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::vector<std::string> & assemblies)186 inline void getAssemblyProperties(
187 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
188 const std::string& chassisId, const std::vector<std::string>& assemblies)
189 {
190 BMCWEB_LOG_DEBUG("Get properties for assembly associated");
191
192 std::size_t assemblyIndex = 0;
193 for (const std::string& assembly : assemblies)
194 {
195 nlohmann::json::object_t item;
196 item["@odata.type"] = "#Assembly.v1_5_1.AssemblyData";
197 item["@odata.id"] = boost::urls::format(
198 "/redfish/v1/Chassis/{}/Assembly#/Assemblies/{}", chassisId,
199 std::to_string(assemblyIndex));
200 item["MemberId"] = std::to_string(assemblyIndex);
201 item["Name"] = sdbusplus::message::object_path(assembly).filename();
202
203 asyncResp->res.jsonValue["Assemblies"].emplace_back(item);
204
205 nlohmann::json::json_pointer assemblyJsonPtr(
206 "/Assemblies/" + std::to_string(assemblyIndex));
207
208 dbus::utility::getDbusObject(
209 assembly, assemblyInterfaces,
210 std::bind_front(afterGetDbusObject, asyncResp, assembly,
211 assemblyJsonPtr));
212
213 nlohmann::json& assemblyArray = asyncResp->res.jsonValue["Assemblies"];
214 asyncResp->res.jsonValue["Assemblies@odata.count"] =
215 assemblyArray.size();
216
217 assemblyIndex++;
218 }
219 }
220
afterHandleChassisAssemblyGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisID,const boost::system::error_code & ec,const std::vector<std::string> & assemblyList)221 inline void afterHandleChassisAssemblyGet(
222 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
223 const std::string& chassisID, const boost::system::error_code& ec,
224 const std::vector<std::string>& assemblyList)
225 {
226 if (ec)
227 {
228 BMCWEB_LOG_WARNING("Chassis {} not found", chassisID);
229 messages::resourceNotFound(asyncResp->res, "Chassis", chassisID);
230 return;
231 }
232
233 asyncResp->res.addHeader(
234 boost::beast::http::field::link,
235 "</redfish/v1/JsonSchemas/Assembly/Assembly.json>; rel=describedby");
236
237 asyncResp->res.jsonValue["@odata.type"] = "#Assembly.v1_5_1.Assembly";
238 asyncResp->res.jsonValue["@odata.id"] =
239 boost::urls::format("/redfish/v1/Chassis/{}/Assembly", chassisID);
240 asyncResp->res.jsonValue["Name"] = "Assembly Collection";
241 asyncResp->res.jsonValue["Id"] = "Assembly";
242
243 asyncResp->res.jsonValue["Assemblies"] = nlohmann::json::array();
244 asyncResp->res.jsonValue["Assemblies@odata.count"] = 0;
245
246 if (!assemblyList.empty())
247 {
248 getAssemblyProperties(asyncResp, chassisID, assemblyList);
249 }
250 }
251
252 /**
253 * @param[in] asyncResp - Shared pointer for asynchronous calls.
254 * @param[in] chassisID - Chassis to which the assemblies are
255 * associated.
256 *
257 * @return None.
258 */
handleChassisAssemblyGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisID)259 inline void handleChassisAssemblyGet(
260 App& app, const crow::Request& req,
261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
262 const std::string& chassisID)
263 {
264 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
265 {
266 return;
267 }
268
269 BMCWEB_LOG_DEBUG("Get chassis Assembly");
270 assembly_utils::getChassisAssembly(
271 asyncResp, chassisID,
272 std::bind_front(afterHandleChassisAssemblyGet, asyncResp, chassisID));
273 }
274
handleChassisAssemblyHead(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisID)275 inline void handleChassisAssemblyHead(
276 crow::App& app, const crow::Request& req,
277 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
278 const std::string& chassisID)
279 {
280 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
281 {
282 return;
283 }
284
285 assembly_utils::getChassisAssembly(
286 asyncResp, chassisID,
287 [asyncResp,
288 chassisID](const boost::system::error_code& ec,
289 const std::vector<std::string>& /*assemblyList*/) {
290 if (ec)
291 {
292 BMCWEB_LOG_WARNING("Chassis {} not found", chassisID);
293 messages::resourceNotFound(asyncResp->res, "Chassis",
294 chassisID);
295 return;
296 }
297 asyncResp->res.addHeader(
298 boost::beast::http::field::link,
299 "</redfish/v1/JsonSchemas/Assembly.json>; rel=describedby");
300 });
301 }
302
requestRoutesAssembly(App & app)303 inline void requestRoutesAssembly(App& app)
304 {
305 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Assembly/")
306 .privileges(redfish::privileges::headAssembly)
307 .methods(boost::beast::http::verb::head)(
308 std::bind_front(handleChassisAssemblyHead, std::ref(app)));
309
310 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Assembly/")
311 .privileges(redfish::privileges::getAssembly)
312 .methods(boost::beast::http::verb::get)(
313 std::bind_front(handleChassisAssemblyGet, std::ref(app)));
314 }
315
316 } // namespace redfish
317