1 #pragma once 2 3 #include "app.hpp" 4 #include "dbus_utility.hpp" 5 #include "query.hpp" 6 #include "registries/privilege_registry.hpp" 7 #include "utils/collection.hpp" 8 #include "utils/dbus_utils.hpp" 9 #include "utils/json_utils.hpp" 10 11 #include <boost/system/error_code.hpp> 12 #include <boost/url/format.hpp> 13 #include <sdbusplus/asio/property.hpp> 14 #include <sdbusplus/unpack_properties.hpp> 15 16 #include <array> 17 #include <functional> 18 #include <memory> 19 #include <optional> 20 #include <string> 21 #include <string_view> 22 23 namespace redfish 24 { 25 26 inline void getFabricAdapterLocation( 27 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 28 const std::string& serviceName, const std::string& fabricAdapterPath) 29 { 30 sdbusplus::asio::getProperty<std::string>( 31 *crow::connections::systemBus, serviceName, fabricAdapterPath, 32 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 33 [asyncResp](const boost::system::error_code& ec, 34 const std::string& property) { 35 if (ec) 36 { 37 if (ec.value() != EBADR) 38 { 39 BMCWEB_LOG_ERROR("DBUS response error for Location"); 40 messages::internalError(asyncResp->res); 41 } 42 return; 43 } 44 45 asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 46 property; 47 }); 48 } 49 50 inline void 51 getFabricAdapterAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 52 const std::string& serviceName, 53 const std::string& fabricAdapterPath) 54 { 55 sdbusplus::asio::getAllProperties( 56 *crow::connections::systemBus, serviceName, fabricAdapterPath, 57 "xyz.openbmc_project.Inventory.Decorator.Asset", 58 [fabricAdapterPath, asyncResp{asyncResp}]( 59 const boost::system::error_code& ec, 60 const dbus::utility::DBusPropertiesMap& propertiesList) { 61 if (ec) 62 { 63 if (ec.value() != EBADR) 64 { 65 BMCWEB_LOG_ERROR("DBUS response error for Properties"); 66 messages::internalError(asyncResp->res); 67 } 68 return; 69 } 70 71 const std::string* serialNumber = nullptr; 72 const std::string* model = nullptr; 73 const std::string* partNumber = nullptr; 74 const std::string* sparePartNumber = nullptr; 75 76 const bool success = sdbusplus::unpackPropertiesNoThrow( 77 dbus_utils::UnpackErrorPrinter(), propertiesList, "SerialNumber", 78 serialNumber, "Model", model, "PartNumber", partNumber, 79 "SparePartNumber", sparePartNumber); 80 81 if (!success) 82 { 83 messages::internalError(asyncResp->res); 84 return; 85 } 86 87 if (serialNumber != nullptr) 88 { 89 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 90 } 91 92 if (model != nullptr) 93 { 94 asyncResp->res.jsonValue["Model"] = *model; 95 } 96 97 if (partNumber != nullptr) 98 { 99 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 100 } 101 102 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 103 { 104 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 105 } 106 }); 107 } 108 109 inline void 110 getFabricAdapterState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 111 const std::string& serviceName, 112 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"] = "Absent"; 131 } 132 }); 133 } 134 135 inline void 136 getFabricAdapterHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 137 const std::string& serviceName, 138 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"] = "Critical"; 158 } 159 }); 160 } 161 162 inline void doAdapterGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 163 const std::string& systemName, 164 const std::string& adapterId, 165 const std::string& fabricAdapterPath, 166 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"] = "Enabled"; 179 asyncResp->res.jsonValue["Status"]["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 260 handleFabricAdapterGet(App& app, const crow::Request& req, 261 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 262 const std::string& systemName, 263 const std::string& adapterId) 264 { 265 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 266 { 267 return; 268 } 269 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 270 { 271 // Option currently returns no systems. TBD 272 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 273 systemName); 274 return; 275 } 276 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 277 { 278 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 279 systemName); 280 return; 281 } 282 getValidFabricAdapterPath( 283 adapterId, std::bind_front(afterHandleFabricAdapterGet, asyncResp, 284 systemName, adapterId)); 285 } 286 287 inline void handleFabricAdapterCollectionGet( 288 crow::App& app, const crow::Request& req, 289 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 290 const std::string& systemName) 291 { 292 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 293 { 294 return; 295 } 296 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 297 { 298 // Option currently returns no systems. TBD 299 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 300 systemName); 301 return; 302 } 303 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 304 { 305 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 306 systemName); 307 return; 308 } 309 310 asyncResp->res.addHeader( 311 boost::beast::http::field::link, 312 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 313 asyncResp->res.jsonValue["@odata.type"] = 314 "#FabricAdapterCollection.FabricAdapterCollection"; 315 asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection"; 316 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 317 "/redfish/v1/Systems/{}/FabricAdapters", systemName); 318 319 constexpr std::array<std::string_view, 1> interfaces{ 320 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 321 collection_util::getCollectionMembers( 322 asyncResp, 323 boost::urls::format("/redfish/v1/Systems/{}/FabricAdapters", 324 BMCWEB_REDFISH_SYSTEM_URI_NAME), 325 interfaces, "/xyz/openbmc_project/inventory"); 326 } 327 328 inline void handleFabricAdapterCollectionHead( 329 crow::App& app, const crow::Request& req, 330 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 331 const std::string& systemName) 332 { 333 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 334 { 335 return; 336 } 337 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 338 { 339 // Option currently returns no systems. TBD 340 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 341 systemName); 342 return; 343 } 344 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 345 { 346 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 347 systemName); 348 return; 349 } 350 asyncResp->res.addHeader( 351 boost::beast::http::field::link, 352 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 353 } 354 355 inline void afterHandleFabricAdapterHead( 356 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 357 const std::string& adapterId, const boost::system::error_code& ec, 358 const std::string& fabricAdapterPath, const std::string& serviceName) 359 { 360 if (ec) 361 { 362 if (ec.value() == boost::system::errc::io_error) 363 { 364 messages::resourceNotFound(asyncResp->res, "FabricAdapter", 365 adapterId); 366 return; 367 } 368 369 BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value()); 370 messages::internalError(asyncResp->res); 371 return; 372 } 373 if (fabricAdapterPath.empty() || serviceName.empty()) 374 { 375 BMCWEB_LOG_WARNING("Adapter not found"); 376 messages::resourceNotFound(asyncResp->res, "FabricAdapter", adapterId); 377 return; 378 } 379 asyncResp->res.addHeader( 380 boost::beast::http::field::link, 381 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 382 } 383 384 inline void 385 handleFabricAdapterHead(crow::App& app, const crow::Request& req, 386 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 387 const std::string& systemName, 388 const std::string& adapterId) 389 { 390 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 391 { 392 return; 393 } 394 395 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM) 396 { 397 // Option currently returns no systems. TBD 398 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 399 systemName); 400 return; 401 } 402 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME) 403 { 404 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 405 systemName); 406 return; 407 } 408 getValidFabricAdapterPath( 409 adapterId, 410 std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId)); 411 } 412 413 inline void requestRoutesFabricAdapterCollection(App& app) 414 { 415 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 416 .privileges(redfish::privileges::getFabricAdapterCollection) 417 .methods(boost::beast::http::verb::get)( 418 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app))); 419 420 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 421 .privileges(redfish::privileges::headFabricAdapterCollection) 422 .methods(boost::beast::http::verb::head)( 423 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app))); 424 } 425 426 inline void requestRoutesFabricAdapters(App& app) 427 { 428 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 429 .privileges(redfish::privileges::getFabricAdapter) 430 .methods(boost::beast::http::verb::get)( 431 std::bind_front(handleFabricAdapterGet, std::ref(app))); 432 433 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 434 .privileges(redfish::privileges::headFabricAdapter) 435 .methods(boost::beast::http::verb::head)( 436 std::bind_front(handleFabricAdapterHead, std::ref(app))); 437 } 438 } // namespace redfish 439