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