Extract specific fields information from Ansible kubernetes.core.k8s_info? - ansible

I have the following tasks, to gather the pods information, more exactly the name and status:
- name: Get pod info
kubernetes.core.k8s_info:
api_version: v1
kind: Pod
label_selectors:
- helmcharts.helm.cattle.io/chart = {{ item }}
- job-name = helm-install-{{ item }}
namespace: kube-system
loop:
- traefik
- traefik-crd
register: pod_info
- name: Debug
ansible.builtin.debug:
var: pod_info
Simplified output:
ok: [apollo.lan] => {
"pod_info": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"api_found": true,
"changed": false,
"failed": false,
"item": "traefik",
"resources": [
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"labels": {
"helmcharts.helm.cattle.io/chart": "traefik",
"job-name": "helm-install-traefik"
},
"name": "helm-install-traefik-fk7fb",
"namespace": "kube-system",
"resourceVersion": "2550"
},
"status": {
"phase": "Succeeded",
"podIP": "10.0.0.75"
}
}
]
},
{
"ansible_loop_var": "item",
"api_found": true,
"changed": false,
"failed": false,
"item": "traefik-crd",
"resources": [
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"labels": {
"helmcharts.helm.cattle.io/chart": "traefik-crd",
"job-name": "helm-install-traefik-crd"
},
"name": "helm-install-traefik-crd-8p86c",
"namespace": "kube-system",
"resourceVersion": "2329"
},
"status": {
"phase": "Succeeded",
"podIP": "10.0.0.76"
}
}
]
}
],
"skipped": false
}
}
I don't know how to extract and register the pod name as list, combined with a when condition that matches the status phase Succeeded.
I first tried to extract just the metadata, in order to drill down to name:
- name: Set fact
ansible.builtin.set_fact:
variable: "{{ pod_info.results | json_query('metadata') }}"
Without success. I have a hard time determining the logic with a mix of lists and dictionaries, I even tried to look at lookup('ansible.utils.to_paths', pod_info.results) structure to see how I can pull the information.

Preliminary remark: I wouldn't do a loop to select the two pods you are looking for, I would rather use a in, like they are showing it in the examples.
So, your first task would become:
- name: Get pod info
kubernetes.core.k8s_info:
api_version: v1
kind: Pod
label_selectors:
- helmcharts.helm.cattle.io/chart in (traefik, traefik-crd)
- job-name in (helm-install-traefik, helm-install-traefik-crd)
namespace: kube-system
register: pod_info
The reason for it is simple: by registering under a loop, you are complexifying the return, as Ansible will create a results list for each elements of the loop, when you won't have it with this way of requesting the Kubernetes pods information.
And so, with this, listing all the pods having the status phase Succeeded should be achievable with
the selectattr filter, in order to filter only the successful status phases
the map filter in order to extract only the names of the pods
- debug:
var: >-
pod_info.resources
| selectattr('status.phase', '==', 'Succeeded')
| map(attribute='metadata.name')

Related

How to Read and Output json Response using Ansible

How to read/display the following response (e.g: name, srcinft, dstinf) from FortiGate Firewall using Ansible. Or is there any way I can read this JSON output from file and display the fields i want.
{
"changed": false,
"meta": {
"http_method": "GET",
"size": 2,
"matched_count": 2,
"next_idx": 1,
"revision": "ac9c4e1d722b74695dee4fb3ce4fcd12",
"results": [
{
"policyid": 1,
"q_origin_key": 1,
"status": "enable",
"name": "test-policy01",
"uuid": "c4de3298-97ce-51ed-ccba-cafc556ba9e0",
"uuid-idx": 14729,
"srcintf": [
{
"name": "port2",
"q_origin_key": "port2"
}
],
"dstintf": [
{
"name": "port1",
"q_origin_key": "port1"
}
],
"action": "accept",
"ztna-status": "disable",
"srcaddr": [
{
"name": "all",
"q_origin_key": "all"
}
],
"dstaddr": [
{
"name": "all",
"q_origin_key": "all"
}
],
"policy-expiry": "disable",
"policy-expiry-date": "0000-00-00 00:00:00",
"service": [
{
"name": "ALL",
"q_origin_key": "ALL"
}
],
"tos": "0x00",
"sgt-check": "disable",
"sgt": []
},
{
"policyid": 2,
"q_origin_key": 2,
"status": "enable",
"name": "test-policy-02",
"uuid": "534b6c9c-97d1-51ed-7aa8-7544c628c7ea",
"uuid-idx": 14730,
"srcintf": [
{
"name": "port1",
"q_origin_key": "port1"
}
],
"dstintf": [
{
"name": "port2",
"q_origin_key": "port2"
}
],
"action": "accept",
"nat64": "disable",
"nat46": "disable",
"ztna-status": "disable",
"srcaddr": [
{
"name": "all",
"q_origin_key": "all"
}
],
"dstaddr": [
{
"name": "login.microsoft.com",
"q_origin_key": "login.microsoft.com"
}
],
"srcaddr6": [],
"reputation-direction6": "destination",
"policy-expiry-date": "0000-00-00 00:00:00",
"service": [
{
"name": "ALL_ICMP6",
"q_origin_key": "ALL_ICMP6"
}
],
"tos": "0x00",
"webcache": "disable",
"webcache-https": "disable",
"sgt-check": "disable",
"sgt": []
}
],
"vdom": "root",
"path": "firewall",
"name": "policy",
"version": "v7.2.3",
"build": 1262
},
"invocation": {
"module_args": {
"vdom": "root",
"access_token": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"selector": "firewall_policy",
"selectors": null
}
},
"_ansible_no_log": false
}
Expected result:
result:
test-policy-02:
dstintf:
- name: port2
q_origin_key: port2
srcintf:
- name: port1
q_origin_key: port1
test-policy01:
dstintf:
- name: port1
q_origin_key: port1
srcintf:
- name: port2
q_origin_key: port2
Unfortunately there is absolute no description or any further information.
However, regarding
How to read/display the following response (e.g: name, srcinft, dstinf) from FortiGate Firewall using Ansible.
you may have a look into the following simple and lazy approach with loop
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Include vars of stuff.yaml into the 'stuff' variable (2.2).
ansible.builtin.include_vars:
file: stuff.json
name: stuff
- name: Show list of dict
debug:
msg: "{{ stuff.meta.results }}"
- name: Print key:value
debug:
msg:
- "name: {{ item.name }}"
- "{{ item.srcintf }}"
- "{{ item.dstintf }}"
loop_control:
label: "policyid: {{ item.policyid }}"
loop: "{{ stuff.meta.results }}"
resulting into an output of
TASK [Print key:value] *****************
ok: [localhost] => (item=policyid: 1) =>
msg:
- 'name: test-policy01'
- - name: port2
q_origin_key: port2
- - name: port1
q_origin_key: port1
ok: [localhost] => (item=policyid: 2) =>
msg:
- 'name: test-policy-02'
- - name: port1
q_origin_key: port1
- - name: port2
q_origin_key: port2
Further Documentation
include_vars module – Load variables from files, dynamically within a task
Loops
Or is there any way I can read this JSON output from file and display the fields I want?
To get familiar with data structure, respective JSON response you've provided in your example, you could start with something like a JSONPathFinder. It will result into an path of
x.meta.results[0].name
x.meta.results[0].srcintf
x.meta.results[0].dstintf
x.meta.results[1].name
x.meta.results[1].srcintf
x.meta.results[1].dstintf
for the provided keys.
It is also possible to use jq on CLI
jq keys stuff.json
[
"_ansible_no_log",
"changed",
"invocation",
"meta"
]
and just proceed further with
jq '.meta.results' stuff.json
jq '.meta.results[0]' stuff.json
jq '.meta.results[1]' stuff.json
Further Q&A
How to get key names from JSON using jq?

Ansible Filtering with Values within Dictionary of the List (FortiOS Facts)

I am tyring to use filter with Values within Dictionary of the List (FortiOS Facts). Sample Resp onse from FortiGate (Facts)
{
"changed": false,
"meta": {
"http_method": "GET",
"size": 2,
"matched_count": 2,
"next_idx": 1,
"revision": "ac9c4e1d722b74695dee4fb3ce4fcd12",
"results": [
{
"policyid": 1,
"q_origin_key": 1,
"status": "enable",
"name": "test-policy01",
"uuid": "c4de3298-97ce-51ed-ccba-cafc556ba9e0",
"uuid-idx": 14729,
"srcintf": [
{
"name": "port2",
"q_origin_key": "port2"
}
],
"dstintf": [
{
"name": "port1",
"q_origin_key": "port1"
}
],
"action": "accept",
"ztna-status": "disable",
"srcaddr": [
{
"name": "all",
"q_origin_key": "all"
}
],
Ansible Filter works fine for the following code
- name: To Fetch Existing Firewall Polices Based on Selector firewall_policy
fortios_configuration_fact:
vdom: "{{ vdom }}"
access_token: "{{ fortigate_access_token }}"
selector: "firewall_policy"
filters:
- action=="accept"
register: existing_fw_policy_object
when: existing_fw_name is defined
- name: Display Existing Firewall Policy
debug:
msg: "{{ existing_fw_policy_object }}"
What is the way to filter on srcintf. Wanted to Filter Firewall Policy with port2.

Filter attributes in a list of dictionaries

I have two attributes I want to see from a list of dictionaries: name and version.
Expected output:
name : kernel
vesion: 3.10.0
Input from a yum task:
{
"yumoutput": {
"changed": false,
"failed": false,
"results": [{
"arch": "x86_64",
"envra": "0:kernel-3.10.0-1160.80.1.el7.x86_64",
"epoch": "0",
"name": "kernel",
"release": "1160.80.1.el7",
"repo": "rhui-rhel-7-server-rhui-rpms",
"version": "3.10.0",
"yumstate": "available"
},
{
"arch": "x86_64",
"envra": "0:python-perf-3.10.0-1160.80.1.el7.x86_64",
"epoch": "0",
"name": "python-perf",
"release": "1160.80.1.el7",
"repo": "rhui-rhel-7-server-rhui-rpms",
"version": "3.10.0",
"yumstate": "available"
},
{
"arch": "noarch",
"envra": "0:tzdata-2022f-1.el7.noarch",
"epoch": "0",
"name": "tzdata",
"release": "1.el7",
"repo": "rhui-rhel-7-server-rhui-rpms",
"version": "2022f",
"yumstate": "available"
}
]
}
}
My tasks:
- name: List Available Patches (Non-Kernel)
yum:
list: updates
update_cache: true
exclude: kernel*
security: true
register: yumoutput
- name: Show result
debug:
var: yumoutput
How can I filter the output to keep only entries with name: kernel and version: 3.10.0?
In order to filter a dictionary you can:
convert it to list with dict2items
only select the keys you care about with selectattr
and finally convert it back to a dictionary with items2dict
Since you have a list of dictionaries, you need to throw all those filters in map's.
So, given the task:
- debug:
var: >-
yumoutput.results
| map('dict2items')
| map('selectattr', 'key', 'in', ['name', 'version'])
| map('items2dict')
You get the expected:
- name: kernel
version: 3.10.0
- name: python-perf
version: 3.10.0
- name: tzdata
version: 2022f
As a follow-up to #β.εηοιτ.βε's answer, if you have a recent enough install of ansible with the ansible.utils collection available, you can acheive the same result using the ansible.utils.keep_keys filter
- debug:
msg: "{{ yumoutput.results
| ansible.utils.keep_keys(target=['name', 'version']) }}"
Gives:
TASK [debug] ******************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"name": "kernel",
"version": "3.10.0"
},
{
"name": "python-perf",
"version": "3.10.0"
},
{
"name": "tzdata",
"version": "2022f"
}
]
}

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

Fetch the values of the variables in ansible

I am trying to list the names whose matches have been found.
- name: search for files containing string
find:
paths: /root/ansible-dir
patterns: "file3.yml"
contains: "{{ item }}"
with_items: "{{ names_list }}"
register: file_match
- name: print file
debug:
msg: "{{ file_match }}"
After the above code is run, the below code gets generated :
"msg": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"examined": 14,
"failed": false,
"files": [],
"invocation": {
"module_args": {
"age": null,
"age_stamp": "mtime",
"contains": "node_mem",
"depth": null,
"excludes": null,
"file_type": "file",
"follow": false,
"get_checksum": false,
"hidden": false,
"paths": [
"/root/ansible-dir"
],
"patterns": [
"file3.yml"
],
"recurse": false,
"size": null,
"use_regex": false
}
},
"item": "node_mem",
"matched": 0,
"msg": ""
},
How to fetch the name of the item where the matched attribute is >=1?
Use subelements. The debug below gives you a hint how to iterate the results and fetch files that contain the items
- debug:
msg: "Fetch {{ item.1 }}"
with_subelements:
- "{{ file_match.results }}"
- files
It is not necessary to test the number of files is >=1. If the list of files is empty the iteration will be skipped anyway.

Resources