blob: 980e8617b41234e627c98880c118410cca815208 [file] [log] [blame]
#pragma once
#include "async_resp.hpp"
#include "chassis_utils.hpp"
#include "dbus_utility.hpp"
#include "dbus_utils.hpp"
#include "error_messages.hpp"
#include "managed_store_types.hpp"
#include <sdbusplus/asio/property.hpp>
#include <string>
#include <unordered_set>
namespace redfish
{
namespace location_util
{
/**
* @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 getOemDevpathLocation(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 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 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,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, const bool isOemDevpath,
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";
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,
isOemDevpath](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)
{
if (!isOemDevpath)
{
updateLocationContext(asyncResp, jsonPtr, locationCode);
}
else
{
updateLocationOemDevpath(asyncResp, jsonPtr, locationCode);
}
}
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,
isOemDevpath));
});
}
/**
* @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,
false));
}
/**
* @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
getOemDevpathLocation(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 oem devpath";
// 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,
true));
}
/**
* @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 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 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