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 (bmcwebEnableMultiHost) 270 { 271 // Option currently returns no systems. TBD 272 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 273 systemName); 274 return; 275 } 276 if (systemName != "system") 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 (bmcwebEnableMultiHost) 297 { 298 // Option currently returns no systems. TBD 299 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 300 systemName); 301 return; 302 } 303 if (systemName != "system") 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::url("/redfish/v1/Systems/system/FabricAdapters"), 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 (bmcwebEnableMultiHost) 337 { 338 // Option currently returns no systems. TBD 339 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 340 systemName); 341 return; 342 } 343 if (systemName != "system") 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 384 handleFabricAdapterHead(crow::App& app, const crow::Request& req, 385 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 386 const std::string& systemName, 387 const std::string& adapterId) 388 { 389 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 390 { 391 return; 392 } 393 394 if constexpr (bmcwebEnableMultiHost) 395 { 396 // Option currently returns no systems. TBD 397 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 398 systemName); 399 return; 400 } 401 if (systemName != "system") 402 { 403 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 404 systemName); 405 return; 406 } 407 getValidFabricAdapterPath( 408 adapterId, 409 std::bind_front(afterHandleFabricAdapterHead, asyncResp, adapterId)); 410 } 411 412 inline void requestRoutesFabricAdapterCollection(App& app) 413 { 414 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 415 .privileges(redfish::privileges::getFabricAdapterCollection) 416 .methods(boost::beast::http::verb::get)( 417 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app))); 418 419 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 420 .privileges(redfish::privileges::headFabricAdapterCollection) 421 .methods(boost::beast::http::verb::head)( 422 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app))); 423 } 424 425 inline void requestRoutesFabricAdapters(App& app) 426 { 427 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 428 .privileges(redfish::privileges::getFabricAdapter) 429 .methods(boost::beast::http::verb::get)( 430 std::bind_front(handleFabricAdapterGet, std::ref(app))); 431 432 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 433 .privileges(redfish::privileges::headFabricAdapter) 434 .methods(boost::beast::http::verb::head)( 435 std::bind_front(handleFabricAdapterHead, std::ref(app))); 436 } 437 } // namespace redfish 438