Restore system vpd code update

To restore system VPD data on EEPROM, call to VPD manager
service is required.
Seldom, when the default call to ibm-read-vpd service is made
with path of motherboard fru, VPD manager service is not up
by then resulting in call failure to manager service.

Hence the call to Manager service to update the System VPD has
been delayed till ibm-read-vpd is called against udev event for
motherboard fru.

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: Ie83cdabf4e4386e0be799c38f9cb92b11d1aeeb4
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index 1a9a7e4..fce6bf8 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -40,6 +40,14 @@
 using namespace openpower::vpd::exceptions;
 using namespace phosphor::logging;
 
+// Map to hold record, kwd pair which can be re-stored at standby.
+// The list of keywords for VSYS record is as per the S0 system. Should
+// be updated for another type of systems
+static const std::unordered_map<std::string, std::vector<std::string>>
+    svpdKwdMap{{"VSYS", {"BR", "TM", "SE", "SU", "RB"}},
+               {"VCEN", {"FC", "SE"}},
+               {"LXR0", {"LX"}}};
+
 /**
  * @brief Returns the power state for chassis0
  */
@@ -770,25 +778,63 @@
 }
 
 /**
+ * @brief An api to get list of blank system VPD properties.
+ * @param[in] vpdMap - IPZ vpd map.
+ * @param[in] objectPath - Object path for the FRU.
+ * @param[out] blankPropertyList - Properties which are blank in System VPD and
+ * needs to be updated as standby.
+ */
+void getListOfBlankSystemVpd(Parsed& vpdMap, const string& objectPath,
+                             std::vector<RestoredEeproms>& blankPropertyList)
+{
+    for (const auto& systemRecKwdPair : svpdKwdMap)
+    {
+        auto it = vpdMap.find(systemRecKwdPair.first);
+
+        // check if record is found in map we got by parser
+        if (it != vpdMap.end())
+        {
+            const auto& kwdListForRecord = systemRecKwdPair.second;
+            for (const auto& keyword : kwdListForRecord)
+            {
+                DbusPropertyMap& kwdValMap = it->second;
+                auto iterator = kwdValMap.find(keyword);
+
+                if (iterator != kwdValMap.end())
+                {
+                    string& kwdValue = iterator->second;
+
+                    // check bus data
+                    const string& recordName = systemRecKwdPair.first;
+                    const string& busValue = readBusProperty(
+                        objectPath, ipzVpdInf + recordName, keyword);
+
+                    if (busValue.find_first_not_of(' ') != string::npos)
+                    {
+                        if (kwdValue.find_first_not_of(' ') == string::npos)
+                        {
+                            // implies data is blank on EEPROM but not on cache.
+                            // So EEPROM vpd update is required.
+                            Binary busData(busValue.begin(), busValue.end());
+
+                            blankPropertyList.push_back(std::make_tuple(
+                                objectPath, recordName, keyword, busData));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+/**
  * @brief API to check if we need to restore system VPD
  * This functionality is only applicable for IPZ VPD data.
  * @param[in] vpdMap - IPZ vpd map
  * @param[in] objectPath - Object path for the FRU
- * @return EEPROMs with records and keywords updated at standby
  */
-std::vector<RestoredEeproms> restoreSystemVPD(Parsed& vpdMap,
-                                              const string& objectPath)
+void restoreSystemVPD(Parsed& vpdMap, const string& objectPath)
 {
-    // the list of keywords for VSYS record is as per the S0 system. Should be
-    // updated for another type of systems
-    static std::unordered_map<std::string, std::vector<std::string>> svpdKwdMap{
-        {"VSYS", {"BR", "TM", "SE", "SU", "RB"}},
-        {"VCEN", {"FC", "SE"}},
-        {"LXR0", {"LX"}}};
-
-    // vector to hold all the EEPROMs updated at standby
-    std::vector<RestoredEeproms> updatedEeproms = {};
-
     for (const auto& systemRecKwdPair : svpdKwdMap)
     {
         auto it = vpdMap.find(systemRecKwdPair.first);
@@ -841,9 +887,6 @@
                             // update the map
                             Binary busData(busValue.begin(), busValue.end());
 
-                            updatedEeproms.push_back(std::make_tuple(
-                                objectPath, recordName, keyword, busData));
-
                             // update the map as well, so that cache data is not
                             // updated as blank while populating VPD map on Dbus
                             // in populateDBus Api
@@ -875,8 +918,6 @@
             }
         }
     }
-
-    return updatedEeproms;
 }
 
 /**
@@ -998,9 +1039,6 @@
     inventory::PropertyMap prop;
     string ccinFromVpd;
 
-    // map to hold all the keywords whose value has been changed at standby
-    vector<RestoredEeproms> updatedEeproms = {};
-
     bool isSystemVpd = (filePath == systemVpdFilePath);
     if constexpr (is_same<T, Parsed>::value)
     {
@@ -1022,7 +1060,7 @@
             if ((subTree.size() != 0) &&
                 (subTree.find(pimPath + mboardPath) != subTree.end()))
             {
-                updatedEeproms = restoreSystemVPD(vpdMap, mboardPath);
+                restoreSystemVPD(vpdMap, mboardPath);
             }
             else
             {
@@ -1171,13 +1209,6 @@
         inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
         objects.insert(primeObject.begin(), primeObject.end());
 
-        // if system VPD has been restored at standby, update the EEPROM
-        for (const auto& item : updatedEeproms)
-        {
-            updateHardware(get<0>(item), get<1>(item), get<2>(item),
-                           get<3>(item));
-        }
-
         // set the U-boot environment variable for device-tree
         if constexpr (is_same<T, Parsed>::value)
         {
@@ -1262,6 +1293,58 @@
             if ((js["frus"].find(file) != js["frus"].end()) &&
                 (file == systemVpdFilePath))
             {
+                // We need manager service active to process restoring of
+                // system VPD on hardware. So in case any system restore is
+                // required, update hardware in the second trigger of parser
+                // code for system vpd file path.
+
+                std::vector<std::string> interfaces{motherBoardInterface};
+
+                // call mapper to check for object path creation
+                MapperResponse subTree =
+                    getObjectSubtreeForInterfaces(pimPath, 0, interfaces);
+                string mboardPath =
+                    js["frus"][file].at(0).value("inventoryPath", "");
+
+                // Attempt system VPD restore if we have a motherboard
+                // object in the inventory.
+                if ((subTree.size() != 0) &&
+                    (subTree.find(pimPath + mboardPath) != subTree.end()))
+                {
+                    vpdVector = getVpdDataInVector(js, file);
+                    ParserInterface* parser =
+                        ParserFactory::getParser(vpdVector);
+                    variant<KeywordVpdMap, Store> parseResult;
+                    parseResult = parser->parse();
+
+                    if (auto pVal = get_if<Store>(&parseResult))
+                    {
+                        // map to hold all the keywords whose value is blank and
+                        // needs to be updated at standby.
+                        vector<RestoredEeproms> blankSystemVpdProperties{};
+                        getListOfBlankSystemVpd(pVal->getVpdMap(), mboardPath,
+                                                blankSystemVpdProperties);
+
+                        // if system VPD restore is required, update the
+                        // EEPROM
+                        for (const auto& item : blankSystemVpdProperties)
+                        {
+                            updateHardware(get<0>(item), get<1>(item),
+                                           get<2>(item), get<3>(item));
+                        }
+                    }
+                    else
+                    {
+                        std::cout << "Not a valid format to restore system VPD"
+                                  << std::endl;
+                    }
+                    // release the parser object
+                    ParserFactory::freeParser(parser);
+                }
+                else
+                {
+                    log<level::ERR>("No object path found");
+                }
                 return 0;
             }
         }