Ansible set_fact is overwriting the items - ansible

Here is my main.yml
---
- name: Gathering VCenter facts
vmware_vm_info:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
validate_certs: false
register: vcenter_facts
delegate_to: localhost
- debug:
var: vcenter_facts.virtual_machines
- name: Find all test-vms to run IO
set_fact:
vm_ip: "{{ item.ip_address }}"
loop: "{{ vcenter_facts.virtual_machines }}"
when: item.guest_name is regex("test_vm*")
- name: print vm_ip variable value
debug:
var: vm_ip
- name: Mount 16TB dropbox in each test vm
shell: mount-16tb-dropbox.sh
args:
chdir: /usr/local/bin/
with_items: "{{ vm_ip }}"
And here is the recap:
ok: [localhost] => {
"vcenter_facts.virtual_machines": [
{
"attributes": {},
"cluster": "Compute Cluster",
"esxi_hostname": "100.80.90.179",
"guest_fullname": "CentOS 7 (64-bit)",
"guest_name": "test_vm4",
"ip_address": "192.168.202.13",
"mac_address": [
"00:50:56:9d:d2:99"
],
"power_state": "poweredOn",
"tags": [],
"uuid": "421d7b54-1359-14e8-3ec4-74b568cb96d2",
"vm_network": {
"00:50:56:9d:d2:99": {
"ipv4": [
"192.168.202.13"
],
"ipv6": [
"fe80::44f6:a395:cde3:4dd1",
"fe80::a357:a163:e44f:2086",
"fe80::cd0c:e7d7:1356:2830"
]
}
}
},
{
"attributes": {},
"cluster": "Compute Cluster",
"esxi_hostname": "100.80.90.178",
"guest_fullname": "CentOS 7 (64-bit)",
"guest_name": "test_vm3",
"ip_address": "192.168.202.12",
"mac_address": [
"00:50:56:9d:a9:e8"
],
"power_state": "poweredOn",
"tags": [],
"uuid": "421d9239-0980-80c1-bca4-540efd726452",
"vm_network": {
"00:50:56:9d:a9:e8": {
"ipv4": [
"192.168.202.12"
],
"ipv6": [
"fe80::cd0c:e7d7:1356:2830"
]
}
}
},
{
"attributes": {},
"cluster": "Compute Cluster",
"esxi_hostname": "100.80.90.178",
"guest_fullname": "CentOS 7 (64-bit)",
"guest_name": "Test_Automation_CentOS8_Linux_VM",
"ip_address": "192.168.202.6",
"mac_address": [
"00:50:56:9d:13:14"
],
"power_state": "poweredOn",
"tags": [],
"uuid": "421d53ba-4824-57e4-06fd-fba0f2b1dbea",
"vm_network": {
"00:50:56:9d:13:14": {
"ipv4": [
"192.168.202.6"
],
"ipv6": [
"fe80::cd0c:e7d7:1356:2830",
"fe80::44f6:a395:cde3:4dd1"
]
}
}
},
{
"attributes": {},
"cluster": "Compute Cluster",
"esxi_hostname": "100.80.90.180",
"guest_fullname": "CentOS 7 (64-bit)",
"guest_name": "test_vm5",
"ip_address": "192.168.202.14",
"mac_address": [
"00:50:56:9d:85:b6"
],
"power_state": "poweredOn",
"tags": [],
"uuid": "421d6855-e60e-cd80-f113-39f11927d63b",
"vm_network": {
"00:50:56:9d:85:b6": {
"ipv4": [
"192.168.202.14"
],
"ipv6": [
"fe80::44f6:a395:cde3:4dd1",
"fe80::cd0c:e7d7:1356:2830",
"fe80::a357:a163:e44f:2086"
]
}
}
}
]
}
I am not able to loop through all the ip_address variable (i.e. 192.168.202.12, 192.168.202.13, 192.168.202.14).
It just reads the last item (i.e. 192.168.202.14).
What am I possibly doing wrong with set_fact that it is not reading all the variable and performing the set of tasks that follows?

An alternate solution using json_query
---
- name: Gathering VCenter facts
vmware_vm_info:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
validate_certs: false
register: vcenter_facts
delegate_to: localhost
- name: Mount 16TB dropbox in each test vm
shell: mount-16tb-dropbox.sh
args:
chdir: /usr/local/bin/
vars:
query: >-
[?contains("guest_name", 'test_vm')].ip_address[]
with_items: "{{ vcenter_facts.virtual_machines | to_json | from_json | json_query(query) | list }}"
Note: to_json | from_json is a workaround for a bug between ansible and jmespath so that all values can be converted to real strings and can be used with the jmespath contains function.

This should give all the IP. You correctly assumed where the code may need correction. In the code, vm_ip variable was overwritten by each loop and the last IP remained. What you need is a list and then append each IP to the list.
- set_fact:
vm_ip: "{{ vm_ip | default([]) + [item.ip_address] }}"
loop: "{{ vcenter_facts.virtual_machines | flatten }}"
when: item.guest_name is regex("test_vm*")
- debug:
var: vm_ip
Alternative solution using Jinja2 filters.
- set_fact:
vm_ip: >-
{{ vcenter_facts.virtual_machines | flatten
| rejectattr('guest_name', 'match', '^(?!test_vm).*')
| map(attribute='ip_address') | list }}

Related

Extract hostvar list

I'm trying to get secondary ip addresses from hosts in a group and set them to a fact.
my hostvars contain
{
"network_interfaces": [
{
"private_ip_address": "10.224.1.48",
"private_ip_addresses": [
{
"primary": true,
"private_dns_name": "ip-10-224-1-48.us-east-2.compute.internal",
"private_ip_address": "10.224.1.48"
},
{
"primary": false,
"private_dns_name": "ip-10-224-1-66.us-east-2.compute.internal",
"private_ip_address": "10.224.1.66"
},
{
"primary": false,
"private_dns_name": "ip-10-224-1-135.us-east-2.compute.internal",
"private_ip_address": "10.224.1.135"
}
],
"source_dest_check": true,
"status": "in-use",
"subnet_id": "subnet-0cfc6e2da31b9cf50",
"vpc_id": "vpc-123456"
}
],
}
Something like
set_fact:
private_ips: "{{ groups['database'] | map('extract', hostvars, ['network_interfaces[0]','private_ip_addresses[1]','private_ip_address']) | join(',') }}"
which doesn't work.
set_fact:
private_ips: "{{ groups['database'] | map('extract', hostvars, ['network_interfaces']) | map(attribute='private_ip_addresses') }}"
ends up with "private_ips": "[AnsibleUndefined, AnsibleUndefined]"
I'm looking for the result to be just a single IP out of private_ip_addresses from each host in the group
Create the list of IPs at each host
- set_fact:
my_ip: "{{ network_interfaces|json_query(_query) }}"
vars:
_query: '[].private_ip_addresses[].private_ip_address'
gives for the data from the example
my_ip:
- 10.224.1.48
- 10.224.1.66
- 10.224.1.135
Then, select the second item from the lists of all hosts in the group database
- set_fact:
private_ips: "{{ groups.database|
map('extract', hostvars, 'my_ip')|
map(attribute=1)|list }}"
run_once: true

Filter debug msg

I am using Ansible 2.9.13 and I have this playbook:
---
- hosts: localhost
connection: local
vars:
ansible_python_interpreter: /usr/bin/env python3
vars_files:
- vars.yml
tasks:
- name: Get Tags from given VM Name
vmware_vm_info:
validate_certs: no
hostname: '{{ vcenter_server }}'
username: '{{ vcenter_user }}'
password: '{{ vcenter_pass }}'
folder: '{{ provision_folder }}'
delegate_to: localhost
register: vm_info
- debug:
msg: "{{ vm_info.virtual_machines | json_query(query) }}"
vars:
query: "[?guest_name=='C97A1612171478']"
When I run it I am getting this output:
ok: [localhost] => {
"msg": [
{
"attributes": {},
"cluster": "xxx01",
"esxi_hostname": "xxxx",
"guest_fullname": "Microsoft Windows 10 (64-bit)",
"guest_name": "C97A1612171478",
"ip_address": "10.x.x.x",
"mac_address": [
"0x:x:x:x:xd:x"
],
"power_state": "poweredOn",
"tags": [],
"uuid": "420xxaf-xxx-xe2-9xe-a5xxxxxa3c",
"vm_network": {
"0x:x:x:xa:x:x": {
"ipv4": [
"169.x.x.x"
],
"ipv6": [
"x::x:x:x:xc"
]
},
"x:x:x:x:x0:x1": {
"ipv4": [
"169.x.x.x"
],
"ipv6": [
"x::x7:xf:x:x"
]
},
"0x:5x:x:x:ax:x": {
"ipv4": [
"10.x.x.x"
],
"ipv6": [
"x::1xx:x:8xx:x"
]
}
}
}
]
}
How can I filter the output to make it show only the "ip_address": "10.x.x.x".
In the end only the 10.x.x.x.
I have tried some ways adding the key ip_address in the message code but all of them gave me an error.
I can filter the msg using Python but if there's a way to get it using Ansible I would like to know how.
If you want to get this information without a loop:
If you need an object as a result:
- debug:
msg: "{{ vm_info.virtual_machines | json_query(query) }}"
vars:
query: "[?guest_name=='C97A1612171478'] | [0].{ip_address: ip_address}"
will yield
{
"ip_address": "10.x.x.x"
}
If you need a string as a result:
- debug:
msg: "{{ vm_info.virtual_machines | json_query(query) }}"
vars:
query: "[?guest_name=='C97A1612171478'] | [0].ip_address"
will yield
"10.x.x.x"
I can't test this properly, but try to fiddle around with the following code:
- debug:
msg: "{{ item.ip_address | json_query(query) }}"
loop: "{{ vm_info.virtual_machines }}"
vars:
query: "[?guest_name=='C97A1612171478']"

How to pull deeply nested data from a dictionary in ansible

I am trying to print nested data from an ansible dictionary. However, I cannot figure out how to pull the nested values. Specifically, in the data below, I am trying to print the two values returned under "ipv4". Currently, the output of item.value.ipv4 is the portion in brackets below:
"msg": "GigabitEthernet0/0/0,[{'address': '192.168.3.1', 'subnet': '28'}]"
I would like to just use the value like so:
"msg": "GigabitEthernet0/0/0, 192.168.3.1, 28"
I cannot figure out how to pull this nested data out of to do this. To put it simply, it would be nice if something like this worked: item.value.ipv4['address']. How is this done?
tasks:
- name: get config for Cisco IOS
ios_facts:
gather_subset: all
gather_network_resources: interfaces
- name: create dictionary with ansible_net_interfaces
set_fact:
foo_value: "{{ query('dict', ansible_net_interfaces) }}"
- name: display the results of foo_value
debug:
msg: "{{ foo_value }}"
- name: display certain detalys values from foo_value
debug:
msg: "{{ item.key }},{{ item.value.ipv4 }}"
with_items: "{{ foo_value }}"
These tasks produce the following:
TASK [display the results of foo] **********************************************************************************************************************
ok: [192.168.3.1] => {
"msg": [
{
"key": "GigabitEthernet0/0/0",
"value": {
"bandwidth": 1000000,
"description": "This is an interface description",
"duplex": "Full",
"ipv4": [
{
"address": "192.168.3.1",
"subnet": "28"
}
],
"lineprotocol": "up",
"macaddress": "50f7.123c.b0c0",
"mediatype": "RJ45",
"mtu": 1500,
"operstatus": "up",
"type": "ISR4331-3x1GE"
}
},
{
"key": "GigabitEthernet0/0/1",
"value": {
"bandwidth": 1000000,
"description": "This is another interface description",
"duplex": "Full",
"ipv4": [
{
"address": "192.168.3.33",
"subnet": "30"
}
],
"lineprotocol": "up",
"macaddress": "50f7.123c.b0c0",
"mediatype": "RJ45",
"mtu": 1500,
"operstatus": "up",
"type": "ISR4331-3x1GE"
}
},
]
}
ipv4 is a list of dictionaries. Assuming you need only the first dictionary,
- name: display certain detalys values from foo_value
debug:
msg: "{{ item.key }},{{ item.value.ipv4[0].address }},{{ item.value.ipv4[0].subnet }}"
when: item.value.ipv4 is defined and item.value.ipv4[0].subnet is defined and item.value.ipv4[0].address is defined
with_items: "{{ foo_value }}"

Is it possible to set/lookup Ansible facts when play returns multiple values

I have the following playbook in AWX that looks up Infoblox hosts based on their Mac Address and then outputs the information in a more user friendly format.
The current playbook works providing that a single host with that Mac address exists but fails if there are multiple.
---
- hosts: localhost
connection: local
vars:
niosip: ""
niosmac: ""
niosdhcp: ""
nioshostname: ""
niossearchcatagory: "{{ 'name' if searchcatagory == 'Hostname' else 'ipv4addr' if searchcatagory == 'IP Address' else 'mac' if searchcatagory == 'Mac Address'}}"
pre_tasks:
- include_vars:
file: creds.yml
tasks:
- name: fetch host record
set_fact:
host: "{{ lookup('nios', 'record:host', filter={niossearchcatagory: searchcriteria, 'view': 'Internal'}, provider=nios_provider) }}"
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
niosmac: "{{ host.ipv4addrs[0].mac }}"
when: host != [] and host.ipv4addrs[0].mac is defined
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
when: host != [] and host.ipv4addrs[0].mac is undefined
- name: Host not found
debug:
msg: 'Cant find related host'
when: host == []
- name: Display Display Registration Info
debug:
msg:
- Hostname = {{ nioshostname }}
- IP = {{ niosip }}
- Mac Address {{ niosmac }}
- Registered for DHCP = {{ niosdhcp }}
when: host != [] and host.ipv4addrs[0].mac is defined
Variables niossearchcatagory and searchcriteria are passed into the playbook via an AWX Survey.
I've searched possible options around using loops or splitting the output down but I'm really at a loss on the best way to process this.
If the output matches this then the playbook works as expected
{
"changed": false,
"ansible_facts": {
"host": [
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LWF3eHRlc3Q1:snat-awxtest5.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtYXd4dGVzdDUuMTQ3LjE4OC4zMS40Lg:192.168.31.4/snat-awxtest5.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-awxtest5.test.com",
"ipv4addr": "192.168.31.4",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-awxtest5.test.com",
"view": "Internal"
},
]
},
"_ansible_no_log": false
}
And here's an example of the play returning multiple values
{
"changed": false,
"ansible_facts": {
"host": [
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LWF3eHRlc3Q1:snat-awxtest5.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtYXd4dGVzdDUuMTQ3LjE4OC4zMS40Lg:192.168.31.4/snat-awxtest5.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-awxtest5.test.com",
"ipv4addr": "192.168.31.4",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-awxtest5.test.com",
"view": "Internal"
},
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LW15d2Vi:snat-myweb.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtbXl3ZWIuMTQ3LjE4OC4zMS4yLg:192.168.31.2/snat-myweb.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-myweb.test.com",
"ipv4addr": "192.168.31.2",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-myweb.test.com",
"view": "Internal"
},
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LnVrLmFjLmJoYW0udGVzdC5zbmF0LXdlYg:snat-web.test.com/Internal",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQudWsuYWMuYmhhbS50ZXN0LnNuYXQtd2ViLjE0Ny4xODguMzEuMy4:192.168.31.3/snat-web.test.com/Internal",
"configure_for_dhcp": false,
"host": "snat-web.test.com",
"ipv4addr": "192.168.31.3",
"mac": "10:20:30:40:50:60"
}
],
"name": "snat-web.test.com",
"view": "Internal"
}
]
},
"_ansible_no_log": false
}
And this results in an error as the variables host.name, host.ipv4addrs etc.. don't exist which I presume is becasue there are multiples.
Any help on how to output each registration would be gratefully received.

Ansible parse JSON output

I am trying to parse the Ansible output the print a value
- name: Creating a new instance
os_server:
state: present
auth:
auth_url: "{{ auth_url }}"
username: "{{ username }}"
password: "{{ password }}"
project_name: "{{ project_name }}"
name: "{{ item.hostname }}"
image: "{{ item.image }}"
nics: "{{ nics }}"
with_items: "{{ servers }}"
register: "os"
Output:
"server": {
"OS-DCF:diskConfig": "MANUAL",
"OS-EXT-AZ:availability_zone": "zoneA",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2018-04-01T18:53:16.000000",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "10.190.230.23",
"accessIPv6": "",
"addresses": {
"provider_corenet_bif_757": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:1:3:3:5e:6a",
"OS-EXT-IPS:type": "fixed",
"addr": "10.19.23.23",
"version": 4
}
],
"provider_nmnet_bif_912": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:1:3:39:b:57",
"OS-EXT-IPS:type": "fixed",
"addr": "10.25.13.64",
"version": 4
}
]
server.addresses.provider_nmnet_bif_912.addr
},
I want to parse addr "10.25.13.64".
I tried {{ item.server.addresses.provider_nmnet_bif_912.addr }} and {{os.server.addresses.provider_nmnet_bif_912.addr}} both didnot work.
Need Help!!!
Finally figured it out:
"{{ item.server.addresses.provider_nmnet_bif_912[0].addr }}"

Resources