| #pragma once |
| |
| #include "managed_store_types.hpp" |
| |
| #include <dbus_utility.hpp> |
| #include <human_sort.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| #include <utility.hpp> |
| #include <utils/dbus_utils.hpp> |
| #include <utils/json_utils.hpp> |
| #include <utils/location_utils.hpp> |
| |
| #include "managed_store.hpp" |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| 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 |