Revamped code for VPD parser

The commit removes all the pre-existing code from the branch
and pushes the revamped code.

Major modification includes:
- Movement from multi exe to single daemon model.
- Multithreaded approach to parse FRU VPD.
- Better error handling.
- Refactored code for performance optimization.

Note: This code supports all the existing functionalities as it is.

Change-Id: I1ddce1f0725ac59020b72709689a1013643bda8b
Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
diff --git a/vpd-manager/include/worker.hpp b/vpd-manager/include/worker.hpp
new file mode 100644
index 0000000..85a1818
--- /dev/null
+++ b/vpd-manager/include/worker.hpp
@@ -0,0 +1,529 @@
+#pragma once
+
+#include "constants.hpp"
+#include "types.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <mutex>
+#include <optional>
+#include <semaphore>
+#include <tuple>
+
+namespace vpd
+{
+/**
+ * @brief A class to process and publish VPD data.
+ *
+ * The class works on VPD and is mainly responsible for following tasks:
+ * 1) Select appropriate device tree and JSON. Reboot if required.
+ * 2) Get desired parser using parser factory.
+ * 3) Calling respective parser class to get parsed VPD.
+ * 4) Arranging VPD data under required interfaces.
+ * 5) Calling PIM to publish VPD.
+ *
+ * The class may also implement helper functions required for VPD handling.
+ */
+class Worker
+{
+  public:
+    /**
+     * List of deleted functions.
+     */
+    Worker(const Worker&);
+    Worker& operator=(const Worker&);
+    Worker(Worker&&) = delete;
+
+    /**
+     * @brief Constructor.
+     *
+     * In case the processing is not JSON based, no argument needs to be passed.
+     * Constructor will also, based on symlink pick the correct JSON and
+     * initialize the parsed JSON variable.
+     *
+     * @param[in] pathToConfigJSON - Path to the config JSON, if applicable.
+     *
+     * Note: Throws std::exception in case of construction failure. Caller needs
+     * to handle to detect successful object creation.
+     */
+    Worker(std::string pathToConfigJson = std::string());
+
+    /**
+     * @brief Destructor
+     */
+    ~Worker() = default;
+
+#ifdef IBM_SYSTEM
+    /**
+     * @brief API to perform initial setup before manager claims Bus name.
+     *
+     * Before BUS name for VPD-Manager is claimed, fitconfig whould be set for
+     * corret device tree, inventory JSON w.r.t system should be linked and
+     * system VPD should be on DBus.
+     */
+    void performInitialSetup();
+#endif
+
+    /**
+     * @brief An API to check if system VPD is already published.
+     *
+     * @return Status, true if system is already collected else false.
+     */
+    bool isSystemVPDOnDBus() const;
+
+    /**
+     * @brief API to process all FRUs presnt in config JSON file.
+     *
+     * This API based on config JSON passed/selected for the system, will
+     * trigger parser for all the FRUs and publish it on DBus.
+     *
+     * Note: Config JSON file path should be passed to worker class constructor
+     * to make use of this API.
+     *
+     */
+    void collectFrusFromJson();
+
+    /**
+     * @brief API to parse VPD data
+     *
+     * @param[in] i_vpdFilePath - Path to the VPD file.
+     */
+    types::VPDMapVariant parseVpdFile(const std::string& i_vpdFilePath);
+
+    /**
+     * @brief An API to populate DBus interfaces for a FRU.
+     *
+     * Note: Call this API to populate D-Bus. Also caller should handle empty
+     * objectInterfaceMap.
+     *
+     * @param[in] parsedVpdMap - Parsed VPD as a map.
+     * @param[out] objectInterfaceMap - Object and its interfaces map.
+     * @param[in] vpdFilePath - EEPROM path of FRU.
+     */
+    void populateDbus(const types::VPDMapVariant& parsedVpdMap,
+                      types::ObjectMap& objectInterfaceMap,
+                      const std::string& vpdFilePath);
+
+    /**
+     * @brief An API to delete FRU VPD over DBus.
+     *
+     * @param[in] i_dbusObjPath - Dbus object path of the FRU.
+     *
+     * @throw std::runtime_error if given input path is empty.
+     */
+    void deleteFruVpd(const std::string& i_dbusObjPath);
+
+    /**
+     * @brief API to get status of VPD collection process.
+     *
+     * @return - True when done, false otherwise.
+     */
+    inline bool isAllFruCollectionDone() const
+    {
+        return m_isAllFruCollected;
+    }
+
+    /**
+     * @brief API to get system config JSON object
+     *
+     * @return System config JSON object.
+     */
+    inline nlohmann::json getSysCfgJsonObj() const
+    {
+        return m_parsedJson;
+    }
+
+    /**
+     * @brief API to get active thread count.
+     *
+     * Each FRU is collected in a separate thread. This API gives the active
+     * thread collecting FRU's VPD at any given time.
+     *
+     * @return Count of active threads.
+     */
+    size_t getActiveThreadCount() const
+    {
+        return m_activeCollectionThreadCount;
+    }
+
+  private:
+    /**
+     * @brief An API to parse and publish a FRU VPD over D-Bus.
+     *
+     * Note: This API will handle all the exceptions internally and will only
+     * return status of parsing and publishing of VPD over D-Bus.
+     *
+     * @param[in] i_vpdFilePath - Path of file containing VPD.
+     * @return Tuple of status and file path. Status, true if successfull else
+     * false.
+     */
+    std::tuple<bool, std::string>
+        parseAndPublishVPD(const std::string& i_vpdFilePath);
+
+    /**
+     * @brief An API to set appropriate device tree and JSON.
+     *
+     * This API based on system chooses corresponding device tree and JSON.
+     * If device tree change is required, it updates the "fitconfig" and reboots
+     * the system. Else it is NOOP.
+     *
+     * @throw std::runtime_error
+     */
+    void setDeviceTreeAndJson();
+
+    /**
+     * @brief API to select system specific JSON.
+     *
+     * The API based on the IM value of VPD, will select appropriate JSON for
+     * the system. In case no system is found corresponding to the extracted IM
+     * value, error will be logged.
+     *
+     * @param[out] systemJson - System JSON name.
+     * @param[in] parsedVpdMap - Parsed VPD map.
+     */
+    void getSystemJson(std::string& systemJson,
+                       const types::VPDMapVariant& parsedVpdMap);
+
+    /**
+     * @brief An API to read IM value from VPD.
+     *
+     * Note: Throws exception in case of error. Caller need to handle.
+     *
+     * @param[in] parsedVpd - Parsed VPD.
+     */
+    std::string getIMValue(const types::IPZVpdMap& parsedVpd) const;
+
+    /**
+     * @brief An API to read HW version from VPD.
+     *
+     * Note: Throws exception in case of error. Caller need to handle.
+     *
+     * @param[in] parsedVpd - Parsed VPD.
+     */
+    std::string getHWVersion(const types::IPZVpdMap& parsedVpd) const;
+
+    /**
+     * @brief An API to parse given VPD file path.
+     *
+     * @param[in] vpdFilePath - EEPROM file path.
+     * @param[out] parsedVpd - Parsed VPD as a map.
+     */
+    void fillVPDMap(const std::string& vpdFilePath,
+                    types::VPDMapVariant& parsedVpd);
+
+    /**
+     * @brief An API to parse and publish system VPD on D-Bus.
+     *
+     * Note: Throws exception in case of invalid VPD format.
+     *
+     * @param[in] parsedVpdMap - Parsed VPD as a map.
+     */
+    void publishSystemVPD(const types::VPDMapVariant& parsedVpdMap);
+
+    /**
+     * @brief An API to process extrainterfaces w.r.t a FRU.
+     *
+     * @param[in] singleFru - JSON block for a single FRU.
+     * @param[out] interfaces - Map to hold interface along with its properties.
+     * @param[in] parsedVpdMap - Parsed VPD as a map.
+     */
+    void processExtraInterfaces(const nlohmann::json& singleFru,
+                                types::InterfaceMap& interfaces,
+                                const types::VPDMapVariant& parsedVpdMap);
+
+    /**
+     * @brief An API to process embedded and synthesized FRUs.
+     *
+     * @param[in] singleFru - FRU to be processed.
+     * @param[out] interfaces - Map to hold interface along with its properties.
+     */
+    void processEmbeddedAndSynthesizedFrus(const nlohmann::json& singleFru,
+                                           types::InterfaceMap& interfaces);
+
+    /**
+     * @brief An API to read process FRU based in CCIN.
+     *
+     * For some FRUs VPD can be processed only if the FRU has some specific
+     * value for CCIN. In case the value is not from that set, VPD for those
+     * FRUs can't be processed.
+     *
+     * @param[in] singleFru - Fru whose CCIN value needs to be matched.
+     * @param[in] parsedVpdMap - Parsed VPD map.
+     */
+    bool processFruWithCCIN(const nlohmann::json& singleFru,
+                            const types::VPDMapVariant& parsedVpdMap);
+
+    /**
+     * @brief API to process json's inherit flag.
+     *
+     * Inherit flag denotes that some property in the child FRU needs to be
+     * inherited from parent FRU.
+     *
+     * @param[in] parsedVpdMap - Parsed VPD as a map.
+     * @param[out] interfaces - Map to hold interface along with its properties.
+     */
+    void processInheritFlag(const types::VPDMapVariant& parsedVpdMap,
+                            types::InterfaceMap& interfaces);
+
+    /**
+     * @brief API to process json's "copyRecord" flag.
+     *
+     * copyRecord flag denotes if some record data needs to be copies in the
+     * given FRU.
+     *
+     * @param[in] singleFru - FRU being processed.
+     * @param[in] parsedVpdMap - Parsed VPD as a map.
+     * @param[out] interfaces - Map to hold interface along with its properties.
+     */
+    void processCopyRecordFlag(const nlohmann::json& singleFru,
+                               const types::VPDMapVariant& parsedVpdMap,
+                               types::InterfaceMap& interfaces);
+
+    /**
+     * @brief An API to populate IPZ VPD property map.
+     *
+     * @param[out] interfacePropMap - Map of interface and properties under it.
+     * @param[in] keyordValueMap - Keyword value map of IPZ VPD.
+     * @param[in] interfaceName - Name of the interface.
+     */
+    void populateIPZVPDpropertyMap(types::InterfaceMap& interfacePropMap,
+                                   const types::IPZKwdValueMap& keyordValueMap,
+                                   const std::string& interfaceName);
+
+    /**
+     * @brief An API to populate Kwd VPD property map.
+     *
+     * @param[in] keyordValueMap - Keyword value map of Kwd VPD.
+     * @param[out] interfaceMap - interface and property,value under it.
+     */
+    void populateKwdVPDpropertyMap(const types::KeywordVpdMap& keyordVPDMap,
+                                   types::InterfaceMap& interfaceMap);
+
+    /**
+     * @brief API to populate all required interface for a FRU.
+     *
+     * @param[in] interfaceJson - JSON containing interfaces to be populated.
+     * @param[out] interfaceMap - Map to hold populated interfaces.
+     * @param[in] parsedVpdMap - Parsed VPD as a map.
+     */
+    void populateInterfaces(const nlohmann::json& interfaceJson,
+                            types::InterfaceMap& interfaceMap,
+                            const types::VPDMapVariant& parsedVpdMap);
+
+    /**
+     * @brief Helper function to insert or merge in map.
+     *
+     * This method checks in the given inventory::InterfaceMap if the given
+     * interface key is existing or not. If the interface key already exists,
+     * given property map is inserted into it. If the key does'nt exist then
+     * given interface and property map pair is newly created. If the property
+     * present in propertymap already exist in the InterfaceMap, then the new
+     * property value is ignored.
+     *
+     * @param[in,out] interfaceMap - map object of type inventory::InterfaceMap
+     * only.
+     * @param[in] interface - Interface name.
+     * @param[in] property - new property map that needs to be emplaced.
+     */
+    void insertOrMerge(types::InterfaceMap& interfaceMap,
+                       const std::string& interface,
+                       types::PropertyMap&& property);
+
+    /**
+     * @brief Check if the given CPU is an IO only chip.
+     *
+     * The CPU is termed as IO, whose all of the cores are bad and can never be
+     * used. Those CPU chips can be used for IO purpose like connecting PCIe
+     * devices etc., The CPU whose every cores are bad, can be identified from
+     * the CP00 record's PG keyword, only if all of the 8 EQs' value equals
+     * 0xE7F9FF. (1EQ has 4 cores grouped together by sharing its cache memory.)
+     *
+     * @param [in] pgKeyword - PG Keyword of CPU.
+     * @return true if the given cpu is an IO, false otherwise.
+     */
+    bool isCPUIOGoodOnly(const std::string& pgKeyword);
+
+    /**
+     * @brief API to prime inventory Objects.
+     *
+     * @param[in] i_vpdFilePath - EEPROM file path.
+     * @return true if the prime inventory is success, false otherwise.
+     */
+    bool primeInventory(const std::string& i_vpdFilePath);
+
+    /**
+     * @brief API to process preAction(base_action) defined in config JSON.
+     *
+     * @note sequence of tags under any given flag of preAction is EXTREMELY
+     * important to ensure proper processing. The API will process all the
+     * nested items under the base action sequentially. Also if any of the tag
+     * processing fails, the code will not process remaining tags under the
+     * flag.
+     * ******** sample format **************
+     * fru EEPROM path: {
+     *     base_action: {
+     *         flag1: {
+     *           tag1: {
+     *            },
+     *           tag2: {
+     *            }
+     *         }
+     *         flag2: {
+     *           tags: {
+     *            }
+     *         }
+     *     }
+     * }
+     * *************************************
+     *
+     * @param[in] i_vpdFilePath - Path to the EEPROM file.
+     * @param[in] i_flagToProcess - To identify which flag(s) needs to be
+     * processed under PreAction tag of config JSON.
+     * @return Execution status.
+     */
+    bool processPreAction(const std::string& i_vpdFilePath,
+                          const std::string& i_flagToProcess);
+
+    /**
+     * @brief API to process postAction(base_action) defined in config JSON.
+     *
+     * @note Sequence of tags under any given flag of postAction is EXTREMELY
+     * important to ensure proper processing. The API will process all the
+     * nested items under the base action sequentially. Also if any of the tag
+     * processing fails, the code will not process remaining tags under the
+     * flag.
+     * ******** sample format **************
+     * fru EEPROM path: {
+     *     base_action: {
+     *         flag1: {
+     *           tag1: {
+     *            },
+     *           tag2: {
+     *            }
+     *         }
+     *         flag2: {
+     *           tags: {
+     *            }
+     *         }
+     *     }
+     * }
+     * *************************************
+     * Also, if post action is required to be processed only for FRUs with
+     * certain CCIN then CCIN list can be provided under flag.
+     *
+     * @param[in] i_vpdFruPath - Path to the EEPROM file.
+     * @param[in] i_flagToProcess - To identify which flag(s) needs to be
+     * processed under postAction tag of config JSON.
+     * @param[in] i_parsedVpd - Optional Parsed VPD map. If CCIN match is
+     * required.
+     * @return Execution status.
+     */
+    bool processPostAction(
+        const std::string& i_vpdFruPath, const std::string& i_flagToProcess,
+        const std::optional<types::VPDMapVariant> i_parsedVpd = std::nullopt);
+
+    /**
+     * @brief Function to enable and bring MUX out of idle state.
+     *
+     * This finds all the MUX defined in the system json and enables them by
+     * setting the holdidle parameter to 0.
+     *
+     * @throw std::runtime_error
+     */
+    void enableMuxChips();
+
+    /**
+     * @brief An API to perform backup or restore of VPD.
+     *
+     * @param[in,out] io_srcVpdMap - Source VPD map.
+     */
+    void performBackupAndRestore(types::VPDMapVariant& io_srcVpdMap);
+
+    /**
+     * @brief API to update "Functional" property.
+     *
+     * The API sets the default value for "Functional" property once if the
+     * property is not yet populated over DBus. As the property value is not
+     * controlled by the VPD-Collection process, if it is found already
+     * populated, the functions skips re-populating the property so that already
+     * existing value can be retained.
+     *
+     * @param[in] i_inventoryObjPath - Inventory path as read from config JSON.
+     * @param[in] io_interfaces - Map to hold all the interfaces for the FRU.
+     */
+    void processFunctionalProperty(const std::string& i_inventoryObjPath,
+                                   types::InterfaceMap& io_interfaces);
+
+    /**
+     * @brief API to update "enabled" property.
+     *
+     * The API sets the default value for "enabled" property once if the
+     * property is not yet populated over DBus. As the property value is not
+     * controlled by the VPD-Collection process, if it is found already
+     * populated, the functions skips re-populating the property so that already
+     * existing value can be retained.
+     *
+     * @param[in] i_inventoryObjPath - Inventory path as read from config JSON.
+     * @param[in] io_interfaces - Map to hold all the interfaces for the FRU.
+     */
+    void processEnabledProperty(const std::string& i_inventoryObjPath,
+                                types::InterfaceMap& io_interfaces);
+
+    /**
+     * @brief API to form asset tag string for the system.
+     *
+     * @param[in] i_parsedVpdMap - Parsed VPD map.
+     *
+     * @throw std::runtime_error
+     *
+     * @return - Formed asset tag string.
+     */
+    std::string
+        createAssetTagString(const types::VPDMapVariant& i_parsedVpdMap);
+
+    /**
+     * @brief API to prime system blueprint.
+     *
+     * The API will traverse the system config JSON and will prime all the FRU
+     * paths which qualifies for priming.
+     */
+    void primeSystemBlueprint();
+
+    /**
+     * @brief API to set symbolic link for system config JSON.
+     *
+     * Once correct device tree is set, symbolic link to the correct sytsem
+     * config JSON is set to be used in subsequent BMC boot.
+     *
+     * @param[in] i_systemJson - system config JSON.
+     */
+    void setJsonSymbolicLink(const std::string& i_systemJson);
+
+    // Parsed JSON file.
+    nlohmann::json m_parsedJson{};
+
+    // Hold if symlink is present or not.
+    bool m_isSymlinkPresent = false;
+
+    // Path to config JSON if applicable.
+    std::string& m_configJsonPath;
+
+    // Keeps track of active thread(s) doing VPD collection.
+    size_t m_activeCollectionThreadCount = 0;
+
+    // Holds status, if VPD collection has been done or not.
+    // Note: This variable does not give information about successfull or failed
+    // collection. It just states, if the VPD collection process is over or not.
+    bool m_isAllFruCollected = false;
+
+    // To distinguish the factory reset path.
+    bool m_isFactoryResetDone = false;
+
+    // Mutex to guard critical resource m_activeCollectionThreadCount.
+    std::mutex m_mutex;
+
+    // Counting semaphore to limit the number of threads.
+    std::counting_semaphore<constants::MAX_THREADS> m_semaphore{
+        constants::MAX_THREADS};
+};
+} // namespace vpd