| #pragma once |
| #include "bmcweb_config.h" |
| |
| #include "dbus_utility.hpp" |
| #include "managed_store_types.hpp" |
| #include "query.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "utils/collection.hpp" |
| #include "utils/dbus_utils.hpp" |
| #include "utils/json_utils.hpp" |
| #include "utils/location_utils.hpp" |
| |
| #include <boost/system/error_code.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <sdbusplus/unpack_properties.hpp> |
| |
| #include <array> |
| #include <string_view> |
| |
| #include "managed_store.hpp" |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| 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; |
| } |
| |
| 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 |