chassis-psu: ChassisManager class to manage chassis PSUs
Introduced the ChassisManager class to detect chassis and manage power
supply devices associated with chassis using D-Bus interfaces. This
includes:
- Subscribing to Entity Manager interface changes
- Implementing timers to validate power supply presence and status
- Scan the system for new chassis; if a chassis is new, add it to the
chassis list.
- Adding event loop integration for continuous monitoring
- Analysis of power supply status and error logging.
Test:
- On simulation system, verified chassis and their power supplies are
added to the chassis list, with each power supply correctly linked
to its corresponding chassis.
- Verified the power supplies in each chassis based on analysis
performed at the specified time interval.
Note: There are some commented code indicates future implementation,
please ignore for now, as they will be implemented soon.
Change-Id: I80c271783e71f668ca1405f7aca80c8ec112f531
Signed-off-by: Faisal Awada <faisal@us.ibm.com>
diff --git a/phosphor-power-supply/chassis.cpp b/phosphor-power-supply/chassis.cpp
index 505a955..85e2a88 100644
--- a/phosphor-power-supply/chassis.cpp
+++ b/phosphor-power-supply/chassis.cpp
@@ -25,9 +25,10 @@
const auto entityMgrService = "xyz.openbmc_project.EntityManager";
const auto decoratorChassisId = "xyz.openbmc_project.Inventory.Decorator.Slot";
-Chassis::Chassis(sdbusplus::bus_t& bus, const std::string& chassisPath) :
+Chassis::Chassis(sdbusplus::bus_t& bus, const std::string& chassisPath,
+ const sdeventplus::Event& e) :
bus(bus), chassisPath(chassisPath),
- chassisPathUniqueId(getChassisPathUniqueId())
+ chassisPathUniqueId(getChassisPathUniqueId(chassisPath)), eventLoop(e)
{
getPSUConfiguration();
}
@@ -149,9 +150,12 @@
lg2::debug(
"make PowerSupply bus: {I2CBUS} addr: {I2CADDR} presline: {PRESLINE}",
"I2CBUS", *i2cbus, "I2CADDR", *i2caddr, "PRESLINE", presline);
+ // auto psu = std::make_unique<PowerSupply>(
+ // bus, invpath, *i2cbus, *i2caddr, driverName, presline,
+ // std::bind(&Chassis::isPowerOn, this), chassisPath);
auto psu = std::make_unique<PowerSupply>(
bus, invpath, *i2cbus, *i2caddr, driverName, presline,
- std::bind(&Chassis::isPowerOn, this), chassisPath);
+ std::bind(&Chassis::isPowerOn, this));
psus.emplace_back(std::move(psu));
// Subscribe to power supply presence changes
@@ -300,20 +304,20 @@
[&driverName](auto& psu) { psu->setDriverName(driverName); });
}
-uint32_t Chassis::getChassisPathUniqueId()
+uint64_t Chassis::getChassisPathUniqueId(const std::string& path)
{
- uint32_t chassisId = invalidObjectPathUniqueId;
try
{
- getProperty(decoratorChassisId, chassisIdProp, chassisPath,
- INVENTORY_MGR_IFACE, bus, chassisId);
+ return getChassisInventoryUniqueId(bus, path);
}
catch (const sdbusplus::exception_t& e)
{
- lg2::error("Failed to find chassis ID - exception: {ERROR}", "ERROR",
- e);
+ lg2::error(
+ "Failed to find chassis path {CHASSIS_PATH} ID - exception: {ERROR}",
+ "CHASSIS_PATH", path, "ERROR", e);
}
- return chassisId;
+ return invalidObjectPathUniqueId;
}
+void Chassis::analyze() {}
} // namespace phosphor::power::chassis
diff --git a/phosphor-power-supply/chassis.hpp b/phosphor-power-supply/chassis.hpp
index 527b0ff..e976921 100644
--- a/phosphor-power-supply/chassis.hpp
+++ b/phosphor-power-supply/chassis.hpp
@@ -29,7 +29,7 @@
namespace phosphor::power::chassis
{
-constexpr uint32_t invalidObjectPathUniqueId = 9999;
+constexpr uint64_t invalidObjectPathUniqueId = 9999;
using PowerSystemInputsInterface = sdbusplus::xyz::openbmc_project::State::
Decorator::server::PowerSystemInputs;
using PowerSystemInputsObject =
@@ -62,8 +62,26 @@
*
* @param[in] bus - D-Bus bus object
* @param[in] chassisPath - Chassis path
+ * @param[in] event - Event loop object
*/
- Chassis(sdbusplus::bus_t& bus, const std::string& chassisPath);
+ Chassis(sdbusplus::bus_t& bus, const std::string& chassisPath,
+ const sdeventplus::Event& e);
+
+ /**
+ * @brief Retrieves the unique identifier of the chassis.
+ *
+ * @return uint64_t The unique 64 bits identifier of the chassis.
+ */
+ uint64_t getChassisId()
+ {
+ return chassisPathUniqueId;
+ }
+
+ /**
+ * @brief Analyze the status of each of the power supplies. Log errors for
+ * faults, when and where appropriate.
+ */
+ void analyze();
/**
* @brief Get the status of Power on.
@@ -125,7 +143,13 @@
/**
* @brief The Chassis path unique ID
*/
- uint32_t chassisPathUniqueId = invalidObjectPathUniqueId;
+ uint64_t chassisPathUniqueId = invalidObjectPathUniqueId;
+
+ /**
+ * @brief Declares a constant reference to an sdeventplus::Event to manage
+ * async processing.
+ */
+ const sdeventplus::Event& eventLoop;
/**
* @brief Get PSU properties from D-Bus, use that to build a power supply
@@ -181,9 +205,10 @@
/**
* @brief Get chassis path unique ID.
*
- * @return uint32_t - Chassis path unique ID.
+ * @param [in] path - Chassis path.
+ * @return uint64_t - Chassis path unique ID.
*/
- uint32_t getChassisPathUniqueId();
+ uint64_t getChassisPathUniqueId(const std::string& path);
};
} // namespace phosphor::power::chassis
diff --git a/phosphor-power-supply/chassis_manager.cpp b/phosphor-power-supply/chassis_manager.cpp
new file mode 100644
index 0000000..710b5dc
--- /dev/null
+++ b/phosphor-power-supply/chassis_manager.cpp
@@ -0,0 +1,145 @@
+#include "config.h"
+
+#include "chassis_manager.hpp"
+
+#include <phosphor-logging/lg2.hpp>
+using namespace phosphor::logging;
+
+namespace phosphor::power::chassis_manager
+{
+using namespace phosphor::power::util;
+constexpr auto managerBusName =
+ "xyz.openbmc_project.Power.MultiChassisPSUMonitor";
+constexpr auto IBMCFFPSInterface =
+ "xyz.openbmc_project.Configuration.IBMCFFPSConnector";
+constexpr auto supportedConfIntf =
+ "xyz.openbmc_project.Configuration.SupportedConfiguration";
+
+ChassisManager::ChassisManager(sdbusplus::bus_t& bus,
+ const sdeventplus::Event& e) :
+ bus(bus), eventLoop(e)
+{
+ // Subscribe to InterfacesAdded before doing a property read, otherwise
+ // the interface could be created after the read attempt but before the
+ // match is created.
+ entityManagerIfacesAddedMatch = std::make_unique<sdbusplus::bus::match_t>(
+ bus,
+ sdbusplus::bus::match::rules::interfacesAdded() +
+ sdbusplus::bus::match::rules::sender(
+ "xyz.openbmc_project.EntityManager"),
+ std::bind(&ChassisManager::entityManagerIfaceAdded, this,
+ std::placeholders::_1));
+
+ initializeChassisList();
+
+ // Request the bus name before the analyze() function, which is the one that
+ // determines the brownout condition and sets the status d-bus property.
+ bus.request_name(managerBusName);
+
+ using namespace sdeventplus;
+ auto interval = std::chrono::milliseconds(1000);
+ timer = std::make_unique<utility::Timer<ClockId::Monotonic>>(
+ e, std::bind(&ChassisManager::analyze, this), interval);
+}
+
+void ChassisManager::entityManagerIfaceAdded(sdbusplus::message_t& msg)
+{
+ try
+ {
+ phosphor::power::chassis::Chassis* chassisMatchPtr = nullptr;
+ sdbusplus::message::object_path objPath;
+ std::map<std::string, std::map<std::string, util::DbusVariant>>
+ interfaces;
+ msg.read(objPath, interfaces);
+
+ std::string objPathStr = objPath;
+
+ auto itInterface = interfaces.find(supportedConfIntf);
+ if (itInterface != interfaces.cend())
+ {
+ lg2::info("InterfacesAdded supportedConfIntf- objPathStr= {OBJ}",
+ "OBJ", objPathStr);
+ auto myChassisId = getParentEMUniqueId(bus, objPathStr);
+ chassisMatchPtr = getMatchingChassisPtr(myChassisId);
+ if (chassisMatchPtr)
+ {
+ lg2::debug("InterfacesAdded for: {SUPPORTED_CONFIGURATION}",
+ "SUPPORTED_CONFIGURATION", supportedConfIntf);
+ // Future implementation
+ // chassisMatchPtr->supportedConfigurationInterfaceAdded(
+ // itInterface->second);
+ }
+ }
+ itInterface = interfaces.find(IBMCFFPSInterface);
+ if (itInterface != interfaces.cend())
+ {
+ lg2::debug("InterfacesAdded IBMCFFPSInterface- objPathStr= {OBJ}",
+ "OBJ", objPathStr);
+ auto myChassisId = getParentEMUniqueId(bus, objPathStr);
+ chassisMatchPtr = getMatchingChassisPtr(myChassisId);
+ if (chassisMatchPtr)
+ {
+ lg2::info("InterfacesAdded for: {IBMCFFPSINTERFACE}",
+ "IBMCFFPSINTERFACE", IBMCFFPSInterface);
+ // Future implementation
+ // chassisMatchPtr->psuInterfaceAdded(itInterface->second);
+ }
+ }
+ if (chassisMatchPtr != nullptr)
+ {
+ lg2::debug(
+ "InterfacesAdded validatePsuConfigAndInterfacesProcessed()");
+ // Future implementation
+ // chassisMatchPtr->validatePsuConfigAndInterfacesProcessed();
+ }
+ }
+ catch (const std::exception& e)
+ {
+ // Ignore, the property may be of a different type than expected.
+ }
+}
+
+phosphor::power::chassis::Chassis* ChassisManager::getMatchingChassisPtr(
+ uint64_t chassisId)
+{
+ for (const auto& chassisPtr : listOfChassis)
+ {
+ if (chassisPtr->getChassisId() == chassisId)
+ {
+ return chassisPtr.get();
+ }
+ }
+ lg2::debug("Chassis ID {ID} not found", "ID", chassisId);
+ return nullptr;
+}
+
+void ChassisManager::analyze()
+{
+ for (const auto& chassis : listOfChassis)
+ {
+ chassis->analyze();
+ }
+}
+
+void ChassisManager::initializeChassisList()
+{
+ try
+ {
+ auto chassisPathList = getChassisInventoryPaths(bus);
+ for (const auto& chassisPath : chassisPathList)
+ {
+ lg2::info(
+ "ChassisManager::initializeChassisList chassisPath= {CHASSIS_PATH}",
+ "CHASSIS_PATH", chassisPath);
+ auto chassis = std::make_unique<phosphor::power::chassis::Chassis>(
+ bus, chassisPath, eventLoop);
+ listOfChassis.push_back(std::move(chassis));
+ }
+ }
+ catch (const sdbusplus::exception_t& e)
+ {
+ lg2::error("Failed to initialize chassis list, error: {ERROR}", "ERROR",
+ e);
+ }
+}
+} // namespace phosphor::power::chassis_manager
diff --git a/phosphor-power-supply/chassis_manager.hpp b/phosphor-power-supply/chassis_manager.hpp
new file mode 100644
index 0000000..100514d
--- /dev/null
+++ b/phosphor-power-supply/chassis_manager.hpp
@@ -0,0 +1,127 @@
+#pragma once
+
+#include "chassis.hpp"
+#include "types.hpp"
+#include "utility.hpp"
+
+#include <sdbusplus/bus/match.hpp>
+#include <sdbusplus/server/manager.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/utility/timer.hpp>
+#include <xyz/openbmc_project/State/Decorator/PowerSystemInputs/server.hpp>
+
+using namespace phosphor::power::psu;
+
+namespace phosphor::power::chassis_manager
+{
+
+// Validation timeout. Allow 30s to detect if new EM interfaces show up in D-Bus
+// before performing the validation.
+constexpr auto validationTimeout = std::chrono::seconds(30);
+
+/**
+ * @class ChassisManager
+ *
+ * @brief Manages and monitors power supply devices for the chassis.
+ *
+ * @detail This class interacts with D-Bus to detect chassis power supply,
+ * subscribe to Entity Manager interface changes.
+ */
+class ChassisManager
+{
+ public:
+ ChassisManager() = delete;
+ ~ChassisManager() = default;
+ ChassisManager(const ChassisManager&) = delete;
+ ChassisManager& operator=(const ChassisManager&) = delete;
+ ChassisManager(ChassisManager&&) = delete;
+ ChassisManager& operator=(ChassisManager&&) = delete;
+
+ /**
+ * @brief Constructs a ChassisManager instance.
+ *
+ * @details Sets up D-Bus interfaces, creates timer for power supply
+ * validation and monitoring, and subscribes to entity-manager interfaces.
+ *
+ * @param[in] bus - Reference to the system D-Bus object.
+ * @param[in] e - Reference to event loop.
+ */
+ ChassisManager(sdbusplus::bus_t& bus, const sdeventplus::Event& e);
+
+ /**
+ * @brief Starts the main event loop for monitoring.
+ *
+ * @return int Returns the result the result of the event loop execution.
+ */
+ int run()
+ {
+ return timer->get_event().loop();
+ }
+
+ private:
+ /**
+ * @brief The D-Bus object
+ */
+ sdbusplus::bus_t& bus;
+
+ /**
+ * @brief The timer that runs to periodically check the power supplies.
+ */
+ std::unique_ptr<
+ sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>
+ timer;
+
+ /**
+ * @brief Used to subscribe to Entity Manager interfaces added
+ */
+ std::unique_ptr<sdbusplus::bus::match_t> entityManagerIfacesAddedMatch;
+
+ /**
+ * @brief List of chassis objects populated dynamically.
+ */
+ std::vector<std::unique_ptr<phosphor::power::chassis::Chassis>>
+ listOfChassis;
+
+ /**
+ * @brief Declares a constant reference to an sdeventplus::Envent to manage
+ * async processing.
+ */
+ const sdeventplus::Event& eventLoop;
+
+ /**
+ * @brief Callback for entity-manager interface added
+ *
+ * @details Process the information from the supported configuration and
+ * or IBM CFFPS Connector interface being added.
+ *
+ * @param[in] msg - D-Bus message containing the interface details.
+ */
+ void entityManagerIfaceAdded(sdbusplus::message_t& msg);
+
+ /**
+ * @brief Invoke the PSU analysis method in each chassis on the system.
+ *
+ * @details Scan the system for chassis and analyze each chassis power
+ * supplies and log any detected errors.
+ */
+ void analyze();
+
+ /**
+ * @brief Initialize the list of chassis object from the inventory, scans
+ * the D-Bus subtree for chassis and creates Chassis instances.
+ */
+ void initializeChassisList();
+
+ /**
+ * @brief Retrieves a pointer to a Chassis object matching the given ID.
+ *
+ * @param[in] chassisId - Unique identifier of the chassis to search for.
+ * @return Raw pointer to the matching Chassis object if found, otherwise a
+ * nullptr.
+ */
+ phosphor::power::chassis::Chassis* getMatchingChassisPtr(
+ uint64_t chassisId);
+};
+
+} // namespace phosphor::power::chassis_manager