Add support for Entity Association Record

Adding support for Entity Association Record (SDR type - 0x08h)
This patch includes:
1) Entity Association Record yaml file example
2) Entity Assocation Record related script and mako file changes
3) Adding Entity Association Record in get_sdr IPMI command response

From the host, tested that entity association records can be fetched

Change-Id: I9cf598e5d27d2e8c6751bbaae2176e7c976974b1
Tested: Yes
Signed-off-by: Jaghathiswari Rankappagounder Natarajan <jaghu@google.com>
diff --git a/scripts/entity-example.yaml b/scripts/entity-example.yaml
new file mode 100755
index 0000000..2db14cc
--- /dev/null
+++ b/scripts/entity-example.yaml
@@ -0,0 +1,127 @@
+# This record has:
+# Container Entity Id and Container Entity Instance = (0x13, 0x81)
+# Contained Entity Id and Contained Entity Instance = (0x0A, 0x1),
+# (0x0A, 0x3), (0x0A, 0x5), (0x0A, 0x7)
+# Entity Record id is the key
+0x01:
+  # Container entity contains other entities
+  # Entity Id and entity Instance for the container entity
+  containerEntityId: 0x13
+  containerEntityInstance: 0x81
+  # A record can have contained entities as a four entry list or as upto
+  # two ranges of entity instances; this record has contained entities
+  # as a four entry list
+  isList: "true"
+  # Records can be linked if necessary to extend the number of contained
+  # entities within a container entity; this record is not linked
+  isLinked: "false"
+  entityId1: 0x0A
+  entityInstance1: 0x1
+  entityId2: 0x0A
+  entityInstance2: 0x3
+  entityId3: 0x0A
+  entityInstance3: 0x5
+  entityId4: 0x0A
+  entityInstance4: 0x7
+
+# The below two records have:
+# Container Entity Id and Container Entity Instance = (0x18, 0x2)
+# Contained Entity Id and Contained Entity Instance = (0x1D, 0x1),
+# (0x1D, 0x4), (0x1D, 0x6), (0x2B, 0x1), (0x2B, 0x3), (0x0F, 0x1),
+# (0x0F, 0x3), (0x10, 0x5)
+0x02:
+  containerEntityId: 0x18
+  containerEntityInstance: 0x2
+  # This record  has contained entities as a four entry list
+  isList: "true"
+  # This record is linked with the below record; this record and the
+  # below record have the same container entity Id and container entity
+  # instance;
+  isLinked: "true"
+  entityId1: 0x1D
+  entityInstance1: 0x1
+  entityId2: 0x1D
+  entityInstance2: 0x4
+  entityId3: 0x1D
+  entityInstance3: 0x6
+  entityId4: 0x2B
+  entityInstance4: 0x1
+
+0x03:
+  containerEntityId: 0x18
+  containerEntityInstance: 0x2
+  # This record  has contained entities as a four entry list
+  isList: "true"
+  # This record is linked with the above record; this record and the
+  # above record have the same container entity Id and container entity
+  # instance
+  isLinked: "true"
+  entityId1: 0x2B
+  entityInstance1: 0x3
+  entityId2: 0x0F
+  entityInstance2: 0x1
+  entityId3: 0x0F
+  entityInstance3: 0x3
+  entityId4: 0x10
+  entityInstance4: 0x5
+
+# This record has:
+# Container Entity Id and Container Entity Instance = (0x1E, 0x1)
+# Contained Entity Id and Contained Entity Instance = (0x20, 0x1),
+# (0x20, 0x2), (0x20, 0x3), (0x20, 0x7), (0x20, 0x8), (0x20, 0x9)
+0x04:
+  containerEntityId: 0x1E
+  containerEntityInstance: 0x1
+  # This record has contained entities as two ranges of entity instances
+  isList: "false"
+  # This record is not linked
+  isLinked: "false"
+  entityId1: 0x20
+  entityInstance1: 0x1
+  entityId2: 0x20
+  entityInstance2: 0x3
+  entityId3: 0x20
+  entityInstance3: 0x7
+  entityId4: 0x20
+  entityInstance4: 0x9
+
+# The below two records have:
+# Container Entity Id and Container Entity Instance = (0x1E, 0x3)
+# Contained Entity Id and Contained Entity Instance = (0x20, 0x1),
+# (0x20, 0x2), (0x20, 0x3), (0x20, 0x6), (0x20, 0x7), (0x20, 0x8),
+# (0x20, 0xA), (0x20, 0xB), (0x20, 0xD), (0x20, 0xE), (0x20, 0xF)
+0x05:
+  containerEntityId: 0x1E
+  containerEntityInstance: 0x03
+  # This record has contained entities as two ranges of entity instances
+  isList: "false"
+  # This record is linked with the below record; this record and the
+  # below record have the same container entity Id and container entity
+  # instance;
+  isLinked: "true"
+  entityId1: 0x20
+  entityInstance1: 0x1
+  entityId2: 0x20
+  entityInstance2: 0x3
+  entityId3: 0x20
+  entityInstance3: 0x6
+  entityId4: 0x20
+  entityInstance4: 0x8
+
+0x06:
+  containerEntityId: 0x1E
+  containerEntityInstance: 0x03
+  # This record has contained entities as two ranges of entity instances
+  isList: "false"
+  # This record is linked with the above record; this record and the
+  # above record have the same container entity Id and container entity
+  # instance;
+  isLinked: "true"
+  entityId1: 0x20
+  entityInstance1: 0xA
+  entityId2: 0x20
+  entityInstance2: 0xB
+  entityId3: 0x20
+  entityInstance3: 0xD
+  entityId4: 0x20
+  entityInstance4: 0xF
diff --git a/scripts/entity_gen.py b/scripts/entity_gen.py
new file mode 100755
index 0000000..057821b
--- /dev/null
+++ b/scripts/entity_gen.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import yaml
+import argparse
+from mako.template import Template
+
+
+def generate_cpp(entity_yaml, output_dir):
+    with open(os.path.join(script_dir, entity_yaml), 'r') as f:
+        ifile = yaml.safe_load(f)
+        if not isinstance(ifile, dict):
+            ifile = {}
+
+        # Render the mako template
+
+        t = Template(filename=os.path.join(
+                     script_dir,
+                     "writeentity.mako.cpp"))
+
+        output_cpp = os.path.join(output_dir, "entity-gen.cpp")
+        with open(output_cpp, 'w') as fd:
+            fd.write(t.render(entityDict=ifile))
+
+
+def main():
+
+    valid_commands = {
+        'generate-cpp': generate_cpp
+    }
+    parser = argparse.ArgumentParser(
+        description="IPMI Entity record parser and code generator")
+
+    parser.add_argument(
+        '-i', '--entity_yaml', dest='entity_yaml',
+        default='example.yaml', help='input entity yaml file to parse')
+
+    parser.add_argument(
+        "-o", "--output-dir", dest="outputdir",
+        default=".",
+        help="output directory")
+
+    parser.add_argument(
+        'command', metavar='COMMAND', type=str,
+        choices=valid_commands.keys(),
+        help='Command to run.')
+
+    args = parser.parse_args()
+
+    if (not (os.path.isfile(os.path.join(script_dir, args.entity_yaml)))):
+        sys.exit("Can not find input yaml file " + args.entity_yaml)
+
+    function = valid_commands[args.command]
+    function(args.entity_yaml, args.outputdir)
+
+
+if __name__ == '__main__':
+    script_dir = os.path.dirname(os.path.realpath(__file__))
+    main()
diff --git a/scripts/writeentity.mako.cpp b/scripts/writeentity.mako.cpp
new file mode 100644
index 0000000..9de11a0
--- /dev/null
+++ b/scripts/writeentity.mako.cpp
@@ -0,0 +1,34 @@
+## This file is a template.  The comment below is emitted
+## into the rendered file; feel free to edit this file.
+// !!! WARNING: This is a GENERATED Code..Please do NOT Edit !!!
+
+#include "types.hpp"
+using namespace ipmi::sensor;
+
+extern const EntityInfoMap entities = {
+% for key in entityDict.iterkeys():
+{${key},{
+<%
+       entity = entityDict[key]
+       containerEntityId = entity["containerEntityId"]
+       containerEntityInstance = entity["containerEntityInstance"]
+       isList = entity["isList"]
+       isLinked = entity["isLinked"]
+       entityId1 = entity["entityId1"]
+       entityInstance1 = entity["entityInstance1"]
+       entityId2 = entity["entityId2"]
+       entityInstance2 = entity["entityInstance2"]
+       entityId3 = entity["entityId3"]
+       entityInstance3 = entity["entityInstance3"]
+       entityId4 = entity["entityId4"]
+       entityInstance4 = entity["entityInstance4"]
+%>
+        ${containerEntityId},${containerEntityInstance},${isList},${isLinked},{
+          std::make_pair(${entityId1}, ${entityInstance1}),
+          std::make_pair(${entityId2}, ${entityInstance2}),
+          std::make_pair(${entityId3}, ${entityInstance3}),
+          std::make_pair(${entityId4}, ${entityInstance4}) }
+
+}},
+% endfor
+};