2

Imagine this dict on 4 different hosts.

# on host 1
my_dict:
  ip: 10.0.0.111
  roles:
    - name: something
      observer: false

# on host 2
my_dict:
  ip: 10.0.0.112
  roles:
    - name: something
      observer: false

# on host 3
my_dict:
  ip: 10.0.0.113
  roles:
    - name: something
      observer: true

# on host 4
my_dict:
  ip: 10.0.0.114
  roles:
    - name: whatever

When Ansible runs for all 4 hosts I want it to build a variable for each host having the roles name 'something'. The desired output is:

10.0.0.111 10.0.0.112 10.0.0.113:observer

There are 2 requirements:

  • when my_dict.roles.name == 'something' it must add the ip to the var
  • but when my_dict.roles.observer , it must add the ip + ':observer'

I eventually want to use the var in a Jinja template, so to me, the var can be either set via an Ansible task or as a jinja template.

This doesn't work:

- name: set fact for ip
  debug:
    msg: >-
      {{ ansible_play_hosts |
      map('extract', hostvars, ['my_dict', 'ip'] ) |
      join(' ') }}
  when: ???
4
  • Why would your variables and task give 10.0.0.111 10.0.0.112 10.0.0.113:observer with the provided example? Is observer the variable name and true or false the values, and you're trying to run a command on hosts that have that variable set? I'm admittedly confused as to what you're trying to accomplish. Commented Feb 18, 2022 at 22:08
  • I don't understand your question. But i'll edit my question to make it more clear. Commented Feb 18, 2022 at 22:12
  • It would be helpful to know what the variable is being constructed for as well. My guess is to template a configuration file for a cluster of some type where hosts take on some type of persona based upon the variable. Commented Feb 18, 2022 at 22:50
  • 1
    Benoit, that was a copy/paste mistake. And Joe, yes, your guess is correct. Commented Feb 18, 2022 at 23:06

1 Answer 1

2

You could create two lists:

  • one with what should be postfixed to the IPs with the condition based on the observer property
  • the other one with the IPs

And then zip them back together.

Given:

- debug:
    msg: >-
      {{ 
        _ips | zip(_is_observer) | map('join') | join(' ')
      }}
  vars:
    _hosts: >-
      {{ 
        hostvars 
        | dict2items 
        | selectattr('key', 'in', ansible_play_hosts)
        | selectattr('value.my_dict.roles.0.name', '==', 'something')
      }}
    _is_observer: >-
      {{ 
        _hosts
        | map(attribute='value.my_dict.roles.0.observer') 
        | map('replace', false, '') 
        | map('replace', true, ':observer')
      }}
    _ips: >-
      {{
        _hosts
        | map(attribute='value.my_dict.ip')
      }}

This yields:

TASK [debug] *************************************************************
ok: [localhost] => 
  msg: 10.0.0.111 10.0.0.112 10.0.0.113:observer
Sign up to request clarification or add additional context in comments.

4 Comments

That should do, and it has been DRY'ed
I love these answers. I really have to learn how to do this stuff.
Simple question, can I also easily configure it, so that when 'observer: true' that IP omitted from the list, the result would then be: '10.0.0.111 10.0.0.112'
Sure, a list without the observer(s) would be "{{ _hosts | selectattr('value.my_dict.roles.0.observer') }}" which is a short version of "{{ _hosts | selectattr('value.my_dict.roles.0.observer', '==', true) }}"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.