PNOR: Restore RedundancyPriority on reset

This commit extends the functionality of the PNOR software updater by
preserving RedundancyPriority values through any kind of reset.

This is accomplished by storing priority values in serial files in
/var/lib/obmc/openpower-pnor-code-mgmt/ using the Cereal library. Each
time a priority value is modified, the value in the corresponding
version file is adjusted.

When the item updater resets, it reads back the priority values from
these files.

Resolves openbmc/openbmc#2040

Change-Id: I908431801e541a1e5b39bcf49ae057f7e340eecc
Signed-off-by: Michael Tritz <mtritz@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index d188b56..e8a7056 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,6 +8,7 @@
 openpower_update_manager_SOURCES = \
 	activation.cpp \
 	version.cpp \
+	serialize.cpp \
 	item_updater.cpp \
 	item_updater_main.cpp
 
@@ -27,4 +28,3 @@
 openpower_update_manager_LDFLAGS = $(generic_ldflags)
 
 SUBDIRS = test
-
diff --git a/activation.cpp b/activation.cpp
index b63853d..4be1958 100755
--- a/activation.cpp
+++ b/activation.cpp
@@ -2,6 +2,7 @@
 #include "activation.hpp"
 #include "config.h"
 #include "item_updater.hpp"
+#include "serialize.hpp"
 
 namespace openpower
 {
@@ -219,6 +220,7 @@
         parent.createSymlinks();
     }
 
+    storeToFile(parent.versionId, value);
     return softwareServer::RedundancyPriority::priority(value);
 }
 
diff --git a/configure.ac b/configure.ac
index e6b0409..48d359a 100755
--- a/configure.ac
+++ b/configure.ac
@@ -82,6 +82,8 @@
     [The name of the PNOR table of contents file])
 AC_DEFINE(MEDIA_DIR, "/media/",
     [The base dir where all PNOR RO AND RW partitions are mounted])
+AC_DEFINE(PERSIST_DIR, "/var/lib/obmc/openpower-pnor-code-mgmt/",
+    [The dir where activation data is stored in files])
 AC_DEFINE(PNOR_RO_PREFIX, "/media/pnor-ro-",
     [The prefix path for the versioned read-only pnor partitions])
 AC_DEFINE(PNOR_RW_PREFIX, "/media/pnor-rw-",
@@ -99,4 +101,3 @@
 
 AC_CONFIG_FILES([Makefile test/Makefile])
 AC_OUTPUT
-
diff --git a/item_updater.cpp b/item_updater.cpp
index fa3938e..3bf085b 100755
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -7,6 +7,7 @@
 #include "config.h"
 #include "item_updater.hpp"
 #include "activation.hpp"
+#include "serialize.hpp"
 
 namespace openpower
 {
@@ -129,16 +130,6 @@
 
 void ItemUpdater::processPNORImage()
 {
-    // Get the current PNOR version
-    auto pnorTOC = fs::path(PNOR_RO_ACTIVE_PATH) / PNOR_TOC_FILE;
-    if (!fs::is_regular_file(pnorTOC))
-    {
-        log<level::INFO>("Error PNOR current version is empty");
-        return;
-    }
-    std::string currentVersion =
-            Version::getValue(pnorTOC, {{ "version", "" }}).begin()->second;
-
     // Read pnor.toc from folders under /media/
     // to get Active Software Versions.
     for(const auto& iter : fs::directory_iterator(MEDIA_DIR))
@@ -198,21 +189,23 @@
             // If Active, create RedundancyPriority instance for this version.
             if (activationState == server::Activation::Activations::Active)
             {
-                // Current PNOR needs the lowest Priority, so setting to 0.
-                // TODO openbmc/openbmc#2040 Need to store Priority in the
-                // RW partition to be able to restore the priorities that
-                // were set before the BMC reboot.
-                auto priority = 1;
-                if (currentVersion == version)
+                if(fs::is_regular_file(PERSIST_DIR + id))
                 {
-                    priority = 0;
+                    uint8_t priority;
+                    restoreFromFile(id, &priority);
+                    activations.find(id)->second->redundancyPriority =
+                             std::make_unique<RedundancyPriority>(
+                                 bus,
+                                 path,
+                                 *(activations.find(id)->second),
+                                 priority);
                 }
-                activations.find(id)->second->redundancyPriority =
-                         std::make_unique<RedundancyPriority>(
-                             bus,
-                             path,
-                             *(activations.find(id)->second),
-                             priority);
+                else
+                {
+                    activations.find(id)->second->activation(
+                            server::Activation::Activations::Invalid);
+                }
+
             }
 
             // Create Version instance for this version.
@@ -293,6 +286,7 @@
     for(const auto& it : activations)
     {
         removeReadWritePartition(it.first);
+        removeFile(it.first);
     }
     removePreservedPartition();
     return;
@@ -355,6 +349,7 @@
         return;
     }
     activations.erase(entryId);
+    removeFile(entryId);
 }
 
 } // namespace updater
diff --git a/serialize.cpp b/serialize.cpp
new file mode 100644
index 0000000..e2e8bfd
--- /dev/null
+++ b/serialize.cpp
@@ -0,0 +1,51 @@
+#include "config.h"
+#include <experimental/filesystem>
+#include <cereal/archives/binary.hpp>
+#include <fstream>
+#include "serialize.hpp"
+
+namespace openpower
+{
+namespace software
+{
+namespace updater
+{
+
+namespace fs = std::experimental::filesystem;
+
+void storeToFile(std::string versionId, uint8_t priority)
+{
+    if(!fs::is_directory(PERSIST_DIR))
+    {
+        fs::create_directory(PERSIST_DIR);
+    }
+    std::string path = PERSIST_DIR + versionId;
+
+    std::ofstream os(path.c_str(), std::ios::binary);
+    cereal::BinaryOutputArchive oarchive(os);
+    oarchive(priority);
+}
+
+void restoreFromFile(std::string versionId, uint8_t *priority)
+{
+    std::string path = PERSIST_DIR + versionId;
+    if (fs::exists(path))
+    {
+        std::ifstream is(path.c_str(), std::ios::in | std::ios::binary);
+        cereal::BinaryInputArchive iarchive(is);
+        iarchive(*priority);
+    }
+}
+
+void removeFile(std::string versionId)
+{
+    std::string path = PERSIST_DIR + versionId;
+    if (fs::exists(path))
+    {
+        fs::remove(path);
+    }
+}
+
+} // namespace updater
+} // namespace software
+} // namespace openpower
diff --git a/serialize.hpp b/serialize.hpp
new file mode 100644
index 0000000..90210a5
--- /dev/null
+++ b/serialize.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <experimental/filesystem>
+#include "config.h"
+
+namespace openpower
+{
+namespace software
+{
+namespace updater
+{
+
+namespace fs = std::experimental::filesystem;
+
+/** @brief Serialization function - stores activation information to file
+ *  @param[in] versionId - The version for which to store information.
+ *  @param[in] priority - RedundancyPriority value for that version.
+ */
+void storeToFile(std::string versionId, uint8_t priority);
+
+/** @brief Serialization function - restores activation information from file
+ *  @param[in] versionId - The version for which to retrieve information.
+ *  @param[in] priority - RedundancyPriority pointer for that version.
+ */
+void restoreFromFile(std::string versionId, uint8_t *priority);
+
+/** @brief Removes the serial file for a given version.
+ *  @param[in] versionId - The version for which to remove a file, if it exists.
+ */
+void removeFile(std::string versionId);
+
+} // namespace updater
+} // namespace software
+} // namespace openpower