Ansible check if value exists in dictionary list - ansible

Below is my variable list
hostlist:
- { name: 'host1', ip_addr: '192.168.2.31', hostgrp: 'physical_workstation' }
- { name: 'host2', ip_addr: '192.168.2.32', hostgrp: 'physical_workstation' }
- { name: 'host3', ip_addr: '192.168.2.33', hostgrp: 'virtual_machine' }
I treid below
- name: Conditional test
debug:
msg: "hello world"
when: hostlist|selectattr("name", "equalto", "host1")|list|length != 0
This does not work as showing below error
The error was: TemplateRuntimeError: no test named 'equalto'
There is solution of upgrding Jinaj2. But is there any other method instead of using selectattr. I wish not to upgrade Jinja2

Create the list of names and test the name is in the list, e.g.
- debug:
msg: "hello world"
loop:
- 'host1'
- 'host9'
when: item in _names
vars:
_names: "{{ hostlist|map(attribute='name')|list }}"
gives
ok: [localhost] => (item=host1) =>
msg: hello world
skipping: [localhost] => (item=host9)
Check only host1
- debug:
msg: "hello world"
when: "'host1' in _names"
vars:
_names: "{{ hostlist|map(attribute='name')|list }}"
gives
ok: [localhost] => (item=host1) =>
msg: hello world

Related

Unique values from ansible output dict's

I have some servers with a lot of wordpress instances, who I ask them what versions they have.
- name: CONTADOR WP VERSIONES
shell: mycommand
register: wp_versions
- debug: msg: "{{ wp_versions.stdout_lines }}"
For example:
TASK [debug] *********************************************************************
ok: [server1] => {
"msg": [
"5.1.13"
]
}
ok: [server2] => {
"msg": [
"5.1.12",
"5.1.13"
]
}
ok: [server3] => {
"msg": [
"5.1.10",
"5.1.13",
]
}
I need to list a unique values like this:
"msg": [
"5.1.10",
"5.1.12",
"5.1.13",
]
I have tried all that i found but nothing works as I want.
Thanks
Use special variable ansible_play_hosts and extract the variables from the hostvars
- set_fact:
all_vers: "{{ ansible_play_hosts|
map('extract', hostvars, ['wp_versions', 'stdout_lines'])|
flatten|unique }}"
run_once: true
gives
all_vers:
- 5.1.13
- 5.1.12
- 5.1.10
You could do something like this:
- hosts: all
gather_facts: false
tasks:
- name: CONTADOR WP VERSIONES
shell: mycommand
register: wp_versions
- hosts: localhost
gather_facts: false
tasks:
# This tasks builds a flattened list of all the
# wp_versions.stdout_lines values collected from your hosts.
- name: Collect wp_versions information
set_fact:
all_wp_versions_pre: "{{ all_wp_versions_pre + hostvars[item].wp_versions.stdout_lines }}"
loop: "{{ groups.all }}"
vars:
all_wp_versions_pre: []
# Here we use the `unique` filter to produce a list of
# unique versions.
- name: Set all_wp_versions fact
set_fact:
all_wp_versions: "{{ all_wp_versions_pre|unique }}"
- debug:
var: all_wp_versions
Given you examples, this would produce the following output:
TASK [debug] ********************************************************************************************
ok: [localhost] => {
"all_wp_versions": [
"5.1.13",
"5.1.12",
"5.1.10"
]
}

Why is ansible creating a new host scoped list variables for each host?

I need to create a variable of ip addresses that are derived from the run state of all hosts. I expect to insert a new string value and have it appended to the list.
When I run the following ansible-playbook, it creates what looks to be a new list instance for each host instead of modifying the playbook level vars.
My understanding is the set_fact below should concatenate the lists and assign them back to the play scoped var ip_list_from_terraform. Why am I getting host scoped results?
---
- name: Bootstrap nodes
hosts: all
become: true
gather_facts: true
vars:
ip_list_from_terraform: ['Verify']
tasks:
- name: assign a list of all the physical network private IPs
set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: " {{ hostvars[inventory_hostname]['ansible_' + ansible_default_ipv4['interface']]['ipv4']['address'] }} "
register: ip_list_from_terraform_list
- name: Debug global var
debug:
var: ip_list_from_terraform
- name: Debug register result
debug:
var: ip_list_from_terraform_list
Expected:
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.41",
"10.0.2.172",
"10.0.2.33",
"10.0.2.215",
"10.0.2.131",
"10.0.2.168",
"10.0.2.118"
]
}
Actual:
TASK [Debug global var] ********************************************************************************************************************************************************************************************************************
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.12"
]
}
ok: [shrimp-master-1] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.33"
]
}
ok: [shrimp-master-2] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.215"
]
}
ok: [shrimp-worker-0-super-wallaby] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.131"
]
}
ok: [shrimp-gpu-worker-0-settled-wombat] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.151"
]
}
Let's simplify the case. Given the inventory
shell> cat hosts
host1 test_ip=10.0.2.41
host2 test_ip=10.0.2.172
host3 test_ip=10.0.2.33
the playbook
- hosts: host1,host2,host3
vars:
ip_list_from_terraform: ['Verify']
tasks:
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: "{{ hostvars[inventory_hostname]['test_ip'] }}"
- debug:
var: ip_list_from_terraform
gives
ok: [host2] =>
ip_list_from_terraform:
- Verify
- 10.0.2.172
ok: [host1] =>
ip_list_from_terraform:
- Verify
- 10.0.2.41
ok: [host3] =>
ip_list_from_terraform:
- Verify
- 10.0.2.33
As a side-note, with_items is needed because the argument is a string. loop would crash with the error 'Invalid data passed to ''loop'', it requires a list,.
Q: "set_fact should concatenate the lists"
A: Run once and loop ansible_play_hosts. For example
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform +
[hostvars[item]['test_ip']] }}"
loop: "{{ ansible_play_hosts }}"
run_once: true
gives
ip_list_from_terraform:
- Verify
- 10.0.2.41
- 10.0.2.172
- 10.0.2.33

Processing embedded lists inside dict variables in Ansible

I got the example code in Ansible:
- name: testing
hosts: localhost
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: port={{item.key}} value={{item.value}}
with_dict:
- "{{svc}}"
And this outputs to:
ok: [127.0.0.1] => (item=None) => {
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
}
ok: [127.0.0.1] => (item=None) => {
"msg": "port=https value=[u'tcp/443']"
}
What I would like to achieve is, when there is more than one element in list of values it would split like this:
- name=https, prot=tcp, port=443
- name=app_svc, prot=tcp, port=5543
- name=app_svc, prot=udp, port=5543
- name=app_svc, prot=tcp, port=3100
with_dict stanza only displays me a whole list and I couldn't find a way do do it differently. Is it possible to do it like that without reorganizing the var section? Thanks in advance for input.
To see the syntax errors run
# ansible-lint <YOUR-PLAYBOOK>
Correct syntax should be
- hosts: localhost
gather_facts: no
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: "port={{ item.key }} value={{ item.value }}"
with_dict: "{{ svc }}"
gives
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
"msg": "port=https value=[u'tcp/443']"
Change the loop
- name: print
debug:
msg: "name={{ item.0.key }},
prot={{ item.1.split('/')[0] }},
port={{ item.1.split('/')[1] }}"
loop: "{{ lookup('subelements', svc|dict2items, 'value') }}"
to get the format
"msg": "name=app_svc, prot=tcp, port=5543"
"msg": "name=app_svc, prot=udp, port=5543"
"msg": "name=app_svc, prot=tcp, port=3100"
"msg": "name=https, prot=tcp, port=443"
dict2items is available since version 2.6 . Without "dict2items" transform the data first. See below (not tested).
https:
- {key: 'https', value: ['tcp/443']}
- {key: 'app_svc', value: ['tcp/5543', 'udp/5543', 'tcp/3100']}

why are stdout_lines [1,2] and [3,4] strings instead of arrays?

A shell command outputs one array per line on the standard output: [1,2] then [3,4]. I would like to use each element of the array as a parameter to a module and decided to loop over stdout_lines. However, item.0 and item.1 contains the first and second character of the string instead of the first and second element of the array.
Why is item a string instead of an array?
What would be the most elegant way to get what I need?
Here is a reproducer to help understand the question, using ansible 2.7.5. The following play:
---
- name: test
hosts: localhost
become: false
gather_facts: false
tasks:
- name: one array per line
shell: |
echo '[1,2]'
echo '[3,4]'
register: result
- name: each item is an array
debug:
msg: "{{ item }}"
loop: "{{ result.stdout_lines }}"
- name: accessing the first element of an item
debug:
msg: "{{ item.0 }} => {{ item.1 }}"
loop: "{{ result.stdout_lines }}"
The actual output is:
TASK [each item is an array] *****************************************************
ok: [localhost] => (item=[1, 2]) => {
"msg": [
1,
2
]
}
ok: [localhost] => (item=[3, 4]) => {
"msg": [
3,
4
]
}
TASK [accessing the first element of an item] ************************************
ok: [localhost] => (item=[1, 2]) => {
"msg": "[ => 1"
}
ok: [localhost] => (item=[3, 4]) => {
"msg": "[ => 3"
}
but I was expecting:
TASK [each item is an array] *****************************************************
ok: [localhost] => (item=[1, 2]) => {
"msg": [
1,
2
]
}
ok: [localhost] => (item=[3, 4]) => {
"msg": [
3,
4
]
}
TASK [accessing the first element of an item] ************************************
ok: [localhost] => (item=[1, 2]) => {
"msg": "1 => 2"
}
ok: [localhost] => (item=[3, 4]) => {
"msg": "3 => 4"
}
An option would be to convert the list of strings to the list of lists.
- name: convert strings to lists
set_fact:
lists: "{{ lists }} + [ {{ item }} ]"
loop: "{{ result.stdout_lines }}"
- name: accessing the 1st and 2nd element of a list
debug:
msg: "{{ item.0 }} => {{ item.1 }}"
loop: "{{ lists }}"
After discussions with Pilou on #ansible-fr#irc.freenode.net, a simple solution was found.
It is enough to trigger a jinja2 evaluation by assigning another variable with the content of item. It will be converted from string to a structure, if possible. See the test playbook updated with the solution below.
debug:
msg: "{{ array.0 }} => {{ array.1 }}"
vars:
array: "{{ item }}"
loop: "{{ result.stdout_lines }}"
There is no rationale explaining why stdout is converted into a structure when possible but not the elements in stdout_lines. It is how the shell module is implemented. It is possible that it will change in the future. If that's the case, the proposed solution will keep working. Only it would not be necessary.
---
- name: test
hosts: localhost
become: false
gather_facts: false
tasks:
- name: one array per line
shell: |
echo '[1,2]'
echo '[3,4]'
register: result
- name: each item is an array
debug:
msg: "{{ item }}"
loop: "{{ result.stdout_lines }}"
- name: accessing the first element of an item
debug:
msg: "{{ array.0 }} => {{ array.1 }}"
vars:
array: "{{ item }}"
loop: "{{ result.stdout_lines }}"

Ansible use dict2items with subelements

I'm new to Ansible and am having trouble figuring out how to properly do this...
I've got a list of users and under them is a list of git config settings that I want Ansible to setup for me. As an example, I've got something like this:
---
- hosts: all
gather_facts: no
vars:
user_list:
- name: john.doe
git_config:
- user.name: John Doe
- user.email: john.doe#example.com
- user.signingkey: 0A46826A
- name: jane.doe
git_config:
- user.name: Jane Doe
- diff.tool: meld
tasks:
- debug:
msg: "User: {{ item.0.name }}, Key: {{ item.1 }}"
loop: "{{ user_list | subelements('git_config') }}"
# Eventually, I would like to be able to do something like this using Ansible's git_config module: https://docs.ansible.com/ansible/2.4/git_config_module.html
# - git_config:
# name: "{{ item.1.key }}"
# value: "{{ item.1.value }}"
# scope: global
# loop: "{{ user_list | <<Some Ansible Magic>> }}"
# become: yes
# become_user: "{{ item.0.name }}"
I run a test with this
ansible-playbook git_config.yml
Which gives me this:
PLAY [all] *****************************************************************************************************************************************************************************************************************************************************************************
TASK [debug] ***************************************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=[{u'name': u'john.doe', u'git_config': [{u'user.name': u'John Doe'}, {u'user.email': u'john.doe#example.com'}, {u'user.signingkey': u'0A46826A'}]}, {u'user.name': u'John Doe'}]) => {
"msg": "User: john.doe, Key: {u'user.name': u'John Doe'}"
}
ok: [localhost] => (item=[{u'name': u'john.doe', u'git_config': [{u'user.name': u'John Doe'}, {u'user.email': u'john.doe#example.com'}, {u'user.signingkey': u'0A46826A'}]}, {u'user.email': u'john.doe#example.com'}]) => {
"msg": "User: john.doe, Key: {u'user.email': u'john.doe#example.com'}"
}
ok: [localhost] => (item=[{u'name': u'john.doe', u'git_config': [{u'user.name': u'John Doe'}, {u'user.email': u'john.doe#example.com'}, {u'user.signingkey': u'0A46826A'}]}, {u'user.signingkey': u'0A46826A'}]) => {
"msg": "User: john.doe, Key: {u'user.signingkey': u'0A46826A'}"
}
ok: [localhost] => (item=[{u'name': u'jane.doe', u'git_config': [{u'user.name': u'Jane Doe'}, {u'diff.tool': u'meld'}]}, {u'user.name': u'Jane Doe'}]) => {
"msg": "User: jane.doe, Key: {u'user.name': u'Jane Doe'}"
}
ok: [localhost] => (item=[{u'name': u'jane.doe', u'git_config': [{u'user.name': u'Jane Doe'}, {u'diff.tool': u'meld'}]}, {u'diff.tool': u'meld'}]) => {
"msg": "User: jane.doe, Key: {u'diff.tool': u'meld'}"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0
What I want is to be able to extract the key and value somehow (I need to know the user's name so that I can become the user and then I need to loop through all of the git config key/values so that I can use Ansible's git config module: https://docs.ansible.com/ansible/2.4/git_config_module.html):
"msg": "User: jane.doe, Key: diff.tool, Value: meld"
with something like this
- name: just testing
debug:
msg: "User: {{ item.0.name }}, Key: {{ item.1.key }}, Value: {{ item.1.value }}"
Please see git_config.yml to get a better idea of what I'm trying to do.

Resources