Undefined variables and filter subelements

414 views
Skip to first unread message

Tobias Kirchhofer

unread,
Nov 7, 2019, 7:43:02 AM11/7/19
to Ansible Project
We think we experience an anomalie with undefined variables and usage of subelements:

- name: Enable/disable service
      firewalld
:
        zone
: "{{ item.0.ZONE }}"
        service
: "{{ item.1.SERVICE }}"
        permanent
: yes
        immediate
: yes
        state
: "{{ item.1.STATE }}"
      loop
: "{{ FIREWALLD_ZONE_SERVICE|subelements('SERVICES') }}"
 
when: FIREWALLD_ZONE_SERVICE is defined


FIREWALLD_ZONE_SERVICE is NOT defined. Our expectation is that ansible will skip this task like it does at other tasks without sublements. With subelements in the loop ansible tries to resolve undefined FIREWALLD_ZONE_SERVICE and fails with an error:

fatal: [...]: FAILED! => {"msg": "obj must be a list of dicts or a nested dict"}


If FIREWALLD_ZONE_SERVICE is defined everything works like expected.

Anyone?


Tobias Kirchhofer

unread,
Nov 8, 2019, 6:54:48 AM11/8/19
to Ansible Project
Maybe i was not precise enough. Situation is this:

If the var is defined it looks like this:

FIREWALLD_ZONE_SERVICE:

 
- ZONE: public
    SERVICES
:
     
- SERVICE: http
        STATE
: enabled
     
- SERVICE: https
        STATE
: enabled




We loop through SERVICES with
FIREWALLD_ZONE_SERVICE|subelements('SERVICES')


This is fine as long the var is defined. It not we expect that

when: FIREWALLD_ZONE_SERVICE is defined

prevents the evaluation of the war in the loop.

In another context it works fine:

loop: "{{ FIREWALLD_ZONE|product(FIREWALLD_ZONE.ZONES)|list }}"

when: FIREWALLD_ZONE is defined

If FIREWALLD_ZONE is not defined the var in the loop is not evaluated.

So the difference is:

If we have 'subelements' in the loop 'when: var is defined' is not working.
If we have eq. 'product' in the loop 'when: var is defined' works.

More clear now? :-)

Could someone spend some thoughts? Maybe it is a misunderstanding on our site... 

Vladimir Botka

unread,
Nov 8, 2019, 7:32:01 AM11/8/19
to Ansible Project
On Fri, 8 Nov 2019 03:54:47 -0800 (PST)
"'Tobias Kirchhofer' via Ansible Project" <ansible...@googlegroups.com>
wrote:

> loop: "{{ FIREWALLD_ZONE_SERVICE|subelements('SERVICES') }}"
> when: FIREWALLD_ZONE_SERVICE is defined
>
> fatal: [...]: FAILED! => {"msg": "obj must be a list of dicts or a nested
> dict"}

Use *default* and *skip_missing* subelements. For example

loop: "{{ FIREWALLD_ZONE_SERVICE|default([])|
subelements('SERVICES', skip_missing=True) }}"

Cheers,

-vlado

Tobias Kirchhofer

unread,
Nov 8, 2019, 9:13:10 AM11/8/19
to Ansible Project
Use *default* and *skip_missing* subelements. For example

    loop: "{{ FIREWALLD_ZONE_SERVICE|default([])|
              subelements('SERVICES', skip_missing=True) }}"

Thank you! This is working. Is only needed with 'subelements'. But why? Anyway - thanks! :-) 

Vladimir Botka

unread,
Nov 8, 2019, 10:56:11 AM11/8/19
to 'Tobias Kirchhofer' via Ansible Project
On Fri, 8 Nov 2019 06:13:10 -0800 (PST)
"'Tobias Kirchhofer' via Ansible Project" <ansible...@googlegroups.com>
wrote:

> >
I guess

- *subelements* filter is executed before *when*, because
- *when* is evaluated on each iteration, because
- *when* argument can change on each iteration (e.g. may include *item*).
- *when* can not be evaluated before *subelements*, because *item* does not
exist yet.

The loop will fail

loop: "{{ FIREWALLD_ZONE_SERVICE|subelements('SERVICES') }}"
when: FIREWALLD_ZONE_SERVICE is defined

FAILED! => {"msg": "'FIREWALLD_ZONE_SERVICE' is undefined"}

The loop below is skipped when FIREWALLD_ZONE_SERVICE is not defined
(this is not consistent, it should also fail. Source would reveal why)

loop: "{{ FIREWALLD_ZONE_SERVICE }}"
when: FIREWALLD_ZONE_SERVICE is defined

,but it will also fail if the evaluation of the *loop* argument
(FIREWALLD_ZONE_SERVICE) before *when* is forced e.g.

loop: "{{ FIREWALLD_ZONE_SERVICE }}"
when: item|length > 10 and
FIREWALLD_ZONE_SERVICE is defined

FAILED! => {"msg": "'FIREWALLD_ZONE_SERVICE' is undefined"}

Cheers,

-vlado

Vladimir Botka

unread,
Nov 8, 2019, 11:07:13 AM11/8/19
to 'Tobias Kirchhofer' via Ansible Project
On Fri, 8 Nov 2019 16:55:59 +0100
Vladimir Botka <vbo...@gmail.com> wrote:

> On Fri, 8 Nov 2019 06:13:10 -0800 (PST)
> "'Tobias Kirchhofer' via Ansible Project" <ansible...@googlegroups.com>
> wrote:
>
> > >
> > > Use *default* and *skip_missing* subelements. For example
> > >
> > > loop: "{{ FIREWALLD_ZONE_SERVICE|default([])|
> > > subelements('SERVICES', skip_missing=True) }}"
> >
> > Thank you! This is working. Is only needed with 'subelements'. But why?

As a result, *default* is better construct

loop: "{{ FIREWALLD_ZONE_SERVICE|default([]) }}"

than *when*

Tobias Kirchhofer

unread,
Nov 9, 2019, 3:04:09 AM11/9/19
to Ansible Project
Thank you for the background explanation! Interesting.
Reply all
Reply to author
Forward
0 new messages