Utilizing Ansible looping thru fact_diff results - loops

I am building a vCenter cluster using Ansible, as part of that I need to be able to find a new drive and add it as a datastore.
The basic process I am following is:
get the ESXi facts ( community.vmware.vmware_host_disk_info
add the drive(s. Could be multiple.
scan the esxi storage (community.vmware.vmware_host_scanhba )
regather the esxi host facts.
I then use the fact_diff to get the difference and at that point I want to add the new drive.
I have not included the esxi and rescan code, as it's kinda moot.
- name: Show the difference between before and after rescan
ansible.utils.fact_diff:
before: "{{ host_facts_before|ansible.utils.to_paths }}"
after: "{{ host_facts_after|ansible.utils.to_paths }}"
register: disk_diff
- debug:
msg: "{{ disk_diff.diff_lines }}"
The output of this is pretty much a git style output.
--- before
+++ after
## -63,5 +63,12 ##
"hosts_disk_info['10.10.20.137'][8].device_path": "/vmfs/devices/disks/naa.6000c29a016671a1ef4a4b5fc01703a0",
"hosts_disk_info['10.10.20.137'][8].device_type": "disk",
"hosts_disk_info['10.10.20.137'][8].disk_uid": "key-vim.host.ScsiDisk-02000000006000c29a016671a1ef4a4b5fc01703a0566972747561",
- "hosts_disk_info['10.10.20.137'][8].display_name": "Local VMware Disk (naa.6000c29a016671a1ef4a4b5fc01703a0)"
+ "hosts_disk_info['10.10.20.137'][8].display_name": "Local VMware Disk (naa.6000c29a016671a1ef4a4b5fc01703a0)",
+ "hosts_disk_info['10.10.20.137'][9].canonical_name": "naa.6000c293214d38d992261b1d830f30d8",
+ "hosts_disk_info['10.10.20.137'][9].capacity_mb": 40960,
+ "hosts_disk_info['10.10.20.137'][9].device_ctd_list[0]": "vmhba0:C0:T8:L0",
+ "hosts_disk_info['10.10.20.137'][9].device_path": "/vmfs/devices/disks/naa.6000c293214d38d992261b1d830f30d8",
+ "hosts_disk_info['10.10.20.137'][9].device_type": "disk",
+ "hosts_disk_info['10.10.20.137'][9].disk_uid": "key-vim.host.ScsiDisk-02000000006000c293214d38d992261b1d830f30d8566972747561",
+ "hosts_disk_info['10.10.20.137'][9].display_name": "Local VMware Disk (naa.6000c293214d38d992261b1d830f30d8)"
}
changed: [localhost]
TASK [debug] ************************************************************************************************************************
ok: [localhost] => {
"msg": [
"--- before",
"+++ after",
"## -63,5 +63,12 ##",
" \"hosts_disk_info['10.10.20.137'][8].device_path\": \"/vmfs/devices/disks/naa.6000c29a016671a1ef4a4b5fc01703a0\",",
" \"hosts_disk_info['10.10.20.137'][8].device_type\": \"disk\",",
" \"hosts_disk_info['10.10.20.137'][8].disk_uid\": \"key-vim.host.ScsiDisk-02000000006000c29a016671a1ef4a4b5fc01703a0566972747561\",",
"- \"hosts_disk_info['10.10.20.137'][8].display_name\": \"Local VMware Disk (naa.6000c29a016671a1ef4a4b5fc01703a0)\"",
"+ \"hosts_disk_info['10.10.20.137'][8].display_name\": \"Local VMware Disk (naa.6000c29a016671a1ef4a4b5fc01703a0)\",",
"+ \"hosts_disk_info['10.10.20.137'][9].canonical_name\": \"naa.6000c293214d38d992261b1d830f30d8\",",
"+ \"hosts_disk_info['10.10.20.137'][9].capacity_mb\": 40960,",
"+ \"hosts_disk_info['10.10.20.137'][9].device_ctd_list[0]\": \"vmhba0:C0:T8:L0\",",
"+ \"hosts_disk_info['10.10.20.137'][9].device_path\": \"/vmfs/devices/disks/naa.6000c293214d38d992261b1d830f30d8\",",
"+ \"hosts_disk_info['10.10.20.137'][9].device_type\": \"disk\",",
"+ \"hosts_disk_info['10.10.20.137'][9].disk_uid\": \"key-vim.host.ScsiDisk-02000000006000c293214d38d992261b1d830f30d8566972747561\",",
"+ \"hosts_disk_info['10.10.20.137'][9].display_name\": \"Local VMware Disk (naa.6000c293214d38d992261b1d830f30d8)\"",
" }",
""
]
}
The question is - how do I take the results above and use to mount any new drives as datastores. The only piece of data I need is the canonical name and thinking of using the "[9]" as part of the name (e.g ds_9 )
- name: Mount VMFS datastores to ESXi
vars:
ansible_python_interpreter: /usr/bin/python3
community.vmware.vmware_host_datastore:
validate_certs: no
hostname: '{{ vcenter_hostname_pod }}'
username: '{{ vcenter_username_pod }}'
password: '{{ vcenter_password_pod }}'
datastore_name: " {{ 'test_' + something??? }}"
datastore_type: 'vmfs'
vmfs_device_name: " {{ canonical_name }} "
vmfs_version: 6
esxi_hostname: '{{ esxi_hostname }}'
state: present
register: mount
- debug:
msg: "{{ mount }}"

If I understand this right, then after adding a new disk, it will show up as a new key in hosts_disk_info['10.10.20.137'], ie. the 9 key in your above example.
You can use the difference filter on 2 lists to get the elements that exist in the first list and don't exist in second list. If we apply that on the list of keys representing the disks after/before:
{{ host_facts_after['hosts_disk_info']['10.10.20.137'].keys() | difference(host_facts_before['hosts_disk_info']['10.10.20.137'].keys()) }}
This should, using the data from your example, return [9]
Then you can loop over this resulting list running your mount
...
community.vmware.vmware_host_datastore:
...
datastore_name: "{{ 'ds_' + item | string }}"
vmfs_device_name: "{{ host_facts_after['hosts_disk_info']['10.10.20.137'][item].canonical_name }}"
...
loop: "{{ host_facts_after['hosts_disk_info']['10.10.20.137'].keys() | difference(host_facts_before['hosts_disk_info']['10.10.20.137'].keys()) }}"
Obviously, you don't want to hardcode the hostname/IP but you get the idea.

Related

Ansible - Get key pair value from file on remote machine and populate a variable

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

Ansible: copying the value from one file to other based on a condition

I've tried a lot many ways to get this done in Ansible, I still get stuck :/
So, I have to read credentials (user & password) from one file:
file1: (dest: /home/usr/Desktop/file1.txt)
#this is a cred file
number= 8910334
user= xyz #(this maybe username=xyz also)
password= abc
After reading the file content, I need to split where ever I find a ('=') sign with 'user' & 'password' based on the following:
user key= user
user value= xyz
password key= password
password value= abc
file2: (dest: /home/usr/Desktop/file2.txt)
#Hi, I need credentials
school= spsg
user = #(this value may be blank or may be filled with some other entry or it may be xyz.user.name= )
password= #(this value may be blank or may be filled with some other entry or it may be xyz.password.value= )
Now, in file2, I want the user=________ value & password=________ value replaced by the extracted user value & password value from file1. (the line number of user & password may vary, so don't fix the line numbers as [1] & [2] respectively)
Also, along with file2, there are some more files which need the values from file1, so it would be better if this task is done with loops.
Please suggest me with possible YAML code for getting this done. Thanks :)
If the file is not local fetch it. Then the task below
vars:
file_name: file1.txt
tasks:
- set_fact:
my_vars: "{{ my_vars|default([]) +
[{'key': item, 'value': my_value}] }}"
loop: "{{ lookup('pipe', my_cat).splitlines()|
select('match', my_regex_comment)|
map('regex_replace', my_regex, my_replace)|
map('trim')|
list }}"
vars:
my_regex_comment: '^(?!\s*\#).*$' # filter commented line
my_regex_any_comment: '(\s*\#.*)' # match comment in value
my_regex: '^(.*?)=(.*)$' # match key and value
my_replace: '\1' # replace with key
my_replace_empty: "" # delete
my_cat: "{{ 'cat ' ~ file_name }}"
my_ini: "{{ item ~ ' type=properties file=' ~ file_name }}"
my_value: "{{ lookup('ini', my_ini)|
regex_replace(my_regex_any_comment, my_replace_empty) }}"
- debug:
var: my_vars
gives
my_vars:
- key: number
value: '8910334'
- key: user
value: xyz
- key: password
value: abc
Use template to write the file. For example the task and the template below
- template:
src: file2.txt.j2
dest: file2.txt
shell> cat file2.txt.j2
{% for item in my_vars %}
{{ item.key }}={{ item.value }}
{% endfor %}
give
shell> cat file2.txt
number=8910334
user=xyz
password=abc
The next option is to use lineinfile to add, or replace the variables in the loop
- lineinfile:
path: file2.txt
regex: '^\s*{{ item.key }}\s*=(.*)$'
line: '{{ item.key }}={{ item.value }}'
loop: "{{ my_vars }}"

Ansible Debug array of object for specific value using Loop?

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.

traversal through the loop in ansible playbook

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' ]

Ansible loop over the first task output array

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 }}"

Resources