I am facing issue while writing nested loops in ansible, for below iteration I am getting error
ansible role I am using looks like below
The conditional check 'item[0]['value']['from'] == COMPONENT_NAME' failed. The error was: error while evaluating conditional (item[0]['value']['from'] == COMPONENT_NAME): 'item' is undefined
Role
- name: create config file
template:
src: "{{template_source}}/{{COMPONENT_NAME}}/sessions/{{ item[0]['value']['protocol']}}/{{item[0]['value']['from_template']}}"
dest: "{{dest_folder}}/{{app}}/{{instance_name}}/{{COMPONENT_NAME}}/{{VENUE}}/Config/Repo/session/{{item[0]['value']['protocol']}}/{{item[1]}}.json"
mode: 0755
vars:
dest_file_name: "{{instance_name}}_{{COMPONENT_NAME}}_{{VENUE}}_{{item[0]['key']}}"
mbFlag: "{{item[0]['value']['broker'] | default('False')}}"
when: item[0]['value']['from'] == COMPONENT_NAME
with_nested:
- "{{ connections | dict2items }}"
- "{{myFileList['from_' + COMPONENT_NAME +'_'+ VENUE +'_'+ item[0]['value']['to']].split(',') }}"
yml file having below variable and I am iterating over it.
myFileList:
{
from_et_AG_zp: DevOpsTest_et_zp_AG_zp,
from_et_AG_sSP: 'DevOpsTest_et_sSP_AG_US,DevOpsTest_et_sSP_AG_BA,DevOpsTest_et_sSP_AG_chex',
from_et_AG_esb: DevOpsTest_et_esb_AG_ABC,
from_et_BA_Y_zp: DevOpsTest_et_zp_BA_Y_zp,
from_et_BA_Y_sSP: 'DevOpsTest_et_sSP_BA_Y_US,DevOpsTest_et_sSP_BA_Y_BA,DevOpsTest_et_sSP_BA_Y_chex',
from_et_BA_Y_esb: DevOpsTest_et_esb_BA_Y_ABC,
from_et_BA_zp: DevOpsTest_et_zp_BA_zp,
from_et_BA_sSP: 'DevOpsTest_et_sSP_BA_US,DevOpsTest_et_sSP_BA_BA,DevOpsTest_et_sSP_BA_chex',
from_et_BA_esb: DevOpsTest_et_esb_BA_ABC,
from_zp_zp_esb: DevOpsTest_zp_esb_zp_ABC,
from_SP_SP_esb: DevOpsTest_SP_esb_SP_ABC,
from_SSS_SSS_esb: DevOpsTest_SSS_esb_SSS_ABC,
to_zp_zp_es: DevOpsTest_et_zp_zp_es,
to_sSP_BA_es: DevOpsTest_et_sSP_BA_es,
to_sSP_chex_es: DevOpsTest_et_sSP_chex_es,
to_sSP_US_es: DevOpsTest_et_sSP_US_es,
to_esb_ABC_es: DevOpsTest_et_esb_ABC_es,
to_esb_ABC_zp: DevOpsTest_zp_esb_ABC_zp,
to_esb_ABC_SP: DevOpsTest_SP_esb_ABC_SP,
to_esb_ABC_SSS: DevOpsTest_SSS_esb_ABC_SSS
}
connections:
et_etb:
from: et
to: etb
from_template: et_etb.json
to_template: etb_et.json
protocol: ZERO
After too many permutation and combination, I realize that we can not pass outer loop variable value from outer loop to inner loop, So as work around I write first loop in first role and second loop in another role and call second role from first by iterating over first loop that full fill my ends
---
- name: create config file
include_role:
name: patch_template
vars:
file_key: "{{ 'from_' + COMPONENT_NAME +'_'+ VENUE +'_'+ role_item.value.to }}"
myprotocol: "{{role_item.value.protocol}}"
myTemplate: "{{role_item.value.from_template}}"
subComponent: "{{VENUE}}"
when: role_item.value.from == COMPONENT_NAME
with_items: "{{ connections | dict2items }}"
loop_control:
loop_var: role_item
patch_template role file
- name: patch template
template:
src: "{{template_source}}/{{COMPONENT_NAME}}/sessions/{{myprotocol}}/{{myTemplate}}"
dest: "{{dest_folder}}/{{app}}/{{instance_name}}/{{COMPONENT_NAME}}/{{subComponent}}/Config/Repo/session/{{myprotocol}}/{{item}}.json"
mode: 0755
vars:
dest_file_name: "{{item}}"
with_items:
- "{{ myFileList[file_key].split(',') }}"
Related
I have a file in host_vars folder which looks like this:
tags:
tag1: value1
tag2: value2
tag3: '' <<-- This is an empty value which I would like to fill with some string
Now I wanted to go through each of these items and check if the value is empty and if it is add "NA" as a string to it, I used the following code for this:
- name: Clean any Tag inconsistencies
set_fact:
tags: "{{ item }} + NA"
when: (item | length == 0)
with_items: tags
- debug: var=tags
However, this does not do anything, it just prints the same list when checking the debug. What is wrong with this approach?
Edit:
I changed my code to respect that I am using a dictionary. This is my current approach. It prints out the values with key and name but does not change any item. I also tried using the jinja default filter which did not do anything:
- name: "Clean Tags"
set_fact:
tags: "{{ (tags | default('NA', true)) }}"
loop: "{{ tags | dict2items }}"
What I would like to achieve in the end is that it checks each dict value and if it is empty, it should add the value "NA" to it, so in the end above tags dictionary will look like this:
tags:
tag1: value1
tag2: value2
tag3: 'NA'
The play below
- hosts: localhost
vars:
my_tags:
tag1: value1
tag2: ''
tag3: ''
tasks:
- set_fact:
my_tags: "{{ my_tags|combine(dict(my_empty_tags|product(['NA']))) }}"
vars:
my_empty_tags: "{{ my_tags|dict2items|json_query(query) }}"
query: "[?value == ''].key"
- debug:
var: my_tags
gives (abridged)
my_tags:
tag1: value1
tag2: NA
tag3: NA
The same result can be achieved without json_query. Replace the task vars
vars:
my_empty_tags: "{{ my_tags|dict2items|
selectattr('value', 'eq', '')|
map(attribute='key')|
list }}"
tags is a playbook's keyword. You should have seen:
[WARNING]: Found variable using reserved name: tags
Q: "What happens in the set_fact section?"
A: Decompose the filter. First, create the product of the empty tags and the string 'NA'
- debug:
msg: "{{ my_empty_tags|product(['NA'])|list }}"
gives
msg:
- - tag2
- NA
- - tag3
- NA
Then create dictionaries from the items in the list
- debug:
msg: "{{ dict(my_empty_tags|product(['NA'])) }}"
gives
msg:
tag2: NA
tag3: NA
The last step in the filter is to combine the dictionaries.
Trying to extract a key pair value from a .yaml file and populate it into a variable:
/etc/puppetlabs/puppet/csr_attributes.yaml
File Example:
extension_requests:
pp_service: 'private'
pp_instance_id: 'blah'
pp_image_name: 'RedHat 7.7 Base'
pp_project: 'TBT'
pp_application: 'xxxxx'
pp_environment: 'dev'
pp_role: 'base_stuff'
pp_software_version: '2020-04-30'
pp_provisioner: 'Ansible Tower'
pp_datacenter: 'DC2'
pp_zone: 'C6600_NPE_RS'
pp_cloudplatform: 'esx'
pp_apptier: 'dev'
pp_securitypolicy: 'Stuff'
1.3.6.1.4.1.34380.1.2.1: ''
1.3.6.1.4.1.34380.1.2.2: '8'
1.3.6.1.4.1.34380.1.2.3: '77504'
I can do it via the line number, but I need it to be more dynamic as the lines are different from server to server:
Current individual line code:
- name: cat file
shell: cat /etc/puppetlabs/puppet/csr_attributes.yaml
register: cat_content_file
- set_fact:
pp_service: "pp_service: {{ cat_content_file.stdout_lines[2].split()[1] }}"
pp_application: "pp_application: {{ cat_content_file.stdout_lines[6].split()[1] }}"
- debug:
msg:
- "{{ pp_service }}"
- "{{ pp_application }}"
I think I need to convert the output into a dict, but I'm completely stuck on how to do it.
Any advice would be appreciated.
You could use a combination of file and from_yaml:
- set_fact:
your_variable: "{{ lookup('file','/etc/puppetlabs/puppet/csr_attributes.yaml') | from_yaml }}"
- debug: var=your_variable.extension_requests.pp_service
I have the following array of record with the value:
myRecord.record.0.number = "Number 0"
myRecord.record.1.number = "Number 1"
myRecord.record.2.number = "Number 2"
myRecord.record.3.number = "Number 3"
How to create a playbook to debug the above value using loop dynamically/for every array?
for other common languange it can be done as follows:
for(int i = 0; i < myRecord.length(); i++)
{
echo myRecord.record.[i].number
}
for the repeating task of the playbook, it will looks like this:
---
- hosts: localhost
name: Array of Object
gather_facts: false
tasks:
- name: using debugMsg
debug:
msg:
- "{{ myRecord.record.0.number }}"
- "{{ myRecord.record.1.number }}"
- "{{ myRecord.record.2.number }}"
- "{{ myRecord.record.3.number }}"
I have figured out how to do this. Basically I just need to use loop_control to filter which specific value I need. Here is the playbook:
---
- hosts: localhost
name: Array of Object
gather_facts: false
tasks:
- name: using loop_control
debug:
msg: "{{ item.number }}"
with_items:
- "{{ myRecord.record }}" #this will become 'item'
loop_control:
label: "{{ item.number }}" #filter to display the value of number only
There are two methods. The first method uses the with_* keyword, and depends on the Lookup plugin. The second method uses loop keyword, which is equivalent to 'with_' + 'list lookup plugin' (so you get 'with_list').
Now, assuming your data structure looks like this:
---
# vars file for print_variable_from_list
myRecord:
record:
- number: "Number 0"
- number: "Number 1"
- number: "Number 2"
- number: "Number 3"
Every number is indexable with the "number" key.
- name: loop through myRecord
debug:
msg: "{{ item.number }}"
loop: "{{ myRecord.record }}"
Kindly refer to other posts for more complex queries.
I am newbie to ansible and trying to write my first playbook.
- name: create volume
volume:
state: present
username: "{{ username }}"
password: "{{ password }}"
hostname: "{{ inventory_hostname }}"
vserver: "{{item[0]}}"
name: "{{item[1]}}"
aggregate_name: "{{output}}"
with_nested:
- [ 'vs10' , 'vs11' ]
- [ 'vol1' , 'vol2', 'vol3' , 'vol4' ,'vol5', ''vol6']
connection: local
Actual output:
vs10-vol1 vol2 vol3 vol4
vs11- vol1 vol2 vol3 vol4
Expected output:
vs10-vol1, vol3 vol5
vs11-vol2, vol4 vol6
This would probably work. I'm basically looping the task against volumes and calculating the vserver in the task.
- name: create volume
volume:
state: present
username: "{{ username }}"
password: "{{ password }}"
hostname: "{{ inventory_hostname }}"
# Calculate which vserver to use based on 'current volume index in the volumes list' and 'length of vservers list'.
# The logic uses modulus to try and distribute volumes across given vcenters
vserver: "{{vservers[(current_index % (vservers|length))]}}"
# Name is item itself because you are looping volumes
name: "{{item}}"
aggregate_name: "{{output}}"
# Loop the volumes
loop: [ 'vol1' , 'vol2', 'vol3' , 'vol4' ,'vol5', 'vol6']
# This is make a loop_control variable available. This will give us 'current_index'
loop_control:
index_var: current_index
# Vservers are defined here
vars:
vservers: [ 'vs10' , 'vs11' ]
I want to loop over the first task output in second task. In the first task getting hostname and IP in the array. In the second task loop over the array print the each item in the array in separate line.
here the code I have so for.
- name: Store known hosts of 'all' the hosts in the inventory file
hosts: localhost
connection: local
vars:
ssh_known_hosts_command: "ssh-keyscan -T 10"
ssh_known_hosts_file: "{{ lookup('env','HOME') + '/.ssh/known_hosts' }}"
ssh_known_hosts: "{{ groups['all'] }}"
tasks:
- name: For each host, find the ip
shell: 'echo -e "{{ item }}\n`dig +short {{ item }}`"'
with_items: "{{ ssh_known_hosts }}"
register: ssh_known_host_results
ignore_errors: yes
- name: print message
debug:
msg: "{{ item.stdout_lines[0] + ' test' }}"
with_items: "{{ ssh_known_host_results.results }}"
in the second task how can I loop over ssh_known_host_results.results array?
thanks
SR
I am looking for something like this:
- name: print message
debug:
msg: "{{ item+ ' test' }}"
with_items: "{{outer_item.stdout_lines "
with_items: "{{ ssh_known_host_results.results }}"
loop_control:
loop_var: outer_item
when I add to ignore localhost its not giving array item. how can it make return hostname and ip as two array elements?
- name: For each host, find the ip
shell: 'echo -e "{{ item }}\n`dig +short {{ item }}`"'
with_items: "{{ ssh_known_hosts }}"
when: not item == 'localhost'
register: ssh_known_host_results
ignore_errors: yes
- name: print message
debug:
msg: "{{ item + ' test' }}"
with_items: "{{ ssh_known_host_results.results | map(attribute='stdout_lines') | list }}"
Support for dynamic nested loops is not implemented in Ansible.
To iterate over each line, you can flatten the result:
- name: print message
debug:
msg: "{{ item + ' test' }}"
with_items: "{{ ssh_known_host_results.results | map(attribute='stdout_lines') | list }}"