ansible - get host by variable - ansible

I would like to filter hosts by a variable set on them. For example:
I have a group of hosts, one is master, the rest are slaves. On the master the variable replica_type: master is set. I would now like to get the master server dynamically.
My working aproach is:
- set_fact:
master_server_string: >-
{% for server in groups.my_servers %}
{% if hostvars[server]['replica_type']=='master' -%}
{{ server }}
{% endif %}
{% endfor %}
- set_fact:
master_server: "{{ master_server_string|trim }}"
Is there a way to perform the same with a filter (or at least in a single task)?
e.g. something like
"{{ server for server in groups.my_servers if hostvars[server]['replica_type'] == 'master' }}"

I believe you are looking for the group_by module.
https://docs.ansible.com/ansible/latest/modules/group_by_module.html
- group_by:
key: {{ replica_type }}

You can pull a value out of a group of hosts by a property by looping the group and checking for a matching value like this too:
- set_fact:
master_server: "{{ item }}"
with_items: "{{ groups.my_servers }}"
when: hostvars[item].replica_type == 'master'

Related

Not able to use for loop in Ansible

- name: checking whether symbolic link created or not
{% for item in ['/opt/lsf/bin/bsh','/opt/praveen'] %}
{{ item }}:
debug:
msg: "{{ item }}"
{% endfor %}
Please let me know if anything is wrong in this
Resolved Using Ansible:
You need to create a Playbook.yaml :
---
- hosts: all
become: yes # ask ansible to be a super user (working like sudo )
roles:
- template-module
also Create a role for templates:
In you main.yaml put this code:
- name: checking whether symbolic link created or not
template:
src: test.j2
dest: /tmp/test.yaml
and in you jinaj2 "test.j2" template put this ;
{% for item in ['/opt/lsf/bin/bsh','/opt/praveen'] %}
{{ item }}:
debug:
msg: "{{ item }}"
{% endfor %}
This can hel you to resolve your issue.

Ansible Jinja2 template for loop

I have two linux servers:
- server1: ip: 10.241.55.6, hostname: server1
- server2: ip: 10.242.55.7, hostname: server2
I have created an ansible inventory file named servers with the content bellow:
[IC]
10.241.55.6
10.241.55.7
Now I have created this jinja2 inventory template file: test.j2 with this content:
[IC]
{% for hostip in groups['IC'] %}
{% if hostip == ansible_default_ipv4.address %}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{% endif %}
{% endfor %}
And I'm running this ansible playbook:
---
- name: Generate portal inventory file
hosts: all
tasks:
- name: Generate inventory
delegate_to: localhost
template:
src: inventory/test.j2
dest: inventory/test
The command is: ansible-playbook -i inventory/servers generate-inventory.yml
The final goal is that ansible connects to each of the servers from the inventory files and then based on the jinja2 inventory template, it creates a new inventory file with this format:
[IC]
10.241.55.6 default_hostname=hostname_of_the_server_with_that_ip
and so on...
The issue here with the for loop is that all the entries are with the same server ip (while I should have an entry for each of the servers with their respective hostnames):
[IC]
10.241.55.6 default_hostname=server1
10.241.55.6 default_hostname=server2
What I'm missing here? Also if there is any other better way to achieve this please let me know.
You're using the same variable twice in the template...
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
{{ ansible_default_ipv4.address }} default_hostname={{ ansible_nodename }}
...so of course you're getting two identical lines. It sounds like you want to access the per-host value of this variable, which means you need to access it via hostvars.
Maybe something like this:
[IC]
{% for host in groups['IC'] %}
{{ hostvars[host].ansible_default_ipv4.address }} default_hostname={{ hostvars[host].ansible_nodename }}
{% endfor %}

Ansible: Loop over dict and filetree

How to loop over dict and filetree? I want to recursively template files with .j2 suffix (key) to destination location (value), also basename should be renamed (remove .j2 suffix). Its a perfect use case. Unfortunatelly ansible is not good with complex data structures.
Input:
vars:
applications:
application1:
svcpaths:
localfolder/bardir1: remotefolder/bardir1
localfolder/bardir2: remotefolder/bardir2
localfolder/bardir3: remotefolder/bardir3
application2:
svcpaths:
localfolder/bardir5: remotefolder/bardir5
localfolder/bardir6: remotefolder/bardir6
My try:
- name: Files to template
template:
src: "{{ item.src }}"
dest: "{{ item.destination }}/{{ item.name | regex_replace('.j2','') }}"
loop: |
[
{% for c in applications %}
{% if applications[c]['svcpaths'] is defined and applications[c]['svcpaths'] |list|length >0 %}
{% for o,m in applications[c]['svcpaths'].items() %}
{% for i in lookup('filetree', o ) %}
{% if i.state == 'file' and i.path | regex_search('.\.j2') %}
{
"name": "{{ i.path }}",
"src": "{{ i.src }}",
"destination": "{{ m }}"
},
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
]
I know that using jinja in plays is not good and I want to avoid it, if its possible. Also input datastructure should not be changed.
Thannks
If I understand what you're trying to do, I think there is a reasonably simple solution. If you write a task file like this called template_files.yml:
---
- name: render templates to dest_dir
loop: "{{ query('filetree', src_dir) }}"
# we need this to avoid conflicts with the "item" variable in
# the calling playbook.
loop_control:
loop_var: template
when: template.src.endswith('.j2')
template:
src: "{{ template.src }}"
dest: "{{ dest_dir }}/{{ (template.src|basename)[:-3] }}"
Then you can write a playbook like this:
---
- hosts: localhost
gather_facts: false
vars:
applications:
application1:
svcpaths:
localfolder/bardir1: /tmp/remotefolder/bardir1
localfolder/bardir2: /tmp/remotefolder/bardir2
localfolder/bardir3: /tmp/remotefolder/bardir3
application2:
svcpaths:
localfolder/bardir5: /tmp/remotefolder/bardir5
localfolder/bardir6: /tmp/remotefolder/bardir6
tasks:
# generate a list of {key: src, value: destination}
# dictionaries from your data structure.
- set_fact:
templates: "{{ templates|default([]) + item|dict2items }}"
loop: "{{ applications|json_query('*.svcpaths')}}"
# show what the generated variable looks like
- debug:
var: templates
# template all the things
- include_tasks: template_files.yml
loop: "{{ templates }}"
vars:
src_dir: "{{ item.key }}"
dest_dir: "{{ item.value }}"
Given that I have a set of local files that look like this:
localfolder/bardir1/example.txt.j2
localfolder/bardir2/example.txt.j2
localfolder/bardir3/example.txt.j2
localfolder/bardir5/example.txt.j2
localfolder/bardir6/example.txt.j2
Running the playbook results in:
/tmp/remotefolder/bardir6/example.txt
/tmp/remotefolder/bardir5/example.txt
/tmp/remotefolder/bardir3/example.txt
/tmp/remotefolder/bardir2/example.txt
/tmp/remotefolder/bardir1/example.txt
I think that's probably easier to read and understand than the Jinja-template based solution you're using.

How to get all inventory variables for hosts in same group (except the current host)?

Suppose we have an ansible inventory like this:
[somegroup]
host1 secondaryIp=1.0.0.0
host2 secondaryIp=1.0.0.1
host3 secondaryIp=1.0.0.2
While running a task (specifically template module) is there a way to get the list of secondaryIp's for "all other hosts" in the group [somegroup] ??
I tried searching ansible filters: http://docs.ansible.com/ansible/latest/playbooks_filters.html#list-filters
My only working idea was to do this in inventory:
[somegroup]
host1 secondaryIp=1.0.0.0 otherIps="1.0.0.1,1.0.0.2"
host2 secondaryIp=1.0.0.1 otherIps="1.0.0.0,1.0.0.2"
host3 secondaryIp=1.0.0.2 otherIps="1.0.0.0,1.0.0.1"
You can get a list of hosts in the group using groups['somegroup'] and access their variables using hostvars. To exclude the host ansible is currently running on you just need to check if the current host in the list equals inventory_hostname. Here's how it works in practice.
In a template
{% for host in groups['somegroup'] %}
{% if host != inventory_hostname %}
{{ hostvars[host]['secondaryIp'] }}
{% endif %}
{% endfor %}
In a task
- name: debug
debug:
msg: "{{ hostvars[item]['secondaryIp'] }}"
when: item != inventory_hostname
with_items: "{{ groups['somegroup'] }}"

Jinja2 in Ansible Playbook

how can i loop in an ansible playbook over gathered facts?
I tried:
...
haproxy_backends:
- name: 'backend'
servers:
{% for host in groups['app-servers'] %}
- name: "{{ hostvars[host]['ansible_hostname'] }}"
ip: "{{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}"
{% endfor %}
But this doesn't work, it results in a Syntax Error. Is it even possible to use jinja in a playbook?
I use a ansible galaxy role (info.haproxy) and i don't want to change the provided templates.
No you can't do this.
This has to be done in a template, something like :
template/haproxy.cfg.j2 :
...
{% for host in groups['app-servers'] %}
backend {{ hostvars[host]['ansible_hostname'] }}
server {{ hostvars[host]['ansible_hostname'] }} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}:1234 check inter 5000 slowstart 2m
{% endfor %}
...
and use :
tasks:
- name: Deploy haproxy config
template: src=templatepath/haproxy.cfg.j2 dest=/etc/haproxy/haproxy.cfg
You get the idea, YMMV.
Good luck.

Resources