Add XML patching bitbake class.

This commit adds a new obmc-xmlpatch class that can be
inherited in a recipe to provide a do_patch task to patch XML
files.  The patch files are themselves XML, and must end in
.patch.xml for do_patch to find them.

This commit also includes Palmetto patches specified in
palmetto.xml.patch.xml that are required to build the system
inventory.

Change-Id: Idae6ffd8e7a3aa247115ff3a840e047727ff0d1a
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/meta-openbmc-machines/meta-openpower/meta-ibm/meta-palmetto/recipes-phosphor/mrw/mrw-native.bbappend b/meta-openbmc-machines/meta-openpower/meta-ibm/meta-palmetto/recipes-phosphor/mrw/mrw-native.bbappend
new file mode 100644
index 0000000..836ef56
--- /dev/null
+++ b/meta-openbmc-machines/meta-openpower/meta-ibm/meta-palmetto/recipes-phosphor/mrw/mrw-native.bbappend
@@ -0,0 +1,2 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+SRC_URI += "file://palmetto.xml.patch.xml"
diff --git a/meta-openbmc-machines/meta-openpower/meta-ibm/meta-palmetto/recipes-phosphor/mrw/mrw-native/palmetto.xml.patch.xml b/meta-openbmc-machines/meta-openpower/meta-ibm/meta-palmetto/recipes-phosphor/mrw/mrw-native/palmetto.xml.patch.xml
new file mode 100644
index 0000000..744a317
--- /dev/null
+++ b/meta-openbmc-machines/meta-openpower/meta-ibm/meta-palmetto/recipes-phosphor/mrw/mrw-native/palmetto.xml.patch.xml
@@ -0,0 +1,467 @@
+<patches>
+
+<targetFile>palmetto.xml</targetFile>
+
+<!-- Add in the 5 fan and connector instances -->
+<targetInstance xpath="/">
+	<id>fanconn-0</id>
+	<type>connector-card-generic</type>
+	<library_target>false</library_target>
+	<instance_name>fanconn</instance_name>
+	<position>0</position>
+	<child_id>fan-0</child_id>
+	<attribute>
+		<id>CLASS</id>
+		<default>CONNECTOR</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>NA</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fan-0</id>
+	<type>card-fan</type>
+	<library_target>false</library_target>
+	<instance_name>fan</instance_name>
+	<position>0</position>
+	<attribute>
+		<id>CLASS</id>
+		<default>CARD</default>
+	</attribute>
+	<attribute>
+		<id>FRU_ID</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>FRU_NAME</id>
+		<default>FAN</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>FAN</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fanconn-1</id>
+	<type>connector-card-generic</type>
+	<library_target>false</library_target>
+	<instance_name>fanconn</instance_name>
+	<position>1</position>
+	<child_id>fan-1</child_id>
+	<attribute>
+		<id>CLASS</id>
+		<default>CONNECTOR</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>1</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>NA</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fan-1</id>
+	<type>card-fan</type>
+	<library_target>false</library_target>
+	<instance_name>fan</instance_name>
+	<position>1</position>
+	<attribute>
+		<id>CLASS</id>
+		<default>CARD</default>
+	</attribute>
+	<attribute>
+		<id>FRU_ID</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>FRU_NAME</id>
+		<default>FAN</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>1</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>FAN</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fanconn-2</id>
+	<type>connector-card-generic</type>
+	<library_target>false</library_target>
+	<instance_name>fanconn</instance_name>
+	<position>2</position>
+	<child_id>fan-2</child_id>
+	<attribute>
+		<id>CLASS</id>
+		<default>CONNECTOR</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>2</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>NA</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fan-2</id>
+	<type>card-fan</type>
+	<library_target>false</library_target>
+	<instance_name>fan</instance_name>
+	<position>2</position>
+	<attribute>
+		<id>CLASS</id>
+		<default>CARD</default>
+	</attribute>
+	<attribute>
+		<id>FRU_ID</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>FRU_NAME</id>
+		<default>FAN</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>2</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>FAN</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fanconn-3</id>
+	<type>connector-card-generic</type>
+	<library_target>false</library_target>
+	<instance_name>fanconn</instance_name>
+	<position>3</position>
+	<child_id>fan-3</child_id>
+	<attribute>
+		<id>CLASS</id>
+		<default>CONNECTOR</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>3</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>NA</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fan-3</id>
+	<type>card-fan</type>
+	<library_target>false</library_target>
+	<instance_name>fan</instance_name>
+	<position>3</position>
+	<attribute>
+		<id>CLASS</id>
+		<default>CARD</default>
+	</attribute>
+	<attribute>
+		<id>FRU_ID</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>FRU_NAME</id>
+		<default>FAN</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>3</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>FAN</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fanconn-4</id>
+	<type>connector-card-generic</type>
+	<library_target>false</library_target>
+	<instance_name>fanconn</instance_name>
+	<position>4</position>
+	<child_id>fan-4</child_id>
+	<attribute>
+		<id>CLASS</id>
+		<default>CONNECTOR</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>4</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>NA</default>
+	</attribute>
+</targetInstance>
+<targetInstance xpath="/">
+	<id>fan-4</id>
+	<type>card-fan</type>
+	<library_target>false</library_target>
+	<instance_name>fan</instance_name>
+	<position>4</position>
+	<attribute>
+		<id>CLASS</id>
+		<default>CARD</default>
+	</attribute>
+	<attribute>
+		<id>FRU_ID</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>FRU_NAME</id>
+		<default>FAN</default>
+	</attribute>
+	<attribute>
+		<id>LOCATION_CODE</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MODEL</id>
+		<default></default>
+	</attribute>
+	<attribute>
+		<id>MRW_TYPE</id>
+		<default>NA</default>
+	</attribute>
+	<attribute>
+		<id>POSITION</id>
+		<default>4</default>
+	</attribute>
+	<attribute>
+		<id>RESOURCE_IS_CRITICAL</id>
+		<default>0</default>
+	</attribute>
+	<attribute>
+		<id>TYPE</id>
+		<default>FAN</default>
+	</attribute>
+</targetInstance>
+
+<!-- place the fan connectors on the motherboard -->
+<targetInstance type="add-child" xpath="targetInstance[id='motherboard-0']">
+	<child_id>fanconn-0</child_id>
+	<child_id>fanconn-1</child_id>
+	<child_id>fanconn-2</child_id>
+	<child_id>fanconn-3</child_id>
+	<child_id>fanconn-4</child_id>
+</targetInstance>
+
+<!-- Add in the motherboard FRU name -->
+<attribute type="add" xpath="targetInstance[id='motherboard-0']">
+    <id>FRU_NAME</id>
+    <default>MOTHERBOARD</default>
+</attribute>
+
+<!-- add in some new types -->
+<enumerationType type="add-child" xpath="enumerationType[id='TYPE']">
+    <enumerator>
+        <name>FAN</name>
+        <value>35</value>
+    </enumerator>
+    <enumerator>
+        <name>PCIE_CARD</name>
+        <value>36</value>
+    </enumerator>
+</enumerationType>
+
+
+
+<!--fix up the other enums that come at the end of the list-->
+<enumerationType type="replace-child" key="name" xpath="enumerationType[id='TYPE']">
+    <enumerator>
+        <name>TEST_FAIL</name>
+        <value>37</value>
+    </enumerator>
+    <enumerator>
+        <name>LAST_IN_RANGE</name>
+        <value>38</value>
+    </enumerator>
+</enumerationType>
+
+<!-- Fix the PCIE card's type -->
+<attribute type="replace" xpath="targetInstance[id='pciecard_x8-0']/attribute[id='TYPE']">
+    <id>TYPE</id>
+    <default>PCIE_CARD</default>
+</attribute>
+
+<!-- Add the card's FRU name -->
+<attribute type="add" xpath="targetInstance[id='pciecard_x8-0']">
+    <id>FRU_NAME</id>
+    <default>PCIE_CARD</default>
+</attribute>
+
+<!-- Fix the PCIE card's type -->
+<attribute type="replace" xpath="targetInstance[id='pciecard_x16-0']/attribute[id='TYPE']">
+    <id>TYPE</id>
+    <default>PCIE_CARD</default>
+</attribute>
+
+<!-- Add the card's FRU name -->
+<attribute type="add" xpath="targetInstance[id='pciecard_x16-0']">
+    <id>FRU_NAME</id>
+    <default>PCIE_CARD</default>
+</attribute>
+
+</patches>
diff --git a/meta-phosphor/classes/obmc-xmlpatch.bbclass b/meta-phosphor/classes/obmc-xmlpatch.bbclass
new file mode 100644
index 0000000..9dc3750
--- /dev/null
+++ b/meta-phosphor/classes/obmc-xmlpatch.bbclass
@@ -0,0 +1,74 @@
+#This class applies patches to an XML file during do_patch().  The
+#patches themselves are specified in XML in a separate file that must
+#be in SRC_URI and end in .patch.xml.  The patch XML file must also
+#have a <targetFile> element that specifies the base name of the file
+#that needs to be patched.
+#
+#See patchxml.py for details on the XML patch format.
+#
+
+inherit pythonnative
+inherit obmc-phosphor-utils
+do_patch[depends] = "mrw-patch-native:do_populate_sysroot"
+
+
+def find_patch_files(d):
+    all_patches = listvar_to_list(d, 'SRC_URI')
+    xml_patches = filter(lambda x: x.endswith('.patch.xml') and
+                         x.startswith('file://'), all_patches)
+
+    return [x.lstrip('file://') for x in xml_patches]
+
+
+def apply_xml_patch(base_patch_name, d):
+    import xml.etree.ElementTree as et
+    import subprocess
+
+    patch_file = os.path.join(d.getVar("WORKDIR", True), base_patch_name)
+
+    if not os.path.exists(patch_file):
+        bb.fatal("Could not find patch file " + patch_file +
+                 " specified in SRC_URI")
+
+    patch_tree = et.parse(patch_file)
+    patch_root = patch_tree.getroot()
+    target_file_element = patch_root.find("targetFile")
+
+    if target_file_element is None:
+        bb.fatal("Could not find <targetFile> element in patch file "
+                 + patch_file)
+    else:
+        xml = target_file_element.text
+
+    xml = os.path.join(d.getVar("S", True), xml)
+
+    if not os.path.exists(xml):
+        bb.fatal("Could not find XML file to patch: " + xml)
+
+    print("Applying XML fixes found in " + patch_file + " to " + xml)
+
+    cmd = []
+    cmd.append(os.path.join(d.getVar("bindir", True), "obmc-mrw/patchxml.py"))
+    cmd.append("-x")
+    cmd.append(xml)
+    cmd.append("-p")
+    cmd.append(patch_file)
+    cmd.append("-o")
+    cmd.append(xml + ".patched")
+
+    try:
+        subprocess.check_call(cmd)
+    except subprocess.CalledProcessError as e:
+        bb.fatal("Failed patching XML:\n%s" % e.output)
+
+    os.rename(xml, xml + ".orig")
+    os.rename(xml + ".patched", xml)
+
+
+python xmlpatch_do_patch() {
+
+    for patch in find_patch_files(d):
+        apply_xml_patch(patch, d)
+}
+
+do_patch[postfuncs] += "xmlpatch_do_patch"
diff --git a/meta-phosphor/common/recipes-phosphor/mrw/mrw-native.bb b/meta-phosphor/common/recipes-phosphor/mrw/mrw-native.bb
index 3ce4c97..e06a682 100644
--- a/meta-phosphor/common/recipes-phosphor/mrw/mrw-native.bb
+++ b/meta-phosphor/common/recipes-phosphor/mrw/mrw-native.bb
@@ -5,6 +5,7 @@
 S = "${WORKDIR}/git"
 
 inherit obmc-phosphor-license
+inherit obmc-xmlpatch
 inherit native
 
 PHOSPHOR_MRW_URI ?= "http://missing-mrw-uri"
diff --git a/meta-phosphor/common/recipes-phosphor/mrw/mrw-patch-native.bb b/meta-phosphor/common/recipes-phosphor/mrw/mrw-patch-native.bb
new file mode 100644
index 0000000..694ece9
--- /dev/null
+++ b/meta-phosphor/common/recipes-phosphor/mrw/mrw-patch-native.bb
@@ -0,0 +1,18 @@
+SUMMARY = "Phosphor machine readable workbook patching script"
+DESCRIPTION = "Retrieve the script that can patch the MRW XML"
+PR = "r1"
+
+S = "${WORKDIR}/git"
+
+inherit obmc-phosphor-license
+inherit native
+
+DEPENDS += "python-native python-lxml-native"
+
+SRC_URI += "git://github.com/openbmc/phosphor-mrw-tools"
+SRCREV = "ab015d7e2a2eb87eab2ca7d731ebcb7a873442e9"
+
+do_install() {
+    install -d ${bindir}/obmc-mrw
+    install -m 0755 patchxml.py ${bindir}/obmc-mrw
+}