Ansible get ip from inventory - ansible

I have role for install & configure elasticsearchm and here is snippet from my elasticsearch.yml file ( to get cluster ip addresses )
discovery.seed_hosts: "{% set comma = joiner(", ") %}{% for host in groups['elasticsearch-tashkent'] -%}{{ comma() }}{{ hostvars[host]['ansible_eth0']['ipv4']['address']}}{%- endfor -%}"
and here is my inventory file
[elasticsearch-tashkent]
elasticsearch-01 ansible_host=178.154.241.134
and all works perfectly! but what should I do if, for example, a second group of hosts with a different name is added to my inventory file? how to make this block more flexible?
"{% set comma = joiner(", ") %}{% for host in groups['elasticsearch-tashkent'] -%}{{ comma() }}{{ hostvars[host]['ansible_eth0']['ipv4']['address']}}{%- endfor -%}"

Related

Jinja2 ansible host groups

I try to build elasticsearch cluster with ansible and I have a problem with jinja2.
How can I set in jinja2 IP addresses of other hosts?
I have in my inventory.ini:
[elasticsearch]
192.168.0.1
192.168.0.2
192.168.0.3
and I want in jinja2 template to pass two addresses 192.168.0.2,192.168.0.3 when there is more hosts than 1, to look something like that:
- discovery.seeds_hosts=192.168.0.2,192.168.0.3
but when is one host in inventory.ini
it should look like this:
- discovery.seeds_hosts=192.168.0.1
I tried with something like this(hostnames):
{% for host in groups['elasticsearch'] %}{% if host == ansible_host %}{% else %}{%if loop.index0 > 0 %}{% endif %}elasticsearch-{{ loop.index }}{% if not loop.last %},{% endif %}{% endif %}{% endfor %}
and it works only for more than 1 host in elasticsearch group. If in the inventory.ini is only one host, the variable discovery.seeds_hosts will be empty.
Ansible version:
ansible [core 2.14.1]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.10/site-packages/ansible
ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.10.9 (main, Dec 8 2022, 01:46:27) [GCC 10.2.1 20210110] (/usr/local/bin/python)
jinja version = 3.1.2
libyaml = True
You could use a conditional that checks the number of hosts:
{% if groups.elasticsearch|length > 1 %}
- discovery.seeds_hosts={{ groups.elasticsearch[1:]|join(',') }}
{% else %}
- discovery.seeds_hosts={{ groups.elasticsearch.0 }}
{% endif %}
I've tested this locally, and with multiple hosts in the inventory:
[elasticsearch]
192.168.0.1
192.168.0.2
192.168.0.3
This produces:
- discovery.seeds_hosts=192.168.0.2,192.168.0.3
With a single host in the inventory:
[elasticsearch]
192.168.0.1
This produces:
- discovery.seeds_hosts=192.168.0.1

How to exclude an host in Ansible Jinja2 template for-loop?

I have a template named foo.yml.j2 used in an Ansible task to generate a foo.yml file:
{% for host in ansible_play_hosts_all %}
{{ host }},
{% endfor %}
Everything works fine except I need something like the following statement: For every host in ansible_play_hosts_all except for host==bar do this or that.
Is this achievable or the only way to do this is to categorize my hosts in different groups and use ansible_play_hosts_group?
You can use the reject filter to take your host out of the list so that it is not part of your loop:
{% for host in ansible_play_hosts_all | reject('==', 'bar') %}
{{ host }},
{% endfor %}
There are more options. The trivial one is using the condition in Jinja
{% for host in ansible_play_hosts_all %}
{% if host != 'bar' %}
{{ host }},
{% endif %}
{% endfor %}
Fit the format to your needs.
Example of a complete playbook for testing
- hosts: host_1,host_2,host_3
gather_facts: false
tasks:
- debug:
msg: |
{% for host in ansible_play_hosts_all %}
{% if host != 'host_2' %}
{{ host }},
{% endif %}
{% endfor %}
run_once: true
gives (abridged)
msg: |-
host_1,
host_3,
The next option is to remove blacklisted hosts from the loop, e.g.
blacklist: [host_2]
The template below gives the same result
{% for host in ansible_play_hosts_all|difference(blacklist) %}
{{ host }},
{% endfor %}

Ansible concat vars to string

I've spent most of the day trying to solve this problem and have thus far failed. I am building some playbooks to automate functions in Splunk, and am attempting to convert a list of hosts from an inventory group E.G.
[search_head]
1.2.3.4
5.6.7.8
My expected (desired) result from the debug output of the play should be:
https://1.2.3.4:8089, https://5.6.7.8:8089
I am attempting to complete this by running the following playbook against a running host:
---
- name: Build search head list to initialize the captain
hosts: search_head
remote_user: ansible
vars:
inventory_file: ./inventory-ec2-single-site
search_head_uri: "{{ lookup('template', './bootstrap-sh-deployer.j2') }}"
pre_tasks:
- include_vars:
dir: 'group_vars'
extensions:
- yml
- yaml
tasks:
- name: dump array
debug:
msg: "{{ search_head_uri }}"`
With the template bootstrap-sh-deployer.j2:
{%- set search_head_uri = [] %}
{% for host in groups['search_head'] %}
{%- if search_head_uri.append("https://{{ host }}:8089") %}
{%- endif %}
{%- if not loop.last %}, {% endif -%}
{%- endfor %}
However, the current play returns search_head_uri: ", " which tells me that the loop is running, but {{ host }} is not resolving.
Once you open a Jinja2 expression or a statement you should use Jinja2 syntax. You cannot nest them (i.e. you can't use {{ }} inside {% %}).
{%- if search_head_uri.append("https://" + host + ":8089") %}
This worked - Combination of the answer above to fix jinja formatting and using hostvars to get to the ansible_nodename.
{%- set search_head_uri = [] %}
{% for host in groups['search_head'] %}
{{ "https://" + hostvars[host]['ansible_nodename'] + ":8089" }}
{%- if not loop.last %}, {% endif -%}
{%- endfor %}

How to set concatenated string variable in Ansible template based on a condition

I need to create a string in a template that can change between hosts, and it needs to be in the form of:
"cores": "0,1,2,3"
And the reason the string is "0,1,2,3" in this example is because the host has 4 processor cores.
So I got stuck with something which seems too convoluted to me and I'm not even sure how to use this core_count variable in my template file.
{% set core_count = '' %}
{% for i in range(ansible_processor_cores) %}
{% set core_count = core_count ~ i %}
{% if not loop.last %}
{% set core_count = core_count ~ ',' %}
{% endif %}
{% endfor %}
There are many handy lookup plugins in Ansible. Take sequence:
- hosts: localhost
gather_facts: yes
tasks:
- debug:
msg: '"cores": "{{ lookup("sequence","start=0 count="+(ansible_processor_cores|string)) }}"'

Ansible nodes IPs as the one string

I use Vagrant with Ansible. In my playbook I have the following variable:
seeds: '192.168.56.11,192.168.56.12'
192.168.56.11 and 192.168.56.12 here are IP addresses of multi-machine Vagrant configuration.
Can I do my configuration more flexible using Ansible i.e. can Ansible compose this string programmatically for me?
You can use Jinja2 to template out your variable from other variables.
So if we have a list of things like this:
seeds:
- 192.168.56.11
- 192.168.56.12
We can turn that into a comma delimited string by looping through it with something like this:
seeds_string: '{% for seed in seeds %} {{ seed }}{% if not loop.last %},{% endif %}{% endfor %}'
As for getting the IP addresses of hosts in your inventory we can access facts about other hosts than the one being configured by using the groups and hostvars magic variables.
So to get the IP addresses of all hosts in the inventory we could use something like:
{% for host in groups['all'] %}
{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}
Combining this together we can then do something like this:
seeds: '{% for host in groups['all'] %} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}{% if not loop.last %},{% endif %}{% endfor %}'

Resources