Syntax to check if var - json

I have a playbook to get all disks letter configured on my server and I need a task to verify if extra var letter is on the list.
For example I need to check if "F" is on the json data below.
Could you please help me on the best best syntax?
Thanks
{
"disks_drives_letter": [
[
"C"
],
[
"D"
],
[
"E"
],
[]
]
}

You can use setup module to get your host information like disks. For more information about the setup module https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html
Example of playbook:
- hosts: localhost
tasks:
gather_facts: false
vars:
my_disk_drives: ['sda', 'sdb']
tasks:
- name: Collect host hardware information
setup:
gather_subset:
- hardware
- name: Output if disk exist
debug:
msg: "{{ item }} exists"
loop: "{{ my_disk_drives }}"
when: item in hostvars[inventory_hostname].ansible_devices.keys() | list
- name: Output if disks does not exist
debug:
msg: "{{ item }} does not exist"
loop: "{{ my_disk_drives }}"
when: not item in hostvars[inventory_hostname].ansible_devices.keys() | list
Output:
TASK [Output if disk exist]
ok: [localhost] => (item=sda) => {
"msg": "sda exists"
}
skipping: [localhost] => (item=sdb)
TASK [Output if disks does not exist]
skipping: [localhost] => (item=sda)
ok: [localhost] => (item=sdb) => {
"msg": "sdb does not exist"
}

Use filters intersect and difference, and declare the lists
my_disks_exist: "{{ ansible_devices.keys()|intersect(my_disks) }}"
my_disks_not_exist: "{{ my_disks|difference(my_disks_exist) }}"
Example of a complete playbook for testing
- hosts: localhost
vars:
my_disks: [sda, sdb, sdc]
my_disks_exist: "{{ ansible_devices.keys()|intersect(my_disks) }}"
my_disks_not_exist: "{{ my_disks|difference(my_disks_exist) }}"
tasks:
- setup:
gather_subset: devices
- debug:
var: ansible_devices.keys()
- debug:
var: my_disks_exist
- debug:
var: my_disks_not_exist

| flatten help me thanks #vladimir-botka
- name: Get all disks letter from the disks infos
set_fact:
disks_drives_letters: "{{ win_disk_facts | json_query(query) | flatten }}"
- name: Check if disk_letter is used on server fail:
msg: "The disk letter already exist on the VM" when: '"{{ drive_letter }}" in "{{ disks_drives_letters}}"'

Related

I'm trying to get ansible output formatted like item1 - item2 instead of two separate lists of items

I have a functioning ansible script that connects to AWS and prints out tags from instances in a specified state.
The problem I'm having is the print out is two separate lists e.g.
What I get is:
name1
name2
name3
description1
description2
description3
What I would like is:
name1 - description1
name2 - description2
name3 - description3
I've tried pushing this all into a dictionary, but got lost in the attempt. There must be an easier way.
Here's my code:
- name: print stopped systems
hosts: all
become: false
tasks:
- name: Gather ec2_metadata_facts (use -vv to show all)
action: ec2_metadata_facts
- name: pull instance info with ec2_instance_info
ec2_instance_info:
region: "{{ lookup('env','AWS_DEFAULT_REGION') }}"
aws_access_key: "{{ lookup('env','AWS_ACCESS_KEY_ID') }}"
aws_secret_key: "{{ lookup('env','AWS_SECRET_ACCESS_KEY') }}"
filters:
instance-state-name: [ "shutting-down", "stopping", "stopped" ]
register: ec2_info
- name: print Instance Info
debug:
msg:
- "{{ ec2_info | json_query(name_tag) }}"
- "{{ ec2_info | json_query(description_tag) }}"
vars:
name_tag: "instances[*].tags.Name"
description_tag: "instances[*].tags.Description"
The output of the debug statement looks like this:
TASK [print Instance Info] **********************************************************************************************************************
ok: [tools-server-01] =>
msg:
- - server-01
- server-02
- - Description for server one
- Description for server two
Thanks #Rickkwa
I've got this mostly working.
here's my current code
- name: Print stopped systems
hosts: all
become: false
tasks:
- name: Gather ec2_metadata_facts (use -vv to show all)
action: ec2_metadata_facts
- name: pull instance info with ec2_instance_info
ec2_instance_info:
region: "{{ lookup('env','AWS_DEFAULT_REGION') }}"
aws_access_key: "{{ lookup('env','AWS_ACCESS_KEY_ID') }}"
aws_secret_key: "{{ lookup('env','AWS_SECRET_ACCESS_KEY') }}"
filters:
instance-state-name: [ "shutting-down", "stopping", "stopped" ]
register: ec2_info
- name: Create Name list
no_log: true
set_fact:
name_l: "{{ec2_info | json_query(name_tag)}}"
vars:
name_tag: "instances[*].tags.Name"
- name: Create Description list
no_log: true
set_fact:
desc_l: "{{ ec2_info | json_query(description_tag) }}"
vars:
description_tag: "instances[*].tags.Description"
- name: print together
debug:
msg: "{{item.0}} --- {{item.1}}"
loop: "{{ name_l|zip(desc_l)|list}}"
Unfortunately, the last debug statement includes some 'garbage" I'd like to remove...anyone have a suggestion to get rid of the stuff following the "u"s?
here's the output :'
TASK [print together] ***************************************************************************************************************************
ok: [dev-bsd-01] => (item=[u'server-01', u'server one description']) =>
msg: server-01 --- server one description
ok: [dev-bsd-01] => (item=[u'server-02', u'server two description']) =>
msg: server-02 --- server two description

Ansible reading nested json values and matching variable

I am using this in an Ansible playbook:
- name: Gather info from Vcenter
vmware_vm_info:
hostname: "{{ result_item.vcenter }}"
username: "{{ ansible_username }}"
password: "{{ ansible_password }}"
validate_certs: no
register: vminfo
loop: "{{ result.list }}"
loop_control:
loop_var: result_item
I loop through a csv which has a list of VMs and their Vcenters. The json output from the Ansible task is this:
{
"results": [
{
"changed": false,
"virtual_machines": [
{
"guest_name": "Server1",
"guest_fullname": "SUSE Linux Enterprise 11 (64-bit)",
"power_state": "poweredOn",
},
{
"guest_name": "Server2",
"guest_fullname": "FreeBSD Pre-11 versions (64-bit)",
"power_state": "poweredOn",
},
Now I need to query this output for the VMs in my csv (guest_name matches vmname) and use set_fact to indicate whether the VMs in the csv are poweredOff or poweredOn. Next I can use it as a conditional on whether to power off the VM or not based on its current status.
I can't seem to get the json_query to work when matching to the VM name in the csv to the json output and then getting the corresponding power status. Any ideas?
CSV file:
vmname vcenter
Server1 Vcenter1
Server2 Vcenter1
Q: "set_fact to indicate whether the VMs in the CSV are powered off or powered on."
A: For example
- read_csv:
path: servers.csv
dialect: excel-tab
register: result
- set_fact:
servers: "{{ result.list|map(attribute='vmname')|list }}"
- set_fact:
virtual_machines: "{{ virtual_machines|default([]) +
[dict(_servers|zip(_values))] }}"
loop: "{{ vminfo.results }}"
vars:
_servers: "{{ servers|intersect(_dict.keys()|list) }}"
_values: "{{ _servers|map('extract',_dict)|list }}"
_dict: "{{ item.virtual_machines|
items2dict(key_name='guest_name', value_name='power_state') }}"
- debug:
var: virtual_machines
gives
virtual_machines:
- Server1: poweredOn
Server2: poweredOn
Servers missing in the vminfo.results will be silently ignored.
Q: "Use it as a conditional on whether to power off the VM or not."
A: For example Server1 in the first host
- debug:
msg: "Host={{ _host }} VM={{ _vm }} is poweredOn"
when: virtual_machines[_host][_vm] == 'poweredOn'
vars:
_host: 0
_vm: Server1
gives
msg: Host=0 VM=Server1 is poweredOn
I suppose, from your your example that you do have a TSV, so a tab separated values and not a CSV, which stands for comma separated values.
Based on this, the read_csv module, along with the dialect: excel-tab will help you read your TSV.
Then, you will need to use a filter projection to query the JSON based on the data in your TSV file.
You could also need to flatten the projection to get rid of the doubles list created by both the list in results and in virtual_machines.
An example of the resulting JMESPath query, for the Server1 ends up being:
results[].virtual_machines[?
guest_name == `Server1`
]|[]|[0].power_state
Then with all this in a playbook we do end up with:
- hosts: localhost
gather_facts: no
tasks:
- read_csv:
path: servers.csv
dialect: excel-tab
register: servers
- debug:
msg: >-
For {{ item.vmname }}, the state is {{
vminfo |
json_query(
'results[].virtual_machines[?
guest_name == `' ~ item.vmname ~ '`
]|[]|[0].power_state'
)
}}
loop: "{{ servers.list }}"
loop_control:
label: "{{ item.vmname }}"
vars:
vminfo:
results:
- changed: false
virtual_machines:
- guest_name: Server1
guest_fullname: SUSE Linux Enterprise 11 (64-bit)
power_state: poweredOn
- guest_name: Server2
guest_fullname: FreeBSD Pre-11 versions (64-bit)
power_state: poweredOn
Which yields the recap:
PLAY [localhost] **************************************************************************************************
TASK [read_csv] ***************************************************************************************************
ok: [localhost]
TASK [debug] ******************************************************************************************************
ok: [localhost] => (item=Server1) =>
msg: For Server1, the state is poweredOn
ok: [localhost] => (item=Server2) =>
msg: For Server2, the state is poweredOn
PLAY RECAP ********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Parsing CSV with Ansible

I need to parse a CSV with Ansible. The issue I'm facing is that I don't have fixed columns, they are variables.
For example: I need to extract network informations and to use them for configure vmware guests networks. In this case I can have a CSV with these:
(other columns) Nic1_Vlan Nic1_IP Nic1_MASK Nic1_Scope (other columns)
Otherwise I can have this:
(other columns) Nic1_Vlan Nic1_IP Nic1_MASK Nic1_Scope Nic2_Vlan Nic2_IP Nic2_MASK Nic2_Scope (other columns )
I can have 1 nic only or 3 in one shot.
Do you have any hint about this scenario? Would you use jinja2 or do you think is better to use a json/yaml variable files?
Thank you for any reply!!
Regards
For example the play below
- hosts: localhost
vars:
rec_len: 4
delimeter: ' '
tasks:
- set_fact:
nics: "{{ (lookup('file', 'data.csv')|trim).split(delimeter) }}"
- set_fact:
len: "{{ (nics|length/rec_len)|int }}"
- set_fact:
nics_list: "{{ nics_list|default([]) + [[
nics[(item*rec_len)|int],
nics[(item*rec_len+1)|int],
nics[(item*rec_len+2)|int],
nics[(item*rec_len+3)|int] ]] }}"
loop: "{{ range(0, len|int)|list }}"
- debug:
msg: "{{ item }}"
loop: "{{ nics_list }}"
gives
ok: [localhost] => (item=[u'Nic1_Vlan', u'Nic1_IP', u'Nic1_MASK', u'Nic1_Scope']) => {
"msg": [
"Nic1_Vlan",
"Nic1_IP",
"Nic1_MASK",
"Nic1_Scope"
]
}
ok: [localhost] => (item=[u'Nic2_Vlan', u'Nic2_IP', u'Nic2_MASK', u'Nic2_Scope']) => {
"msg": [
"Nic2_Vlan",
"Nic2_IP",
"Nic2_MASK",
"Nic2_Scope"
]
}
Is this what you're looking for?

How to loop through two lists & add conditional statement to execute something when one condition is true

I have a question, i got the sid list & the DB open_mode, i am trying to run a sql script on the DB when below two conditions satisfy:
DB name should end with '1'.
DB Open_Mode should be 'READ WRITE'.
i am using ansible dynamic inventory to get the sid's from the host & loop through that list, but i am unable to use the two conditions to work through the conditions i am adding.
- hosts: all
gather_facts: false
strategy: free
tasks:
- include_vars: roles/oracle/vars/install_vars.yaml
vars:
var_list:
- script_name
- set_fact:
ORACLE_HOMES_DIR: "/u01/app/oracle/product"
DB_HOME: "{{ ORACLE_HOMES_DIR }}/{{ ORACLE_VERSION }}/dbinst_1"
- name: Copy script to host
copy:
src: "{{ playbook_dir }}/{{ script_name }}"
dest: "/tmp/"
owner: "{{ USER_ORACLE }}"
group: "{{ GROUP_ORACLE }}"
mode: 0755
- name: Verify if the DB is open READ WRITE (or) not
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ sid }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "echo \"set pagesize 0\n select trim(open_mode) from v\\$database;\" | {{ORACLE_HOME}}/bin/sqlplus -S / as sysdba"
with_items: "{{ hostvars[inventory_hostname]['sid_list'] }}"
loop_control:
loop_var: sid
register: om
- name: Get list of sid that are open in READ WRITE mode
set_fact:
sid_list: "{{ om.results | selectattr('sid','search','1$') | map (attribute='sid') | list }}"
- name: Get the OPEN MODE output of the sid's from the list
set_fact:
om_out: "{{ om.results | selectattr('stdout') | map (attribute='stdout') | list }}"
- name: execute sql script
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ item.0 }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "{{ ORACLE_HOME }}/bin/sqlplus / as sysdba #/tmp/{{ script_name }}"
when: item.1 == 'READ WRITE'
with_together:
- "{{ sid_list }}"
- "{{ om_out }}"
I am expected the playbook to execute the SQL script on the DB, but i am getting error saying "conditional result was False"
TASK [Get list of sid that are open in READ WRITE mode] ****************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:44
ok: [dwracdb1] => {
"ansible_facts": {
"sid_list": [
"abhitest1",
"dw1"
]
},
"changed": false
}
TASK [Get the SQL output from all the sid's] ***************************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:48
ok: [dwracdb1] => {
"ansible_facts": {
"om_out": [
"READ WRITE",
"READ WRITE"
]
},
"changed": false
}
TASK [Print om out] ****************************************************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:52
ok: [dwracdb1] => (item=[u'abhitest1', u'READ WRITE']) => {
"msg": "sid output is abhitest1 om output is READ WRITE"
}
ok: [dwracdb1] => (item=[u'dw1', u'READ WRITE']) => {
"msg": "sid output is dw1 om output is READ WRITE"
}
TASK [execute sql script] **********************************************************************************************************************************************************************************
task path: /uhome/abhi/ansible/sql_script_execute.yaml:61
fatal: [dwracdb1]: FAILED! => {
"msg": "The conditional check 'item.1 == 'READ WRITE'' failed. The error was: error while evaluating conditional (item.1 == 'READ WRITE'): 'item' is undefined\n\nThe error appears to have been in '/uhome/abhi/ansible/sql_script_execute.yaml': line 61, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: execute sql script\n ^ here\n"
}
First, a formatting hint: move the when condition on your block up to the top so that it's obvious what it controls. When you put it at the bottom it's not obvious:
- block:
when:
- hostvars[inventory_hostname]['sid_list'] is defined
In this task, you're collecting multiple results (one for each entry in sid_list):
- name: Verify if the DB is open READ WRITE (or) not
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ sid }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "echo \"set pagesize 0\n select trim(open_mode) from v\\$database;\" | {{ORACLE_HOME}}/bin/sqlplus -S / as sysdba"
with_items: "{{ hostvars[inventory_hostname]['sid_list'] }}"
loop_control:
loop_var: sid
register: om
- name: Get list of sid that are open in READ WRITE mode
set_fact:
sid_list: "{{ om.results | selectattr('sid','search','1$') | map (attribute='sid') | list }}"
That's why, when you run this task, you end up with a list of results:
- name: Get the SQL output from all the sid's
set_fact:
om_out: "{{ om.results | selectattr(\"stdout\",'equalto','READ WRITE') | map (attribute='stdout') | list }}"
You're doing the correct thing here in your debug task using with_together: you need that in order to associate a result in om_out with one of the entries in sid_list:
- name: Print om out
debug:
msg: sid output is {{ item.0 }} om output is {{ item.1 }}
with_together:
- "{{ sid_list }}"
- "{{ om_out }}"
You should do the same thing when trying to execute your sql script. Get rid of the block, because you only have a single task and you can't loop a block:
- name: execute sql script
become_user: "{{ USER_ORACLE }}"
environment:
ORACLE_SID: "{{ sid.0 }}"
ORACLE_HOME: "{{ ORACLE_HOME }}"
shell: "{{ ORACLE_HOME }}/bin/sqlplus / as sysdba #/tmp/{{ script_name }}"
when:
- sid.1 == 'READ WRITE'
with_together:
- "{{ sid_list }}"
- "{{ om_out }}"
loop_control:
loop_var: sid
In this loop, sid.0 will be the value from sid_list, and sid.1 will be the corresponding value from om_out.

Ansible parse json and read result into different variables

I've set up a task which queries the github api meta endpoint and returns the following
{
"verifiable_password_authentication": true,
"github_services_sha": "f9e3a6b98d76d9964a6613d581164039b8d54d89",
"hooks": [
"192.30.252.0/22",
"185.199.108.0/22",
"140.82.112.0/20"
],
"git": [
"192.30.252.0/22",
"185.199.108.0/22",
"140.82.112.0/20",
"13.229.188.59/32",
"13.250.177.223/32",
"18.194.104.89/32",
"18.195.85.27/32",
"35.159.8.160/32",
"52.74.223.119/32"
],
"pages": [
"192.30.252.153/32",
"192.30.252.154/32",
"185.199.108.153/32",
"185.199.109.153/32",
"185.199.110.153/32",
"185.199.111.153/32"
],
"importer": [
"54.87.5.173",
"54.166.52.62",
"23.20.92.3"
]
}
What I need to do is get the 3 hook IPs and read them each into their own variable.
I've tried a couple of solutions i've found around but nothing is seeming to work for me.
I've got as far as drilling down into the json so i'm being returned only the 3 IPs, but how do I get them out and into variables individually?
i gave it a shot using j2 syntax in the variable name part, and - TIL - looks like the jinja2 syntax is allowed in that part as well!
please see playbook to process the hooks list variable and assign to variables variable_1, variable_2, variable_3 and so on:
- hosts: localhost
gather_facts: false
vars:
counter: 1
hooks:
- 192.30.252.0/22
- 185.199.108.0/22
- 140.82.112.0/20
tasks:
- name: populate vars
set_fact:
variable_{{counter}}: "{{ item }}"
counter: "{{ counter | int + 1 }}"
with_items:
- "{{ hooks }}"
- name: print vars
debug:
msg: "variable_1: {{variable_1}}, variable_2: {{variable_2}}, variable_3: {{variable_3}}"
and the output:
[root#optima-ansible ILIAS]# ansible-playbook 50257063.yml
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [populate vars] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=192.30.252.0/22)
ok: [localhost] => (item=185.199.108.0/22)
ok: [localhost] => (item=140.82.112.0/20)
TASK [print vars] **********************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "variable_1: 192.30.252.0/22, variable_2: 185.199.108.0/22, variable_3: 140.82.112.0/20"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
[root#optima-ansible ILIAS]#
hope it helps
UPDATE:
something weird i noticed - also TIL - is that if you reverse the lines:
variable_{{counter}}: "{{ item }}"
counter: "{{ counter | int + 1 }}"
to:
counter: "{{ counter | int + 1 }}"
variable_{{counter}}: "{{ item }}"
you still end up with the same variable names, _1 to _3, while i would expect to get _2 to _4.
I guess ansible loops behave differently than expected from other programming languages.
---
- name: Query Github Meta API and get Hook Ips
hosts: local
connection: local
vars:
counter: 1
tasks:
- name: Query API
uri:
url: https://api.github.com/meta
return_content: yes
register: response
- name: Populate Hook Variables
set_fact:
webhook_ip_{{counter}}: "{{ item }}"
counter: "{{ counter | int + 1 }}"
with_items:
- "{{ response['json']['hooks'] }}"
- name: print vars
debug:
msg: "Variable_1: {{ webhook_ip_1 }}, Variable_2: {{ webhook_ip_2 }}, Variable_3: {{ webhook_ip_3 }}"
Works with GitHub Webhook IPs in a loop
- name: get request to github
uri:
url: "https://api.github.com/meta"
method: GET
return_content: yes
status_code: 200
headers:
Content-Type: "application/json"
#X-Auth-Token: "0010101010"
body_format: json
register: json_response
- name: GitHub webhook IPs
debug:
msg: "{{ item }}"
with_items: "{{ (json_response.content | from_json).hooks }}"