Exit from Ansible Loop - ansible

I have a play that search for available storage in vm datastore to find disk space.
- name: Select the datastore which has minimum of 500GB
include_role: find_disk_space_datastore
with_item:
-datastore_001
-datastore_002
-datastore_003
Role: roles/find_disk_space_datastore/tasks/main.yml
- name: search for domain {{ item }}
blah ...blah save the free space on {{ disk_space }} variable
- name: find the datastore
setfact:
datastore_name:
when: {{ disk_space }} >> 500
Assume I have found free space in second item which is datastore_002 instead of going to datastore_003 in the loop how can I exit from there ?????
note - I need to break the loop in between.
thank you inadvanced

Related

How to pass multiple lines as value to Ansible

I'm trying to pass multiple lines as a value to my Ansible file But it's only just passing the first line.
cat slot_number.txt
slot4
slot2
slot1
slot3
My ansible file as below
update_bios.yml
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{slot_number}} off"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{slot_number}} --update --bios"
ansible-playbook -l myhosts update_bios.yml -e "slot_number=$(cat slot_number.txt)"
I want to run my command like below:
/usr/local/bin/power-util slot1 off
/usr/local/bin/power-util slot2 off
/usr/local/bin/power-util slot3 off
Edit after understanding the question details:
Map the slot numbers to a host:
cat slot_numbers.yml
slot_numbers:
server1:
-slot4
-slot2
server2:
-slot1
-slot3
After that distinguish between hosts in the playbook as follows,
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{ item }} off"
loop: "{{ slot_numbers[inventory_hostname] }}"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{ item }} --update --bios"
loop: "{{ slot_numbers[inventory_hostname] }}"
In this approach we are doing the mapping between hosts and slots in the slot_numbers variable file. Alternatively, you can define the variables as host variables also.
Previous answer:
Ansible understands yaml and json.
Also, playbook needs to handle what happens with the data available to it via variables. So, if you want to loop through a list of slot numbers you have to tell your playbook to do exactly that.
Define your slot number file as follows:
In this file, you are creating a list of slot numbers and assigning it to the key 'slot_numbers'
cat slot_numbers.yml
slot_numbers:
-slot4
-slot2
-slot1
-slot3
Modify your update_bios.yml to loop through the slot numbers you are passing,
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{ item }} off"
loop: "{{ slot_numbers }}"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{ item }} --update --bios"
loop: "{{ slot_numbers }}"
then use:
ansible-playbook -l myhosts update_bios.yml -e "#slot_numbers.yml"
Detailed documentation of how to use variables and playbooks can be found here
Something like this should work.
vars:
slot_numbers: "{{ lookup('file', './slot_number.txt').splitlines() }}"
tasks:
- name: Powering off slot number
command: "/usr/local/bin/power-util {{ item }} off"
loop: "{{ slot_numbers }}"
- name: Starting to update BIOS
command: "/usr/bin/fw-util {{ item }} --update --bios"
loop: "{{ slot_numbers }}"

How can I use Ansible to check usage of specific file system

I need to create a check very similar to that one explained here: Ansible to check diskspace for mounts mentioned as variable
Except I need it only for specified paths (for example /var).
{{ ansible_mounts }} is an array of dictionaries, each containing variable mount that is the actual path. I need to perform the check in a loop for all items in {{ ansible_mounts }} only if mount is equal to some value.
Example of what I want to achieve in pseudo code:
foreach (mountpoint in ansible_mounts)
{
if (mountpoint["mount"] == "/var" || mountpoint["mount"] == "/opt")
{
// do the check
}
}
How can I do this in Jinja / Ansible? This code does the check for every single item. I need to filter it only for explicitly specified paths:
- name: Ensure that free space on the tested volume is greater than 15%
assert:
that:
- mount.size_available > mount.size_total|float * 0.15
msg: Disk space has reached 85% threshold
vars:
mount: "{{ ansible_mounts | selectattr('mount','equalto',item.mount) | list | first }}"
with_items:
- "{{ ansible_mounts }}"
you'll need to add a when condition, for example
vars:
my_mounts:
- '/var/log'
- '/var/logs/foo'
tasks:
- name: do the check
when: item.mount in my_mounts
with_items: '{{ ansible_mounts }}'

loop using with_sequence over with_subelement in ansible

I am trying to loop over a subelement variables using the a loop like with_sequence,
For the moment I have :
---
- hosts: corosync
gather_facts: no
vars:
host_list:
- node_one
- node_two
list_services:
- group: ALPHA
services:
- name: DHCP
directory: /etc/dhcp
- name: DNS
directory : /etc/dns
- group: BETA
services:
- name: SSH
directory: /etc/ssh
- name: FTP
directory: /ztc/ftp
tasks:
- name: create group-services
debug:
msg: "the service name is {{ item.0.group}}-{{ item.1.name}} , directory is {{ item.1.directory }}"
with_subelements:
- "{{ list_services }}"
- services
Since I have 2 nodes in my cluster
node_one
node_two
I want to deplucate each service like below :
{{ item.0.group}}-{{host_id}}-{{ item.1.name}}
with {{ host_id }} a list that equal ['0','1'] since I have 2 nodes
and the with_subelement function loop over the {{ host_id }} twice since we have two nodes, what gives :
ALPHA-0-DHCP
ALPHA-0-DNS
ALPHA-1-DHCP
ALPHA-1-DNS
BETA-0-SSH
BETA-0-FTP
BETA-1-SSH
BETA-1-FTP
I want to use something like with_sequence function beside with_subelement like
with_sequence: start=0, end={{ groups['host_list']|length}}
Any suggestions please
The loop declaration introduced in Ansible 2.5 makes it pretty straightforward ー you just need to combine the two patterns replacing legacy with_sequence and legacy with_subelements:
- name: create group-services
debug:
msg: "{{item.1.0.group}}-{{item.0}}-{{item.1.1.name}}"
loop: "{{ range(0, host_list|length) | product(list_services|subelements('services')) | list }}"

Generate single configuration for multiple hosts from Jinja2 template

Problem: I've got 2 tasks that are executed sequentially as (expected) given the Ansible nature. As I understand, you can only execute one module per each task.
Task 1 - Gather facts information (serials, versions, etc) from network devices.
Task 2 - Render a template with the info gathered in Task1
Ideal Result: Since I'm looping through loads of network devices my ideal result would be to choose one device at a time, gather the information from it and then render a template with that information, next go to the other device on the loop and so on.
Approach: I've thought to keep the same syntax, On task 1 save the facts on a file (.json) and on task 2, read the JSON file and get the variables I'm interested in.
Is there any better way to do this? (Probably there's more than one)
What I've got currently doesn't fit my purpose as when the template renders, it only contains information about the last device:
Tasks: roles/juniper.junos/tasks/main.yaml
- name: 1 - Gathering Facts
junos_get_facts:
host: "{{ inventory_hostname}}"
user: ""
passwd: ""
savedir: "~/Ansible/Ouput/Facts"
ignore_errors: True
register: junos
- name: 2 - Creating the template
template:
src="~/Ansible/roles/juniper.junos/templates/template.j2"
dest="~/Ansible/Ouput/Facts/Device_facts.yml"
Template: ~/Ansible/roles/juniper.junos/templates/template.j2
{% for host in groups['OOB_AMS'] %}
ANSIBLE NAME: {{ inventory_hostname}}
HOSTNAME: {{ junos.facts.hostname }}
MODEL: {{ junos.facts.model }}
SERIAL: {{ junos.facts.serialnumber }}
VERSION: {{ junos.facts.model }}
UP TIME: {{ junos.facts.RE0.up_time }}
{% endfor %}
IDEAL Output:"~/Ansible/Ouput/Facts/Device_facts.yml"
ANSIBLE NAME: DEVICE 1
HOSTNAME: DEVICE 1 HOSTNAME
MODEL: DEVICE 1 MODEL
SERIAL: DEVICE 1 SERIAL
VERSION: DEVICE 1 VERSION
UP TIME: DEVICE 1 UP TIME
ANSIBLE NAME: DEVICE 2
HOSTNAME: DEVICE 2 HOSTNAME
MODEL: DEVICE 2 MODEL
SERIAL: DEVICE 2 SERIAL
VERSION: DEVICE 2 VERSION
UP TIME: DEVICE 2 UP TIME
ANSIBLE NAME: DEVICE 3
HOSTNAME: DEVICE 3 HOSTNAME
MODEL: DEVICE 3 MODEL
SERIAL: DEVICE 3 SERIAL
VERSION: DEVICE 3 VERSION
UP TIME: DEVICE 3 UP TIME
You wrote a for-loop with a host variable, but not used it even once.
Change the template to:
{% for host in ansible_play_hosts %}
ANSIBLE NAME: {{ hostvars[host].inventory_hostname}}
HOSTNAME: {{ hostvars[host].junos.facts.hostname }}
MODEL: {{ hostvars[host].junos.facts.model }}
SERIAL: {{ hostvars[host].junos.facts.serialnumber }}
VERSION: {{ hostvars[host].junos.facts.model }}
UP TIME: {{ hostvars[host].junos.facts.RE0.up_time }}
{% endfor %}
Looping over groups['OOB_AMS'] was not bad, but hardcoding group name seems unnecessary for your case. Instead you can use: ansible_play_hosts (play_hosts before version 2.2).
Also for clarity you might add run_once: true to the template task. This is not critical, because the template generates the same output in each iteration, so the subsequent runs will be skipped anyway, but there is no need to do it several times.

Ansible to check diskspace for mounts mentioned as variable

I am new to ansible and currently working on a play which will see if disk space of remote machines has reached 70% threshold. If they have reached it should throw error.
i found a good example at : Using ansible to manage disk space
but at this example the mount names are hard coded. And my requirement is to pass them dynamically. So i wrote below code which seems to not work:
name: test for available disk space
assert:
that:
- not {{ item.mount == '{{mountname}}' and ( item.size_available <
item.size_total - ( item.size_total|float * 0.7 ) ) }}
with_items: '{{ansible_mounts}}'
ignore_errors: yes
register: disk_free
name: Fail the play
fail: msg="disk space has reached 70% threshold"
when: disk_free|failed
This play works when i use:
item.mount == '/var/app'
Is there any way to enter mountname dynamically ? and can i enter multiple mount names ??
I am using ansible 2.3 on rhel
Thanks in advance :)
Try this:
name: Ensure that free space on {{ mountname }} is grater than 30%
assert:
that: mount.size_available > mount.size_total|float * 0.3
msg: disk space has reached 70% threshold
vars:
mount: "{{ ansible_mounts | selectattr('mount','equalto',mountname) | list | first }}"
that is a raw Jinja2 expression, don't use curly brackets in it.
why do you use separate fail task, if assert can fail with a message?
For those who cannot use selectattr (like me), here is a variant of the first answer using when and with_items to select the mount point to check.
name: 'Ensure that free space on {{ mountname }} is grater than 30%'
assert:
that: item.size_available > item.size_total|float * 0.3
msg: 'disk space has reached 70% threshold'
when: item.mount == mountname
with_items: '{{ ansible_mounts }}'
Note: To be able to use the variable {{ ansible_mounts }} you need to turn gather_facts to yes that can be limited to gather_subset=!all,hardware.
I'm running Ansible 2.5 and was able to get Konstantin Suvorov's solution to work with a slight mod by adding with_items. Sample code below:
- name: Ensure that free space on the tested volume is greater than 15%
assert:
that:
- mount.size_available > mount.size_total|float * 0.15
msg: Disk space has reached 85% threshold
vars:
mount: "{{ ansible_mounts | selectattr('mount','equalto',item.mount) | list | first }}"
with_items:
- "{{ ansible_mounts }}"

Resources