blob: 6e8d2e2d8a3eddb163f990337225eaed02d2e2a1 [file] [log] [blame]
#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