0

I want to read the attributes under <PropertySection> from the following file using ansible:

<?xml version='1.0' encoding='UTF-8'?>
<Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" xmlns:ve="http://www.vmware.com/schema/ovfenv" oe:id="" ve:vCenterId="vm-6863">
  <PlatformSection>
    <Kind>VMware ESXi</Kind>
    <Version>6.0.0</Version>
    <Vendor>VMware, Inc.</Vendor>
    <Locale>en</Locale>
  </PlatformSection>
  <PropertySection>
    <Property oe:key="dns" oe:value="test.myeng.com"/>
    <Property oe:key="dns-servers" oe:value="8.8.8.8"/>
    <Property oe:key="gateway" oe:value="192.168.100.1"/>
    <Property oe:key="mask" oe:value="255.255.255.0"/>
  </PropertySection>
  <ve:EthernetAdapterSection>
    <ve:Adapter ve:mac="00:50:56:90:2d:a3" ve:network="VM Network" ve:unitNumber="7"/>
  </ve:EthernetAdapterSection>
</Environment>

Reading elements from the <PlatformSection> seems easy enough but I cannot figure out how to read a specific Property from the <PropertySection>

- hosts: localhost
  tasks:
  - name: Read XML
    xml:
      path: /tmp/input.xml
      xpath: /a:Environment/a:PlatformSection/a:Version
      content: text
      namespaces:
        a: http://schemas.dmtf.org/ovf/environment/1
    register: xml_out

  - debug: var=xml_out
linuxfan
  • 1,110
  • 2
  • 17
  • 29

2 Answers2

2

larsks' solution seems spot on for anyone working with VMware's vApp properties in Ansible.

However, I think the solution only works when running Ansible on python2; with python3, the syntax item.values().0 is not supported.

The following code got this working with python3:

- ansible.builtin.set_fact:
    properties: >-
      {{ properties | default({}) |
      combine({((item.values() | list)[0].values() | list)[0]:
      ((item.values() | list)[0].values() | list)[1]})
      }}
  loop: "{{ xml_out.matches }}"
1

We can use an xpath expression like this to return all of the property values:

xpath: '/a:Environment/a:PropertySection/a:Property'

Unfortunately, the return value from the xml module in this situation is a little hard to work with; we get back a data structure like this:

ok: [localhost] => {                                                                                                                                                                  [53/7069]
    "xml_out.matches": [                               
        {                                                                   
            "{http://schemas.dmtf.org/ovf/environment/1}Property": {
                "{http://schemas.dmtf.org/ovf/environment/1}key": "dns", 
                "{http://schemas.dmtf.org/ovf/environment/1}value": "test.myeng.com"
            }                                                                                                                                                                                 
        }, 
        {
            "{http://schemas.dmtf.org/ovf/environment/1}Property": {                                                                                                                           
                "{http://schemas.dmtf.org/ovf/environment/1}key": "dns-servers", 
                "{http://schemas.dmtf.org/ovf/environment/1}value": "8.8.8.8"                                                                                                                  
            }  
        }, 
        {                                                                                                                                                                                      
            "{http://schemas.dmtf.org/ovf/environment/1}Property": {                                                                                                                                           "{http://schemas.dmtf.org/ovf/environment/1}key": "gateway", 
                "{http://schemas.dmtf.org/ovf/environment/1}value": "192.168.100.1"                                                                                                                        }            
        },                                                                                                                                                                                             {                  
            "{http://schemas.dmtf.org/ovf/environment/1}Property": {                                                                                                                                           "{http://schemas.dmtf.org/ovf/environment/1}key": "mask", 
                "{http://schemas.dmtf.org/ovf/environment/1}value": "255.255.255.0"
            }                                                                                                                                                                                  
        }           
    ]         
}                                

Yuck, right? But with a little hackery we can massage that into a more useful structure:

---
- hosts: localhost
  gather_facts: false
  tasks:
    - xml:
        path: ./data.xml
        xpath: '/a:Environment/a:PropertySection/a:Property'
        content: attribute
        namespaces:
          a: http://schemas.dmtf.org/ovf/environment/1
      register: xml_out

    - set_fact:
        properties: >-
          {{ properties|default({})|
          combine({item.values().0['{http://schemas.dmtf.org/ovf/environment/1}key']:
          item.values().0['{http://schemas.dmtf.org/ovf/environment/1}value']})
          }}
      loop: "{{ xml_out.matches }}"

    - debug:
        var: properties

As you can see from the output of this playbook, we now have a simple dictionary:

ok: [localhost] => {
    "properties": {
        "dns": "test.myeng.com", 
        "dns-servers": "8.8.8.8", 
        "gateway": "192.168.100.1", 
        "mask": "255.255.255.0"
    }
}

So we can access individual attributes like this:

- debug:
    msg: "The gateway address is {{ properties.gateway }}"

The set_fact task in the above is a little hairy. We're taking advantage of the default and combine filters to build up a dictionary; a simplified version of that might look like:

---
- hosts: localhost
  gather_facts: false
  vars:
    example:
      - [color, red]
      - [name, alice]
      - [size, medium]
  tasks:
    - set_fact:
        properties: "{{ properties|default({})|combine({item.0: item.1}) }}"
      loop: "{{ example }}"

    - debug:
        var: properties
larsks
  • 277,717
  • 41
  • 399
  • 399