jmserra
jmserra

Reputation: 1306

Ansible with_subelements default value

i have a vars definition like this:

sites:
 - site: mysite1.com
   exec_init:
    - "command1 to exec"
    - "command2 to exec"
 - site: mysite2.com

then i have play with the following task

- name: Execute init scripts for all sites
  shell: "{{item.1}}"
  with_subelements: 
    - sites
    - exec_init
  when: item.0.exec_init is defined

The idea here is that i will have multiple "Site" definitions with dozens of other properties in my vars, then i would like to execute multiple Shell script commands for those sites having "exec_init" defined

Doing it this way it just always skip executing the task, i've tried this in all combinations i can imagine but i just can't get it to work...

Is this the proper way of doing it? maybe i'm trying to achieve something that doesn't make sense?

Thanks for your help

Upvotes: 12

Views: 10622

Answers (5)

Dmitriy Rabotyagov
Dmitriy Rabotyagov

Reputation: 140

The valid solution here (considering ansible 2.7+) is to use loop instead of with_subelements. As with loop you can apply subelements filter, that has skip_missing option (ie what @hkariti suggested in option 3 but in proper way).

So, code should look like:

- name: Execute init scripts for all sites
  shell: "{{ item.1 }}"
  loop: "{{ sites | subelements('exec_init', skip_missing=True) }}"

Upvotes: 7

Vidya
Vidya

Reputation: 41

This works for me. I am using version 2.1.1 Just add the third element in the subelements list as shown

- name: Iterate over something
  with_subelements:
     - "{{ unit }}"
     - config
     - skip_missing: True

Upvotes: 4

blockchaindev
blockchaindev

Reputation: 3196

And yet another way, with the skip_missing flag (Ansible 2.0+):

- name: nested loop skip missing elements
  with_subelements:
    - sites
    - exec_init
    - flags:
      skip_missing: true

Upvotes: 5

user5066686
user5066686

Reputation: 191

There's another way, try:

- debug: "var=item"
  with_subelements:
    - "{{ sites | selectattr('exec_init', 'defined') | list }}"
    - exec_init

Thanks to: https://github.com/PublicaMundi/ansible-plugins/blob/master/lookup_plugins/subelements_if_exist.py

Upvotes: 19

hkariti
hkariti

Reputation: 1719

Hm, looks like having a non-uniform structure for the elements in sites is something with_subelements doesn't like. And also that item doesn't contain the subelement you specified in the with_subelements list. You can do several things:

  1. Make sure to have an exec_init list, even if it's empty. with_subelements will skip items with empty subelements. I think this is the best option, although a bit inconvenient when writing the playbook.

  2. Don't use with_subelements and batch execute yourself (a bit ugly):

    - name: Execute init scripts for all sites
      shell: "echo '{{item.exec_init | join(';')}}' | bash"
      when: item.exec_init is defined
      with_items: sites
    
  3. Customize with_subelements so that it would items with the missing subelement. You can copy the original (mine is in /usr/local/lib/python2.7/dist-packages/ansible/runner/lookup_plugins/with_subelements.py) and put it in a lookup_plugins directory next to your playbook, under a different name (say subelements_missingok.py). Then change line 59 from:

    raise errors.AnsibleError("could not find '%s' key in iterated item '%s'" % (subelement, item0))
    

    to:

    continue
    

    Then your task can look like this:

    - name: Execute init scripts for all sites
      debug: "msg={{item.1}}"
      with_subelements_missingok:
        - sites
        - exec_init
    

Upvotes: 6

Related Questions