blob: 4280a0e22d29c317f9102c8697623edaceb435c8 [file] [log] [blame]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_DBUS_UTILS_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_DBUS_UTILS_H_
#include <algorithm>
#include <cstdint>
#include <functional>
#include <set>
#include <span> // NOLINT
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "logging.hpp"
#include "dbus_utility.hpp"
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/exception.hpp"
#include "sdbusplus/message/native_types.hpp"
// Remove unpack_properties header. Have to have this header
// now since there are other files indirectly depending on this header and they
// haven't been ported to g3 yet.
#include "sdbusplus/unpack_properties.hpp" // NOLINT
#ifdef UNIT_TEST_BUILD
#include "test/g3/mock_managed_store.hpp" // NOLINT
#endif
namespace redfish {
namespace dbus_utils {
struct UnpackErrorPrinter {
void operator()(const sdbusplus::UnpackErrorReason reason,
const std::string& property) const noexcept {
BMCWEB_LOG_ERROR
<< "DBUS property error in property: " << property << ", reason: "
<< static_cast<std::underlying_type_t<sdbusplus::UnpackErrorReason>>(
reason);
}
};
inline void getPropertiesFromManagedObjects(
const dbus::utility::ManagedObjectType& managedObjects,
const std::string& objPath, const std::string& interface,
dbus::utility::DBusPropertiesMap& properties) {
for (const auto& object : managedObjects) {
if (object.first.str != objPath) {
continue;
}
for (const auto& interface_map : object.second) {
if (interface_map.first != interface) {
continue;
}
properties = interface_map.second;
}
}
}
// Populates |serviceNames| list with unique services found in the given
// subtree.
inline std::vector<std::string> getServiceNamesFromSubtree(
const dbus::utility::MapperGetSubTreeResponse& subtree) {
std::set<std::string> unique_service_names;
for (const auto& [_, serviceMap] : subtree) {
for (const auto& service_pair : serviceMap) {
unique_service_names.insert(service_pair.first);
}
}
return {unique_service_names.begin(), unique_service_names.end()};
}
// Checks if any one of given interfaces is present in the service map.
inline bool findInterfacesInServiceMap(
const dbus::utility::MapperServiceMap& serviceMap,
std::span<const std::string_view> interfaces) {
for (const auto& [service_name, interfaceList] : serviceMap) { // NOLINT
if (std::find_first_of(interfaceList.begin(), interfaceList.end(),
interfaces.begin(),
interfaces.end()) != interfaceList.end()) {
return true;
}
}
return false;
}
/**
* Recursively gets managed objects under given object path from each service in
* the given list of service names and maps objects to the service name they
* originate from.
*
* @param[in] serviceList Mutable list of unique service names.
* @param[in] objPath Managed objects are requested relative
* to this path.
* @param[in,out] managed_objects_by_service Service name to managed objects
* map.
* @param[in] callback Callback to execute after getting
* managed objects from all service names
* in the list.
*/
template <typename Callback>
inline void getManagedObjectsInEachServiceCallback(
std::vector<std::string>&& serviceList,
const sdbusplus::message::object_path& objPath,
std::unordered_map<std::string, dbus::utility::ManagedObjectType>&&
managed_objects_by_service,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
BMCWEB_LOG_DEBUG << "GetManagedObjects from next service in list.";
if (serviceList.empty()) {
BMCWEB_LOG_DEBUG << "Recieved managed objects from all services.";
callback(managed_objects_by_service);
return;
}
std::string service_name = serviceList.back();
serviceList.pop_back();
managedStore::GetManagedObjectStore()->getManagedObjectsWithContext(
service_name, objPath, context,
[serviceList{std::move(serviceList)}, service_name, objPath,
managed_objects_by_service{std::move(managed_objects_by_service)},
context = context, callback{std::forward<Callback>(callback)}](
const boost::system::error_code& ec,
const dbus::utility::ManagedObjectType& managedObjects) mutable {
if (!ec) {
managed_objects_by_service[service_name] = managedObjects;
}
getManagedObjectsInEachServiceCallback(
std::move(serviceList), objPath,
std::move(managed_objects_by_service), context,
std::forward<decltype(callback)>(callback));
});
}
/**
* Gets managed objects under given object path from each service in the given
* list of service names.
*
* @param[in] serviceList Mutable list of unique service names.
* @param[in] objPath Managed objects are requested relative
* to this path.
* @param[in] callback Callback to execute after getting
* managed objects from all service names
* in the list.
*/
template <typename Callback>
inline void getManagedObjectsInEachService(
std::vector<std::string>&& serviceList,
const sdbusplus::message::object_path& objPath,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
BMCWEB_LOG_DEBUG << "Requesting managed objects from given list of "
<< "services ";
std::unordered_map<std::string, dbus::utility::ManagedObjectType>
managed_objects_by_service;
if (serviceList.empty()) {
BMCWEB_LOG_ERROR << "Service list is empty";
callback(managed_objects_by_service);
return;
}
std::string service_name = serviceList.back();
serviceList.pop_back();
managedStore::GetManagedObjectStore()->getManagedObjectsWithContext(
service_name, objPath, context,
[serviceList{std::move(serviceList)}, service_name, objPath,
managed_objects_by_service{std::move(managed_objects_by_service)},
context = context, callback{std::forward<Callback>(callback)}](
const boost::system::error_code& ec,
const dbus::utility::ManagedObjectType& managedObjects) mutable {
if (!ec) {
managed_objects_by_service[service_name] = managedObjects;
}
getManagedObjectsInEachServiceCallback(
std::move(serviceList), objPath,
std::move(managed_objects_by_service), context,
std::forward<decltype(callback)>(callback));
});
}
template <typename PropertyType>
void getProperty(const std::string& service, const std::string& path,
const std::string& interface, const std::string& property,
const managedStore::ManagedObjectStoreContext& requestContext,
std::function<void(const boost::system::error_code&,
const PropertyType&)>&& callback) {
managedStore::KeyType key_type(managedStore::ManagedType::kManagedProperty,
service, {path}, interface, property);
BMCWEB_LOG_STATEFUL_DEBUG << "getProperty: Key " << key_type.toString()
<< " requestContext " << requestContext.toString();
managedStore::GetManagedObjectStore()->getProperty(
key_type, requestContext,
[callback{std::move(callback)}](
const boost::system::error_code& ec,
const dbus::utility::DbusVariantType& managedProperty,
[[maybe_unused]] uint64_t age_ms) {
if (ec) {
callback(ec, {});
return;
}
const PropertyType* propertyValue =
std::get_if<PropertyType>(&managedProperty);
if (propertyValue) {
callback(ec, *propertyValue);
return;
}
callback(boost::system::errc::make_error_code(
boost::system::errc::invalid_argument),
{});
});
}
template <typename PropertyType>
void getProperty(
const std::string& service, const std::string& path,
const std::string& interface, const std::string& property,
const managedStore::ManagedObjectStoreContext& requestContext,
std::function<void(const boost::system::error_code&, const PropertyType&,
uint64_t)>&& callback) {
managedStore::KeyType key_type(managedStore::ManagedType::kManagedProperty,
service, {path}, interface, property);
BMCWEB_LOG_STATEFUL_DEBUG << "getProperty: Key " << key_type.toString()
<< " requestContext " << requestContext.toString();
managedStore::GetManagedObjectStore()->getProperty(
key_type, requestContext,
[callback{std::move(callback)}](
const boost::system::error_code& ec,
const dbus::utility::DbusVariantType& managedProperty,
uint64_t age_ms) {
if (ec) {
callback(ec, {}, 0);
return;
}
const PropertyType* propertyValue =
std::get_if<PropertyType>(&managedProperty);
if (propertyValue) {
callback(ec, *propertyValue, age_ms);
return;
}
callback(boost::system::errc::make_error_code(
boost::system::errc::invalid_argument),
{}, 0);
});
}
inline void getAssociationEndPoints(
const std::string& path,
const managedStore::ManagedObjectStoreContext& requestContext,
std::function<void(const boost::system::error_code&,
const dbus::utility::MapperEndPoints&)>&& callback) {
getProperty<dbus::utility::MapperEndPoints>(
"xyz.openbmc_project.ObjectMapper", {path},
"xyz.openbmc_project.Association", "endpoints", requestContext,
std::move(callback));
}
template <typename Callback>
inline void checkDbusPathExists(
const std::string& path,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getDbusObject(
path, {}, context,
[callback{std::forward<Callback>(callback)}](
const boost::system::error_code& ec,
const dbus::utility::MapperGetObject& objectNames) {
callback(!ec && !objectNames.empty());
});
}
template <typename Callback>
inline void getAssociatedSubTree(
const sdbusplus::message::object_path& associatedPath,
const sdbusplus::message::object_path& path, int32_t depth,
std::span<const std::string_view> interfaces,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getAssociatedSubTree(
associatedPath, path, depth, interfaces, context,
std::forward<Callback>(callback));
}
template <typename Callback>
inline void getAssociatedSubTreePaths(
const sdbusplus::message::object_path& associatedPath,
const sdbusplus::message::object_path& path, int32_t depth,
std::span<const std::string_view> interfaces,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getAssociatedSubTreePaths(
associatedPath, path, depth, interfaces, context,
std::forward<Callback>(callback));
}
template <typename Callback>
inline void getSubTree(const std::string& path, int32_t depth,
std::span<const std::string_view> interfaces,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getSubTree(
path, depth, interfaces, context, std::forward<Callback>(callback));
}
// Overload to support span of strings for interface names.
template <typename Callback>
inline void getSubTree(const std::string& path, int32_t depth,
std::span<std::string> interfaces,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getSubTree(
path, depth, interfaces, context, std::forward<Callback>(callback));
}
template <typename Callback>
inline void getSubTreePaths(
const std::string& path, int32_t depth,
std::span<const std::string_view> interfaces,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getSubTreePaths(
path, depth, interfaces, context, std::forward<Callback>(callback));
}
template <typename Callback>
inline void getManagedObjects(
const std::string& service, const sdbusplus::message::object_path& path,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getManagedObjectsWithContext(
service, path, context, std::forward<Callback>(callback));
}
template <typename Callback>
inline void getDbusObject(
const std::string& path, std::span<const std::string_view> interfaces,
const managedStore::ManagedObjectStoreContext& context,
Callback&& callback) {
managedStore::GetManagedObjectStore()->getDbusObject(
path, interfaces, context, std::forward<Callback>(callback));
}
} // namespace dbus_utils
} // namespace redfish
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_INCLUDE_UTILS_DBUS_UTILS_H_