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