Ansible merge listed dictionaries - ansible

I'm currently building a Ansible playbook which provides me with below two variables. I need to merge these into one variable where the ipAddresses match as showed in the example below.
---
- hosts: localhost
gather_facts: no
vars:
sites:
- siteGroupId: 123
siteName: name123
siteDevices:
- ipAddress: 1.1.1.1
- ipAddress: 2.2.2.2
- siteGroupId: 456
siteName: name456
siteDevices:
- ipAddress: 3.3.3.3
- ipAddress: 4.4.4.4
devices:
- ipAddress: 1.1.1.1
deviceName: name123-a.tld
- ipAddress: 2.2.2.2
deviceName: name123-b.tld
- ipAddress: 3.3.3.3
deviceName: name456-a.tld
- ipAddress: 4.4.4.4
deviceName: name456-b.tld
Expected output:
sites:
- siteGroupId: 123
siteName: name123
siteDevices:
- ipAddress: 1.1.1.1
deviceName: name123-a.tld
- ipAddress: 2.2.2.2
deviceName: name123-b.tld
- siteGroupId: 456
siteName: name456
siteDevices:
- ipAddress: 3.3.3.3
deviceName: name456-a.tld
- ipAddress: 4.4.4.4
deviceName: name456-b.tld
What I have succeed so far with the below code is that I get the name added to the site but get 4 sites (duplicated siteGroupID as output).
---
- hosts: localhost
gather_facts: no
vars:
sites:
- siteGroupId: 123
siteName: name123
siteDevices:
- ipAddress: 1.1.1.1
- ipAddress: 2.2.2.2
- siteGroupId: 456
siteName: name456
siteDevices:
- ipAddress: 3.3.3.3
- ipAddress: 4.4.4.4
devices:
- ipAddress: 1.1.1.1
deviceName: name123-a.tld
- ipAddress: 2.2.2.2
deviceName: name123-b.tld
- ipAddress: 3.3.3.3
deviceName: name456-a.tld
- ipAddress: 4.4.4.4
deviceName: name456-b.tld
tasks:
- set_fact:
tmpVar1: "{{ item.0 | combine({'siteDevices': [ item.1 | combine(devices | selectattr('ipAddress','equalto',item.1.ipAddress) | first) ] }) }}"
loop: "{{lookup('subelements', sites, 'siteDevices', {'skip_missing': True})}}"
register: tmpVar2
- name: tmpVar1
debug:
msg: "{{ tmpVar1 }}"
- name: tmpVar2
debug:
msg: "{{ tmpVar2 }}"
- set_fact:
sites: "{{ tmpVar2.results | map(attribute='ansible_facts.tmpVar1') | list }}"
- name: SITES
debug:
msg: "{{ sites }}"
OUTPUT:
{
"siteDevices": [
{
"deviceName": "name123-a.tld",
"ipAddress": "1.1.1.1"
}
],
"siteGroupId": 123,
"siteName": "name123"
},
{
"siteDevices": [
{
"deviceName": "name123-b.tld",
"ipAddress": "2.2.2.2"
}
],
"siteGroupId": 123,
"siteName": "name123"
},
{
"siteDevices": [
{
"deviceName": "name456-a.tld",
"ipAddress": "3.3.3.3"
}
],
"siteGroupId": 456,
"siteName": "name456"
},
{
"siteDevices": [
{
"deviceName": "name456-b.tld",
"ipAddress": "4.4.4.4"
}
],
"siteGroupId": 456,
"siteName": "name456"
}

The tasks
- set_fact:
devices_dict: "{{ devices|
items2dict(key_name='ipAddress',
value_name='deviceName') }}"
- set_fact:
sites2: "{{ sites2|default([]) + [
item|
combine({'siteDevices':
dict(my_ipAddress|
zip(my_ipAddress|
map('extract', devices_dict)))|
dict2items(key_name='ipAddress',
value_name='deviceName')})] }}"
vars:
my_ipAddress: "{{ item.siteDevices|json_query('[].ipAddress') }}"
loop: "{{ sites }}"
- debug:
var: sites2
give
"sites2": [
{
"siteDevices": [
{
"deviceName": "name123-a.tld",
"ipAddress": "1.1.1.1"
},
{
"deviceName": "name123-b.tld",
"ipAddress": "2.2.2.2"
}
],
"siteGroupId": 123,
"siteName": "name123"
},
{
"siteDevices": [
{
"deviceName": "name456-a.tld",
"ipAddress": "3.3.3.3"
},
{
"deviceName": "name456-b.tld",
"ipAddress": "4.4.4.4"
}
],
"siteGroupId": 456,
"siteName": "name456"
}
]

Related

To collect backing_datastore from the vmware_guest_disk_info module output

I'm using the below tasks in the playbook to find the backing datastore in the vmware but I tried various options, which is not helping to gather the backing_datastore name, kindly suggest how to fetch:
- name: Take Snapshot
hosts: patching
serial: 1
vars_files:
- 1credentials.yml
tasks:
- name: Gather data of the registered virtual machines
community.vmware.vmware_vm_info:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
vm_type: vm
validate_certs: no
delegate_to: localhost
register: vminfo
- debug:
msg: "{{ item.datacenter }}"
loop: "{{ vminfo.virtual_machines}}"
when: item.ip_address == inventory_hostname
- name: Gather Disk facts for the server - {{ inventory_hostname }}
vmware_guest_disk_info:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
datacenter: "{{ item.datacenter }}"
uuid: "{{ item.uuid }}"
validate_certs: False
delegate_to: localhost
register: disk_fact
loop: "{{ vminfo.virtual_machines }}"
when: item.ip_address == inventory_hostname
- debug:
msg: "{{ item.backing_datastore }}"
loop: "{{ disk_fact }}"
The raw output of the registered variable disk_fact is mentioned below:
ok: [172.17.92.62] => {
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"item": {
"allocated": {},
"attributes": {},
"cluster": "Training",
"datacenter": "opendc-rookie",
"datastore_url": [
{
"name": "Rookie-Core",
"url": "/vmfs/volumes/60ebc5fb-2e22fef3-ec42-1402ec6fadc8"
},
{
"name": "ENV08",
"url": "/vmfs/volumes/60ebd414-4e6253d6-7e29-1402ec6fadc8"
}
],
"esxi_hostname": "172.17.138.13",
"folder": "/opendc-rookie/vm/ENV08",
"guest_fullname": "Microsoft Windows 8.x (64-bit)",
"guest_name": "win1",
"ip_address": "172.17.92.43",
"mac_address": [
"00:50:56:81:2e:64"
],
"moid": "vm-968",
"power_state": "poweredOn",
"tags": [],
"uuid": "4201e3dd-0cf5-1027-7eaf-d2b8804761d9",
"vm_network": {
"00:50:56:81:2e:64": {
"ipv4": [
"172.17.92.43"
],
"ipv6": []
}
}
},
"skip_reason": "Conditional result was False",
"skipped": true
},
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"guest_disk_info": {
"0": {
"backing_datastore": "ENV04",
"backing_disk_mode": "persistent",
"backing_diskmode": "persistent",
"backing_eagerlyscrub": false,
"backing_filename": "[ENV04] ansible_automation_platofrm_RHEL8/ansible_automation_platofrm_RHEL8-000005.vmdk",
"backing_thinprovisioned": false,
"backing_type": "FlatVer2",
"backing_uuid": "6000C294-c77a-56fa-180c-d17ac9693433",
"backing_writethrough": false,
"capacity_in_bytes": 25769803776,
"capacity_in_kb": 25165824,
"controller_bus_number": 0,
"controller_key": 1000,
"controller_type": "paravirtual",
"key": 2000,
"label": "Hard disk 1",
"summary": "25,165,824 KB",
"unit_number": 0
}
},
"invocation": {
"module_args": {
"datacenter": "opendc-rookie",
"folder": null,
"hostname": "172.17.168.212",
"moid": null,
"name": null,
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"port": 443,
"proxy_host": null,
"proxy_port": null,
"use_instance_uuid": false,
"username": "Administrator#vsphere.local",
"uuid": "42013f12-5142-2b83-1ce7-6c334958afd5",
"validate_certs": false
}
},
"item": {
"allocated": {},
"attributes": {},
"cluster": "Training",
"datacenter": "opendc-rookie",
"datastore_url": [
{
"name": "Rookie-Core",
"url": "/vmfs/volumes/60ebc5fb-2e22fef3-ec42-1402ec6fadc8"
},
{
"name": "ENV04",
"url": "/vmfs/volumes/60ebd318-7c586ef0-5838-1402ec6fadc8"
}
],
"esxi_hostname": "172.17.138.13",
"folder": "/opendc-rookie/vm/Pugaz",
"guest_fullname": "Red Hat Enterprise Linux 8 (64-bit)",
"guest_name": "ansible_automation_platofrm_RHEL8",
"ip_address": "172.17.92.62",
"mac_address": [
"00:50:56:81:c6:e3"
],
"moid": "vm-836",
"power_state": "poweredOn",
"tags": [],
"uuid": "42013f12-5142-2b83-1ce7-6c334958afd5",
"vm_network": {
"00:50:56:81:c6:e3": {
"ipv4": [
"172.17.92.62"
],
"ipv6": [
"fe80::250:56ff:fe81:c6e3"
]
}
}
}
}
Kindly suggest how can I fetch the backing_datastore from the above mentioned output.
The result is a list because there might be more items with the attribute backing_datastore. For example,
backing_datastores: "{{ disk_fact.results|json_query(_query) }}"
_query: '[].guest_disk_info[].*.backing_datastore'
gives
backing_datastores:
- - ENV04
If you want the first one
backing_datastore: "{{ backing_datastores|flatten|first }}"
gives
backing_datastore: ENV04
If you want to iterate results
- debug:
msg: "{{ item.guest_disk_info['0'].backing_datastore }}"
loop: "{{ disk_fact.results }}"
loop_control:
label: "{{ item.item.ip_address }}"
when: not item.skipped|d(false)
gives the attribute backing_datastore of the disk "0"
TASK [debug] **************************************************
skipping: [localhost] => (item=172.17.92.43)
ok: [localhost] => (item=172.17.92.62) =>
msg: ENV04
If you want to get the list of attributes backing_datastore of all disks use json_query in the loop
- debug:
msg: "{{ item.guest_disk_info|json_query('*.backing_datastore') }}"
loop: "{{ disk_fact.results }}"
loop_control:
label: "{{ item.item.ip_address }}"
when: not item.skipped|d(false)
gives
TASK [debug] **************************************************
skipping: [localhost] => (item=172.17.92.43)
ok: [localhost] => (item=172.17.92.62) =>
msg:
- ENV04
Example of a complete playbook for testing
- hosts: localhost
vars_files:
- disk_fact.json
vars:
backing_datastores: "{{ disk_fact.results|json_query(_query) }}"
_query: '[].guest_disk_info[].*.backing_datastore'
backing_datastore: "{{ backing_datastores|flatten|first }}"
tasks:
- debug:
var: backing_datastores
- debug:
var: backing_datastore
- debug:
msg: "{{ item.guest_disk_info['0'].backing_datastore }}"
loop: "{{ disk_fact.results }}"
loop_control:
label: "{{ item.item.ip_address }}"
when: not item.skipped|d(false)

Parsing dictionary from VMware module

I'm using ansible community.vmware.vmware_datacenter_info.I only want to get the name of my several datacenter in vCenter.
Here is my play:
---
- hosts: localhost
gather_facts: False
tasks:
- name: Gather information about all datacenters
community.vmware.vmware_datacenter_info:
hostname: "{{ vCenter }}"
username: "{{ vCenter_username }}"
password: "{{ vCenter_password }}"
validate_certs: False
delegate_to: localhost
register: result
Here is the output of my variable result:
TASK [Print the gateway for each host when defined] ********************************
ok: [localhost] => {
"msg": {
"changed": false,
"datacenter_info": [
{
"config_status": "gray",
"moid": "datacenter-271",
"name": "Sample_DC_1",
"overall_status": "gray"
},
{
"config_status": "gray",
"moid": "datacenter-276",
"name": "Sample_DC_2",
"overall_status": "gray"
}
],
"failed": false
}
}
What I would like to get is only the name for both datacenter: Sample_DC_1 and Simple_DC_2 so I can use them in another play.
What I tried:
- name: Display datacenter name
debug:
msg:
- "{{ item.value.name }}"
loop: "{{ lookup('dict', result) }}"
Result:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'bool object' has no attribute 'name
if I only try to display "Key: {{ item.key }}" & " Value: {{ item.value }}" I got this output:
"msg": [
"Key : datacenter_info Value : [{'name': 'Sample_DC_1', 'moid': 'datacenter-271', 'config_status': 'gray', 'overall_status': 'gray'}, {'name': 'Sample_DC_2', 'moid': 'datacenter-276', 'config_status': 'gray', 'overall_status': 'gray'}]"
]
How can I register my both datacenter's name in a var so I can re-use them later in the playbook?
Q: "Get the names of both datacenter."
A: Map the attribute
dc_names: "{{ result.datacenter_info|map(attribute='name')|list }}"
Example of a complete playbook for testing
- hosts: localhost
vars:
result:
changed: false
datacenter_info:
- config_status: gray
moid: datacenter-271
name: Sample_DC_1
overall_status: gray
- config_status: gray
moid: datacenter-276
name: Sample_DC_2
overall_status: gray
failed: false
dc_names: "{{ result.datacenter_info|map(attribute='name')|list }}"
tasks:
- debug:
var: dc_names
gives
dc_names:
- Sample_DC_1
- Sample_DC_2

SUM value from 2 lists

I have the following outputs:
{
"vmoff": [
{
"CONT2": [
"vm1",
"vm2",
"vm5"
]
},
{
"CONT1": [
"vm3",
"vm4",
"vm6"
]
}
]
}
AND
{
"vmprof2": [
{
"proc": {
"vm1": "0.5",
"vm2": "0.7",
"vm3": "1.0",
"vm4": "0.5",
"vm5": "0.5",
"vm6": "0.5"
},
"sec_sys": "CONT1"
},
{
"proc": {
"vm1": "1.0",
"vm2": "0.7",
"vm3": "1.0",
"vm4": "0.7",
"vm5": "0.5",
"vm6": "0.7"
},
"sec_sys": "CONT2"
}
]
}
I want to sum the value of proc of each vm in vmoff, for example:
proc_steal:
- sec_sys: CONT1
proc: 2.0
- sec_sys: CONT2
proc: 2.2
Obtain the proc of the not activated vm, I'm getting confused on the output, don't know to use list or dict.
I suggest you to use a custom filter:
Create a folder filter_plugins at the same level as your playbook.
I created a file myfilter.py with a filter named extractfilter
.
├── filter_plugins
│ └── myfilter.py
└── playbook.yml
whith the following content:
#!/usr/bin/python
class FilterModule(object):
def filters(self):
return {
'extractfilter': self.extractfilter
}
def extractfilter(self, l1, l2):
result = []
ky = {}
for k in l1:
ky.update(k)
for k in l2:
s = k['sec_sys']
l = ky[s]
if s in ky:
total = 0.0
proc = k['proc']
for p in proc:
if p in l:
total = total + float(proc[p])
result.append({'sec_sys': s, 'proc': total})
return result
Then the playbook which uses the custom filter:
- name: testplaybook jinja2
hosts: localhost
gather_facts: no
vars:
vmoff:
- CONT2:
- vm1
- vm2
- vm5
- CONT1:
- vm3
- vm4
- vm6
vmprof2:
- proc:
vm1: '0.5'
vm2: '0.7'
vm3: '1.0'
vm4: '0.5'
vm5: '0.5'
vm6: '0.5'
sec_sys: CONT1
- proc:
vm1: '1.0'
vm2: '0.7'
vm3: '1.0'
vm4: '0.7'
vm5: '0.5'
vm6: '0.7'
sec_sys: CONT2
tasks:
- name: DEFINE VARIABLE SPINE
set_fact:
proc_steal: "{{ vmoff | extractfilter(vmprof2) }}"
- debug: msg="{{ proc_steal }}"
result:
{
"msg": [
{
"proc": 2.0,
"sec_sys": "CONT1"
},
{
"proc": 2.2,
"sec_sys": "CONT2"
}
]
}
If you have a problem with the custom filter you could use jinja:
- name: "make this working"
hosts: localhost
vars:
vmoff:
- CONT2:
- vm1
- vm2
- vm5
- CONT1:
- vm3
- vm4
- vm6
vmprof2:
- proc:
vm1: '0.5'
vm2: '0.7'
vm3: '1.0'
vm4: '0.5'
vm5: '0.5'
vm6: '0.5'
sec_sys: CONT1
- proc:
vm1: '1.0'
vm2: '0.7'
vm3: '1.0'
vm4: '0.7'
vm5: '0.5'
vm6: '0.7'
sec_sys: CONT2
tasks:
- name: list to dict
set_fact:
ky: "{{ ky | d({}) | combine( item ) }}"
loop: "{{ vmoff }}"
- name: list to dict
set_fact:
result: "{{ result| d([]) + [{'sec_sys': s, 'proc': total}] }}"
loop: "{{ vmprof2 }}"
vars:
s: "{{ item['sec_sys'] }}"
l: "{{ ky[s] }}"
total: >-
{%- set st = namespace(tt=0.0) -%}
{%- for p in item['proc'] if p in l -%}
{%- set st.tt = st.tt + (item['proc'][p] | float) -%}
{%- endfor -%}
{{ st.tt | float }}
when: s in ky
- name: list to dict
debug:
msg: "{{ result }}"

Setting multiple values in sysctl with Ansible

I have a playbook with several tasks setting values to sysctl. Instead of having a task for each setting, how can I set all the values with one task, using the sysctl module?
Playbook snippet:
- name: Set tcp_keepalive_probes in sysctl
become: yes
sysctl:
name: net.ipv4.tcp_keepalive_probes
value: 3
state: present
reload: yes
- name: Set tcp_keepalive_intvl in sysctl
become: yes
sysctl:
name: net.ipv4.tcp_keepalive_intvl
value: 10
state: present
reload: yes
- name: Set rmem_default in sysctl
become: yes
sysctl:
name: net.core.rmem_default
value: 16777216
state: present
reload: yes
define all the variables in a var file:
e.g.
sysctl:
- name: test
value: test
...
...
playbook:
- hosts: "{{ }}"
tasks:
- name: update sysctl param
sysctl:
name: "{{ item.name }}"
value: "{{ item.value }}"
state: present
reload: yes
with_items:
- "{{ hosts }}"
Simple solution: define variable as a dict
Example playbook:
---
- hosts: all
gather_facts: false
become: true
vars:
ansible_python_interpreter: /usr/bin/python3
sysctl_config:
net.ipv4.ip_forward: 1
net.ipv4.conf.all.forwarding: 1
net.ipv6.conf.all.forwarding: 1
tasks:
- name: Change various sysctl-settings
sysctl:
name: '{{ item.key }}'
value: '{{ item.value }}'
sysctl_set: yes
state: present
reload: yes
ignoreerrors: yes
with_dict: '{{ sysctl_config }}'
Output:
TASK [Change various sysctl-settings] **********************************************************************************************************************************************************************
changed: [10.10.10.10] => (item={'key': 'net.ipv4.ip_forward', 'value': 1}) => {
"ansible_loop_var": "item",
"changed": true,
"item": {
"key": "net.ipv4.ip_forward",
"value": 1
}
}
changed: [10.10.10.10] => (item={'key': 'net.ipv4.conf.all.forwarding', 'value': 1}) => {
"ansible_loop_var": "item",
"changed": true,
"item": {
"key": "net.ipv4.conf.all.forwarding",
"value": 1
}
}
changed: [10.10.10.10] => (item={'key': 'net.ipv6.conf.all.forwarding', 'value': 1}) => {
"ansible_loop_var": "item",
"changed": true,
"item": {
"key": "net.ipv6.conf.all.forwarding",
"value": 1
}
}

Create an AMI using ansible ec2_ami module and grab snapshot_id(s) from registered variable

I have the following playbook:
- hosts: localhost
connection: local
gather_facts: False
tasks:
- name: Read Variables
include_vars:
file: variables.yml
- name: Create AMI
ec2_ami:
aws_access_key: "{{ ACCESS_KEY }}"
aws_secret_key: "{{ SECRET_KEY }}"
region: "{{ AWS_REGION }}"
delete_snapshot: yes
instance_id: "{{ ID_INSTANCE }}"
name: "{{ SUFFIX_BACKUP }}-{{ NAME_BACKUP }}"
wait: yes
description: "{{ DESCR }}"
tags:
project: project1
register: ami_data
Example content of "ami_data" variable:
"msg": {
"architecture": "x86_64",
"block_device_mapping": {
"/dev/sda1": {
"delete_on_termination": true,
"encrypted": false,
"size": 30,
"snapshot_id": "snap-0e95eac98734af1d1",
"volume_type": "gp2"
}
},
"changed": false,
"creationDate": "2017-12-13T17:02:47.000Z",
"description": "descripition",
"failed": false,
"hypervisor": "xen",
"image_id": "ami-XXXXXX",
"is_public": false,
"launch_permissions": {},
"location": "457841571138/MYAMINAME",
"msg": "AMI not updated",
"name": "MYAMINAME",
"ownerId": "XXXXXXXXXX",
"platform": null,
"root_device_name": "/dev/sda1",
"root_device_type": "ebs",
"state": "available",
"tags": {
"project": "project1",
},
"virtualization_type": "hvm"
}
}
And now I want to grab the "snapshot_id" that was registered on "ami_data" variable for every disk on it. I have tried the following debug tasks using "msg" to show the value(s) of "snapshot_id" but non of them worked. What I'm doing wrong?
- name: Show data
debug:
msg: "{{ ami_data.block_device_mapping[*].snapshot_id }}"
- name: Show data
debug:
msg: "{{ ami_data.block_device_mapping[/dev/sda1].snapshot_id }}"
- name: Show data
debug:
msg: "{{ ami_data.block_device_mapping./dev/sda1.snapshot_id }}"
- name: Show data
debug:
msg: "{{ ami_data.block_device_mapping.[*].snapshot_id }}"
This one works:
- name: Show data
debug:
msg: "{{ ami_data.block_device_mapping }}"
And give me the following:
ok: [localhost] => {
"msg": {
"/dev/sda1": {
"delete_on_termination": true,
"encrypted": false,
"size": 30,
"snapshot_id": "snap-0ea846d9aca82b51f",
"volume_type": "gp2"
}
}
}
Firstly, with the ami_data.block_device_mapping you have to iterate with the list and after search under the map with the key name of the element (snapshot_id in this case):
- debug:
msg: {{ item }}
with_items: "{{ ami_data.block_device_mapping|map(attribute='snapshot_id')|list}}"
You were really close. Use square brackets but use quotes to specify literals:
- name: Show data
debug:
msg: "{{ ami_data.block_device_mapping['/dev/sda1'].snapshot_id }}"
or using variables:
- name: Show data
debug:
msg: "{{ ami_data.block_device_mapping[item].snapshot_id }}"
with_items: "{{ ami_data.block_device_mapping }}"

Resources