for each host need to print only one item - ansible

I have a list of descriptions that will be added one per host. I would like to print one item per host and it is becoming harder than I expected. The same item is being printed per each host.
tasks:
- name: items in list
debug:
msg: "{{ item }}"
with_items:
- ['a', 'b']
THIS IS WHAT I GET:
TASK [items in list] *****************************************************************************************************************************************************************************************************************************
ok: [Host1] => (item=a) => {
"msg": "a"
}
ok: [Host2] => (item=a) => {
"msg": "a"
}
ok: [Host1] => (item=b) => {
"msg": "b"
}
ok: [Host2] => (item=b) => {
"msg": "b"
}
THIS IS WHAT I WANT:
TASK [items in list] *****************************************************************************************************************************************************************************************************************************
ok: [Host1] => (item=a) => {
"msg": "a"
}
ok: [Host2] => (item=b) => {
"msg": "b"
}

There are many options. For example,
Create lists for the hosts
- name: items in list
debug:
msg: "{{ item }}"
loop: "{{ _list[inventory_hostname] }}"
vars:
_list:
host1: ['a']
host2: ['b']
gives
ok: [host1] => (item=['a']) =>
msg: a
ok: [host2] => (item=['b']) =>
msg: b
Calculate the index of the host in the group, e.g.
- name: items in list
debug:
msg: "{{ _list[_idx|int] }}"
vars:
_list: ['a', 'b']
_idx: "{{ groups.all.index(inventory_hostname) }}"
gives
ok: [host2] =>
msg: b
ok: [host1] =>
msg: a
Run the task once and iterate the sequence, e.g.
- name: items in list
debug:
msg: "{{ _list[item|int] }}"
with_sequence: start=0 end="{{ groups.all|length -1 }}"
vars:
_list: ['a', 'b']
run_once: true
delegate_to: localhost
gives
ok: [host1 -> localhost] => (item=0) =>
msg: a
ok: [host1 -> localhost] => (item=1) =>
msg: b

Related

Ansible: parsing a dictionary recursively

I'm struggling to implement something like a "recursive function" in Ansible, but it seems to be a slightly tricky task.
Let's say, I have a tree-like dictionary:
tree:
alpha: 1
foo/:
bravo: 2
bar/:
charlie: 3
baz/:
delta: 4
What I need is to parse it, create some entities (let's say, a sub-directory or sub-domain, it doesn't matter what exactly) for each element which name ends with the / character and create some other entity (let's say a file or a DNS-record, it doesn't matter as well) for each element which name ends with anything else.
Here's a minimal example of the solution I'm trying to implement.
The playbook.yaml file:
- hosts:
- localhost
tasks:
- include_tasks: proceed_branch.yaml
vars:
tree:
alpha: 1
foo/:
bravo: 2
bar/:
charlie: 3
baz/:
delta: 4
The proceed_branch.yaml file:
- debug:
msg: "{{ tree }}"
- debug:
msg: "Got {{ item.key }} = {{ item.value }}"
loop: "{{ tree | dict2items }}"
when: item.key is not regex('/$')
- include_tasks: proceed_branch.yaml
vars:
tree: "{{ item.value }}"
loop: "{{ tree | dict2items }}"
when: item.key is regex('/$')
And here's what I get as the output:
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
PLAY [localhost] ***************************************************************
TASK [include_tasks] ***********************************************************
Included: /tmp/ansible/proceed_branch.yaml for localhost
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"foo/": {
"bar/": {
"baz/": {
"delta": 4
},
"charlie": 3
},
"bravo": 2
},
"alpha": 1
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'alpha', 'value': 1}) => {
"msg": "Got alpha = 1"
}
skipping: [localhost] => (item={'key': 'foo/', 'value': {'bravo': 2, 'bar/': {'charlie': 3, 'baz/': {'delta': 4}}}})
TASK [include_tasks] ***********************************************************
fatal: [localhost]: FAILED! => {"msg": "Unable to look up a name or access an attribute in template string ({{ tree | dict2items }}).\nMake sure your variable name does not contain invalid characters like '-': dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead.. dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead.. Unable to look up a name or access an attribute in template string ({{ tree | dict2items }}).\nMake sure your variable name does not contain invalid characters like '-': dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead.. dict2items requires a dictionary, got <class 'ansible.template.AnsibleUndefined'> instead."}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
What do you think, why dict2items gets the "undefined value" at some point?
Thanks in advance!
P.S. Here's what I expect to get as the result:
PLAY [localhost] ***************************************************************
TASK [include_tasks] ***********************************************************
included: /tmp/ansible/proceed_branch.yaml for localhost
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"alpha": 1,
"foo/": {
"bar/": {
"baz/": {
"delta": 4
},
"charlie": 3
},
"bravo": 2
}
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'alpha', 'value': 1}) => {
"msg": "Got alpha = 1"
}
skipping: [localhost] => (item={'key': 'foo/', 'value': {'bravo': 2, 'bar/': {'charlie': 3, 'baz/': {'delta': 4}}}})
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'alpha', 'value': 1})
included: /tmp/ansible/proceed_branch.yaml for localhost => (item={'key': 'foo/', 'value': {'bravo': 2, 'bar/': {'charlie': 3
, 'baz/': {'delta': 4}}}})
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"bar/": {
"baz/": {
"delta": 4
},
"charlie": 3
},
"bravo": 2
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'bravo', 'value': 2}) => {
"msg": "Got bravo = 2"
}
skipping: [localhost] => (item={'key': 'bar/', 'value': {'charlie': 3, 'baz/': {'delta': 4}}})
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'bravo', 'value': 2})
included: /tmp/ansible/proceed_branch.yaml for localhost => (item={'key': 'bar/', 'value': {'charlie': 3, 'baz/': {'delta': 4}}})
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"baz/": {
"delta": 4
},
"charlie": 3
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'charlie', 'value': 3}) => {
"msg": "Got charlie = 3"
}
skipping: [localhost] => (item={'key': 'baz/', 'value': {'delta': 4}})
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'charlie', 'value': 3})
included: /tmp/ansible/proceed_branch.yaml for localhost => (item={'key': 'baz/', 'value': {'delta': 4}})
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": {
"delta": 4
}
}
TASK [debug] *******************************************************************
ok: [localhost] => (item={'key': 'delta', 'value': 4}) => {
"msg": "Got delta = 4"
}
TASK [include_tasks] ***********************************************************
skipping: [localhost] => (item={'key': 'delta', 'value': 4})
skipping: [localhost]
Found a pretty ugly solution, though it seems to be working. The point is to add a temporary variable (by setting a fact):
- hosts:
- localhost
tasks:
- include_tasks: proceed_branch.yaml
vars:
tree:
alpha: 1
foo/:
bravo: 2
bar/:
charlie: 3
baz/:
delta: 4
- debug:
msg: "{{ tree }}"
- set_fact:
__t: "{{ tree }}"
- debug:
msg: "Got {{ item.key }} = {{ item.value }}"
loop: "{{ __t | dict2items }}"
when: item.key is not regex('/$')
- include_tasks: proceed_branch.yaml
vars:
tree: "{{ item.value }}"
loop: "{{ __t | dict2items }}"
when: item.key is regex('/$')
I hate how it looks like, though it works.
If anyone have a better idea, please, be so kind as to share.

Ansible set_fact array not appending

I'm trying to look for all drives that have certain properties and append those drives to an array under set_facts.
When I print the contents of the array I only see the last drive in the system that matched the search. I can't figure out why it's not appending all the drives that match the criteria.
---
- hosts: worker1
gather_facts: yes
tasks:
- name: Initialize an empty list for our strings
set_fact:
disks: []
run_once: True
- name: Print disk result
set_fact:
disks: "{{ disks + [item.key] }}"
when:
- not item.value.partitions
- not item.value.holders
- not item.value.links.ids
- item.key | search ("vd")
with_dict: "{{ ansible_devices }}"
- name: debug
debug:
msg: "{{disks}}"
Output
ok: [worker1] => {
"msg": [
"vdd"
]
}
it should contain "vdb", "vdc", "vdd", and "vde"
FYI when I replace the
set_fact:
disks: "{{ disks + [item.key] }}"
with
debug:
msg: "{{ item.key }}"
output
ok: [worker1] => (item=None) => {
"msg": "vdc"
}
ok: [worker1] => (item=None) => {
"msg": "vdb"
}
ok: [worker1] => (item=None) => {
"msg": "vde"
}
skipping: [worker1] => (item=None)
skipping: [worker1] => (item=None)
ok: [vmaas-worker1] => (item=None) => {
"msg": "vdd"
}
skipping: [worker1] => (item=None)
skipping: [worker1] => (item=None)
I do see the correct drives listed, hence why I'm baffled it doesn't append to the array.

Check the defined variable is empty or not in Ansible

- hosts: localhost
vars:
files_list:
#it may contain the file_list like
#file_list:
# - "file*"
tasks:
- name: copy
copy:
src: "{{item}}"
dest: "/tmp/"
with_fileglob: "{{files_list}}"
when: files != None
I want to copy some multiple files with a specific pattern from the files_list. but sometimes the file_list may be empty. so how to check if the file_list is empty I have tried above code but it doesn't work. it is giving me following error
The full traceback is:<br>
Traceback (most recent call last):<br>
File "/usr/lib/python2.7/site-packages/ansible/executor/task_executor.py", line 104, in run<br>
items = self._get_loop_items()<br>
File "/usr/lib/python2.7/site-packages/ansible/executor/task_executor.py", line 245, in _get_loop_items<br>
items = wrap_var(mylookup.run(terms=loop_terms, variables=self._job_vars, wantlist=True))<br>
File "/usr/lib/python2.7/site-packages/ansible/plugins/lookup/fileglob.py", line 60, in run<br>
term_file = os.path.basename(term)<br>
File "/usr/lib64/python2.7/posixpath.py", line 121, in basename<br>
i = p.rfind('/') + 1<br>
AttributeError: 'NoneType' object has no attribute 'rfind'<br>
fatal: [machine1.kirusa.com]: FAILED! => {<br>
"msg": "Unexpected failure during module execution.", <br>
"stdout": ""<br>
}
Can you also explain whats this mean.
thank you in advance.
Q: "Check whether the defined variable is empty or not in Ansible."
A: Simply test the variable. An empty list evaluates to False. This also covers the case when the variable is not defined. YAML None is Python null. None also evaluates to False. For example
- debug:
msg: The variable files is an empty list or None.
when: not files|default(None)
In the loop, it's not necessary to test whether the list is empty or not. An empty list will be skipped anyway.
YAML string is a list of characters. An empty string evaluates to False the same way as an empty list does.
Notes
The evaluation of 'not item' works in the context of this question if the variable is a string, list, dictionary, or None. if the variable is a boolean or number this test does not work. See the self-explaining test below
- debug:
msg: "{{ not item }}"
loop:
- ''
- []
- {}
-
- null
- true
- false
- 1
- 0
gives
TASK [debug] **************************************************
ok: [localhost] => (item=) =>
msg: true
ok: [localhost] => (item=[]) =>
msg: true
ok: [localhost] => (item={}) =>
msg: true
ok: [localhost] => (item=None) =>
msg: true
ok: [localhost] => (item=None) =>
msg: true
ok: [localhost] => (item=True) =>
msg: false
ok: [localhost] => (item=False) =>
msg: true
ok: [localhost] => (item=1) =>
msg: false
ok: [localhost] => (item=0) =>
msg: true
To fix this problem test the variable is iterable
- debug:
msg: "{{ item is iterable }}"
loop:
- ''
- []
- {}
-
- null
- false
- 0
gives
TASK [debug] ******************************************************
ok: [localhost] => (item=) =>
msg: true
ok: [localhost] => (item=[]) =>
msg: true
ok: [localhost] => (item={}) =>
msg: true
ok: [localhost] => (item=None) =>
msg: false
ok: [localhost] => (item=None) =>
msg: false
ok: [localhost] => (item=False) =>
msg: false
ok: [localhost] => (item=0) =>
msg: false
Complete test: The variable is empty or None
- debug:
msg: The variable is empty or None.
when:
- not item
- item is iterable or item is none
loop:
- ''
- []
- {}
-
- null
- true
- false
- 1
- 0
gives
TASK [debug] ***************************************************
ok: [localhost] => (item=) =>
msg: The variable is empty or None.
ok: [localhost] => (item=[]) =>
msg: The variable is empty or None.
ok: [localhost] => (item={}) =>
msg: The variable is empty or None.
ok: [localhost] => (item=None) =>
msg: The variable is empty or None.
ok: [localhost] => (item=None) =>
msg: The variable is empty or None.
skipping: [localhost] => (item=True)
skipping: [localhost] => (item=False)
skipping: [localhost] => (item=1)
skipping: [localhost] => (item=0)
A dictionary is also iterable. A list of the keys is used
- debug:
var: item
loop: "{{ files|list }}"
vars:
files:
a: 1
b: 2
c: 3
gives
TASK [debug] ********************************************************
ok: [localhost] => (item=a) =>
ansible_loop_var: item
item: a
ok: [localhost] => (item=b) =>
ansible_loop_var: item
item: b
ok: [localhost] => (item=c) =>
ansible_loop_var: item
item: c
To check if it is empty you need to give as below
when: not files_list
See Default rules here: https://docs.ansible.com/ansible-lint/rules/default_rules.html
It states: Don’t compare to empty string, use when: var rather than when: var != "" (or conversely when: not var rather than when: var == "")
You can first assert that it is indeed a list and then assert that it has more than one element:
when: (files_list is defined) and (files_list | type_debug == 'list') and (files_list | count > 0)

How can I traverse nested lists in Ansible?

I have a list of devices, each of which has a varying list of attributes that must be created on the devices, one at a time. Is there a way to build a nested set of loops to do this? The with_nested construct applies every attribute to every device; I need only a single attribute per device per call.
This playbook demonstrates what I have tried (Ansible 2.7.1, Python 2.7.1):
- name: Test nested list traversal
hosts: localhost
connection: local
vars:
Stuff:
- Name: DeviceA
Info: AInfo
Values:
- ValueA1
- ValueA2
- Name: DeviceB
Info: BInfo
Values:
- ValueB1
- ValueB2
- ValueB3
tasks:
- name: Loop test
debug:
msg: "{{ item.Name }},{{ item.Info }}, {{ item.Values }}"
with_items:
- "{{ Stuff }}"
What I get: (One call per device, containing all attributes at once)
ok: [localhost] => (item={u'Info': u'AInfo', u'Values': [u'ValueA1', u'ValueA2'], u'Name': u'DeviceA'}) =>
msg: DeviceA,AInfo, [u'ValueA1', u'ValueA2']
ok: [localhost] => (item={u'Info': u'BInfo', u'Values': [u'ValueB1', u'ValueB2', u'ValueB3'], u'Name': u'DeviceB'}) =>
msg: DeviceB,BInfo, [u'ValueB1', u'ValueB2', u'ValueB3']
What I want (each msg represents a separate operation to be performed on the device with just that one attribute)
msg: DeviceA, AInfo, ValueA1
msg: DeviceA, AInfo, ValueA2
msg: DeviceB, BInfo, ValueB1
msg: DeviceB, BInfo, ValueB2
msg: DeviceB, BInfo, ValueB3
You can get what you want using the subelements filter:
---
- hosts: localhost
gather_facts: false
vars:
Stuff:
- Name: DeviceA
Info: AInfo
Values:
- ValueA1
- ValueA2
- Name: DeviceB
Info: BInfo
Values:
- ValueB1
- ValueB2
- ValueB3
tasks:
- debug:
msg: "{{ item.0.Name }}, {{ item.0.Info }}, {{ item.1 }}"
loop: "{{ Stuff|subelements('Values') }}"
loop_control:
label: "{{ item.0.Name }}"
Running the above playbook gets you:
PLAY [localhost] ******************************************************************************************************************************************************************************
TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => (item=DeviceA) => {
"msg": "DeviceA, AInfo, ValueA1"
}
ok: [localhost] => (item=DeviceA) => {
"msg": "DeviceA, AInfo, ValueA2"
}
ok: [localhost] => (item=DeviceB) => {
"msg": "DeviceB, BInfo, ValueB1"
}
ok: [localhost] => (item=DeviceB) => {
"msg": "DeviceB, BInfo, ValueB2"
}
ok: [localhost] => (item=DeviceB) => {
"msg": "DeviceB, BInfo, ValueB3"
}
PLAY RECAP ************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0

Ansible - Looping and Debug/Register

I have the following task.
However, it doesn't output the output for each ping that is produced, and I only get a 1 x output. When there should be x 5.
Task
tasks:
- name: "Check Connectivity (ping)"
nxos_ping:
provider: "{{ nxos_ssh }}"
source: "{{ hostvars[inventory_hostname]['lo0_ipaddr'] }}"
vrf: default
dest: "192.168.1.{{item}}"
with_sequence: start=1 end=5
register: out
- debug:
msg:
- "command: {{ out['results'][0]['commands'][0] }}"
Example
TASK [Check Connectivity (ping)] ***************************************************************************************************
ok: [spine-nxos-1] => (item=1)
ok: [spine-nxos-2] => (item=1)
ok: [spine-nxos-2] => (item=2)
ok: [spine-nxos-1] => (item=2)
ok: [spine-nxos-1] => (item=3)
ok: [spine-nxos-2] => (item=3)
ok: [spine-nxos-1] => (item=4)
ok: [spine-nxos-2] => (item=4)
ok: [spine-nxos-1] => (item=5)
ok: [spine-nxos-2] => (item=5)
TASK [debug] ***********************************************************************************************************************
ok: [spine-nxos-1] => {
"msg": [
"command: ping 192.168.1.1 count 5 source 192.168.1.1 vrf default"
]
}
ok: [spine-nxos-2] => {
"msg": [
"command: ping 192.168.1.1 count 5 source 192.168.1.2 vrf default"
]
}
Your code is working correct, as you only print out the first element of the registered output. If you want to see all your commands you should replace the last line of your playbook with:
- "command {{ out | json_query('results[*].commands[*]') }}"
or loop through your output corresponding to your sequence:
debug:
msg:
- "command {{ out.results[item|int].commands[0]}}"
with_sequence: start=0 end=2

Resources