blob: df653cf92dd70cb07f75c3b8d2a57c4086b3089a [file] [log] [blame]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_COLLECTION_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_COLLECTION_H_
#include <algorithm>
#include <memory>
#include <set>
#include <span> // NOLINT
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "boost/url/url.hpp" // NOLINT
#include "logging.hpp"
#include "utility.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "human_sort.hpp"
#include "error_messages.hpp"
#include "dbus_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 {
namespace collection_util {
/**
* @brief Populate the collection "Members" from given dbus subtree.
*
* @param[in] aResp Async response object
* @param[in] collectionPath Redfish collection path which is used for the
* Members Redfish Path
* @param[in] interfaces Interface list to constrain the subtree search
* @param[in] subtree MapperGetSubTreeResponse to parse
*
* @return void
*/
inline void getCollectionMembersFromSubtree(
const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const boost::urls::url& collectionPath,
std::span<const std::string_view> interfaces,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
BMCWEB_LOG_DEBUG << "Get collection members for: " << collectionPath.buffer();
std::set<std::string, AlphanumLess<std::string>> pathNames;
for (const auto& [objectPath, serviceMap] : subtree) {
if (!dbus_utils::findInterfacesInServiceMap(serviceMap, interfaces)) {
continue;
}
sdbusplus::message::object_path path(objectPath);
std::string leaf = path.filename();
if (leaf.empty()) {
continue;
}
pathNames.insert(leaf);
}
nlohmann::json& members = aResp->res.jsonValue["Members"];
members = nlohmann::json::array();
for (const std::string& leaf : pathNames) {
boost::urls::url url = collectionPath;
crow::utility::appendUrlPieces(url, leaf);
nlohmann::json::object_t member;
member["@odata.id"] = std::move(url);
members.push_back(std::move(member));
}
aResp->res.jsonValue["Members@odata.count"] = members.size();
}
/**
* @brief Populate the collection "Members" from a GetSubTreePaths search of
* inventory
*
* @param[i,o] aResp Async response object
* @param[i] collectionPath Redfish collection path which is used for the
* Members Redfish Path
* @param[i] interfaces List of interfaces to constrain the GetSubTree search
* @param[in] subtree D-Bus base path to constrain search to.
*
* @return void
*/
inline void getCollectionMembers(
std::shared_ptr<bmcweb::AsyncResp> aResp,
const boost::urls::url& collectionPath,
std::span<const std::string_view> interfaces,
const char* subtree = "/xyz/openbmc_project/inventory") {
BMCWEB_LOG_DEBUG << "Get collection members for: " << collectionPath.buffer();
managedStore::ManagedObjectStoreContext requestContext(aResp);
managedStore::GetManagedObjectStore()->getSubTreePaths(
subtree, 0, interfaces, requestContext,
[collectionPath, aResp{std::move(aResp)}](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreePathsResponse& objects) {
if (ec == boost::system::errc::io_error) {
auto foundMembers = aResp->res.jsonValue.find("Members");
if (foundMembers == aResp->res.jsonValue.end()) {
aResp->res.jsonValue["Members"] = nlohmann::json::array();
aResp->res.jsonValue["Members@odata.count"] = 0;
}
return;
}
if (ec) {
BMCWEB_LOG_DEBUG << "DBUS response error " << ec.value();
messages::internalError(aResp->res);
return;
}
std::vector<std::string> pathNames;
for (const auto& object : objects) {
sdbusplus::message::object_path path(object);
std::string leaf = path.filename();
if (leaf.empty()) {
continue;
}
pathNames.push_back(leaf);
}
std::sort(pathNames.begin(), pathNames.end(),
AlphanumLess<std::string>());
nlohmann::json& members = aResp->res.jsonValue["Members"];
if (!members.is_array()) {
// With Redfish aggregation, top level collections can have their
// Members array already created if a satellite response has been
// processed first. In that case we don't want to clobber the
// aggregated data
members = nlohmann::json::array();
}
for (const std::string& leaf : pathNames) {
boost::urls::url url = collectionPath;
crow::utility::appendUrlPieces(url, leaf);
nlohmann::json::object_t member;
member["@odata.id"] = std::move(url);
members.push_back(std::move(member));
}
aResp->res.jsonValue["Members@odata.count"] = members.size();
});
}
/**
* @brief Populate the collection "Members" from the Associations endpoint
* path
*
* @param[i,o] aResp Async response object
* @param[i] collectionPath Redfish collection path which is used for the
* Members Redfish Path
* @param[i] interfaces List of interfaces to constrain the associated
* members
* @param[in] associationEndPointPath Path that points to the association end
* point
* @param[in] subtree D-Bus base path to constrain search to.
*
* @return void
*/
inline void getAssociatedCollectionMembers(
const std::shared_ptr<bmcweb::AsyncResp>& aResp,
const boost::urls::url& collectionPath,
std::span<const std::string_view> interfaces,
const char* associationEndPointPath,
const char* subtree = "/xyz/openbmc_project/inventory") {
BMCWEB_LOG_DEBUG << "Get collection members for: " << collectionPath.buffer();
managedStore::ManagedObjectStoreContext requestContext(aResp);
managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths(
{associationEndPointPath}, {subtree}, 0, interfaces, requestContext,
[collectionPath, aResp, requestContext](
const boost::system::error_code& ec,
const std::vector<std::string>& objects) {
if (ec == boost::system::errc::io_error) {
auto foundMembers = aResp->res.jsonValue.find("Members");
if (foundMembers == aResp->res.jsonValue.end()) {
aResp->res.jsonValue["Members"] = nlohmann::json::array();
aResp->res.jsonValue["Members@odata.count"] = 0;
}
return;
}
if (ec) {
BMCWEB_LOG_DEBUG << "DBUS response error " << ec.value();
messages::internalError(aResp->res);
return;
}
std::vector<std::string> pathNames;
for (const auto& object : objects) {
sdbusplus::message::object_path path(object);
std::string leaf = path.filename();
if (leaf.empty()) {
continue;
}
pathNames.push_back(leaf);
}
std::sort(pathNames.begin(), pathNames.end(),
AlphanumLess<std::string>());
nlohmann::json& members = aResp->res.jsonValue["Members"];
if (!members.is_array()) {
// With Redfish aggregation, top level collections can have their
// Members array already created if a satellite response has been
// processed first. In that case we don't want to clobber the
// aggregated data
members = nlohmann::json::array();
}
for (const std::string& leaf : pathNames) {
boost::urls::url url = collectionPath;
crow::utility::appendUrlPieces(url, leaf);
nlohmann::json::object_t member;
member["@odata.id"] = std::move(url);
members.push_back(std::move(member));
}
aResp->res.jsonValue["Members@odata.count"] = members.size();
});
}
} // namespace collection_util
} // namespace redfish
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_COLLECTION_H_