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-tool/include/tool_constants.hpp b/vpd-tool/include/tool_constants.hpp
new file mode 100644
index 0000000..6e89957
--- /dev/null
+++ b/vpd-tool/include/tool_constants.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <cstdint>
+
+namespace vpd
+{
+namespace constants
+{
+static constexpr auto KEYWORD_SIZE = 2;
+static constexpr auto RECORD_SIZE = 4;
+static constexpr auto INDENTATION = 4;
+static constexpr auto SUCCESS = 0;
+static constexpr auto FAILURE = -1;
+
+// To be explicitly used for string comparison.
+static constexpr auto STR_CMP_SUCCESS = 0;
+
+constexpr auto inventoryManagerService =
+    "xyz.openbmc_project.Inventory.Manager";
+constexpr auto baseInventoryPath = "/xyz/openbmc_project/inventory";
+constexpr auto ipzVpdInfPrefix = "com.ibm.ipzvpd.";
+
+constexpr auto vpdManagerService = "com.ibm.VPD.Manager";
+constexpr auto vpdManagerObjectPath = "/com/ibm/VPD/Manager";
+constexpr auto vpdManagerInfName = "com.ibm.VPD.Manager";
+constexpr auto inventoryItemInf = "xyz.openbmc_project.Inventory.Item";
+constexpr auto kwdVpdInf = "com.ibm.ipzvpd.VINI";
+constexpr auto locationCodeInf = "com.ibm.ipzvpd.Location";
+constexpr auto assetInf = "xyz.openbmc_project.Inventory.Decorator.Asset";
+constexpr auto objectMapperService = "xyz.openbmc_project.ObjectMapper";
+constexpr auto objectMapperObjectPath = "/xyz/openbmc_project/object_mapper";
+constexpr auto objectMapperInfName = "xyz.openbmc_project.ObjectMapper";
+} // namespace constants
+} // namespace vpd
diff --git a/vpd-tool/include/tool_types.hpp b/vpd-tool/include/tool_types.hpp
new file mode 100644
index 0000000..1e9ff7d
--- /dev/null
+++ b/vpd-tool/include/tool_types.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include <sdbusplus/message/types.hpp>
+
+#include <cstdint>
+#include <tuple>
+#include <variant>
+#include <vector>
+
+namespace vpd
+{
+namespace types
+{
+using BinaryVector = std::vector<uint8_t>;
+
+// This covers mostly all the data type supported over DBus for a property.
+// clang-format off
+using DbusVariantType = std::variant<
+    std::vector<std::tuple<std::string, std::string, std::string>>,
+    std::vector<std::string>,
+    std::vector<double>,
+    std::string,
+    int64_t,
+    uint64_t,
+    double,
+    int32_t,
+    uint32_t,
+    int16_t,
+    uint16_t,
+    uint8_t,
+    bool,
+    BinaryVector,
+    std::vector<uint32_t>,
+    std::vector<uint16_t>,
+    sdbusplus::message::object_path,
+    std::tuple<uint64_t, std::vector<std::tuple<std::string, std::string, double, uint64_t>>>,
+    std::vector<std::tuple<std::string, std::string>>,
+    std::vector<std::tuple<uint32_t, std::vector<uint32_t>>>,
+    std::vector<std::tuple<uint32_t, size_t>>,
+    std::vector<std::tuple<sdbusplus::message::object_path, std::string,
+                           std::string, std::string>>
+ >;
+
+//IpzType contains tuple of <Record, Keyword>
+using IpzType = std::tuple<std::string, std::string>;
+
+//ReadVpdParams either of IPZ or keyword format
+using ReadVpdParams = std::variant<IpzType, std::string>;
+
+//KwData contains tuple of <keywordName, KeywordValue>
+using KwData = std::tuple<std::string, BinaryVector>;
+
+//IpzData contains tuple of <RecordName, KeywordName, KeywordValue>
+using IpzData = std::tuple<std::string, std::string, BinaryVector>;
+
+//WriteVpdParams either of IPZ or keyword format
+using WriteVpdParams = std::variant<IpzData, KwData>;
+// Return type of ObjectMapper GetObject API
+using MapperGetObject = std::map<std::string,std::vector<std::string>>;
+
+// Table row data
+using TableRowData = std::vector<std::string>;
+
+// Type used to populate table data
+using TableInputData = std::vector<TableRowData>;
+
+// A table column name-size pair
+using TableColumnNameSizePair = std::pair<std::string, std::size_t>;
+
+enum UserOption
+{
+    Exit,
+    UseBackupDataForAll,
+    UseSystemBackplaneDataForAll,
+    MoreOptions,
+    UseBackupDataForCurrent,
+    UseSystemBackplaneDataForCurrent,
+    NewValueOnBoth,
+    SkipCurrent
+};
+
+} // namespace types
+} // namespace vpd
diff --git a/vpd-tool/include/tool_utils.hpp b/vpd-tool/include/tool_utils.hpp
new file mode 100644
index 0000000..4d650c1
--- /dev/null
+++ b/vpd-tool/include/tool_utils.hpp
@@ -0,0 +1,726 @@
+#pragma once
+
+#include "tool_constants.hpp"
+#include "tool_types.hpp"
+
+#include <nlohmann/json.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/exception.hpp>
+
+#include <fstream>
+#include <iostream>
+
+namespace vpd
+{
+namespace utils
+{
+/**
+ * @brief An API to read property from Dbus.
+ *
+ * API reads the property value for the specified interface and object path from
+ * the given Dbus service.
+ *
+ * The caller of the API needs to validate the validity and correctness of the
+ * type and value of data returned. The API will just fetch and return the data
+ * without any data validation.
+ *
+ * Note: It will be caller's responsibility to check for empty value returned
+ * and generate appropriate error if required.
+ *
+ * @param[in] i_serviceName - Name of the Dbus service.
+ * @param[in] i_objectPath - Object path under the service.
+ * @param[in] i_interface - Interface under which property exist.
+ * @param[in] i_property - Property whose value is to be read.
+ *
+ * @return - Value read from Dbus.
+ *
+ * @throw std::runtime_error
+ */
+inline types::DbusVariantType readDbusProperty(
+    const std::string& i_serviceName, const std::string& i_objectPath,
+    const std::string& i_interface, const std::string& i_property)
+{
+    types::DbusVariantType l_propertyValue;
+
+    // Mandatory fields to make a dbus call.
+    if (i_serviceName.empty() || i_objectPath.empty() || i_interface.empty() ||
+        i_property.empty())
+    {
+        // TODO: Enable logging when verbose is enabled.
+        /*std::cout << "One of the parameter to make Dbus read call is empty."
+                  << std::endl;*/
+        throw std::runtime_error("Empty Parameter");
+    }
+
+    try
+    {
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method =
+            l_bus.new_method_call(i_serviceName.c_str(), i_objectPath.c_str(),
+                                  "org.freedesktop.DBus.Properties", "Get");
+        l_method.append(i_interface, i_property);
+
+        auto result = l_bus.call(l_method);
+        result.read(l_propertyValue);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        // TODO: Enable logging when verbose is enabled.
+        // std::cout << std::string(l_ex.what()) << std::endl;
+        throw std::runtime_error(std::string(l_ex.what()));
+    }
+    return l_propertyValue;
+}
+
+/**
+ * @brief An API to print json data on stdout.
+ *
+ * @param[in] i_jsonData - JSON object.
+ */
+inline void printJson(const nlohmann::json& i_jsonData)
+{
+    try
+    {
+        std::cout << i_jsonData.dump(constants::INDENTATION) << std::endl;
+    }
+    catch (const nlohmann::json::type_error& l_ex)
+    {
+        throw std::runtime_error(
+            "Failed to dump JSON data, error: " + std::string(l_ex.what()));
+    }
+}
+
+/**
+ * @brief An API to convert binary value into ascii/hex representation.
+ *
+ * If given data contains printable characters, ASCII formated string value of
+ * the input data will be returned. Otherwise if the data has any non-printable
+ * value, returns the hex represented value of the given data in string format.
+ *
+ * @param[in] i_keywordValue - Data in binary format.
+ *
+ * @throw - Throws std::bad_alloc or std::terminate in case of error.
+ *
+ * @return - Returns the converted string value.
+ */
+inline std::string getPrintableValue(const types::BinaryVector& i_keywordValue)
+{
+    bool l_allPrintable =
+        std::all_of(i_keywordValue.begin(), i_keywordValue.end(),
+                    [](const auto& l_byte) { return std::isprint(l_byte); });
+
+    std::ostringstream l_oss;
+    if (l_allPrintable)
+    {
+        l_oss << std::string(i_keywordValue.begin(), i_keywordValue.end());
+    }
+    else
+    {
+        l_oss << "0x";
+        for (const auto& l_byte : i_keywordValue)
+        {
+            l_oss << std::setfill('0') << std::setw(2) << std::hex
+                  << static_cast<int>(l_byte);
+        }
+    }
+
+    return l_oss.str();
+}
+
+/**
+ * @brief API to read keyword's value from hardware.
+ *
+ * This API reads keyword's value by requesting DBus service(vpd-manager) who
+ * hosts the 'ReadKeyword' method to read keyword's value.
+ *
+ * @param[in] i_eepromPath - EEPROM file path.
+ * @param[in] i_paramsToReadData - Property whose value has to be read.
+ *
+ * @return - Value read from hardware
+ *
+ * @throw std::runtime_error, sdbusplus::exception::SdBusError
+ */
+inline types::DbusVariantType
+    readKeywordFromHardware(const std::string& i_eepromPath,
+                            const types::ReadVpdParams i_paramsToReadData)
+{
+    if (i_eepromPath.empty())
+    {
+        throw std::runtime_error("Empty EEPROM path");
+    }
+
+    try
+    {
+        types::DbusVariantType l_propertyValue;
+
+        auto l_bus = sdbusplus::bus::new_default();
+
+        auto l_method = l_bus.new_method_call(
+            constants::vpdManagerService, constants::vpdManagerObjectPath,
+            constants::vpdManagerInfName, "ReadKeyword");
+
+        l_method.append(i_eepromPath, i_paramsToReadData);
+        auto l_result = l_bus.call(l_method);
+
+        l_result.read(l_propertyValue);
+
+        return l_propertyValue;
+    }
+    catch (const sdbusplus::exception::SdBusError& l_error)
+    {
+        throw;
+    }
+}
+
+/**
+ * @brief API to save keyword's value on file.
+ *
+ * API writes keyword's value on the given file path. If the data is in hex
+ * format, API strips '0x' and saves the value on the given file.
+ *
+ * @param[in] i_filePath - File path.
+ * @param[in] i_keywordValue - Keyword's value.
+ *
+ * @return - true on successfully writing to file, false otherwise.
+ */
+inline bool saveToFile(const std::string& i_filePath,
+                       const std::string& i_keywordValue)
+{
+    bool l_returnStatus = false;
+
+    if (i_keywordValue.empty())
+    {
+        // ToDo: log only when verbose is enabled
+        std::cerr << "Save to file[ " << i_filePath
+                  << "] failed, reason: Empty keyword's value received"
+                  << std::endl;
+        return l_returnStatus;
+    }
+
+    std::string l_keywordValue{i_keywordValue};
+    if (i_keywordValue.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
+    {
+        l_keywordValue = i_keywordValue.substr(2);
+    }
+
+    std::ofstream l_outPutFileStream;
+    l_outPutFileStream.exceptions(
+        std::ifstream::badbit | std::ifstream::failbit);
+    try
+    {
+        l_outPutFileStream.open(i_filePath);
+
+        if (l_outPutFileStream.is_open())
+        {
+            l_outPutFileStream.write(l_keywordValue.c_str(),
+                                     l_keywordValue.size());
+            l_returnStatus = true;
+        }
+        else
+        {
+            // ToDo: log only when verbose is enabled
+            std::cerr << "Error opening output file " << i_filePath
+                      << std::endl;
+        }
+    }
+    catch (const std::ios_base::failure& l_ex)
+    {
+        // ToDo: log only when verbose is enabled
+        std::cerr
+            << "Failed to write to file: " << i_filePath
+            << ", either base folder path doesn't exist or internal error occured, error: "
+            << l_ex.what() << '\n';
+    }
+
+    return l_returnStatus;
+}
+
+/**
+ * @brief API to print data in JSON format on console
+ *
+ * @param[in] i_fruPath - FRU path.
+ * @param[in] i_keywordName - Keyword name.
+ * @param[in] i_keywordStrValue - Keyword's value.
+ */
+inline void displayOnConsole(const std::string& i_fruPath,
+                             const std::string& i_keywordName,
+                             const std::string& i_keywordStrValue)
+{
+    nlohmann::json l_resultInJson = nlohmann::json::object({});
+    nlohmann::json l_keywordValInJson = nlohmann::json::object({});
+
+    l_keywordValInJson.emplace(i_keywordName, i_keywordStrValue);
+    l_resultInJson.emplace(i_fruPath, l_keywordValInJson);
+
+    printJson(l_resultInJson);
+}
+
+/**
+ * @brief API to write keyword's value.
+ *
+ * This API writes keyword's value by requesting DBus service(vpd-manager) who
+ * hosts the 'UpdateKeyword' method to update keyword's value.
+ *
+ * @param[in] i_vpdPath - EEPROM or object path, where keyword is present.
+ * @param[in] i_paramsToWriteData - Data required to update keyword's value.
+ *
+ * @return - Number of bytes written on success, -1 on failure.
+ *
+ * @throw - std::runtime_error, sdbusplus::exception::SdBusError
+ */
+inline int writeKeyword(const std::string& i_vpdPath,
+                        const types::WriteVpdParams& i_paramsToWriteData)
+{
+    if (i_vpdPath.empty())
+    {
+        throw std::runtime_error("Empty path");
+    }
+
+    int l_rc = constants::FAILURE;
+    auto l_bus = sdbusplus::bus::new_default();
+
+    auto l_method = l_bus.new_method_call(
+        constants::vpdManagerService, constants::vpdManagerObjectPath,
+        constants::vpdManagerInfName, "UpdateKeyword");
+
+    l_method.append(i_vpdPath, i_paramsToWriteData);
+    auto l_result = l_bus.call(l_method);
+
+    l_result.read(l_rc);
+    return l_rc;
+}
+
+/**
+ * @brief API to write keyword's value on hardware.
+ *
+ * This API writes keyword's value by requesting DBus service(vpd-manager) who
+ * hosts the 'WriteKeywordOnHardware' method to update keyword's value.
+ *
+ * Note: This API updates keyword's value only on the given hardware path, any
+ * backup or redundant EEPROM (if exists) paths won't get updated.
+ *
+ * @param[in] i_eepromPath - EEPROM where keyword is present.
+ * @param[in] i_paramsToWriteData - Data required to update keyword's value.
+ *
+ * @return - Number of bytes written on success, -1 on failure.
+ *
+ * @throw - std::runtime_error, sdbusplus::exception::SdBusError
+ */
+inline int
+    writeKeywordOnHardware(const std::string& i_eepromPath,
+                           const types::WriteVpdParams& i_paramsToWriteData)
+{
+    if (i_eepromPath.empty())
+    {
+        throw std::runtime_error("Empty path");
+    }
+
+    int l_rc = constants::FAILURE;
+    auto l_bus = sdbusplus::bus::new_default();
+
+    auto l_method = l_bus.new_method_call(
+        constants::vpdManagerService, constants::vpdManagerObjectPath,
+        constants::vpdManagerInfName, "WriteKeywordOnHardware");
+
+    l_method.append(i_eepromPath, i_paramsToWriteData);
+    auto l_result = l_bus.call(l_method);
+
+    l_result.read(l_rc);
+
+    if (l_rc > 0)
+    {
+        std::cout << "Data updated successfully " << std::endl;
+    }
+    return l_rc;
+}
+
+/**
+ * @brief API to get data in binary format.
+ *
+ * This API converts given string value into array of binary data.
+ *
+ * @param[in] i_value - Input data.
+ *
+ * @return - Array of binary data on success, throws as exception in case
+ * of any error.
+ *
+ * @throw std::runtime_error, std::out_of_range, std::bad_alloc,
+ * std::invalid_argument
+ */
+inline types::BinaryVector convertToBinary(const std::string& i_value)
+{
+    if (i_value.empty())
+    {
+        throw std::runtime_error(
+            "Provide a valid hexadecimal input. (Ex. 0x30313233)");
+    }
+
+    std::vector<uint8_t> l_binaryValue{};
+
+    if (i_value.substr(0, 2).compare("0x") == constants::STR_CMP_SUCCESS)
+    {
+        if (i_value.length() % 2 != 0)
+        {
+            throw std::runtime_error(
+                "Write option accepts 2 digit hex numbers. (Ex. 0x1 "
+                "should be given as 0x01).");
+        }
+
+        auto l_value = i_value.substr(2);
+
+        if (l_value.empty())
+        {
+            throw std::runtime_error(
+                "Provide a valid hexadecimal input. (Ex. 0x30313233)");
+        }
+
+        if (l_value.find_first_not_of("0123456789abcdefABCDEF") !=
+            std::string::npos)
+        {
+            throw std::runtime_error("Provide a valid hexadecimal input.");
+        }
+
+        for (size_t l_pos = 0; l_pos < l_value.length(); l_pos += 2)
+        {
+            uint8_t l_byte = static_cast<uint8_t>(
+                std::stoi(l_value.substr(l_pos, 2), nullptr, 16));
+            l_binaryValue.push_back(l_byte);
+        }
+    }
+    else
+    {
+        l_binaryValue.assign(i_value.begin(), i_value.end());
+    }
+    return l_binaryValue;
+}
+
+/**
+ * @brief API to parse respective JSON.
+ *
+ * @param[in] i_pathToJson - Path to JSON.
+ *
+ * @return Parsed JSON, throws exception in case of error.
+ *
+ * @throw std::runtime_error
+ */
+inline nlohmann::json getParsedJson(const std::string& i_pathToJson)
+{
+    if (i_pathToJson.empty())
+    {
+        throw std::runtime_error("Path to JSON is missing");
+    }
+
+    std::error_code l_ec;
+    if (!std::filesystem::exists(i_pathToJson, l_ec))
+    {
+        std::string l_message{
+            "file system call failed for file: " + i_pathToJson};
+
+        if (l_ec)
+        {
+            l_message += ", error: " + l_ec.message();
+        }
+        throw std::runtime_error(l_message);
+    }
+
+    if (std::filesystem::is_empty(i_pathToJson, l_ec))
+    {
+        throw std::runtime_error("Empty file: " + i_pathToJson);
+    }
+    else if (l_ec)
+    {
+        throw std::runtime_error("is_empty file system call failed for file: " +
+                                 i_pathToJson + ", error: " + l_ec.message());
+    }
+
+    std::ifstream l_jsonFile(i_pathToJson);
+    if (!l_jsonFile)
+    {
+        throw std::runtime_error("Failed to access Json path: " + i_pathToJson);
+    }
+
+    try
+    {
+        return nlohmann::json::parse(l_jsonFile);
+    }
+    catch (const nlohmann::json::parse_error& l_ex)
+    {
+        throw std::runtime_error("Failed to parse JSON file: " + i_pathToJson);
+    }
+}
+
+/**
+ * @brief API to get list of interfaces under a given object path.
+ *
+ * Given a DBus object path, this API returns a map of service -> implemented
+ * interface(s) under that object path. This API calls DBus method GetObject
+ * hosted by ObjectMapper DBus service.
+ *
+ * @param[in] i_objectPath - DBus object path.
+ * @param[in] i_constrainingInterfaces - An array of result set constraining
+ * interfaces.
+ *
+ * @return On success, returns a map of service -> implemented interface(s),
+ * else returns an empty map. The caller of this
+ * API should check for empty map.
+ */
+inline types::MapperGetObject GetServiceInterfacesForObject(
+    const std::string& i_objectPath,
+    const std::vector<std::string>& i_constrainingInterfaces) noexcept
+{
+    types::MapperGetObject l_serviceInfMap;
+    if (i_objectPath.empty())
+    {
+        // TODO: log only when verbose is enabled
+        std::cerr << "Object path is empty." << std::endl;
+        return l_serviceInfMap;
+    }
+
+    try
+    {
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method = l_bus.new_method_call(
+            constants::objectMapperService, constants::objectMapperObjectPath,
+            constants::objectMapperInfName, "GetObject");
+
+        l_method.append(i_objectPath, i_constrainingInterfaces);
+
+        auto l_result = l_bus.call(l_method);
+        l_result.read(l_serviceInfMap);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        // TODO: log only when verbose is enabled
+        // std::cerr << std::string(l_ex.what()) << std::endl;
+    }
+    return l_serviceInfMap;
+}
+
+/** @brief API to get list of sub tree paths for a given object path
+ *
+ * Given a DBus object path, this API returns a list of object paths under that
+ * object path in the DBus tree. This API calls DBus method GetSubTreePaths
+ * hosted by ObjectMapper DBus service.
+ *
+ * @param[in] i_objectPath - DBus object path.
+ * @param[in] i_constrainingInterfaces - An array of result set constraining
+ * interfaces.
+ * @param[in] i_depth - The maximum subtree depth for which results should be
+ * fetched. For unconstrained fetches use a depth of zero.
+ *
+ * @return On success, returns a std::vector<std::string> of object paths in
+ * Phosphor Inventory Manager DBus service's tree, else returns an empty vector.
+ * The caller of this API should check for empty vector.
+ */
+inline std::vector<std::string> GetSubTreePaths(
+    const std::string i_objectPath, const int i_depth = 0,
+    const std::vector<std::string>& i_constrainingInterfaces = {}) noexcept
+{
+    std::vector<std::string> l_objectPaths;
+
+    try
+    {
+        auto l_bus = sdbusplus::bus::new_default();
+        auto l_method = l_bus.new_method_call(
+            constants::objectMapperService, constants::objectMapperObjectPath,
+            constants::objectMapperInfName, "GetSubTreePaths");
+
+        l_method.append(i_objectPath, i_depth, i_constrainingInterfaces);
+
+        auto l_result = l_bus.call(l_method);
+        l_result.read(l_objectPaths);
+    }
+    catch (const sdbusplus::exception::SdBusError& l_ex)
+    {
+        // TODO: log only when verbose is enabled
+        std::cerr << std::string(l_ex.what()) << std::endl;
+    }
+    return l_objectPaths;
+}
+
+/**
+ * @brief A class to print data in tabular format
+ *
+ * This class implements methods to print data in a two dimensional tabular
+ * format. All entries in the table must be in string format.
+ *
+ */
+class Table
+{
+    class Column : public types::TableColumnNameSizePair
+    {
+      public:
+        /**
+         * @brief API to get the name of the Column
+         *
+         * @return Name of the Column.
+         */
+        const std::string& Name() const
+        {
+            return this->first;
+        }
+
+        /**
+         * @brief API to get the width of the Column
+         *
+         * @return Width of the Column.
+         */
+        std::size_t Width() const
+        {
+            return this->second;
+        }
+    };
+
+    // Current width of the table
+    std::size_t m_currentWidth;
+
+    // Character to be used as fill character between entries
+    char m_fillCharacter;
+
+    // Separator character to be used between columns
+    char m_separator;
+
+    // Array of columns
+    std::vector<Column> m_columns;
+
+    /**
+     * @brief API to Print Header
+     *
+     * Header line prints the names of the Column headers separated by the
+     * specified separator character and spaced accordingly.
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    void PrintHeader() const
+    {
+        for (const auto& l_column : m_columns)
+        {
+            PrintEntry(l_column.Name(), l_column.Width());
+        }
+        std::cout << m_separator << std::endl;
+    }
+
+    /**
+     * @brief API to Print Horizontal Line
+     *
+     * A horizontal line is a sequence of '*'s.
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    void PrintHorizontalLine() const
+    {
+        std::cout << std::string(m_currentWidth, '*') << std::endl;
+    }
+
+    /**
+     * @brief API to print an entry in the table
+     *
+     * An entry is a separator character followed by the text to print.
+     * The text is centre-aligned.
+     *
+     * @param[in] i_text - text to print
+     * @param[in] i_columnWidth - width of the column
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    void PrintEntry(const std::string& i_text, std::size_t i_columnWidth) const
+    {
+        const std::size_t l_textLength{i_text.length()};
+
+        constexpr std::size_t l_minFillChars{3};
+        const std::size_t l_numFillChars =
+            ((l_textLength >= i_columnWidth ? l_minFillChars
+                                            : i_columnWidth - l_textLength)) -
+            1; // -1 for the separator character
+
+        const unsigned l_oddFill = l_numFillChars % 2;
+
+        std::cout << m_separator
+                  << std::string((l_numFillChars / 2) + l_oddFill,
+                                 m_fillCharacter)
+                  << i_text << std::string(l_numFillChars / 2, m_fillCharacter);
+    }
+
+  public:
+    /**
+     * @brief Table Constructor
+     *
+     * Parameterized constructor for a Table object
+     *
+     */
+    constexpr explicit Table(const char i_fillCharacter = ' ',
+                             const char i_separator = '|') noexcept :
+        m_currentWidth{0}, m_fillCharacter{i_fillCharacter},
+        m_separator{i_separator}
+    {}
+
+    // deleted methods
+    Table(const Table&) = delete;
+    Table operator=(const Table&) = delete;
+    Table(const Table&&) = delete;
+    Table operator=(const Table&&) = delete;
+
+    ~Table() = default;
+
+    /**
+     * @brief API to add column to Table
+     *
+     * @param[in] i_name - Name of the column.
+     *
+     * @param[in] i_width - Width to allocate for the column.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int AddColumn(const std::string& i_name, std::size_t i_width)
+    {
+        if (i_width < i_name.length())
+            return constants::FAILURE;
+        m_columns.emplace_back(types::TableColumnNameSizePair(i_name, i_width));
+        m_currentWidth += i_width;
+        return constants::SUCCESS;
+    }
+
+    /**
+     * @brief API to print the Table to console.
+     *
+     * This API prints the table data to console.
+     *
+     * @param[in] i_tableData - The data to be printed.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     *
+     * @throw std::out_of_range, std::length_error, std::bad_alloc
+     */
+    int Print(const types::TableInputData& i_tableData) const
+    {
+        PrintHorizontalLine();
+        PrintHeader();
+        PrintHorizontalLine();
+
+        // print the table data
+        for (const auto& l_row : i_tableData)
+        {
+            unsigned l_columnNumber{0};
+
+            // number of columns in input data is greater than the number of
+            // columns specified in Table
+            if (l_row.size() > m_columns.size())
+            {
+                return constants::FAILURE;
+            }
+
+            for (const auto& l_entry : l_row)
+            {
+                PrintEntry(l_entry, m_columns[l_columnNumber].Width());
+
+                ++l_columnNumber;
+            }
+            std::cout << m_separator << std::endl;
+        }
+        PrintHorizontalLine();
+        return constants::SUCCESS;
+    }
+};
+
+} // namespace utils
+} // namespace vpd
diff --git a/vpd-tool/include/vpd_tool.hpp b/vpd-tool/include/vpd_tool.hpp
new file mode 100644
index 0000000..80be8e5
--- /dev/null
+++ b/vpd-tool/include/vpd_tool.hpp
@@ -0,0 +1,287 @@
+#pragma once
+
+#include "tool_utils.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <optional>
+#include <string>
+
+namespace vpd
+{
+/**
+ * @brief Class to support operations on VPD.
+ *
+ * The class provides API's to,
+ * Read keyword value from DBus/hardware.
+ * Update keyword value to DBus/hardware.
+ * Dump DBus object's critical information.
+ * Fix system VPD if any mismatch between DBus and hardware data.
+ * Reset specific system VPD keywords to its default value.
+ * Force VPD collection for hardware.
+ */
+class VpdTool
+{
+  private:
+    /**
+     * @brief Get specific properties of a FRU in JSON format.
+     *
+     * For a given object path of a FRU, this API returns the following
+     * properties of the FRU in JSON format:
+     * - Pretty Name, Location Code, Sub Model
+     * - SN, PN, CC, FN, DR keywords under VINI record.
+     *
+     * @param[in] i_objectPath - DBus object path
+     *
+     * @return On success, returns the properties of the FRU in JSON format,
+     * otherwise returns an empty JSON.
+     * If FRU's "Present" property is false, this API returns an empty JSON.
+     * Note: The caller of this API should handle empty JSON.
+     *
+     * @throw json::exception
+     */
+    nlohmann::json getFruProperties(const std::string& i_objectPath) const;
+
+    /**
+     * @brief Get any inventory property in JSON.
+     *
+     * API to get any property of a FRU in JSON format. Given an object path,
+     * interface and property name, this API does a D-Bus read property on PIM
+     * to get the value of that property and returns it in JSON format. This API
+     * returns empty JSON in case of failure. The caller of the API must check
+     * for empty JSON.
+     *
+     * @param[in] i_objectPath - DBus object path
+     * @param[in] i_interface - Interface name
+     * @param[in] i_propertyName - Property name
+     *
+     * @return On success, returns the property and its value in JSON format,
+     * otherwise return empty JSON.
+     * {"SN" : "ABCD"}
+     */
+    template <typename PropertyType>
+    nlohmann::json getInventoryPropertyJson(
+        const std::string& i_objectPath, const std::string& i_interface,
+        const std::string& i_propertyName) const noexcept;
+
+    /**
+     * @brief Get the "type" property for a FRU.
+     *
+     * Given a FRU path, and parsed System Config JSON, this API returns the
+     * "type" property for the FRU in JSON format. This API gets
+     * these properties from Phosphor Inventory Manager.
+     *
+     * @param[in] i_objectPath - DBus object path.
+     *
+     * @return On success, returns the "type" property in JSON
+     * format, otherwise returns empty JSON. The caller of this API should
+     * handle empty JSON.
+     */
+    nlohmann::json
+        getFruTypeProperty(const std::string& i_objectPath) const noexcept;
+
+    /**
+     * @brief Check if a FRU is present in the system.
+     *
+     * Given a FRU's object path, this API checks if the FRU is present in the
+     * system by reading the "Present" property of the FRU.
+     *
+     * @param[in] i_objectPath - DBus object path.
+     *
+     * @return true if FRU's "Present" property is true, false otherwise.
+     */
+    bool isFruPresent(const std::string& i_objectPath) const noexcept;
+
+    /**
+     * @brief An API to get backup-restore config JSON of the system.
+     *
+     * API gets this file by prasing system config JSON file and reading
+     * backupRestoreConfigPath tag.
+     *
+     * @return On success returns valid JSON object, otherwise returns empty
+     * JSON object.
+     *
+     * Note: The caller of this API should verify, is received JSON object is
+     * empty or not.
+     */
+    nlohmann::json getBackupRestoreCfgJsonObj() const noexcept;
+
+    /**
+     * @brief Prints the user options for fix system VPD command.
+     *
+     * @param[in] i_option - Option to use.
+     */
+    void printFixSystemVpdOption(
+        const types::UserOption& i_option) const noexcept;
+
+    /**
+     * @brief API to update source and destination keyword's value.
+     *
+     * API fetches source and destination keyword's value,
+     * for each keyword entries found in the input JSON object and updates the
+     * JSON object. If the path(source / destination) in JSON object is
+     * inventory object path, API sends the request to Inventory.Manager DBus
+     * service. Otherwise if its a hardware path, API sends the request to
+     * vpd-manager DBus service to get the keyword's value.
+     *
+     * @param[in,out] io_parsedJsonObj - Parsed JSON object.
+     *
+     * @return true on success, false in case of any error.
+     */
+    bool fetchKeywordInfo(nlohmann::json& io_parsedJsonObj) const noexcept;
+
+    /**
+     * @brief API to print system VPD keyword's information.
+     *
+     * The API prints source and destination keyword's information in the table
+     * format, found in the JSON object.
+     *
+     * @param[in] i_parsedJsonObj - Parsed JSON object.
+     */
+    void printSystemVpd(const nlohmann::json& i_parsedJsonObj) const noexcept;
+
+    /**
+     * @brief API to update keyword's value.
+     *
+     * API iterates the given JSON object for all record-keyword pairs, if there
+     * is any mismatch between source and destination keyword's value, API calls
+     * the utils::writeKeyword API to update keyword's value.
+     *
+     * Note: writeKeyword API, internally updates primary, backup, redundant
+     * EEPROM paths(if exists) with the given keyword's value.
+     *
+     * @param i_parsedJsonObj - Parsed JSON object.
+     * @param i_useBackupData - Specifies whether to use source or destination
+     * keyword's value to update the keyword's value.
+     *
+     * @return On success return 0, otherwise return -1.
+     */
+    int updateAllKeywords(const nlohmann::json& i_parsedJsonObj,
+                          bool i_useBackupData) const noexcept;
+
+    /**
+     * @brief API to handle more option for fix system VPD command.
+     *
+     * @param i_parsedJsonObj - Parsed JSON object.
+     *
+     * @return On success return 0, otherwise return -1.
+     */
+    int handleMoreOption(const nlohmann::json& i_parsedJsonObj) const noexcept;
+
+  public:
+    /**
+     * @brief Read keyword value.
+     *
+     * API to read VPD keyword's value from the given input path.
+     * If the provided i_onHardware option is true, read keyword's value from
+     * the hardware. Otherwise read keyword's value from DBus.
+     *
+     * @param[in] i_vpdPath - DBus object path or EEPROM path.
+     * @param[in] i_recordName - Record name.
+     * @param[in] i_keywordName - Keyword name.
+     * @param[in] i_onHardware - True if i_vpdPath is EEPROM path, false
+     * otherwise.
+     * @param[in] i_fileToSave - File path to save keyword's value, if not given
+     * result will redirect to a console.
+     *
+     * @return On success return 0, otherwise return -1.
+     */
+    int readKeyword(const std::string& i_vpdPath,
+                    const std::string& i_recordName,
+                    const std::string& i_keywordName, const bool i_onHardware,
+                    const std::string& i_fileToSave = {});
+
+    /**
+     * @brief Dump the given inventory object in JSON format to console.
+     *
+     * For a given object path of a FRU, this API dumps the following properties
+     * of the FRU in JSON format to console:
+     * - Pretty Name, Location Code, Sub Model
+     * - SN, PN, CC, FN, DR keywords under VINI record.
+     * If the FRU's "Present" property is not true, the above properties are not
+     * dumped to console.
+     *
+     * @param[in] i_fruPath - DBus object path.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int dumpObject(std::string i_fruPath) const noexcept;
+
+    /**
+     * @brief API to fix system VPD keywords.
+     *
+     * The API to fix the system VPD keywords. Mainly used when there
+     * is a mismatch between the primary and backup(secondary) VPD. User can
+     * choose option to update all primary keywords value with corresponding
+     * backup keywords value or can choose primary keyword value to sync
+     * secondary VPD. Otherwise, user can also interactively choose different
+     * action for individual keyword.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int fixSystemVpd() const noexcept;
+
+    /**
+     * @brief Write keyword's value.
+     *
+     * API to update VPD keyword's value to the given input path.
+     * If i_onHardware value in true, i_vpdPath is considered has hardware path
+     * otherwise it will be considered as DBus object path.
+     *
+     * For provided DBus object path both primary path or secondary path will
+     * get updated, also redundant EEPROM(if any) path with new keyword's value.
+     *
+     * In case of hardware path, only given hardware path gets updated with new
+     * keyword’s value, any backup or redundant EEPROM (if exists) paths won't
+     * get updated.
+     *
+     * @param[in] i_vpdPath - DBus object path or EEPROM path.
+     * @param[in] i_recordName - Record name.
+     * @param[in] i_keywordName - Keyword name.
+     * @param[in] i_keywordValue - Keyword value.
+     * @param[in] i_onHardware - True if i_vpdPath is EEPROM path, false
+     * otherwise.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int writeKeyword(std::string i_vpdPath, const std::string& i_recordName,
+                     const std::string& i_keywordName,
+                     const std::string& i_keywordValue,
+                     const bool i_onHardware) noexcept;
+
+    /**
+     * @brief Reset specific keywords on System VPD to default value.
+     *
+     * This API resets specific System VPD keywords to default value. The
+     * keyword values are reset on:
+     * 1. Primary EEPROM path.
+     * 2. Secondary EEPROM path.
+     * 3. D-Bus cache.
+     * 4. Backup path.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int cleanSystemVpd() const noexcept;
+
+    /**
+     * @brief Dump all the inventory objects in JSON or table format to console.
+     *
+     * This API dumps specific properties of all the inventory objects to
+     * console in JSON or table format to console. The inventory object paths
+     * are extracted from PIM. For each object, the following properties are
+     * dumped to console:
+     * - Present property, Pretty Name, Location Code, Sub Model
+     * - SN, PN, CC, FN, DR keywords under VINI record.
+     * If the "Present" property of a FRU is false, the FRU is not dumped to
+     * console.
+     * FRUs whose object path end in "unit([0-9][0-9]?)" are also not dumped to
+     * console.
+     *
+     * @param[in] i_dumpTable - Flag which specifies if the inventory should be
+     * dumped in table format or not.
+     *
+     * @return On success returns 0, otherwise returns -1.
+     */
+    int dumpInventory(bool i_dumpTable = false) const noexcept;
+};
+} // namespace vpd