Skip to content

Commit

Permalink
New method to retrieve DBUS resources for systems
Browse files Browse the repository at this point in the history
In an effort to support multi-host in bmcweb, a new method to retrieve
dbus resources is needed. Since the long-term goal for bmcweb is to get
rid of hardcoded URIs as much as possible to align with the redfish
spec, this method shall handle single host as well.

Initially bmcweb hardcoded object paths and services to retrieve various
resources via dbus method calls. However, the introduction of multi host
platforms also introduces new service names and object paths.
Hardcoding them does not cut it anymore, since we don't necessarily
know how they are called, nor should we do any assumptions on naming
schemes by default.

This method follows this idea and lets DBUS return needed paths and
services, if any, primarily based on a systemName the user sends
a request for.

Change-Id: I67c17c3dd7a354fa9a2ebbc56d4def7a7e788909
Signed-off-by: Oliver Brewka <[email protected]>
  • Loading branch information
Oliver Brewka committed Nov 3, 2024
1 parent f408e67 commit eb9a959
Show file tree
Hide file tree
Showing 4 changed files with 1,264 additions and 342 deletions.
1 change: 1 addition & 0 deletions config/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ feature_options = [
'cookie-auth',
'experimental-http2',
'experimental-redfish-multi-computer-system',
'experimental-approach-to-dbus',
'google-api',
'host-serial-socket',
'hypervisor-computer-system',
Expand Down
8 changes: 8 additions & 0 deletions meson.options
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,14 @@ option(
)

# BMCWEB_EXPERIMENTAL_HTTP2
option(
'experimental-approach-to-dbus',
type: 'feature',
value: 'disabled',
description: '''This is a temporary option flag for staging the
the new approach handling dbus resources. Solely for testing purposes.''',
)

option(
'experimental-http2',
type: 'feature',
Expand Down
330 changes: 330 additions & 0 deletions redfish-core/include/utils/systems_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "human_sort.hpp"
#include "str_utility.hpp"

#include <boost/url/format.hpp>

Expand Down Expand Up @@ -79,6 +80,11 @@ inline void handleSystemCollectionMembers(
asyncResp->res.jsonValue["[email protected]"] = membersArray.size();
}

/**
* Version 2 ---- WIP towards associations
*
*/

/**
* @brief Populate the system collection members from a GetSubTreePaths search
* of inventory
Expand All @@ -105,4 +111,328 @@ inline void getSystemCollectionMembers(
"/xyz/openbmc_project/inventory", 0, interfaces,
std::bind_front(handleSystemCollectionMembers, asyncResp));
}

inline void getValidServiceName(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& interface,
const size_t computerSystemIndex,
std::function<void(const sdbusplus::message::object_path& path,
const std::string& service)>& callback,
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreePathsResponse& paths)
{
// match the found object paths against the index returned by
// getManagedHostProperty. Then getObject call on found dbus object path and
// the requested interface to find correct service name and finally call
// callback with found object path and service

BMCWEB_LOG_DEBUG("in getValidServiceName..");
sdbusplus::message::object_path path;

if (ec)
{
BMCWEB_LOG_ERROR("DBUS response error {}", ec);
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}

BMCWEB_LOG_DEBUG("Got index: {}; Got paths:\n[", computerSystemIndex);
for (const auto& p : paths)
{
BMCWEB_LOG_DEBUG(" {}", p);
}
BMCWEB_LOG_DEBUG("]; determining dbus path..");

if (paths.size() == 1)
{
// single host should only return a single path containing "host0" when
// we run host specific queries
path = paths.front();
}
else
{
// multi host will return multiple paths, find the correct one
// naive approach, assuming that the chassis is fully slotted
path = paths[computerSystemIndex - 1];
}

BMCWEB_LOG_DEBUG("found path: {} .. calling getDbusObject",
std::string(path));
std::array<std::string_view, 1> interfaces{interface};
dbus::utility::getDbusObject(
path, interfaces,
[asyncResp, systemName, callback = std::move(callback),
path](const boost::system::error_code& ec2,
const dbus::utility::MapperGetObject& object) {
if (ec2 || object.empty())
{
BMCWEB_LOG_ERROR("DBUS response error on getDbusObject {}",
ec2.value());
messages::internalError(asyncResp->res);
return;
}
callback(path, object.begin()->first);
});
}

inline void getValidObjectPaths(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& interface,
std::function<void(const sdbusplus::message::object_path& path,
const std::string& service)>& callback,
const boost::system::error_code& ec, const size_t computerSystemIndex)

{
// get all object paths that implement the requested interface

BMCWEB_LOG_DEBUG("in getValidObjectPaths.. got index {}",
computerSystemIndex);
// getSubTreePaths search on interface to get all possible paths
// afterwards match against the found index
if (ec)
{
BMCWEB_LOG_ERROR("DBUS response error {}", ec);
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}

BMCWEB_LOG_DEBUG(
"calling getSubTreePaths for interface: {} using getValidServiceName callback",
interface);
std::array<std::string_view, 1> interfaces{interface};
dbus::utility::getSubTreePaths(
"/", 0, interfaces,
std::bind_front(getValidServiceName, asyncResp, systemName, interface,
computerSystemIndex, std::move(callback)));
}

inline void getManagedHostProperty(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& interface,
const boost::system::error_code& ec, const std::string& systemId,
const std::string& serviceName,
std::function<void(const sdbusplus::message::object_path& path,
const std::string& service)>&& callback)
{
// get HostIndex property associated with found system path

BMCWEB_LOG_DEBUG("in getManagedHostProperty..");
if (ec)
{
if (ec.value() == boost::system::errc::io_error)
{
BMCWEB_LOG_DEBUG("EIO - System not found");
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}

BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
messages::internalError(asyncResp->res);
return;
}
if (systemId.empty() || serviceName.empty())
{
BMCWEB_LOG_WARNING("System not found");
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}

BMCWEB_LOG_DEBUG(
"calling getProperty for HostIndex and binding getValidObjectPaths");
sdbusplus::asio::getProperty<uint64_t>(
*crow::connections::systemBus, serviceName, systemId,
"xyz.openbmc_project.Inventory.Decorator.ManagedHost", "HostIndex",
std::bind_front(getValidObjectPaths, asyncResp, systemName, interface,
std::move(callback)));
}

inline void afterGetValidSystemPaths(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& interface,
std::function<void(const sdbusplus::message::object_path& path,
const std::string& service)>& callback,
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree)
{
// match systemName against found system paths

BMCWEB_LOG_DEBUG("in afterGetValidSystemPaths");
std::string systemId;
std::string serviceName;
if (ec)
{
getManagedHostProperty(asyncResp, systemName, interface, ec, systemId,
serviceName, std::move(callback));
return;
}

for (const auto& [path, serviceMap] : subtree)
{
systemId = sdbusplus::message::object_path(path).filename();

if (systemId == systemName)
{
serviceName = serviceMap.begin()->first;
systemId = path;
break;
}
}
BMCWEB_LOG_DEBUG(
"found systemId: {}, serviceName: {} .. calling getManagedHostProperty",
systemId, serviceName);

getManagedHostProperty(asyncResp, systemName, interface, ec, systemId,
serviceName, std::move(callback));
}

inline void getValidSystemPaths(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& interface,
std::function<void(const sdbusplus::message::object_path& path,
const std::string& service)>&& callback)
{
// getSubTree call on ManagedHost dbus interface to retrieve all
// available system paths
BMCWEB_LOG_DEBUG(
"in getValidSystemPaths: running getSubTree and binding afterGetValidSystemPaths");
constexpr std::array<std::string_view, 1> interfaces{
"xyz.openbmc_project.Inventory.Decorator.ManagedHost"};
dbus::utility::getSubTree(
"/xyz/openbmc_project/inventory", 0, interfaces,
std::bind_front(afterGetValidSystemPaths, asyncResp, systemName,
interface, std::move(callback)));
}

/**
* @brief Wrapper - Retrieve object path and associated service that implement
* passed in interface
*
* @param[in] asyncResp Shared pointer for completing asynchronous calls
* @param[in] systemName Name of the requested system
* @param[in] interface D-Bus interface, to base search for path and service
* on
* @param[in] callback Function to call with the found path and service
*
* @return None.
*/
inline void getComputerSystemDBusResourcesV2(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const std::string& interface,
std::function<void(const sdbusplus::message::object_path& path,
const std::string& service)>&& callback)
{
BMCWEB_LOG_DEBUG("calling getValidSystemPaths");
getValidSystemPaths(asyncResp, systemName, interface, std::move(callback));
}

/**
* Version 1 ---- WIP, manual path constructing via found HostIndex
*
*/

inline void getManagedHostPropertyV1(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName, const boost::system::error_code& ec,
const std::string& systemId, const std::string& serviceName,
std::function<void(const uint64_t& computerSystemIndex)> callback)
{
// get HostIndex property associated with found system path
BMCWEB_LOG_DEBUG("in getManagedHostProperty..");
if (ec)
{
if (ec.value() == boost::system::errc::io_error)
{
BMCWEB_LOG_DEBUG("EIO - System not found");
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}

BMCWEB_LOG_ERROR("DBus method call failed with error {}", ec.value());
messages::internalError(asyncResp->res);
return;
}
if (systemId.empty() || serviceName.empty())
{
BMCWEB_LOG_WARNING("System not found");
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}

BMCWEB_LOG_DEBUG(
"calling getProperty for HostIndex and binding getValidObjectPaths");
sdbusplus::asio::getProperty<uint64_t>(
*crow::connections::systemBus, serviceName, systemId,
"xyz.openbmc_project.Inventory.Decorator.ManagedHost", "HostIndex",
[asyncResp, systemName, serviceName, callback = std::move(callback)](
const boost::system::error_code& ec2, const uint64_t hostIndex) {
if (ec2)
{
BMCWEB_LOG_ERROR("DBUS response error {}", ec2);
messages::resourceNotFound(asyncResp->res, "ComputerSystem",
systemName);
return;
}

callback(hostIndex);
});
}

inline void getValidSystemPathsV1(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
std::function<void(const uint64_t& computerSystemIndex)>&& callback)
{
BMCWEB_LOG_DEBUG(
"in getValidSystemPaths: running getSubTree and binding afterGetValidSystemPaths");
constexpr std::array<std::string_view, 1> interfaces{
"xyz.openbmc_project.Inventory.Decorator.ManagedHost"};
dbus::utility::getSubTree(
"/xyz/openbmc_project/inventory", 0, interfaces,
[asyncResp, systemName, callback = std::move(callback)](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
BMCWEB_LOG_DEBUG("in afterGetValidSystemPaths");
std::string systemId;
std::string serviceName;
if (ec)
{
getManagedHostPropertyV1(asyncResp, systemName, ec, systemId,
serviceName, callback);
return;
}

for (const auto& [path, serviceMap] : subtree)
{
systemId = sdbusplus::message::object_path(path).filename();

if (systemId == systemName)
{
serviceName = serviceMap.begin()->first;
systemId = path;
break;
}
}
BMCWEB_LOG_DEBUG(
"found systemId: {}, serviceName: {} .. calling getManagedHostProperty",
systemId, serviceName);

getManagedHostPropertyV1(asyncResp, systemName, ec, systemId,
serviceName, callback);
});
}

inline void getComputerSystemIndexV1(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& systemName,
std::function<void(const uint64_t& computerSystemIndex)>&& callback)
{
getValidSystemPathsV1(asyncResp, systemName, std::move(callback));
}

} // namespace redfish
Loading

0 comments on commit eb9a959

Please sign in to comment.