ansible match variable pattern to json output - json

I am using the Ansible vmware_vm_info module to generate json like this:
{
"virtual_machines": [
{
"guest_name": "serverx",
"datacenter": "datacenter",
"guest_fullname": "Red Hat Enterprise Linux 7 (64-bit)",
"esxi_hostname": "host1",
"tags": [
],
"cluster": "cluster1",
"vm_network": {
},
"mac_address": [
],
"attributes": {
},
"folder": "",
"power_state": "poweredOff",
"ip_address": "",
"uuid": "33234323oijdlk"
},
{
"guest_name": "server2_01112022",
"datacenter": "datacenter",
"guest_fullname": "Microsoft Windows Server 2012 (64-bit)",
"esxi_hostname": "host1",
"tags": [
],
"cluster": "cluster1",
"vm_network": {
},
"mac_address": [
],
"attributes": {
},
"folder": "",
"power_state": "poweredOff",
"ip_address": "",
"uuid": "287292lkqjjjjd"
},
I have a variable that reads a csv and sets the vmname like this:
vmname
server1
server2
I need set a new fact that will look for the vmname string existing in the vminfo json output. In the example above, server2 would match server2_01112022.
I have tried the following but I don't get a match:
- set_fact:
renamedvm: "{{ item.guest_name }}"
with_items:
- "{{ vminfo.virtual_machines }}"
when:
- item.guest_name |regex_search('^result_item.vmname.$')

For example, given the list
vmname: [server1, server2]
the task
- set_fact:
renamedvm: "{{ renamedvm|d({})|combine({item: _value}) }}"
loop: "{{ vmname|unique|sort }}"
vars:
_selection: "{{ virtual_machines|selectattr('guest_name', 'search', item) }}"
_value: "{{ _selection|map(attribute='guest_fullname')|list }}"
gives
renamedvm:
server1: []
server2:
- Microsoft Windows Server 2012 (64-bit)

Related

How many times item into a json

I have a json file (/tmp/lst_instalaciones_wordpress.json) on a group of servers.
This json has the info about some Wordpress installs (domain, path, version...)
{
"id": 1,
"name": "My blog",
"version": "5.9.1",
"fullPath": "/var/www/vhosts/myblog"
}
{
"id": 2,
"name": "My blog2",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog2"
}
{
"id": 3,
"name": "My blog3",
"version": "5.7.6",
"fullPath": "/var/www/vhosts/myblog3"
}
{
"id": 4,
"name": "My blog4",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog4"
}
I slurped it in a var:
- slurp:
src: /tmp/lst_instalaciones_wordpress.json
register: fichero_json
- set_fact:
lst_versiones: "{{ fichero_json.content | b64decode | from_json}}"
I have a list of all the versions, and I need to print how much installs are for each versions.
all_vers:
- 5.7.6
- 5.9.1
- 5.9.2
I'am trying different options like this but nothing works:
- set_fact:
all_installs_vers: "{{ lst_versiones | json_query('[?version==`'{{item}}'`].version') | length }}"
with_items: "{{all_vers}}"
Given the fixed YAML
shell> cat tmp/lst_instalaciones_wordpress.yml
---
[
{
"id": 1,
"name": "My blog",
"version": "5.9.1",
"fullPath": "/var/www/vhosts/myblog"
},
{
"id": 2,
"name": "My blog2",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog2"
},
{
"id": 3,
"name": "My blog3",
"version": "5.7.6",
"fullPath": "/var/www/vhosts/myblog3"
},
{
"id": 4,
"name": "My blog4",
"version": "5.9.2",
"fullPath": "/var/www/vhosts/myblog4"
}
]
Read the file and decode the list
- slurp:
src: tmp/lst_instalaciones_wordpress.yml
register: fichero_json
- set_fact:
lst_versiones: "{{ fichero_json.content|b64decode|from_yaml }}"
gives
lst_versiones:
- fullPath: /var/www/vhosts/myblog
id: 1
name: My blog
version: 5.9.1
- fullPath: /var/www/vhosts/myblog2
id: 2
name: My blog2
version: 5.9.2
- fullPath: /var/www/vhosts/myblog3
id: 3
name: My blog3
version: 5.7.6
- fullPath: /var/www/vhosts/myblog4
id: 4
name: My blog4
version: 5.9.2
Then, given the list of the versions
all_vers:
- 5.7.6
- 5.9.1
- 5.9.2
iterate the list, select the version, count the items and combine the dictionary
- set_fact:
all_installs_vers: "{{ all_installs_vers|d({})|
combine({item: _val|int}) }}"
loop: "{{ all_vers }}"
vars:
_val: "{{ lst_versiones|selectattr('version', 'eq', item)|length }}"
gives
all_installs_vers:
5.7.6: 1
5.9.1: 1
5.9.2: 2
Another solution with only one step
- set_fact:
versions: "{{ versions | d({}) | combine({ item.version: nbversion }) }}"
loop: '{{ lst_versiones }}'
vars:
nbversion: >-
{{ (versions[item.version] | int + 1) if versions[item.version] is defined
else (1 | int) }}
result if you debug versions:
ok: [localhost] => {
"msg": {
"5.7.6": "1",
"5.9.1": "1",
"5.9.2": "2"
}
}

How to access key/pair values within objects inside of an array in Ansible?

So, we are wanting to propagate a list of logged-in users to our hosts.
We have a batch script that does this for us and parses it to JSON format.
- name: Process win_shell output
set_fact:
qusers: "{{ qusers_ps.stdout | from_json }}"
- debug:
var: qusers
verbosity: 2
This debug outputs the following:
{
"qusers": {
"Server1": []
},
"_ansible_verbose_always": true,
"_ansible_no_log": false,
"changed": false
}
{
"qusers": {
"Server2": [
{
"USERNAME": "user102",
"SESSIONNAME": "rdp-tcp#0",
"ID": "6",
"STATE": "Active",
"IDLE TIME": "32",
"LOGON TIME": "5/29/2020 9:13 AM"
}
]
},
"_ansible_verbose_always": true,
"_ansible_no_log": false,
"changed": false
}
{
"qusers": {
"Server3": [
{
"USERNAME": "user183",
"SESSIONNAME": "",
"ID": "49",
"STATE": "Disc",
"IDLE TIME": "14:34",
"LOGON TIME": "5/28/2020 7:58 AM"
},
{
"USERNAME": "user103",
"SESSIONNAME": "",
"ID": "51",
"STATE": "Disc",
"IDLE TIME": "18:26",
"LOGON TIME": "5/28/2020 8:18 AM"
},
{
"USERNAME": "user148",
"SESSIONNAME": "",
"ID": "52",
"STATE": "Disc",
"IDLE TIME": "17:10",
"LOGON TIME": "5/28/2020 9:08 AM"
}
]
},
"_ansible_verbose_always": true,
"_ansible_no_log": false,
"changed": false
}
So you can see that debug returns JSON with the server name as the root element, and then an array of objects with key/pair values.
We need to be able to query values within these array objects, such as 'USERNAME' and 'IDLE TIME' for each server. I've been able to do this statically (shown below) to grab the user of the first array object, but I can't figure out how to do this dynamically.
- name: Test selecting JSON output and register as array
debug:
msg: "{{ item.value }}"
loop: "{{ q('dict', qusers) }}"
register: user_op
- name: Set array variable
set_fact:
user_array: "{{ user_op.results }}"
- name: Print item value # Prints array object as is
debug:
msg: "{{ item.item.value }} "
with_items: "{{ user_array }}"
- name: Test pulling JSON from array with conditional # This one works, but only grabs the first user
debug:
msg: "{{ item.item.value[0]['USERNAME'] }} is logged into {{item.item.key }}"
with_items: "{{ user_array }}"
when: item.item.value != []
The last play is the only time I've ever been able to reach a specific key/pair element. How do I modify this to dynamically perform this operation for every user session object in the array instead of only the first, and without hard coding numbers??
I cannot easily reproduce your play loop over your three hosts, so I made a demo with your example data and a prompt allowing to choose which host to display. Of course this will be taken in charge directly for you when you repoduce this in your current playbook.
The idea is to:
transform the dict to a list as you already did using dict2items (same result as the dict lookup you used).
grab only the first item of the list as this is your data model
grab the key of the item as the name of the current server to display
grab the value of the item and loop over it since it is our list of users
display the info we need without even carring about the list being empty or not. Empty list means empty loop therefore nothing will happen in that case.
Below my MCVE and a play for the third element. You can play yourself for the other ones. As you can see this can actually fit in a single task once you have loaded the info from your json output.
---
- hosts: localhost
gather_facts: false
vars_prompt:
- name: host
prompt: "display host ? (1-3)"
private: no
default: 1
vars:
"qusers1": {
"Server1": []
}
"qusers2": {
"Server2": [
{
"USERNAME": "user102",
"SESSIONNAME": "rdp-tcp#0",
"ID": "6",
"STATE": "Active",
"IDLE TIME": "32",
"LOGON TIME": "5/29/2020 9:13 AM"
}
]
}
"qusers3": {
"Server3": [
{
"USERNAME": "user183",
"SESSIONNAME": "",
"ID": "49",
"STATE": "Disc",
"IDLE TIME": "14:34",
"LOGON TIME": "5/28/2020 7:58 AM"
},
{
"USERNAME": "user103",
"SESSIONNAME": "",
"ID": "51",
"STATE": "Disc",
"IDLE TIME": "18:26",
"LOGON TIME": "5/28/2020 8:18 AM"
},
{
"USERNAME": "user148",
"SESSIONNAME": "",
"ID": "52",
"STATE": "Disc",
"IDLE TIME": "17:10",
"LOGON TIME": "5/28/2020 9:08 AM"
}
]
}
tasks:
- name: Load the relevent host data for demo to be in same condition as in original play
set_fact:
qusers: "{{ lookup('vars', 'qusers' + host) }}"
- name: Show info about users
vars:
host_users_info: "{{ (qusers | dict2items).0 }}"
servername: "{{ host_users_info.key }}"
loguedin_users: "{{ host_users_info.value }}"
debug:
msg: "{{ item.USERNAME }} is logged into {{ servername }} and is idle for {{ item['IDLE TIME'] }}"
loop: "{{ loguedin_users }}"
Which gives for element 3
$ ansible-playbook test.yml
display host ? (1-3) [1]: 3
PLAY [localhost] ************************************************************************************************************************************
TASK [Load the relevent host data for demo to be in same condition as in original play] *************************************************************
ok: [localhost]
TASK [Show info about users] ************************************************************************************************************************
ok: [localhost] => (item={'USERNAME': 'user183', 'SESSIONNAME': '', 'ID': '49', 'STATE': 'Disc', 'IDLE TIME': '14:34', 'LOGON TIME': '5/28/2020 7:58 AM'}) => {
"msg": "user183 is logged into Server3 and is idle for 14:34"
}
ok: [localhost] => (item={'USERNAME': 'user103', 'SESSIONNAME': '', 'ID': '51', 'STATE': 'Disc', 'IDLE TIME': '18:26', 'LOGON TIME': '5/28/2020 8:18 AM'}) => {
"msg": "user103 is logged into Server3 and is idle for 18:26"
}
ok: [localhost] => (item={'USERNAME': 'user148', 'SESSIONNAME': '', 'ID': '52', 'STATE': 'Disc', 'IDLE TIME': '17:10', 'LOGON TIME': '5/28/2020 9:08 AM'}) => {
"msg": "user148 is logged into Server3 and is idle for 17:10"
}
PLAY RECAP ******************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Access nested items on a dictionary ansible

I have a json file with server information and the applications related.
Something like this:
{
"Apps": [
{
"AppOwner": "Me",
"AppNm": "Vacations"
}
],
"Hostnm": "some_server",
"Environment": "Prod",
"OSnm": "Windows Server 2008",
"OSManu": "Microsoft",
"SerialNum": "VMware-42 14 e1 37 7a 63 9b 0e-43 07 15 46 64 9c 3c 12"
}
I want to create a list with all the applications and the servers related
For example
{
"Vacations": [server1, server2]
},
{
"other_app": [server2, server3]
},
For that, I have been trying the following.
- set_fact:
apps: "{{ apps }} + {{ item.Apps | map(attribute='AppNm') | list}}"
loop: "{{ server_changes }}"
- set_fact:
apps_revised: "{{apps | unique}}"
- set_fact:
apps_server: "{{ apps_revised | default([]) + [{item : []}] }}"
with_items: "{{ apps_revised }}"
The first part of the code, will obtain all the applications with the duplicates from the json.
The second part of the code, will clean the apps with unique.
The last part its going to create a list of application with a nested list (of servers).
The problem is that I don't understand how to navigate the nested server_changes application list and then add the server to my new list.
Probably there is an easier way to do it, so any help will be appreciated.
Thanks
Given a test data
server_changes:
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
"Hostnm": "server1"
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server2"
- "Apps":
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server3"
Let's simplify the data. For example
- set_fact:
my_server_changes: "{{ my_server_changes|default([]) +
[{'hostname': item.Hostnm,
'applications': item.Apps|
json_query('[].AppNm')}] }}"
loop: "{{ server_changes }}"
- debug:
var: my_server_changes
gives
"my_server_changes": [
{
"applications": [
"Vacations"
],
"hostname": "server1"
},
{
"applications": [
"Vacations",
"other_app"
],
"hostname": "server2"
},
{
"applications": [
"other_app"
],
"hostname": "server3"
}
]
Create the list of the applications
- set_fact:
my_apps: "{{ server_changes|json_query('[].Apps[].AppNm')|unique }}"
- debug:
var: my_apps
gives
"my_apps": [
"Vacations",
"other_app"
]
Then loop the list of the applications, select records that contains the item and combine the dictionary
- set_fact:
apps_server: "{{ apps_server|default({})|
combine({item: my_server_changes|json_query(query)}) }}"
loop: "{{ my_apps }}"
vars:
query: "[?applications.contains(#, '{{ item }}')].hostname"
- debug:
var: apps_server
gives
"apps_server": {
"Vacations": [
"server1",
"server2"
],
"other_app": [
"server2",
"server3"
]
}

Ansible - Register variable and then search the variable to set_fact (Cisco Aci)

I query the Cisco ACI to attain the advanced vmm provider details for a specific EPG.
The result is successful.
I then register the result to a variable.
I try to search that variable and obtain\extract a single specific piece of information such as 'dn' or 'encap' as this would allow me to use the information in other plays.
Unfortunately i'm unable to extract the information as the result comes back in an usual format. Looking at a debug on the register variable, it would appear it's a dictionary variable but no matter what I try the only item i'm able to access is the 'current' item.
All other items are not registered as dictionary items.
I have tried to change the variable to a list but still i'm unable to attain the information I require.
I've searched forums to see if the there is a methodology to convert the variable from a json result or dictionary variable to a string and then grep for the information but no success.
Ideally I would like to extract the information without installing additional 'apps'.
Will be very grateful if someone can advise how to search for a specific result from an irregular nested result which doesn't list the items in a correct dictionary format.
- name: Access VMM provider Information
hosts: apics
gather_facts: false
connection: local
#
vars:
ansible_python_interpreter: /usr/bin/python3
#
tasks:
- name: Play 1 Obtain VMM Provider Information
aci_epg_to_domain:
hostname: "{{ apics.hostname }}"
username: "{{ apics.username }}"
password: "{{ apics.password }}"
tenant: Tenant_A
ap: AP_Test
epg: EPG_Test
domain: DVS_Dell
domain_type: vmm
vm_provider: vmware
state: query
validate_certs: no
register: DVS_Result
#
- set_fact:
aci_result1: "{{ DVS_Result.current }}"
- set_fact:
aci_result2: "{{ DVS_Result.fvRsDomAtt.attributes.dn }}"
#
- debug:
msg: "{{ DVS_Result }}"
- debug:
var=aci_result1
- debug:
var=aci_result2
DVS_Result
ok: [apic1r] => {
"msg": {
"changed": false,
"current": [
{
"fvRsDomAtt": {
"attributes": {
"annotation": "",
"bindingType": "none",
"childAction": "",
"classPref": "encap",
"configIssues": "",
"delimiter": "",
"dn": "uni/tn-TN_prod/ap-AP_Test/epg-EPG_Test/rsdomAtt-[uni/vmmp-VMware/dom-DVS_Dell]",
"encap": "unknown",
"encapMode": "auto",
"epgCos": "Cos0",
"epgCosPref": "disabled",
"extMngdBy": "",
"forceResolve": "yes",
"instrImedcy": "lazy",
"lagPolicyName": "",
"lcOwn": "local",
"modTs": "2019-08-18T20:52:13.570+00:00",
"mode": "default",
"monPolDn": "uni/tn-common/monepg-default",
"netflowDir": "both",
"netflowPref": "disabled",
"numPorts": "0",
"portAllocation": "none",
"primaryEncap": "unknown",
"primaryEncapInner": "unknown",
"rType": "mo",
"resImedcy": "lazy",
"secondaryEncapInner": "unknown",
"state": "missing-target",
"stateQual": "none",
"status": "",
"switchingMode": "native",
"tCl": "infraDomP",
"tDn": "uni/vmmp-VMware/dom-DVS_Dell",
"tType": "mo",
"triggerSt": "triggerable",
"txId": "8646911284551354729",
"uid": "15374"
}
}
}
],
"failed": false
}
}
######################################
### aci_result1
ok: [apic1r] => {
"aci_result1": [
{
"fvRsDomAtt": {
"attributes": {
"annotation": "",
"bindingType": "none",
"childAction": "",
"classPref": "encap",
"configIssues": "",
"delimiter": "",
"dn": "uni/tn-TN_prod/ap-AP_Test/epg-EPG_Test/rsdomAtt-[uni/vmmp-VMware/dom-DVS_Dell]",
"encap": "unknown",
"encapMode": "auto",
"epgCos": "Cos0",
"epgCosPref": "disabled",
"extMngdBy": "",
"forceResolve": "yes",
"instrImedcy": "lazy",
"lagPolicyName": "",
"lcOwn": "local",
"modTs": "2019-08-18T20:52:13.570+00:00",
"mode": "default",
"monPolDn": "uni/tn-common/monepg-default",
"netflowDir": "both",
"netflowPref": "disabled",
"numPorts": "0",
"portAllocation": "none",
"primaryEncap": "unknown",
"primaryEncapInner": "unknown",
"rType": "mo",
"resImedcy": "lazy",
"secondaryEncapInner": "unknown",
"state": "missing-target",
"stateQual": "none",
"status": "",
"switchingMode": "native",
"tCl": "infraDomP",
"tDn": "uni/vmmp-VMware/dom-DVS_Dell",
"tType": "mo",
"triggerSt": "triggerable",
"txId": "8646911284551354729",
"uid": "15374"
}
}
}
]
}
############################################
### aci_result2
fatal: [apic1r]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'fvRsDomAtt'\n\nThe error appears to be in '/etc/ansible/playbooks/cisco/aci/create_bd_ap_epg3.yml': line 37, column 8, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - set_fact:\n ^ here\n"}
Use json_query. For example
- debug:
msg: "{{ DVS_Result.current|
json_query('[].fvRsDomAtt.attributes.dn') }}"

How to get values from registered json output conditionally in ansible

I am trying to parse the json output of yum module, to conditionally get data.
My playbook looks like below:
---
- hosts: all
become: true
tasks:
- name: list ggk rpms
yum:
list: "{{ item }}"
register: ggk_njk_info
ignore_errors: yes
with_items:
- ggk_base
- njk_tt_client
- debug: msg="{{ item.results }}"
with_items: "{{ ggk_njk_info.results }}"
when: item.results
The output for the debug task looks like below:
A part of the debug looks like below:
"msg": [
{
"arch": "noarch",
"envra": "0:njk_tt_client-2.36.11-1.noarch",
"epoch": "0",
"name": "njk_tt_client",
"release": "1",
"repo": "ggk_Software",
"version": "2.36.11",
"yumstate": "available"
},
{
"arch": "noarch",
"envra": "0:njk_tt_client-2.36.11-1.noarch",
"epoch": "0",
"name": "njk_tt_client",
"release": "1",
"repo": "installed",
"version": "2.36.11",
"yumstate": "installed"
},
{
"arch": "noarch",
"envra": "0:njk_tt_client-2.36.3-1.noarch",
"epoch": "0",
"name": "njk_tt_client",
"release": "1",
"repo": "ggk_Software",
"version": "2.36.3",
"yumstate": "available"
}
]
}
I would like to find the rpm "version" ONLY when its corresponding "yumstate" is "installed"
In this case i would like to be able to get the version for below:
"repo": "installed",
"version": "2.36.11",
json_query does the job. For example the task below
- debug:
msg: "{{ ggk_njk_info.results|
json_query('[?yumstate==`installed`].{repo: repo,
version: version}') }}"
gives
"msg": [
{
"repo": "installed",
"version": "2.36.11"
}
]