Add support for PartCalloutResolution

Change-Id: Ia9ab315e43d98a742d2ad88ba2fb33bd3e56ee86
Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
diff --git a/analyzer/callout.hpp b/analyzer/callout.hpp
index 06812a5..b4f7912 100644
--- a/analyzer/callout.hpp
+++ b/analyzer/callout.hpp
@@ -181,6 +181,39 @@
 inline const ClockType ClockType::OSC_REF_CLOCK_0{"OSC_REF_CLOCK_0"};
 inline const ClockType ClockType::OSC_REF_CLOCK_1{"OSC_REF_CLOCK_1"};
 
+/** @brief Container class for part callout service actions. */
+class PartType
+{
+  public:
+    /** The part containing the PNOR. */
+    static const PartType PNOR;
+
+  private:
+    /**
+     * @brief Constructor from components.
+     * @param i_string The string representation of the part callout.
+     */
+    explicit PartType(const std::string& i_string) : iv_string(i_string) {}
+
+  private:
+    /** The string representation of the part callout. */
+    const std::string iv_string;
+
+  public:
+    bool operator==(const PartType& r) const
+    {
+        return this->iv_string == r.iv_string;
+    }
+
+    /** iv_string accessor */
+    std::string getString() const
+    {
+        return iv_string;
+    }
+};
+
+inline const PartType PartType::PNOR{"PNOR"};
+
 /** @brief Container class for guard service actions. */
 class GuardType
 {
diff --git a/analyzer/ras-data/ras-data-definition.md b/analyzer/ras-data/ras-data-definition.md
index 1ad92d1..9c4b10f 100644
--- a/analyzer/ras-data/ras-data-definition.md
+++ b/analyzer/ras-data/ras-data-definition.md
@@ -210,8 +210,9 @@
 
 Supported parts:
 
-| Part     | Description                                                       |
-|----------|-------------------------------------------------------------------|
+| Part Type     | Description                                                  |
+|---------------|--------------------------------------------------------------|
+| PNOR          | The part containing the PNOR                                 |
 
 #### 5.1.9) action type `plugin`
 
diff --git a/analyzer/ras-data/ras-data-parser.cpp b/analyzer/ras-data/ras-data-parser.cpp
index c6d228e..b97ac01 100644
--- a/analyzer/ras-data/ras-data-parser.cpp
+++ b/analyzer/ras-data/ras-data-parser.cpp
@@ -250,9 +250,15 @@
             auto name     = a.at("name").get<std::string>();
             auto priority = a.at("priority").get<std::string>();
 
-            // TODO
-            trace::inf("callout_part: name=%s priority=%s", name.c_str(),
-                       priority.c_str());
+            // clang-format off
+            static const std::map<std::string, callout::PartType> m =
+            {
+                {"PNOR", callout::PartType::PNOR},
+            };
+            // clang-format on
+
+            o_list->push(std::make_shared<PartCalloutResolution>(
+                m.at(name), getPriority(priority)));
         }
         else if ("plugin" == type)
         {
diff --git a/analyzer/ras-data/schema/ras-data-schema-v01.json b/analyzer/ras-data/schema/ras-data-schema-v01.json
index 4923ba4..e1bced1 100644
--- a/analyzer/ras-data/schema/ras-data-schema-v01.json
+++ b/analyzer/ras-data/schema/ras-data-schema-v01.json
@@ -193,7 +193,9 @@
                                     "not": { "required": [ "guard" ] },
                                     "properties": {
                                         "name": {
-                                            "enum": []
+                                            "enum": [
+                                                "PNOR"
+                                            ]
                                         }
                                     }
                                 }
diff --git a/analyzer/resolution.cpp b/analyzer/resolution.cpp
index f9c09b5..5ba4b6f 100644
--- a/analyzer/resolution.cpp
+++ b/analyzer/resolution.cpp
@@ -100,6 +100,14 @@
 
 //------------------------------------------------------------------------------
 
+void PartCalloutResolution::resolve(ServiceData& io_sd) const
+{
+    // Add the callout and the FFDC to the service data.
+    io_sd.calloutPart(iv_part, iv_priority);
+}
+
+//------------------------------------------------------------------------------
+
 void PluginResolution::resolve(ServiceData& io_sd) const
 {
     // Get the plugin function and call it.
diff --git a/analyzer/resolution.hpp b/analyzer/resolution.hpp
index 6626318..cb2cc6e 100644
--- a/analyzer/resolution.hpp
+++ b/analyzer/resolution.hpp
@@ -195,6 +195,32 @@
     void resolve(ServiceData& io_sd) const override;
 };
 
+/** @brief Resolves a part callout service event. */
+class PartCalloutResolution : public Resolution
+{
+  public:
+    /**
+     * @brief Constructor from components.
+     * @param i_part     The part callout type.
+     * @param i_priority The callout priority.
+     */
+    PartCalloutResolution(const callout::PartType& i_part,
+                          const callout::Priority& i_priority) :
+        iv_part(i_part),
+        iv_priority(i_priority)
+    {}
+
+  private:
+    /** The part callout type. */
+    const callout::PartType iv_part;
+
+    /** The callout priority. */
+    const callout::Priority iv_priority;
+
+  public:
+    void resolve(ServiceData& io_sd) const override;
+};
+
 /**
  * @brief Some service actions cannot be contained within the RAS data files.
  *        This resolution class allows a predefined plugin function to be
diff --git a/analyzer/service_data.cpp b/analyzer/service_data.cpp
index e544b6e..de2c0a7 100644
--- a/analyzer/service_data.cpp
+++ b/analyzer/service_data.cpp
@@ -116,6 +116,31 @@
 
 //------------------------------------------------------------------------------
 
+void ServiceData::calloutPart(const callout::PartType& i_part,
+                              const callout::Priority& i_priority)
+{
+    if (callout::PartType::PNOR == i_part)
+    {
+        // The PNOR is on the BMC card.
+        // TODO: Will need to be modified if we ever support systems with more
+        //       than one BMC.
+        addTargetCallout(util::pdbg::getTrgt("/bmc0"), i_priority, false);
+    }
+    else
+    {
+        throw std::logic_error("Unsupported part type: " + i_part.getString());
+    }
+
+    // Add the callout FFDC.
+    nlohmann::json ffdc;
+    ffdc["Callout Type"] = "Part Callout";
+    ffdc["Part Type"]    = i_part.getString();
+    ffdc["Priority"]     = i_priority.getRegistryString();
+    addCalloutFFDC(ffdc);
+}
+
+//------------------------------------------------------------------------------
+
 void ServiceData::addCallout(const nlohmann::json& i_callout)
 {
     // The new callout is either a hardware callout with a location code or a
diff --git a/analyzer/service_data.hpp b/analyzer/service_data.hpp
index 7e39589..1639ed0 100644
--- a/analyzer/service_data.hpp
+++ b/analyzer/service_data.hpp
@@ -125,6 +125,14 @@
     void calloutProcedure(const callout::Procedure& i_procedure,
                           const callout::Priority& i_priority);
 
+    /**
+     * @brief Add callout for part type.
+     * @param i_part     The part type.
+     * @param i_priority The callout priority.
+     */
+    void calloutPart(const callout::PartType& i_part,
+                     const callout::Priority& i_priority);
+
     /** @brief Accessor to iv_calloutList. */
     const nlohmann::json& getCalloutList() const
     {
diff --git a/test/resolution_test.cpp b/test/resolution_test.cpp
index c894d01..23f952c 100644
--- a/test/resolution_test.cpp
+++ b/test/resolution_test.cpp
@@ -416,3 +416,43 @@
 ])";
     EXPECT_EQ(s, j.dump(4));
 }
+
+TEST(Resolution, PartCallout)
+{
+    pdbg_targets_init(nullptr);
+
+    auto c1 = std::make_shared<PartCalloutResolution>(callout::PartType::PNOR,
+                                                      callout::Priority::MED);
+
+    libhei::Chip chip{util::pdbg::getTrgt(chip_str), 0xdeadbeef};
+    libhei::Signature sig{chip, 0xabcd, 0, 0, libhei::ATTN_TYPE_CHECKSTOP};
+    ServiceData sd{sig, AnalysisType::SYSTEM_CHECKSTOP};
+
+    c1->resolve(sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "/bmc0",
+        "Priority": "M"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Part Callout",
+        "Part Type": "PNOR",
+        "Priority": "medium"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+}