1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "bmcweb_config.h" 6 7 #include "app.hpp" 8 #include "async_resp.hpp" 9 #include "dbus_utility.hpp" 10 #include "error_messages.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 #include "utils/collection.hpp" 17 #include "utils/dbus_utils.hpp" 18 19 #include <asm-generic/errno.h> 20 21 #include <boost/beast/http/field.hpp> 22 #include <boost/beast/http/verb.hpp> 23 #include <boost/system/error_code.hpp> 24 #include <boost/url/format.hpp> 25 #include <sdbusplus/unpack_properties.hpp> 26 27 #include <array> 28 #include <functional> 29 #include <memory> 30 #include <string> 31 #include <string_view> 32 #include <utility> 33 34 namespace redfish 35 { 36 37 inline void getFabricAdapterLocation( 38 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 39 const std::string& serviceName, const std::string& fabricAdapterPath) 40 { 41 dbus::utility::getProperty<std::string>( 42 serviceName, fabricAdapterPath, 43 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 44 [asyncResp](const boost::system::error_code& ec, 45 const std::string& property) { 46 if (ec) 47 { 48 if (ec.value() != EBADR) 49 { 50 BMCWEB_LOG_ERROR("DBUS response error for Location"); 51 messages::internalError(asyncResp->res); 52 } 53 return; 54 } 55 56 asyncResp->res 57 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 58 property; 59 }); 60 } 61 62 inline void getFabricAdapterAsset( 63 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 64 const std::string& serviceName, const std::string& fabricAdapterPath) 65 { 66 dbus::utility::getAllProperties( 67 serviceName, fabricAdapterPath, 68 "xyz.openbmc_project.Inventory.Decorator.Asset", 69 [fabricAdapterPath, asyncResp{asyncResp}]( 70 const boost::system::error_code& ec, 71 const dbus::utility::DBusPropertiesMap& propertiesList) { 72 if (ec) 73 { 74 if (ec.value() != EBADR) 75 { 76 BMCWEB_LOG_ERROR("DBUS response error for Properties"); 77 messages::internalError(asyncResp->res); 78 } 79 return; 80 } 81 82 const std::string* serialNumber = nullptr; 83 const std::string* model = nullptr; 84 const std::string* partNumber = nullptr; 85 const std::string* sparePartNumber = nullptr; 86 87 const bool success = sdbusplus::unpackPropertiesNoThrow( 88 dbus_utils::UnpackErrorPrinter(), propertiesList, 89 "SerialNumber", serialNumber, "Model", model, "PartNumber", 90 partNumber, "SparePartNumber", sparePartNumber); 91 92 if (!success) 93 { 94 messages::internalError(asyncResp->res); 95 return; 96 } 97 98 if (serialNumber != nullptr) 99 { 100 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 101 } 102 103 if (model != nullptr) 104 { 105 asyncResp->res.jsonValue["Model"] = *model; 106 } 107 108 if (partNumber != nullptr) 109 { 110 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 111 } 112 113 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 114 { 115 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 116 } 117 }); 118 } 119 120 inline void getFabricAdapterState( 121 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 122 const std::string& serviceName, const std::string& fabricAdapterPath) 123 { 124 dbus::utility::getProperty<bool>( 125 serviceName, fabricAdapterPath, "xyz.openbmc_project.Inventory.Item", 126 "Present", 127 [asyncResp](const boost::system::error_code& ec, const bool present) { 128 if (ec) 129 { 130 if (ec.value() != EBADR) 131 { 132 BMCWEB_LOG_ERROR("DBUS response error for State"); 133 messages::internalError(asyncResp->res); 134 } 135 return; 136 } 137 138 if (!present) 139 { 140 asyncResp->res.jsonValue["Status"]["State"] = 141 resource::State::Absent; 142 } 143 }); 144 } 145 146 inline void getFabricAdapterHealth( 147 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 148 const std::string& serviceName, const std::string& fabricAdapterPath) 149 { 150 dbus::utility::getProperty<bool>( 151 serviceName, fabricAdapterPath, 152 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 153 [asyncResp](const boost::system::error_code& ec, 154 const bool functional) { 155 if (ec) 156 { 157 if (ec.value() != EBADR) 158 { 159 BMCWEB_LOG_ERROR("DBUS response error for Health"); 160 messages::internalError(asyncResp->res); 161 } 162 return; 163 } 164 165 if (!functional) 166 { 167 asyncResp->res.jsonValue["Status"]["Health"] = 168 resource::Health::Critical; 169 } 170 }); 171 } 172 173 inline void doAdapterGet( 174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 175 const std::string& systemName, const std::string& adapterId, 176 const std::string& fabricAdapterPath, const std::string& serviceName) 177 { 178 asyncResp->res.addHeader( 179 boost::beast::http::field::link, 180 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 181 asyncResp->res.jsonValue["@odata.type"] = 182 "#FabricAdapter.v1_4_0.FabricAdapter"; 183 asyncResp->res.jsonValue["Name"] = "Fabric Adapter"; 184 asyncResp->res.jsonValue["Id"] = adapterId; 185 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 186 "/redfish/v1/Systems/{}/FabricAdapters/{}", systemName, adapterId); 187 188 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 189 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 190 191 getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath); 192 getFabricAdapterAsset(asyncResp, serviceName, fabricAdapterPath); 193 getFabricAdapterState(asyncResp, serviceName, fabricAdapterPath); 194 getFabricAdapterHealth(asyncResp, serviceName, fabricAdapterPath); 195 } 196 197 inline void afterGetValidFabricAdapterPath( 198 const std::string& adapterId, 199 std::function<void(const boost::system::error_code&, 200 const std::string& fabricAdapterPath, 201 const std::string& serviceName)>& callback, 202 const boost::system::error_code& ec, 203 const dbus::utility::MapperGetSubTreeResponse& subtree) 204 { 205 std::string fabricAdapterPath; 206 std::string serviceName; 207 if (ec) 208 { 209 callback(ec, fabricAdapterPath, serviceName); 210 return; 211 } 212 213 for (const auto& [adapterPath, serviceMap] : subtree) 214 { 215 std::string fabricAdapterName = 216 sdbusplus::message::object_path(adapterPath).filename(); 217 if (fabricAdapterName == adapterId) 218 { 219 fabricAdapterPath = adapterPath; 220 serviceName = serviceMap.begin()->first; 221 break; 222 } 223 } 224 callback(ec, fabricAdapterPath, serviceName); 225 } 226 227 inline void getValidFabricAdapterPath( 228 const std::string& adapterId, 229 std::function<void(const boost::system::error_code& ec, 230 const std::string& fabricAdapterPath, 231 const std::string& serviceName)>&& callback) 232 { 233 constexpr std::array<std::string_view, 1> interfaces{ 234 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 235 dbus::utility::getSubTree("/xyz/openbmc_project/inventory", 0, interfaces, 236 std::bind_front(afterGetValidFabricAdapterPath, 237 adapterId, std::move(callback))); 238 } 239 240 inline void afterHandleFabricAdapterGet( 241 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 242 const std::string& systemName, const std::string& adapterId, 243 const boost::system::error_code& ec, const std::string& fabricAdapterPath, 244 const std::string& serviceName) 245 { 246 if (ec) 247 { 248 if (ec.value() == boost::system::errc::io_error) 249 { 250 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 251 adapterId); 252 return; 253 } 254 255 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 256 messages::internalError(asyncResp->res); 257 return; 258 } 259 if (fabricAdapterPath.empty() || serviceName.empty()) 260 { 261 BMCWEB_LOG_WARNING("Adapter not found"); 262 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 263 return; 264 } 265 doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath, 266 serviceName); 267 } 268 269 inline void handleFabricAdapterGet( 270 App& app, const crow::Request& req, 271 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 272 const std::string& systemName, const std::string& adapterId) 273 { 274 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 275 { 276 return; 277 } 278 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 279 { 280 // Option currently returns no systems. TBD 281 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 282 systemName); 283 return; 284 } 285 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 286 { 287 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 288 systemName); 289 return; 290 } 291 getValidFabricAdapterPath( 292 adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp, 293 systemName, adapterId)); 294 } 295 296 inline void handleFabricAdapterCollectionGet( 297 crow::App& app, const crow::Request& req, 298 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 299 const std::string& systemName) 300 { 301 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 302 { 303 return; 304 } 305 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 306 { 307 // Option currently returns no systems. TBD 308 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 309 systemName); 310 return; 311 } 312 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 313 { 314 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 315 systemName); 316 return; 317 } 318 319 asyncResp->res.addHeader( 320 boost::beast::http::field::link, 321 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 322 asyncResp->res.jsonValue["@odata.type"] = 323 "#FabricAdapterCollection.FabricAdapterCollection"; 324 asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection"; 325 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 326 "/redfish/v1/Systems/{}/FabricAdapters", systemName); 327 328 constexpr std::array<std::string_view, 1> interfaces{ 329 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 330 collection_util::getCollectionMembers( 331 asyncResp, 332 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters", 333 BMCWEB_REDFISH_SYSTEM_URI_NAME), 334 interfaces, "/xyz/openbmc_project/inventory"); 335 } 336 337 inline void handleFabricAdapterCollectionHead( 338 crow::App& app, const crow::Request& req, 339 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 340 const std::string& systemName) 341 { 342 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 343 { 344 return; 345 } 346 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 347 { 348 // Option currently returns no systems. TBD 349 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 350 systemName); 351 return; 352 } 353 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 354 { 355 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 356 systemName); 357 return; 358 } 359 asyncResp->res.addHeader( 360 boost::beast::http::field::link, 361 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 362 } 363 364 inline void afterHandleFabricAdapterHead( 365 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 366 const std::string& adapterId, const boost::system::error_code& ec, 367 const std::string& fabricAdapterPath, const std::string& serviceName) 368 { 369 if (ec) 370 { 371 if (ec.value() == boost::system::errc::io_error) 372 { 373 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 374 adapterId); 375 return; 376 } 377 378 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 379 messages::internalError(asyncResp->res); 380 return; 381 } 382 if (fabricAdapterPath.empty() || serviceName.empty()) 383 { 384 BMCWEB_LOG_WARNING("Adapter not found"); 385 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 386 return; 387 } 388 asyncResp->res.addHeader( 389 boost::beast::http::field::link, 390 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 391 } 392 393 inline void handleFabricAdapterHead( 394 crow::App& app, const crow::Request& req, 395 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 396 const std::string& systemName, const std::string& adapterId) 397 { 398 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 399 { 400 return; 401 } 402 403 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 404 { 405 // Option currently returns no systems. TBD 406 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 407 systemName); 408 return; 409 } 410 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 411 { 412 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 413 systemName); 414 return; 415 } 416 getValidFabricAdapterPath( 417 adapterId, 418 std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId)); 419 } 420 421 inline void requestRoutesFabricAdapterCollection(App& app) 422 { 423 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 424 .privileges(redfish::privileges::getFabricAdapterCollection) 425 .methods(boost::beast::http::verb::get)( 426 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app))); 427 428 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 429 .privileges(redfish::privileges::headFabricAdapterCollection) 430 .methods(boost::beast::http::verb::head)( 431 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app))); 432 } 433 434 inline void requestRoutesFabricAdapters(App& app) 435 { 436 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 437 .privileges(redfish::privileges::getFabricAdapter) 438 .methods(boost::beast::http::verb::get)( 439 std::bind_front(handleFabricAdapterGet, std::ref(app))); 440 441 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 442 .privileges(redfish::privileges::headFabricAdapter) 443 .methods(boost::beast::http::verb::head)( 444 std::bind_front(handleFabricAdapterHead, std::ref(app))); 445 } 446 } // namespace redfish 447