psu-ng: Fix chassis association call

The association name from a power supply to its chassis changed from
'chassis' to 'powering', so change that in the code.

Also update the code to find the association using the new
GetAssociatedSubTreePaths mapper method which is like the existing
GetSubTreePaths but also ensures the results are an endpoint of the
passed in association path.  This way if the 'powering' association is
used to show the power supply powers other things it will still work.

Change-Id: I9076a6b1502ba43a29404a191bd8bc56a9c5df45
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/phosphor-power-supply/util.hpp b/phosphor-power-supply/util.hpp
index 01f5b0b..b09731a 100644
--- a/phosphor-power-supply/util.hpp
+++ b/phosphor-power-supply/util.hpp
@@ -199,21 +199,22 @@
     std::string getChassis(sdbusplus::bus_t& bus,
                            const std::string& invpath) const
     {
-        // Use the 'chassis' association to find the parent chassis.
-        auto assocPath = invpath + "/chassis";
-        std::vector<std::string> endpoints;
+        sdbusplus::message::object_path assocPath = invpath + "/powering";
+        sdbusplus::message::object_path basePath{"/"};
+        std::vector<std::string> interfaces{CHASSIS_IFACE};
 
-        phosphor::power::util::getProperty<decltype(endpoints)>(
-            ASSOCIATION_IFACE, ENDPOINTS_PROP, assocPath,
-            "xyz.openbmc_project.ObjectMapper", bus, endpoints);
+        // Find the object path that implements the chassis interface
+        // and also shows up in the endpoints list of the powering assoc.
+        auto chassisPaths = phosphor::power::util::getAssociatedSubTreePaths(
+            bus, assocPath, basePath, interfaces, 0);
 
-        if (endpoints.empty())
+        if (chassisPaths.empty())
         {
-            throw std::runtime_error(
-                fmt::format("Missing chassis association for {}", invpath));
+            throw std::runtime_error(fmt::format(
+                "No association to a chassis found for {}", invpath));
         }
 
-        return endpoints[0];
+        return chassisPaths[0];
     }
 };
 
diff --git a/types.hpp b/types.hpp
index 502c134..b682d9b 100644
--- a/types.hpp
+++ b/types.hpp
@@ -16,6 +16,7 @@
 constexpr auto AVAILABILITY_IFACE =
     "xyz.openbmc_project.State.Decorator.Availability";
 constexpr auto ASSOC_DEF_IFACE = "xyz.openbmc_project.Association.Definitions";
+constexpr auto CHASSIS_IFACE = "xyz.openbmc_project.Inventory.Item.Chassis";
 #ifdef IBM_VPD
 constexpr auto DINF_IFACE = "com.ibm.ipzvpd.DINF";
 constexpr auto VINI_IFACE = "com.ibm.ipzvpd.VINI";
diff --git a/utility.cpp b/utility.cpp
index ad8e232..0082d36 100644
--- a/utility.cpp
+++ b/utility.cpp
@@ -103,6 +103,27 @@
     return response;
 }
 
+std::vector<DbusPath> getAssociatedSubTreePaths(
+    sdbusplus::bus_t& bus,
+    const sdbusplus::message::object_path& associationPath,
+    const sdbusplus::message::object_path& path,
+    const std::vector<std::string>& interfaces, int32_t depth)
+{
+    auto mapperCall =
+        bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, MAPPER_INTERFACE,
+                            "GetAssociatedSubTreePaths");
+    mapperCall.append(associationPath);
+    mapperCall.append(path);
+    mapperCall.append(depth);
+    mapperCall.append(interfaces);
+
+    auto reply = bus.call(mapperCall);
+
+    std::vector<DbusPath> response;
+    reply.read(response);
+    return response;
+}
+
 json loadJSONFromFile(const char* path)
 {
     std::ifstream ifs(path);
diff --git a/utility.hpp b/utility.hpp
index 1db17f3..043c3c0 100644
--- a/utility.hpp
+++ b/utility.hpp
@@ -130,6 +130,27 @@
 DbusSubtree getSubTree(sdbusplus::bus_t& bus, const std::string& path,
                        const std::string& interface, int32_t depth);
 
+/** @brief GetAssociatedSubTreePaths wrapper from the object mapper.
+ *
+ * Helper function to find object paths that implement a certain
+ * interface and are also an association endpoint.
+ * See:
+ * https://github.com/openbmc/docs/blob/master/architecture/object-mapper.md
+ *
+ * @param[in] bus - The D-Bus object.
+ * @param[in] associationPath - The association it must be an endpoint of.
+ * @param[in] path - The root of the tree to search.
+ * @param[in] interfaces - The interfaces in the subtree to search for
+ * @param[in] depth - The number of path elements to descend.
+ *
+ * @return std::vector<DbusPath> - The object paths.
+ */
+std::vector<DbusPath> getAssociatedSubTreePaths(
+    sdbusplus::bus_t& bus,
+    const sdbusplus::message::object_path& associationPath,
+    const sdbusplus::message::object_path& path,
+    const std::vector<std::string>& interfaces, int32_t depth);
+
 /**
  * Logs an error and powers off the system.
  *