psu-ng: Validate configuration during power on

Create a function to validate the power supply configuration. Initially,
check that all power supplies are of the same model, and if not, create
an error log. This function will later add checks for total number of
power supplies and expected input voltage.

The validation is run on every power on. To cover the case where entity
manager has not run yet on power, trigger the validation on interfaces
added signal. Use a boolean flag to determine if the validation needs to
be run to avoid running it multiple times.

Tested: Verified the validation was successful on Rainier.
        Verified the error log data:
$ curl -k -H "X-Auth-Token: $token" \
  https://${bmc}/xyz/openbmc_project/logging/entry/2
{
  "data": {
    "AdditionalData": [
      "ACTUAL_MODEL=51E8",
      "CALLOUT_INVENTORY_PATH=/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1",
      "EXPECTED_MODEL=51E9"
    ],
    "Associations": [],
    "Id": 2,
    "Message":
"xyz.openbmc_project.Power.PowerSupply.Error.NotSupported",
    "Path":
"/var/lib/phosphor-logging/extensions/pels/logs/2021040718580625_50000002",

Change-Id: Ib8dd23465bfd36b510197a88d41fc8419862f33b
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/phosphor-power-supply/psu_manager.cpp b/phosphor-power-supply/psu_manager.cpp
index c6d5855..2ab8cfb 100644
--- a/phosphor-power-supply/psu_manager.cpp
+++ b/phosphor-power-supply/psu_manager.cpp
@@ -247,6 +247,14 @@
                     .c_str());
             getPSUProperties(itIntf->second);
         }
+
+        // Call to validate the psu configuration if the power is on and both
+        // the IBMCFFPSConnector and SupportedConfiguration interfaces have been
+        // processed
+        if (powerOn && !psus.empty() && !supportedConfigs.empty())
+        {
+            validateConfig();
+        }
     }
     catch (std::exception& e)
     {
@@ -271,11 +279,13 @@
         if (state)
         {
             powerOn = true;
+            validateConfig();
             clearFaults();
         }
         else
         {
             powerOn = false;
+            runValidateConfig = true;
         }
     }
 }
@@ -407,4 +417,49 @@
     }
 }
 
+void PSUManager::validateConfig()
+{
+    if (!runValidateConfig)
+    {
+        return;
+    }
+
+    // Check that all PSUs have the same model name. Initialize the model
+    // variable with the first PSU name found, then use it as a base to compare
+    // against the rest of the PSUs.
+    std::string model{};
+    for (const auto& p : psus)
+    {
+        auto psuModel = p->getModelName();
+        if (psuModel.empty())
+        {
+            continue;
+        }
+        if (model.empty())
+        {
+            model = psuModel;
+            continue;
+        }
+        if (psuModel.compare(model) != 0)
+        {
+            log<level::ERR>(
+                fmt::format("Mismatched power supply models: {}, {}",
+                            model.c_str(), psuModel.c_str())
+                    .c_str());
+            std::map<std::string, std::string> additionalData;
+            additionalData["EXPECTED_MODEL"] = model;
+            additionalData["ACTUAL_MODEL"] = psuModel;
+            additionalData["CALLOUT_INVENTORY_PATH"] = p->getInventoryPath();
+            createError(
+                "xyz.openbmc_project.Power.PowerSupply.Error.NotSupported",
+                additionalData);
+
+            // No need to do the validation anymore, a mismatched model needs to
+            // be fixed by the user.
+            runValidateConfig = false;
+            return;
+        }
+    }
+}
+
 } // namespace phosphor::power::manager