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 47 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 48 property; 49 }); 50 } 51 52 inline void getFabricAdapterAsset( 53 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 54 const std::string& serviceName, 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, 79 "SerialNumber", serialNumber, "Model", model, "PartNumber", 80 partNumber, "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 getFabricAdapterState( 111 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 112 const std::string& serviceName, const std::string& fabricAdapterPath) 113 { 114 sdbusplus::asio::getProperty<bool>( 115 *crow::connections::systemBus, serviceName, fabricAdapterPath, 116 "xyz.openbmc_project.Inventory.Item", "Present", 117 [asyncResp](const boost::system::error_code& ec, const bool present) { 118 if (ec) 119 { 120 if (ec.value() != EBADR) 121 { 122 BMCWEB_LOG_ERROR("DBUS response error for State"); 123 messages::internalError(asyncResp->res); 124 } 125 return; 126 } 127 128 if (!present) 129 { 130 asyncResp->res.jsonValue["Status"]["State"] = 131 resource::State::Absent; 132 } 133 }); 134 } 135 136 inline void getFabricAdapterHealth( 137 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 138 const std::string& serviceName, const std::string& fabricAdapterPath) 139 { 140 sdbusplus::asio::getProperty<bool>( 141 *crow::connections::systemBus, serviceName, fabricAdapterPath, 142 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 143 [asyncResp](const boost::system::error_code& ec, 144 const bool functional) { 145 if (ec) 146 { 147 if (ec.value() != EBADR) 148 { 149 BMCWEB_LOG_ERROR("DBUS response error for Health"); 150 messages::internalError(asyncResp->res); 151 } 152 return; 153 } 154 155 if (!functional) 156 { 157 asyncResp->res.jsonValue["Status"]["Health"] = 158 resource::Health::Critical; 159 } 160 }); 161 } 162 163 inline void doAdapterGet( 164 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 165 const std::string& systemName, const std::string& adapterId, 166 const std::string& fabricAdapterPath, const std::string& serviceName) 167 { 168 asyncResp->res.addHeader( 169 boost::beast::http::field::link, 170 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 171 asyncResp->res.jsonValue["@odata.type"] = 172 "#FabricAdapter.v1_4_0.FabricAdapter"; 173 asyncResp->res.jsonValue["Name"] = "Fabric Adapter"; 174 asyncResp->res.jsonValue["Id"] = adapterId; 175 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 176 "/redfish/v1/Systems/{}/FabricAdapters/{}", systemName, adapterId); 177 178 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 179 asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK; 180 181 getFabricAdapterLocation(asyncResp, serviceName, fabricAdapterPath); 182 getFabricAdapterAsset(asyncResp, serviceName, fabricAdapterPath); 183 getFabricAdapterState(asyncResp, serviceName, fabricAdapterPath); 184 getFabricAdapterHealth(asyncResp, serviceName, fabricAdapterPath); 185 } 186 187 inline void afterGetValidFabricAdapterPath( 188 const std::string& adapterId, 189 std::function<void(const boost::system::error_code&, 190 const std::string& fabricAdapterPath, 191 const std::string& serviceName)>& callback, 192 const boost::system::error_code& ec, 193 const dbus::utility::MapperGetSubTreeResponse& subtree) 194 { 195 std::string fabricAdapterPath; 196 std::string serviceName; 197 if (ec) 198 { 199 callback(ec, fabricAdapterPath, serviceName); 200 return; 201 } 202 203 for (const auto& [adapterPath, serviceMap] : subtree) 204 { 205 std::string fabricAdapterName = 206 sdbusplus::message::object_path(adapterPath).filename(); 207 if (fabricAdapterName == adapterId) 208 { 209 fabricAdapterPath = adapterPath; 210 serviceName = serviceMap.begin()->first; 211 break; 212 } 213 } 214 callback(ec, fabricAdapterPath, serviceName); 215 } 216 217 inline void getValidFabricAdapterPath( 218 const std::string& adapterId, 219 std::function<void(const boost::system::error_code& ec, 220 const std::string& fabricAdapterPath, 221 const std::string& serviceName)>&& callback) 222 { 223 constexpr std::array<std::string_view, 1> interfaces{ 224 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 225 dbus::utility::getSubTree("/xyz/openbmc_project/inventory", 0, interfaces, 226 std::bind_front(afterGetValidFabricAdapterPath, 227 adapterId, std::move(callback))); 228 } 229 230 inline void afterHandleFabricAdapterGet( 231 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 232 const std::string& systemName, const std::string& adapterId, 233 const boost::system::error_code& ec, const std::string& fabricAdapterPath, 234 const std::string& serviceName) 235 { 236 if (ec) 237 { 238 if (ec.value() == boost::system::errc::io_error) 239 { 240 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 241 adapterId); 242 return; 243 } 244 245 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 246 messages::internalError(asyncResp->res); 247 return; 248 } 249 if (fabricAdapterPath.empty() || serviceName.empty()) 250 { 251 BMCWEB_LOG_WARNING("Adapter not found"); 252 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 253 return; 254 } 255 doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath, 256 serviceName); 257 } 258 259 inline void handleFabricAdapterGet( 260 App& app, const crow::Request& req, 261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 262 const std::string& systemName, const std::string& adapterId) 263 { 264 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 265 { 266 return; 267 } 268 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 269 { 270 // Option currently returns no systems. TBD 271 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 272 systemName); 273 return; 274 } 275 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 276 { 277 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 278 systemName); 279 return; 280 } 281 getValidFabricAdapterPath( 282 adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp, 283 systemName, adapterId)); 284 } 285 286 inline void handleFabricAdapterCollectionGet( 287 crow::App& app, const crow::Request& req, 288 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 289 const std::string& systemName) 290 { 291 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 292 { 293 return; 294 } 295 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 296 { 297 // Option currently returns no systems. TBD 298 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 299 systemName); 300 return; 301 } 302 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 303 { 304 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 305 systemName); 306 return; 307 } 308 309 asyncResp->res.addHeader( 310 boost::beast::http::field::link, 311 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 312 asyncResp->res.jsonValue["@odata.type"] = 313 "#FabricAdapterCollection.FabricAdapterCollection"; 314 asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection"; 315 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 316 "/redfish/v1/Systems/{}/FabricAdapters", systemName); 317 318 constexpr std::array<std::string_view, 1> interfaces{ 319 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 320 collection_util::getCollectionMembers( 321 asyncResp, 322 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters", 323 BMCWEB_REDFISH_SYSTEM_URI_NAME), 324 interfaces, "/xyz/openbmc_project/inventory"); 325 } 326 327 inline void handleFabricAdapterCollectionHead( 328 crow::App& app, const crow::Request& req, 329 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 330 const std::string& systemName) 331 { 332 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 333 { 334 return; 335 } 336 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 337 { 338 // Option currently returns no systems. TBD 339 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 340 systemName); 341 return; 342 } 343 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 344 { 345 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 346 systemName); 347 return; 348 } 349 asyncResp->res.addHeader( 350 boost::beast::http::field::link, 351 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 352 } 353 354 inline void afterHandleFabricAdapterHead( 355 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 356 const std::string& adapterId, const boost::system::error_code& ec, 357 const std::string& fabricAdapterPath, const std::string& serviceName) 358 { 359 if (ec) 360 { 361 if (ec.value() == boost::system::errc::io_error) 362 { 363 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 364 adapterId); 365 return; 366 } 367 368 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 369 messages::internalError(asyncResp->res); 370 return; 371 } 372 if (fabricAdapterPath.empty() || serviceName.empty()) 373 { 374 BMCWEB_LOG_WARNING("Adapter not found"); 375 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 376 return; 377 } 378 asyncResp->res.addHeader( 379 boost::beast::http::field::link, 380 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 381 } 382 383 inline void handleFabricAdapterHead( 384 crow::App& app, const crow::Request& req, 385 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 386 const std::string& systemName, const std::string& adapterId) 387 { 388 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 389 { 390 return; 391 } 392 393 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 394 { 395 // Option currently returns no systems. TBD 396 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 397 systemName); 398 return; 399 } 400 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 401 { 402 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 403 systemName); 404 return; 405 } 406 getValidFabricAdapterPath( 407 adapterId, 408 std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId)); 409 } 410 411 inline void requestRoutesFabricAdapterCollection(App& app) 412 { 413 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 414 .privileges(redfish::privileges::getFabricAdapterCollection) 415 .methods(boost::beast::http::verb::get)( 416 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app))); 417 418 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 419 .privileges(redfish::privileges::headFabricAdapterCollection) 420 .methods(boost::beast::http::verb::head)( 421 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app))); 422 } 423 424 inline void requestRoutesFabricAdapters(App& app) 425 { 426 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 427 .privileges(redfish::privileges::getFabricAdapter) 428 .methods(boost::beast::http::verb::get)( 429 std::bind_front(handleFabricAdapterGet, std::ref(app))); 430 431 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 432 .privileges(redfish::privileges::headFabricAdapter) 433 .methods(boost::beast::http::verb::head)( 434 std::bind_front(handleFabricAdapterHead, std::ref(app))); 435 } 436 } // namespace redfish 437