Using an attribute from parted output - ansible

I need Ansible to install a server and I must partition for LVM. How can I use the attribute "size" from the second partition of "sda_info.partitions"?
hosts: node2
become: yes
become_method: sudo
become_flags: -H -S
tasks:
- name: Some name
parted:
device: /dev/sda
unit: s
register: sda_info
OUTPUT
TASK [debug] ***************************************************************
ok: [node2] => {
"sda_info.partitions": [
{
"begin": 4096.0,
"end": 51202047.0,
"flags": [
"boot"
],
"fstype": "ext4",
"name": "",
"num": 1,
"size": 51197952.0,
"unit": "s"
},
{
"begin": 51202048.0,
"end": 52248575.0,
"flags": [],
"fstype": "linux-swap(v1)",
"name": "",
"num": 2,
"size": 1046528.0,
"unit": "s"
}
]
}

sda_info.partitions is a list with 2 dictionaries. The 2nd dictionary can be referenced
sda_info.partitions[1]
and the attribute size can be assigned to a variable
- set_fact:
size_2nd_part: "{{ sda_info.partitions[1].size }}"
Very probably the list will be sorted by the number of the partition. If not the variable above won't be the size of the 2nd partition. Use json_query to be sure
- set_fact:
size_2nd_part: "{{ sda_info.partitions|json_query('[?num==`2`].size') }}"

Related

Ansible - Extracting a variable from a large JSON output

The gcp_sql_instance_info module does not seem to allow you to specify the name of the database you are trying to get info for. All it does is every detail of every database in that project.
What would be the best way to extract the "ipAddress" of a database based on it's name and register that value to a variable?
{
"resources": [
{
"kind": "sql#instance",
"state": "RUNNABLE",
"databaseVersion": "POSTGRES_13",
"settings": {
"authorizedGaeApplications": [],
"tier": "db-custom-1-3840",
"kind": "sql#settings",
"availabilityType": "ZONAL",
"pricingPlan": "PER_USE",
"replicationType": "SYNCHRONOUS",
"activationPolicy": "ALWAYS",
"ipConfiguration": {
"privateNetwork": "example-network",
"authorizedNetworks": [],
"ipv4Enabled": false
},
"locationPreference": {
"zone": "us-west1-b",
"kind": "sql#locationPreference"
},
"dataDiskType": "PD_SSD",
"maintenanceWindow": {
"kind": "sql#maintenanceWindow",
"hour": 0,
"day": 0
},
"backupConfiguration": {
"startTime": "02:00",
"kind": "sql#backupConfiguration",
"backupRetentionSettings": {
"retentionUnit": "COUNT",
"retainedBackups": 7
},
"enabled": false,
"replicationLogArchivingEnabled": false,
"pointInTimeRecoveryEnabled": false,
"transactionLogRetentionDays": 7
},
"settingsVersion": "3",
"storageAutoResizeLimit": "0",
"storageAutoResize": true,
"dataDiskSizeGb": "100"
},
"ipAddresses": [
{
"type": "PRIVATE",
"ipAddress": "10.10.10.1"
}
],
"instanceType": "CLOUD_SQL_INSTANCE",
"backendType": "SECOND_GEN",
"name": "database-1",
"region": "us-west1",
"gceZone": "us-west1-b",
"databaseInstalledVersion": "POSTGRES_13_7",
"createTime": "2022-07-22T20:20:32.274Z"
},
{
"kind": "sql#instance",
"state": "RUNNABLE",
"databaseVersion": "MYSQL_8_0",
"settings": {
"authorizedGaeApplications": [],
"tier": "db-n1-standard-1",
"kind": "sql#settings",
"availabilityType": "ZONAL",
"pricingPlan": "PER_USE",
"replicationType": "SYNCHRONOUS",
"activationPolicy": "ALWAYS",
"ipConfiguration": {
"privateNetwork": "example-network",
"authorizedNetworks": [],
"ipv4Enabled": false
},
"locationPreference": {
"zone": "us-west1-c",
"kind": "sql#locationPreference"
},
"dataDiskType": "PD_SSD",
"backupConfiguration": {
"startTime": "21:00",
"kind": "sql#backupConfiguration",
"backupRetentionSettings": {
"retentionUnit": "COUNT",
"retainedBackups": 7
},
"enabled": false,
"transactionLogRetentionDays": 7
},
"settingsVersion": "1",
"storageAutoResizeLimit": "0",
"storageAutoResize": true,
"dataDiskSizeGb": "10"
},
"ipAddresses": [
{
"type": "PRIVATE",
"ipAddress": "10.10.10.2"
}
],
"instanceType": "CLOUD_SQL_INSTANCE",
"backendType": "SECOND_GEN",
"name": "database-2",
"region": "us-west1",
"gceZone": "us-west1-c",
"databaseInstalledVersion": "MYSQL_8_0_26",
"createTime": "2022-07-27T19:27:59.235Z"
}
]
Q: "Extract the ipAddress of a database based on its name."
A: Crete a dictionary. For example, select the first item from the list ipAddresses
name_ip: "{{ dict(resources|json_query(name_ip_query)) }}"
name_ip_query: '[].[name, ipAddresses[0].ipAddress]'
give
name_ip:
database-1: 10.10.10.1
database-2: 10.10.10.2
Notes
Example of a complete playbook for testing
- hosts: localhost
vars_files:
- resources.yml
vars:
name_ip: "{{ dict(resources|json_query(name_ip_query)) }}"
name_ip_query: '[].[name, ipAddresses[0].ipAddress]'
name_ver: "{{ dict(resources|json_query(name_ver_query)) }}"
name_ver_query: '[].[name, databaseVersion]'
ip_of_db1: "{{ name_ip['database-1'] }}"
tasks:
- debug:
var: name_ip
- debug:
var: name_ver
- debug:
var: ip_of_db1
gives
TASK [debug] *********************************************************************************
ok: [localhost] =>
name_ip:
database-1: 10.10.10.1
database-2: 10.10.10.2
TASK [debug] *********************************************************************************
ok: [localhost] =>
name_ver:
database-1: POSTGRES_13
database-2: MYSQL_8_0
TASK [debug] *********************************************************************************
ok: [localhost] =>
ip_of_db1: 10.10.10.1
There might be more IP addresses. The variable ipAddresses is a list. An option would be to create a list of all IP addresses. For example,
name_ips: "{{ dict(resources|json_query(name_ips_query)) }}"
name_ips_query: '[].[name, ipAddresses[].ipAddress]'
give
name_ips:
database-1: [10.10.10.1]
database-2: [10.10.10.2]
This is a working playbook to achieve the desired output:
- hosts: localhost
vars:
db_instance_name: instance-1
tasks:
- name: get info on an instance
gcp_sql_instance_info:
project: project_id
auth_kind: serviceaccount
service_account_file: "/path/to/sa/example.json"
register: cloud_sql_resources
- set_fact:
query: "resources[?contains(name,'{{ db_instance_name }}')].ipAddresses[].ipAddress"
- set_fact:
ipAddress: "{{ cloud_sql_resources | to_json | from_json | json_query(query) }}"
- debug:
msg: "{{ ipAddress }}"
You can run your playbook like this: ansible-playbook -v main.yaml --extra-vars "db_instance_name=instance-1", that would print:
TASK [debug] ********************************************************************************************************************
ok: [localhost] => {
"msg": [
"35.224.46.127"
]
}

Parse / Loop ansible registered variable

Trying to figure out how to filter out the list of UserNames in the output of the following playbook.
- name: Get all users
ome_user_info:
hostname: "{{ dellome_hostname }}"
username: "{{ dellome_username }}"
password: "{{ dellome_password }}"
register: users
Now the output provides the following:
ok: [192.168.1.100] => {
"users": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"deprecations": [
{
"msg": "Distribution Ubuntu 18.04 on host 192.168.1.100 should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python for this host. See https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information",
"version": "2.12"
}
],
"failed": false,
"user_info": {
"192.168.1.100": {
"#odata.context": "/api/$metadata#Collection(AccountService.Account)",
"#odata.count": 3,
"value": [
{
"#odata.id": "/api/AccountService/Accounts('10066')",
"#odata.type": "#AccountService.Account",
"Description": "admin",
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10066",
"IsBuiltin": true,
"Locked": false,
"Name": "admin",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10066')/Permissions",
"RoleId": "10",
"UserName": "admin",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10102')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10102",
"IsBuiltin": false,
"Locked": false,
"Name": "dell",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10102')/Permissions",
"RoleId": "10",
"UserName": "dell",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10233')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 10232,
"Enabled": true,
"Id": "10233",
"IsBuiltin": false,
"Locked": false,
"Name": "Domain Users",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10233')/Permissions",
"RoleId": "10",
"UserName": "Domain Users",
"UserTypeId": 2
}
]
}
}
}
}
I was able to determine the following from the output of the registered variable users.
- debug:
var: "{{ users | length }}"
This provides me the length of 5. Which makes sense to an extent. If I start poking into the output I can then determine the following:
- debug:
var: "{{ users.user_info | length }}"
This shows me the length of 1 which makes sense. If I add the var using users.user_info I can then see the output below.
TASK [manage_users : debug] *************************************************************************************************************************************************************
ok: [192.168.1.100] => {
"users.user_info": {
"192.168.1.100": {
"#odata.context": "/api/$metadata#Collection(AccountService.Account)",
"#odata.count": 3,
"value": [
{
"#odata.id": "/api/AccountService/Accounts('10066')",
"#odata.type": "#AccountService.Account",
"Description": "admin",
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10066",
"IsBuiltin": true,
"Locked": false,
"Name": "admin",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10066')/Permissions",
"RoleId": "10",
"UserName": "admin",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10102')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 0,
"Enabled": true,
"Id": "10102",
"IsBuiltin": false,
"Locked": false,
"Name": "dell",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10102')/Permissions",
"RoleId": "10",
"UserName": "dell",
"UserTypeId": 1
},
{
"#odata.id": "/api/AccountService/Accounts('10233')",
"#odata.type": "#AccountService.Account",
"Description": null,
"DirectoryServiceId": 10232,
"Enabled": true,
"Id": "10233",
"IsBuiltin": false,
"Locked": false,
"Name": "Domain Users",
"Password": null,
"Permissions#odata.navigationLink": "/api/AccountService/Accounts('10233')/Permissions",
"RoleId": "10",
"UserName": "Domain Users”,
"UserTypeId": 2
}
]
}
}
}
Trying to figure out how I can loop through and get an array of the following: value —> UserName. Essentially I am going to take the following value and loop through and delete users that don’t equal the following. Admin, dell, domain users.
Now one might say you would easily just say while not = to items - then that list would work - I first need to figure out how to search and get the values out. I have tried the following:
- debug:
var: users.user_info().value()
- debug:
var: users.user_info.find('UserName')
- debug:
msg: UserName
loop: users.user_info."192.168.1.100".value
#- debug:
# var: users.user_info."{{ dellome_hostname }}".UserName
#- debug:
# var: "(claims1 | from_json).value"
# msg: "{{ users.user_info.UserName | list }}"
# (output_text.stdout | from_json).ismaster
#- debug:
# msg: "{{ item }}"
#loop: "{{ users.user_info | from_json | list }}"
At the end of this once I understand how to get the data out i can then create a loop to execute the following:
---
- name: Delete a User in Dell OME
ome_user:
hostname: "{{ dellome_hostname }}"
username: "{{ dellome_username }}"
password: "{{ dellome_password }}"
state: "{{ requestedState }}"
name: "{{ requstedUserName }}"
This is where i can then add the loop to eliminate users that don't meet the list of names i provide. Any help would be greatly appreciated.
Here are some of the errors i have run into.
TASK [manage_users : debug] *************************************************************************************************************************************************************
fatal: [192.168.1.100]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{users.user_info().value()}}): 'dict' object is not callable"}
TASK [manage_users : debug] *************************************************************************************************************************************************************
fatal: [192.168.1.100]: FAILED! => {"msg": "template error while templating string: expected name or number. String: {{users.user_info.\"192.168.1.100\".value()}}"}
Or as i am looking at this - if i can figure out a way to create a loop that looks for the roleID and when it is not equal to 10 then delete the user.
Use json_query. The tasks below
- set_fact:
users_rm: "{{ users.user_info|
json_query('*.value[].UserName') }}"
- debug:
var: users_rm
give
users_rm:
- admin
- dell
- Domain Users
You are running the query at the host 192.168.1.100 and the dictionary users comprises the users from this single host only. If there are more hosts in the dictionary the asterisk '*' in the query above would select them all. It would be better to select users for the particular host the query is running at. For example, the task below gives the same result
- set_fact:
users_rm: "{{ users.user_info[inventory_hostname].value|
map(attribute='UserName')|
list }}"

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 }}"

How to access duplicate values in a JSON array and set each to a unique variable in ansible?

I am running an ansible playbook which outputs my information in JSON. This array has a loop that has multiple identical keys with different values. Please see below, I am interested in the "intf_id":
"stdout": [
{
"TABLE_cdp_neighbor_brief_info": {
"ROW_cdp_neighbor_brief_info": [
{
"capability": [
"switch",
"IGMP_cnd_filtering"
],
"device_id": "osw1J15",
"ifindex": "83886080",
"intf_id": "mgmt0",
"platform_id": "cisco WS-C2960X-48TS-LL",
"port_id": "GigabitEthernet0/45",
"ttl": "160"
},
{
"capability": [
"router",
"switch",
"Supports-STP-Dispute"
],
"device_id": "spine01",
"ifindex": "436232192",
"intf_id": "Ethernet1/49",
"platform_id": "N9K-C9508",
"port_id": "Ethernet1/11",
"ttl": "159"
},
{
"capability": [
"router",
"switch",
"Supports-STP-Dispute"
],
"device_id": "spine02",
"ifindex": "436232704",
"intf_id": "Ethernet1/50",
"platform_id": "N9K-C9508",
"port_id": "Ethernet1/11",
"ttl": "127"
},
{
"capability": [
"router",
"switch",
"IGMP_cnd_filtering",
"Supports-STP-Dispute"
],
"device_id": "leaf1J1402",
"ifindex": "436234240",
"intf_id": "Ethernet1/53",
"platform_id": "N9K-C93180YC-EX",
"port_id": "Ethernet1/53",
"ttl": "175"
},
{
"capability": [
"router",
"switch",
"IGMP_cnd_filtering",
"Supports-STP-Dispute"
],
"device_id": "leaf1J1402",
"ifindex": "436234752",
"intf_id": "Ethernet1/54",
"platform_id": "N9K-C93180YC-EX",
"port_id": "Ethernet1/54",
"ttl": "175"
}
]
},
"neigh_count": "5"
}
]
Currently I can access the first iteration of "intf_id" with :
- debug: msg="{{ list.stdout[0].TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[0].intf_id }}"
I feel like I'm close, but cannot figure out how to get each iteration of "intf_id". I can get subsequent ones by changing ROW_cdp_neighbor_brief_info[0] to [1] or [2], etc. I need to be able to access each key, without knowing how many keys there will be. Each value to the keys also need to be individually callable by subsequent tasks.
1) How do I get ansible to debug each iteration?
2) Depending on the answer to 1, how can I assign a particualr variable to each value? (ie. interface1, interface2, interface3...)
ansible-playbook json_query.yml
tasks:
- name:
debug:
msg: "{{ item }}"
loop: "{{ stdout | json_query('TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[*].intf_id') }}"
(output abridged)
"msg": "mgmt0"
"msg": "Ethernet1/49"
"msg": "Ethernet1/50"
"msg": "Ethernet1/53"
"msg": "Ethernet1/54"
You can use the filter json_query with jmespath syntax to loop over the various elements intf_id this way
---
- hosts: localhost
gather_facts: no
tasks:
- name: populate router data from json
set_fact:
data: "{{ lookup('file','so-router-info.json') }}"
- name: looping over interfaces
debug:
var: item
loop: "{{ data | json_query('TABLE_cdp_neighbor_brief_info.ROW_cdp_neighbor_brief_info[*].intf_id') }}"
it gives this output
…
TASK [looping over interfaces] ********************************************************************************************************
ok: [localhost] => (item=mgmt0) => {
"item": "mgmt0"
}
ok: [localhost] => (item=Ethernet1/49) => {
"item": "Ethernet1/49"
}
ok: [localhost] => (item=Ethernet1/50) => {
"item": "Ethernet1/50"
…
You can use the site http://jmespath.org/ to tests filters

Q: Ansible - How can I merge 2 lists of hashes with a common key/value pair

How can I merge 2 lists of hashes based on a key/value pair, using Ansible 2.4.4
"foo": [
{
"hostname": "web1.example.com",
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2"
},
{
"hostname": "web2.example.com",
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd"
},
{
"hostname": "web3.example.com",
"guid": "bba27304-c1bb-4889-aa44-125626be8488"
}
]
"bar": [
{
"ipaddress": "1.1.1.1",
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2"
},
{
"ipaddress": "2.2.2.2",
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd"
},
{
"ipaddress": "3.3.3.3",
"guid": "bba27304-c1bb-4889-aa44-125626be8488"
}
]
I want something like:
"foobar" : [
{
"hostname": "web1.example.com",
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2",
"ipaddress": "1.1.1.1"
},
{
"hostname": "web2.example.com",
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd",
"ipaddress": "2.2.2.2"
},
{
"hostname": "web3.example.com",
"guid": "bba27304-c1bb-4889-aa44-125626be8488",
"ipaddress": "3.3.3.3"
}
]
I've looked into several Ansible / Jinja2 filters including combine, union, map, custom plugins, but not having much success. I need to be able to match on the guid.
not sure if there is a smarter way, but to achieve what you need you can use the nested query plugin to loop over the combinations of the elements from the 2 list variables, find the combinations that have the common field equal, and then construct a new dictionary element and append it to the "final" list variable.
playbook:
- hosts: localhost
gather_facts: false
vars:
foo:
- hostname: web1.example.com
guid: 73e85eb2-2ad5-4699-8a09-adf658a11ff2
- hostname: web2.example.com
guid: 827025fe-f13c-4fc8-ba51-7ff582596bbd
- hostname: web3.example.com
guid: bba27304-c1bb-4889-aa44-125626be8488
bar:
- ipaddress: 1.1.1.1
guid: 73e85eb2-2ad5-4699-8a09-adf658a11ff2
- ipaddress: 2.2.2.2
guid: 827025fe-f13c-4fc8-ba51-7ff582596bbd
- ipaddress: 3.3.3.3
guid: bba27304-c1bb-4889-aa44-125626be8488
tasks:
- name: merge lists
set_fact:
merged_list: "{{ merged_list|default([]) + [{ 'hostname': item[0].hostname, 'guid': item[0].guid, 'ipaddress': item[1].ipaddress }] }}"
when: "item[0].guid == item[1].guid"
loop: "{{ query('nested', foo, bar) }}"
- name: print results
debug:
var: merged_list
result:
TASK [print results] ************************************************************************************************************************************************************************************************
ok: [localhost] => {
"merged_list": [
{
"guid": "73e85eb2-2ad5-4699-8a09-adf658a11ff2",
"hostname": "web1.example.com",
"ipaddress": "1.1.1.1"
},
{
"guid": "827025fe-f13c-4fc8-ba51-7ff582596bbd",
"hostname": "web2.example.com",
"ipaddress": "2.2.2.2"
},
{
"guid": "bba27304-c1bb-4889-aa44-125626be8488",
"hostname": "web3.example.com",
"ipaddress": "3.3.3.3"
}
]
}

Resources