blob: 4e59324bc7b7eee52a30189c4f7533eea33ad61f [file] [log] [blame]
#pragma once
#include "bmcweb_config.h"
#include "dbus_utility.hpp"
#include "managed_store_types.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
#include "utils/collection.hpp"
#include "utils/dbus_utils.hpp"
#include "utils/json_utils.hpp"
#include "utils/location_utils.hpp"
#include <boost/system/error_code.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <array>
#include <string_view>
#include "managed_store.hpp"
#ifdef UNIT_TEST_BUILD
#include "test/g3/mock_managed_store.hpp" // NOLINT
#endif
namespace redfish
{
/**
* @brief Get Cable's asset properties.
* @param[in,out] resp HTTP response.
* @param[in] service Cable's service name.
* @param[in] path Cable's path.
*/
inline void getCableAsset(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& service, const std::string& path)
{
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getAllProperties(
service, path, "xyz.openbmc_project.Inventory.Decorator.Asset", context,
[path, asyncResp](const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& assetList) {
if (ec)
{
if (ec.value() != EBADR)
{
BMCWEB_LOG_ERROR << "DBUS response error for Properties"
<< ec.value();
messages::internalError(asyncResp->res);
}
return;
}
const std::string* model = nullptr;
const std::string* manufacturer = nullptr;
const std::string* partNumber = nullptr;
const std::string* serialNumber = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), assetList, "Model", model,
"Manufacturer", manufacturer, "PartNumber", partNumber,
"SerialNumber", serialNumber);
if (!success)
{
messages::internalError(asyncResp->res);
return;
}
if (model != nullptr)
{
asyncResp->res.jsonValue["Model"] = *model;
}
if (manufacturer != nullptr)
{
asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
}
if (partNumber != nullptr)
{
asyncResp->res.jsonValue["PartNumber"] = *partNumber;
}
if (serialNumber != nullptr)
{
asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
}
});
}
/**
* @brief Fill cable specific properties.
* @param[in,out] resp HTTP response.
* @param[in] ec Error code corresponding to Async method call.
* @param[in] properties List of Cable Properties key/value pairs.
*/
inline void
fillCableProperties(crow::Response& resp,
const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& properties)
{
if (ec)
{
BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
messages::internalError(resp);
return;
}
const std::string* cableTypeDescription = nullptr;
const double* length = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), properties, "CableTypeDescription",
cableTypeDescription, "Length", length);
if (!success)
{
messages::internalError(resp);
return;
}
if (cableTypeDescription != nullptr)
{
resp.jsonValue["CableType"] = *cableTypeDescription;
}
if (length != nullptr)
{
if (!std::isfinite(*length))
{
// Cable length is NaN by default, do not throw an error
if (!std::isnan(*length))
{
messages::internalError(resp);
return;
}
}
else
{
resp.jsonValue["LengthMeters"] = *length;
}
}
}
/**
* @brief Create Links for Chassis in Cable resource.
* @param[in,out] asyncResp Async HTTP response.
* @param[in] associationPath Cable association path.
* @param[in] chassisPropertyName Chassis of PropertyName of Cable.
*/
inline void getCableChassisAssociation(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& associationPath, const std::string& chassisPropertyName)
{
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
dbus_utils::getProperty<std::vector<std::string>>(
"xyz.openbmc_project.ObjectMapper", associationPath,
"xyz.openbmc_project.Association", "endpoints", requestContext,
[asyncResp, chassisPropertyName](const boost::system::error_code ec,
const std::vector<std::string>& resp) {
if (ec)
{
return; // no downstream_chassis = no failures
}
nlohmann::json& chassis =
asyncResp->res.jsonValue["Links"][chassisPropertyName];
chassis = nlohmann::json::array();
const std::string chassisCollectionPath = "/redfish/v1/Chassis";
for (const std::string& chassisPath : resp)
{
BMCWEB_LOG_INFO << chassisPath << "chassis path";
sdbusplus::message::object_path path(chassisPath);
std::string leaf = path.filename();
if (leaf.empty())
{
continue;
}
std::string newPath = chassisCollectionPath;
newPath += "/";
newPath += leaf;
chassis.emplace_back(nlohmann::json({{"@odata.id", std::move(newPath)}}));
}
});
}
/**
* @brief Api to get Cable properties.
* @param[in,out] asyncResp Async HTTP response.
* @param[in] cableObjectPath Object path of the Cable.
* @param[in] serviceMap A map to hold Service and corresponding
* interface list for the given cable id.
*/
inline void
getCableProperties(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& cableObjectPath,
const dbus::utility::MapperServiceMap& serviceMap)
{
BMCWEB_LOG_DEBUG << "Get Properties for cable " << cableObjectPath;
for (const auto& [service, interfaces] : serviceMap)
{
for (const auto& interface : interfaces)
{
if (interface == "xyz.openbmc_project.Inventory.Item.Cable")
{
managedStore::ManagedObjectStoreContext context(asyncResp);
managedStore::GetManagedObjectStore()->getAllProperties(
service, cableObjectPath, interface, context,
[asyncResp](
const boost::system::error_code ec,
const dbus::utility::DBusPropertiesMap& properties) {
fillCableProperties(asyncResp->res, ec, properties);
});
}
else if (interface ==
"xyz.openbmc_project.Inventory.Decorator.LocationCode")
{
location_util::getLocationCode(asyncResp, service,
cableObjectPath,
"/Location"_json_pointer);
location_util::getPartLocationContext(
asyncResp, "/Location"_json_pointer,
cableObjectPath + "/upstream_chassis");
}
else if (location_util::isConnector(interface))
{
std::optional<std::string> locationType =
location_util::getLocationType(interface);
if (!locationType)
{
BMCWEB_LOG_DEBUG << "getLocationType for Cable failed for "
<< interface;
continue;
}
asyncResp->res
.jsonValue["Location"]["PartLocation"]["LocationType"] =
*locationType;
}
else if (interface ==
"xyz.openbmc_project.Inventory.Decorator.Asset")
{
getCableAsset(asyncResp, service, cableObjectPath);
}
else if (interface == "xyz.openbmc_project.Inventory.Item")
{
managedStore::ManagedObjectStoreContext requestContext(
asyncResp);
dbus_utils::getProperty<bool>(
service, cableObjectPath, interface, "Present",
requestContext,
[asyncResp, cableObjectPath](
const boost::system::error_code& ec, bool present) {
if (ec)
{
BMCWEB_LOG_DEBUG << "get presence failed for Cable "
<< cableObjectPath << "with error "
<< ec;
return;
}
if (!present)
{
asyncResp->res.jsonValue["Status"]["State"] = "Absent";
}
});
}
}
}
}
inline void handleCableGet(App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& cableId)
{
if (!redfish::setUpRedfishRoute(app, req, asyncResp))
{
return;
}
BMCWEB_LOG_DEBUG << "Cable Id: " << cableId;
constexpr std::array<std::string_view, 1> interfaces = {
"xyz.openbmc_project.Inventory.Item.Cable"};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTree(
"/xyz/openbmc_project/inventory", 0, interfaces, requestContext,
[asyncResp,
cableId](const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec.value() == EBADR)
{
messages::resourceNotFound(asyncResp->res, "Cable", cableId);
return;
}
if (ec)
{
BMCWEB_LOG_ERROR << "DBUS response error " << ec;
messages::internalError(asyncResp->res);
return;
}
for (const auto& [objectPath, serviceMap] : subtree)
{
sdbusplus::message::object_path path(objectPath);
if (path.filename() != cableId)
{
continue;
}
if constexpr (enablePlatform9)
{
// Hardcode relations
if (cableId == "cat5_cable")
{
nlohmann::json::array_t upArray;
nlohmann::json::object_t upObj;
upObj["@odata.id"] =
"/redfish/v1/Chassis/" + std::string(platform9Chassis0);
upArray.emplace_back(std::move(upObj));
asyncResp->res.jsonValue["Links"]["UpstreamChassis"] =
std::move(upArray);
}
if (cableId == platform9Cable0)
{
nlohmann::json::array_t downArray;
nlohmann::json::array_t upArray;
nlohmann::json::object_t downObj;
nlohmann::json::object_t upObj;
downObj["@odata.id"] =
"/redfish/v1/Cables/" + std::string(platform9Cable1);
downArray.emplace_back(std::move(downObj));
asyncResp->res.jsonValue["Links"]["DownstreamResources"] =
std::move(downArray);
upObj["@odata.id"] =
"/redfish/v1/Chassis/" + std::string(platform9Chassis0);
upArray.emplace_back(std::move(upObj));
asyncResp->res.jsonValue["Links"]["UpstreamChassis"] =
std::move(upArray);
}
if (cableId == platform9Cable1)
{
nlohmann::json::array_t downArray;
nlohmann::json::array_t upArray;
nlohmann::json::object_t downObj;
nlohmann::json::object_t upObj;
downObj["@odata.id"] =
"/redfish/v1/Cables/" + std::string(platform9Cable2);
downArray.emplace_back(std::move(downObj));
asyncResp->res.jsonValue["Links"]["DownstreamResources"] =
std::move(downArray);
upObj["@odata.id"] =
"/redfish/v1/Cables/" + std::string(platform9Cable0);
upArray.emplace_back(std::move(upObj));
asyncResp->res.jsonValue["Links"]["UpstreamResources"] =
std::move(upArray);
}
if (cableId == platform9Cable2)
{
nlohmann::json::array_t downArray;
nlohmann::json::array_t upArray;
nlohmann::json::object_t downObj;
nlohmann::json::object_t upObj;
downObj["@odata.id"] =
"/redfish/v1/Chassis/" + std::string(platform9Chassis3);
downArray.emplace_back(std::move(downObj));
asyncResp->res.jsonValue["Links"]["DownstreamResources"] =
std::move(downArray);
upObj["@odata.id"] =
"/redfish/v1/Cables/" + std::string(platform9Cable1);
upArray.emplace_back(std::move(upObj));
asyncResp->res.jsonValue["Links"]["UpstreamResources"] =
std::move(upArray);
}
if (cableId == platform9Cable3)
{
nlohmann::json::array_t downArray;
nlohmann::json::array_t upArray;
nlohmann::json::object_t downObj;
nlohmann::json::object_t upObj;
downObj["@odata.id"] =
"/redfish/v1/Cables/" + std::string(platform9Cable4);
downArray.emplace_back(std::move(downObj));
asyncResp->res.jsonValue["Links"]["DownstreamResources"] =
std::move(downArray);
upObj["@odata.id"] =
"/redfish/v1/Chassis/" + std::string(platform9Chassis3);
upArray.emplace_back(std::move(upObj));
asyncResp->res.jsonValue["Links"]["UpstreamResources"] =
std::move(upArray);
}
if (cableId == platform9Cable4)
{
nlohmann::json::array_t downArray;
nlohmann::json::array_t upArray;
nlohmann::json::object_t downObj;
nlohmann::json::object_t upObj;
downObj["@odata.id"] =
"/redfish/v1/Chassis/" + std::string(platform9Chassis2);
downArray.emplace_back(std::move(downObj));
asyncResp->res.jsonValue["Links"]["DownstreamResources"] =
std::move(downArray);
upObj["@odata.id"] =
"/redfish/v1/Cables/" + std::string(platform9Cable3);
upArray.emplace_back(std::move(upObj));
asyncResp->res.jsonValue["Links"]["UpstreamResources"] =
std::move(upArray);
}
}
asyncResp->res.jsonValue["@odata.type"] = "#Cable.v1_0_0.Cable";
asyncResp->res.jsonValue["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Cables",
cableId);
asyncResp->res.jsonValue["Id"] = cableId;
asyncResp->res.jsonValue["Name"] = "Cable";
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
getCableProperties(asyncResp, objectPath, serviceMap);
getCableChassisAssociation(asyncResp,
objectPath + "/downstream_chassis",
"DownstreamChassis");
getCableChassisAssociation(
asyncResp, objectPath + "/upstream_chassis", "UpstreamChassis");
return;
}
messages::resourceNotFound(asyncResp->res, "Cable", cableId);
});
}
/**
* The Cable schema
*/
inline void requestRoutesCable(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/Cables/<str>/")
.privileges(redfish::privileges::getCable)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleCableGet, std::ref(app)));
}
inline void handleCableCollectionGet(
App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
{
if (!redfish::setUpRedfishRoute(app, req, asyncResp))
{
return;
}
asyncResp->res.jsonValue["@odata.type"] =
"#CableCollection.CableCollection";
asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Cables";
asyncResp->res.jsonValue["Name"] = "Cable Collection";
asyncResp->res.jsonValue["Description"] = "Collection of Cable Entries";
constexpr std::array<std::string_view, 1> interfaces{
"xyz.openbmc_project.Inventory.Item.Cable"};
collection_util::getCollectionMembers(
asyncResp, boost::urls::url("/redfish/v1/Cables"), interfaces);
}
/**
* Collection of Cable resource instances
*/
inline void requestRoutesCableCollection(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/Cables/")
.privileges(redfish::privileges::getCableCollection)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleCableCollectionGet, std::ref(app)));
}
} // namespace redfish