| #ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_CABLE_H_ | |
| #define THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_CABLE_H_ | |
| #include <array> | |
| #include <cmath> | |
| #include <functional> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <string_view> | |
| #include <utility> | |
| #include <vector> | |
| #include "boost/system/error_code.hpp" // NOLINT | |
| #include "bmcweb_config.h" | |
| #include "app.hpp" | |
| #include "http_request.hpp" | |
| #include "http_response.hpp" | |
| #include "logging.hpp" | |
| #include "utility.hpp" | |
| #include "async_resp.hpp" | |
| #include "dbus_utility.hpp" | |
| #include "error_messages.hpp" | |
| #include "query.hpp" | |
| #include "registries/privilege_registry.hpp" | |
| #include "collection.hpp" | |
| #include "dbus_utils.hpp" | |
| #include "location_utils.hpp" | |
| #ifdef HEALTH_POPULATE | |
| #include "health.hpp" | |
| #endif /* HEALTH_POPULATE */ | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store.hpp" | |
| #include "managed_store_types.hpp" | |
| #include "sdbusplus/message/native_types.hpp" | |
| #include "sdbusplus/unpack_properties.hpp" | |
| namespace redfish { | |
| /** | |
| * @brief Get Cable's asset properties. | |
| * @param[in,out] resp HTTP response. | |
| * @param[in] service Cable's service name. | |
| * @param[in] path Cable's path. | |
| */ | |
| inline void getCableAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& service, const std::string& path) { | |
| managedStore::ManagedObjectStoreContext context(asyncResp); | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| service, path, "xyz.openbmc_project.Inventory.Decorator.Asset", context, | |
| [path, asyncResp](const boost::system::error_code& ec, | |
| const dbus::utility::DBusPropertiesMap& assetList) { | |
| if (ec) { | |
| if (ec.value() != EBADR) { | |
| BMCWEB_LOG_ERROR << "DBUS response error for Properties" | |
| << ec.value(); | |
| messages::internalError(asyncResp->res); | |
| } | |
| return; | |
| } | |
| const std::string* model = nullptr; | |
| const std::string* manufacturer = nullptr; | |
| const std::string* partNumber = nullptr; | |
| const std::string* serialNumber = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), assetList, "Model", model, | |
| "Manufacturer", manufacturer, "PartNumber", partNumber, | |
| "SerialNumber", serialNumber); | |
| if (!success) { | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| if (model != nullptr) { | |
| asyncResp->res.jsonValue["Model"] = *model; | |
| } | |
| if (manufacturer != nullptr) { | |
| asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; | |
| } | |
| if (partNumber != nullptr) { | |
| asyncResp->res.jsonValue["PartNumber"] = *partNumber; | |
| } | |
| if (serialNumber != nullptr) { | |
| asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Fill cable specific properties. | |
| * @param[in,out] resp HTTP response. | |
| * @param[in] ec Error code corresponding to Async method call. | |
| * @param[in] properties List of Cable Properties key/value pairs. | |
| */ | |
| inline void fillCableProperties( | |
| crow::Response& resp, const boost::system::error_code& ec, | |
| const dbus::utility::DBusPropertiesMap& properties) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; | |
| messages::internalError(resp); | |
| return; | |
| } | |
| const std::string* cableTypeDescription = nullptr; | |
| const double* length = nullptr; | |
| const bool success = sdbusplus::unpackPropertiesNoThrow( | |
| dbus_utils::UnpackErrorPrinter(), properties, "CableTypeDescription", | |
| cableTypeDescription, "Length", length); | |
| if (!success) { | |
| messages::internalError(resp); | |
| return; | |
| } | |
| if (cableTypeDescription != nullptr) { | |
| resp.jsonValue["CableType"] = *cableTypeDescription; | |
| } | |
| if (length != nullptr) { | |
| if (!std::isfinite(*length)) { | |
| // Cable length is NaN by default, do not throw an error | |
| if (!std::isnan(*length)) { | |
| messages::internalError(resp); | |
| return; | |
| } | |
| } else { | |
| resp.jsonValue["LengthMeters"] = *length; | |
| } | |
| } | |
| } | |
| /** | |
| * @brief Create Links for Chassis in Cable resource. | |
| * @param[in,out] asyncResp Async HTTP response. | |
| * @param[in] associationPath Cable association path. | |
| * @param[in] chassisPropertyName Chassis of PropertyName of Cable. | |
| */ | |
| inline void getCableChassisAssociation( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& associationPath, | |
| const std::string& chassisPropertyName) { | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| dbus_utils::getProperty<std::vector<std::string>>( | |
| "xyz.openbmc_project.ObjectMapper", associationPath, | |
| "xyz.openbmc_project.Association", "endpoints", requestContext, | |
| [asyncResp, chassisPropertyName](const boost::system::error_code ec, | |
| const std::vector<std::string>& resp) { | |
| if (ec) { | |
| return; // no downstream_chassis = no failures | |
| } | |
| nlohmann::json& chassis = | |
| asyncResp->res.jsonValue["Links"][chassisPropertyName]; | |
| chassis = nlohmann::json::array(); | |
| const std::string chassisCollectionPath = "/redfish/v1/Chassis"; | |
| for (const std::string& chassisPath : resp) { | |
| BMCWEB_LOG_INFO << chassisPath << "chassis path"; | |
| sdbusplus::message::object_path path(chassisPath); | |
| std::string leaf = path.filename(); | |
| if (leaf.empty()) { | |
| continue; | |
| } | |
| std::string newPath = chassisCollectionPath; | |
| newPath += "/"; | |
| newPath += leaf; | |
| chassis.emplace_back( | |
| nlohmann::json({{"@odata.id", std::move(newPath)}})); | |
| } | |
| }); | |
| } | |
| /** | |
| * @brief Api to get Cable properties. | |
| * @param[in,out] asyncResp Async HTTP response. | |
| * @param[in] cableObjectPath Object path of the Cable. | |
| * @param[in] serviceMap A map to hold Service and corresponding | |
| * interface list for the given cable id. | |
| */ | |
| inline void getCableProperties( | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cableObjectPath, | |
| const dbus::utility::MapperServiceMap& serviceMap) { | |
| BMCWEB_LOG_DEBUG << "Get Properties for cable " << cableObjectPath; | |
| for (const auto& [service, interfaces] : serviceMap) { | |
| for (const auto& interface : interfaces) { | |
| if (interface == "xyz.openbmc_project.Inventory.Item.Cable") { | |
| managedStore::ManagedObjectStoreContext context(asyncResp); | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| service, cableObjectPath, interface, context, | |
| [asyncResp](const boost::system::error_code ec, | |
| const dbus::utility::DBusPropertiesMap& properties) { | |
| fillCableProperties(asyncResp->res, ec, properties); | |
| }); | |
| } else if (interface == | |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode") { | |
| location_util::getLocationCode(asyncResp, service, cableObjectPath, | |
| "/Location"_json_pointer); | |
| location_util::getPartLocationContext( | |
| asyncResp, "/Location"_json_pointer, | |
| cableObjectPath + "/upstream_chassis"); | |
| } else if (location_util::isConnector(interface)) { | |
| std::optional<std::string> locationType = | |
| location_util::getLocationType(interface); | |
| if (!locationType) { | |
| BMCWEB_LOG_DEBUG << "getLocationType for Cable failed for " | |
| << interface; | |
| continue; | |
| } | |
| asyncResp->res.jsonValue["Location"]["PartLocation"]["LocationType"] = | |
| *locationType; | |
| } else if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") { | |
| getCableAsset(asyncResp, service, cableObjectPath); | |
| } else if (interface == "xyz.openbmc_project.Inventory.Item") { | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| dbus_utils::getProperty<bool>( | |
| service, cableObjectPath, interface, "Present", requestContext, | |
| [asyncResp, cableObjectPath](const boost::system::error_code& ec, | |
| bool present) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "get presence failed for Cable " | |
| << cableObjectPath << "with error " << ec; | |
| return; | |
| } | |
| if (!present) { | |
| asyncResp->res.jsonValue["Status"]["State"] = "Absent"; | |
| } | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| inline void handleCableGet(App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, | |
| const std::string& cableId) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| BMCWEB_LOG_DEBUG << "Cable Id: " << cableId; | |
| constexpr std::array<std::string_view, 1> interfaces = { | |
| "xyz.openbmc_project.Inventory.Item.Cable"}; | |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); | |
| managedStore::GetManagedObjectStore()->getSubTree( | |
| "/xyz/openbmc_project/inventory", 0, interfaces, requestContext, | |
| [asyncResp, cableId]( | |
| const boost::system::error_code& ec, | |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { | |
| if (ec.value() == EBADR) { | |
| messages::resourceNotFound(asyncResp->res, "Cable", cableId); | |
| return; | |
| } | |
| if (ec) { | |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec; | |
| messages::internalError(asyncResp->res); | |
| return; | |
| } | |
| for (const auto& [objectPath, serviceMap] : subtree) { | |
| sdbusplus::message::object_path path(objectPath); | |
| if (path.filename() != cableId) { | |
| continue; | |
| } | |
| #ifdef HEALTH_POPULATE | |
| auto health = std::make_shared<HealthPopulate>( | |
| asyncResp, "/redfish/v1/Cables/" + cableId, objectPath, "Cable", | |
| cableId); | |
| #endif /* HEALTH_POPULATE */ | |
| if constexpr (enablePlatform9) { | |
| // Hardcode relations | |
| if (cableId == "cat5_cable") { | |
| nlohmann::json::array_t upArray; | |
| nlohmann::json::object_t upObj; | |
| upObj["@odata.id"] = | |
| "/redfish/v1/Chassis/" + std::string(platform9Chassis0); | |
| upArray.emplace_back(std::move(upObj)); | |
| asyncResp->res.jsonValue["Links"]["UpstreamChassis"] = | |
| std::move(upArray); | |
| } | |
| if (cableId == platform9Cable0) { | |
| nlohmann::json::array_t downArray; | |
| nlohmann::json::array_t upArray; | |
| nlohmann::json::object_t downObj; | |
| nlohmann::json::object_t upObj; | |
| downObj["@odata.id"] = | |
| "/redfish/v1/Cables/" + std::string(platform9Cable1); | |
| downArray.emplace_back(std::move(downObj)); | |
| asyncResp->res.jsonValue["Links"]["DownstreamResources"] = | |
| std::move(downArray); | |
| upObj["@odata.id"] = | |
| "/redfish/v1/Chassis/" + std::string(platform9Chassis0); | |
| upArray.emplace_back(std::move(upObj)); | |
| asyncResp->res.jsonValue["Links"]["UpstreamChassis"] = | |
| std::move(upArray); | |
| } | |
| if (cableId == platform9Cable1) { | |
| nlohmann::json::array_t downArray; | |
| nlohmann::json::array_t upArray; | |
| nlohmann::json::object_t downObj; | |
| nlohmann::json::object_t upObj; | |
| downObj["@odata.id"] = | |
| "/redfish/v1/Cables/" + std::string(platform9Cable2); | |
| downArray.emplace_back(std::move(downObj)); | |
| asyncResp->res.jsonValue["Links"]["DownstreamResources"] = | |
| std::move(downArray); | |
| upObj["@odata.id"] = | |
| "/redfish/v1/Cables/" + std::string(platform9Cable0); | |
| upArray.emplace_back(std::move(upObj)); | |
| asyncResp->res.jsonValue["Links"]["UpstreamResources"] = | |
| std::move(upArray); | |
| } | |
| if (cableId == platform9Cable2) { | |
| nlohmann::json::array_t downArray; | |
| nlohmann::json::array_t upArray; | |
| nlohmann::json::object_t downObj; | |
| nlohmann::json::object_t upObj; | |
| downObj["@odata.id"] = | |
| "/redfish/v1/Chassis/" + std::string(platform9Chassis3); | |
| downArray.emplace_back(std::move(downObj)); | |
| asyncResp->res.jsonValue["Links"]["DownstreamResources"] = | |
| std::move(downArray); | |
| upObj["@odata.id"] = | |
| "/redfish/v1/Cables/" + std::string(platform9Cable1); | |
| upArray.emplace_back(std::move(upObj)); | |
| asyncResp->res.jsonValue["Links"]["UpstreamResources"] = | |
| std::move(upArray); | |
| } | |
| if (cableId == platform9Cable3) { | |
| nlohmann::json::array_t downArray; | |
| nlohmann::json::array_t upArray; | |
| nlohmann::json::object_t downObj; | |
| nlohmann::json::object_t upObj; | |
| downObj["@odata.id"] = | |
| "/redfish/v1/Cables/" + std::string(platform9Cable4); | |
| downArray.emplace_back(std::move(downObj)); | |
| asyncResp->res.jsonValue["Links"]["DownstreamResources"] = | |
| std::move(downArray); | |
| upObj["@odata.id"] = | |
| "/redfish/v1/Chassis/" + std::string(platform9Chassis3); | |
| upArray.emplace_back(std::move(upObj)); | |
| asyncResp->res.jsonValue["Links"]["UpstreamResources"] = | |
| std::move(upArray); | |
| } | |
| if (cableId == platform9Cable4) { | |
| nlohmann::json::array_t downArray; | |
| nlohmann::json::array_t upArray; | |
| nlohmann::json::object_t downObj; | |
| nlohmann::json::object_t upObj; | |
| downObj["@odata.id"] = | |
| "/redfish/v1/Chassis/" + std::string(platform9Chassis2); | |
| downArray.emplace_back(std::move(downObj)); | |
| asyncResp->res.jsonValue["Links"]["DownstreamResources"] = | |
| std::move(downArray); | |
| upObj["@odata.id"] = | |
| "/redfish/v1/Cables/" + std::string(platform9Cable3); | |
| upArray.emplace_back(std::move(upObj)); | |
| asyncResp->res.jsonValue["Links"]["UpstreamResources"] = | |
| std::move(upArray); | |
| } | |
| } | |
| asyncResp->res.jsonValue["@odata.type"] = "#Cable.v1_0_0.Cable"; | |
| asyncResp->res.jsonValue["@odata.id"] = | |
| crow::utility::urlFromPieces("redfish", "v1", "Cables", cableId); | |
| asyncResp->res.jsonValue["Id"] = cableId; | |
| asyncResp->res.jsonValue["Name"] = "Cable"; | |
| asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; | |
| getCableProperties(asyncResp, objectPath, serviceMap); | |
| getCableChassisAssociation(asyncResp, | |
| objectPath + "/downstream_chassis", | |
| "DownstreamChassis"); | |
| getCableChassisAssociation( | |
| asyncResp, objectPath + "/upstream_chassis", "UpstreamChassis"); | |
| return; | |
| } | |
| messages::resourceNotFound(asyncResp->res, "Cable", cableId); | |
| }); | |
| } | |
| /** | |
| * The Cable schema | |
| */ | |
| inline void requestRoutesCable(App& app) { | |
| BMCWEB_ROUTE(app, "/redfish/v1/Cables/<str>/") | |
| .privileges(redfish::privileges::getCable) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleCableGet, std::ref(app))); | |
| } | |
| inline void handleCableCollectionGet( | |
| App& app, const crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { | |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { | |
| return; | |
| } | |
| asyncResp->res.jsonValue["@odata.type"] = "#CableCollection.CableCollection"; | |
| asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Cables"; | |
| asyncResp->res.jsonValue["Name"] = "Cable Collection"; | |
| asyncResp->res.jsonValue["Description"] = "Collection of Cable Entries"; | |
| constexpr std::array<std::string_view, 1> interfaces{ | |
| "xyz.openbmc_project.Inventory.Item.Cable"}; | |
| collection_util::getCollectionMembers( | |
| asyncResp, boost::urls::url("/redfish/v1/Cables"), interfaces); | |
| } | |
| /** | |
| * Collection of Cable resource instances | |
| */ | |
| inline void requestRoutesCableCollection(App& app) { | |
| BMCWEB_ROUTE(app, "/redfish/v1/Cables/") | |
| .privileges(redfish::privileges::getCableCollection) | |
| .methods(boost::beast::http::verb::get)( | |
| std::bind_front(handleCableCollectionGet, std::ref(app))); | |
| } | |
| } // namespace redfish | |
| #endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_CABLE_H_ |