ansible-collections / community.general

Ansible Community General Collection
https://galaxy.ansible.com/ui/repo/published/community/general/
GNU General Public License v3.0
831 stars 1.53k forks source link

xml's "print_matches" misdocumented #9125

Open fabricat-mdb opened 2 days ago

fabricat-mdb commented 2 days ago

Summary

Revamping from https://github.com/ansible/ansible/issues/55051

The documentation of the XML-module is incorrect about the matches and print_match parts:

  1. When print_match is set, the matches part of the returned dict may be empty, even when matches are found.
  2. Even when the print_match is not set, the matches part may be returned -- not empty -- when the content is requested, contrary to the documentation.
  3. No (documented) way to get the entire element matched by the xpath expression.

Issue Type

Bug Report

Component Name

xml

Ansible Version

$ ansible --version
ansible [core 2.17.5]
  config file = /Users/fab/workdir/ansible/ansible.cfg
  configured module search path = ['/Users/fab/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/homebrew/Cellar/ansible/10.5.0/libexec/lib/python3.12/site-packages/ansible
  ansible collection location = /Users/fab/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/homebrew/bin/ansible
  python version = 3.12.7 (main, Oct  1 2024, 02:05:46) [Clang 15.0.0 (clang-1500.3.9.4)] (/opt/homebrew/Cellar/ansible/10.5.0/libexec/bin/python)
  jinja version = 3.1.4
  libyaml = True

Community.general Version

$ ansible-galaxy collection list community.general
Collection        Version
----------------- -------
community.general 9.5.0

Configuration

$ ansible-config dump --only-changed
CACHE_PLUGIN(/Users/fab/workdir/ansible/ansible.cfg) = ansible.builtin.jsonfile
CACHE_PLUGIN_CONNECTION(/Users/fab/workdir/ansible/ansible.cfg) = /tmp/ansible_facts
CALLBACKS_ENABLED(/Users/fab/workdir/ansible/ansible.cfg) = ['community.general.log_plays']
CONFIG_FILE() = /Users/fab/workdir/ansible/ansible.cfg
DEFAULT_GATHERING(/Users/fab/workdir/ansible/ansible.cfg) = smart
DEFAULT_HOST_LIST(/Users/fab/workdir/ansible/ansible.cfg) = ['/Users/fab/workdir/ansible/inventory']
DEFAULT_LOAD_CALLBACK_PLUGINS(/Users/fab/workdir/ansible/ansible.cfg) = True
DEFAULT_MANAGED_STR(/Users/fab/workdir/ansible/ansible.cfg) = Managed by Ansible (itproductivity-ansible).
INTERPRETER_PYTHON(/Users/fab/workdir/ansible/ansible.cfg) = auto_silent
INVENTORY_CACHE_ENABLED(/Users/fab/workdir/ansible/ansible.cfg) = True
INVENTORY_CACHE_PLUGIN(/Users/fab/workdir/ansible/ansible.cfg) = ansible.builtin.jsonfile
INVENTORY_CACHE_PLUGIN_CONNECTION(/Users/fab/workdir/ansible/ansible.cfg) = /tmp/ansible_inventory
TRANSFORM_INVALID_GROUP_CHARS(/Users/fab/workdir/ansible/ansible.cfg) = always

OS / Environment

Target: Amazon Linux 2023

Steps to Reproduce

Run the below mini-playbook as ansible-playbook test.yml

---
- hosts: localhost
  gather_facts: false
  tasks:
  - name: Create test XML
    set_fact:
      xml: >-
        <?xml version='1.0' encoding='US-ASCII'?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns:html="http://www.w3.org/1999/xhtml">
        <head><title>Listing of environment details</title></head>
        <body>
            <table class="envlist" border="1">
                <caption>Meow</caption>
                <TR class="envrow"><TH class="first">Uno</TH><TD class="second">Dos</TD></TR>
                <TR class="envrow"><TD class="second">Dos</TD></TR>
            </table>
        </body>
        </html>

  - name: Search for "envrow"
    community.general.xml:
      xmlstring: "{{ xml }}"
      xpath: '//*[@class = "envrow"]'
      print_match: "{% if item %}true{% else %}{{ omit }}{% endif %}"
      content: "{% if item %}{{ omit }}{% else %}text{% endif %}"
      namespaces:
        html: 'http://www.w3.org/1999/xhtml'
    register: counts
    loop:
    - true
    - false

  - debug: var=counts

Expected Results

  1. When print_matches is set, I expect the non-empty matches list in the result -- instead it kinda-sorta appears in the msg string.
  2. When print_matches is omitted, I expect there to be no matches list in the result at all, because documentation says so.
  3. If the matches are listed -- which makes sense, since I do request content: text -- the values for each TR should not be null, in my opinion. I'm trying to get the entire rows so as to be able to manipulate them further...

Actual Results

Though matches were found -- and are listed in msg, the actual matches part is empty:

$ ansible-playbook test.yml

PLAY [localhost] *******************************************************************************************************************************************************************

TASK [Create test XML] *************************************************************************************************************************************************************
ok: [localhost]

TASK [Search for "envrow"] *********************************************************************************************************************************************************
ok: [localhost] => (item=True)
ok: [localhost] => (item=False)

TASK [debug] ***********************************************************************************************************************************************************************
ok: [localhost] => {
    "counts": {
        "changed": false,
        "msg": "All items completed",
        "results": [
            {
                "actions": {
                    "namespaces": {
                        "html": "http://www.w3.org/1999/xhtml"
                    },
                    "state": "present",
                    "xpath": "//*[@class = \"envrow\"]"
                },
                "ansible_loop_var": "item",
                "changed": false,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "add_children": null,
                        "attribute": null,
                        "backup": false,
                        "content": null,
                        "count": false,
                        "input_type": "yaml",
                        "insertafter": false,
                        "insertbefore": false,
                        "namespaces": {
                            "html": "http://www.w3.org/1999/xhtml"
                        },
                        "path": null,
                        "pretty_print": false,
                        "print_match": true,
                        "set_children": null,
                        "state": "present",
                        "strip_cdata_tags": false,
                        "value": null,
                        "xmlstring": "<?xml version='1.0' encoding='US-ASCII'?> <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> <html xmlns:html=\"http://www.w3.org/1999/xhtml\"> <head><title>Listing of environment details</title></head> <body>\n\t<table class=\"envlist\" border=\"1\">\n\t\t<caption>Meow</caption>\n\t\t<TR class=\"envrow\"><TH class=\"first\">Uno</TH><TD class=\"second\">Dos</TD></TR>\n\t\t<TR class=\"envrow\"><TD class=\"second\">Dos</TD></TR>\n\t</table>\n</body> </html>",
                        "xpath": "//*[@class = \"envrow\"]"
                    }
                },
                "item": true,
                "matches": [],
                "msg": "selector '//*[@class = \"envrow\"]' match: [\"/html/body/table/TR[1]\", \"/html/body/table/TR[2]\"]",
                "xmlstring": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns:html=\"http://www.w3.org/1999/xhtml\"> <head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /><title>Listing of environment details</title></head> <body>\n\t<table class=\"envlist\" border=\"1\">\n\t\t<caption>Meow</caption>\n\t\t<TR class=\"envrow\"><TH class=\"first\">Uno</TH><TD class=\"second\">Dos</TD></TR>\n\t\t<TR class=\"envrow\"><TD class=\"second\">Dos</TD></TR>\n\t</table>\n</body> </html>"
            },
            {
                "actions": {
                    "namespaces": {
                        "html": "http://www.w3.org/1999/xhtml"
                    },
                    "state": "present",
                    "xpath": "//*[@class = \"envrow\"]"
                },
                "ansible_loop_var": "item",
                "changed": false,
                "count": 2,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "add_children": null,
                        "attribute": null,
                        "backup": false,
                        "content": "text",
                        "count": false,
                        "input_type": "yaml",
                        "insertafter": false,
                        "insertbefore": false,
                        "namespaces": {
                            "html": "http://www.w3.org/1999/xhtml"
                        },
                        "path": null,
                        "pretty_print": false,
                        "print_match": false,
                        "set_children": null,
                        "state": "present",
                        "strip_cdata_tags": false,
                        "value": null,
                        "xmlstring": "<?xml version='1.0' encoding='US-ASCII'?> <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> <html xmlns:html=\"http://www.w3.org/1999/xhtml\"> <head><title>Listing of environment details</title></head> <body>\n\t<table class=\"envlist\" border=\"1\">\n\t\t<caption>Meow</caption>\n\t\t<TR class=\"envrow\"><TH class=\"first\">Uno</TH><TD class=\"second\">Dos</TD></TR>\n\t\t<TR class=\"envrow\"><TD class=\"second\">Dos</TD></TR>\n\t</table>\n</body> </html>",
                        "xpath": "//*[@class = \"envrow\"]"
                    }
                },
                "item": false,
                "matches": [
                    {
                        "TR": null
                    },
                    {
                        "TR": null
                    }
                ],
                "msg": 2,
                "xmlstring": "<?xml version='1.0' encoding='UTF-8'?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns:html=\"http://www.w3.org/1999/xhtml\"> <head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /><title>Listing of environment details</title></head> <body>\n\t<table class=\"envlist\" border=\"1\">\n\t\t<caption>Meow</caption>\n\t\t<TR class=\"envrow\"><TH class=\"first\">Uno</TH><TD class=\"second\">Dos</TD></TR>\n\t\t<TR class=\"envrow\"><TD class=\"second\">Dos</TD></TR>\n\t</table>\n</body> </html>"
            }
        ],
        "skipped": false
    }
}

PLAY RECAP *************************************************************************************************************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Code of Conduct

ansibullbot commented 2 days ago

Files identified in the description:

If these files are incorrect, please update the component name section of the description or use the !component bot command.

click here for bot help

ansibullbot commented 2 days ago

cc @cmprescott @dagwieers @sm4rk0 @tbielawa click here for bot help