Specify PEL callout priority and sort callouts.

Allow the priority to be passed in at creation time by calling the
CALLOUT_PRIORITY keyword. When creating a PEL, callouts will always
be sorted by priority instead of creation time, the order is as follows:
H,M,A,B,C,L.

Signed-off-by: Miguel Gomez <mgomez@mx1.ibm.com>
Change-Id: I84345aaf3fad7b2e4958b698ab761966f499986b
diff --git a/extensions/openpower-pels/callouts.cpp b/extensions/openpower-pels/callouts.cpp
index 2440621..885dda5 100644
--- a/extensions/openpower-pels/callouts.cpp
+++ b/extensions/openpower-pels/callouts.cpp
@@ -61,7 +61,19 @@
         using namespace phosphor::logging;
         log<level::INFO>("Dropping PEL callout because at max");
     }
+
+    // Mapping including the  3 Medium levels as A,B and C
+    const std::map<std::uint8_t, int> priorities = {
+        {'H', 10}, {'M', 9}, {'A', 8}, {'B', 7}, {'C', 6}, {'L', 5}};
+
+    auto sortPriority = [&priorities](const std::unique_ptr<Callout>& p1,
+                                      const std::unique_ptr<Callout>& p2) {
+        return priorities.at(p1->priority()) > priorities.at(p2->priority());
+    };
+
+    std::sort(_callouts.begin(), _callouts.end(), sortPriority);
 }
+
 } // namespace src
 } // namespace pels
-} // namespace openpower
+} // namespace openpower
\ No newline at end of file
diff --git a/extensions/openpower-pels/src.cpp b/extensions/openpower-pels/src.cpp
index 3fd62e8..643adec 100644
--- a/extensions/openpower-pels/src.cpp
+++ b/extensions/openpower-pels/src.cpp
@@ -748,7 +748,19 @@
         getRegistryCallouts(regEntry, additionalData, dataIface);
 
     auto item = additionalData.getValue("CALLOUT_INVENTORY_PATH");
+    auto priority = additionalData.getValue("CALLOUT_PRIORITY");
 
+    std::optional<CalloutPriority> calloutPriority;
+
+    // Only  H, M or L priority values.
+    if (priority && !(*priority).empty())
+    {
+        uint8_t p = (*priority)[0];
+        if (p == 'H' || p == 'M' || p == 'L')
+        {
+            calloutPriority = static_cast<CalloutPriority>(p);
+        }
+    }
     // If the first registry callout says to use the passed in inventory
     // path to get the location code for a symbolic FRU callout with a
     // trusted location code, then do not add the inventory path as a
@@ -759,7 +771,7 @@
 
     if (item && !useInvForSymbolicFRULocCode)
     {
-        addInventoryCallout(*item, std::nullopt, std::nullopt, dataIface);
+        addInventoryCallout(*item, calloutPriority, std::nullopt, dataIface);
     }
 
     addDevicePathCallouts(additionalData, dataIface);
diff --git a/test/openpower-pels/src_callouts_test.cpp b/test/openpower-pels/src_callouts_test.cpp
index b1fe9c4..69c26b8 100644
--- a/test/openpower-pels/src_callouts_test.cpp
+++ b/test/openpower-pels/src_callouts_test.cpp
@@ -120,3 +120,83 @@
 
     EXPECT_EQ(callouts.callouts().size(), maxNumberOfCallouts);
 }
+
+TEST(CalloutsTest, TestSortCallouts)
+{
+    Callouts callouts;
+
+    // Add  callouts with different priorities to test sorting in descending
+    // order
+
+    auto c0 = std::make_unique<Callout>(CalloutPriority::high, "U1-P1",
+                                        "1234567", "ABC", "123456789ABC");
+
+    callouts.addCallout(std::move(c0));
+
+    auto c1 = std::make_unique<Callout>(CalloutPriority::medium, "U1-P2",
+                                        "1234567", "ABCD", "123456789ABC");
+
+    callouts.addCallout(std::move(c1));
+
+    auto c2 = std::make_unique<Callout>(CalloutPriority::low, "U1-P3",
+                                        "1234567", "ABCDE", "123456789ABC");
+
+    callouts.addCallout(std::move(c2));
+
+    auto c3 = std::make_unique<Callout>(CalloutPriority::high, "U1-P4",
+                                        "1234567", "ABCDE1", "123456789ABC");
+
+    callouts.addCallout(std::move(c3));
+
+    auto c4 = std::make_unique<Callout>(CalloutPriority::high, "U1-P5",
+                                        "1234567", "ABCDE2", "123456789ABC");
+
+    callouts.addCallout(std::move(c4));
+
+    auto c5 = std::make_unique<Callout>(CalloutPriority::low, "U1-P6",
+                                        "1234567", "ABCDE2", "123456789ABC");
+
+    callouts.addCallout(std::move(c5));
+
+    auto c6 = std::make_unique<Callout>(CalloutPriority::medium, "U1-P7",
+                                        "1234567", "ABCD2", "123456789ABC");
+
+    callouts.addCallout(std::move(c6));
+
+    auto c7 = std::make_unique<Callout>(CalloutPriority::mediumGroupA, "U1-P8",
+                                        "1234567", "ABCDE3", "123456789ABC");
+
+    callouts.addCallout(std::move(c7));
+
+    auto c8 = std::make_unique<Callout>(CalloutPriority::mediumGroupC, "U1-P9",
+                                        "1234567", "ABCDE4", "123456789ABC");
+
+    callouts.addCallout(std::move(c8));
+
+    auto c9 = std::make_unique<Callout>(CalloutPriority::low, "U1-P10",
+                                        "1234567", "ABCDE3", "123456789ABC");
+
+    callouts.addCallout(std::move(c9));
+
+    const auto& calloutObjects = callouts.callouts();
+    EXPECT_EQ(calloutObjects[0]->locationCode(), "U1-P1");
+    EXPECT_EQ(calloutObjects[0]->priority(), 'H');
+    EXPECT_EQ(calloutObjects[1]->locationCode(), "U1-P4");
+    EXPECT_EQ(calloutObjects[1]->priority(), 'H');
+    EXPECT_EQ(calloutObjects[2]->locationCode(), "U1-P5");
+    EXPECT_EQ(calloutObjects[2]->priority(), 'H');
+    EXPECT_EQ(calloutObjects[3]->locationCode(), "U1-P2");
+    EXPECT_EQ(calloutObjects[3]->priority(), 'M');
+    EXPECT_EQ(calloutObjects[4]->locationCode(), "U1-P7");
+    EXPECT_EQ(calloutObjects[4]->priority(), 'M');
+    EXPECT_EQ(calloutObjects[5]->locationCode(), "U1-P8");
+    EXPECT_EQ(calloutObjects[5]->priority(), 'A');
+    EXPECT_EQ(calloutObjects[6]->locationCode(), "U1-P9");
+    EXPECT_EQ(calloutObjects[6]->priority(), 'C');
+    EXPECT_EQ(calloutObjects[7]->locationCode(), "U1-P3");
+    EXPECT_EQ(calloutObjects[7]->priority(), 'L');
+    EXPECT_EQ(calloutObjects[8]->locationCode(), "U1-P6");
+    EXPECT_EQ(calloutObjects[8]->priority(), 'L');
+    EXPECT_EQ(calloutObjects[9]->locationCode(), "U1-P10");
+    EXPECT_EQ(calloutObjects[9]->priority(), 'L');
+}
diff --git a/test/openpower-pels/src_test.cpp b/test/openpower-pels/src_test.cpp
index 1f0db2a..671bd54 100644
--- a/test/openpower-pels/src_test.cpp
+++ b/test/openpower-pels/src_test.cpp
@@ -1265,3 +1265,39 @@
                  "Failed extracting callout data from JSON: Invalid "
                  "priority 'X' found in JSON callout");
 }
+
+// Test that an inventory path callout can have
+// a different priority than H.
+TEST_F(SRCTest, InventoryCalloutTestPriority)
+{
+    message::Entry entry;
+    entry.src.type = 0xBD;
+    entry.src.reasonCode = 0xABCD;
+    entry.subsystem = 0x42;
+    entry.src.powerFault = false;
+
+    std::vector<std::string> adData{"CALLOUT_INVENTORY_PATH=motherboard",
+                                    "CALLOUT_PRIORITY=M"};
+    AdditionalData ad{adData};
+    NiceMock<MockDataInterface> dataIface;
+
+    EXPECT_CALL(dataIface, getLocationCode("motherboard"))
+        .WillOnce(Return("UTMS-P1"));
+
+    EXPECT_CALL(dataIface, getHWCalloutFields("motherboard", _, _, _))
+        .Times(1)
+        .WillOnce(DoAll(SetArgReferee<1>("1234567"), SetArgReferee<2>("CCCC"),
+                        SetArgReferee<3>("123456789ABC")));
+
+    SRC src{entry, ad, dataIface};
+    EXPECT_TRUE(src.valid());
+
+    ASSERT_TRUE(src.callouts());
+
+    EXPECT_EQ(src.callouts()->callouts().size(), 1);
+
+    auto& callout = src.callouts()->callouts().front();
+
+    EXPECT_EQ(callout->locationCode(), "UTMS-P1");
+    EXPECT_EQ(callout->priority(), 'M');
+}
\ No newline at end of file