Activation: add BMC reboot guard

BMC shall not reboot during PSU update, otherwise it has the risk of
putting the PSU in a bad state.
So add BMC reboot guard in ActivationBlocksTransition to enable reboot
guard when PSU update is started, and disable it when it's finished or
failed.

During test, it's found that the ActivationBlocksTransition is created
too early before checking the PSU compatibility, it is fixed by
constructing ActivationBlocksTransition after the check.

Tested: Verify the BMC guard is enabled and disabled during PSU update.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I405fe640929aa91ecbcb3d48e19309d38b6849e5
diff --git a/src/activation.cpp b/src/activation.cpp
index af56f28..37610c0 100644
--- a/src/activation.cpp
+++ b/src/activation.cpp
@@ -154,15 +154,6 @@
                             entry("VERSION_ID=%s", versionId.c_str()));
         return activation(); // Return the previous activation status
     }
-    if (!activationProgress)
-    {
-        activationProgress = std::make_unique<ActivationProgress>(bus, objPath);
-    }
-    if (!activationBlocksTransition)
-    {
-        activationBlocksTransition =
-            std::make_unique<ActivationBlocksTransition>(bus, objPath);
-    }
 
     auto psuPaths = utils::getPSUInventoryPath(bus);
     if (psuPaths.empty())
@@ -196,6 +187,16 @@
         return activation(); // Return the previous activation status
     }
 
+    if (!activationProgress)
+    {
+        activationProgress = std::make_unique<ActivationProgress>(bus, objPath);
+    }
+    if (!activationBlocksTransition)
+    {
+        activationBlocksTransition =
+            std::make_unique<ActivationBlocksTransition>(bus, objPath);
+    }
+
     // The progress to be increased for each successful update of PSU
     // E.g. in case we have 4 PSUs:
     //   1. Initial progrss is 10
@@ -336,6 +337,26 @@
     return service;
 }
 
+void ActivationBlocksTransition::enableRebootGuard()
+{
+    log<level::INFO>("PSU image activating - BMC reboots are disabled.");
+
+    auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                      SYSTEMD_INTERFACE, "StartUnit");
+    method.append("reboot-guard-enable.service", "replace");
+    bus.call_noreply_noerror(method);
+}
+
+void ActivationBlocksTransition::disableRebootGuard()
+{
+    log<level::INFO>("PSU activation has ended - BMC reboots are re-enabled.");
+
+    auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                      SYSTEMD_INTERFACE, "StartUnit");
+    method.append("reboot-guard-disable.service", "replace");
+    bus.call_noreply_noerror(method);
+}
+
 } // namespace updater
 } // namespace software
 } // namespace phosphor
diff --git a/src/activation.hpp b/src/activation.hpp
index 3b03803..bc95065 100644
--- a/src/activation.hpp
+++ b/src/activation.hpp
@@ -51,12 +51,14 @@
     {
         std::vector<std::string> interfaces({interface});
         bus.emit_interfaces_added(path.c_str(), interfaces);
+        enableRebootGuard();
     }
 
     ~ActivationBlocksTransition()
     {
         std::vector<std::string> interfaces({interface});
         bus.emit_interfaces_removed(path.c_str(), interfaces);
+        disableRebootGuard();
     }
 
   private:
@@ -65,6 +67,12 @@
         "xyz.openbmc_project.Software.ActivationBlocksTransition";
     sdbusplus::bus::bus& bus;
     std::string path;
+
+    /** @brief Enables a Guard that blocks any BMC reboot commands */
+    void enableRebootGuard();
+
+    /** @brief Disables any guard that was blocking the BMC reboot */
+    void disableRebootGuard();
 };
 
 using ActivationProgressInherit = sdbusplus::server::object::object<
diff --git a/test/test_item_updater.cpp b/test/test_item_updater.cpp
index b2cb62a..6b92e38 100644
--- a/test/test_item_updater.cpp
+++ b/test/test_item_updater.cpp
@@ -677,7 +677,8 @@
         .WillByDefault(Return(false));
     EXPECT_CALL(sdbusMock, sd_bus_message_new_method_call(_, _, _, _, _,
                                                           StrEq("StartUnit")))
-        .Times(1);
+        .Times(3); // There are 3 systemd units are started, enable bmc reboot
+                   // guard, start activation, and disable bmc reboot guard
     onPsuInventoryChanged(psuPath, propAdded);
     onPsuInventoryChanged(psuPath, propModel);
 }