Create an association to BMC inventory item

Create an association from /xyz/openbmc_project/software/<id>
to the BMC inventory item.
To determine the BMC inventory item path, look for paths under
/xyz/openbmc_project/inventory/system/chassis/ that end in /bmc.

Change-Id: I8da748743368e3e607b30a76a6729829dcceec54
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/activation.hpp b/activation.hpp
index 89269bd..0a98b85 100644
--- a/activation.hpp
+++ b/activation.hpp
@@ -14,6 +14,8 @@
 namespace updater
 {
 
+using AssociationList =
+     std::vector<std::tuple<std::string, std::string, std::string>>;
 using ActivationInherit = sdbusplus::server::object::object<
     sdbusplus::xyz::openbmc_project::Software::server::Activation,
     sdbusplus::org::openbmc::server::Associations>;
@@ -177,12 +179,14 @@
          * @param[in] parent - Parent object.
          * @param[in] versionId  - The software version id
          * @param[in] activationStatus - The status of Activation
+         * @param[in] assocs - Association objects
          */
         Activation(sdbusplus::bus::bus& bus, const std::string& path,
                    ItemUpdater& parent,
                    std::string& versionId,
                    sdbusplus::xyz::openbmc_project::Software::
-                   server::Activation::Activations activationStatus) :
+                   server::Activation::Activations activationStatus,
+                   AssociationList& assocs) :
                    ActivationInherit(bus, path.c_str(), true),
                    bus(bus),
                    path(path),
@@ -202,6 +206,8 @@
             subscribeToSystemdSignals();
             // Set Properties.
             activation(activationStatus);
+            associations(assocs);
+
             // Emit deferred signal.
             emit_object_added();
         }
diff --git a/configure.ac b/configure.ac
index c6ca235..22eece4 100755
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,10 @@
 AX_CXX_COMPILE_STDCXX_14([noext])
 AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
 
+AC_DEFINE(ACTIVATION_FWD_ASSOCIATION, "activation", [The name of the activation's forward association.])
+AC_DEFINE(ACTIVATION_REV_ASSOCIATION, "inventory", [The name of the activation's reverse association.])
+AC_DEFINE(CHASSIS_INVENTORY_PATH, "/xyz/openbmc_project/inventory/system/chassis/", [The chassis inventory path root.])
+
 AC_ARG_VAR(VERSION_BUSNAME, [The Dbus busname to own])
 AS_IF([test "x$VERSION_BUSNAME" == "x"], [VERSION_BUSNAME="xyz.openbmc_project.Software.Version"])
 AC_DEFINE_UNQUOTED([VERSION_BUSNAME], ["$VERSION_BUSNAME"], [The DBus busname to own])
@@ -64,6 +68,10 @@
     [DOWNLOAD_BUSNAME="xyz.openbmc_project.Software.Download"])
 AC_DEFINE_UNQUOTED([DOWNLOAD_BUSNAME], ["$DOWNLOAD_BUSNAME"], [The DBus busname to own])
 
+AC_DEFINE([MAPPER_BUSNAME], ["xyz.openbmc_project.ObjectMapper"], [Object mapper bus name])
+AC_DEFINE([MAPPER_INTERFACE], ["xyz.openbmc_project.ObjectMapper"], [Object mapper interface])
+AC_DEFINE([MAPPER_PATH], ["/xyz/openbmc_project/object_mapper"], [Object mapper DBUS path])
+
 AC_DEFINE(VERSION_IFACE, "xyz.openbmc_project.Software.Version",
     [The software version manager interface])
 AC_DEFINE(FILEPATH_IFACE, "xyz.openbmc_project.Common.FilePath",
diff --git a/item_updater.cpp b/item_updater.cpp
index 1d68037..aa072ef 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -107,6 +107,13 @@
         {
             activationState = server::Activation::Activations::Ready;
         }
+
+        // Create an association to the BMC inventory item
+        AssociationList associations{(std::make_tuple(
+                                          ACTIVATION_FWD_ASSOCIATION,
+                                          ACTIVATION_REV_ASSOCIATION,
+                                          bmcInventoryPath))};
+
         activations.insert(std::make_pair(
                                versionId,
                                std::make_unique<Activation>(
@@ -114,7 +121,8 @@
                                         path,
                                         *this,
                                         versionId,
-                                        activationState)));
+                                        activationState,
+                                        associations)));
         versions.insert(std::make_pair(
                             versionId,
                             std::make_unique<VersionClass>(
@@ -138,10 +146,18 @@
 void ItemUpdater::processBMCImage()
 {
     using VersionClass = phosphor::software::manager::Version;
+
     auto purpose = server::Version::VersionPurpose::BMC;
     auto version = phosphor::software::manager::Version::getBMCVersion();
     auto id = phosphor::software::manager::Version::getId(version);
     auto path =  std::string{SOFTWARE_OBJPATH} + '/' + id;
+
+    // Create an association to the BMC inventory item
+    AssociationList associations{(std::make_tuple(
+                                      ACTIVATION_FWD_ASSOCIATION,
+                                      ACTIVATION_REV_ASSOCIATION,
+                                      bmcInventoryPath))};
+
     activations.insert(std::make_pair(
                            id,
                            std::make_unique<Activation>(
@@ -149,7 +165,8 @@
                                path,
                                *this,
                                id,
-                               server::Activation::Activations::Active)));
+                               server::Activation::Activations::Active,
+                               associations)));
     versions.insert(std::make_pair(
                         id,
                         std::make_unique<VersionClass>(
@@ -320,6 +337,52 @@
     }
 }
 
+void ItemUpdater::setBMCInventoryPath()
+{
+    //TODO: openbmc/openbmc#1786 - Get the BMC path by looking for objects
+    //      that implement the BMC inventory interface
+    auto depth = 0;
+    auto mapperCall = bus.new_method_call(MAPPER_BUSNAME,
+                                          MAPPER_PATH,
+                                          MAPPER_INTERFACE,
+                                          "GetSubTreePaths");
+
+    mapperCall.append(CHASSIS_INVENTORY_PATH);
+    mapperCall.append(depth);
+
+    // TODO: openbmc/openbmc#2226 - Add Inventory Item filter when
+    //       mapper is fixed.
+    std::vector<std::string> filter = {};
+    mapperCall.append(filter);
+
+    auto response = bus.call(mapperCall);
+    if (response.is_method_error())
+    {
+        log<level::ERR>("Error in mapper GetSubTreePath");
+        return;
+    }
+
+    using ObjectPaths = std::vector<std::string>;
+    ObjectPaths result;
+    response.read(result);
+
+    if (result.empty())
+    {
+        log<level::ERR>("Invalid response from mapper");
+        return;
+    }
+
+    for (auto& iter : result)
+    {
+        const auto& path = iter;
+        if (path.substr(path.find_last_of('/') + 1).compare("bmc") == 0)
+        {
+            bmcInventoryPath = path;
+            return;
+        }
+    }
+}
+
 } // namespace updater
 } // namespace software
 } // namespace phosphor
diff --git a/item_updater.hpp b/item_updater.hpp
index 4d8924a..4015760 100644
--- a/item_updater.hpp
+++ b/item_updater.hpp
@@ -51,6 +51,7 @@
                                     this,
                                     std::placeholders::_1))
         {
+            setBMCInventoryPath();
             processBMCImage();
             restoreFieldModeStatus();
             emit_object_added();
@@ -111,6 +112,13 @@
          */
         bool fieldModeEnabled(bool value) override;
 
+        /** @brief Sets the BMC inventory item path under
+         *  /xyz/openbmc_project/inventory/system/chassis/. */
+        void setBMCInventoryPath();
+
+        /** @brief The path to the BMC inventory item. */
+        std::string bmcInventoryPath;
+
         /** @brief Restores field mode status on reboot. */
         void restoreFieldModeStatus();