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 259 getValidFabricAdapterPath( 260 adapterId, systemName, asyncResp, 261 [asyncResp, systemName, adapterId](const std::string& fabricAdapterPath, 262 const std::string& serviceName) { 263 doAdapterGet(asyncResp, systemName, adapterId, fabricAdapterPath, 264 serviceName); 265 }); 266 } 267 268 inline void handleFabricAdapterCollectionGet( 269 crow::App& app, const crow::Request& req, 270 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 271 const std::string& systemName) 272 { 273 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 274 { 275 return; 276 } 277 if (systemName != "system") 278 { 279 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 280 systemName); 281 return; 282 } 283 284 asyncResp->res.addHeader( 285 boost::beast::http::field::link, 286 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 287 asyncResp->res.jsonValue["@odata.type"] = 288 "#FabricAdapterCollection.FabricAdapterCollection"; 289 asyncResp->res.jsonValue["Name"] = "Fabric Adapter Collection"; 290 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 291 "/redfish/v1/Systems/{}/FabricAdapters", systemName); 292 293 constexpr std::array<std::string_view, 1> interfaces{ 294 "xyz.openbmc_project.Inventory.Item.FabricAdapter"}; 295 collection_util::getCollectionMembers( 296 asyncResp, 297 boost::urls::url("/redfish/v1/Systems/system/FabricAdapters"), 298 interfaces); 299 } 300 301 inline void handleFabricAdapterCollectionHead( 302 crow::App& app, const crow::Request& req, 303 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 304 const std::string& systemName) 305 { 306 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 307 { 308 return; 309 } 310 if (systemName != "system") 311 { 312 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 313 systemName); 314 return; 315 } 316 asyncResp->res.addHeader( 317 boost::beast::http::field::link, 318 "</redfish/v1/JsonSchemas/FabricAdapterCollection/FabricAdapterCollection.json>; rel=describedby"); 319 } 320 321 inline void 322 handleFabricAdapterHead(crow::App& app, const crow::Request& req, 323 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 324 const std::string& systemName, 325 const std::string& adapterId) 326 { 327 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 328 { 329 return; 330 } 331 332 getValidFabricAdapterPath(adapterId, systemName, asyncResp, 333 [asyncResp, systemName, adapterId]( 334 const std::string&, const std::string&) { 335 asyncResp->res.addHeader( 336 boost::beast::http::field::link, 337 "</redfish/v1/JsonSchemas/FabricAdapter/FabricAdapter.json>; rel=describedby"); 338 }); 339 } 340 341 inline void requestRoutesFabricAdapterCollection(App& app) 342 { 343 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 344 .privileges(redfish::privileges::getFabricAdapterCollection) 345 .methods(boost::beast::http::verb::get)( 346 std::bind_front(handleFabricAdapterCollectionGet, std::ref(app))); 347 348 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/") 349 .privileges(redfish::privileges::headFabricAdapterCollection) 350 .methods(boost::beast::http::verb::head)( 351 std::bind_front(handleFabricAdapterCollectionHead, std::ref(app))); 352 } 353 354 inline void requestRoutesFabricAdapters(App& app) 355 { 356 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 357 .privileges(redfish::privileges::getFabricAdapter) 358 .methods(boost::beast::http::verb::get)( 359 std::bind_front(handleFabricAdapterGet, std::ref(app))); 360 361 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/FabricAdapters/<str>/") 362 .privileges(redfish::privileges::headFabricAdapter) 363 .methods(boost::beast::http::verb::head)( 364 std::bind_front(handleFabricAdapterHead, std::ref(app))); 365 } 366 } // namespace redfish 367