Ansible loop over dictionaries - ansible

I was trying to loop over dictionaries and I referred the following thread but it kept on failing:
How to loop over this dictionary in Ansible?
Following is my play book:
- hosts: server_hosts
tasks:
- name: Include dictionary data
include_vars:
file: vars/input_vars.yaml
- name: Show info field from data.yml
debug:
msg: "Id: {{ input_data[item]['version'] }} - info: {{ input_data[item]['name'] }}"
with_items: "{{ input_data.keys() }}"
Following is my dictionary vars/input_vars.yaml file:
input_data:
item_1:
name: "test1"
version: "18.3"
item_2:
name: "test2"
version: "18.3"
item_3:
name: "test3"
version: "18.3"
When I executed the playbook, it fails with following error:
fatal: [192.168.16.120]: FAILED! => {
"ansible_facts": {},
"ansible_included_var_files": [],
"changed": false,
"message": "Syntax Error while loading YAML.\n mapping values are not allowed here\n\nThe error appears to have been in '/git_projects/base/vars/input_vars.yaml': line 2, column 12, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n----\n input_data:\n ^ here\n"
}

I have tested it and it works, I have removed extra newlines from input_vars.yaml
➜ ~ cat input_vars.yaml
input_data:
item_1:
name: "test1"
version: "18.3"
item_2:
name: "test2"
version: "18.3"
item_3:
name: "test3"
version: "18.3"
➜ ~ cat example.yml
---
- hosts: localhost
tasks:
- name: Include dictionary data
include_vars:
file: input_vars.yaml
- name: Show info field from data.yml
debug:
msg: "Id: {{ input_data[item]['version'] }} - info: {{ input_data[item]['name'] }}"
with_items: "{{ input_data.keys() }}"
Output
➜ ~ ansible-playbook example.yml
[WARNING]: Unable to parse /etc/ansible/hosts as an inventory source
[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 [Gathering Facts] *****************************************************************************************************************
ok: [localhost]
TASK [Include dictionary data] *********************************************************************************************************
ok: [localhost]
TASK [Show info field from data.yml] ***************************************************************************************************
ok: [localhost] => (item=item_2) => {
"msg": "Id: 18.3 - info: test2"
}
ok: [localhost] => (item=item_3) => {
"msg": "Id: 18.3 - info: test3"
}
ok: [localhost] => (item=item_1) => {
"msg": "Id: 18.3 - info: test1"
}
PLAY RECAP *****************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0

Related

Ansible for event automation with Cisco Nessus device. need to extract past 15 minutes logs from show clock of the device

What I am trying to achieve with this ansible code: Ansible code should be triggered during an interface flapping with cisco nessus device. It should fetch the time of nessus device(show clock) and try to pull the past 15 minutes from the "show logging" output of nessus device.
Ansible experts, I need to help to manipulate the time to get past 15 minutes from show logging. Below s the code written so far.
cat interfaceflapping.yml
---
- name: Cisco NXOS
hosts: all
connection: network_cli
gather_facts: false
vars:
- cmdlist: sh clock
- ansible_python_interpreter: /usr/bin/python3
- ansible_network_os: nxos
tasks:
- name: Execute command
nxos_command:
commands: "{{ cmdlist }}"
register: output
- set_fact:
arr: "{{ output.stdout_lines[0][1].split() }}"
- debug:
msg: "{{ arr[0] }}"
- name: print variable
set_fact:
my_string_var: "{{ arr[0].split(':') | default('') }}"
- debug:
msg: "{{my_string_var}}"
- name: Execute command
nxos_command:
commands: sh logging | include 2/3
register: output1
- debug:
msg: "{{ output1 }}"
when: output1.stdout is search(my_string_var)
```ansible
==================
output as below
[LABPC#lab-jump-host dow]$ ansible-playbook interfaceflapping.yml -i inventory1.txt --limit nxos --verbose
Using /etc/ansible/ansible.cfg as config file
PLAY [Cisco NXOS] ******************************************************************************************************************************************************************************************
TASK [Execute command] *************************************************************************************************************************************************************************************
ok: [nxos] => {"changed": false, "stdout": ["Time source is NTP\n18:50:15.681 UTC Sat Jul 02 2022"], "stdout_lines": [["Time source is NTP", "18:50:15.681 UTC Sat Jul 02 2022"]]}
TASK [set_fact] ********************************************************************************************************************************************************************************************
ok: [nxos] => {"ansible_facts": {"arr": ["18:50:15.681", "UTC", "Sat", "Jul", "02", "2022"]}, "changed": false}
TASK [debug] ***********************************************************************************************************************************************************************************************
ok: [nxos] => {
"msg": "18:50:15.681"
}
TASK [print variable] **************************************************************************************************************************************************************************************
ok: [nxos] => {"ansible_facts": {"my_string_var": ["18", "50", "15.681"]}, "changed": false}
TASK [debug] ***********************************************************************************************************************************************************************************************
ok: [nxos] => {
"msg": [
"18",
"50",
"15.681"
]
}
TASK [Execute command] *************************************************************************************************************************************************************************************
ok: [nxos] => {"changed": false, "stdout": [""], "stdout_lines": [[""]]}
TASK [debug] ***********************************************************************************************************************************************************************************************
fatal: [nxos]: FAILED! => {"msg": "The conditional check 'output1.stdout is search(my_string_var)' failed. The error was: Unexpected templating type error occurred on ({% if output1.stdout is search(my_string_var) %} True {% else %} False {% endif %}): unhashable type: 'list'\n\nThe error appears to be in '/home/LABPC/gomathi/dow/root/dow/interfaceflapping.yml': line 32, column 8, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
PLAY RECAP *************************************************************************************************************************************************************************************************
nxos : ok=6 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
==============
PLZ HELP ME TO GET THE PAST 15 MINUTES OUTPUT FROM SHOW LOGGING. I HAVE HARDCODED THE INTERFACE NUMBER AS 2/3 .

Use dynamic variable name

I'm trying to get the value of ip_address from the following yaml that I'm including as variables on ansible:
common:
ntp:
- time.google.com
node1:
default_route: 10.128.0.1
dns:
- 10.128.0.2
hostname: ip-10-128-5-17
device_interface: ens5
cluster_interface: ens5
interfaces:
ens5:
ip_address: 10.128.5.17
nat_ip_address: 18.221.63.178
netmask: 255.255.240.0
version: 2
However the network interface (ens5 here) may be named something else, such as eth0. My ansible code is this:
- hosts: all
tasks:
- name: Read configuration from the yaml file
include_vars: "{{ config_yaml }}"
- name: Dump Interface Settings
vars:
msg: node1.interfaces.{{ cvp_device_interface }}.ip_address
debug:
msg: "{{ msg }}"
tags: debug_info
Running the code like this I can get the key's name:
TASK [Dump Interface Settings] *************************************************
│ ok: [18.221.63.178] => {
│ "msg": "node1.interfaces.ens5.ip_address"
│ }
But what I actually need is the value (i.e: something like {{ vars[msg] }}, which should expand into {{ node1.interfaces.ens5.ip_address }}). How can I accomplish this?
Use sqare brackets.
Example: a minimal playbook, which defines a variable called "device". This variable is used to return the active status of the device.
- hosts: localhost
connection: local
vars:
device: enx0050b60c19af
tasks:
- debug: var=device
- debug: var=hostvars.localhost.ansible_facts[device].active
Output:
$ ansible-playbook example.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
PLAY [localhost] *******************************************************************
TASK [Gathering Facts] *************************************************************
ok: [localhost]
TASK [debug] ***********************************************************************
ok: [localhost] => {
"device": "enx0050b60c19af"
}
TASK [debug] ***********************************************************************
ok: [localhost] => {
"hostvars.localhost.ansible_facts[device].active": true
}
PLAY RECAP *************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
see comment
- hosts: all
tasks:
- name: Read configuration from the yaml file
include_vars: "{{ config_yaml }}"
- name: Dump Interface Settings
debug:
msg: "{{ node1['interfaces'][cvp_device_interface]['ip_address'] }}"
debug:
msg: "{{ msg }}"
tags: debug_info

How can I filter certain element in a string

playbook.yml
---
hosts: local_host
connection: local
gather_facts: False
tasks:
- name: set-details
set_fact:
filter: "{{ lookup('file', 'tmp/task2.yml') | from_json }}"
- set_fact:
result: "{{ filter['msg'] }}"
- debug:
var: result
task2.yml
{
"ansible_loop_var": "item",
"_ansible_no_log": false,
"invocation": {
"module_args": {
"wait_for_task": true,
"policy_package": "Mills07_Simplified",
"version": null,
"wait_for_task_timeout": 30
}
},
"item": "Mills07_Simplified",
"changed": false,
"msg": "Task Verify policy operation with task id 01234567-89ab-cdef-928b-bef7e174fc7a failed. Look at the logs for more details",
"_ansible_item_label": "Mills07_Simplified"
}
debug message
TASK [debug] *****************************************************************************************************************************************************************************
ok: [localhost] => {
"result": "Task Verify policy operation with task id 01234567-89ab-cdef-928b-bef7e174fc7a failed. Look at the logs for more details"
}
When I did the following,
- set_fact:
task_id: "{{ result |
select('regex', my_regex)|
first|
regex_replace(my_regex, my_replace) }}"
vars:
my_regex: '^Task Verify policy operation with task id (.*)$'
my_replace: '\1'
- debug:
var: task_id
I got an error message
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'my_regex' is undefined\n\nThe error appears to be in
Goal: I want to get the task-id "01234567-89ab-cdef-928b-bef7e174fc7a"
How can I get this string 01234567-89ab-cdef-928b-bef7e174fc7a
Since you are looking for a universally unique identifier (or UUID) which has a defined format of 8-4-4-4-12 characters for a total of 36 characters (32 hexadecimal characters and 4 hyphens) source, you can use a simple regex to extract it.
It can be handled with the following regex:
[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}
You can test it there: https://regex101.com/r/4Hs7il/1
So, in a set_fact:
- set_fact:
uuid: >-
{{ filter.msg
| regex_search(
'([0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12})',
'\1',
ignorecase=True
)
| first
}}
As an example:
- hosts: localhost
gather_facts: no
tasks:
- set_fact:
uuid: >-
{{ filter.msg
| regex_search(
'([0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12})',
'\1',
ignorecase=True
)
| first
}}
vars:
filter:
msg: "Task Verify policy operation with task id 01234567-89ab-cdef-928b-bef7e174fc7a failed. Look at the logs for more details"
- debug:
var: uuid
Would yield the recap:
PLAY [localhost] ***************************************************************
TASK [set_fact] ****************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] =>
uuid: 01234567-89ab-cdef-928b-bef7e174fc7a
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Iterating via nested loops

The packages.yml file defined as:
---
- packages:
- name: Some description 1,
packageList:
- package1,
- package2,
- package3
- name: Some description 2,
package: package4
The first item contains a field packageList, the 2nd item does not have it, but only package field.
Playbook:
---
- hosts: all
become: yes
vars_files:
- packages.yml
How to iterate via all packageList items of the packages list only if this packageList is defined for an item.
Here is how I can iterate through items which contain package field:
- name: iteration
debug:
msg: "name: {{ item.package }}"
when: item.package is defined
with_items: "{{ packages }}"
As noted in my comment, if you simply want to install multiple yum/apt packages, it is usually more efficient to simply pass the list to the apt/yum/package module. As the docs state:
"When used with a loop: each package will be processed individually, it is much more efficient to pass the list directly to the name option."
However, if you really need a loop, here is a possible solution:
playbook.yml:
---
- hosts: all
gather_facts: false
vars_files:
- packages.yml
tasks:
- name: iteration over single items
debug:
msg: "name: {{ item.package }}"
when: item.package is defined
with_items: "{{ packages }}"
- name: iteration over lists
debug:
msg: "name: {{ item.packageList }}"
when: item.packageList is defined
with_items: "{{ packages }}"
- name: Do something with individual packages in the list
include_tasks: process_list.yml
vars:
mylist: "{{outer.packageList}}"
when: outer.packageList is defined
loop: "{{ packages }}"
loop_control:
loop_var: outer
process_list.yml:
- name: See what we have received
debug:
var: item
loop: "{{mylist}}"
result:
PLAY [all] *******************************************************************************************************************************
TASK [iteration over single items] *******************************************************************************************************
skipping: [localhost] => (item={u'packageList': [u'package1,', u'package2,', u'package3'], u'name': u'Some description 1,'})
ok: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'}) => {
"msg": "name: package4"
}
TASK [iteration over lists] **************************************************************************************************************
ok: [localhost] => (item={u'packageList': [u'package1,', u'package2,', u'package3'], u'name': u'Some description 1,'}) => {
"msg": "name: [u'package1,', u'package2,', u'package3']"
}
skipping: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'})
TASK [Do something with individual packages in the list] *********************************************************************************
skipping: [localhost] => (item={u'name': u'Some description 2,', u'package': u'package4'})
included: /root/tmp/process_list.yml for localhost
TASK [See what we have received] *********************************************************************************************************
ok: [localhost] => (item=package1,) => {
"ansible_loop_var": "item",
"item": "package1,"
}
ok: [localhost] => (item=package2,) => {
"ansible_loop_var": "item",
"item": "package2,"
}
ok: [localhost] => (item=package3) => {
"ansible_loop_var": "item",
"item": "package3"
}
PLAY RECAP *******************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The loop_control/loop_var part is used because otherwise both the outer and inner loop will use {{item}} as the loop variable - and this will cause... interesting results :)
You can define a default value with an empty list for the cases, where the packageList is undefined.
{{ item.packageList | default ([]) }}
If the packageList is undefined, the job iterates over an empty list, which means, it does not do anything.
You can use default, as #ceving mentioned:
---
- hosts: localhost
connection: local
vars_files:
- packages.yml
tasks:
- name: iteration
debug:
msg: "name: {{ item.packageList | default([item.package]) }}"
with_items: "{{ packages }}"
If packageList exists, it will use that, else package put into a single-element array to match the form of packageList:
PLAY [localhost] **********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************
ok: [localhost]
TASK [iteration] **********************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "name: [u'package1,', u'package2,', u'package3']"
}
ok: [localhost] => (item=None) => {
"msg": "name: [u'package4']"
}
PLAY RECAP ****************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=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

Resources