Ansible Query how to query inside JSON output - json

I'm asking to a URL API and passing to output to a register. This should be a JSON output.
The first variable "vcclust" JSON, I can extract the "item.cluster" as it's just one level, bad the second output "vchosts" looks, I cant parse to get "item.name" from it and get the hostnames
This is the code:
- hosts: localhost
gather_facts: False
vars_prompt:
- name: "vcenter_hostname"
prompt: "Enter vcenter hostname"
private: no
- name: "vcenter_user"
prompt: "Enter vcenter username"
private: no
- name: "vcenter_pass"
prompt: "Enter vcenter password"
private: yes
- name: "cluster_touched"
prompt: "Enter cluster name"
private: no
tasks:
- name: Login in to vCenter and store login information
uri:
url: "https://{{ vcenter_hostname }}/rest/com/vmware/cis/session"
force_basic_auth: yes
method: POST
user: '{{ vcenter_user }}'
password: '{{ vcenter_pass }}'
status_code: 200
validate_certs: no
register: login
- name: Get vCenter cluster ID for {{ cluster_touched }}
uri:
url: "https://{{ vcenter_hostname }}/rest/vcenter/cluster?filter.names={{ cluster_touched }}"
force_basic_auth: yes
validate_certs: no
headers:
Cookie: '{{ login.set_cookie }}'
register: vcclust
- name: Get ESXi nodes from cluster ID for {{ cluster_touched }}
uri:
url: "https://{{ vcenter_hostname }}/rest/vcenter/host?filter.clusters={{ item.cluster }}"
force_basic_auth: yes
validate_certs: no
headers:
Cookie: '{{ login.set_cookie }}'
register: vchosts
with_items:
- '{{ vcclust.json.value }}'
- name: Modify root local user to ESXi
vmware_local_user_manager:
hostname: '{{ item.name }}'
username: root
password: '{{ esxi_pass }}'
local_user_name: root
local_user_password: '{{ esxi_new_pass }}'
validate_certs: False
with_items:
- '{{ vchosts.json.value }}'
And this is the output of the debug the vchosts and vcclust
vcclust:
TASK [debug] *************************************************************************************************************************************************************************************************
ok: [localhost] => {
"vcclust": {
"changed": false,
"connection": "close",
"content_type": "application/json",
"cookies": {},
"date": "Mon, 11 Jun 2018 09:50:43 GMT",
"failed": false,
"json": {
"value": [
{
"cluster": "domain-c310",
"drs_enabled": true,
"ha_enabled": false,
"name": "DB-CLUSTER"
}
]
},
"msg": "OK (unknown bytes)",
"redirected": false,
"status": 200,
"url": "https://vcenter01.lab.test/rest/vcenter/cluster?filter.names=DB-CLUSTER"
}
}
vchosts:
TASK [debug] *************************************************************************************************************************************************************************************************
ok: [localhost] => {
"vchosts": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_ignore_errors": null,
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"connection": "close",
"content_type": "application/json",
"cookies": {},
"date": "Mon, 11 Jun 2018 09:50:45 GMT",
"failed": false,
"invocation": {
"module_args": {
"attributes": null,
"backup": null,
"body": null,
"body_format": "raw",
"client_cert": null,
"client_key": null,
"content": null,
"creates": null,
"delimiter": null,
"dest": null,
"directory_mode": null,
"follow": false,
"follow_redirects": "safe",
"force": false,
"force_basic_auth": true,
"group": null,
"headers": {
"Cookie": "vmware-api-session-id=56fa6d3015150212b086917d15165bee;Path=/rest;Secure;HttpOnly"
},
"http_agent": "ansible-httpget",
"method": "GET",
"mode": null,
"owner": null,
"regexp": null,
"remote_src": null,
"removes": null,
"return_content": false,
"selevel": null,
"serole": null,
"setype": null,
"seuser": null,
"src": null,
"status_code": [
200
],
"timeout": 30,
"unsafe_writes": null,
"url": "https://vcenter01.lab.test/rest/vcenter/host?filter.clusters=domain-c310",
"url_password": null,
"url_username": null,
"use_proxy": true,
"validate_certs": false
}
},
"item": {
"cluster": "domain-c310",
"drs_enabled": true,
"ha_enabled": false,
"name": "DB-CLUSTER"
},
"json": {
"value": [
{
"connection_state": "CONNECTED",
"host": "host-312",
"name": "vmh19.lab.test",
"power_state": "POWERED_ON"
},
{
"connection_state": "CONNECTED",
"host": "host-313",
"name": "vmh20.lab.test",
"power_state": "POWERED_ON"
}
]
},
"msg": "OK (unknown bytes)",
"redirected": false,
"status": 200,
"url": "https://vcenter01.lab.test/rest/vcenter/host?filter.clusters=domain-c310"
}
]
}
}
The error I got is:
ansible (ansible_test) ✗)ansible-playbook site.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'
Enter vcenter hostname [vcenter01.lab.test]:
Enter vcenter username :
Enter vcenter password :
Enter cluster name [DB-CLUSTER]:
PLAY [localhost] *********************************************************************************************************************************************************************************************
TASK [Start SSH service setting for an ESXi Host in given Cluster] *******************************************************************************************************************************************
ok: [localhost]
TASK [Login in to vCenter and store login information] *******************************************************************************************************************************************************
ok: [localhost]
TASK [Get vCenter cluster ID for DB-CLUSTER] *************************************************************************************************************************************************************
ok: [localhost]
TASK [Get ESXi nodes from cluster ID for DB-CLUSTER] *****************************************************************************************************************************************************
ok: [localhost] => (item={'drs_enabled': True, 'cluster': 'domain-c310', 'name': 'DB-CLUSTER', 'ha_enabled': False})
TASK [Modify root local user to ESXi] ************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "'dict object' has no attribute 'json'"}
to retry, use: --limit #/Users/jv/Workspace/vmware-powershell/ansible/site.retry
PLAY RECAP ***************************************************************************************************************************************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=1
What I'm doing wrong?

Try using json_query. For that, you might need to install jmespath on your ansible host. You can do it with pip install jmespath.
- name: Get ESXi nodes from cluster ID for {{ cluster_touched }}
uri:
url: "https://{{ vcenter_hostname }}/rest/vcenter/host?filter.clusters={{ item.cluster }}"
force_basic_auth: yes
validate_certs: no
headers:
Cookie: '{{ login.set_cookie }}'
register: vchosts
with_items: "{{ vcclust | json_query('*.value') }}"
- name: Modify root local user to ESXi
vmware_local_user_manager:
hostname: '{{ item.name }}'
username: root
password: '{{ esxi_pass }}'
local_user_name: root
local_user_password: '{{ esxi_new_pass }}'
validate_certs: False
with_items: "{{ vchosts.results | json_query('*.value') }}"

Thanks a lot #Kelson Silva
you pointed the clue to the answer.
I dont know why this
- debug: var=item.name
with_items:
- "{{ vchosts.json.value }}"
is not the same as
- debug:
var: item.name
with_items:
- "{{ vchosts.json.value }}"
and actually that was driving me crazy, this is for sure a bug in this version of ansible
the problem is that it was tooking the elements in my first request as list inside a Json and you need to enumerate them.
In order to dont get a dirty "vchosts" JSON you need to use just one element from "vcclust", so the code end like:
- name: Get vCenter cluster ID for {{ cluster_touched }}
uri:
url: "https://{{ vcenter_hostname }}/rest/vcenter/cluster?filter.names={{ cluster_touched }}"
force_basic_auth: yes
validate_certs: no
headers:
Cookie: '{{ login.set_cookie }}'
register: vcclust
- name: Get ESXi nodes from cluster ID for {{ cluster_touched }}
uri:
url: "https://{{ vcenter_hostname }}/rest/vcenter/host?filter.clusters={{ vcclust.json.value[0].cluster }}"
force_basic_auth: yes
validate_certs: no
headers:
Cookie: '{{ login.set_cookie }}'
register: vchosts
- name: Modify root local user to ESXi
vmware_local_user_manager:
hostname: '{{ item.name }}'
username: root
password: '{{ esxi_pass }}'
local_user_name: root
local_user_password: '{{ esxi_new_pass }}'
validate_certs: False
with_items:
- "{{ vchosts.json.value }}"

Related

Change output of ansible module 'vmware_dvs_portgroup_find'

I try to dynamically get the right DVS Portgroup for my VM Deployment based on the variables I provide to my ansible playbook.
This is my playbook to get all Portgroups which contain the vlanid.
- name: Portgroup Info
hosts: localhost
become: false
gather_facts: false
collections:
- community.vmware
pre_tasks:
- include_vars: vars.yaml
tasks:
- name: Confirm if vlan is present
vmware_dvs_portgroup_find:
hostname: "{{ vcenter_hostname }}"
username: "{{ vcenter_username }}"
password: "{{ vcenter_password }}"
vlanid: "{{ vm_vlanid }}"
delegate_to: localhost
register: pg_info
- name: msg pg_info
ansible.builtin.debug:
msg: "{{ pg_info }}"
The problem is that the output also contains trunk ports.
TASK [msg pg_info] *******************************************************************************************************************
ok: [localhost] => {
"msg": {
"changed": false,
"dvs_portgroups": [
{
"dvswitch": "Lab-DSwitch",
"name": "VLAN1176-K8SManagement",
"pvlan": false,
"trunk": false,
"vlan_id": "1176"
},
{
"dvswitch": "Lab-DSwitch",
"name": "Lab-DSwitch-DVUplinks-1032",
"pvlan": false,
"trunk": true,
"vlan_id": "0-4094"
}
],
"failed": false
}
}
I've tried some manipulating with json_query in different ways. Tried to filter by trunk==true and things like that.
- name: filter by vlan id
debug:
var: item
with_items: "{{pg_info|json_query(vlanid_filter)}}"
vars:
vlanid_filter: >-
[].dvs_portgroups[?vlan_id=='{{ vm_vlanid }}'].name
- name: msg pg_info
ansible.builtin.debug:
msg: "{{ pg_info }}"
But the output is the same.
I'd love an output like this:
TASK [msg pg_info] *******************************************************************************************************************
ok: [localhost] => {
"msg": {
"changed": false,
"dvs_portgroups": [
{
"dvswitch": "Lab-DSwitch",
"name": "VLAN1176-K8SManagement",
"pvlan": false,
"trunk": false,
"vlan_id": "1176"
},
],
"failed": false
}
}
Maybe my thoughts on this are completely wrong. I am open for any helpful method on archiving the correct result.
Thanks.

How to Insert array inside JSON object?

I have a json file that I need to reformat - insert array around some of the nested objects.
It looks like that:
{
"test": {
"UserData": "test",
"password": "123"
},
"test2": {
"UserData": "test2",
"password": "123"
},
"test3": {
"UserData": "test3",
"password": "123"
}
}
And I need it to look like that:
{
"test": [{
"UserData": "test",
"password": "123"
}],
"test2": [{
"UserData": "test2",
"password": "123"
}],
"test3": [{
"UserData": "test3",
"password": "123"
}]
}
I'm using Ansible, jq and bash.
Given the data
d1:
test:
UserData: test
password: '123'
test2:
UserData: test2
password: '123'
test3:
UserData: test3
password: '123'
Convert the dictionaries to the first items in the lists. For example
d2_query: '[].[key, [value]]'
d2: "{{ dict(d1|dict2items|json_query(d2_query)) }}"
gives
d2:
test:
- UserData: test
password: '123'
test2:
- UserData: test2
password: '123'
test3:
- UserData: test3
password: '123'
Example of a complete playbook for testing
- hosts: localhost
vars:
d1:
test:
UserData: test
password: '123'
test2:
UserData: test2
password: '123'
test3:
UserData: test3
password: '123'
d2_query: '[].[key, [value]]'
d2: "{{ dict(d1|dict2items|json_query(d2_query)) }}"
tasks:
- debug:
var: d2
Using jq: Select each field of the object with .[], and update it |= to itself wrapped in an array [.].
jq '.[] |= [.]' file.json
{
"test": [
{
"UserData": "test",
"password": "123"
}
],
"test2": [
{
"UserData": "test2",
"password": "123"
}
],
"test3": [
{
"UserData": "test3",
"password": "123"
}
]
}
Demo
you could use this playbook (using dict2items and combine):
- name: "tips4"
hosts: localhost
vars:
json1:
test:
UserData: test
password: '123'
test2:
UserData: test2
password: '123'
test3:
UserData: test3
password: '123'
tasks:
- name: set var
set_fact:
newjson: "{{newjson|d({})|combine({item.key:[item.value]}) }}"
loop: "{{ json1|dict2items }}"
- name: disp result
debug:
msg: "{{ newjson }}"
result:
ok: [localhost] => {
"msg": {
"test": [
{
"UserData": "test",
"password": "123"
}
],
"test2": [
{
"UserData": "test2",
"password": "123"
}
],
"test3": [
{
"UserData": "test3",
"password": "123"
}
]
}
}

Ansible: Error reading JSON: Invalid data passed to 'loop', it requires a list, got this instead

Here is my JSON
[
{
"?xml": {
"attributes": {
"encoding": "UTF_8",
"version": "1.0"
}
}
},
{
"jdbc_data_source": [
{
"attributes": {
"xmlns": "http://xmlns.oracle.com/weblogic/jdbc_data_source"
}
},
{
"name": "canwebds"
},
{
"jdbc_driver_params": [
{
"url": "jdbc:oracle:thin:#//myhost.mrshmc.com:1521/OLTT206"
},
{
"driver_name": "oracle.jdbc.OracleDriver"
},
{
"properties": {
"property": [
{
"name": "user"
},
{
"value": "WEB_USER"
}
]
}
},
{
"password_encrypted": "{AES}BcqmURyYoCkLvC5MmREXsfpRMO93KPIubqUAbb95+nE="
}
]
},
{
"jdbc_connection_pool_params": [
{
"initial_capacity": "1"
},
{
"statement_cache_type": "LRU"
}
]
},
{
"jdbc_data_source_params": {
"jndi_name": "canwebds"
}
}
]
},
{
"?xml": {
"attributes": {
"encoding": "UTF_8",
"version": "1.0"
}
}
},
{
"jdbc_data_source": [
{
"attributes": {
"xmlns": "http://xmlns.oracle.com/weblogic/jdbc_data_source"
}
},
{
"name": "dsARSVelocity"
},
{
"jdbc_driver_params": [
{
"url": "jdbc:oracle:thin:#myhost:1521:DB01"
},
{
"driver_name": "oracle.jdbc.OracleDriver"
},
{
"properties": {
"property": [
{
"name": "user"
},
{
"value": "AP05"
}
]
}
},
{
"password_encrypted": "{AES}wP5Se+OQdR21hKiC2fDw1WPEaTMU5Sc17Ax0+rmjmPI="
}
]
},
{
"jdbc_connection_pool_params": [
{
"initial_capacity": "1"
},
{
"statement_cache_type": "LRU"
}
]
},
{
"jdbc_data_source_params": [
{
"jndi_name": "dsARSVel"
},
{
"global_transactions_protocol": "OnePhaseCommit"
}
]
}
]
}
]
I need to print the below for any jdbc_data_source found
expected output:
jdbc_data_source name is has username and jndi name <jndi_name>
which will translate as:
jdbc_data_source name is cwds has username CAN_USER and jndi name cwdsjndi
Below is something i tried but it does not work:
- name: create YML for server name with DB
debug:
msg: "{{ dsname.0.name }} has jndi {{ dsurl[0]['jdbc_driver_params'][2]['properties][0]['property'][1]['value'] }}"
loop: "{{ jsondata[1] }}"
vars:
dsname: "{{ item.jdbc_data_source| selectattr('name', 'defined') | list }}"
dsurl: "{{ item.jdbc_data_source| selectattr('jdbc_driver_params', 'defined') | list }}"
However, it does not get me the desired output. Below is the error i get:
fatal: [localhost]: FAILED! => {"msg": "Invalid data passed to 'loop',
it requires a list, got this instead: {'jdbc_data_source':
[{'attributes': {'xmlns':
'http://xmlns.oracle.com/weblogic/jdbc_data_source', 'xml
If I loop loop: "{{ jsondata }}", then it works but the desired values still do not get printed.
Kindly suggest.
this playbook does the job:
- hosts: localhost
gather_facts: no
vars:
json: "{{ lookup('file', 'file.json') | from_json }}"
tasks:
- name: display datas
debug:
msg: "jdbc_data_source name is {{ name }} has username: {{ user }} and jndi name: {{ jndiname }}"
loop: "{{ json }}"
when: item.jdbc_data_source is defined
vars:
datasource1: "{{ item.jdbc_data_source | selectattr('jdbc_driver_params', 'defined') }}"
properties: "{{ (datasource1.0.jdbc_driver_params | selectattr('properties', 'defined')).0.properties }}"
name: "{{ (item.jdbc_data_source | selectattr('jdbc_data_source_params', 'defined')).0.jdbc_data_source_params.jndi_name }}"
user: "{{ (properties.property | selectattr('value', 'defined')).0.value }}"
jndiname: "{{ (item.jdbc_data_source | selectattr('name', 'defined') ).0.name}}"
result:
skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}})
ok: [localhost] => "msg": "jdbc_data_source name is cwds has username: CAN_USER and jndi name: cwdsjndi"
}
skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}})
ok: [localhost] => "msg": "jdbc_data_source name is dsvelcw has username: WEB_USER and jndi name: dsvelcw"
if you have a mixed of list and dictionary, change:
vars:
datasource1: "{{ item.jdbc_data_source | selectattr('jdbc_driver_params', 'defined') }}"
properties: "{{ (datasource1.0.jdbc_driver_params | selectattr('properties', 'defined')).0.properties }}"
params: "{{ (item.jdbc_data_source | selectattr('jdbc_data_source_params', 'defined')).0.jdbc_data_source_params }}"
name: "{{ params.jndi_name if params is mapping else (params | selectattr('jndi_name', 'defined')).0.jndi_name }}"
user: "{{ (properties.property | selectattr('value', 'defined')).0.value }}"
jndiname: "{{ (item.jdbc_data_source | selectattr('name', 'defined') ).0.name}}"
i test if its a dict else its a list..
result with new json:
skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}})
"msg": "jdbc_data_source name is canwebds has username: WEB_USER and jndi name: canwebds"
}
skipping: [localhost] => (item={'?xml': {'attributes': {'encoding': 'UTF_8', 'version': '1.0'}}})
"msg": "jdbc_data_source name is dsARSVel has username: AP05 and jndi name: dsARSVelocity"
}
some explanations:
(item.jdbc_data_source | selectattr('jdbc_data_source_params', 'defined')) selectattr create a list with all keys jdbc_data_source_params present in the list jdbc_data_source, here there are only one key jdbc_data_source_params, so the 0.jdbc_data_source_params selects the first key.
then we check if params is a dict else its a list
if you want to understand, i suggest you to decompose in lot of actions and display with debug the result.
FYI selectattr equivalent with json_query
selectattr('something', 'defined') = json_query("[?something]")

Rebuilt a list of dictionary

I can't rebuild a list of dictionary in Ansible code as below:
Input:
{
"list_report": [
{
"objectid": "8502d7d99a435532",
"value": "10.70.108.15,10.70.106.10,10.72.106.167"
}
]
}
To have in output:
{
"list_report": [
{
"objectid": "8502d7d99a435532",
"value": [10.70.108.15,10.70.106.10,10.72.106.167]
}
]
}
So, I need to keep item: objectid as it is, but, I want to change item: value from string to a list of IPs. I tried a lot of solution but nothing seems to work.
Can someone give me a clue?
Given that you did not over simplified your example input, you could just split the value using Python's split method.
Given the playbook:
- hosts: localhost
gather_facts: no
vars:
list_report:
- objectid: "8502d7d99a435532"
value: "10.70.108.15,10.70.106.10,10.72.106.167"
tasks:
- set_fact:
list_report: "{{
[{
'objectid': list_report.0.objectid,
'value': list_report.0.value.split(',')
}]
}}"
- debug:
var: list_report
This yields:
PLAY [localhost] *************************************************************************************************
TASK [set_fact] **************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"list_report": [
{
"objectid": "8502d7d99a435532",
"value": [
"10.70.108.15",
"10.70.106.10",
"10.72.106.167"
]
}
]
}
PLAY RECAP *******************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now, if you have more than one item in this array, you can resort to a temporary variable.
Given the playbook:
- hosts: localhost
gather_facts: no
vars:
list_report:
- objectid: "8502d7d99a435532"
value: "10.70.108.15,10.70.106.10,10.72.106.167"
- objectid: "8502d7d99a435533"
value: "10.70.108.16,10.70.106.11,10.72.106.168"
tasks:
- set_fact:
_list_report: "{{
_list_report | default([]) +
[{
'objectid': item.objectid,
'value': item.value.split(',')
}]
}}"
loop: "{{ list_report }}"
loop_control:
label: "{{ item.objectid }}"
- set_fact:
list_report: "{{ _list_report }}"
- debug:
var: list_report
This yields the reacp:
PLAY [localhost] *************************************************************************************************
TASK [set_fact] **************************************************************************************************
ok: [localhost] => (item=8502d7d99a435532)
ok: [localhost] => (item=8502d7d99a435533)
TASK [set_fact] **************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************
ok: [localhost] =>
list_report:
- objectid: 8502d7d99a435532
value:
- 10.70.108.15
- 10.70.106.10
- 10.72.106.167
- objectid: 8502d7d99a435533
value:
- 10.70.108.16
- 10.70.106.11
- 10.72.106.168
PLAY RECAP *******************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
For example, given the list
list_report:
- objectid: "8502d7d99a435532"
value: "10.70.108.15,10.70.106.10,10.72.106.167"
- objectid: "8502d7d99a435533"
value: "10.70.108.16,10.70.106.11,10.72.106.168"
the task below does the job
- set_fact:
list_report: "{{ dict(_objs|zip(_vals))|
dict2items(key_name='objectid', value_name='value') }}"
vars:
_objs: "{{ list_report|map(attribute='objectid')|list }}"
_vals: "{{ list_report|map(attribute='value')|map('split', ',')|list }}"
gives
list_report:
- objectid: 8502d7d99a435532
value:
- 10.70.108.15
- 10.70.106.10
- 10.72.106.167
- objectid: 8502d7d99a435533
value:
- 10.70.108.16
- 10.70.106.11
- 10.72.106.168

loop over multiple list of dictionaries using ansible

I am trying to loop over a following variable structure using ansible and what I want is to loop over each subnet.
For subnets-1 I want to get network, cidr, vlan
similarly loop other keys subnets-2, subnets-3
flavor.json
{
"subnets-1": [
{
"network": "test1",
"cidr": 21,
"vlan": 123
},
{
"network": "test2",
"cidr": 22,
"vlan": 234
}
],
"subnets-2": [
{
"network": "test3",
"cidr": 43,
"vlan": 879
},
{
"network": "test4",
"cidr": 21,
"vlan": "12fsd"
},
{
"network": "test5",
"cidr": "22sdf",
"vlan": "234sdfd"
}
],
"subnets-3": [
{
"network": "test44",
"cidr": "fg",
"vlan": "dsfsd"
}
]
}
I have tried something like below using with_dict
playbook.yml (ansible 2.9.7)
---
- hosts: local
gather_facts: no
tasks:
- name: lookup yaml
set_fact:
flavors: "{{ lookup('file','flavor.json') | from_json }}"
- name: json out
debug:
msg: "{{ flavors }}"
- name: check keys
shell: |
echo "s_name = {{ item.network }}"
echo "s_cidr = {{ item.cidr }}"
echo "s_vlan = {{ item.vlan }}"
with_dict:
"{{ flavors }}"
This is the error I get when playbook was executed
Error:
fatal: [192.168.110.128]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'network'\n\nThe error appears to be in '/root/fla.yml': line 19, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: check keys\n ^ here\n"
}
Use include_vars. It's simpler compared to lookup and from_yaml, e.g.
- include_vars:
file: flavor.json
name: flavors
gives
flavors:
subnets-1:
- {cidr: 21, network: test1, vlan: 123}
- {cidr: 22, network: test2, vlan: 234}
subnets-2:
- {cidr: 43, network: test3, vlan: 879}
- {cidr: 21, network: test4, vlan: 12fsd}
- {cidr: 22sdf, network: test5, vlan: 234sdfd}
subnets-3:
- {cidr: fg, network: test44, vlan: dsfsd}
Then convert the dictionary to list and iterate with_subelements, e.g.
- debug:
msg: "{{ item.0.key }}
{{ item.1.network }}
{{ item.1.cidr }}
{{ item.1.vlan }}"
with_subelements:
- "{{ flavors|dict2items }}"
- value
gives
msg: subnets-1 test1 21 123
msg: subnets-1 test2 22 234
msg: subnets-2 test3 43 879
msg: subnets-2 test4 21 12fsd
msg: subnets-2 test5 22sdf 234sdfd
msg: subnets-3 test44 fg dsfsd
Ansible does not do nested loops well. In this case, you know you have a maximum of three array elements. So you can loop like this:
- name: loop over flavors
debug:
msg: "{{ flavors[item.0][item.1] }}"
with_nested:
- "{{ flavors }}"
- [ 0, 1, 2 ]
when: flavors[item.0][item.1] is defined
Which results in something like this:
TASK [loop over flavors] *********************************************************************************************
ok: [localhost] => (item=['subnets-1', 0]) => {
"msg": {
"cidr": 21,
"network": "test1",
"vlan": 123
}
}
ok: [localhost] => (item=['subnets-1', 1]) => {
"msg": {
"cidr": 22,
"network": "test2",
"vlan": 234
}
}
skipping: [localhost] => (item=['subnets-1', 2])
ok: [localhost] => (item=['subnets-2', 0]) => {
"msg": {
"cidr": 43,
"network": "test3",
"vlan": 879
}
}
ok: [localhost] => (item=['subnets-2', 1]) => {
"msg": {
"cidr": 21,
"network": "test4",
"vlan": "12fsd"
}
}
ok: [localhost] => (item=['subnets-2', 2]) => {
"msg": {
"cidr": "22sdf",
"network": "test5",
"vlan": "234sdfd"
}
}
ok: [localhost] => (item=['subnets-3', 0]) => {
"msg": {
"cidr": "fg",
"network": "test44",
"vlan": "dsfsd"
}
}
skipping: [localhost] => (item=['subnets-3', 1])
skipping: [localhost] => (item=['subnets-3', 2])