Activation: create associations

Create activation, functional, activate associations during PSU update.

Tested: With dummy psu update service, verify the assocations are
        created when the PSU update is completed, and not created
        when PSU update fails.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I3d457e65b55066b93f7fc9a3311093dcec05d020
diff --git a/src/activation.cpp b/src/activation.cpp
index f63352a..f1181fb 100644
--- a/src/activation.cpp
+++ b/src/activation.cpp
@@ -105,7 +105,8 @@
 
 bool Activation::doUpdate(const std::string& psuInventoryPath)
 {
-    psuUpdateUnit = internal::getUpdateService(psuInventoryPath, versionId);
+    currentUpdatingPsu = psuInventoryPath;
+    psuUpdateUnit = internal::getUpdateService(currentUpdatingPsu, versionId);
     try
     {
         auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
@@ -141,6 +142,13 @@
     auto progress = activationProgress->progress() + progressStep;
     activationProgress->progress(progress);
 
+    // Update the activation association
+    auto assocs = associations();
+    assocs.emplace_back(ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
+                        currentUpdatingPsu);
+    currentUpdatingPsu.clear();
+    associations(assocs);
+
     psuQueue.pop();
     doUpdate(); // Update the next psu
 }
@@ -202,8 +210,11 @@
     activationProgress->progress(100);
 
     // TODO: delete the old software object
-    // TODO: create related associations
     deleteImageManagerObject();
+
+    associationInterface->createActiveAssociation(path);
+    associationInterface->addFunctionalAssociation(path);
+
     activation(Status::Active);
 }
 
diff --git a/src/activation.hpp b/src/activation.hpp
index 59364e4..d0fdaba 100644
--- a/src/activation.hpp
+++ b/src/activation.hpp
@@ -2,6 +2,7 @@
 
 #include "config.h"
 
+#include "association_interface.hpp"
 #include "types.hpp"
 
 #include <queue>
@@ -123,7 +124,8 @@
      */
     Activation(sdbusplus::bus::bus& bus, const std::string& path,
                const std::string& versionId, const std::string& extVersion,
-               Status activationStatus, const AssociationList& assocs) :
+               Status activationStatus, const AssociationList& assocs,
+               AssociationInterface* associationInterface) :
         ActivationInherit(bus, path.c_str(), true),
         versionId(versionId), bus(bus), path(path),
         systemdSignals(
@@ -132,7 +134,8 @@
                 sdbusRule::path("/org/freedesktop/systemd1") +
                 sdbusRule::interface("org.freedesktop.systemd1.Manager"),
             std::bind(&Activation::unitStateChange, this,
-                      std::placeholders::_1))
+                      std::placeholders::_1)),
+        associationInterface(associationInterface)
     {
         // Set Properties.
         extendedVersion(extVersion);
@@ -227,11 +230,17 @@
     /** @brief The PSU update systemd unit */
     std::string psuUpdateUnit;
 
+    /** @brief The PSU Inventory path of the current updating PSU */
+    std::string currentUpdatingPsu;
+
     /** @brief Persistent ActivationBlocksTransition dbus object */
     std::unique_ptr<ActivationBlocksTransition> activationBlocksTransition;
 
     /** @brief Persistent ActivationProgress dbus object */
     std::unique_ptr<ActivationProgress> activationProgress;
+
+    /** @brief The AssociationInterface pointer */
+    AssociationInterface* associationInterface;
 };
 
 } // namespace updater
diff --git a/src/association_interface.hpp b/src/association_interface.hpp
new file mode 100644
index 0000000..e3d653c
--- /dev/null
+++ b/src/association_interface.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <string>
+
+class AssociationInterface
+{
+  public:
+    virtual ~AssociationInterface() = default;
+
+    /** @brief Create an active association to the
+     *  newly active software image
+     *
+     * @param[in]  path - The path to create the association to.
+     */
+    virtual void createActiveAssociation(const std::string& path) = 0;
+
+    /** @brief Add the functional association to the
+     *  new "running" PSU images
+     *
+     * @param[in]  path - The path to add the association to.
+     */
+    virtual void addFunctionalAssociation(const std::string& path) = 0;
+
+    /** @brief Remove the associations from the provided software image path
+     *
+     * @param[in]  path - The path to remove the association from.
+     */
+    virtual void removeAssociation(const std::string& path) = 0;
+};
diff --git a/src/item_updater.cpp b/src/item_updater.cpp
index 50c1a26..e100381 100644
--- a/src/item_updater.cpp
+++ b/src/item_updater.cpp
@@ -190,7 +190,7 @@
     const AssociationList& assocs)
 {
     return std::make_unique<Activation>(bus, path, versionId, extVersion,
-                                        activationStatus, assocs);
+                                        activationStatus, assocs, this);
 }
 
 void ItemUpdater::createPsuObject(const std::string& psuInventoryPath,
diff --git a/src/item_updater.hpp b/src/item_updater.hpp
index eabe66f..c0a64fa 100644
--- a/src/item_updater.hpp
+++ b/src/item_updater.hpp
@@ -3,6 +3,7 @@
 #include "config.h"
 
 #include "activation.hpp"
+#include "association_interface.hpp"
 #include "types.hpp"
 #include "utils.hpp"
 #include "version.hpp"
@@ -31,7 +32,7 @@
 /** @class ItemUpdater
  *  @brief Manages the activation of the PSU version items.
  */
-class ItemUpdater : public ItemUpdaterInherit
+class ItemUpdater : public ItemUpdaterInherit, public AssociationInterface
 {
     friend class ::TestItemUpdater;
 
@@ -63,27 +64,27 @@
      */
     void deleteAll();
 
-  private:
     /** @brief Creates an active association to the
      *  newly active software image
      *
      * @param[in]  path - The path to create the association to.
      */
-    void createActiveAssociation(const std::string& path);
+    void createActiveAssociation(const std::string& path) override;
 
     /** @brief Add the functional association to the
      *  new "running" PSU images
      *
      * @param[in]  path - The path to add the association to.
      */
-    void addFunctionalAssociation(const std::string& path);
+    void addFunctionalAssociation(const std::string& path) override;
 
     /** @brief Removes the associations from the provided software image path
      *
      * @param[in]  path - The path to remove the association from.
      */
-    void removeAssociation(const std::string& path);
+    void removeAssociation(const std::string& path) override;
 
+  private:
     /** @brief Callback function for Software.Version match.
      *  @details Creates an Activation D-Bus object.
      *
diff --git a/test/mocked_association_interface.hpp b/test/mocked_association_interface.hpp
new file mode 100644
index 0000000..731ea58
--- /dev/null
+++ b/test/mocked_association_interface.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "association_interface.hpp"
+
+#include <gmock/gmock.h>
+
+class MockedAssociationInterface : public AssociationInterface
+{
+  public:
+    virtual ~MockedAssociationInterface() = default;
+
+    MOCK_METHOD1(createActiveAssociation, void(const std::string& path));
+    MOCK_METHOD1(addFunctionalAssociation, void(const std::string& path));
+    MOCK_METHOD1(removeAssociation, void(const std::string& path));
+};
diff --git a/test/test_activation.cpp b/test/test_activation.cpp
index 338dc60..891cdb6 100644
--- a/test/test_activation.cpp
+++ b/test/test_activation.cpp
@@ -1,4 +1,5 @@
 #include "activation.hpp"
+#include "mocked_association_interface.hpp"
 #include "mocked_utils.hpp"
 
 #include <sdbusplus/test/sdbus_mock.hpp>
@@ -37,13 +38,14 @@
     {
         return activation->activationProgress->progress();
     }
-    static constexpr auto dBusPath = SOFTWARE_OBJPATH;
     sdbusplus::SdBusMock sdbusMock;
     sdbusplus::bus::bus mockedBus = sdbusplus::get_mocked_new(&sdbusMock);
     const utils::MockedUtils& mockedUtils;
+    MockedAssociationInterface mockedAssociationInterface;
     std::unique_ptr<Activation> activation;
     std::string versionId = "abcdefgh";
     std::string extVersion = "Some Ext Version";
+    std::string dBusPath = std::string(SOFTWARE_OBJPATH) + "/" + versionId;
     Status status = Status::Ready;
     AssociationList associations;
 };
@@ -51,7 +53,8 @@
 TEST_F(TestActivation, ctordtor)
 {
     activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
-                                              extVersion, status, associations);
+                                              extVersion, status, associations,
+                                              &mockedAssociationInterface);
 }
 
 namespace phosphor::software::updater::internal
@@ -75,12 +78,17 @@
 TEST_F(TestActivation, doUpdateWhenNoPSU)
 {
     activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
-                                              extVersion, status, associations);
+                                              extVersion, status, associations,
+                                              &mockedAssociationInterface);
     ON_CALL(mockedUtils, getPSUInventoryPath(_))
         .WillByDefault(
             Return(std::vector<std::string>({}))); // No PSU inventory
     activation->requestedActivation(RequestedStatus::Active);
 
+    EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
+        .Times(0);
+    EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
+        .Times(0);
     EXPECT_EQ(Status::Failed, activation->activation());
 }
 
@@ -88,7 +96,8 @@
 {
     constexpr auto psu0 = "/com/example/inventory/psu0";
     activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
-                                              extVersion, status, associations);
+                                              extVersion, status, associations,
+                                              &mockedAssociationInterface);
     ON_CALL(mockedUtils, getPSUInventoryPath(_))
         .WillByDefault(
             Return(std::vector<std::string>({psu0}))); // One PSU inventory
@@ -96,6 +105,10 @@
 
     EXPECT_EQ(Status::Activating, activation->activation());
 
+    EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
+        .Times(1);
+    EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
+        .Times(1);
     onUpdateDone();
     EXPECT_EQ(Status::Active, activation->activation());
 }
@@ -107,7 +120,8 @@
     constexpr auto psu2 = "/com/example/inventory/psu2";
     constexpr auto psu3 = "/com/example/inventory/psu3";
     activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
-                                              extVersion, status, associations);
+                                              extVersion, status, associations,
+                                              &mockedAssociationInterface);
     ON_CALL(mockedUtils, getPSUInventoryPath(_))
         .WillByDefault(Return(
             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
@@ -128,6 +142,11 @@
     EXPECT_EQ(Status::Activating, activation->activation());
     EXPECT_EQ(70, getProgress());
 
+    EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
+        .Times(1);
+    EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
+        .Times(1);
+
     onUpdateDone();
     EXPECT_EQ(Status::Active, activation->activation());
 }
@@ -139,7 +158,8 @@
     constexpr auto psu2 = "/com/example/inventory/psu2";
     constexpr auto psu3 = "/com/example/inventory/psu3";
     activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
-                                              extVersion, status, associations);
+                                              extVersion, status, associations,
+                                              &mockedAssociationInterface);
     ON_CALL(mockedUtils, getPSUInventoryPath(_))
         .WillByDefault(Return(
             std::vector<std::string>({psu0, psu1, psu2, psu3}))); // 4 PSUs
@@ -152,6 +172,10 @@
     EXPECT_EQ(Status::Activating, activation->activation());
     EXPECT_EQ(30, getProgress());
 
+    EXPECT_CALL(mockedAssociationInterface, createActiveAssociation(dBusPath))
+        .Times(0);
+    EXPECT_CALL(mockedAssociationInterface, addFunctionalAssociation(dBusPath))
+        .Times(0);
     onUpdateFailed();
     EXPECT_EQ(Status::Failed, activation->activation());
 }
@@ -160,7 +184,8 @@
 {
     constexpr auto psu0 = "/com/example/inventory/psu0";
     activation = std::make_unique<Activation>(mockedBus, dBusPath, versionId,
-                                              extVersion, status, associations);
+                                              extVersion, status, associations,
+                                              &mockedAssociationInterface);
     ON_CALL(mockedUtils, getPSUInventoryPath(_))
         .WillByDefault(
             Return(std::vector<std::string>({psu0}))); // One PSU inventory