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