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 */ 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 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 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 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 */ 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 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 */ 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 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 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