pseq: Add is_power_supply_rail JSON file property

Enhance the JSON configuration file for the phosphor-power-sequencer
application.  Add an "is_power_supply_rail" property to the "rail"
object.

Update the config file documentation, config file parser, Rail class,
and the associated tests.

Change-Id: I3d0e8276324dd9cf25f386016c6f275b540515aa
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/docs/config_file/rail.md b/phosphor-power-sequencer/docs/config_file/rail.md
index 982c6a6..7346c13 100644
--- a/phosphor-power-sequencer/docs/config_file/rail.md
+++ b/phosphor-power-sequencer/docs/config_file/rail.md
@@ -10,14 +10,15 @@
 
 ## Properties
 
-| Name                      |      Required       | Type                    | Description                                                                                                                                                                                                                                                                                                             |
-| :------------------------ | :-----------------: | :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| name                      |         yes         | string                  | Unique name for the rail. Can only contain letters (A-Z, a-z), numbers (0-9), period (.), and underscore (\_).                                                                                                                                                                                                          |
-| presence                  |         no          | string                  | D-Bus inventory path of a system component which must be present in order for the rail to be present. If this property is not specified, the rail is assumed to always be present.                                                                                                                                      |
-| page                      | see [notes](#notes) | number                  | PMBus PAGE number of the rail.                                                                                                                                                                                                                                                                                          |
-| check_status_vout         |         no          | boolean (true or false) | If true, determine the pgood status of the rail by checking the value of the PMBus STATUS_VOUT command. If one of the error bits is set, the rail pgood will be considered false. The default value of this property is false.                                                                                          |
-| compare_voltage_to_limits |         no          | boolean (true or false) | If true, determine the pgood status of the rail by comparing the output voltage (READ_VOUT) to the undervoltage (VOUT_UV_FAULT_LIMIT) and overvoltage (VOUT_OV_FAULT_LIMIT) limits. If the output voltage is beyond those limits, the rail pgood will be considered false. The default value of this property is false. |
-| gpio                      |         no          | [gpio](gpio.md)         | Determine the pgood status of the rail by reading a GPIO.                                                                                                                                                                                                                                                               |
+| Name                      |      Required       | Type                    | Description                                                                                                                                                                                                                                                                                                                    |
+| :------------------------ | :-----------------: | :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| name                      |         yes         | string                  | Unique name for the rail. Can only contain letters (A-Z, a-z), numbers (0-9), period (.), and underscore (\_).                                                                                                                                                                                                                 |
+| presence                  |         no          | string                  | D-Bus inventory path of a system component which must be present in order for the rail to be present. If this property is not specified, the rail is assumed to always be present.                                                                                                                                             |
+| page                      | see [notes](#notes) | number                  | PMBus PAGE number of the rail.                                                                                                                                                                                                                                                                                                 |
+| is_power_supply_rail      |         no          | boolean (true or false) | If true, this rail is produced by a power supply. Power supply rails require special error handling. If an error is detected in a power supply device, and the pgood status is false for a power supply rail, the power supply error is logged as the cause of the pgood failure. The default value of this property is false. |
+| check_status_vout         |         no          | boolean (true or false) | If true, determine the pgood status of the rail by checking the value of the PMBus STATUS_VOUT command. If one of the error bits is set, the rail pgood will be considered false. The default value of this property is false.                                                                                                 |
+| compare_voltage_to_limits |         no          | boolean (true or false) | If true, determine the pgood status of the rail by comparing the output voltage (READ_VOUT) to the undervoltage (VOUT_UV_FAULT_LIMIT) and overvoltage (VOUT_OV_FAULT_LIMIT) limits. If the output voltage is beyond those limits, the rail pgood will be considered false. The default value of this property is false.        |
+| gpio                      |         no          | [gpio](gpio.md)         | Determine the pgood status of the rail by reading a GPIO.                                                                                                                                                                                                                                                                      |
 
 ### Notes
 
diff --git a/phosphor-power-sequencer/src/config_file_parser.cpp b/phosphor-power-sequencer/src/config_file_parser.cpp
index db055d1..37c6b04 100644
--- a/phosphor-power-sequencer/src/config_file_parser.cpp
+++ b/phosphor-power-sequencer/src/config_file_parser.cpp
@@ -100,6 +100,15 @@
         ++propertyCount;
     }
 
+    // Optional is_power_supply_rail property
+    bool isPowerSupplyRail{false};
+    auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
+    if (isPowerSupplyRailIt != element.end())
+    {
+        isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt);
+        ++propertyCount;
+    }
+
     // Optional check_status_vout property
     bool checkStatusVout{false};
     auto checkStatusVoutIt = element.find("check_status_vout");
@@ -137,8 +146,9 @@
     // Verify no invalid properties exist
     verifyPropertyCount(element, propertyCount);
 
-    return std::make_unique<Rail>(name, presence, page, checkStatusVout,
-                                  compareVoltageToLimits, gpio);
+    return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
+                                  checkStatusVout, compareVoltageToLimits,
+                                  gpio);
 }
 
 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
diff --git a/phosphor-power-sequencer/src/rail.hpp b/phosphor-power-sequencer/src/rail.hpp
index 5445449..6ac8610 100644
--- a/phosphor-power-sequencer/src/rail.hpp
+++ b/phosphor-power-sequencer/src/rail.hpp
@@ -71,6 +71,8 @@
      *                 must be present in order for the rail to be present
      * @param page Optional PMBus PAGE number of the rail.  Required if
      *             checkStatusVout or compareVoltageToLimits is true.
+     * @param isPowerSupplyRail Specifies whether the rail is produced by a
+     *                          power supply
      * @param checkStatusVout Specifies whether to check the value of the PMBus
      *                        STATUS_VOUT command when determining the pgood
      *                        status of the rail
@@ -83,11 +85,12 @@
      */
     explicit Rail(const std::string& name,
                   const std::optional<std::string>& presence,
-                  const std::optional<uint8_t>& page, bool checkStatusVout,
-                  bool compareVoltageToLimits,
+                  const std::optional<uint8_t>& page, bool isPowerSupplyRail,
+                  bool checkStatusVout, bool compareVoltageToLimits,
                   const std::optional<GPIO>& gpio) :
         name{name},
-        presence{presence}, page{page}, checkStatusVout{checkStatusVout},
+        presence{presence}, page{page}, isPsuRail{isPowerSupplyRail},
+        checkStatusVout{checkStatusVout},
         compareVoltageToLimits{compareVoltageToLimits}, gpio{gpio}
     {
         // If checking STATUS_VOUT or output voltage, verify PAGE was specified
@@ -129,6 +132,16 @@
     }
 
     /**
+     * Returns whether the rail is produced by a power supply.
+     *
+     * @return true if rail is produced by a power supply, false otherwise
+     */
+    bool isPowerSupplyRail() const
+    {
+        return isPsuRail;
+    }
+
+    /**
      * Returns whether the value of the PMBus STATUS_VOUT command is checked
      * when determining the pgood status of the rail.
      *
@@ -180,6 +193,11 @@
     std::optional<uint8_t> page{};
 
     /**
+     * Specifies whether the rail is produced by a power supply.
+     */
+    bool isPsuRail{false};
+
+    /**
      * Specifies whether to check the value of the PMBus STATUS_VOUT command
      * when determining the pgood status of the rail.
      *
diff --git a/phosphor-power-sequencer/test/config_file_parser_tests.cpp b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
index bfc9680..738d025 100644
--- a/phosphor-power-sequencer/test/config_file_parser_tests.cpp
+++ b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
@@ -306,6 +306,7 @@
         EXPECT_EQ(rail->getName(), "VDD_CPU0");
         EXPECT_FALSE(rail->getPresence().has_value());
         EXPECT_FALSE(rail->getPage().has_value());
+        EXPECT_FALSE(rail->isPowerSupplyRail());
         EXPECT_FALSE(rail->getCheckStatusVout());
         EXPECT_FALSE(rail->getCompareVoltageToLimits());
         EXPECT_FALSE(rail->getGPIO().has_value());
@@ -315,22 +316,23 @@
     {
         const json element = R"(
             {
-                "name": "VCS_CPU1",
-                "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1",
+                "name": "12.0VB",
+                "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply1",
                 "page": 11,
+                "is_power_supply_rail": true,
                 "check_status_vout": true,
                 "compare_voltage_to_limits": true,
                 "gpio": { "line": 60, "active_low": true }
             }
         )"_json;
         std::unique_ptr<Rail> rail = parseRail(element);
-        EXPECT_EQ(rail->getName(), "VCS_CPU1");
+        EXPECT_EQ(rail->getName(), "12.0VB");
         EXPECT_TRUE(rail->getPresence().has_value());
-        EXPECT_EQ(
-            rail->getPresence().value(),
-            "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1");
+        EXPECT_EQ(rail->getPresence().value(),
+                  "/xyz/openbmc_project/inventory/system/chassis/powersupply1");
         EXPECT_TRUE(rail->getPage().has_value());
         EXPECT_EQ(rail->getPage().value(), 11);
+        EXPECT_TRUE(rail->isPowerSupplyRail());
         EXPECT_TRUE(rail->getCheckStatusVout());
         EXPECT_TRUE(rail->getCompareVoltageToLimits());
         EXPECT_TRUE(rail->getGPIO().has_value());
@@ -417,6 +419,23 @@
         EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
     }
 
+    // Test where fails: is_power_supply_rail value is invalid
+    try
+    {
+        const json element = R"(
+            {
+                "name": "12.0VA",
+                "is_power_supply_rail": "true"
+            }
+        )"_json;
+        parseRail(element);
+        ADD_FAILURE() << "Should not have reached this line.";
+    }
+    catch (const std::invalid_argument& e)
+    {
+        EXPECT_STREQ(e.what(), "Element is not a boolean");
+    }
+
     // Test where fails: check_status_vout value is invalid
     try
     {
diff --git a/phosphor-power-sequencer/test/rail_tests.cpp b/phosphor-power-sequencer/test/rail_tests.cpp
index 3caf573..bd1b51a 100644
--- a/phosphor-power-sequencer/test/rail_tests.cpp
+++ b/phosphor-power-sequencer/test/rail_tests.cpp
@@ -45,18 +45,25 @@
 {
     // Test where succeeds: No optional parameters have values
     {
-        std::string name{"VDD1"};
+        std::string name{"12.0V"};
         std::optional<std::string> presence{};
         std::optional<uint8_t> page{};
+        bool isPowerSupplyRail{true};
         bool checkStatusVout{false};
         bool compareVoltageToLimits{false};
         std::optional<GPIO> gpio{};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
 
-        EXPECT_EQ(rail.getName(), "VDD1");
+        EXPECT_EQ(rail.getName(), "12.0V");
         EXPECT_FALSE(rail.getPresence().has_value());
         EXPECT_FALSE(rail.getPage().has_value());
+        EXPECT_TRUE(rail.isPowerSupplyRail());
         EXPECT_FALSE(rail.getCheckStatusVout());
         EXPECT_FALSE(rail.getCompareVoltageToLimits());
         EXPECT_FALSE(rail.getGPIO().has_value());
@@ -68,10 +75,16 @@
         std::optional<std::string> presence{
             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1"};
         std::optional<uint8_t> page{11};
+        bool isPowerSupplyRail{false};
         bool checkStatusVout{true};
         bool compareVoltageToLimits{true};
         std::optional<GPIO> gpio{GPIO(60, true)};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
 
         EXPECT_EQ(rail.getName(), "VCS_CPU1");
@@ -81,6 +94,7 @@
             "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1");
         EXPECT_TRUE(rail.getPage().has_value());
         EXPECT_EQ(rail.getPage().value(), 11);
+        EXPECT_FALSE(rail.isPowerSupplyRail());
         EXPECT_TRUE(rail.getCheckStatusVout());
         EXPECT_TRUE(rail.getCompareVoltageToLimits());
         EXPECT_TRUE(rail.getGPIO().has_value());
@@ -93,11 +107,12 @@
         std::string name{"VDD1"};
         std::optional<std::string> presence{};
         std::optional<uint8_t> page{};
+        bool isPowerSupplyRail{false};
         bool checkStatusVout{true};
         bool compareVoltageToLimits{false};
         std::optional<GPIO> gpio{};
-        EXPECT_THROW((Rail{name, presence, page, checkStatusVout,
-                           compareVoltageToLimits, gpio}),
+        EXPECT_THROW((Rail{name, presence, page, isPowerSupplyRail,
+                           checkStatusVout, compareVoltageToLimits, gpio}),
                      std::invalid_argument);
     }
 
@@ -106,11 +121,12 @@
         std::string name{"VDD1"};
         std::optional<std::string> presence{};
         std::optional<uint8_t> page{};
+        bool isPowerSupplyRail{false};
         bool checkStatusVout{false};
         bool compareVoltageToLimits{true};
         std::optional<GPIO> gpio{};
-        EXPECT_THROW((Rail{name, presence, page, checkStatusVout,
-                           compareVoltageToLimits, gpio}),
+        EXPECT_THROW((Rail{name, presence, page, isPowerSupplyRail,
+                           checkStatusVout, compareVoltageToLimits, gpio}),
                      std::invalid_argument);
     }
 }
@@ -120,10 +136,16 @@
     std::string name{"VDD2"};
     std::optional<std::string> presence{};
     std::optional<uint8_t> page{};
+    bool isPowerSupplyRail{false};
     bool checkStatusVout{false};
     bool compareVoltageToLimits{false};
     std::optional<GPIO> gpio{};
-    Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+    Rail rail{name,
+              presence,
+              page,
+              isPowerSupplyRail,
+              checkStatusVout,
+              compareVoltageToLimits,
               gpio};
 
     EXPECT_EQ(rail.getName(), "VDD2");
@@ -133,6 +155,7 @@
 {
     std::string name{"VDDR2"};
     std::optional<uint8_t> page{};
+    bool isPowerSupplyRail{false};
     bool checkStatusVout{false};
     bool compareVoltageToLimits{false};
     std::optional<GPIO> gpio{};
@@ -140,7 +163,12 @@
     // Test where presence has no value
     {
         std::optional<std::string> presence{};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
         EXPECT_FALSE(rail.getPresence().has_value());
     }
@@ -149,7 +177,12 @@
     {
         std::optional<std::string> presence{
             "/xyz/openbmc_project/inventory/system/chassis/motherboard/dimm2"};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
         EXPECT_TRUE(rail.getPresence().has_value());
         EXPECT_EQ(
@@ -162,6 +195,7 @@
 {
     std::string name{"VDD2"};
     std::optional<std::string> presence{};
+    bool isPowerSupplyRail{false};
     bool checkStatusVout{false};
     bool compareVoltageToLimits{false};
     std::optional<GPIO> gpio{};
@@ -169,7 +203,12 @@
     // Test where page has no value
     {
         std::optional<uint8_t> page{};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
         EXPECT_FALSE(rail.getPage().has_value());
     }
@@ -177,22 +216,53 @@
     // Test where page has a value
     {
         std::optional<uint8_t> page{7};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
         EXPECT_TRUE(rail.getPage().has_value());
         EXPECT_EQ(rail.getPage().value(), 7);
     }
 }
 
+TEST(RailTests, IsPowerSupplyRail)
+{
+    std::string name{"12.0V"};
+    std::optional<std::string> presence{};
+    std::optional<uint8_t> page{};
+    bool isPowerSupplyRail{true};
+    bool checkStatusVout{false};
+    bool compareVoltageToLimits{false};
+    std::optional<GPIO> gpio{};
+    Rail rail{name,
+              presence,
+              page,
+              isPowerSupplyRail,
+              checkStatusVout,
+              compareVoltageToLimits,
+              gpio};
+
+    EXPECT_TRUE(rail.isPowerSupplyRail());
+}
+
 TEST(RailTests, GetCheckStatusVout)
 {
     std::string name{"VDD2"};
     std::optional<std::string> presence{};
     std::optional<uint8_t> page{};
+    bool isPowerSupplyRail{false};
     bool checkStatusVout{false};
     bool compareVoltageToLimits{false};
     std::optional<GPIO> gpio{};
-    Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+    Rail rail{name,
+              presence,
+              page,
+              isPowerSupplyRail,
+              checkStatusVout,
+              compareVoltageToLimits,
               gpio};
 
     EXPECT_FALSE(rail.getCheckStatusVout());
@@ -203,10 +273,16 @@
     std::string name{"VDD2"};
     std::optional<std::string> presence{};
     std::optional<uint8_t> page{13};
+    bool isPowerSupplyRail{false};
     bool checkStatusVout{false};
     bool compareVoltageToLimits{true};
     std::optional<GPIO> gpio{};
-    Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+    Rail rail{name,
+              presence,
+              page,
+              isPowerSupplyRail,
+              checkStatusVout,
+              compareVoltageToLimits,
               gpio};
 
     EXPECT_TRUE(rail.getCompareVoltageToLimits());
@@ -217,13 +293,19 @@
     std::string name{"VDD2"};
     std::optional<std::string> presence{};
     std::optional<uint8_t> page{};
+    bool isPowerSupplyRail{false};
     bool checkStatusVout{false};
     bool compareVoltageToLimits{false};
 
     // Test where gpio has no value
     {
         std::optional<GPIO> gpio{};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
         EXPECT_FALSE(rail.getGPIO().has_value());
     }
@@ -231,7 +313,12 @@
     // Test where gpio has a value
     {
         std::optional<GPIO> gpio{GPIO(12, false)};
-        Rail rail{name, presence, page, checkStatusVout, compareVoltageToLimits,
+        Rail rail{name,
+                  presence,
+                  page,
+                  isPowerSupplyRail,
+                  checkStatusVout,
+                  compareVoltageToLimits,
                   gpio};
         EXPECT_TRUE(rail.getGPIO().has_value());
         EXPECT_EQ(rail.getGPIO().value().line, 12);