Search code examples
xmlansiblenamespaces

How do you edit XML file with Ansible module community.general.xml


Using the Ansible module community.general.xml, I am attempting to add a child node to the following XML data: (saved into namespace-test.xml)

<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xi="http://www.w3.org/2001/XInclude" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
  <core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq:core ">
  </core>
</configuration>

The data I would like to add, under the core node, is:

<xi:include href="/conf/xmlconfig-to-include.xml"/>

The Ansible task I am running is:

- name: include modular XML configuration file
  community.general.xml:
    path: namespace-test.xml
    xpath: /conf:configuration/core:core
    input_type: xml
    add_children:
      - '<xi:include href="/conf/xmlconfig-to-include.xml"/>'
    namespaces:
      conf: urn:activemq
      core: urn:activemq:core
      xi: "http://www.w3.org/2001/XInclude"
    pretty_print: yes

I am expecting to get this result:

<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xi="http://www.w3.org/2001/XInclude" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
  <core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq:core ">
    <xi:include href="/conf/xmlconfig-to-include.xml"/>
  </core>
</configuration>

But I am getting the following error message, instead:

Error while parsing child element: Namespace prefix xi on include is not defined


More information after the very usefull options suggested by β.εηοιτ.βε.

The idempotent option did not meet the additionnal requirements (non-specified initially) that I need more than one include in the XML block.

This is really what I was aiming for:

<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xi="http://www.w3.org/2001/XInclude" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
  <core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq:core ">
    <xi:include href="/conf/addresses.xml"/>
    <xi:include href="/conf/address-settings.xml"/>
    <xi:include href="/conf/security-settings.xml"/>
  </core>
</configuration>

Solution

  • There are really two approaches for your issue at hand:

    1. the highly recommended one: constructing the xpath with the node you are expecting — /conf:configuration/core:core/xi:include — and just adding the required attribute.
    2. in the line with your actual trial, using an add_children, but in a YAML form, because the XML form does not seems to cope well with namespace reference.

    Why is the first one highly recommended? Because it makes your task idempotent, so, re-running your playbook won't keep on adding the same node over and over again, if it exists with the right attribute already.

    So, here is the idempotent task:

    - name: include modular XML configuration file
      community.general.xml:
        path: namespace-test.xml
        xpath: /conf:configuration/core:core/xi:include
        attribute: href
        value: /conf/xmlconfig-to-include.xml
        namespaces:
          conf: urn:activemq
          core: urn:activemq:core
          xi: "http://www.w3.org/2001/XInclude"
        pretty_print: yes
    

    Which would give your expected XML.


    And regarding the fact that you do need multiple include, the trick, then is to further specialize your xpath expression, for example:

    /conf:configuration
      /core:core
        /xi:include[@href='/conf/xmlconfig-to-include.xml']
    

    So, your task to add the three include nodes could end up in a loop:

    - name: include modular XML configuration file
      community.general.xml:
        path: namespace-test.xml
        xpath: /conf:configuration/core:core/xi:include[@href='{{ item }}']
        namespaces:
          conf: urn:activemq
          core: urn:activemq:core
          xi: "http://www.w3.org/2001/XInclude"
        pretty_print: yes
      loop:
        - /conf/addresses.xml
        - /conf/address-settings.xml
        - /conf/security-settings.xml
    

    And if you really want the non-idempotent option, here it is:

    - name: include modular XML configuration file
      community.general.xml:
        path: namespace-test.xml
        xpath: /conf:configuration/core:core
        add_children:
          - "{http://www.w3.org/2001/XInclude}include":
              href: /conf/xmlconfig-to-include.xml
        namespaces:
          conf: urn:activemq
          core: urn:activemq:core
          xi: "http://www.w3.org/2001/XInclude"
        pretty_print: yes