psutils: Move functions from updater to utils

Move common, utility functions from updater.*pp to utils.*pp.  This will
enable those functions to be used by other command line options in the
psutils tool.

Modify --get-version and --get-model to use the new utility functions.

Also update --get-version to provide a single getVersion() function that
handles the existence of the psu.json file as a low-level implementation
detail.

Tested:
* Verified all automated tests run successfully
* Verified --get-version still works
  * With psu.json file
  * Without psu.json file
* Verified --get-model still works
  * With psu.json file
  * Without psu.json file
* Verified --update still gets correct device path, device name, and I2C
  bus/address from functions that moved to utils.*pp
* The complete test plan is available at
  https://gist.github.com/smccarney/c049e24655d32e22cab9d521d145774a

Change-Id: I51ceca10957dc9a924d0d7516dc29632a6ed82d3
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/tools/power-utils/version.cpp b/tools/power-utils/version.cpp
index 26507ee..738664d 100644
--- a/tools/power-utils/version.cpp
+++ b/tools/power-utils/version.cpp
@@ -39,6 +39,17 @@
 namespace internal
 {
 
+// PsuInfo contains the device path, PMBus access type, and sysfs file name
+using PsuVersionInfo =
+    std::tuple<std::string, phosphor::pmbus::Type, std::string>;
+
+/**
+ * @brief Get PSU version information
+ *
+ * @param[in] psuInventoryPath - The PSU inventory path.
+ *
+ * @return tuple - device path, PMBus access type, and sysfs file name
+ */
 PsuVersionInfo getVersionInfo(const std::string& psuInventoryPath)
 {
     auto data = loadJSONFromFile(PSU_JSON_PATH);
@@ -64,24 +75,83 @@
 
     auto type = getPMBusAccessType(data);
 
-    std::string versionStr;
+    std::string fileName;
     for (const auto& fru : data["fruConfigs"])
     {
-        if (fru["propertyName"] == "Version")
+        if (fru.contains("propertyName") &&
+            (fru["propertyName"] == "Version") && fru.contains("fileName"))
         {
-            versionStr = fru["fileName"].get<std::string>();
+            fileName = fru["fileName"];
             break;
         }
     }
-    if (versionStr.empty())
+    if (fileName.empty())
     {
         log<level::WARNING>("Unable to find Version file");
         return {};
     }
-    return std::make_tuple(*devicePath, type, versionStr);
+    return std::make_tuple(*devicePath, type, fileName);
 }
 
-// A default implemention compare the string itself
+/**
+ * @brief Get the PSU version from sysfs.
+ *
+ * Obtain PSU information from the PSU JSON file.
+ *
+ * Throws an exception if an error occurs.
+ *
+ * @param[in] psuInventoryPath - PSU D-Bus inventory path
+ *
+ * @return PSU version, or "" if none found
+ */
+std::string getVersionJson(const std::string& psuInventoryPath)
+{
+    // Get PSU device path, PMBus access type, and sysfs file name from JSON
+    const auto [devicePath, type, fileName] = getVersionInfo(psuInventoryPath);
+
+    // Read version from sysfs file
+    std::string version;
+    if (!devicePath.empty() && !fileName.empty())
+    {
+        phosphor::pmbus::PMBus pmbus(devicePath);
+        version = pmbus.readString(fileName, type);
+    }
+    return version;
+}
+
+/**
+ * @brief Get the PSU version from sysfs.
+ *
+ * Obtain PSU information from D-Bus.
+ *
+ * Throws an exception if an error occurs.
+ *
+ * @param[in] bus - D-Bus connection
+ * @param[in] psuInventoryPath - PSU D-Bus inventory path
+ *
+ * @return PSU version, or "" if none found
+ */
+std::string getVersionDbus(sdbusplus::bus_t& bus,
+                           const std::string& psuInventoryPath)
+{
+    // Get PSU I2C bus/address and create PMBus interface
+    const auto [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
+    auto pmbus = getPmbusIntf(i2cbus, i2caddr);
+
+    // Read version from sysfs file
+    std::string name = "fw_version";
+    auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
+    std::string version = pmbus->readString(name, type);
+    return version;
+}
+
+/**
+ * @brief Get firmware latest version
+ *
+ * @param[in] versions - String of versions
+ *
+ * @return version - latest firmware level
+ */
 std::string getLatestDefault(const std::vector<std::string>& versions)
 {
     std::string latest;
@@ -97,38 +167,22 @@
 
 } // namespace internal
 
-std::string getVersion(const std::string& psuInventoryPath)
-{
-    const auto& [devicePath, type, versionStr] =
-        internal::getVersionInfo(psuInventoryPath);
-    if (devicePath.empty() || versionStr.empty())
-    {
-        return "";
-    }
-    std::string version;
-    try
-    {
-        phosphor::pmbus::PMBus pmbus(devicePath);
-        version = pmbus.readString(versionStr, type);
-    }
-    catch (const std::exception& ex)
-    {
-        log<level::ERR>(ex.what());
-    }
-    return version;
-}
-
 std::string getVersion(sdbusplus::bus_t& bus,
                        const std::string& psuInventoryPath)
 {
     std::string version;
     try
     {
-        const auto& [i2cbus, i2caddr] = getPsuI2c(bus, psuInventoryPath);
-        auto pmbus = getPmbusIntf(i2cbus, i2caddr);
-        std::string name = "fw_version";
-        auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
-        version = pmbus->readString(name, type);
+        if (usePsuJsonFile())
+        {
+            // Obtain PSU information from JSON file
+            version = internal::getVersionJson(psuInventoryPath);
+        }
+        else
+        {
+            // Obtain PSU information from D-Bus
+            version = internal::getVersionDbus(bus, psuInventoryPath);
+        }
     }
     catch (const std::exception& e)
     {
@@ -157,4 +211,5 @@
     // So just compare by strings is OK for these cases
     return internal::getLatestDefault(versions);
 }
+
 } // namespace version