ups: check state on startup and update power status
Look for a UPS (uninterruptible power supply) D-Bus object on chassis
startup. If found, read its State and BatteryLevel properties to check
the condition of the power to the chassis. Update the CurrentPowerStatus
property hosted by the chassis state manager based on this information.
Tested:
- Utilized ibm-ups feature to start in debug mode so all properties
could be manually set. Validated good path and bad paths for all
properties and ensured CurrentPowerStatus was set as expected for each
test.
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I064a9e7fecbed8f859828359a761877e0fa95251
diff --git a/chassis_state_manager.cpp b/chassis_state_manager.cpp
index 4d6b445..ae998ef 100644
--- a/chassis_state_manager.cpp
+++ b/chassis_state_manager.cpp
@@ -55,6 +55,12 @@
constexpr auto SYSTEMD_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
constexpr auto SYSTEMD_INTERFACE_UNIT = "org.freedesktop.systemd1.Unit";
+constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
+constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
+constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
+constexpr auto UPOWER_INTERFACE = "org.freedesktop.UPower.Device";
+constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
+
void Chassis::subscribeToSystemdSignals()
{
try
@@ -77,6 +83,8 @@
// has read property function
void Chassis::determineInitialState()
{
+ determineStatusOfPower();
+
std::variant<int> pgood = -1;
auto method = this->bus.new_method_call(
"org.openbmc.control.Power", "/org/openbmc/control/power0",
@@ -152,6 +160,117 @@
return;
}
+void Chassis::determineStatusOfPower()
+{
+
+ // Details at https://upower.freedesktop.org/docs/Device.html
+ constexpr uint TYPE_UPS = 3;
+ constexpr uint STATE_FULLY_CHARGED = 4;
+ constexpr uint BATTERY_LVL_FULL = 8;
+
+ // Default PowerStatus to good
+ server::Chassis::currentPowerStatus(PowerStatus::Good);
+
+ // Find all implementations of the UPower interface
+ auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
+ MAPPER_INTERFACE, "GetSubTree");
+
+ mapper.append("/", 0, std::vector<std::string>({UPOWER_INTERFACE}));
+
+ std::map<std::string, std::map<std::string, std::vector<std::string>>>
+ mapperResponse;
+
+ try
+ {
+ auto mapperResponseMsg = bus.call(mapper);
+ mapperResponseMsg.read(mapperResponse);
+ }
+ catch (const sdbusplus::exception::exception& e)
+ {
+ error("Error in mapper GetSubTree call for UPS: {ERROR}", "ERROR", e);
+ throw;
+ }
+
+ if (mapperResponse.empty())
+ {
+ debug("No UPower devices found in system");
+ return;
+ }
+
+ // Iterate through all returned Upower interfaces and look for UPS's
+ for (const auto& [path, services] : mapperResponse)
+ {
+ for (const auto& serviceIter : services)
+ {
+ const std::string& service = serviceIter.first;
+
+ try
+ {
+ auto method = bus.new_method_call(service.c_str(), path.c_str(),
+ PROPERTY_INTERFACE, "GetAll");
+ method.append(UPOWER_INTERFACE);
+
+ auto response = bus.call(method);
+ using Property = std::string;
+ using Value = std::variant<bool, uint>;
+ using PropertyMap = std::map<Property, Value>;
+ PropertyMap properties;
+ response.read(properties);
+
+ if (std::get<bool>(properties["IsPresent"]) != true)
+ {
+ info("UPower device {OBJ_PATH} is not present", "OBJ_PATH",
+ path);
+ continue;
+ }
+
+ if (std::get<uint>(properties["Type"]) != TYPE_UPS)
+ {
+ info("UPower device {OBJ_PATH} is not a UPS device",
+ "OBJ_PATH", path);
+ continue;
+ }
+
+ if (std::get<uint>(properties["State"]) == STATE_FULLY_CHARGED)
+ {
+ info("UPS is fully charged");
+ }
+ else
+ {
+ info("UPS is not fully charged: {UPS_STATE}", "UPS_STATE",
+ std::get<uint>(properties["State"]));
+ server::Chassis::currentPowerStatus(
+ PowerStatus::UninterruptiblePowerSupply);
+ return;
+ }
+
+ if (std::get<uint>(properties["BatteryLevel"]) ==
+ BATTERY_LVL_FULL)
+ {
+ info("UPS Battery Level is Full");
+ }
+ else
+ {
+ info("UPS Battery Level is Low: {UPS_BAT_LEVEL}",
+ "UPS_BAT_LEVEL",
+ std::get<uint>(properties["BatteryLevel"]));
+ server::Chassis::currentPowerStatus(
+ PowerStatus::UninterruptiblePowerSupply);
+ return;
+ }
+ }
+ catch (const sdbusplus::exception::exception& e)
+ {
+ error("Error reading UPS property, error: {ERROR}, "
+ "service: {SERVICE} path: {PATH}",
+ "ERROR", e, "SERVICE", service, "PATH", path);
+ throw;
+ }
+ }
+ }
+ return;
+}
+
void Chassis::executeTransition(Transition tranReq)
{
auto sysdTarget = SYSTEMD_TARGET_TABLE.find(tranReq)->second;