| #ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_LOCATION_UTILS_H_ |
| #define THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_LOCATION_UTILS_H_ |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <functional> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/functional/any_invocable.h" |
| #include "logging.hpp" |
| #include "async_resp.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "chassis_utils.hpp" |
| #include "dbus_utils.hpp" |
| #include <nlohmann/json.hpp> |
| #include "managed_store_types.hpp" |
| #include "sdbusplus/unpack_properties.hpp" |
| |
| namespace redfish { |
| namespace location_util { |
| |
| enum class PopulateLocationContextOptions { |
| kWithPartLocationContext = 0, |
| kWithOemDevpathOnly = 1, |
| kWithPartLocationContextAndOemDevpath = 2, |
| }; |
| |
| /** |
| * @brief Check if the interface is a supported connector |
| * |
| * @param[in] interface Location type interface. |
| * |
| * @return true if the interface is a supported connector |
| */ |
| inline bool isConnector(const std::string& interface) { |
| return interface == "xyz.openbmc_project.Inventory.Connector.Backplane" || |
| interface == "xyz.openbmc_project.Inventory.Connector.Bay" || |
| interface == "xyz.openbmc_project.Inventory.Connector.Connector" || |
| interface == "xyz.openbmc_project.Inventory.Connector.Embedded" || |
| interface == "xyz.openbmc_project.Inventory.Connector.Slot" || |
| interface == "xyz.openbmc_project.Inventory.Connector.Socket"; |
| } |
| |
| /** |
| * @brief Fill out location info of a resource by requesting data from the |
| * given D-Bus object. |
| * |
| * @param[in,out] asyncResp Async HTTP response. |
| * @param[in] interface Location type interface. |
| * |
| * @return location if interface is supported Connector, otherwise, return |
| * std::nullopt. |
| */ |
| inline std::optional<std::string> getLocationType( |
| const std::string& interface) { |
| if (!isConnector(interface)) { |
| return std::nullopt; |
| } |
| return interface.substr(interface.find_last_of('.') + 1); |
| } |
| |
| void getPartLocationContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath, |
| const std::string& locationCode = ""); |
| |
| void getLocationContextWithOemDevpath( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath, const std::string& locationCode = ""); |
| |
| void getLocationContextWithOemDevpathOnly( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath, const std::string& locationCode = ""); |
| |
| /** |
| * |
| * @brief Fill out location code of a resource by requesting data from the |
| * given D-Bus object. |
| * |
| * @param[in,out] asyncResp Async HTTP response. |
| * @param[in] service D-Bus service to query. |
| * @param[in] objPath D-Bus object to query. |
| * @param[in] location Json path of where to find the location |
| * property. |
| */ |
| inline void getLocationCode( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& service, const std::string& objPath, |
| const nlohmann::json::json_pointer& location, |
| const std::optional<std::string>& contextPath = std::nullopt) { |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<std::string>( |
| service, objPath, "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "LocationCode", requestContext, |
| [asyncResp, location, contextPath](const boost::system::error_code ec, |
| const std::string& property) { |
| if (ec) { |
| BMCWEB_LOG_DEBUG << "DBUS response error for Location"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| asyncResp->res.jsonValue[location]["PartLocation"]["ServiceLabel"] = |
| property; |
| if (contextPath != std::nullopt) { |
| getPartLocationContext(asyncResp, location, contextPath.value(), |
| property); |
| } |
| }); |
| } |
| |
| /** |
| * @brief Fill out PartLocationContext of a redfish resource. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json path of where to put the location context |
| * @param[in] label Service label to update the location context with |
| */ |
| inline void updateLocationContext( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& label) { |
| BMCWEB_LOG_DEBUG << "Add service label " << label |
| << " to PartLocationContext"; |
| nlohmann::json& propertyJson = |
| asyncResp->res.jsonValue[jsonPtr]["PartLocationContext"]; |
| const std::string* val = propertyJson.get_ptr<const std::string*>(); |
| if (val != nullptr && !val->empty()) { |
| propertyJson = label + "/" + *val; |
| } else { |
| propertyJson = label; |
| } |
| } |
| |
| /** |
| * @brief Fill out Oem.LocationContext.ServiceLabel of a redfish resource. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json path of where to put the location context |
| * @param[in] label Service label to update the location context with |
| */ |
| inline void updateOemServiceLabel( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& label) { |
| asyncResp->res |
| .jsonValue[jsonPtr]["Google"]["LocationContext"]["ServiceLabel"] = label; |
| } |
| |
| /** |
| * @brief Fill out Oem devpath of a redfish resource. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json path of where to put the Oem Devpath |
| * @param[in] label Service label to update the Oem Devpath with |
| */ |
| inline void updateLocationOemDevpath( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& label) { |
| BMCWEB_LOG_DEBUG << "update devpath" << label << "to Oem/Google/Devpath"; |
| const std::string prefix = "/phys/"; |
| nlohmann::json& propertyJson = |
| asyncResp->res.jsonValue[jsonPtr]["Oem"]["Google"]["Devpath"]; |
| std::string* val = propertyJson.get_ptr<std::string*>(); |
| if (val != nullptr && !val->empty()) { |
| if (val->rfind(prefix, 0) == 0) { |
| val->erase(0, prefix.length()); |
| propertyJson = prefix + label + "/" + *val; |
| } else { |
| BMCWEB_LOG_ERROR << "Oem devpath " << *val << " missing /phys/"; |
| return; |
| } |
| } else { |
| propertyJson = prefix + label; |
| } |
| } |
| |
| /** |
| * @brief Context for selecting the best cable among multiple attached cables. |
| * Uses RAII to trigger the completion callback when all async requests are |
| * done. |
| */ |
| struct CableSelectionContext { |
| std::optional<std::pair<std::string, std::string>> bestCable; |
| absl::AnyInvocable<void( |
| const std::optional<std::pair<std::string, std::string>>&)> |
| onComplete; |
| |
| explicit CableSelectionContext( |
| absl::AnyInvocable< |
| void(const std::optional<std::pair<std::string, std::string>>&)> |
| callback) |
| : onComplete(std::move(callback)) {} |
| |
| ~CableSelectionContext() { onComplete(bestCable); } |
| |
| CableSelectionContext(const CableSelectionContext&) = delete; |
| CableSelectionContext& operator=(const CableSelectionContext&) = delete; |
| CableSelectionContext(CableSelectionContext&&) = delete; |
| CableSelectionContext& operator=(CableSelectionContext&&) = delete; |
| }; |
| |
| /** |
| * @brief Parses given chassis subtree for object path and connection to retrive |
| * chassis location code used to fill PartLocationContext. |
| * |
| * @param[in] upstreamChassisPaths Set of chassis object paths queried. |
| * @param[in] jsonPtr Json location to fill PartLocationContext |
| * @param[in] currentLocationCode Location code of the current chassis. |
| * @param[in] withOemDevpath Whether to fill Oem Devpath. |
| * @param[in] associationPath Path used to associate with upstream chassis |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] subtree Subtree associated with chassis endpoint |
| */ |
| inline void getAssociatedChassisSubtreeCallback( |
| std::unordered_set<std::string> upstreamChassisPaths, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& currentLocationCode, |
| PopulateLocationContextOptions populateLocationContextOptions, |
| const std::string& associationPath, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| // Base case as we recurse to get upstream chassis association. |
| if (subtree.empty()) { |
| BMCWEB_LOG_DEBUG << "Chassis association not found." |
| "Must be root chassis or link is broken"; |
| std::string currentPath; |
| size_t pos = associationPath.find_last_of('/'); |
| if (pos != std::string::npos) { |
| currentPath = associationPath.substr(0, pos); |
| } |
| |
| if (currentPath.empty()) { |
| return; |
| } |
| |
| // Check for attached cables |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| |
| dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", currentPath + "/attached_cables", |
| "xyz.openbmc_project.Association", "endpoints", requestContext, |
| [asyncResp, jsonPtr, populateLocationContextOptions, |
| currentLocationCode, currentPath, |
| upstreamChassisPaths](const boost::system::error_code ec, |
| const std::vector<std::string>& cableList) { |
| if (ec || cableList.empty()) { |
| return; |
| } |
| |
| auto selectionContext = std::make_shared<CableSelectionContext>( |
| [asyncResp, jsonPtr, populateLocationContextOptions, |
| upstreamChassisPaths, currentLocationCode, currentPath]( |
| const std::optional<std::pair<std::string, std::string>>& |
| bestResult) mutable { |
| if (!bestResult.has_value()) { |
| return; |
| } |
| |
| const auto& [bestLocationCode, bestCablePath] = |
| bestResult.value(); |
| |
| switch (populateLocationContextOptions) { |
| case PopulateLocationContextOptions::kWithOemDevpathOnly: |
| updateLocationOemDevpath(asyncResp, jsonPtr, |
| bestLocationCode); |
| break; |
| case PopulateLocationContextOptions::kWithPartLocationContext: |
| updateLocationContext(asyncResp, jsonPtr, bestLocationCode); |
| break; |
| case PopulateLocationContextOptions:: |
| kWithPartLocationContextAndOemDevpath: |
| updateLocationContext(asyncResp, jsonPtr, bestLocationCode); |
| updateLocationOemDevpath(asyncResp, jsonPtr, |
| bestLocationCode); |
| break; |
| } |
| |
| // Recurse upward to the cable's upstream chassis. |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, bestCablePath + "/upstream_chassis", |
| std::bind_front(getAssociatedChassisSubtreeCallback, |
| upstreamChassisPaths, jsonPtr, |
| bestLocationCode, |
| populateLocationContextOptions, |
| bestCablePath + "/upstream_chassis")); |
| }); |
| |
| for (const std::string& cablePath : cableList) { |
| managedStore::ManagedObjectStoreContext cableAssocRequestContext( |
| asyncResp); |
| // Check if the chassis is downstream of this cable. |
| dbus_utils::getProperty<std::vector<std::string>>( |
| "xyz.openbmc_project.ObjectMapper", |
| cablePath + "/downstream_chassis", |
| "xyz.openbmc_project.Association", "endpoints", |
| cableAssocRequestContext, |
| [asyncResp, cablePath, currentPath, selectionContext]( |
| const boost::system::error_code ec2, |
| const std::vector<std::string>& endpoints) mutable { |
| bool isMatchingCable = false; |
| |
| if (!ec2) { |
| isMatchingCable = |
| std::find(endpoints.begin(), endpoints.end(), |
| currentPath) != endpoints.end(); |
| } |
| |
| if (isMatchingCable) { |
| // Valid cable, so we need to get its LocationCode. |
| managedStore::ManagedObjectStoreContext cableRequestContext( |
| asyncResp); |
| dbus_utils::getProperty<std::string>( |
| "xyz.openbmc_project.EntityManager", cablePath, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", |
| "LocationCode", cableRequestContext, |
| [selectionContext, cablePath]( |
| const boost::system::error_code ec3, |
| const std::string& cableLocationCode) mutable { |
| if (!ec3) { |
| if (!selectionContext->bestCable || |
| cableLocationCode < |
| selectionContext->bestCable->first) { |
| selectionContext->bestCable = {cableLocationCode, |
| cablePath}; |
| } |
| } |
| }); |
| } |
| }); |
| } |
| }); |
| |
| return; |
| } |
| |
| if (subtree.size() > 1) { |
| BMCWEB_LOG_ERROR << "Found multiple upstream chassis."; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| auto chassisPathToConnMap = subtree.begin(); |
| |
| // Upstream chassis. |
| const std::string& chassisPath = chassisPathToConnMap->first; |
| if (!upstreamChassisPaths.emplace(chassisPath).second) { |
| BMCWEB_LOG_ERROR << "Loop detected in upstream chassis associations."; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| const dbus::utility::MapperServiceMap& serviceMap = |
| chassisPathToConnMap->second; |
| if (serviceMap.empty()) { |
| BMCWEB_LOG_ERROR << "Associated chassis obj path " << chassisPath |
| << " has no service mapping."; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| if (serviceMap.size() > 1) { |
| BMCWEB_LOG_ERROR << "Associated chassis path " << chassisPath |
| << " found in multiple connections"; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| // Get service name to retrieve upstream chassis location code property. |
| const std::string& service = serviceMap.begin()->first; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<std::string>( |
| service, chassisPath, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", |
| requestContext, |
| [asyncResp, jsonPtr, chassisPath, currentLocationCode, |
| upstreamChassisPaths, |
| populateLocationContextOptions](const boost::system::error_code ec2, |
| const std::string& locationCode) { |
| if (ec2) { |
| BMCWEB_LOG_DEBUG << "DBUS response error for Location, ec: " |
| << ec2.message(); |
| return; |
| } |
| |
| std::string nextLocationCode; |
| if (currentLocationCode != locationCode) { |
| switch (populateLocationContextOptions) { |
| case PopulateLocationContextOptions::kWithOemDevpathOnly: |
| updateLocationOemDevpath(asyncResp, jsonPtr, locationCode); |
| break; |
| case PopulateLocationContextOptions::kWithPartLocationContext: |
| updateLocationContext(asyncResp, jsonPtr, locationCode); |
| break; |
| case PopulateLocationContextOptions:: |
| kWithPartLocationContextAndOemDevpath: |
| updateLocationContext(asyncResp, jsonPtr, locationCode); |
| updateLocationOemDevpath(asyncResp, jsonPtr, locationCode); |
| break; |
| } |
| } else { |
| // Continue checking to see if the locationCode matches |
| nextLocationCode = currentLocationCode; |
| } |
| |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, chassisPath + "/contained_by", |
| std::bind_front(getAssociatedChassisSubtreeCallback, |
| upstreamChassisPaths, jsonPtr, nextLocationCode, |
| populateLocationContextOptions, |
| chassisPath + "/contained_by")); |
| }); |
| } |
| |
| /** |
| * @brief Recursively gets LocationCode for upstream chassis to populate |
| * PartLocationContext. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json location to fill PartLocationContext |
| * @param[in] associationPath Path used to associate with upstream chassis |
| */ |
| inline void getPartLocationContext( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath, const std::string& locationCode) { |
| BMCWEB_LOG_DEBUG << "Get chassis endpoints associated with " |
| << associationPath; |
| // Set of chassis object paths used to detect cycle as we resolve usptream |
| // chassis associations to get to root chassis to create PartLocationContext |
| std::unordered_set<std::string> upstreamChassisPaths; |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, associationPath, |
| std::bind_front(getAssociatedChassisSubtreeCallback, |
| std::move(upstreamChassisPaths), jsonPtr, locationCode, |
| PopulateLocationContextOptions::kWithPartLocationContext, |
| associationPath)); |
| } |
| |
| /** |
| * @brief Recursively gets LocationCode for upstream chassis to populate oem |
| * devpath. This translates "/phys/A/SL" to "/phys/.../DC_DC_A/DC_A/A/SL" |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json location to fill PartLocationContext |
| * @param[in] associationPath Path used to associate with upstream chassis |
| */ |
| inline void getLocationContextWithOemDevpath( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath, const std::string& locationCode) { |
| BMCWEB_LOG_DEBUG << "Get chassis endpoints associated with " |
| << associationPath |
| << " for location context and oem devpath"; |
| // Set of chassis object paths used to detect cycle as we resolve upstream |
| // chassis associations to get to root chassis to create PartLocationContext |
| std::unordered_set<std::string> upstreamChassisPaths; |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, associationPath, |
| std::bind_front( |
| getAssociatedChassisSubtreeCallback, std::move(upstreamChassisPaths), |
| jsonPtr, locationCode, |
| PopulateLocationContextOptions::kWithPartLocationContextAndOemDevpath, |
| associationPath)); |
| } |
| |
| /** |
| * @brief Gets LocationContext for a given chassis. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json location to fill PartLocationContext |
| * @param[in] associationPath Path used to associate with upstream chassis |
| */ |
| inline void getLocationContextWithOemDevpathOnly( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath, const std::string& locationCode) { |
| BMCWEB_LOG_DEBUG << "Get chassis endpoints associated with " |
| << associationPath |
| << " for location context and oem devpath"; |
| // Set of chassis object paths used to detect cycle as we resolve upstream |
| // chassis associations to get to root chassis to create PartLocationContext |
| std::unordered_set<std::string> upstreamChassisPaths; |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, associationPath, |
| std::bind_front(getAssociatedChassisSubtreeCallback, |
| std::move(upstreamChassisPaths), jsonPtr, locationCode, |
| PopulateLocationContextOptions::kWithOemDevpathOnly, |
| associationPath)); |
| } |
| |
| /** |
| * @brief Fill out location code of a resource from given managed objects. |
| * |
| * @param[in,out] aResp Async HTTP response. |
| * @param[in] objPath D-Bus object to query. |
| * @param[in] interface D-Bus interface to query. |
| * @param[in] jsonPtr Json pointer to populate. |
| * @param[in] managedObjects Managed objects. |
| */ |
| inline void getLocationCodeFromManagedObject( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& objPath, |
| const std::string& interface, const nlohmann::json::json_pointer& jsonPtr, |
| const dbus::utility::ManagedObjectType& managedObjects) { |
| dbus::utility::DBusPropertiesMap properties; |
| dbus_utils::getPropertiesFromManagedObjects(managedObjects, objPath, |
| interface, properties); |
| |
| const std::string* locationProperty = nullptr; |
| |
| const bool success = sdbusplus::unpackPropertiesNoThrow( |
| dbus_utils::UnpackErrorPrinter(), properties, "LocationCode", |
| locationProperty); |
| |
| if (!success) { |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| if (locationProperty != nullptr) { |
| aResp->res.jsonValue[jsonPtr]["PartLocation"]["ServiceLabel"] = |
| *locationProperty; |
| } |
| } |
| |
| /** |
| * @brief Validate the association subtree. |
| * |
| * @param[in] upstreamChassisPaths Set of chassis object paths queried. |
| * @param[in] subtree Subtree associated with chassis endpoint |
| */ |
| |
| inline bool validateSubtree( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (subtree.empty()) { |
| BMCWEB_LOG_DEBUG << "Chassis association not found." |
| "Must be root chassis or link is broken"; |
| return false; |
| } |
| if (subtree.size() > 1) { |
| BMCWEB_LOG_ERROR << "Found multiple upstream chassis."; |
| messages::internalError(asyncResp->res); |
| return false; |
| } |
| auto chassisPathToConnMap = subtree.begin(); |
| // Upstream chassis. |
| const std::string& chassisPath = chassisPathToConnMap->first; |
| const dbus::utility::MapperServiceMap& serviceMap = |
| chassisPathToConnMap->second; |
| if (serviceMap.empty()) { |
| BMCWEB_LOG_ERROR << "Associated chassis obj path " << chassisPath |
| << " has no service mapping."; |
| messages::internalError(asyncResp->res); |
| return false; |
| } |
| if (serviceMap.size() > 1) { |
| BMCWEB_LOG_ERROR << "Associated chassis path " << chassisPath |
| << " found in multiple connections"; |
| messages::internalError(asyncResp->res); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * @brief Parses given chassis subtree for object path and connection to retrive |
| * chassis location code used to fill Oem ServiceLabel. |
| * |
| * @param[in] jsonPtr Json location to fill PartLocationContext |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] subtree Subtree associated with chassis endpoint |
| */ |
| inline void getOemServiceLabelCallback( |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (!validateSubtree(asyncResp, subtree)) { |
| BMCWEB_LOG_DEBUG << "Not a valid subree"; |
| return; |
| } |
| auto chassisPathToConnMap = subtree.begin(); |
| |
| // Upstream chassis. |
| const std::string& chassisPath = chassisPathToConnMap->first; |
| const dbus::utility::MapperServiceMap& serviceMap = |
| chassisPathToConnMap->second; |
| // Get service name to retrieve upstream chassis location code property. |
| const std::string& service = serviceMap.begin()->first; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<std::string>( |
| service, chassisPath, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", |
| requestContext, |
| [asyncResp, jsonPtr, chassisPath](const boost::system::error_code ec2, |
| const std::string& locationCode) { |
| if (ec2) { |
| BMCWEB_LOG_DEBUG << "DBUS response error for Location, ec: " |
| << ec2.message(); |
| return; |
| } |
| updateOemServiceLabel(asyncResp, jsonPtr, locationCode); |
| }); |
| } |
| |
| /** |
| * @brief gets LocationCode for upstream chassis to populate |
| * Oem ServiceLabel. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json location to fill ServiceLabel |
| * @param[in] associationPath Path used to associate with upstream chassis |
| */ |
| inline void getOemServiceLabel( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath) { |
| // Oem.Google.LocationContext.ServiceLabel is ServiceLabel inherited from |
| // parent replaceable resource. |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, associationPath, |
| std::bind_front(getOemServiceLabelCallback, jsonPtr)); |
| } |
| |
| /** |
| * @brief Fill out Oem PartLocationContext of a redfish resource. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json path of where to put the location context |
| * @param[in] label Service label to update the location context with |
| */ |
| inline void updateOemPartLocationContext( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, const std::string& label) { |
| nlohmann::json& propertyJson = |
| asyncResp->res.jsonValue[jsonPtr]["Google"]["LocationContext"] |
| ["PartLocationContext"]; |
| const std::string* val = propertyJson.get_ptr<const std::string*>(); |
| if (val != nullptr && !val->empty()) { |
| propertyJson = label + "/" + *val; |
| } else { |
| propertyJson = label; |
| } |
| } |
| |
| /** |
| * @brief Parses given chassis subtree for object path and connection to retrive |
| * chassis location code used to fill Oem PartLocationContext. |
| * |
| * @param[in] upstreamChassisPaths Set of chassis object paths queried. |
| * @param[in] jsonPtr Json location to fill PartLocationContext |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] subtree Subtree associated with chassis endpoint |
| */ |
| inline void getOemPartLocationContextCallback( |
| std::unordered_set<std::string> upstreamChassisPaths, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (!validateSubtree(asyncResp, subtree)) { |
| BMCWEB_LOG_DEBUG << "Not a valid subree"; |
| return; |
| } |
| |
| auto chassisPathToConnMap = subtree.begin(); |
| // Upstream chassis. |
| const std::string& chassisPath = chassisPathToConnMap->first; |
| if (!upstreamChassisPaths.emplace(chassisPath).second) { |
| BMCWEB_LOG_ERROR << "Loop detected in upstream chassis associations."; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| const dbus::utility::MapperServiceMap& serviceMap = |
| chassisPathToConnMap->second; |
| |
| // Get service name to retrieve upstream chassis location code property. |
| const std::string& service = serviceMap.begin()->first; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<std::string>( |
| service, chassisPath, |
| "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", |
| requestContext, |
| [asyncResp, jsonPtr, chassisPath, upstreamChassisPaths]( |
| const boost::system::error_code ec2, |
| const std::string& locationCode) { |
| if (ec2) { |
| BMCWEB_LOG_DEBUG << "DBUS response error for Location, ec: " |
| << ec2.message(); |
| return; |
| } |
| |
| updateOemPartLocationContext(asyncResp, jsonPtr, locationCode); |
| |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, chassisPath + "/contained_by", |
| std::bind_front(getOemPartLocationContextCallback, |
| upstreamChassisPaths, jsonPtr)); |
| }); |
| } |
| |
| /** |
| * @brief Recursively gets LocationCode for upstream chassis to populate |
| * Oem PartLocationContext. |
| * |
| * @param[in,out] asyncResp Async HTTP response |
| * @param[in] jsonPtr Json location to fill PartLocationContext |
| * @param[in] associationPath Path used to associate with upstream chassis |
| */ |
| inline void getOemPartLocationContext( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const nlohmann::json::json_pointer& jsonPtr, |
| const std::string& associationPath) { |
| // Oem.Google.LocationContext.PartLocationContext is PartLocationContext |
| // inherited from parent replaceable resource. |
| asyncResp->res |
| .jsonValue[jsonPtr]["Google"]["LocationContext"]["PartLocationContext"] = |
| ""; |
| std::unordered_set<std::string> upstreamChassisPaths; |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, associationPath, |
| [jsonPtr, upstreamChassisPaths]( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const dbus::utility::MapperGetSubTreeResponse& subtree) { |
| if (!validateSubtree(asyncResp, subtree)) { |
| BMCWEB_LOG_DEBUG << "Not a valid subree"; |
| return; |
| } |
| // Upstream chassis. |
| const std::string& chassisPath = subtree.begin()->first; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| chassis_utils::getAssociatedChassisSubtree( |
| asyncResp, chassisPath + "/contained_by", |
| std::bind_front(getOemPartLocationContextCallback, |
| upstreamChassisPaths, jsonPtr)); |
| }); |
| } |
| |
| } // namespace location_util |
| } // namespace redfish |
| |
| #endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_LOCATION_UTILS_H_ |