I am using Ansible's vmware_cluster_info to give the following json. I need to be able to count the number of hosts in a cluster. Cluster names will change.
{
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"clusters": {
"Cluster1": {
"drs_default_vm_behavior": "fullyAutomated",
"ha_restart_priority": [
"medium"
],
"ha_vm_failure_interval": [
50
],
"ha_vm_max_failure_window": [
0
],
"ha_vm_max_failures": [
10
],
"ha_vm_min_up_time": [
90
],
"ha_vm_monitoring": "vmMonitoringOnly",
"ha_vm_tools_monitoring": [
"vmAndAppMonitoring"
],
"hosts": [
{
"folder": "/Datacenter/host/Cluster1",
"name": "host1"
},
{
"folder": "/Datacenter/host/Cluster1",
"name": "host2"
},
{
"folder": "/Datacenter/host/Cluster1",
"name": "host3"
},
{
"folder": "/Datacenter/host/Cluster1",
"name": "host4"
}
],
"resource_summary": {
"cpuCapacityMHz": 144000,
},
"tags": [],
"vsan_auto_claim_storage": false
}
},
"invocation": {
"module_args": {
"cluster_name": "Cluster1",
}
}
}
I have tried:
- debug:
msg: "{{ cluster_info.clusters.[].hosts.name | length }}"
- debug:
msg: "{{ cluster_info.clusters.*.hosts.name | length }}"
Both give me template error while templating string... Also, any suggestions on tutorials, etc to learn how to parse json would be appreciated. It seems to be a difficult topic for me. Any ideas?
one similar approach to what you were trying is by using json_query. I understand you want to print the count of hosts, so the .name is not needed in your code:
- name: print count of hosts
debug:
var: cluster_info.clusters | json_query('*.hosts') | first | length
json_query returns a list, this is why we pass it to first before length.
Related
How can I merge 2 json arrays based on the same keys and add a 3rd item from the input json pog_id in the output json file? I have tried with the code mentioned below that is creating 2 different arrays inside a key in json and not merging the values inside a same array.
mergedobject.json
[
{
"name": "ALL_DMZ",
"objectIds": [
"29570",
"29571"
],
"orgid": "777777",
"pog_id": "333333"
},
{
"name": "ALL_DMZ",
"objectIds": [
"729548",
"729549",
"295568"
],
"orgid": "777777",
"pog_id": "333333"
}
]
Playbook
- set_fact:
output: "{{ output|d([]) + [{'orgid': item.0,
'objectIds': item.1|
map(attribute='objectIds')|
list}] }}"
loop: "{{ mergedobject|groupby('name') }}"
Current Output
[
{
"name": "ALL_DMZ",
"objectIds": [
[ "29570",
"29571"
],
[
"729548",
"729549",
"295568"
]
]
"orgid": "777777"
}
]
Expected Output
[
{
"name": "ALL_DMZ",
"objectIds": [
"29570",
"29571",
"729548",
"729549",
"295568"
]
"orgid": "777777",
"pog_id": "333333"
}
]
Given the data
mergedobject:
- name: ALL_DMZ
objectIds: ['29570', '29571']
orgid: '777777'
pog_id: '333333'
- name: ALL_DMZ
objectIds: ['729548', '729549', '295568']
orgid: '777777'
pog_id: '333333'
combine the items in the list. Append the lists in attributes
output: "{{ mergedobject|combine(list_merge='append') }}"
gives
output:
name: ALL_DMZ
objectIds: ['29570', '29571', '729548', '729549', '295568']
orgid: '777777'
pog_id: '333333'
You can put the result into a list
output: "[{{ mergedobject|combine(list_merge='append') }}]"
gives what you want
output:
- name: ALL_DMZ
objectIds: ['29570', '29571', '729548', '729549', '295568']
orgid: '777777'
pog_id: '333333'
Below is my JSON file:
[
{
"?xml": {
"attributes": {
"encoding": "UTF-8",
"version": "1.0"
}
}
},
{
"domain": [
{
"name": "mydom"
},
{
"domain-version": "12.2.1.3.0"
},
{
"server": [
{
"name": "AdminServer"
},
{
"ssl": {
"name": "AdminServer"
}
},
{
"listen-port": "12400"
},
{
"listen-address": "mydom.host1.bank.com"
}
]
},
{
"server": [
{
"name": "myserv1"
},
{
"ssl": [
{
"name": "myserv1"
},
{
"login-timeout-millis": "25000"
}
]
},
{
"log": [
{
"name": "myserv1"
},
{
"file-name": "/web/bea_logs/domains/mydom/myserv1/myserv1.log"
}
]
}
]
},
{
"server": [
{
"name": "myserv2"
},
{
"ssl": {
"name": "myserv2"
}
},
{
"reverse-dns-allowed": "false"
},
{
"log": [
{
"name": "myserv2"
},
{
"file-name": "/web/bea_logs/domains/mydom/myserv2/myserv2.log"
}
]
}
]
}
]
}
]
I need to get log list's name and file-name like below using ansible code.
myserv1_log: "/web/bea_logs/domains/mydom/myserv1/myserv1.log"
myserv2_log: "/web/bea_logs/domains/mydom/myserv2/myserv2.log"
There are two challenges that i m facing.
server may not always be the 3rd key of domain array.
log array may not alway be a key for all server arrays and thus should not be printed. For example. server name AdminServer does not have any log list while myserv1 & myserv2 do have.
I need an ansible code to print the desired for the dynamically changing json.
Note: server will always be a key in the domain array
I'm posting with reference to my similar query here: unable to ideally parse a json file in ansible
Kindly suggest.
you just test if both keys exist:
- hosts: localhost
gather_facts: no
vars:
json: "{{ lookup('file', './file.json') | from_json }}"
tasks:
- name: display
debug:
msg: "name: {{ servername }} --> filename: {{ filename }}"
loop: "{{ json[1].domain }}"
vars:
servername: "{{ item.server.0.name }}_log"
filename: "{{ item['server'][2]['log'][1]['file-name'] }}"
when: item.server is defined and item.server.2.log is defined
result:
TASK [display]
skipping: [localhost] => (item={'name': 'USWL1212MRSHM01'})
skipping: [localhost] => (item={'domain-version': '12.2.1.3.0'})
skipping: [localhost] => (item={'server': [{'name': 'AdminServer'}, {'ssl': {'name': 'AdminServer'}}, {'listen-port': '12400'}, {'listen-address': 'myhost1'}]})
ok: [localhost] => (item={'server': [{'name': 'myserv1'}, {'ssl': {'name': 'myserv1'}}, {'log': [{'name': 'myserv1'}, {'file-name': '/web/bea_logs/domains/mydom/myserv1/myserv1.log'}]}]}) => {
"msg": "name: myserv1_log --> filename: /web/bea_logs/domains/mydom/myserv1/myserv1.log"
}
ok: [localhost] => (item={'server': [{'name': 'myserv2'}, {'ssl': {'name': 'myserv2'}}, {'log': [{'name': 'myserv2'}, {'file-name': '/web/bea_logs/domains/mydom/myserv2/myserv2.log'}]}]}) => {
"msg": "name: myserv2_log --> filename: /web/bea_logs/domains/mydom/myserv2/myserv2.log"
}
As you can see, when the condition is not true, the action is skipped...
you could simplify by testing only key log, because in your case, keylog is always linked to key server
when: item.server.2.log is defined
can somebody please help me with this json parse?
I have this json
{
"declaration": {
"ACS-AS3": {
"ACS": {
"class": "Application",
"vs_ubuntu_22": {
"virtualAddresses": ["10.11.205.167"]
},
"pool_ubuntu_22": {
"members": {
"addressDiscovery": "static",
"servicePort": 22
}
},
"vs_ubuntu_443": {
"virtualAddresses": ["10.11.205.167"],
"virtualPort": 443
},
"pool_ubuntu01_443": {
"members": [{
"addressDiscovery": "static",
"servicePort": 443,
"serverAddresses": [
"10.11.205.133",
"10.11.205.165"
]
}]
},
"vs_ubuntu_80": {
"virtualAddresses": [
"10.11.205.167"
],
"virtualPort": 80
},
"pool_ubuntu01_80": {
"members": [{
"addressDiscovery": "static",
"servicePort": 80,
"serverAddresses": [
"10.11.205.133",
"10.11.205.165"
],
"shareNodes": true
}],
"monitors": [{
"bigip": "/Common/tcp"
}]
}
}
}
}
}
and I am trying this playbook
tasks:
- name : deploy json file AS3 to F5
debug:
msg: "{{ lookup('file', 'parse2.json') }}"
register: atc_AS3_status
no_log: true
- name : Parse json 1
debug:
var: atc_AS3_status.msg.declaration | json_query(query_result) | list
vars:
query_result: "\"ACS-AS3\".ACS"
#query_result1: "\"ACS-AS3\".ACS.*.virtualAddresses"
register: atc_AS3_status1
I got this response
TASK [Parse json 1] ******************************************************************************************************************************************************************************************
ok: [avx-bigip01.dhl.com] => {
"atc_AS3_status1": {
"atc_AS3_status.msg.declaration | json_query(query_result) | list": [
"class",
"vs_ubuntu_22",
"pool_ubuntu_22",
"vs_ubuntu_443",
"pool_ubuntu01_443",
"vs_ubuntu_80",
"pool_ubuntu01_80"
],
"changed": false,
"failed": false
}
}
but I would like to print just key which has inside key virtualAddresses
if ""ACS-AS3".ACS.*.virtualAddresses" is defined the print the key .
the result should be
vs_ubuntu_22
vs_ubuntu_443
vs_ubuntu_80
One way to get the keys of a dict, is to use the dict2items filter. This will give vs_ubuntu_22 etc. as "key" and their sub-dicts as "value". Using this we can conditionally check if virtualAddresses is defined in values.
Also parse2.json can be included as vars_file or with include_vars rather than having a task to debug and register the result.
Below task using vars_file in playbook should get you the intended keys from the JSON:
vars_files:
- parse2.json
tasks:
- name: show atc_status
debug:
var: item.key
loop: "{{ declaration['ACS-AS3']['ACS'] | dict2items }}"
when: item['value']['virtualAddresses'] is defined
Basically, I need to read a value from a JSON output and use it in subsequent tasks. So, I tried with_items, loop, but nothing worked.
Sample JSON that I generated from a registered variable:
TASK [local_volume_mount : debug Info from device that is parted] **************
Monday 29 March 2021 21:33:39 +0000 (0:00:02.271) 0:00:02.417 **********
ok: [node1] => {
"partitioned_device_live_info": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"disk": {
"dev": "/dev/nvme2n1",
"logical_block": 512,
"model": "SAMSUNG MZQLW960HMJP-00003",
"physical_block": 512,
"size": 937692504.0,
"table": "msdos",
"unit": "kib"
},
"failed": false,
"invocation": {
"module_args": {
"align": "optimal",
"device": "/dev/nvme2n1",
"flags": null,
"label": "msdos",
"name": null,
"number": 1,
"part_end": "100%",
"part_start": "0%",
"part_type": "primary",
"state": "present",
"unit": "KiB"
}
},
"item": [
{
"device": "/dev/nvme2n1",
"partitions": [
{
"end": "100%",
"number": 1,
"start": "0%",
"storage_class": "ssd-wkr-services"
}
]
},
{
"end": "100%",
"number": 1,
"start": "0%",
"storage_class": "ssd-wkr-services"
}
],
"partitions": [
{
"begin": 1024.0,
"end": 937692160.0,
"flags": [],
"fstype": "",
"name": "",
"num": 1,
"size": 937691136.0,
"unit": "kib"
}
],
"script": ""
}
]
}
}
So, from the above I need to read the partitions.num value and use it in the next task, but, I don't know how to do that.
Task:
- name: THIS IS BEING TESTED
debug:
var: "{{ item.partitions }}"
ignore_errors: no
#loop: "{{ partitioned_device_live_info.results }}"
with_items: "{{ partitioned_device_live_info.results }}"
Output of the above task is
'dict object' has no attribute 'partitions'
I want to store that value item.partitions.num in a variable and then use it in further tasks.
Figured out using msg, below is my change
- name: THIS IS BEING TESTED with MSG and with_items
debug:
msg: "{{ item.partitions[0].num }}"
with_items: "{{ partitioned_device_live_info.results }}"
You can always use regex in ansible to set facts. More details stackoverflow
Following are some use cases
- name: Long form task does not
ansible.builtin.replace:
path: /etc/hosts
regexp: '\b(localhost)(\d*)\b'
replace: '\1\2.localdomain\2 \1\2'
- name: Explicitly specifying positional matched groups in replacement
ansible.builtin.replace:
path: /etc/ssh/sshd_config
regexp: '^(ListenAddress[ ]+)[^\n]+$'
replace: '\g<1>0.0.0.0'
Setting fact
- name: set version in file after replacement
set_fact:
version_in_file: "{{ version_deployment_file | regex_search(docker_image_version) }}"
More details - ansible.builtin.replace
I use elb_application_lb_info module to get info about my application load balancer. Here is the code I am using for it:
- name: Test playbook
hosts: tag_elastic_role_logstash
vars:
aws_access_key: AKIARWXXVHXJS5BOIQ6P
aws_secret_key: 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
tasks:
- name: Gather information about all ELBs
elb_application_lb_info:
aws_access_key: AKIXXXXXXXXXXXXXXXXXXX
aws_secret_key: gG6a586XXXXXXXXXXXXXXXXXX
region: ap-southeast-2
names:
- LoadBalancer
register: albinfo
- debug:
msg: "{{ albinfo }}"
This is working fine and I got the following output:
"load_balancers": [
{
"idle_timeout_timeout_seconds": "60",
"routing_http2_enabled": "true",
"created_time": "2021-01-26T23:58:27.890000+00:00",
"access_logs_s3_prefix": "",
"security_groups": [
"sg-094c894246db1bd92"
],
"waf_fail_open_enabled": "false",
"availability_zones": [
{
"subnet_id": "subnet-0195c9c0df024d221",
"zone_name": "ap-southeast-2b",
"load_balancer_addresses": []
},
{
"subnet_id": "subnet-071060fde585476e0",
"zone_name": "ap-southeast-2c",
"load_balancer_addresses": []
},
{
"subnet_id": "subnet-0d5f856afab8f0eec",
"zone_name": "ap-southeast-2a",
"load_balancer_addresses": []
}
],
"access_logs_s3_bucket": "",
"deletion_protection_enabled": "false",
"load_balancer_name": "LoadBalancer",
"state": {
"code": "active"
},
"scheme": "internet-facing",
"type": "application",
"load_balancer_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:loadbalancer/app/LoadBalancer/27cfc970d48501fd",
"access_logs_s3_enabled": "false",
"tags": {
"Name": "loadbalancer_test",
"srg:function": "Storage",
"srg:owner": "ISCloudPlatforms#superretailgroup.com",
"srg:cost-centre": "G110",
"srg:managed-by": "ISCloudPlatforms#superretailgroup.com",
"srg:environment": "TST"
},
"routing_http_desync_mitigation_mode": "defensive",
"canonical_hosted_zone_id": "Z1GM3OXH4ZPM65",
"dns_name": "LoadBalancer-203283612.ap-southeast-2.elb.amazonaws.com",
"ip_address_type": "ipv4",
"listeners": [
{
"default_actions": [
{
"target_group_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:targetgroup/test-ALBID-W04X8DBT450Q/c999ac1cda7b1d4a",
"type": "forward",
"forward_config": {
"target_group_stickiness_config": {
"enabled": false
},
"target_groups": [
{
"target_group_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:targetgroup/test-ALBID-W04X8DBT450Q/c999ac1cda7b1d4a",
"weight": 1
}
]
}
}
],
"protocol": "HTTP",
"rules": [
{
"priority": "default",
"is_default": true,
"rule_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:listener-rule/app/LoadBalancer/27cfc970d48501fd/671ad3428c35c834/5b5953a49a886c03",
"conditions": [],
"actions": [
{
"target_group_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:targetgroup/test-ALBID-W04X8DBT450Q/c999ac1cda7b1d4a",
"type": "forward",
"forward_config": {
"target_group_stickiness_config": {
"enabled": false
},
"target_groups": [
{
"target_group_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:targetgroup/test-ALBID-W04X8DBT450Q/c999ac1cda7b1d4a",
"weight": 1
}
]
}
}
]
}
],
"listener_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:listener/app/LoadBalancer/27cfc970d48501fd/671ad3428c35c834",
"load_balancer_arn": "arn:aws:elasticloadbalancing:ap-southeast-2:117557247443:loadbalancer/app/LoadBalancer/27cfc970d48501fd",
"port": 9200
}
],
"vpc_id": "vpc-0016dcdf5abe4fef0",
"routing_http_drop_invalid_header_fields_enabled": "false"
}
]
I need to fetch "dns_name" which is dns name of the load balancer and pass it in another play as a variable.
I tried with json_query but got the error. Here is the code:
- name: save the Json data to a Variable as a Fact
set_fact:
jsondata: "{{ albinfo.stdout | from_json }}"
- name: Get ALB dns name
set_fact:
dns_name: "{{ jsondata | json_query(jmesquery) }}"
vars:
jmesquery: 'load_balancers.dns_name'
- debug:
msg: "{{ dns_name }}"
And here is the error:
"msg": "The task includes an option with an undefined variable. The error was: Unable to look up a name or access an attribute in template string ({{ albinfo.stdout | from_json }}).\nMake sure your variable name does not contain invalid characters like '-': the JSON object must be str, bytes or bytearray
Any idea how to extract "dns_name" from the json above?
Here is the way to get the dns_name from above json output:
- name: Get Application Load Balancer DNS Name
set_fact:
rezultat: "{{ albinfo | json_query('load_balancers[*].dns_name') }}"
- debug:
msg: "{{ rezultat }}"