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
Related
{
"vmware_dc": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"datacenter_info": [
{
"config_status": "gray",
"moid": "datacenter-1146",
"name": "dc-1",
"overall_status": "gray"
}
],
"failed": false,
"invocation": {
"module_args": {
"datacenter": null,
"hostname": "vc-001",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"port": 443,
"properties": null,
"proxy_host": null,
"proxy_port": null,
"schema": "summary",
"show_tag": false,
"username": "",
"validate_certs": false
}
},
"item": "vc-001"
},
{
"ansible_loop_var": "item",
"changed": false,
"datacenter_info": [
{
"config_status": "gray",
"moid": "datacenter-424",
"name": "dc-2",
"overall_status": "gray"
},
{
"config_status": "gray",
"moid": "datacenter-2",
"name": "dc-3",
"overall_status": "gray"
}
],
"failed": false,
"invocation": {
"module_args": {
"datacenter": null,
"hostname": "vc-002",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"port": 443,
"properties": null,
"proxy_host": null,
"proxy_port": null,
"schema": "summary",
"show_tag": false,
"username": "",
"validate_certs": false
}
},
"item": "vc-002"
}
]
}
}
Please see JSON results above. I hope that someone can help me. I should take two fields, an item and datacenter_info.name
I can get values separate. I don't know how to create a dictionary because I have 2 items and 3 datacenter_info.name
enter - name: Set all_items
set_fact:
all_items: "{{ vmware_dc_info.results | flatten | map(attribute='item) | flatten }}"
- name: debug all_items
debug:
var: all_items
{
"all_items": [
"vc-001",
"vc-002"
]
- name: Set all dc
set_fact:
all_dc: "{{ vmware_dc_info.results | flatten | map(attribute='datacenter_info') | flatten | map(attribute='name') | flatten }}"
- name: debug all_dc
debug:
var: all_dc
"all_dc": [
"dc-1",
"dc-2",
"dc-3"
Dictionary should looks like below
vc-001: dc-1
vc-002: dc-2
vc-002: dc-3
Is it possible? What do you think?
Resolved with code below
- name: Set fact
set_fact:
dc_list: "{{ dc_list | default([]) + [{'vcenter': item[0].item, 'dc': item[1].name}] }}"
with_subelements:
- "{{ vmware_dc_info.results }}"
- datacenter_info
loop_control:
label: "{{ item[0].item }}"
- name: Debug
debug:
var: dc_list
For example
- set_fact:
hostnames: "{{ hostnames|d({})|combine({item: names}) }}"
loop: "{{ vmware_dc.results|map(attribute='item')|list }}"
vars:
_item: "{{ vmware_dc.results|selectattr('item', '==', item) }}"
names: "{{ _item.0.datacenter_info|map(attribute='name')|list }}"
gives you the expected result
hostnames:
vc-001:
- dc-1
vc-002:
- dc-2
- dc-3
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.
I have this task:
- name: Get Status page url
uri:
url: "{{ responseversion.json.results[0]._links.self }}?expand=version,body.storage"
method: GET
return_content: yes
body_format: json
force_basic_auth: yes
headers:
Authorization: Basic blablab
register: responseurl
- name: print body without updates
debug: var=responseurl
- debug:
msg: "{{ responseurl | combine({'json':{'body':{ 'version':{'minorEdit':true, 'number':responseversion.json.results[0].version.number+1} }}}) }}"
Basically I get a response, get the page "version" and "minorEdit" keys from response and replace them and their values in the original response. There are other keys besides "version", I need to keep them also, as well as the neighboring keys inside "version" key.
The targeted "version" key before update looks like this:
"version": {
"_expandable": {
"content": "/rest/api/content/164794022"
},
"_links": {
"self": "https://hostname.com/confluence/rest/experimental/content/164794022/version/4"
},
"by": {
"_expandable": {
"status": ""
},
"_links": {
"self": "https://hostname.com/confluence/rest/api/user?key=ff80808164c5cf240164c6e351d30002"
},
"displayName": "my name",
"profilePicture": {
"height": 48,
"isDefault": false,
"path": "/confluence/download/attachments/98107443/user-avatar",
"width": 48
},
"type": "known",
"userKey": "ff80808164c5cf240164c6e351d30002",
"username": "my name"
},
"hidden": false,
"message": "",
"minorEdit": false,
"number": 4,
"when": "2021-08-25T15:36:35.285+03:00"
}
The final debug task print the version key like this:
"json": {
"body": {
"version": {
"minorEdit": true,
"number": 5
}
}
}
You can see the JSON for replacement inline, inside the "combine" function call. Whole JSON key gets overriden.
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 }}"
I need to parse the json response from an API request using ansible and then take action on items returned in the response. Here is part of my ansible playbook -
```- name: Invoke Import API
uri:
url: "{{ atl_bitbucket_dataset_url }}/rest/api/1.0/migration/imports"
user: admin
password: "{{ atl_bitbucket_admin_password }}"
method: POST
follow_redirects: yes
force_basic_auth: yes
creates: "{{ atl_product_home_shared }}/data/migration/import/lock.file"
body: "{ \"archivePath\": \"{{ atl_bitbucket_dataset_url | basename }}\" }"
body_format: json
return_content: yes
register: response
until: response.status == 200
retries: 6
delay: 15
failed_when: response.response.json.state != 'INITIALISING'
- name: get status of import
debug: var=response```
and here is the json response i got on a previous run
```TASK [bitbucket_dataset_restore : get status of import] ************************
ok: [localhost] => {
"response": {
"attempts": 1,
"cache_control": "no-cache, no-transform",
"changed": false,
"connection": "close",
"content": "{\"id\":1,\"initiator\":{\"name\":\"admin\",\"emailAddress\":\"admin#yourcompany.com\",\"id\":1,\"displayName\":\"AdminIstrator\",\"active\":true,\"slug\":\"admin\",\"type\":\"NORMAL\",\"links\":{\"self\":[{\"href\":\"http://bbdc-test-loadbala-t6vnlr2363vl-1404860112.us-west-2.elb.amazonaws.com/users/admin\"}]}},\"nodeId\":\"e72aa995-5016-4b2c-80e8-edba5eda3ab4\",\"progress\":{\"percentage\":0},\"startDate\":1574803309090,\"state\":\"INITIALISING\",\"type\":\"com.atlassian.bitbucket.migration.import\",\"updateDate\":1574803309090}",
"content_type": "application/json;charset=UTF-8",
"cookies": {},
"cookies_string": "",
"date": "Tue, 26 Nov 2019 21:21:49 GMT",
"elapsed": 1,
"failed": false,
"failed_when_result": False,
"json": {
"id": 1,
"initiator": {
"active": True,
"displayName": "AdminIstrator",
"emailAddress": "admin#yourcompany.com",
"id": 1,
"links": {
"self": [
{
"href": "http://bbdc-test-loadbala-t6vnlr2363vl-1404860112.us-west-2.elb.amazonaws.com/users/admin"
}
]
},
"name": "admin",
"slug": "admin",
"type": "NORMAL"
},
"nodeId": "e72aa995-5016-4b2c-80e8-edba5eda3ab4",
"progress": {
"percentage": 0
},
"startDate": 1574803309090,
"state": "INITIALISING",
"type": "com.atlassian.bitbucket.migration.import",
"updateDate": 1574803309090
},
"msg": "OK (unknown bytes)",
"redirected": False,
"status": 200,
"transfer_encoding": "chunked",
"url": "http://localhost:7990/rest/api/1.0/migration/imports",
"vary": "accept-encoding,x-auserid,cookie,x-ausername,accept-encoding",
"warnings": [
"The value True (type bool) in a string field was converted to u'True' (type string). If this does not look like what you expect, quote the entire value to ensure it does not change."
],
"x_arequestid": "#XXAH7Kx1281x1x0",
"x_asen": "SEN-500",
"x_auserid": "1",
"x_ausername": "admin",
"x_content_type_options": "nosniff"
}
}```
I want to retrieve the value "state": "INITIALISING" and the "id": 1. I have tried various methods response.response.json.state or response['response']['json']['state']. And it has not worked. Can anyone help me on how i can retrieve those values so i can use in other tasks further down?
I was able to fix this by adding is defined. So my code looks like this -
failed_when: output is defined and output.json is defined and output.json.state != 'INITIALISING'