psu-ng: Get max PSU number from entity manager

Read the max number of PSUs supported from entity manager instead of
JSON since entity manager would have the value based on system. If the
entity manager interface does not exist by the time the psu manager
starts, subscribe to interfaces added. The psu manager can take action
once the max count property is available on D-Bus.

Tested: On Rainier 2U with debug traces:
1. Restarted phosphor-psu-monitor and verified maxPowerSupplies was 2.
2. Stopped xyz.openbmc_project.Inventory.Manager. Restarted
   phosphor-psu-monitor and verified maxPowerSupplies was 0.
3. Started xyz.openbmc_project.Inventory.Manager and verified the
   psu callback was triggered setting maxPowerSupplies to 2.
4. Stopped and started xyz.openbmc_project.Inventory.Manager and
   verified the psu callback was not called again.

Change-Id: Ieff796ca5c79bfddc1fc6e15e339a0b15eb422d7
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index 1f578aa..3117e2c 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -11,6 +11,10 @@
 namespace phosphor::power::manager
 {
 
+constexpr auto supportedConfIntf =
+    "xyz.openbmc_project.Configuration.SupportedConfiguration";
+constexpr auto maxCountProp = "MaxCount";
+
 PSUManager::PSUManager(sdbusplus::bus::bus& bus, const sdeventplus::Event& e,
                        const std::string& configfile) :
     bus(bus)
@@ -18,6 +22,7 @@
     // Parse out the JSON properties
     sysProperties = {0};
     getJSONProperties(configfile);
+    getSystemProperties();
 
     using namespace sdeventplus;
     auto interval = std::chrono::milliseconds(1000);
@@ -44,23 +49,11 @@
         throw std::runtime_error("Failed to load JSON configuration file");
     }
 
-    if (!configFileJSON.contains("SystemProperties"))
-    {
-        throw std::runtime_error("Missing required SystemProperties");
-    }
-
     if (!configFileJSON.contains("PowerSupplies"))
     {
         throw std::runtime_error("Missing required PowerSupplies");
     }
 
-    auto sysProps = configFileJSON["SystemProperties"];
-
-    if (sysProps.contains("MaxPowerSupplies"))
-    {
-        sysProperties.maxPowerSupplies = sysProps["MaxPowerSupplies"];
-    }
-
     for (auto psuJSON : configFileJSON["PowerSupplies"])
     {
         if (psuJSON.contains("Inventory") && psuJSON.contains("Bus") &&
@@ -85,6 +78,82 @@
     }
 }
 
+void PSUManager::getSystemProperties()
+{
+    // 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(&PSUManager::supportedConfIfaceAdded, this,
+                  std::placeholders::_1));
+
+    uint64_t maxCount;
+    try
+    {
+        util::DbusSubtree subtree =
+            util::getSubTree(bus, INVENTORY_OBJ_PATH, supportedConfIntf, 0);
+        auto objectIt = subtree.cbegin();
+        if (objectIt == subtree.cend())
+        {
+            throw std::runtime_error("Supported Configuration Not Found");
+        }
+        std::string objPath = objectIt->first;
+        auto serviceIt = objectIt->second.cbegin();
+        if (serviceIt != objectIt->second.cend())
+        {
+            std::string service = serviceIt->first;
+            if (!service.empty())
+            {
+                util::getProperty<uint64_t>(supportedConfIntf, maxCountProp,
+                                            objPath, service, bus, maxCount);
+                sysProperties.maxPowerSupplies = maxCount;
+
+                // Don't need the match anymore
+                entityManagerIfacesAddedMatch.reset();
+            }
+        }
+    }
+    catch (std::exception& e)
+    {
+        // Interface or property not found. Let the Interfaces Added callback
+        // process the information once the interfaces are added to D-Bus.
+    }
+}
+
+void PSUManager::supportedConfIfaceAdded(sdbusplus::message::message& msg)
+{
+    try
+    {
+        sdbusplus::message::object_path objPath;
+        std::map<std::string, std::map<std::string, std::variant<uint64_t>>>
+            interfaces;
+        msg.read(objPath, interfaces);
+
+        auto itIntf = interfaces.find(supportedConfIntf);
+        if (itIntf == interfaces.cend())
+        {
+            return;
+        }
+
+        auto itProp = itIntf->second.find(maxCountProp);
+        if (itProp != itIntf->second.cend())
+        {
+            sysProperties.maxPowerSupplies = std::get<0>(itProp->second);
+
+            // Don't need the match anymore
+            entityManagerIfacesAddedMatch.reset();
+        }
+    }
+    catch (std::exception& e)
+    {
+        // Ignore, the property may be of a different type than expected.
+    }
+}
+
 void PSUManager::powerStateChanged(sdbusplus::message::message& msg)
 {
     int32_t state = 0;