blob: cb204bcc08ba45ac3fafb28b38234fc01d4c6542 [file] [log] [blame]
#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"
#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
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_ASSEMBLY_H_