如何在 ansible 中解析 URI 的 XML 响应

How to Parse the XML response of a URI in ansible

有调用应用程序网络服务的代码。

 - uri:
         url: http://10.164.52.61:8080/ems/v74/ws/customer.ws?customerRefId=f4XXXb15d69c3
         method: GET
         content_as_json: true
         password: admin
         user: admin
         validate_certs: no
         return_content: yes
         HEADER_Cookie: "{{login.set_cookie}}"
        register: customerId

      - debug:
          var: customerId.content

示例响应是

"customerId.content": "<listResponse type=\"customer\" count=\"1\"><instance customerId=\"28\" name=\"abc\" customerRefId=\"xyz\" refId1=\"12\" type=\"org\" enabled=\"true\" phone=\"\" fax=\"\" billingZip=\"\" billingAddress=\"\" billingCity=\"\" billingCountry=\"\" billingState=\"\" vendor=\"1\" defaultEmail=\"test\" defaultContactName=\"test\"/></listResponse>"

我想在我的下一个代码块中访问列表响应。就像我只需要 "customerId" 的值一样。如何使用 anisble

实现这一点

开箱即用的 Ansible 中没有 XML 支持。
您可以使用正则表达式过滤器在 XML 数据中进行一些简单的搜索。
在你的例子中:

- debug: msg="{{ customerId.content | regex_findall('customerId=\"(\d+)\"') }}"

将搜索 customerId=\"<number>\" 字符串和 return 引号中的数字列表。

更新:完整的示例:

- hosts: localhost
  gather_facts: no
  vars:
    myvar: "<listResponse type=\"customer\" count=\"1\"><instance customerId=\"28\" name=\"abc\" customerRefId=\"xyz\" refId1=\"12\" type=\"org\" enabled=\"true\" phone=\"\" fax=\"\" billingZip=\"\" billingAddress=\"\" billingCity=\"\" billingCountry=\"\" billingState=\"\" vendor=\"1\" defaultEmail=\"test\" defaultContactName=\"test\"/></listResponse>"
  tasks:
    - debug: msg="{{ myvar | regex_findall('customerId=\"(\d+)\"') }}"

我用ansible 2.1.1.0.

为此值得一看 ansible-xml。它不是 Ansible 的一部分,但它是一个非常有用的解析模块 xml,它在后台使用 lxml Python 库。

从 Ansible 2.4 开始,您可以这样做:

- get_url:
    url: http://10.164.52.61:8080/ems/v74/ws/customer.ws?customerRefId=f4XXXb15d69c3
    dest: ./response.xml
    url_password: admin
    url_user: admin
    validate_certs: no
    headers:
      Cookie: "{{login.set_cookie}}"

- xml:
    path: ./response.xml
    xpath: /listResponse/instance
    content: attribute
  register: instance_attributes

- debug:
    var: instance_attributes.matches.0.instance.customerId

这类似于 sebaszw 的回答,但是不需要将 xml 响应写入文件,而是将其存储在变量中。

- uri:
    url: http://10.164.52.61:8080/ems/v74/ws/customer.ws?customerRefId=f4XXXb15d69c3
    method: GET
    content_as_json: true
    password: admin
    user: admin
    validate_certs: no
    return_content: yes
    HEADER_Cookie: "{{login.set_cookie}}"
  register: customerId

- xml:
    xmlstring: "{{customerId.content}}"
    xpath: /listResponse/instance
    content: attribute
  register: instance_attributes

- debug:
    var: instance_attributes.matches.0.instance.customerId 

我刚刚在 uri 模块中添加了 XML 解析支持,因为我也需要它。 https://github.com/ansible/ansible/pull/53045

就像JSON支持一样,它将return一个'xml'键和一个由XML内容组成的字典,以方便访问中的数据有效负载。

您的示例如下所示:

 - uri:
     url: http://10.164.52.61:8080/ems/v74/ws/customer.ws?customerRefId=f4XXXb15d69c3
     method: GET
     password: admin
     user: admin
     validate_certs: no
     HEADER_Cookie: "{{login.set_cookie}}"
    register: customerId

  - debug:
      var: customerId.xml.listResponse.instance['@customerId']

customerId.xml 中的输出将是:

{
    'listResponse': {
        '@type': 'customer',
        '@count', '1',
        'instance': {
            '@customerId': '28',
            '@name': 'abc'
            '@customerRefId': 'xyz',
            '@refId1': '12',
            '@type': 'org',
            '@enabled': 'true',
            '@phone': '',
            '@fax': '',
            '@billingZip': '',
            '@billingAddress': '',
            '@billingCity': '',
            '@billingCountry': '',
            '@billingState': '',
            '@vendor': '1',
            '@defaultEmail': 'test',
            '@defaultContactName': 'test'
        }
    }
}

因为这相当复杂,所以我想我会 post 我的 Ansible - XML 用于解析 SOAP 响应的参数:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <S:Body xmlns:ns2="http://ws.coverity.com/v9">
      <ns2:getLdapServerDomainsResponse>
        <return>
            <name>soapui_test</name>
        </return>
      </ns2:getLdapServerDomainsResponse>
   </S:Body>
</S:Envelope>

诀窍是让命名空间和 xpath 正确。另外,因为我想要我需要的实际名称 content: text

- name: Extract the name from the SOAP
  xml:
    xmlstring: "{{ ldap_response.content }}"
    xpath: "/S:Envelope/S:Body/ns2:getLdapServerDomainsResponse/return/name"
    namespaces:
      S: "http://schemas.xmlsoap.org/soap/envelope/"
      SOAP-ENV: "http://schemas.xmlsoap.org/soap/envelope/"
      ns2: "http://ws.coverity.com/v9"
    content: text
  register: ldap_name

我仍然不明白为什么最后的 xpath 部分不能像 Ansible - XML docs 示例所建议的那样与 .../ns2:return/ns2:name 一起工作。上面用(简化)填充ldap_name

"ldap_name": {
    "matches": [
        {
            "name": "soapui_test"
        }
    ],
}