ansible Iterate var only if var is not empty - ansible

I m trying to iterate over some variables in an ansible role. However, I want to ignore if the var is empty ex:ns3 from below code? I m trying when item length is greater than 0 but it seems not working? any ideas on how to do it?
---
- hosts: localhost
gather_facts: no
vars:
ns1: adm_analytics
ns2: adm_snap
ns3: ""
ns4: adm_eck
tasks:
- name: print namespace loop
with_items:
- "{{ ns1 }}"
- "{{ ns2 }}"
- "{{ ns3 }}"
- "{{ ns4 }}"
include_role:
name: verify_pod_status
vars:
NAMESPACE: "{{ item }}"
when: "{{ item | lenght > 0 }}"

You may have a look into the documentation about Conditionals.
There are some typos in your when clause.
---
- hosts: localhost
gather_facts: false
vars:
ns1: adm_analytics
ns2: adm_snap
ns3: ""
ns4: adm_eck
tasks:
- name: Print namespace loop
debug:
msg: "{{ item }}"
when: item | length > 0
with_items:
- "{{ ns1 }}"
- "{{ ns2 }}"
- "{{ ns3 }}"
- "{{ ns4 }}"
result into an output of
TASK [Print namespace loop] **************
ok: [localhost] => (item=adm_analytics) =>
msg: adm_analytics
ok: [localhost] => (item=adm_snap) =>
msg: adm_snap
ok: [localhost] => (item=adm_eck) =>
msg: adm_eck

when: condition is expanded by default. Fix the syntax
when: item|length > 0
Make your life easier and put the ns* variables into a dictionary. Then you can simply reference the values in the loop instead of listing the variables again. For example, the playbook
shell> cat playbook.yml
- hosts: localhost
gather_facts: no
vars:
ns:
ns1: adm_analytics
ns2: adm_snap
ns3: ""
ns4: adm_eck
tasks:
- name: print namespace loop
include_role:
name: verify_pod_status
loop: "{{ ns.values()|list }}"
vars:
NAMESPACE: "{{ item }}"
when: item|length > 0
and the role
shell> cat roles/verify_pod_status/tasks/main.yml
- debug:
var: NAMESPACE
give (abridged)
TASK [verify_pod_status : debug] ***********************************
skipping: [localhost] => (item=)
TASK [verify_pod_status : debug] ***********************************
ok: [localhost] =>
NAMESPACE: adm_analytics
TASK [verify_pod_status : debug] ***********************************
ok: [localhost] =>
NAMESPACE: adm_snap
TASK [verify_pod_status : debug] ***********************************
ok: [localhost] =>
NAMESPACE: adm_eck

Related

Ansible sum register value

How to get the sum of two hosts with Jinja2 filtering ansible
host1 and host 2
---
- name: Count Check
hosts: MYGROUP
gather_facts: true
user: sv_admin
tasks:
- name: count check
shell: cat /etc/hosts | wc -l
register: command_result
- debug:
var: command_result.stdout
- set_fact:
total_result: "{{ command_result.stdout | map('int') | sum(start=0) }}"
- debug:
msg: "Total count: {{ total_result }}"
Playbook Output
TASK [debug] *****************************************************************
ok: [Host-01] => {
"msg": "Total count: 134"
}
ok: [Host-02] => {
"msg": "Total count: 133"
}
Use extract and sum. For example, the playbook below
shell> cat playbook.yml
- hosts: test_01:test_03
gather_facts: false
tasks:
- shell: cat /etc/hosts | wc -l
register: command_result
- debug:
var: command_result.stdout
- set_fact:
total_result: "{{ ansible_play_hosts_all|
map('extract', hostvars, ['command_result', 'stdout'])|
map('int')|
sum }}"
run_once: true
- debug:
var: total_result
gives (abridged)
shell> ansible-playbook playbook.yml
PLAY [test_01:test_03] ****
TASK [shell] ****
changed: [test_01]
changed: [test_03]
TASK [debug] ****
ok: [test_01] => {
"command_result.stdout": " 62"
}
ok: [test_03] => {
"command_result.stdout": " 31"
}
TASK [set_fact] ****
ok: [test_01]
TASK [debug] ****
ok: [test_03] => {
"total_result": "93"
}
ok: [test_01] => {
"total_result": "93"
}
See serial
See the difference between ansible_play_hosts and ansible_play_hosts_all
You can use custom stats to do that: https://docs.ansible.com/ansible/latest/modules/set_stats_module.html
So for your case it would look like
---
- name: Count Check
hosts: MYGROUP
gather_facts: true
user: sv_admin
tasks:
- name: count check
shell: cat /etc/hosts | wc -l
register: command_result
- debug:
var: command_result.stdout
- set_fact:
host_result: "{{ command_result.stdout }}"
- debug:
msg: "Count for this host: {{ host_result }}"
- set_stats:
data: "{{ { 'total_count': host_result | int } }}"
Then if you run it with ANSIBLE_SHOW_CUSTOM_STATS=yes it will show you the result at the end:
$ ANSIBLE_SHOW_CUSTOM_STATS=yes ansible-playbook -i inventory pb.yml
... (usual output)
CUSTOM STATS: *************************************************************
RUN: { "total_count": 267}
The set_stats task adds results together from all the hosts by default, which is what you are looking for. You need to make sure the values are integers though, because if they are strings it will just concatenate them and you will end up with something like RUN: { "total_count": "134133"}. That's why I have put the data: bit the way I have - if you try to create the dictionary in regular yaml, like
data:
total_count: "{{ host_result | int }}"
you will see that the value is still a string (due to the way yaml/jinja works) and it won't work properly.

Ansible only run when subelement of variable exists

I have some variables defined like this:
x_php_versions_installed:
php70:
- php70-curl
- php70-xml
- php70-xmlrpc
- php70-zip
- pecl-memcached
php71:
- php71-curl
- php71-xml
- php71-xmlrpc
- php71-zip
php72:
- php72-curl
- php72-xml
- php72-xmlrpc
- php72-zip
- pecl-memcached
And I would like to check all of the vars (php70, php71, php72 and so on) has this variable: pecl-memcached and if one has, then run a command. My playbook looks like this:
- name: memcached pecl install
pear:
executable: '/usr/local/{{ item }}/bin/pecl'
name: 'pecl/memcached'
state: 'latest'
with_items: '{{ x_php_versions_installed | list }}'
when: 'item.pecl-memcached is defined'
this should call the /usr/local/php70/bin/pecl and /usr/local/php72/bin/pecl binary to install memcached. As soon as I remove the when condition, it works very well, but it will call every variable inside x_php_versions_installed not only where pecl-memcached is defined. So I need to fix the when condition in this case, but all of my tries are gives me an error.
If you want your when to check a list properly, you'll have to use the test operator in of Jinja:
- name: memcached pecl install
pear:
executable: '/usr/local/{{ item }}/bin/pecl'
name: 'pecl/memcached'
state: 'latest'
with_items: '{{ x_php_versions_installed | list }}'
when: "'pecl-memcached' in x_php_versions_installed[item]"
Given the playbook:
- hosts: localhost
gather_facts: no
vars:
x_php_versions_installed:
php70:
- php70-curl
- php70-xml
- php70-xmlrpc
- php70-zip
- pecl-memcached
php71:
- php71-curl
- php71-xml
- php71-xmlrpc
- php71-zip
php72:
- php72-curl
- php72-xml
- php72-xmlrpc
- php72-zip
- pecl-memcached
tasks:
- debug:
msg: "{{ item }}"
with_items: "{{ x_php_versions_installed | list }}"
when: "'pecl-memcached' in x_php_versions_installed[item]"
The recap would be:
TASK [debug] *******************************************************************
ok: [localhost] => (item=php70) => {
"msg": "php70"
}
skipping: [localhost] => (item=php71)
ok: [localhost] => (item=php72) => {
"msg": "php72"
}
Your subelement is a list not a dictionary, so accessing an element key like your are trying here, i.e.:
when: "x_php_versions_installed[item]['pecl-memcached'] is defined"
would work on a dictionary like this one:
x_php_versions_installed:
php70:
php70-curl:
php70-xml:
php70-xmlrpc:
php70-zip:
pecl-memcached:
# same goes for the other versions of PHP
Given the playbook:
- hosts: localhost
gather_facts: no
vars:
x_php_versions_installed:
php70:
php70-curl:
php70-xml:
php70-xmlrpc:
php70-zip:
pecl-memcached:
php71:
php71-curl:
php71-xml:
php71-xmlrpc:
php71-zip:
php72:
php72-curl:
php72-xml:
php72-xmlrpc:
php72-zip:
pecl-memcached:
tasks:
- debug:
msg: "{{ item }}"
with_items: "{{ x_php_versions_installed | list }}"
when: "x_php_versions_installed[item]['pecl-memcached'] is defined"
The recap would be:
TASK [debug] *******************************************************************
ok: [localhost] => (item=php70) => {
"msg": "php70"
}
skipping: [localhost] => (item=php71)
ok: [localhost] => (item=php72) => {
"msg": "php72"
}

How do i count task success/failure in ansible?

I am using ansible to set up a distributed application. i'm installing nodes, and then creating virtual interfaces, and cannot have more virtual interfaces than nodes. therefore, if i install on X nodes, and Y nodes fail, I need to check there are no more that (X-Y) virtual interfaces.
Is there a way to get, for a specific task, a numerical value of how many nodes succeeded/failed, so i can later use it to check the number of virtual interfaces?
Use ansible-runner. See Runner Artifact Job Events and "stats" in particular. For example ansible-runner and the playbook
shell> cat private3/project/test.yml
- hosts: test_01:test_02
gather_facts: false
tasks:
- debug:
var: inventory_hostname
- fail:
msg: Fail test_02
when: inventory_hostname == 'test_02'
shell> ansible-runner -p test.yml -i ID01 run private3
...
ASK [fail] ********************************************************************
skipping: [test_01]
fatal: [test_02]: FAILED! => {"changed": false, "msg": "Fail test_02"}
...
created records in the directory private3/artifacts/ID01/job_events/. I'm not aware of any publicly available tool to analyze the events. I've created a playbook that displays failed tasks
shell> cat pb.yml
- hosts: localhost
gather_facts: false
vars:
events_dir: private3/artifacts/ID01/job_events
tasks:
- find:
paths: "{{ events_dir }}"
register: result
- include_vars:
file: "{{ item }}"
name: "{{ 'my_var_' ~ my_idx }}"
loop: "{{ result.files|json_query('[].path') }}"
loop_control:
index_var: my_idx
label: "{{ my_idx }}"
- set_fact:
my_events: "{{ my_events|default({})|
combine({my_key: lookup('vars', my_key)}) }}"
loop: "{{ range(0, result.matched)|list }}"
loop_control:
index_var: my_idx
vars:
my_key: "{{ 'my_var_' ~ my_idx }}"
- set_fact:
my_list: "{{ my_events|json_query('*.{counter: counter,
event: event,
task: event_data.task_action,
host: event_data.host}') }}"
- debug:
var: item
loop: "{{ my_list|sort(attribute='counter') }}"
loop_control:
label: "{{ item.counter }}"
when: item.event == 'runner_on_failed'
gives
shell> ansible-playbook pb.yml
...
skipping: [localhost] => (item=11)
ok: [localhost] => (item=12) => {
"ansible_loop_var": "item",
"item": {
"counter": 12,
"event": "runner_on_failed",
"host": "test_02",
"task": "fail"
}
}
skipping: [localhost] => (item=13)
...
Feel free to fit the playbook to your needs.

How to pair hosts with variables in ansible

I have an inventory like:
all:
children:
server_group1:
hosts:
host1:
server_group2:
children:
app1:
hosts:
host2:
host3:
app2:
hosts:
host4:
host5:
server_group3:
...
I have organized my server variables like so:
> cat group_vars/server_group2/app1
app1:
name1: value1
name2: value2
> cat group_vars/server_group2/app2
app2:
name1: value11
name2: value21
I am trying to name my dict after the group (thus making them unique) and access it in my playbook:
hosts: server_group2
tasks:
- name: check file
local_action: stat path=path/to/test/{{hostvars[0].name1}}
register: payld_txt
- name: conditional transfer
copy:
src: path/to/test/{{hostvars[0].name1}}
dest: /svr/path/{{hostvars[0].name2}}
when: payld_txt.stat.exists
I end up with this error:
The task includes an option with an undefined variable. The error was: 'name1' is undefined
Where am I going wrong?
Before you go any further, you need to fix your inventory which does not respect ansible's structure for yaml sources. A simple command as the following can give you some hints:
$ ansible -i inventories/test.yml all --list-hosts
[WARNING]: Skipping unexpected key (server_group1) in group (all), only "vars", "children" and "hosts" are valid
[WARNING]: Skipping unexpected key (server_group2) in group (all), only "vars", "children" and "hosts" are valid
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
hosts (0):
The correct syntax is:
---
all:
children:
server_group1:
hosts:
host1:
server_group2:
children:
app1:
hosts:
host2:
host3:
app2:
hosts:
host4:
host5:
Which now gives:
$ ansible -i inventories/test.yml all --list-hosts
hosts (5):
host1
host2
host3
host4
host5
``hostvars[0].name1`` The error was: 'name1' is undefined
Q: "Where am I going wrong?"
A: The variable name1 is item of the dictionary app1 or app2. It must be referenced app1.name1 or app2.name1. In addition to this,
hostvars is a dictionary not an array. hostvars[0] does not exist. An item of a dictionary must be referenced by a key. For example the play below
- hosts: server_group2
tasks:
- set_fact:
my_keys: "{{ hostvars.keys()|list }}"
run_once: true
- debug:
var: my_keys
run_once: true
- debug:
msg: "{{ hostvars[item].app1.name1 }}"
loop: "{{ my_keys }}"
when: "item in groups['app1']"
run_once: true
- debug:
msg: "{{ hostvars[item].app2.name1 }}"
loop: "{{ my_keys }}"
when: "item in groups['app2']"
run_once: true
gives
ok: [host5] =>
my_keys:
- host5
- host4
- host3
- host2
- host1
ok: [host5] => (item=host3) =>
msg: value1
ok: [host5] => (item=host2) =>
msg: value1
ok: [host5] => (item=host5) =>
msg: value11
ok: [host5] => (item=host4) =>
msg: value11
Optionally use json_query to create the list of the keys
- set_fact:
my_keys: "{{ hostvars|dict2items|json_query('[].key') }}"
run_once: true
The simplified version of the playbook
- hosts: server_group2
tasks:
- debug:
msg: "{{ hostvars[inventory_hostname].app1.name1 }}"
when: "inventory_hostname in groups['app1']"
- debug:
msg: "{{ hostvars[inventory_hostname].app2.name1 }}"
when: "inventory_hostname in groups['app2']"
gives
skipping: [host5]
skipping: [host4]
ok: [host3] =>
msg: value1
ok: [host2] =>
msg: value1
ok: [host5] =>
msg: value11
ok: [host4] =>
msg: value11
skipping: [host3]
skipping: [host2]
In fact, addressing hostvars[inventory_hostname] is not necessary. The simplified tasks below give the same output.
- debug:
msg: "{{ app1.name1 }}"
when: "inventory_hostname in groups['app1']"
- debug:
msg: "{{ app2.name1 }}"
when: "inventory_hostname in groups['app2']"

How to select mandatory character when generating random password with ansible?

I create a random password with Ansible. 4 characters in length.
- hosts: localhost
vars:
pwd_alias: "{{ lookup('password', '/dev/null length=4 chars=ascii_letters,digits,hexdigits,punctuation' ) }}"
user: root
tasks:
- debug:
msg: Sifre= {{pwd_alias}}
- debug:
msg: Sifre= {{pwd_alias}}
- debug:
msg: Sifre= {{pwd_alias}}
- debug:
msg: Sifre= {{pwd_alias}}
I want it to be password example. I want the output to look like this example.
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= Z/bO"
}
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= a_4G"
}
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= 9a&0"
}
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= d.2C"
}
ascii_letters = 1
hexdigits = 1
digits = 1
punctuation = 1
I want him to generate a random password like this. But what the system produces sometimes changes. Sometimes there are no digits, sometimes there is no punctuation. I want these 4 features to be absolutely.

ansible version = 2.7.10
This is how the outputs are
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= Z/bh"
}
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= a_-G"
}
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= 9ad0"
}
TASK [debug]
ok: [localhost] => {
"msg": "Sifre= d.aC"
}
How do I get each character? Thank you so much
Generate the passwords in a separate file. Get random character from each set and create pwd_alias_list. Then shuffle and join the list.
$ cat generate-password-4.yml
- set_fact:
pwd_alias_list: []
- set_fact:
pwd_alias_list: "{{ pwd_alias_list + [
lookup('password', '/dev/null length=1 chars=' ~ item) ]
}}"
loop:
- ascii_letters
- digits
- hexdigits
- punctuation
- set_fact:
pwd_alias: "{{ pwd_alias_list|shuffle|join('') }}"
The tasks below
tasks:
- include_tasks: generate-password-4.yml
- debug:
var: pwd_alias
- include_tasks: generate-password-4.yml
- debug:
var: pwd_alias
- include_tasks: generate-password-4.yml
- debug:
var: pwd_alias
- include_tasks: generate-password-4.yml
- debug:
var: pwd_alias
give
"pwd_alias": "ld(9"
"pwd_alias": "2R`9"
"pwd_alias": "O5(0"
"pwd_alias": "2>z5"
It's possible to make the generation of the password more flexible and create a list of the characters' sets my_char_specs and number of the repetitions my_repeat
$ cat generate-password.yml
- set_fact:
pwd_alias_list: []
- set_fact:
pwd_alias_list: "{{ pwd_alias_list + [
lookup('password', '/dev/null length=1 chars=' ~ item.0) ]
}}"
with_nested:
- "{{ my_char_specs }}"
- "{{ range(0, my_repeat)|list }}"
- set_fact:
pwd_alias: "{{ pwd_alias_list|shuffle|join('') }}"
The task below repeat the random choice from four sets four times
vars:
my_char_specs:
- ascii_letters
- digits
- hexdigits
- punctuation
my_repeat: 4
tasks:
- include_tasks: generate-password.yml
- debug:
var: pwd_alias
and gives
"pwd_alias": "8=3[9BD(7?3bJ5y3"
This solution works, you need to generate each type chars separately then concatenate them :
- hosts: localhost
vars:
pwd_alias_digit1: "{{ lookup('password', '/dev/null length=1 chars=ascii_letters' ) }}"
pwd_alias_digit2: "{{ lookup('password', '/dev/null length=1 chars=digits' ) }}"
pwd_alias_digit3: "{{ lookup('password', '/dev/null length=1 chars=hexdigits' ) }}"
pwd_alias_digit4: "{{ lookup('password', '/dev/null length=1 chars=punctuation' ) }}"
pwd_alias: "{{ pwd_alias_digit1 + pwd_alias_digit2 + pwd_alias_digit3 + pwd_alias_digit4 }}"
user: root
tasks:
- debug:
msg: Sifre= {{pwd_alias}}
- debug:
msg: Sifre= {{pwd_alias}}
- debug:
msg: Sifre= {{pwd_alias}}
- debug:
msg: Sifre= {{pwd_alias}}
Another way is to create your own password lookup plugin : my_password. It's easier to create a new plugin and use it simple in a playbook. It's better and the playbook will remain readable.

Resources