3

I spent a bit of time today trying to write some Ansible plays to only run commands if the corresponding lines weren't present in the relevant command output. After a bit of trial & error I've got something that works for me, but I'm not clear as to why my initial comparison with an empty string doesn't work.

Here is a playbook demonstrating my problem:

- name: test
  hosts: localhost
  tasks:
  - shell: "cat /tmp/cmdoutput"
    register: cmdoutput

  - debug: var=filtered_output
    vars:
      filtered_output: "{{ cmdoutput.stdout | regex_search(item) }}"
    with_items:
      - "aa .* xx"
      - "bb .* yy"

  - debug: msg="do action that inserts {{ item }}"
    with_items:
      - "aa .* xx"
      - "bb .* yy"
    when: cmdoutput.stdout | regex_search(item) == ""

  - debug: msg="do action that inserts {{ item }}"
    with_items:
      - "aa .* xx"
      - "bb .* yy"
    when: not cmdoutput.stdout | regex_search(item)
cat /tmp/cmdoutput
aa b c d xx
aa f g h yy
bb i j k xx

This results in the following output:

$ ansible-playbook test.yml 
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'


PLAY [test] **********************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************
ok: [localhost]

TASK [shell] *********************************************************************************************************************
changed: [localhost]

TASK [debug] *********************************************************************************************************************
ok: [localhost] => (item=None) => {
    "filtered_output": "aa b c d xx"
}
ok: [localhost] => (item=None) => {
    "filtered_output": ""
}

TASK [debug] *********************************************************************************************************************
skipping: [localhost] => (item=None) 
skipping: [localhost] => (item=None) 

TASK [debug] *********************************************************************************************************************
skipping: [localhost] => (item=None) 
ok: [localhost] => (item=None) => {
    "msg": "do action that inserts bb .* yy"
}

PLAY RECAP ***********************************************************************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0   

i.e. "filtered_output": "", but the following when comparison doesn't match.

So my questions are:

  • why doesn't the second debug when condition match ""?
  • what type of object is the output of the regex_search? - a string or an array or something else?
  • is there any, or where could I find more info on the regex_search filter? - (at this point in time AFAICT) it's not in the jinja doco and only appears in the filters section of the official docs as brief examples

My Ansible version: 2.5.1

Thanks

lambfrier
  • 757
  • 1
  • 8
  • 13

1 Answers1

4

Q: Why doesn't the second debug when condition match ""?

A: When there is no regex match object of type NoneType is returned. This type has no length. Instead of testing empty string (off-topic see Ansible Lint: Don’t compare to empty string)

    when: cmdoutput.stdout|regex_search(item) == ""

use (you already have it in your example)

    when: not cmdoutput.stdout|regex_search(item)

Both None and an empty string evaluate to False and the non-empty string evaluates to True.


Q: "How to easily see this NoneType object?"

A: Use filter type_debug. For example, if the search is successful

    - debug:
        msg: "{{ cmdoutput.stdout|regex_search('a') }}"
      vars:
        cmdoutput:
          stdout: abc

the result is a string

  msg: a

You can see the type of the result

    - debug:
        msg: "{{ cmdoutput.stdout|regex_search('a')|type_debug }}"
      vars:
        cmdoutput:
          stdout: abc

gives

  msg: str

If the search fails the result is None

    - debug:
        msg: "{{ cmdoutput.stdout|regex_search('x') }}"
      vars:
        cmdoutput:
          stdout: abc
  msg: ''
    - debug:
        msg: "{{ cmdoutput.stdout|regex_search('x')|type_debug }}"
      vars:
        cmdoutput:
          stdout: abc
  msg: NoneType
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
  • 1
    So I guess one of the problems was my interpretation of the "filtered_output": "" debug line above. The output is an empty string because it's cast to a String due to the quotes. I had seen a [bug report for E602](https://github.com/ansible/ansible-lint/issues/457) but my quick read was that empty string comparison was needed in some cases, which was not quite the point. I had also tried piping to length, but got 'condition object of type 'NoneType' has no len()' errors. Your answer clarifies why - thanks. I guess part of my question was how to easily see this NoneType object. – lambfrier Mar 22 '19 at 01:56