PEL: Adding the support for Systems Key in message registry

The current implementation has the support for adding system specific
callouts with the help of 'System' key in message_registry.json.

Adding one more key named 'Systems' where it can have array of system
names in the form of strings. The 'Systems' key can be used to define
the shared callouts for a group of systems.

A unique callout to a specific system can be added using the existing
System key. If both 'System' and 'Systems' are not present or not
matching with the system name, then the default calloutList will be
taken if configured.

Tested:

The test setup has the following names for the compatible interface.

```
busctl -j get-property xyz.openbmc_project.EntityManager
/xyz/openbmc_project/inventory/system/chassis/Rainier_2U_Chassis
xyz.openbmc_project.Inventory.Decorator.Compatible Names
{
	"type" : "as",
	"data" : [
		"com.ibm.Hardware.Chassis.Model.Rainier2U",
		"com.ibm.Hardware.Chassis.Model.Rainier"
	]
}
```
The callout section in the message_registry.json for TestError1 is
defined as below.
```
"Callouts": [
        {
            "Systems": ["com.ibm.Hardware.Chassis.Model.Rainier",
                     "com.ibm.Hardware.Chassis.Model.Blue_Ridge"],
            "CalloutList": [
                {"Priority": "medium", "SymbolicFRU": "service_docs"}
            ]
        },
        {
            "System": "com.ibm.Hardware.Chassis.Model.Rainier",
            "CalloutList": [
                {"Priority": "high", "Procedure": "BMC0001"}
            ]
        },
        {
             "CalloutList": [
                { "LocCode": "P0", "Priority": "high" },
                { "LocCode": "P0-C15","Priority": "low" }
            ]
        }
    ]
```

Leads to PEL callouts section as below:
```
"Callout Section": {
        "Callout Count":        "2",
        "Callouts": [{
            "FRU Type":         "Maintenance Procedure Required",
            "Priority":         "Mandatory, replace all with this type
                                 as a unit",
            "Procedure":        "BMC0001"
        }, {
            "FRU Type":         "Symbolic FRU",
            "Priority":         "Medium Priority",
            "Part Number":      "SVCDOCS"
        }]
    }
```

Signed-off-by: Arya K Padman <aryakpadman@gmail.com>
Change-Id: Iea65816dcb822bb07043897488a6251929548dc7
diff --git a/extensions/openpower-pels/registry.cpp b/extensions/openpower-pels/registry.cpp
index 4dc1f0b..05b286a 100644
--- a/extensions/openpower-pels/registry.cpp
+++ b/extensions/openpower-pels/registry.cpp
@@ -21,6 +21,7 @@
 
 #include <phosphor-logging/lg2.hpp>
 
+#include <algorithm>
 #include <fstream>
 
 namespace openpower
@@ -330,10 +331,19 @@
  * @brief Finds the callouts to use when there is no AdditionalData,
  *        but the system type may be used as a key.
  *
- * One entry in the array looks like the following.  The System key
- * is optional and if not present it means that entry applies to
- * every configuration that doesn't have another entry with a matching
- * System key.
+ * A sample calloutList array looks like the following.  The System and Systems
+ * key are optional.
+ *
+ * System key - Value of the key will be the system name as a string. The
+ * callouts for a specific system can define under this key.
+ *
+ * Systems key - Value of the key will be an array of system names in the form
+ * of string. The callouts common to the systems mentioned in the array can
+ * define under this key.
+ *
+ * If both System and Systems not present it means that entry applies to every
+ * configuration that doesn't have another entry with a matching System and
+ * Systems key.
  *
  *    {
  *        "System": "system1",
@@ -348,42 +358,85 @@
  *                "LocCode": "P1"
  *            }
  *        ]
+ *    },
+ *    {
+ *        "Systems": ["system1", 'system2"],
+ *        "CalloutList":
+ *        [
+ *            {
+ *                "Priority": "high",
+ *                "LocCode": "P0-C1"
+ *            },
+ *            {
+ *                "Priority": "low",
+ *                "LocCode": "P0"
+ *            }
+ *        ]
  *    }
+ *
+ * @param[in] json - The callout JSON
+ * @param[in] systemNames - List of compatible system type names
+ * @param[out] calloutLists - The JSON array which will hold the calloutlist to
+ * use specific to the system.
+ *
+ * @return - Throws runtime exception if json is not an array or if calloutLists
+ *           is empty.
  */
-const nlohmann::json&
-    findCalloutList(const nlohmann::json& json,
-                    const std::vector<std::string>& systemNames)
+static void findCalloutList(const nlohmann::json& json,
+                            const std::vector<std::string>& systemNames,
+                            nlohmann::json& calloutLists)
 {
-    const nlohmann::json* callouts = nullptr;
-
     if (!json.is_array())
     {
         throw std::runtime_error{"findCalloutList was not passed a JSON array"};
     }
 
-    // The entry with the system type match will take precedence over the entry
-    // without any "System" field in it at all, which will match all other
-    // cases.
-    for (const auto& calloutList : json)
+    // Flag to indicate whether system specific callouts found or not
+    bool foundCallouts = false;
+
+    for (const auto& callouts : json)
     {
-        if (calloutList.contains("System"))
+        if (callouts.contains("System"))
         {
-            if (std::find(systemNames.begin(), systemNames.end(),
-                          calloutList["System"].get<std::string>()) !=
+            if (std::ranges::find(systemNames,
+                                  callouts["System"].get<std::string>()) !=
                 systemNames.end())
             {
-                callouts = &calloutList["CalloutList"];
-                break;
+                calloutLists.insert(calloutLists.end(),
+                                    callouts["CalloutList"].begin(),
+                                    callouts["CalloutList"].end());
+                foundCallouts = true;
             }
+            continue;
         }
-        else
+
+        if (callouts.contains("Systems"))
         {
-            // Any entry with no System key
-            callouts = &calloutList["CalloutList"];
+            std::vector<std::string> systems =
+                callouts["Systems"].get<std::vector<std::string>>();
+            auto inSystemNames = [systemNames](const auto& system) {
+                return (std::ranges::find(systemNames, system) !=
+                        systemNames.end());
+            };
+            if (std::ranges::any_of(systems, inSystemNames))
+            {
+                calloutLists.insert(calloutLists.end(),
+                                    callouts["CalloutList"].begin(),
+                                    callouts["CalloutList"].end());
+                foundCallouts = true;
+            }
+            continue;
+        }
+
+        // Any entry if neither System/Systems key matches with system name
+        if (!foundCallouts)
+        {
+            calloutLists.insert(calloutLists.end(),
+                                callouts["CalloutList"].begin(),
+                                callouts["CalloutList"].end());
         }
     }
-
-    if (!callouts)
+    if (calloutLists.empty())
     {
         std::string types;
         std::for_each(systemNames.begin(), systemNames.end(),
@@ -396,8 +449,6 @@
         throw std::runtime_error{
             "Could not find a CalloutList JSON for this error and system name"};
     }
-
-    return *callouts;
 }
 
 /**
@@ -464,18 +515,34 @@
  *       everything.
  *
  * The JSON looks like:
- *    [
- *        {
- *            "System": "systemA",
- *            "CalloutList":
- *            [
- *                {
- *                    "Priority": "high",
- *                    "LocCode": "P1-C5"
- *                }
- *            ]
- *         }
- *    ]
+ *    {
+ *        "System": "system1",
+ *        "CalloutList":
+ *        [
+ *            {
+ *                "Priority": "high",
+ *                "LocCode": "P1-C1"
+ *            },
+ *            {
+ *                "Priority": "low",
+ *                "LocCode": "P1"
+ *            }
+ *        ]
+ *    },
+ *    {
+ *        "Systems": ["system1", 'system2"],
+ *        "CalloutList":
+ *        [
+ *            {
+ *                "Priority": "high",
+ *                "LocCode": "P0-C1"
+ *            },
+ *            {
+ *                "Priority": "low",
+ *                "LocCode": "P0"
+ *            }
+ *        ]
+ *    }
  *
  * @param[in] json - The callout JSON
  * @param[in] systemNames - List of compatible system type names
@@ -488,11 +555,13 @@
 {
     std::vector<RegistryCallout> calloutEntries;
 
+    nlohmann::json calloutLists = nlohmann::json::array();
+
     // Find the CalloutList to use based on the system type
-    const auto& calloutList = findCalloutList(json, systemNames);
+    findCalloutList(json, systemNames, calloutLists);
 
     // We finally found the callouts, make the objects.
-    for (const auto& callout : calloutList)
+    for (const auto& callout : calloutLists)
     {
         calloutEntries.push_back(std::move(makeRegistryCallout(callout)));
     }
diff --git a/extensions/openpower-pels/registry/README.md b/extensions/openpower-pels/registry/README.md
index 1955ad8..27c0044 100644
--- a/extensions/openpower-pels/registry/README.md
+++ b/extensions/openpower-pels/registry/README.md
@@ -329,6 +329,16 @@
 
 There is room for up to 10 callouts in a PEL.
 
+The callouts based on system type can be added in two ways, by using either a
+key called `System` or by `Systems`.
+
+The `System` key will accept the system name as a string and the user can add
+the callouts specific to that system under the `System`.
+
+Suppose if multiple systems have same callouts, the `Systems` key can be used.
+The `Systems` can accept the system names as an array of strings and the list of
+callouts common to those systems can be listed under the key.
+
 Available maintenance procedures are listed [here][1] and in the source code
 [here][2].
 
@@ -373,10 +383,61 @@
 
 ```
 
-The above example shows that on system 'system1', the FRU at location P1-C1 will
+The above example shows that on system `system1`, the FRU at location P1-C1 will
 be called out with a priority of high, and the FRU at P1 with a priority of low.
 On every other system, the maintenance procedure BMC0002 is called out.
 
+#### Callouts example based on the Systems type
+
+```json
+"Callouts":
+[
+    {
+        "Systems": ["system1", "system2"],
+        "CalloutList":
+        [
+            {
+                "Priority": "high",
+                "LocCode": "P1-C1"
+            },
+            {
+                "Priority": "low",
+                "LocCode": "P1"
+            }
+        ]
+    },
+    {
+        "System": "system1",
+        "CalloutList":
+        [
+            {
+                "Priority": "low",
+                "SymbolicFRU": "service_docs"
+            },
+            {
+                "Priority": "low",
+                "SymbolicFRUTrusted": "air_mover",
+                "UseInventoryLocCode": true
+            }
+        ]
+    },
+    {
+        "CalloutList":
+        [
+            {
+                "Priority": "medium",
+                "Procedure": "BMC0001"
+            }
+        ]
+    }
+]
+```
+
+The above example shows that on `system1`, the FRU at location P1-C1, P1,
+service_docs and air_mover will be called out. For `system2`, the FRU at
+location P1-C1, P1 will be called out. On every other system, the maintenance
+procedure BMC0001 is called out.
+
 #### Callouts example based on an AdditionalData field
 
 ```json
diff --git a/extensions/openpower-pels/registry/schema/schema.json b/extensions/openpower-pels/registry/schema/schema.json
index 429d3bf..d4fea9d 100644
--- a/extensions/openpower-pels/registry/schema/schema.json
+++ b/extensions/openpower-pels/registry/schema/schema.json
@@ -642,6 +642,15 @@
             "minLength": 1
         },
 
+        "systems": {
+            "description": "The Systems key can be defined and can be used to keep the name of the systems as an array of string if the systems possess same callout list.",
+            "type": "array",
+            "items": {
+                "type": "string"
+            },
+            "minItems": 1
+        },
+
         "callouts": {
             "description": "This contains callouts that can vary based on system type.  Each entry contains an optional System property and a required CalloutList property.  If the System property is left out it indicates that the CalloutList callouts are valid for every system type, unless there is another Callouts entry that has a matching System property, in which case that entry is valid.",
             "type": "array",
@@ -650,11 +659,22 @@
 
                 "properties": {
                     "System": { "$ref": "#/definitions/system" },
+                    "Systems": { "$ref": "#/definitions/systems" },
                     "CalloutList": { "$ref": "#/definitions/calloutList" }
                 },
-                "required": ["CalloutList"],
                 "additionalProperties": false
             },
+            "anyOf": [
+                {
+                    "required": ["System", "CalloutList"]
+                },
+                {
+                    "required": ["Systems", "CalloutList"]
+                },
+                {
+                    "required": ["CalloutList"]
+                }
+            ],
             "minItems": 1,
             "maxItems": 10,
 
@@ -665,6 +685,10 @@
                         "CalloutList": [{ "Priority": "high", "LocCode": "P1" }]
                     },
                     {
+                        "Systems": ["system1", "system2"],
+                        "CalloutList": [{ "Priority": "low", "LocCode": "P2" }]
+                    },
+                    {
                         "CalloutList": [
                             { "Priority": "high", "Procedure": "NEXTLVL" }
                         ]
diff --git a/test/openpower-pels/registry_test.cpp b/test/openpower-pels/registry_test.cpp
index 1fb78b0..1646a49 100644
--- a/test/openpower-pels/registry_test.cpp
+++ b/test/openpower-pels/registry_test.cpp
@@ -423,7 +423,7 @@
 // Test when callouts are in the JSON.
 TEST_F(RegistryTest, TestGetCallouts)
 {
-    std::vector<std::string> names;
+    std::vector<std::string> systemNames;
 
     {
         // Callouts without AD, that depend on system type,
@@ -471,9 +471,9 @@
         ])"_json;
 
         AdditionalData ad;
-        names.push_back("system1");
+        systemNames.push_back("system1");
 
-        auto callouts = Registry::getCallouts(json, names, ad);
+        auto callouts = Registry::getCallouts(json, systemNames, ad);
         EXPECT_EQ(callouts.size(), 4);
         EXPECT_EQ(callouts[0].priority, "high");
         EXPECT_EQ(callouts[0].locCode, "P1-C1");
@@ -498,8 +498,8 @@
         EXPECT_EQ(callouts[3].useInventoryLocCode, true);
 
         // system2 isn't in the JSON, so it will pick the default one
-        names[0] = "system2";
-        callouts = Registry::getCallouts(json, names, ad);
+        systemNames[0] = "system2";
+        callouts = Registry::getCallouts(json, systemNames, ad);
         EXPECT_EQ(callouts.size(), 2);
         EXPECT_EQ(callouts[0].priority, "medium");
         EXPECT_EQ(callouts[0].locCode, "");
@@ -517,8 +517,8 @@
     {
         auto json = R"([])"_json;
         AdditionalData ad;
-        names[0] = "system1";
-        EXPECT_THROW(Registry::getCallouts(json, names, ad),
+        systemNames[0] = "system1";
+        EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
                      std::runtime_error);
     }
 
@@ -557,9 +557,9 @@
         ])"_json;
 
         AdditionalData ad;
-        names[0] = "system1";
+        systemNames[0] = "system1";
 
-        auto callouts = Registry::getCallouts(json, names, ad);
+        auto callouts = Registry::getCallouts(json, systemNames, ad);
         EXPECT_EQ(callouts.size(), 2);
         EXPECT_EQ(callouts[0].priority, "high");
         EXPECT_EQ(callouts[0].locCode, "P1-C1");
@@ -572,8 +572,8 @@
         EXPECT_EQ(callouts[1].symbolicFRU, "1234567");
         EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
 
-        names[0] = "system2";
-        callouts = Registry::getCallouts(json, names, ad);
+        systemNames[0] = "system2";
+        callouts = Registry::getCallouts(json, systemNames, ad);
         EXPECT_EQ(callouts.size(), 1);
         EXPECT_EQ(callouts[0].priority, "medium");
         EXPECT_EQ(callouts[0].locCode, "P7");
@@ -583,8 +583,8 @@
 
         // There is no entry for system3 or a default system,
         // so this should fail.
-        names[0] = "system3";
-        EXPECT_THROW(Registry::getCallouts(json, names, ad),
+        systemNames[0] = "system3";
+        EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
                      std::runtime_error);
     }
 
@@ -655,9 +655,9 @@
             // Find callouts for PROC_NUM 0 on system3
             std::vector<std::string> adData{"PROC_NUM=0"};
             AdditionalData ad{adData};
-            names[0] = "system3";
+            systemNames[0] = "system3";
 
-            auto callouts = Registry::getCallouts(json, names, ad);
+            auto callouts = Registry::getCallouts(json, systemNames, ad);
             EXPECT_EQ(callouts.size(), 3);
             EXPECT_EQ(callouts[0].priority, "high");
             EXPECT_EQ(callouts[0].locCode, "P1-C5");
@@ -676,9 +676,9 @@
             EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
 
             // Find callouts for PROC_NUM 0 that uses the default system entry.
-            names[0] = "system99";
+            systemNames[0] = "system99";
 
-            callouts = Registry::getCallouts(json, names, ad);
+            callouts = Registry::getCallouts(json, systemNames, ad);
             EXPECT_EQ(callouts.size(), 1);
             EXPECT_EQ(callouts[0].priority, "low");
             EXPECT_EQ(callouts[0].locCode, "P55");
@@ -690,9 +690,9 @@
             // Find callouts for PROC_NUM 1 that uses a default system entry.
             std::vector<std::string> adData{"PROC_NUM=1"};
             AdditionalData ad{adData};
-            names[0] = "system1";
+            systemNames[0] = "system1";
 
-            auto callouts = Registry::getCallouts(json, names, ad);
+            auto callouts = Registry::getCallouts(json, systemNames, ad);
             EXPECT_EQ(callouts.size(), 1);
             EXPECT_EQ(callouts[0].priority, "high");
             EXPECT_EQ(callouts[0].locCode, "P1-C6");
@@ -705,7 +705,7 @@
             std::vector<std::string> adData{"PROC_NUM=2"};
             AdditionalData ad{adData};
 
-            auto callouts = Registry::getCallouts(json, names, ad);
+            auto callouts = Registry::getCallouts(json, systemNames, ad);
             EXPECT_TRUE(callouts.empty());
         }
     }
@@ -750,15 +750,389 @@
         // so it should choose the P1-C1 callout.
         std::vector<std::string> adData{"PROC_NUM=8"};
         AdditionalData ad{adData};
-        names.clear();
+        systemNames.clear();
 
-        auto callouts = Registry::getCallouts(json, names, ad);
+        auto callouts = Registry::getCallouts(json, systemNames, ad);
         EXPECT_EQ(callouts.size(), 1);
         EXPECT_EQ(callouts[0].priority, "medium");
         EXPECT_EQ(callouts[0].locCode, "P1-C1");
     }
 }
 
+TEST_F(RegistryTest, TestGetCalloutsWithSystems)
+{
+    std::vector<std::string> systemNames;
+
+    auto json = R"(
+        [
+        {
+            "Systems": ["system1", "system2"],
+            "CalloutList":
+            [
+                {
+                    "Priority": "high",
+                    "LocCode": "P1-C1"
+                },
+                {
+                    "Priority": "low",
+                    "LocCode": "P1"
+                },
+                {
+                    "Priority": "low",
+                    "SymbolicFRU": "service_docs"
+                },
+                {
+                    "Priority": "low",
+                    "SymbolicFRUTrusted": "air_mover",
+                    "UseInventoryLocCode": true
+                }
+            ]
+        },
+        {
+            "CalloutList":
+            [
+                {
+                    "Priority": "medium",
+                    "Procedure": "BMC0001"
+                },
+                {
+                    "Priority": "low",
+                    "LocCode": "P3-C8",
+                    "SymbolicFRUTrusted": "service_docs"
+                }
+            ]
+
+        }
+        ])"_json;
+
+    AdditionalData ad;
+    systemNames.push_back("system1");
+
+    auto callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 4);
+    EXPECT_EQ(callouts[0].priority, "high");
+    EXPECT_EQ(callouts[0].locCode, "P1-C1");
+    EXPECT_EQ(callouts[0].procedure, "");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P1");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[2].priority, "low");
+    EXPECT_EQ(callouts[2].locCode, "");
+    EXPECT_EQ(callouts[2].procedure, "");
+    EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
+    EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[3].priority, "low");
+    EXPECT_EQ(callouts[3].locCode, "");
+    EXPECT_EQ(callouts[3].procedure, "");
+    EXPECT_EQ(callouts[3].symbolicFRU, "");
+    EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
+    EXPECT_EQ(callouts[3].useInventoryLocCode, true);
+
+    // System3 isn't in the JSON, so it will pick the default one
+    systemNames[0] = "system3";
+
+    callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 2);
+    EXPECT_EQ(callouts[0].priority, "medium");
+    EXPECT_EQ(callouts[0].locCode, "");
+    EXPECT_EQ(callouts[0].procedure, "BMC0001");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P3-C8");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
+    EXPECT_EQ(callouts[1].useInventoryLocCode, false);
+}
+
+TEST_F(RegistryTest, TestGetCalloutsWithSystemAndSystems)
+{
+    std::vector<std::string> systemNames;
+
+    auto json = R"(
+        [
+        {
+            "Systems": ["system1", "system2"],
+            "CalloutList":
+            [
+                {
+                    "Priority": "high",
+                    "LocCode": "P1-C1"
+                },
+                {
+                    "Priority": "low",
+                    "LocCode": "P1"
+                }
+            ]
+        },
+        {
+            "System": "system1",
+            "CalloutList":
+            [
+                {
+                    "Priority": "low",
+                    "SymbolicFRU": "service_docs"
+                },
+                {
+                    "Priority": "low",
+                    "SymbolicFRUTrusted": "air_mover",
+                    "UseInventoryLocCode": true
+                }
+            ]
+        },
+        {
+            "CalloutList":
+            [
+                {
+                    "Priority": "medium",
+                    "Procedure": "BMC0001"
+                },
+                {
+                    "Priority": "low",
+                    "LocCode": "P3-C8",
+                    "SymbolicFRUTrusted": "service_docs"
+                }
+            ]
+        }
+        ])"_json;
+
+    AdditionalData ad;
+    systemNames.push_back("system1");
+
+    auto callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 4);
+    EXPECT_EQ(callouts[0].priority, "high");
+    EXPECT_EQ(callouts[0].locCode, "P1-C1");
+    EXPECT_EQ(callouts[0].procedure, "");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P1");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[2].priority, "low");
+    EXPECT_EQ(callouts[2].locCode, "");
+    EXPECT_EQ(callouts[2].procedure, "");
+    EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
+    EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[3].priority, "low");
+    EXPECT_EQ(callouts[3].locCode, "");
+    EXPECT_EQ(callouts[3].procedure, "");
+    EXPECT_EQ(callouts[3].symbolicFRU, "");
+    EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
+    EXPECT_EQ(callouts[3].useInventoryLocCode, true);
+
+    // if system name is "System2"
+    systemNames[0] = "system2";
+
+    callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 2);
+    EXPECT_EQ(callouts[0].priority, "high");
+    EXPECT_EQ(callouts[0].locCode, "P1-C1");
+    EXPECT_EQ(callouts[0].procedure, "");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P1");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
+
+    // system name is System3 which is not in json thereby will take default
+    systemNames[0] = "system3";
+
+    callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 2);
+    EXPECT_EQ(callouts[0].priority, "medium");
+    EXPECT_EQ(callouts[0].locCode, "");
+    EXPECT_EQ(callouts[0].procedure, "BMC0001");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P3-C8");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "service_docs");
+    EXPECT_EQ(callouts[1].useInventoryLocCode, false);
+}
+
+TEST_F(RegistryTest, TestGetCalloutsWithOnlySystemAndSystems)
+{
+    std::vector<std::string> systemNames;
+
+    auto json = R"(
+        [
+        {
+            "Systems": ["system1", "system2"],
+            "CalloutList":
+            [
+                {
+                    "Priority": "high",
+                    "LocCode": "P1-C1"
+                },
+                {
+                    "Priority": "low",
+                    "LocCode": "P1"
+                }
+            ]
+        },
+        {
+            "System": "system1",
+            "CalloutList":
+            [
+                {
+                    "Priority": "low",
+                    "SymbolicFRU": "service_docs"
+                },
+                {
+                    "Priority": "low",
+                    "SymbolicFRUTrusted": "air_mover",
+                    "UseInventoryLocCode": true
+                }
+            ]
+        }
+        ])"_json;
+
+    AdditionalData ad;
+    systemNames.push_back("system1");
+
+    auto callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 4);
+    EXPECT_EQ(callouts[0].priority, "high");
+    EXPECT_EQ(callouts[0].locCode, "P1-C1");
+    EXPECT_EQ(callouts[0].procedure, "");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P1");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[2].priority, "low");
+    EXPECT_EQ(callouts[2].locCode, "");
+    EXPECT_EQ(callouts[2].procedure, "");
+    EXPECT_EQ(callouts[2].symbolicFRU, "service_docs");
+    EXPECT_EQ(callouts[2].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[3].priority, "low");
+    EXPECT_EQ(callouts[3].locCode, "");
+    EXPECT_EQ(callouts[3].procedure, "");
+    EXPECT_EQ(callouts[3].symbolicFRU, "");
+    EXPECT_EQ(callouts[3].symbolicFRUTrusted, "air_mover");
+    EXPECT_EQ(callouts[3].useInventoryLocCode, true);
+
+    // if system name is "System2"
+    systemNames[0] = "system2";
+
+    callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 2);
+    EXPECT_EQ(callouts[0].priority, "high");
+    EXPECT_EQ(callouts[0].locCode, "P1-C1");
+    EXPECT_EQ(callouts[0].procedure, "");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P1");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
+
+    // There is no entry for system3 or a default system,
+    // so this should fail.
+    systemNames[0] = "system3";
+    EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
+                 std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetCalloutsWithOnlySystems)
+{
+    std::vector<std::string> systemNames;
+
+    auto json = R"(
+        [
+        {
+            "Systems": ["system1", "system2"],
+            "CalloutList":
+            [
+                {
+                    "Priority": "high",
+                    "LocCode": "P1-C1"
+                },
+                {
+                    "Priority": "low",
+                    "LocCode": "P1"
+                }
+            ]
+        }
+        ])"_json;
+
+    AdditionalData ad;
+    systemNames.push_back("system1");
+
+    // system1 is available in JSON array
+    auto callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 2);
+    EXPECT_EQ(callouts[0].priority, "high");
+    EXPECT_EQ(callouts[0].locCode, "P1-C1");
+    EXPECT_EQ(callouts[0].procedure, "");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P1");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
+
+    // There is no entry for system3 or a default system,
+    // so this should fail.
+    systemNames[0] = "system3";
+    EXPECT_THROW(Registry::getCallouts(json, systemNames, ad),
+                 std::runtime_error);
+}
+
+TEST_F(RegistryTest, TestGetCalloutsWithOnlyDefaults)
+{
+    std::vector<std::string> systemNames;
+
+    auto json = R"(
+        [
+        {
+            "CalloutList":
+            [
+                {
+                    "Priority": "high",
+                    "LocCode": "P1-C1"
+                },
+                {
+                    "Priority": "low",
+                    "LocCode": "P1"
+                }
+            ]
+        }
+        ])"_json;
+
+    AdditionalData ad;
+    systemNames.push_back("system1");
+
+    // Since neither System or Systems available, it will pick the default one
+    // only
+    auto callouts = Registry::getCallouts(json, systemNames, ad);
+    EXPECT_EQ(callouts.size(), 2);
+    EXPECT_EQ(callouts[0].priority, "high");
+    EXPECT_EQ(callouts[0].locCode, "P1-C1");
+    EXPECT_EQ(callouts[0].procedure, "");
+    EXPECT_EQ(callouts[0].symbolicFRU, "");
+    EXPECT_EQ(callouts[0].symbolicFRUTrusted, "");
+    EXPECT_EQ(callouts[1].priority, "low");
+    EXPECT_EQ(callouts[1].locCode, "P1");
+    EXPECT_EQ(callouts[1].procedure, "");
+    EXPECT_EQ(callouts[1].symbolicFRU, "");
+    EXPECT_EQ(callouts[1].symbolicFRUTrusted, "");
+}
+
 TEST_F(RegistryTest, TestNoSubsystem)
 {
     auto path = RegistryTest::writeData(registryData);