0

I've got the following XML excerpt. The full XML is the OVF definition.

<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" x xml:lang="en-US">
      <Item>
        <rasd:Caption ovf:msgid="network.eth0.caption"/>
        <rasd:Connection>eth0</rasd:Connection>
        <rasd:Description ovf:msgid="network.eth0.description"/>
        <rasd:ElementName>eth0</rasd:ElementName>
        <rasd:InstanceID>13</rasd:InstanceID>
        <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
        <rasd:ResourceType>10</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:Caption ovf:msgid="network.eth1.caption"/>
        <rasd:Connection>eth1</rasd:Connection>
        <rasd:Description ovf:msgid="network.eth1.description"/>
        <rasd:ElementName>eth1</rasd:ElementName>
        <rasd:InstanceID>14</rasd:InstanceID>
        <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
        <rasd:ResourceType>10</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:Caption ovf:msgid="network.eth2.caption"/>
        <rasd:Connection>eth2</rasd:Connection>
        <rasd:Description ovf:msgid="network.eth2.description"/>
        <rasd:ElementName>eth2</rasd:ElementName>
        <rasd:InstanceID>15</rasd:InstanceID>
        <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
        <rasd:ResourceType>10</rasd:ResourceType>
      </Item>
      <Item>
        <rasd:Caption ovf:msgid="network.eth3.caption"/>
        <rasd:Connection>eth3</rasd:Connection>
        <rasd:Description ovf:msgid="network.eth3.description"/>
        <rasd:ElementName>eth3</rasd:ElementName>
        <rasd:InstanceID>16</rasd:InstanceID>
        <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
        <rasd:ResourceType>10</rasd:ResourceType>
      </Item>     
</Envelope>

I am trying to insert the line <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>before the <rasd:Connection>eth*</rasd:Connection> line but not on all of them. I've gotten the following XSL so far and it works, the issue though is that I have to hard code each interface I want to disable.

<xsl:template match="rasd:Connection[text()='eth0']">
    <xsl:if test="$disableEths='true'">
        <xsl:element name="rasd:AutomaticAllocation">false</xsl:element>
        <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template> 
<xsl:template match="rasd:Connection[text()='eth1']">
    <xsl:if test="$disableEths='true'">
        <xsl:element name="rasd:AutomaticAllocation">false</xsl:element>
        <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template> 
<xsl:template match="rasd:Connection[text()='eth2']">
    <xsl:if test="$disableEths='true'">
        <xsl:element name="rasd:AutomaticAllocation">false</xsl:element>
        <xsl:copy-of select="."/>
    </xsl:if>
</xsl:template>

Is there a way to have the user pass in a param containing a delimited list of values they want to disable and if no param is input don't disable any of them? Using xsltproc as the processor if it matters.

stormbard
  • 56
  • 1
  • 9
  • 1
    Is it possible the user passes in an XML file of list of values to disable? Then, the XSLT can read in using `document()` function to dynamically add element. – Parfait Jan 23 '16 at 14:55

2 Answers2

2

As suggested in the comments, should the user produce an xml file, named for example DisableItems.xml like below (which by the way can be produced from text delimited files, .txt, .csv, .tab, etc., using general purpose languages: C#, Java, Perl, PHP, Python, R, VB...):

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <disableitem>eth1</disableitem>
  <disableitem>eth2</disableitem>
  <disableitem>eth3</disableitem>  
</root>

Then, XSLT can search accordingly using its document() function. Be sure other xml file is in same directory as original source xml:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
               xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

  <!-- Identity Transform -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>  

  <xsl:template match="rasd:Connection[text()=document('DisableItems.xml')/root/disableitem]">
    <xsl:element name="rasd:AutomaticAllocation">false</xsl:element>
    <xsl:copy-of select="."/>
  </xsl:template>

</xsl:transform>

Output (notice eth0 not specified in lookup xml does not have the false element)

<?xml version='1.0' encoding='UTF-8'?>
<Envelope xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en-US">
  <Item>
    <rasd:Caption ovf:msgid="network.eth0.caption"/>
    <rasd:Connection>eth0</rasd:Connection>
    <rasd:Description ovf:msgid="network.eth0.description"/>
    <rasd:ElementName>eth0</rasd:ElementName>
    <rasd:InstanceID>13</rasd:InstanceID>
    <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
    <rasd:ResourceType>10</rasd:ResourceType>
  </Item>
  <Item>
    <rasd:Caption ovf:msgid="network.eth1.caption"/>
    <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
    <rasd:Connection>eth1</rasd:Connection>
    <rasd:Description ovf:msgid="network.eth1.description"/>
    <rasd:ElementName>eth1</rasd:ElementName>
    <rasd:InstanceID>14</rasd:InstanceID>
    <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
    <rasd:ResourceType>10</rasd:ResourceType>
  </Item>
  <Item>
    <rasd:Caption ovf:msgid="network.eth2.caption"/>
    <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
    <rasd:Connection>eth2</rasd:Connection>
    <rasd:Description ovf:msgid="network.eth2.description"/>
    <rasd:ElementName>eth2</rasd:ElementName>
    <rasd:InstanceID>15</rasd:InstanceID>
    <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
    <rasd:ResourceType>10</rasd:ResourceType>
  </Item>
  <Item>
    <rasd:Caption ovf:msgid="network.eth3.caption"/>
    <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
    <rasd:Connection>eth3</rasd:Connection>
    <rasd:Description ovf:msgid="network.eth3.description"/>
    <rasd:ElementName>eth3</rasd:ElementName>
    <rasd:InstanceID>16</rasd:InstanceID>
    <rasd:ResourceSubType>VmxNet3</rasd:ResourceSubType>
    <rasd:ResourceType>10</rasd:ResourceType>
  </Item>
</Envelope>
Parfait
  • 104,375
  • 17
  • 94
  • 125
  • This is an elegant solution that I think I will work with. I'll test it out next time I start up my work computer. Will it fail ungracefully if the file is not found or will I need to create an empty file no matter what? – stormbard Jan 23 '16 at 19:40
  • Should file not be found, depending on XSLT processor an error may appear. Consider keeping a file always even with empty root. Glad I could help. – Parfait Jan 24 '16 at 02:51
0

If you pass in a --stringparam with a string of a comma separated list of values then with xsltproc you can use the EXSLT str:tokenize function as follows:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0"
  xmlns:str="http://exslt.org/strings"
  exclude-result-prefixes="str"
  xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">

<xsl:output indent="yes"/>

<xsl:param name="disableEths" select="'true'"/>
<xsl:param name="con-to-change" select="'eth0,eth1,eth2'"/>

<xsl:template match="@* | node()" name="identity"> 
  <xsl:copy> 
    <xsl:apply-templates select="@* | node()"/> 
  </xsl:copy> 
</xsl:template> 

<xsl:template match="rasd:Connection">
  <xsl:choose>
    <xsl:when test=". = str:tokenize($con-to-change, ',') and $disableEths='true'">
        <rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
        <xsl:copy-of select="."/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="identity"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template> 

</xsl:stylesheet>

Of course it would be desirable to use the parameter directly in the match pattern but the use of a variable or parameter reference in a match pattern is not allowed in XSLT 1.0, I thought xsltproc allows it, but in a test having match="rasd:Connection[. = str:tokenize($con-to-change, ',')]" I got some error message about an undefined variable, so the above that moves the check into the template is all I can suggest.

Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Solution works but I'd like to stay away from extension functions if possible. The reason being is keeping it as lightweight and dependency free as possible. – stormbard Jan 23 '16 at 19:50