Handle repeated activation requests

PSU code versions are represented on D-Bus as a combination of Version
and Activation objects.

When this application determines that one or more PSUs need a code
update, it sets the RequestedActivation property to Active on the
Activation object. This triggers the code update.

The code is updated on each PSU in sequential order. This process can
take several minutes depending on the PSU type and number of PSUs.

During this long code update process, it is possible for one of the
following to occur:
* New PSU information is found on D-Bus
* A new PSU is plugged in (hot-plug)

If the new PSU requires a code update, the application will again set
the RequestedActivation property to Active on the Activation object. If
a code update is already occurring, the property change is currently
ignored. That means that the new PSU will not be code updated.

Enhance the application so that the second code update request is not
ignored. If it is requested while another code update is already in
progress, then restart the code update process once the current one
completes.

Tested:
* Tested a repeated code update request occurring due to new PSU
  information found on D-Bus.
* Tested a repeated code update request occurring due to a new PSU
  being plugged in.
* Verified that code update cycle was repeated if a request was deferred
  and the previous code update was successful.
* Verified that code update cycle was not repeated if a request was
  deferred and the previous code update failed.
* See the complete test plan at
  https://gist.github.com/smccarney/424f92af23fc25c6c2b6f67ee54d8919

Change-Id: Ie73e3f83c68945f6c85c2747003c36637791a24b
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/src/activation.cpp b/src/activation.cpp
index a7ffd6c..11da384 100644
--- a/src/activation.cpp
+++ b/src/activation.cpp
@@ -50,17 +50,28 @@
 auto Activation::requestedActivation(RequestedActivations value)
     -> RequestedActivations
 {
-    if ((value == SoftwareActivation::RequestedActivations::Active) &&
-        (SoftwareActivation::requestedActivation() !=
-         SoftwareActivation::RequestedActivations::Active))
+    if (value == SoftwareActivation::RequestedActivations::Active)
     {
-        // PSU image could be activated even when it's in active,
-        // e.g. in case a PSU is replaced and has a older image, it will be
-        // updated with the running PSU image that is stored in BMC.
-        if ((activation() == Status::Ready) ||
-            (activation() == Status::Failed) || activation() == Status::Active)
+        if (SoftwareActivation::requestedActivation() !=
+            SoftwareActivation::RequestedActivations::Active)
         {
-            activation(Status::Activating);
+            // PSU image could be activated even when it's in active,
+            // e.g. in case a PSU is replaced and has a older image, it will be
+            // updated with the running PSU image that is stored in BMC.
+            if ((activation() == Status::Ready) ||
+                (activation() == Status::Failed) ||
+                (activation() == Status::Active))
+            {
+                activation(Status::Activating);
+            }
+        }
+        else if (activation() == Status::Activating)
+        {
+            // Activation was requested when one was already in progress.
+            // Activate again once the current activation is done. New PSU
+            // information may have been found on D-Bus, or a new PSU may have
+            // been plugged in.
+            shouldActivateAgain = true;
         }
     }
     return SoftwareActivation::requestedActivation(value);
@@ -165,6 +176,7 @@
     lg2::error("Failed to update PSU {PSU}", "PSU", psuQueue.front());
     std::queue<std::string>().swap(psuQueue); // Clear the queue
     activation(Status::Failed);
+    shouldActivateAgain = false;
 }
 
 Activation::Status Activation::startActivation()
@@ -257,6 +269,15 @@
     // future
     requestedActivation(SoftwareActivation::RequestedActivations::None);
     activation(Status::Active);
+
+    // Automatically restart activation if a request occurred while code update
+    // was already in progress. New PSU information may have been found on
+    // D-Bus, or a new PSU may have been plugged in.
+    if (shouldActivateAgain)
+    {
+        shouldActivateAgain = false;
+        requestedActivation(SoftwareActivation::RequestedActivations::Active);
+    }
 }
 
 void Activation::deleteImageManagerObject()
diff --git a/src/activation.hpp b/src/activation.hpp
index cd90217..5cf040a 100644
--- a/src/activation.hpp
+++ b/src/activation.hpp
@@ -295,6 +295,10 @@
 
     /** @brief The PSU model of the software */
     std::string model;
+
+    /** @brief Indicates whether to automatically activate again after current
+     * request finishes */
+    bool shouldActivateAgain{false};
 };
 
 } // namespace updater