How can I loop around an array in this situation? - loops

I have the following variables in my playbook:
frontends:
- domain01.fr
- domain02.fr
- domain03.fr
- domain04.fr
I need to be able to write the following in a file via an Ansible playbook step:
tcp://domain01.fr:11211,tcp://domain02.fr:11211,tcp://domain03.fr:11211,tcp://domain04.fr:11211
I came up with the following solution, but I'm not pleased with it.
- name: Setting up Apache (2/2)
lineinfile:
path: /etc/opt/rh/rh-php56/php.ini
regexp: '^session.save_path ='
line: "session.save_path = 'tcp://{{ frontends | join(':11211,tcp://') }}'"
I can't hardwrite the domains inside the lineinfile method, because it depends a lot, plus there are situations where I only have 2 domains instead of 4.
Is it possible to have something like the following:
- name: Setting up Apache (2/2)
lineinfile:
path: /etc/opt/rh/rh-php56/php.ini
regexp: '^session.save_path ='
line: "session.save_path = '{% for frontend in frontends %} tcp://{% frontend %}:11211,{% endfor %}'"
Thank you in advance

Yes. It's possible. The line below
regexp: '^session.save_path ='
line: >-
session.save_path ={% for frontend in frontends %}
tcp://{{ frontend }}:11211{% if not loop.last %},{% endif %}{% endfor %}
gives
session.save_path = tcp://domain01.fr:11211, tcp://domain02.fr:11211, tcp://domain03.fr:11211, tcp://domain04.fr:11211

Related

Looping through a list when building group_vars in ansible

I'm rather new to ansible and would like to deploy prometheus-grok-exporter (via ansible-grok-exporter role) with a specific configuration for all my nodes that run the cacti application.
My inventory is like this:
cacti_first ansible_host=192.168.50.50
cacti_second ansible_host=192.168.50.51
[group__cacti]
cacti_first
cacti_second
Inside group_vars/group__cacti I want to add something like this:
---
prometheus_grok_services_template:
- name: cacti_metrics
config_version: 3
input:
type: file
paths:
{% for cacti_dir in cacti_path %}
- "{{cacti_dir}}/log/cacti.log"
{% endfor %}
readall: false
extaConfigContinuesFromHere: true
And I have host config like this:
host_vars/cacti_first:
---
cacti_path:
- /usr/share/cacti
prometheus_grok_services:
- prometheus_grok_services_template
host_vars/cacti_second:
---
cacti_path:
- /usr/share/cacti
- /usr/share/cacti2
prometheus_grok_services:
- prometheus_grok_services_template
Inside the playbook I do a loop for prometheus_grok_services and use the yaml data to provision the service.
Now - this works as long as I don't try to use a loop inside group_vars/group__cacti. ansible-inventory reports that:
$ ansible-inventory -i hosts --list cacti_second
ERROR! Syntax Error while loading YAML.
found character that cannot start any token
The error appears to be in '/home/bastion/ansible-playbooks/group_vars/group__cacti': line 8, column 10, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
paths:
{% for cacti_dir in cacti_path %}
^ here
So, I'd like to ask - is it allowed to do jinja loops to build yaml for group vars? Is it a syntax error on my end? How am I supposed to template it?
I'd like to avoid moving the block to host vars (which I know works), mostly because it's a large piece of code (about 2KB of yaml config) and it's not as elegant as using group vars.
Thanks!
Fix the group_vars. For example
shell> cat group_vars/group__cacti
---
prometheus_grok_services_template:
- name: cacti_metrics
config_version: 3
input:
type: file
paths: "{{ paths_str|from_yaml }}"
readall: false
extaConfigContinuesFromHere: true
paths_str: |
{% for cacti_dir in cacti_path %}
- {{ cacti_dir }}/log/cacti.log
{% endfor %}
Then, the playbook
- hosts: group__cacti
gather_facts: false
tasks:
- debug:
msg: "{{ lookup('vars', item) }}"
loop: "{{ prometheus_grok_services }}"
gives
ok: [cacti_first] => (item=prometheus_grok_services_template) =>
msg:
- config_version: 3
input:
extaConfigContinuesFromHere: true
paths:
- /usr/share/cacti/log/cacti.log
readall: false
type: file
name: cacti_metrics
ok: [cacti_second] => (item=prometheus_grok_services_template) =>
msg:
- config_version: 3
input:
extaConfigContinuesFromHere: true
paths:
- /usr/share/cacti/log/cacti.log
- /usr/share/cacti2/log/cacti.log
readall: false
type: file
name: cacti_metrics
You can't use this kind of for loop in a variables file or a playbook - it only works in template files. To acheive what you're after, you can use product filters, as described https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#products
In your example, you would have:
---
prometheus_grok_services_template:
- name: cacti_metrics
config_version: 3
input:
type: file
paths: "{{ cacti_path | product(['/log/cacti.log']) | map('join') | list }}"
readall: false
extaConfigContinuesFromHere: true

jinja loop on hosts from a group failing when one node of this group is down

I have this piece of code
---
- name: gather facts
hosts: all #
- hosts: proxy
tasks:
- blockinfile:
path: /etc/example
block: |
{% for n in groups['all'] %}
{{ hostvars[n]['ansible_default_ipv4']['address'] }}
{% endfor %}
if one node from the group all is down, the jinja loop is failing.
I did not find a solution to prevent that (tried with skip_untreachable: true without success for instance). Off course I can comment the down node in the inventory but this is neither convenient nor reliable.
How can I prevent that ?

How to loop over my inventory list in jinja2 (currently using a template in my playbook)

I'm attempting to get the output of some show commands on some network devices. My current code loops over the same host 4 times instead of over all 4 hosts in my inventory file. How do I rectify this ?
Using Ubuntu 16.04 server, Ansible 2.7
My Jinja template:
{% for host in groups.ios_devices %}
{% if not host==inventory_hostname %}
.......
hostname: {{device_info.ansible_facts['ansible_net_hostname']}}
Interfaces: {{int_status}}
.......
{% endif %}
{% endfor %}
## ios_devices is my host inventory file with all ip-addresses##
Playbook:
----------
template:
src: ./template/temp.j2
dest: report.txt
I expected this to run across all my inventory host ip's , but see that the output has loops over the same ip.
An option would be to use 'hostvars'. See below
hostname: {{ hostvars[host].ansible_hostname }}
Interfaces: {{ hostvars[host].ansible_interfaces }}
"In order to do this, Ansible must have already talked to ‘ios_devices’ in the current play, or another play up higher in the playbook. This is the default configuration of ansible." See Caching Facts.
For example, starting the play like below will cache facts about hosts in the group ‘ios_devices’
- hosts: ios_devices
gather_facts: yes
But, this will run the playbook and the 'template' task at each host in the group. To avoid it the 'template' task can be run_once. See below
- template:
src: ./template/temp.j2
dest: report.txt
run_once: true
But, because of the condition in the template (see below), this will exclude from 'report.txt' the host which is the playbook run at.
{% if not host==inventory_hostname %}
Cache the facts about ‘ios_devices’ and run the playbook at a host which is not a member of ‘ios_devices’ if all members of ‘ios_devices’ shall be included in 'report.txt'. See below
- hosts: ios_devices
gather_facts: yes
- hosts: localhost
gather_facts: no
tasks:
- template:
src: ./template/temp.j2
dest: report.txt
Or, remove the condition 'host==inventory_hostname' from the template. The file 'report.txt' will be created at the host the 'template' task is run at, of course.

Ansible update variables for module execution during loop

I need to use ansible to do the following job:
1. send a Http Post reuqest to a WebApi
2. check wheter the response meet requirement, otherwise extract some data from the response
3. send another Http Post request with body filled with the data got in step 2
4. loop step 1 to step 3 until the response meet requirement
My code is as the following, but it did not work. The finished_res seems to be undefined forever inside uri module. what I mean is that the variable finished_res inside the uri module seems to never change because I keep receiving the same request body in server. However, the finished_res outside the loop works, I could see the debug message.
- hosts: all
remote_user: ubuntu
gather_facts: no
vars:
default_job_list: "xxx,yyy,zzz,aaa,bbb"
tasks:
- name: test is jobs finish
uri:
url: "{{ API_URL }}"
method: POST
body: "{% if finished_res is defined %}{{ finished_res.json.remainingJobs }}{% else %}{{ default_job_list }}{% endif %}"
return_content: yes
register: finished_res
changed_when: True
until: finished_res is defined and finished_res.json is defined and finished_res.json.status is defined and finished_res.json.status != "FALSE"
- set_fact:
RES: "{{ finished_res }}"
- debug: msg="{{ RES }}"
Could anybody help me ? Thanks in advance !
At last, I solved the problem in another way : create a new ansible module

ansible template loop jinja2 "i need a line separated with , but the last entry without ,

i want to create an dynamical playbook for my infrastructure.
i need this line in my config:
wsrep_cluster_address='gcomm://192.168.126.38,192.168.126.39,192.168.126.40'
my template looks like this:
wsrep_cluster_address = 'gcomm://{% for host in groups['db-server']%}{{hostvars[host]['ansible_host']}},{% endfor %}'
it works and looks like this on the host:
wsrep_cluster_address = 'gcomm://172.16.120.45,172.16.120.40,172.16.120.42,'
the last comma is breaking my nerves.
Is there a way to tell ansible not to comma the last entry of the loop?
Tanks for any help, have a great day
Found the solution, thanks to my developer.
wsrep_cluster_address = 'gcomm://{% for host in groups['db-server']%}{{hostvars[host]['ansible_host']}}{% if not loop.last %},{% endif %}{% endfor %}'
You saved my Day!
Also if you need line in config file like JSON:
nodelist = ["192.168.126.38","192.168.126.39","192.168.126.40"]
This is your Ansible for this:
nodelist={%for host in groups['mygroup']%}"{{hostvars[host].ansible_eth0.ipv4.address}}"{% if not loop.last %},{% endif %}{% endfor %}
Here is if full example:
- name: Create List of nodes to be added into Cluster
set_fact: nodelist={%for host in groups['mygroup']%}"{{hostvars[host].ansible_eth0.ipv4.address}}"{% if not loop.last %},{% endif %}{% endfor %}
- debug: msg=[{{nodelist}}]
- name: Set Cluster node list in config file
lineinfile:
path: "/etc/myfonfig.cfg"
line: "hosts: [{{ nodelist }}]"
as results you will have the following line in config file:
hosts: ["192.168.126.38","192.168.126.39","192.168.126.40"]

Resources