| #ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_ASSEMBLY_H_ | |
| #define THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_ASSEMBLY_H_ | |
| #include <array> | |
| #include <cstddef> | |
| #include <functional> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <string_view> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "logging.hpp" | |
| #include "utility.hpp" | |
| #include "async_resp.hpp" | |
| #include "dbus_utility.hpp" | |
| #include "error_messages.hpp" | |
| #include "dbus_utils.hpp" | |
| #include "location_utils.hpp" | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store.hpp" | |
| #include "managed_store_types.hpp" | |
| #include "sdbusplus/message/native_types.hpp" | |
| namespace redfish { | |
| inline void addAssemblyAssetProperties( | |
| const std::string& serviceName, const std::string& path, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, std::size_t index) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| managedStore::GetManagedObjectStore()->getAllProperties( | |
| serviceName, path, "xyz.openbmc_project.Inventory.Decorator.Asset", | |
| context, | |
| [aResp, index](const boost::system::error_code ec, | |
| const dbus::utility::DBusPropertiesMap& propertiesList) { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR << "Failed to get asset properties: " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| nlohmann::json& assembliesJson = aResp->res.jsonValue["Assemblies"]; | |
| if (!assembliesJson.is_array() || assembliesJson.size() <= index) { | |
| BMCWEB_LOG_ERROR << "Assemblies list too small"; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| for (const auto& [propertyName, value] : propertiesList) { | |
| // This should really be a string_view, but nlohmann::json | |
| // doesn't support string_view keys. | |
| std::string jsonKey; | |
| if ((propertyName == "PartNumber") || | |
| (propertyName == "SerialNumber") || (propertyName == "Model") || | |
| (propertyName == "SparePartNumber")) { | |
| jsonKey = propertyName; | |
| } else if (propertyName == "Manufacturer") { | |
| jsonKey = "Producer"; | |
| } else if (propertyName == "BuildDate") { | |
| jsonKey = "ProductionDate"; | |
| } else { | |
| // Not supported. | |
| BMCWEB_LOG_WARNING << "Unsupported assembly asset " << propertyName; | |
| continue; | |
| } | |
| const auto* valueStr = std::get_if<std::string>(&value); | |
| if (valueStr == nullptr) { | |
| BMCWEB_LOG_ERROR << "Null value returned for " << propertyName; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| // SparePartNumber default is empty | |
| if (propertyName == "SparePartNumber" && valueStr->empty()) { | |
| continue; | |
| } | |
| assembliesJson[index][jsonKey] = *valueStr; | |
| } | |
| }); | |
| } | |
| inline void getAssemblyPrettyName( | |
| const std::string& serviceName, const std::string& path, | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, std::size_t index) { | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| redfish::dbus_utils::getProperty<std::string>( | |
| serviceName, path, "xyz.openbmc_project.Inventory.Item", "PrettyName", | |
| context, | |
| [aResp, index](const boost::system::error_code& ec, | |
| const std::string& prettyName) { | |
| if (ec) { | |
| BMCWEB_LOG_ERROR << "Failed to get Item properties: " << ec; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| aResp->res.jsonValue["Assemblies"][index]["Name"] = prettyName; | |
| }); | |
| } | |
| inline void populateAssemblyProperties( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const std::string& assembly, const boost::urls::url& assemblyId, | |
| const boost::system::error_code ec, | |
| const std::vector<std::pair<std::string, std::vector<std::string>>>& | |
| object) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << "DBUS response error"; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| // All Assemblys implement interface | |
| // "xyz.openbmc_project.Inventory.Item.Component" | |
| // If interface is not found, then it is not an Assembly connection and we | |
| // should abort. | |
| constexpr std::array<std::string_view, 1> assemblyInterface = { | |
| "xyz.openbmc_project.Inventory.Item.Component", | |
| }; | |
| if (!redfish::dbus_utils::findInterfacesInServiceMap(object, | |
| assemblyInterface)) { | |
| return; | |
| } | |
| nlohmann::json& assembliesJson = aResp->res.jsonValue["Assemblies"]; | |
| std::size_t assemblyIndex = assembliesJson.size(); | |
| boost::urls::url dataID = assemblyId; | |
| std::string assemblyName = | |
| sdbusplus::message::object_path(assembly).filename(); | |
| dataID.set_fragment(crow::utility::urlFromPieces( | |
| "Assemblies", std::to_string(assembliesJson.size())) | |
| .buffer()); | |
| assembliesJson.push_back({{"@odata.type", "#Assembly.v1_3_0.AssemblyData"}, | |
| {"@odata.id", dataID.buffer()}, | |
| {"MemberId", std::to_string(assemblyIndex)}, | |
| {"Name", assemblyName}}); | |
| aResp->res.jsonValue["Assemblies@odata.count"] = assembliesJson.size(); | |
| for (const auto& [serviceName, interfaceList] : object) { | |
| for (const auto& interface : interfaceList) { | |
| if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") { | |
| addAssemblyAssetProperties(serviceName, assembly, aResp, assemblyIndex); | |
| } else if (interface == | |
| "xyz.openbmc_project.Inventory." | |
| "Decorator.LocationCode") { | |
| nlohmann::json::json_pointer locationPtr = | |
| "/Assemblies"_json_pointer / assemblyIndex / "Location"; | |
| location_util::getLocationCode(aResp, serviceName, assembly, | |
| locationPtr); | |
| location_util::getPartLocationContext(aResp, locationPtr, | |
| assembly + "/contained_by"); | |
| } else if (interface == "xyz.openbmc_project.Inventory.Item") { | |
| getAssemblyPrettyName(serviceName, assembly, aResp, assemblyIndex); | |
| } else { | |
| std::optional<std::string> type = | |
| location_util::getLocationType(interface); | |
| if (type) { | |
| assembliesJson[assemblyIndex]["Location"]["PartLocation"] | |
| ["LocationType"] = *type; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * @brief Get properties for an assembly | |
| * @param[in] aResp - Shared pointer for asynchronous calls. | |
| * @param[in] assemblyId - Assembly resource ID. | |
| * @param[in] assemblies - list of all the dbus paths associated as an assembly | |
| * with the parent. | |
| * @return None. | |
| */ | |
| inline void getAssemblyProperties( | |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const boost::urls::url& assemblyId, const boost::system::error_code& ec, | |
| const std::vector<std::string>& assemblies) { | |
| if (ec) { | |
| BMCWEB_LOG_DEBUG << " error_code = " << ec | |
| << " error msg = " << ec.message(); | |
| // Don't throw error DBus object may not exist. | |
| return; | |
| } | |
| aResp->res.jsonValue["Assemblies"] = nlohmann::json::array(); | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| for (const std::string& assembly : assemblies) { | |
| std::string name = sdbusplus::message::object_path(assembly).filename(); | |
| if (name.empty()) { | |
| BMCWEB_LOG_DEBUG << "Empty name in assembly"; | |
| messages::internalError(aResp->res); | |
| return; | |
| } | |
| constexpr std::array<std::string_view, 4> interfaces = { | |
| "xyz.openbmc_project.Inventory.Decorator.Asset", | |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", | |
| "xyz.openbmc_project.Inventory.Connector.Embedded", | |
| "xyz.openbmc_project.Inventory.Connector.Slot"}; | |
| managedStore::GetManagedObjectStore()->getDbusObject( | |
| assembly, interfaces, requestContext, | |
| std::bind_front(populateAssemblyProperties, aResp, assembly, | |
| assemblyId)); | |
| } | |
| } | |
| /** | |
| * @brief Gets assemblies from assembly associations to an object. | |
| * @param[in] aResp - Shared pointer for asynchronous calls. | |
| * @param[in] assemblyId - Assembly resource ID. | |
| * @param[in] parentPath - DBus path of parent of assembly. | |
| * | |
| * @return None. | |
| */ | |
| inline void getAssembly(const std::shared_ptr<bmcweb::AsyncResp>& aResp, | |
| const boost::urls::url& assemblyId, | |
| const std::string& parentPath) { | |
| aResp->res.jsonValue["@odata.type"] = "#Assembly.v1_3_0.Assembly"; | |
| aResp->res.jsonValue["@odata.id"] = assemblyId.buffer(); | |
| aResp->res.jsonValue["Name"] = "Assembly Collection"; | |
| aResp->res.jsonValue["Id"] = "Assembly"; | |
| aResp->res.jsonValue["Assemblies"] = nlohmann::json::array(); | |
| aResp->res.jsonValue["Assemblies@odata.count"] = 0; | |
| managedStore::ManagedObjectStoreContext requestContext(aResp); | |
| constexpr std::array<std::string_view, 1> assemblyInterface = { | |
| "xyz.openbmc_project.Inventory.Item.Component", | |
| }; | |
| const std::string inventoryPath = "/xyz/openbmc_project/inventory"; | |
| managedStore::ManagedObjectStoreContext context(aResp); | |
| dbus_utils::getAssociatedSubTreePaths( | |
| parentPath + "/containing", inventoryPath, 0, assemblyInterface, context, | |
| std::bind_front(getAssemblyProperties, aResp, assemblyId)); | |
| } | |
| } // namespace redfish | |
| #endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_ASSEMBLY_H_ |