Remove incidental_xml integration tests (#71917)

* Add explicit integration tests to cover incidental_xml coverage

* normalize diff

* More normalization

* diff without changed

* ci_complete ci_coverage

* nuke incidental_xml

* ci_complete ci_coverage
This commit is contained in:
Matt Martz 2020-09-29 08:39:44 -05:00 committed by GitHub
parent cbd3df369f
commit f2512f7931
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 103 additions and 2856 deletions

View file

@ -50,6 +50,23 @@ ok: [testhost] => {
"item": 1
}
TASK [copy] ********************************************************************
changed: [testhost]
TASK [replace] *****************************************************************
--- before: .../test_diff.txt
+++ after: .../test_diff.txt
@@ -1 +1 @@
-foo
\ No newline at end of file
+bar
\ No newline at end of file
changed: [testhost]
TASK [replace] *****************************************************************
ok: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -76,5 +93,5 @@ ok: [testhost] => {
}
PLAY RECAP *********************************************************************
testhost : ok=16 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2
testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2

View file

@ -47,6 +47,23 @@ ok: [testhost] => {
"item": 1
}
TASK [copy] ********************************************************************
changed: [testhost]
TASK [replace] *****************************************************************
--- before: .../test_diff.txt
+++ after: .../test_diff.txt
@@ -1 +1 @@
-foo
\ No newline at end of file
+bar
\ No newline at end of file
changed: [testhost]
TASK [replace] *****************************************************************
ok: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -73,5 +90,5 @@ ok: [testhost] => {
}
PLAY RECAP *********************************************************************
testhost : ok=16 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2
testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2

View file

@ -39,6 +39,20 @@ changed: [testhost]
TASK [include_tasks] ***********************************************************
included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1)
TASK [copy] ********************************************************************
changed: [testhost]
TASK [replace] *****************************************************************
--- before: .../test_diff.txt
+++ after: .../test_diff.txt
@@ -1 +1 @@
-foo
\ No newline at end of file
+bar
\ No newline at end of file
changed: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -57,5 +71,5 @@ TASK [Include some tasks] ******************************************************
included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1)
PLAY RECAP *********************************************************************
testhost : ok=16 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2
testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2

View file

@ -46,6 +46,23 @@ ok: [testhost] => {
"item": 1
}
TASK [copy] ********************************************************************
changed: [testhost]
TASK [replace] *****************************************************************
--- before: .../test_diff.txt
+++ after: .../test_diff.txt
@@ -1 +1 @@
-foo
\ No newline at end of file
+bar
\ No newline at end of file
changed: [testhost]
TASK [replace] *****************************************************************
ok: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -72,5 +89,5 @@ ok: [testhost] => {
}
PLAY RECAP *********************************************************************
testhost : ok=16 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2
testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2

View file

@ -35,6 +35,20 @@ changed: [testhost]
TASK [include_tasks] ***********************************************************
included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1)
TASK [copy] ********************************************************************
changed: [testhost]
TASK [replace] *****************************************************************
--- before: .../test_diff.txt
+++ after: .../test_diff.txt
@@ -1 +1 @@
-foo
\ No newline at end of file
+bar
\ No newline at end of file
changed: [testhost]
RUNNING HANDLER [Test handler 1] ***********************************************
changed: [testhost]
@ -53,5 +67,5 @@ TASK [Include some tasks] ******************************************************
included: .../test/integration/targets/callback_default/include_me.yml for testhost => (item=1)
PLAY RECAP *********************************************************************
testhost : ok=16 changed=9 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2
testhost : ok=19 changed=11 unreachable=0 failed=0 skipped=1 rescued=1 ignored=2

View file

@ -24,6 +24,8 @@ run_test() {
# Scrub deprication warning that shows up in Python 2.6 on CentOS 6
sed -i -e '/RandomPool_DeprecationWarning/d' "${OUTFILE}.${testname}.stderr"
sed -i -e 's/included: .*\/test\/integration/included: ...\/test\/integration/g' "${OUTFILE}.${testname}.stdout"
sed -i -e 's/@@ -1,1 +1,1 @@/@@ -1 +1 @@/g' "${OUTFILE}.${testname}.stdout"
sed -i -e 's/: .*\/test_diff\.txt/: ...\/test_diff.txt/g' "${OUTFILE}.${testname}.stdout"
diff -u "${ORIGFILE}.${testname}.stdout" "${OUTFILE}.${testname}.stdout" || diff_failure
diff -u "${ORIGFILE}.${testname}.stderr" "${OUTFILE}.${testname}.stderr" || diff_failure

View file

@ -61,6 +61,23 @@
loop:
- 1
- copy:
dest: '{{ lookup("env", "OUTPUT_DIR") }}/test_diff.txt'
content: foo
- replace:
path: '{{ lookup("env", "OUTPUT_DIR") }}/test_diff.txt'
regexp: '^foo$'
replace: bar
diff: true
- replace:
path: '{{ lookup("env", "OUTPUT_DIR") }}/test_diff.txt'
regexp: '^bar$'
replace: baz
diff: true
changed_when: false
handlers:
- name: Test handler 1
command: echo foo

View file

@ -1,4 +0,0 @@
destructive
shippable/posix/incidental
skip/aix
skip/power/centos

View file

@ -1,13 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Толстый бар</name>
<beers>
<beer>Окское</beer>
<beer>Невское</beer>
</beers>
<rating subjective="да">десять</rating>
<website>
<mobilefriendly/>
<address>http://tolstyybar.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar">
<name>Tasty Beverage Co.</name>
<beers xmlns="http://test.beers">
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating xmlns="http://test.rating" attr:subjective="true">10</rating>
<website xmlns="http://test.website">
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer>Окское</beer></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer>Old Rasputin</beer></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer>Natty Lite</beer><beer>Miller Lite</beer><beer>Coors Lite</beer></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,17 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Old Rasputin</beer>
<beer>Old Motor Oil</beer>
<beer>Old Curmudgeon</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,17 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>Old Rasputin</beer>
<beer>Old Motor Oil</beer>
<beer>Old Curmudgeon</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer name="Окское" type="экстра"/></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer name="Ansible Brew" type="light"/></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,32 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer color="red">George Killian's Irish Red</beer>
<beer origin="CZ" color="blonde">Pilsner Urquell</beer>
</beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
<validxhtml validateon=""/>
</website>
<phonenumber>555-555-1234</phonenumber>
<owner dob="1976-04-12">
<name>
<last>Smith</last>
<first>John</first>
<middle>Q</middle>
</name>
</owner>
<website_bis>
<validxhtml validateon=""/>
</website_bis>
<testnormalelement>xml tag with no special characters</testnormalelement>
<test-with-dash>xml tag with dashes</test-with-dash>
<test-with-dash.and.dot>xml tag with dashes and dots</test-with-dash.and.dot>
<test-with.dash_and.dot_and-underscores>xml tag with dashes, dots and underscores</test-with.dash_and.dot_and-underscores>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar">
<name>Tasty Beverage Co.</name>
<beers xmlns="http://test.beers">
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer>Old Rasputin</beer></beers>
<rating xmlns="http://test.rating" attr:subjective="true">10</rating>
<website xmlns="http://test.website">
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,15 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
<beer>Old Rasputin</beer>
</beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating>10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,13 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar">
<name>Tasty Beverage Co.</name>
<beers xmlns="http://test.beers">
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating xmlns="http://test.rating">10</rating>
<website xmlns="http://test.website">
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,13 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar">
<name>Tasty Beverage Co.</name>
<beers xmlns="http://test.beers">
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<website xmlns="http://test.website">
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="нет">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="false">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,11 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer alcohol="0.5" name="90 Minute IPA"><Water liter="0.2" quantity="200g"/><Starch quantity="10g"/><Hops quantity="50g"/><Yeast quantity="20g"/></beer><beer alcohol="0.3" name="Harvest Pumpkin Ale"><Water liter="0.2" quantity="200g"/><Hops quantity="25g"/><Yeast quantity="20g"/></beer></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,11 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Окское</beer><beer>Невское</beer></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,11 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>90 Minute IPA</beer><beer>Harvest Pumpkin Ale</beer></beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="true">10</rating>
<website>
<mobilefriendly/>
<address></address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="true">пять</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
<rating>пять</rating></business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business type="bar">
<name>Tasty Beverage Co.</name>
<beers>
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating subjective="true">5</rating>
<website>
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
<rating>5</rating></business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar">
<name>Tasty Beverage Co.</name>
<beers xmlns="http://test.beers">
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating xmlns="http://test.rating" attr:subjective="false">10</rating>
<website xmlns="http://test.website">
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,14 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<business xmlns="http://test.business" xmlns:attr="http://test.attribute" type="bar">
<name>Tasty Beverage Co.</name>
<beers xmlns="http://test.beers">
<beer>Rochefort 10</beer>
<beer>St. Bernardus Abbot 12</beer>
<beer>Schlitz</beer>
</beers>
<rating xmlns="http://test.rating" attr:subjective="true">11</rating>
<website xmlns="http://test.website">
<mobilefriendly/>
<address>http://tastybeverageco.com</address>
</website>
</business>

View file

@ -1,67 +0,0 @@
- name: Gather facts
setup:
- name: Install lxml (FreeBSD)
package:
name: '{{ "py27-lxml" if ansible_python.version.major == 2 else "py36-lxml" }}'
state: present
when: ansible_os_family == "FreeBSD"
# Needed for MacOSX !
- name: Install lxml
pip:
name: lxml
state: present
# when: ansible_os_family == "Darwin"
- name: Get lxml version
command: "{{ ansible_python_interpreter }} -c 'from lxml import etree; print(\".\".join(str(v) for v in etree.LXML_VERSION))'"
register: lxml_version
- name: Set lxml capabilities as variables
set_fact:
# NOTE: Some tests require predictable element attribute order,
# which is only guaranteed starting from lxml v3.0alpha1
lxml_predictable_attribute_order: '{{ lxml_version.stdout is version("3", ">=") }}'
# NOTE: The xml module requires at least lxml v2.3.0
lxml_xpath_attribute_result_attrname: '{{ lxml_version.stdout is version("2.3.0", ">=") }}'
- name: Only run the tests when lxml v2.3.0+
when: lxml_xpath_attribute_result_attrname
block:
- include_tasks: test-add-children-elements.yml
- include_tasks: test-add-children-from-groupvars.yml
- include_tasks: test-add-children-insertafter.yml
- include_tasks: test-add-children-insertbefore.yml
- include_tasks: test-add-children-with-attributes.yml
- include_tasks: test-add-element-implicitly.yml
- include_tasks: test-count.yml
- include_tasks: test-mutually-exclusive-attributes.yml
- include_tasks: test-remove-attribute.yml
- include_tasks: test-remove-element.yml
- include_tasks: test-set-attribute-value.yml
- include_tasks: test-set-children-elements.yml
- include_tasks: test-set-children-elements-level.yml
- include_tasks: test-set-element-value.yml
- include_tasks: test-set-element-value-empty.yml
- include_tasks: test-pretty-print.yml
- include_tasks: test-pretty-print-only.yml
- include_tasks: test-add-namespaced-children-elements.yml
- include_tasks: test-remove-namespaced-attribute.yml
- include_tasks: test-set-namespaced-attribute-value.yml
- include_tasks: test-set-namespaced-element-value.yml
- include_tasks: test-set-namespaced-children-elements.yml
- include_tasks: test-get-element-content.yml
- include_tasks: test-xmlstring.yml
- include_tasks: test-children-elements-xml.yml
# Unicode tests
- include_tasks: test-add-children-elements-unicode.yml
- include_tasks: test-add-children-with-attributes-unicode.yml
- include_tasks: test-set-attribute-value-unicode.yml
- include_tasks: test-count-unicode.yml
- include_tasks: test-get-element-content.yml
- include_tasks: test-set-children-elements-unicode.yml
- include_tasks: test-set-element-value-unicode.yml

View file

@ -1,29 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
add_children:
- beer: Окское
register: add_children_elements_unicode
- name: Compare to expected result
copy:
src: results/test-add-children-elements-unicode.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- add_children_elements_unicode.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-add-children-elements-unicode.xml /tmp/ansible-xml-beers.xml

View file

@ -1,29 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
add_children:
- beer: Old Rasputin
register: add_children_elements
- name: Compare to expected result
copy:
src: results/test-add-children-elements.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- add_children_elements.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml

View file

@ -1,28 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
add_children: '{{ bad_beers }}'
register: add_children_from_groupvars
- name: Compare to expected result
copy:
src: results/test-add-children-from-groupvars.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- add_children_from_groupvars.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-add-children-from-groupvars.xml /tmp/ansible-xml-beers.xml

View file

@ -1,32 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]'
insertafter: yes
add_children:
- beer: Old Rasputin
- beer: Old Motor Oil
- beer: Old Curmudgeon
pretty_print: yes
register: add_children_insertafter
- name: Compare to expected result
copy:
src: results/test-add-children-insertafter.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- add_children_insertafter.changed == true
- comparison.changed == false # identical

View file

@ -1,32 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]'
insertbefore: yes
add_children:
- beer: Old Rasputin
- beer: Old Motor Oil
- beer: Old Curmudgeon
pretty_print: yes
register: add_children_insertbefore
- name: Compare to expected result
copy:
src: results/test-add-children-insertbefore.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- add_children_insertbefore.changed == true
- comparison.changed == false # identical

View file

@ -1,31 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
add_children:
- beer:
name: Окское
type: экстра
register: add_children_with_attributes_unicode
- name: Compare to expected result
copy:
src: results/test-add-children-with-attributes-unicode.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- add_children_with_attributes_unicode.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-add-children-with-attributes-unicode.xml /tmp/ansible-xml-beers.xml

View file

@ -1,35 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
add_children:
- beer:
name: Ansible Brew
type: light
register: add_children_with_attributes
- name: Compare to expected result
copy:
src: results/test-add-children-with-attributes.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
# NOTE: This test may fail if lxml does not support predictable element attribute order
# So we filter the failure out for these platforms (e.g. CentOS 6)
# The module still works fine, we simply are not comparing as smart as we should.
- name: Test expected result
assert:
that:
- add_children_with_attributes.changed == true
- comparison.changed == false # identical
when: lxml_predictable_attribute_order
#command: diff -u {{ role_path }}/results/test-add-children-with-attributes.xml /tmp/ansible-xml-beers.xml

View file

@ -1,237 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers-implicit.xml
- name: Add a phonenumber element to the business element. Implicit mkdir -p behavior where applicable
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/phonenumber
value: 555-555-1234
- name: Add a owner element to the business element, testing implicit mkdir -p behavior 1/2
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/owner/name/last
value: Smith
- name: Add a owner element to the business element, testing implicit mkdir -p behavior 2/2
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/owner/name/first
value: John
- name: Add a validxhtml element to the website element. Note that ensure is present by default and while value defaults to null for elements, if one doesn't specify it we don't know what to do.
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/website/validxhtml
- name: Add an empty validateon attribute to the validxhtml element. This actually makes the previous example redundant because of the implicit parent-node creation behavior.
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/website/validxhtml/@validateon
- name: Add an empty validateon attribute to the validxhtml element. Actually verifies the implicit parent-node creation behavior.
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/website_bis/validxhtml/@validateon
- name: Add an attribute with a value
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/owner/@dob='1976-04-12'
- name: Add an element with a value, alternate syntax
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/beers/beer/text()="George Killian's Irish Red" # note the quote within an XPath string thing
- name: Add an element without special characters
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/testnormalelement
value: xml tag with no special characters
pretty_print: yes
- name: Add an element with dash
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/test-with-dash
value: xml tag with dashes
pretty_print: yes
- name: Add an element with dot
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/test-with-dash.and.dot
value: xml tag with dashes and dots
pretty_print: yes
- name: Add an element with underscore
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/test-with.dash_and.dot_and-underscores
value: xml tag with dashes, dots and underscores
pretty_print: yes
- name: Add an attribute on a conditional element
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/beers/beer[text()="George Killian's Irish Red"]/@color='red'
- name: Add two attributes on a conditional element
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/beers/beer[text()="Pilsner Urquell" and @origin='CZ']/@color='blonde'
- name: Add a owner element to the business element, testing implicit mkdir -p behavior 3/2 -- complex lookup
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/owner/name[first/text()='John']/middle
value: Q
- name: Pretty Print this!
xml:
file: /tmp/ansible-xml-beers-implicit.xml
pretty_print: yes
- name: Compare to expected result
copy:
src: results/test-add-element-implicitly.yml
dest: /tmp/ansible-xml-beers-implicit.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-add-element-implicitly.yml /tmp/ansible-xml-beers-implicit.xml
# Now we repeat the same, just to ensure proper use of namespaces
- name: Add a phonenumber element to the business element. Implicit mkdir -p behavior where applicable
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:phonenumber
value: 555-555-1234
namespaces:
a: http://example.com/some/namespace
- name: Add a owner element to the business element, testing implicit mkdir -p behavior 1/2
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:owner/a:name/a:last
value: Smith
namespaces:
a: http://example.com/some/namespace
- name: Add a owner element to the business element, testing implicit mkdir -p behavior 2/2
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:owner/a:name/a:first
value: John
namespaces:
a: http://example.com/some/namespace
- name: Add a validxhtml element to the website element. Note that ensure is present by default and while value defaults to null for elements, if one doesn't specify it we don't know what to do.
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:website/a:validxhtml
namespaces:
a: http://example.com/some/namespace
- name: Add an empty validateon attribute to the validxhtml element. This actually makes the previous example redundant because of the implicit parent-node creation behavior.
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:website/a:validxhtml/@a:validateon
namespaces:
a: http://example.com/some/namespace
- name: Add an empty validateon attribute to the validxhtml element. Actually verifies the implicit parent-node creation behavior.
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:website_bis/a:validxhtml/@a:validateon
namespaces:
a: http://example.com/some/namespace
- name: Add an attribute with a value
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:owner/@a:dob='1976-04-12'
namespaces:
a: http://example.com/some/namespace
- name: Add an element with a value, alternate syntax
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:beers/a:beer/text()="George Killian's Irish Red" # note the quote within an XPath string thing
namespaces:
a: http://example.com/some/namespace
- name: Add an attribute on a conditional element
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:beers/a:beer[text()="George Killian's Irish Red"]/@a:color='red'
namespaces:
a: http://example.com/some/namespace
- name: Add two attributes on a conditional element
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:beers/a:beer[text()="Pilsner Urquell" and @a:origin='CZ']/@a:color='blonde'
namespaces:
a: http://example.com/some/namespace
- name: Add a owner element to the business element, testing implicit mkdir -p behavior 3/2 -- complex lookup
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/a:owner/a:name[a:first/text()='John']/a:middle
value: Q
namespaces:
a: http://example.com/some/namespace
- name: Add an element without special characters
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/testnormalelement
value: xml tag with no special characters
pretty_print: yes
namespaces:
a: http://example.com/some/namespace
- name: Add an element with dash
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/test-with-dash
value: xml tag with dashes
pretty_print: yes
namespaces:
a: http://example.com/some/namespace
- name: Add an element with dot
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/test-with-dash.and.dot
value: xml tag with dashes and dots
pretty_print: yes
namespaces:
a: http://example.com/some/namespace
- name: Add an element with underscore
xml:
file: /tmp/ansible-xml-beers-implicit.xml
xpath: /business/test-with.dash_and.dot_and-underscores
value: xml tag with dashes, dots and underscores
pretty_print: yes
namespaces:
a: http://example.com/some/namespace
- name: Pretty Print this!
xml:
file: /tmp/ansible-xml-beers-implicit.xml
pretty_print: yes

View file

@ -1,32 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
- name: Add namespaced child element
xml:
path: /tmp/ansible-xml-namespaced-beers.xml
xpath: /bus:business/ber:beers
namespaces:
bus: http://test.business
ber: http://test.beers
add_children:
- beer: Old Rasputin
register: add_namespaced_children_elements
- name: Compare to expected result
copy:
src: results/test-add-namespaced-children-elements.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- add_namespaced_children_elements.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-add-namespaced-children-elements.xml /tmp/ansible-xml-namespaced-beers.xml

View file

@ -1,30 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element with xml format
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
input_type: xml
add_children:
- '<beer>Old Rasputin</beer>'
register: children_elements
- name: Compare to expected result
copy:
src: results/test-add-children-elements.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- children_elements.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml

View file

@ -1,19 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers-unicode.xml
dest: /tmp/ansible-xml-beers-unicode.xml
- name: Count child element
xml:
path: /tmp/ansible-xml-beers-unicode.xml
xpath: /business/beers/beer
count: yes
register: beers
- name: Test expected result
assert:
that:
- beers.changed == false
- beers.count == 2

View file

@ -1,19 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add child element
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers/beer
count: yes
register: beers
- name: Test expected result
assert:
that:
- beers.changed == false
- beers.count == 3

View file

@ -1,32 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers-unicode.xml
dest: /tmp/ansible-xml-beers-unicode.xml
- name: Get element attributes
xml:
path: /tmp/ansible-xml-beers-unicode.xml
xpath: /business/rating
content: attribute
register: get_element_attribute
- name: Test expected result
assert:
that:
- get_element_attribute.changed == false
- get_element_attribute.matches[0]['rating'] is defined and get_element_attribute.matches[0]['rating']['subjective'] == 'да'
- name: Get element text
xml:
path: /tmp/ansible-xml-beers-unicode.xml
xpath: /business/rating
content: text
register: get_element_text
- name: Test expected result
assert:
that:
- get_element_text.changed == false
- get_element_text.matches[0]['rating'] == 'десять'

View file

@ -1,52 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Get element attributes
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
content: attribute
register: get_element_attribute
- name: Test expected result
assert:
that:
- get_element_attribute.changed == false
- get_element_attribute.matches[0]['rating'] is defined
- get_element_attribute.matches[0]['rating']['subjective'] == 'true'
# TODO: Remove this in Ansible v2.12 when this incorrect use of attribute is deprecated
- name: Get element attributes
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
content: attribute
attribute: subjective
register: get_element_attribute_wrong
- name: Test expected result
assert:
that:
- get_element_attribute_wrong.changed == false
- get_element_attribute_wrong.matches[0]['rating'] is defined
- get_element_attribute_wrong.matches[0]['rating']['subjective'] == 'true'
- get_element_attribute_wrong.deprecations is defined
- get_element_attribute_wrong.deprecations[0].msg == "Parameter 'attribute=subjective' is ignored when using 'content=attribute' only 'xpath' is used. Please remove entry."
- get_element_attribute_wrong.deprecations[0].version == '2.12'
- name: Get element text
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
content: text
register: get_element_text
- name: Test expected result
assert:
that:
- get_element_text.changed == false
- get_element_text.matches[0]['rating'] == '10'

View file

@ -1,22 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Specify both children to add and a value
xml:
path: /tmp/ansible-xml-beers.xml
add_children:
- child01
- child02
value: conflict!
register: module_output
ignore_errors: yes
- name: Test expected result
assert:
that:
- module_output.changed == false
- module_output.failed == true

View file

@ -1,29 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml.orig
- name: Remove spaces from test fixture
shell: sed 's/^[ ]*//g' < /tmp/ansible-xml-beers.xml.orig > /tmp/ansible-xml-beers.xml
- name: Pretty print without modification
xml:
path: /tmp/ansible-xml-beers.xml
pretty_print: yes
register: pretty_print_only
- name: Compare to expected result
copy:
src: results/test-pretty-print-only.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- pretty_print_only.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml

View file

@ -1,30 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Pretty print
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
pretty_print: yes
add_children:
- beer: Old Rasputin
register: pretty_print
- name: Compare to expected result
copy:
src: results/test-pretty-print.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- pretty_print.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml

View file

@ -1,28 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Remove '/business/rating/@subjective'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating/@subjective
state: absent
register: remove_attribute
- name: Compare to expected result
copy:
src: results/test-remove-attribute.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- remove_attribute.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-remove-attribute.xml /tmp/ansible-xml-beers.xml

View file

@ -1,28 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Remove '/business/rating'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
state: absent
register: remove_element
- name: Compare to expected result
copy:
src: results/test-remove-element.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- remove_element.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-beers.xml

View file

@ -1,33 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
- name: Remove namespaced '/bus:business/rat:rating/@attr:subjective'
xml:
path: /tmp/ansible-xml-namespaced-beers.xml
xpath: /bus:business/rat:rating/@attr:subjective
namespaces:
bus: http://test.business
ber: http://test.beers
rat: http://test.rating
attr: http://test.attribute
state: absent
register: remove_namespaced_attribute
- name: Compare to expected result
copy:
src: results/test-remove-namespaced-attribute.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- remove_element.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-remove-namespaced-attribute.xml /tmp/ansible-xml-namespaced-beers.xml

View file

@ -1,33 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
- name: Remove namespaced '/bus:business/rat:rating'
xml:
path: /tmp/ansible-xml-namespaced-beers.xml
xpath: /bus:business/rat:rating
namespaces:
bus: http://test.business
ber: http://test.beers
rat: http://test.rating
attr: http://test.attribute
state: absent
register: remove_namespaced_element
- name: Compare to expected result
copy:
src: results/test-remove-element.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- remove_namespaced_element.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-namespaced-beers.xml

View file

@ -1,29 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Set '/business/rating/@subjective' to 'нет'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
attribute: subjective
value: нет
register: set_attribute_value_unicode
- name: Compare to expected result
copy:
src: results/test-set-attribute-value-unicode.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_attribute_value_unicode.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-attribute-value-unicode.xml /tmp/ansible-xml-beers.xml

View file

@ -1,29 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Set '/business/rating/@subjective' to 'false'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
attribute: subjective
value: 'false'
register: set_attribute_value
- name: Compare to expected result
copy:
src: results/test-set-attribute-value.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_attribute_value.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-attribute-value.xml /tmp/ansible-xml-beers.xml

View file

@ -1,74 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Set child elements
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
set_children: &children
- beer:
alcohol: "0.5"
name: 90 Minute IPA
_:
- Water:
liter: "0.2"
quantity: 200g
- Starch:
quantity: 10g
- Hops:
quantity: 50g
- Yeast:
quantity: 20g
- beer:
alcohol: "0.3"
name: Harvest Pumpkin Ale
_:
- Water:
liter: "0.2"
quantity: 200g
- Hops:
quantity: 25g
- Yeast:
quantity: 20g
register: set_children_elements_level
- name: Compare to expected result
copy:
src: results/test-set-children-elements-level.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_children_elements_level.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-children-elements-level.xml /tmp/ansible-xml-beers.xml
- name: Set child elements (again)
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
set_children: *children
register: set_children_again
- name: Compare to expected result
copy:
src: results/test-set-children-elements-level.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_children_again.changed == false
- comparison.changed == false # identical

View file

@ -1,46 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Set child elements
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
set_children: &children
- beer: Окское
- beer: Невское
register: set_children_elements_unicode
- name: Compare to expected result
copy:
src: results/test-set-children-elements-unicode.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_children_elements_unicode.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml
- name: Compare to expected result
copy:
src: results/test-set-children-elements-unicode.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_children_again.changed == false
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml

View file

@ -1,53 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Set child elements
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
set_children: &children
- beer: 90 Minute IPA
- beer: Harvest Pumpkin Ale
register: set_children_elements
- name: Compare to expected result
copy:
src: results/test-set-children-elements.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_children_elements.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml
- name: Set child elements (again)
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/beers
set_children: *children
register: set_children_again
- name: Compare to expected result
copy:
src: results/test-set-children-elements.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_children_again.changed == false
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml

View file

@ -1,28 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Set '/business/website/address' to empty string.
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/website/address
value: ''
register: set_element_value_empty
- name: Compare to expected result
copy:
src: results/test-set-element-value-empty.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_element_value_empty.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-element-value-empty.xml /tmp/ansible-xml-beers.xml

View file

@ -1,43 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add 2nd '/business/rating' with value 'пять'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business
add_children:
- rating: пять
- name: Set '/business/rating' to 'пять'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
value: пять
register: set_element_first_run
- name: Set '/business/rating' to 'false'... again
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
value: пять
register: set_element_second_run
- name: Compare to expected result
copy:
src: results/test-set-element-value-unicode.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_element_first_run.changed == true
- set_element_second_run.changed == false
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-element-value-unicode.xml /tmp/ansible-xml-beers.xml

View file

@ -1,43 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-beers.xml
dest: /tmp/ansible-xml-beers.xml
- name: Add 2nd '/business/rating' with value '5'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business
add_children:
- rating: '5'
- name: Set '/business/rating' to '5'
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
value: '5'
register: set_element_first_run
- name: Set '/business/rating' to '5'... again
xml:
path: /tmp/ansible-xml-beers.xml
xpath: /business/rating
value: '5'
register: set_element_second_run
- name: Compare to expected result
copy:
src: results/test-set-element-value.xml
dest: /tmp/ansible-xml-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_element_first_run.changed == true
- set_element_second_run.changed == false
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-element-value.xml /tmp/ansible-xml-beers.xml

View file

@ -1,34 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
- name: Set namespaced '/bus:business/rat:rating/@attr:subjective' to 'false'
xml:
path: /tmp/ansible-xml-namespaced-beers.xml
xpath: /bus:business/rat:rating
namespaces:
bus: http://test.business
ber: http://test.beers
rat: http://test.rating
attr: http://test.attribute
attribute: attr:subjective
value: 'false'
register: set_namespaced_attribute_value
- name: Compare to expected result
copy:
src: results/test-set-namespaced-attribute-value.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- set_namespaced_attribute_value.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-set-namespaced-attribute-value.xml /tmp/ansible-xml-namespaced-beers.xml

View file

@ -1,57 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers-xml.xml
- name: Set child elements
xml:
path: /tmp/ansible-xml-namespaced-beers-xml.xml
xpath: /bus:business/ber:beers
namespaces:
bus: http://test.business
ber: http://test.beers
set_children:
- beer: 90 Minute IPA
- beer: Harvest Pumpkin Ale
- name: Copy state after first set_children
copy:
src: /tmp/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers-1.xml
remote_src: yes
- name: Set child elements again
xml:
path: /tmp/ansible-xml-namespaced-beers-xml.xml
xpath: /bus:business/ber:beers
namespaces:
bus: http://test.business
ber: http://test.beers
set_children:
- beer: 90 Minute IPA
- beer: Harvest Pumpkin Ale
register: set_children_again
- name: Copy state after second set_children
copy:
src: /tmp/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers-2.xml
remote_src: yes
- name: Compare to expected result
copy:
src: /tmp/ansible-xml-namespaced-beers-1.xml
dest: /tmp/ansible-xml-namespaced-beers-2.xml
remote_src: yes
check_mode: yes
diff: yes
register: comparison
#command: diff /tmp/ansible-xml-namespaced-beers-1.xml /tmp/ansible-xml-namespaced-beers-2.xml
- name: Test expected result
assert:
that:
- set_children_again.changed == false # idempotency
- set_namespaced_attribute_value.changed == true
- comparison.changed == false # identical

View file

@ -1,46 +0,0 @@
---
- name: Setup test fixture
copy:
src: fixtures/ansible-xml-namespaced-beers.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
- name: Set namespaced '/bus:business/rat:rating' to '11'
xml:
path: /tmp/ansible-xml-namespaced-beers.xml
namespaces:
bus: http://test.business
ber: http://test.beers
rat: http://test.rating
attr: http://test.attribute
xpath: /bus:business/rat:rating
value: '11'
register: set_element_first_run
- name: Set namespaced '/bus:business/rat:rating' to '11' again
xml:
path: /tmp/ansible-xml-namespaced-beers.xml
namespaces:
bus: http://test.business
ber: http://test.beers
rat: http://test.rating
attr: http://test.attribute
xpath: /bus:business/rat:rating
value: '11'
register: set_element_second_run
- name: Compare to expected result
copy:
src: results/test-set-namespaced-element-value.xml
dest: /tmp/ansible-xml-namespaced-beers.xml
check_mode: yes
diff: yes
register: comparison
#command: diff -u {{ role_path }}/results/test-set-namespaced-element-value.xml /tmp/ansible-xml-namespaced-beers.xml
- name: Test expected result
assert:
that:
- set_element_first_run.changed == true
- set_element_second_run.changed == false
- comparison.changed == false # identical

View file

@ -1,81 +0,0 @@
---
- name: Copy expected results to remote
copy:
src: "results/{{ item }}"
dest: "/tmp/{{ item }}"
with_items:
- test-pretty-print.xml
- test-pretty-print-only.xml
# NOTE: Jinja2 templating eats trailing newlines
- name: Read from xmlstring (not using pretty_print)
xml:
xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}"
xpath: .
register: xmlresponse
- name: Compare to expected result
copy:
content: "{{ xmlresponse.xmlstring }}\n"
dest: '/tmp/test-pretty-print-only.xml'
check_mode: yes
diff: yes
register: comparison
- name: Test expected result
assert:
that:
- xmlresponse.changed == false
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml
# NOTE: Jinja2 templating eats trailing newlines
- name: Read from xmlstring (using pretty_print)
xml:
xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}"
pretty_print: yes
register: xmlresponse
- name: Compare to expected result
copy:
content: '{{ xmlresponse.xmlstring }}'
dest: '/tmp/test-pretty-print-only.xml'
check_mode: yes
diff: yes
register: comparison
# FIXME: This change is related to the newline added by pretty_print
- name: Test expected result
assert:
that:
- xmlresponse.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml
# NOTE: Jinja2 templating eats trailing newlines
- name: Read from xmlstring
xml:
xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}"
xpath: /business/beers
pretty_print: yes
add_children:
- beer: Old Rasputin
register: xmlresponse_modification
- name: Compare to expected result
copy:
content: '{{ xmlresponse_modification.xmlstring }}'
dest: '/tmp/test-pretty-print.xml'
check_mode: yes
diff: yes
register: comparison
# FIXME: This change is related to the newline added by pretty_print
- name: Test expected result
assert:
that:
- xmlresponse_modification.changed == true
- comparison.changed == false # identical
#command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml

View file

@ -1,6 +0,0 @@
# -*- mode: yaml -*
---
bad_beers:
- beer: "Natty Lite"
- beer: "Miller Lite"
- beer: "Coors Lite"

View file

@ -1,966 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2014, Red Hat, Inc.
# Copyright: (c) 2014, Tim Bielawa <tbielawa@redhat.com>
# Copyright: (c) 2014, Magnus Hedemark <mhedemar@redhat.com>
# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: xml
short_description: Manage bits and pieces of XML files or strings
description:
- A CRUD-like interface to managing bits of XML files.
version_added: '2.4'
options:
path:
description:
- Path to the file to operate on.
- This file must exist ahead of time.
- This parameter is required, unless C(xmlstring) is given.
type: path
required: yes
aliases: [ dest, file ]
xmlstring:
description:
- A string containing XML on which to operate.
- This parameter is required, unless C(path) is given.
type: str
required: yes
xpath:
description:
- A valid XPath expression describing the item(s) you want to manipulate.
- Operates on the document root, C(/), by default.
type: str
namespaces:
description:
- The namespace C(prefix:uri) mapping for the XPath expression.
- Needs to be a C(dict), not a C(list) of items.
type: dict
state:
description:
- Set or remove an xpath selection (node(s), attribute(s)).
type: str
choices: [ absent, present ]
default: present
aliases: [ ensure ]
attribute:
description:
- The attribute to select when using parameter C(value).
- This is a string, not prepended with C(@).
type: raw
value:
description:
- Desired state of the selected attribute.
- Either a string, or to unset a value, the Python C(None) keyword (YAML Equivalent, C(null)).
- Elements default to no value (but present).
- Attributes default to an empty string.
type: raw
add_children:
description:
- Add additional child-element(s) to a selected element for a given C(xpath).
- Child elements must be given in a list and each item may be either a string
(eg. C(children=ansible) to add an empty C(<ansible/>) child element),
or a hash where the key is an element name and the value is the element value.
- This parameter requires C(xpath) to be set.
type: list
set_children:
description:
- Set the child-element(s) of a selected element for a given C(xpath).
- Removes any existing children.
- Child elements must be specified as in C(add_children).
- This parameter requires C(xpath) to be set.
type: list
count:
description:
- Search for a given C(xpath) and provide the count of any matches.
- This parameter requires C(xpath) to be set.
type: bool
default: no
print_match:
description:
- Search for a given C(xpath) and print out any matches.
- This parameter requires C(xpath) to be set.
type: bool
default: no
pretty_print:
description:
- Pretty print XML output.
type: bool
default: no
content:
description:
- Search for a given C(xpath) and get content.
- This parameter requires C(xpath) to be set.
type: str
choices: [ attribute, text ]
input_type:
description:
- Type of input for C(add_children) and C(set_children).
type: str
choices: [ xml, yaml ]
default: yaml
backup:
description:
- Create a backup file including the timestamp information so you can get
the original file back if you somehow clobbered it incorrectly.
type: bool
default: no
strip_cdata_tags:
description:
- Remove CDATA tags surrounding text values.
- Note that this might break your XML file if text values contain characters that could be interpreted as XML.
type: bool
default: no
version_added: '2.7'
insertbefore:
description:
- Add additional child-element(s) before the first selected element for a given C(xpath).
- Child elements must be given in a list and each item may be either a string
(eg. C(children=ansible) to add an empty C(<ansible/>) child element),
or a hash where the key is an element name and the value is the element value.
- This parameter requires C(xpath) to be set.
type: bool
default: no
version_added: '2.8'
insertafter:
description:
- Add additional child-element(s) after the last selected element for a given C(xpath).
- Child elements must be given in a list and each item may be either a string
(eg. C(children=ansible) to add an empty C(<ansible/>) child element),
or a hash where the key is an element name and the value is the element value.
- This parameter requires C(xpath) to be set.
type: bool
default: no
version_added: '2.8'
requirements:
- lxml >= 2.3.0
notes:
- Use the C(--check) and C(--diff) options when testing your expressions.
- The diff output is automatically pretty-printed, so may not reflect the actual file content, only the file structure.
- This module does not handle complicated xpath expressions, so limit xpath selectors to simple expressions.
- Beware that in case your XML elements are namespaced, you need to use the C(namespaces) parameter, see the examples.
- Namespaces prefix should be used for all children of an element where namespace is defined, unless another namespace is defined for them.
seealso:
- name: Xml module development community wiki
description: More information related to the development of this xml module.
link: https://github.com/ansible/community/wiki/Module:-xml
- name: Introduction to XPath
description: A brief tutorial on XPath (w3schools.com).
link: https://www.w3schools.com/xml/xpath_intro.asp
- name: XPath Reference document
description: The reference documentation on XSLT/XPath (developer.mozilla.org).
link: https://developer.mozilla.org/en-US/docs/Web/XPath
author:
- Tim Bielawa (@tbielawa)
- Magnus Hedemark (@magnus919)
- Dag Wieers (@dagwieers)
'''
EXAMPLES = r'''
# Consider the following XML file:
#
# <business type="bar">
# <name>Tasty Beverage Co.</name>
# <beers>
# <beer>Rochefort 10</beer>
# <beer>St. Bernardus Abbot 12</beer>
# <beer>Schlitz</beer>
# </beers>
# <rating subjective="true">10</rating>
# <website>
# <mobilefriendly/>
# <address>http://tastybeverageco.com</address>
# </website>
# </business>
- name: Remove the 'subjective' attribute of the 'rating' element
xml:
path: /foo/bar.xml
xpath: /business/rating/@subjective
state: absent
- name: Set the rating to '11'
xml:
path: /foo/bar.xml
xpath: /business/rating
value: 11
# Retrieve and display the number of nodes
- name: Get count of 'beers' nodes
xml:
path: /foo/bar.xml
xpath: /business/beers/beer
count: yes
register: hits
- debug:
var: hits.count
# Example where parent XML nodes are created automatically
- name: Add a 'phonenumber' element to the 'business' element
xml:
path: /foo/bar.xml
xpath: /business/phonenumber
value: 555-555-1234
- name: Add several more beers to the 'beers' element
xml:
path: /foo/bar.xml
xpath: /business/beers
add_children:
- beer: Old Rasputin
- beer: Old Motor Oil
- beer: Old Curmudgeon
- name: Add several more beers to the 'beers' element and add them before the 'Rochefort 10' element
xml:
path: /foo/bar.xml
xpath: '/business/beers/beer[text()="Rochefort 10"]'
insertbefore: yes
add_children:
- beer: Old Rasputin
- beer: Old Motor Oil
- beer: Old Curmudgeon
# NOTE: The 'state' defaults to 'present' and 'value' defaults to 'null' for elements
- name: Add a 'validxhtml' element to the 'website' element
xml:
path: /foo/bar.xml
xpath: /business/website/validxhtml
- name: Add an empty 'validatedon' attribute to the 'validxhtml' element
xml:
path: /foo/bar.xml
xpath: /business/website/validxhtml/@validatedon
- name: Add or modify an attribute, add element if needed
xml:
path: /foo/bar.xml
xpath: /business/website/validxhtml
attribute: validatedon
value: 1976-08-05
# How to read an attribute value and access it in Ansible
- name: Read an element's attribute values
xml:
path: /foo/bar.xml
xpath: /business/website/validxhtml
content: attribute
register: xmlresp
- name: Show an attribute value
debug:
var: xmlresp.matches[0].validxhtml.validatedon
- name: Remove all children from the 'website' element (option 1)
xml:
path: /foo/bar.xml
xpath: /business/website/*
state: absent
- name: Remove all children from the 'website' element (option 2)
xml:
path: /foo/bar.xml
xpath: /business/website
children: []
# In case of namespaces, like in below XML, they have to be explicitly stated.
#
# <foo xmlns="http://x.test" xmlns:attr="http://z.test">
# <bar>
# <baz xmlns="http://y.test" attr:my_namespaced_attribute="true" />
# </bar>
# </foo>
# NOTE: There is the prefix 'x' in front of the 'bar' element, too.
- name: Set namespaced '/x:foo/x:bar/y:baz/@z:my_namespaced_attribute' to 'false'
xml:
path: foo.xml
xpath: /x:foo/x:bar/y:baz
namespaces:
x: http://x.test
y: http://y.test
z: http://z.test
attribute: z:my_namespaced_attribute
value: 'false'
'''
RETURN = r'''
actions:
description: A dictionary with the original xpath, namespaces and state.
type: dict
returned: success
sample: {xpath: xpath, namespaces: [namespace1, namespace2], state=present}
backup_file:
description: The name of the backup file that was created
type: str
returned: when backup=yes
sample: /path/to/file.xml.1942.2017-08-24@14:16:01~
count:
description: The count of xpath matches.
type: int
returned: when parameter 'count' is set
sample: 2
matches:
description: The xpath matches found.
type: list
returned: when parameter 'print_match' is set
msg:
description: A message related to the performed action(s).
type: str
returned: always
xmlstring:
description: An XML string of the resulting output.
type: str
returned: when parameter 'xmlstring' is set
'''
import copy
import json
import os
import re
import traceback
from distutils.version import LooseVersion
from io import BytesIO
LXML_IMP_ERR = None
try:
from lxml import etree, objectify
HAS_LXML = True
except ImportError:
LXML_IMP_ERR = traceback.format_exc()
HAS_LXML = False
from ansible.module_utils.basic import AnsibleModule, json_dict_bytes_to_unicode, missing_required_lib
from ansible.module_utils.six import iteritems, string_types
from ansible.module_utils._text import to_bytes, to_native
from ansible.module_utils.common._collections_compat import MutableMapping
_IDENT = r"[a-zA-Z-][a-zA-Z0-9_\-\.]*"
_NSIDENT = _IDENT + "|" + _IDENT + ":" + _IDENT
# Note: we can't reasonably support the 'if you need to put both ' and " in a string, concatenate
# strings wrapped by the other delimiter' XPath trick, especially as simple XPath.
_XPSTR = "('(?:.*)'|\"(?:.*)\")"
_RE_SPLITSIMPLELAST = re.compile("^(.*)/(" + _NSIDENT + ")$")
_RE_SPLITSIMPLELASTEQVALUE = re.compile("^(.*)/(" + _NSIDENT + ")/text\\(\\)=" + _XPSTR + "$")
_RE_SPLITSIMPLEATTRLAST = re.compile("^(.*)/(@(?:" + _NSIDENT + "))$")
_RE_SPLITSIMPLEATTRLASTEQVALUE = re.compile("^(.*)/(@(?:" + _NSIDENT + "))=" + _XPSTR + "$")
_RE_SPLITSUBLAST = re.compile("^(.*)/(" + _NSIDENT + ")\\[(.*)\\]$")
_RE_SPLITONLYEQVALUE = re.compile("^(.*)/text\\(\\)=" + _XPSTR + "$")
def has_changed(doc):
orig_obj = etree.tostring(objectify.fromstring(etree.tostring(orig_doc)))
obj = etree.tostring(objectify.fromstring(etree.tostring(doc)))
return (orig_obj != obj)
def do_print_match(module, tree, xpath, namespaces):
match = tree.xpath(xpath, namespaces=namespaces)
match_xpaths = []
for m in match:
match_xpaths.append(tree.getpath(m))
match_str = json.dumps(match_xpaths)
msg = "selector '%s' match: %s" % (xpath, match_str)
finish(module, tree, xpath, namespaces, changed=False, msg=msg)
def count_nodes(module, tree, xpath, namespaces):
""" Return the count of nodes matching the xpath """
hits = tree.xpath("count(/%s)" % xpath, namespaces=namespaces)
msg = "found %d nodes" % hits
finish(module, tree, xpath, namespaces, changed=False, msg=msg, hitcount=int(hits))
def is_node(tree, xpath, namespaces):
""" Test if a given xpath matches anything and if that match is a node.
For now we just assume you're only searching for one specific thing."""
if xpath_matches(tree, xpath, namespaces):
# OK, it found something
match = tree.xpath(xpath, namespaces=namespaces)
if isinstance(match[0], etree._Element):
return True
return False
def is_attribute(tree, xpath, namespaces):
""" Test if a given xpath matches and that match is an attribute
An xpath attribute search will only match one item"""
if xpath_matches(tree, xpath, namespaces):
match = tree.xpath(xpath, namespaces=namespaces)
if isinstance(match[0], etree._ElementStringResult):
return True
elif isinstance(match[0], etree._ElementUnicodeResult):
return True
return False
def xpath_matches(tree, xpath, namespaces):
""" Test if a node exists """
if tree.xpath(xpath, namespaces=namespaces):
return True
return False
def delete_xpath_target(module, tree, xpath, namespaces):
""" Delete an attribute or element from a tree """
try:
for result in tree.xpath(xpath, namespaces=namespaces):
# Get the xpath for this result
if is_attribute(tree, xpath, namespaces):
# Delete an attribute
parent = result.getparent()
# Pop this attribute match out of the parent
# node's 'attrib' dict by using this match's
# 'attrname' attribute for the key
parent.attrib.pop(result.attrname)
elif is_node(tree, xpath, namespaces):
# Delete an element
result.getparent().remove(result)
else:
raise Exception("Impossible error")
except Exception as e:
module.fail_json(msg="Couldn't delete xpath target: %s (%s)" % (xpath, e))
else:
finish(module, tree, xpath, namespaces, changed=True)
def replace_children_of(children, match):
for element in list(match):
match.remove(element)
match.extend(children)
def set_target_children_inner(module, tree, xpath, namespaces, children, in_type):
matches = tree.xpath(xpath, namespaces=namespaces)
# Create a list of our new children
children = children_to_nodes(module, children, in_type)
children_as_string = [etree.tostring(c) for c in children]
changed = False
# xpaths always return matches as a list, so....
for match in matches:
# Check if elements differ
if len(list(match)) == len(children):
for idx, element in enumerate(list(match)):
if etree.tostring(element) != children_as_string[idx]:
replace_children_of(children, match)
changed = True
break
else:
replace_children_of(children, match)
changed = True
return changed
def set_target_children(module, tree, xpath, namespaces, children, in_type):
changed = set_target_children_inner(module, tree, xpath, namespaces, children, in_type)
# Write it out
finish(module, tree, xpath, namespaces, changed=changed)
def add_target_children(module, tree, xpath, namespaces, children, in_type, insertbefore, insertafter):
if is_node(tree, xpath, namespaces):
new_kids = children_to_nodes(module, children, in_type)
if insertbefore or insertafter:
insert_target_children(tree, xpath, namespaces, new_kids, insertbefore, insertafter)
else:
for node in tree.xpath(xpath, namespaces=namespaces):
node.extend(new_kids)
finish(module, tree, xpath, namespaces, changed=True)
else:
finish(module, tree, xpath, namespaces)
def insert_target_children(tree, xpath, namespaces, children, insertbefore, insertafter):
"""
Insert the given children before or after the given xpath. If insertbefore is True, it is inserted before the
first xpath hit, with insertafter, it is inserted after the last xpath hit.
"""
insert_target = tree.xpath(xpath, namespaces=namespaces)
loc_index = 0 if insertbefore else -1
index_in_parent = insert_target[loc_index].getparent().index(insert_target[loc_index])
parent = insert_target[0].getparent()
if insertafter:
index_in_parent += 1
for child in children:
parent.insert(index_in_parent, child)
index_in_parent += 1
def _extract_xpstr(g):
return g[1:-1]
def split_xpath_last(xpath):
"""split an XPath of the form /foo/bar/baz into /foo/bar and baz"""
xpath = xpath.strip()
m = _RE_SPLITSIMPLELAST.match(xpath)
if m:
# requesting an element to exist
return (m.group(1), [(m.group(2), None)])
m = _RE_SPLITSIMPLELASTEQVALUE.match(xpath)
if m:
# requesting an element to exist with an inner text
return (m.group(1), [(m.group(2), _extract_xpstr(m.group(3)))])
m = _RE_SPLITSIMPLEATTRLAST.match(xpath)
if m:
# requesting an attribute to exist
return (m.group(1), [(m.group(2), None)])
m = _RE_SPLITSIMPLEATTRLASTEQVALUE.match(xpath)
if m:
# requesting an attribute to exist with a value
return (m.group(1), [(m.group(2), _extract_xpstr(m.group(3)))])
m = _RE_SPLITSUBLAST.match(xpath)
if m:
content = [x.strip() for x in m.group(3).split(" and ")]
return (m.group(1), [('/' + m.group(2), content)])
m = _RE_SPLITONLYEQVALUE.match(xpath)
if m:
# requesting a change of inner text
return (m.group(1), [("", _extract_xpstr(m.group(2)))])
return (xpath, [])
def nsnameToClark(name, namespaces):
if ":" in name:
(nsname, rawname) = name.split(":")
# return "{{%s}}%s" % (namespaces[nsname], rawname)
return "{{{0}}}{1}".format(namespaces[nsname], rawname)
# no namespace name here
return name
def check_or_make_target(module, tree, xpath, namespaces):
(inner_xpath, changes) = split_xpath_last(xpath)
if (inner_xpath == xpath) or (changes is None):
module.fail_json(msg="Can't process Xpath %s in order to spawn nodes! tree is %s" %
(xpath, etree.tostring(tree, pretty_print=True)))
return False
changed = False
if not is_node(tree, inner_xpath, namespaces):
changed = check_or_make_target(module, tree, inner_xpath, namespaces)
# we test again after calling check_or_make_target
if is_node(tree, inner_xpath, namespaces) and changes:
for (eoa, eoa_value) in changes:
if eoa and eoa[0] != '@' and eoa[0] != '/':
# implicitly creating an element
new_kids = children_to_nodes(module, [nsnameToClark(eoa, namespaces)], "yaml")
if eoa_value:
for nk in new_kids:
nk.text = eoa_value
for node in tree.xpath(inner_xpath, namespaces=namespaces):
node.extend(new_kids)
changed = True
# module.fail_json(msg="now tree=%s" % etree.tostring(tree, pretty_print=True))
elif eoa and eoa[0] == '/':
element = eoa[1:]
new_kids = children_to_nodes(module, [nsnameToClark(element, namespaces)], "yaml")
for node in tree.xpath(inner_xpath, namespaces=namespaces):
node.extend(new_kids)
for nk in new_kids:
for subexpr in eoa_value:
# module.fail_json(msg="element=%s subexpr=%s node=%s now tree=%s" %
# (element, subexpr, etree.tostring(node, pretty_print=True), etree.tostring(tree, pretty_print=True))
check_or_make_target(module, nk, "./" + subexpr, namespaces)
changed = True
# module.fail_json(msg="now tree=%s" % etree.tostring(tree, pretty_print=True))
elif eoa == "":
for node in tree.xpath(inner_xpath, namespaces=namespaces):
if (node.text != eoa_value):
node.text = eoa_value
changed = True
elif eoa and eoa[0] == '@':
attribute = nsnameToClark(eoa[1:], namespaces)
for element in tree.xpath(inner_xpath, namespaces=namespaces):
changing = (attribute not in element.attrib or element.attrib[attribute] != eoa_value)
if changing:
changed = changed or changing
if eoa_value is None:
value = ""
else:
value = eoa_value
element.attrib[attribute] = value
# module.fail_json(msg="arf %s changing=%s as curval=%s changed tree=%s" %
# (xpath, changing, etree.tostring(tree, changing, element[attribute], pretty_print=True)))
else:
module.fail_json(msg="unknown tree transformation=%s" % etree.tostring(tree, pretty_print=True))
return changed
def ensure_xpath_exists(module, tree, xpath, namespaces):
changed = False
if not is_node(tree, xpath, namespaces):
changed = check_or_make_target(module, tree, xpath, namespaces)
finish(module, tree, xpath, namespaces, changed)
def set_target_inner(module, tree, xpath, namespaces, attribute, value):
changed = False
try:
if not is_node(tree, xpath, namespaces):
changed = check_or_make_target(module, tree, xpath, namespaces)
except Exception as e:
missing_namespace = ""
# NOTE: This checks only the namespaces defined in root element!
# TODO: Implement a more robust check to check for child namespaces' existence
if tree.getroot().nsmap and ":" not in xpath:
missing_namespace = "XML document has namespace(s) defined, but no namespace prefix(es) used in xpath!\n"
module.fail_json(msg="%sXpath %s causes a failure: %s\n -- tree is %s" %
(missing_namespace, xpath, e, etree.tostring(tree, pretty_print=True)), exception=traceback.format_exc())
if not is_node(tree, xpath, namespaces):
module.fail_json(msg="Xpath %s does not reference a node! tree is %s" %
(xpath, etree.tostring(tree, pretty_print=True)))
for element in tree.xpath(xpath, namespaces=namespaces):
if not attribute:
changed = changed or (element.text != value)
if element.text != value:
element.text = value
else:
changed = changed or (element.get(attribute) != value)
if ":" in attribute:
attr_ns, attr_name = attribute.split(":")
# attribute = "{{%s}}%s" % (namespaces[attr_ns], attr_name)
attribute = "{{{0}}}{1}".format(namespaces[attr_ns], attr_name)
if element.get(attribute) != value:
element.set(attribute, value)
return changed
def set_target(module, tree, xpath, namespaces, attribute, value):
changed = set_target_inner(module, tree, xpath, namespaces, attribute, value)
finish(module, tree, xpath, namespaces, changed)
def get_element_text(module, tree, xpath, namespaces):
if not is_node(tree, xpath, namespaces):
module.fail_json(msg="Xpath %s does not reference a node!" % xpath)
elements = []
for element in tree.xpath(xpath, namespaces=namespaces):
elements.append({element.tag: element.text})
finish(module, tree, xpath, namespaces, changed=False, msg=len(elements), hitcount=len(elements), matches=elements)
def get_element_attr(module, tree, xpath, namespaces):
if not is_node(tree, xpath, namespaces):
module.fail_json(msg="Xpath %s does not reference a node!" % xpath)
elements = []
for element in tree.xpath(xpath, namespaces=namespaces):
child = {}
for key in element.keys():
value = element.get(key)
child.update({key: value})
elements.append({element.tag: child})
finish(module, tree, xpath, namespaces, changed=False, msg=len(elements), hitcount=len(elements), matches=elements)
def child_to_element(module, child, in_type):
if in_type == 'xml':
infile = BytesIO(to_bytes(child, errors='surrogate_or_strict'))
try:
parser = etree.XMLParser()
node = etree.parse(infile, parser)
return node.getroot()
except etree.XMLSyntaxError as e:
module.fail_json(msg="Error while parsing child element: %s" % e)
elif in_type == 'yaml':
if isinstance(child, string_types):
return etree.Element(child)
elif isinstance(child, MutableMapping):
if len(child) > 1:
module.fail_json(msg="Can only create children from hashes with one key")
(key, value) = next(iteritems(child))
if isinstance(value, MutableMapping):
children = value.pop('_', None)
node = etree.Element(key, value)
if children is not None:
if not isinstance(children, list):
module.fail_json(msg="Invalid children type: %s, must be list." % type(children))
subnodes = children_to_nodes(module, children)
node.extend(subnodes)
else:
node = etree.Element(key)
node.text = value
return node
else:
module.fail_json(msg="Invalid child type: %s. Children must be either strings or hashes." % type(child))
else:
module.fail_json(msg="Invalid child input type: %s. Type must be either xml or yaml." % in_type)
def children_to_nodes(module=None, children=None, type='yaml'):
"""turn a str/hash/list of str&hash into a list of elements"""
children = [] if children is None else children
return [child_to_element(module, child, type) for child in children]
def make_pretty(module, tree):
xml_string = etree.tostring(tree, xml_declaration=True, encoding='UTF-8', pretty_print=module.params['pretty_print'])
result = dict(
changed=False,
)
if module.params['path']:
xml_file = module.params['path']
with open(xml_file, 'rb') as xml_content:
if xml_string != xml_content.read():
result['changed'] = True
if not module.check_mode:
if module.params['backup']:
result['backup_file'] = module.backup_local(module.params['path'])
tree.write(xml_file, xml_declaration=True, encoding='UTF-8', pretty_print=module.params['pretty_print'])
elif module.params['xmlstring']:
result['xmlstring'] = xml_string
# NOTE: Modifying a string is not considered a change !
if xml_string != module.params['xmlstring']:
result['changed'] = True
module.exit_json(**result)
def finish(module, tree, xpath, namespaces, changed=False, msg='', hitcount=0, matches=tuple()):
result = dict(
actions=dict(
xpath=xpath,
namespaces=namespaces,
state=module.params['state']
),
changed=has_changed(tree),
)
if module.params['count'] or hitcount:
result['count'] = hitcount
if module.params['print_match'] or matches:
result['matches'] = matches
if msg:
result['msg'] = msg
if result['changed']:
if module._diff:
result['diff'] = dict(
before=etree.tostring(orig_doc, xml_declaration=True, encoding='UTF-8', pretty_print=True),
after=etree.tostring(tree, xml_declaration=True, encoding='UTF-8', pretty_print=True),
)
if module.params['path'] and not module.check_mode:
if module.params['backup']:
result['backup_file'] = module.backup_local(module.params['path'])
tree.write(module.params['path'], xml_declaration=True, encoding='UTF-8', pretty_print=module.params['pretty_print'])
if module.params['xmlstring']:
result['xmlstring'] = etree.tostring(tree, xml_declaration=True, encoding='UTF-8', pretty_print=module.params['pretty_print'])
module.exit_json(**result)
def main():
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', aliases=['dest', 'file']),
xmlstring=dict(type='str'),
xpath=dict(type='str'),
namespaces=dict(type='dict', default={}),
state=dict(type='str', default='present', choices=['absent', 'present'], aliases=['ensure']),
value=dict(type='raw'),
attribute=dict(type='raw'),
add_children=dict(type='list'),
set_children=dict(type='list'),
count=dict(type='bool', default=False),
print_match=dict(type='bool', default=False),
pretty_print=dict(type='bool', default=False),
content=dict(type='str', choices=['attribute', 'text']),
input_type=dict(type='str', default='yaml', choices=['xml', 'yaml']),
backup=dict(type='bool', default=False),
strip_cdata_tags=dict(type='bool', default=False),
insertbefore=dict(type='bool', default=False),
insertafter=dict(type='bool', default=False),
),
supports_check_mode=True,
required_by=dict(
add_children=['xpath'],
# TODO: Reinstate this in Ansible v2.12 when we have deprecated the incorrect use below
# attribute=['value'],
content=['xpath'],
set_children=['xpath'],
value=['xpath'],
),
required_if=[
['count', True, ['xpath']],
['print_match', True, ['xpath']],
['insertbefore', True, ['xpath']],
['insertafter', True, ['xpath']],
],
required_one_of=[
['path', 'xmlstring'],
['add_children', 'content', 'count', 'pretty_print', 'print_match', 'set_children', 'value'],
],
mutually_exclusive=[
['add_children', 'content', 'count', 'print_match', 'set_children', 'value'],
['path', 'xmlstring'],
['insertbefore', 'insertafter'],
],
)
xml_file = module.params['path']
xml_string = module.params['xmlstring']
xpath = module.params['xpath']
namespaces = module.params['namespaces']
state = module.params['state']
value = json_dict_bytes_to_unicode(module.params['value'])
attribute = module.params['attribute']
set_children = json_dict_bytes_to_unicode(module.params['set_children'])
add_children = json_dict_bytes_to_unicode(module.params['add_children'])
pretty_print = module.params['pretty_print']
content = module.params['content']
input_type = module.params['input_type']
print_match = module.params['print_match']
count = module.params['count']
backup = module.params['backup']
strip_cdata_tags = module.params['strip_cdata_tags']
insertbefore = module.params['insertbefore']
insertafter = module.params['insertafter']
# Check if we have lxml 2.3.0 or newer installed
if not HAS_LXML:
module.fail_json(msg=missing_required_lib("lxml"), exception=LXML_IMP_ERR)
elif LooseVersion('.'.join(to_native(f) for f in etree.LXML_VERSION)) < LooseVersion('2.3.0'):
module.fail_json(msg='The xml ansible module requires lxml 2.3.0 or newer installed on the managed machine')
elif LooseVersion('.'.join(to_native(f) for f in etree.LXML_VERSION)) < LooseVersion('3.0.0'):
module.warn('Using lxml version lower than 3.0.0 does not guarantee predictable element attribute order.')
# Report wrongly used attribute parameter when using content=attribute
# TODO: Remove this in Ansible v2.12 (and reinstate strict parameter test above) and remove the integration test example
if content == 'attribute' and attribute is not None:
module.deprecate("Parameter 'attribute=%s' is ignored when using 'content=attribute' only 'xpath' is used. Please remove entry." % attribute,
'2.12', collection_name='ansible.builtin')
# Check if the file exists
if xml_string:
infile = BytesIO(to_bytes(xml_string, errors='surrogate_or_strict'))
elif os.path.isfile(xml_file):
infile = open(xml_file, 'rb')
else:
module.fail_json(msg="The target XML source '%s' does not exist." % xml_file)
# Parse and evaluate xpath expression
if xpath is not None:
try:
etree.XPath(xpath)
except etree.XPathSyntaxError as e:
module.fail_json(msg="Syntax error in xpath expression: %s (%s)" % (xpath, e))
except etree.XPathEvalError as e:
module.fail_json(msg="Evaluation error in xpath expression: %s (%s)" % (xpath, e))
# Try to parse in the target XML file
try:
parser = etree.XMLParser(remove_blank_text=pretty_print, strip_cdata=strip_cdata_tags)
doc = etree.parse(infile, parser)
except etree.XMLSyntaxError as e:
module.fail_json(msg="Error while parsing document: %s (%s)" % (xml_file or 'xml_string', e))
# Ensure we have the original copy to compare
global orig_doc
orig_doc = copy.deepcopy(doc)
if print_match:
do_print_match(module, doc, xpath, namespaces)
if count:
count_nodes(module, doc, xpath, namespaces)
if content == 'attribute':
get_element_attr(module, doc, xpath, namespaces)
elif content == 'text':
get_element_text(module, doc, xpath, namespaces)
# File exists:
if state == 'absent':
# - absent: delete xpath target
delete_xpath_target(module, doc, xpath, namespaces)
# - present: carry on
# children && value both set?: should have already aborted by now
# add_children && set_children both set?: should have already aborted by now
# set_children set?
if set_children:
set_target_children(module, doc, xpath, namespaces, set_children, input_type)
# add_children set?
if add_children:
add_target_children(module, doc, xpath, namespaces, add_children, input_type, insertbefore, insertafter)
# No?: Carry on
# Is the xpath target an attribute selector?
if value is not None:
set_target(module, doc, xpath, namespaces, attribute, value)
# If an xpath was provided, we need to do something with the data
if xpath is not None:
ensure_xpath_exists(module, doc, xpath, namespaces)
# Otherwise only reformat the xml data?
if pretty_print:
make_pretty(module, doc)
module.fail_json(msg="Don't know what to do")
if __name__ == '__main__':
main()