How to use mongoDB collection output as variables in ansible - json

I can able to print the mongodb data using ansible. but here my requirement is to use the printed data as variables in ansible.
here is the output I'm getting ansible playbook output:
here is my ansible playbook.
---
- hosts: localhost
vars:
- i: "db.repo.find({ $and: [{'product': 'Admin'}, {'env':'SHK'}] }).pretty()"
tasks:
- name: Printing the retrieved data
command: mongo Advantage --quiet --eval "{{i}}"
register: temp
- name: Printing the retrieved data
set_fact:
"{{item}}"
with_items:
- [ "{{temp.stdout.split('\t')[0] }}", "{{temp.stdout.split('\t')[1] }}", "{{temp.stdout.split('\t')[2] }}", "{{temp.stdout.split('\t')[3] }}", "{{temp.stdout.split('\t')[4] }}", "{{temp.stdout.split('\t')[5] }}", "{{temp.stdout.split('\t')[6] }}", "{{temp.stdout.split('\t')[7] }}", "{{temp.stdout.split('\t')[8] }}", "{{temp.stdout.split('\t')[9] }}" ]
- include: /etc/ansible/roles/patchdeployment_3_11_2/tasks/applypatch/applypatch_windows_websphere.yml PR_ID={{PR_ID}}
#- include: /etc/ansible/roles/patchdeployment_3_11_2/tasks/applypatch/applypatch_linux_websphere.yml
please help me on the same.

Make mongo output pure JSON friendly by disabling all fields with custom types (like _id) in query.
Then use from_json Ansible filter to parse output.
- hosts: localhost
vars:
qry: "db.repo.findOne({ $and: [{'product': 'Admin'}, {'env':'SHK'}] },{_id:false})"
tasks:
- name: Get data
command: mongo Advantage --quiet --eval "{{qry}}"
register: temp
- name: Save parsed data
set_fact:
mongo_result: "{{ temp.stdout | from_json }}"
- name: Print some data
debug:
var: mongo_result.appName

Related

Using Key(#) function to extract keys from an object in Ansible

I have a file file.sub which contains this JSON object {"kas_sub.test1": "true", "kas_sub.test2": "true"}. I would extract the keys and to get this: kas_sub.test1 kas_sub.test1.
When i try
- shell: 'cat path/to/file.sub'
register: file1
- debug:
var: file1.stdout_lines
I got:
TASK [shell] *****************************************************************************************************************
changed: [ansible4]
changed: [control]
TASK [debug] *****************************************************************************************************************
ok: [control] => {
"file1.stdout_lines": [
"{\"kas_sub.tes1\": \"true\", \"kas_sub.test2\": \"true\"}"
]
}
So it's not conserving the same JSON format because i would use the json_query filter.
- debug:
msg: "{{ file1.stdout_lines| json_query(value1)}}"
vars:
value1: "#[?keys(#)]"
keys(#)function doesn't return anything
ok: [control] => {
"msg": ""
}
note: taking for granted you want to read a file on the target machine
In a nutshell:
- hosts: your_group
gather_facts: false
vars:
file_to_read: /path/to/file.sub
tasks:
- name: slurp file content from target
slurp:
src: "{{ file_to_read }}"
register: slurped_file
- name: display keys from json inside file
debug:
msg: "{{ (slurped_file.content | b64decode | from_json).keys() }}"
Given the file
shell> cat /tmp/file.sub
{"kas_sub.test1": "true", "kas_sub.test2": "true"}
Use jq (if you can). For example, get the keys
- command: jq 'keys' /tmp/file.sub
register: result
and convert them to a list
keys: "{{ result.stdout|from_yaml }}"
gives
keys:
- kas_sub.test1
- kas_sub.test2
Example of a complete playbook
- hosts: localhost
vars:
keys: "{{ result.stdout|from_yaml }}"
tasks:
- command: jq 'keys' /tmp/file.sub
register: result
- debug:
var: keys

Ansible module returns non json data

Ansible Azure module returns the data in a weird format Double single quoted characters.
''network_interface_names'': [''Ubuntu915'']
As a result I can't use this anywhere to filter other resources.
Any idea what can be done to fix this or there make it json?
- name: Get facts by name
azure_rm_virtualmachine_info:
resource_group: "{{ resource_group }}"
name: "{{ vm_name }}"
register: azure_vm_info
- name: "Network interface List"
set_fact:
azure_vm_network_interface: "{{ [ azure_vm_info.vms[0].network_interface_names ] }}"
- name: Print Azure VM Info
debug:
msg: "Azure VM INFO: {{ azure_vm_info.vms[0].network_interface_names }}"
Output:
TASK [Print Azure VM Info] ****************************************************************************************************************************************************************************************************************************************************
ok: [localhost] =>
msg: 'Azure VM INFO: [''Ubuntu915'']'
Figured it out.
In ansible.cfg I had set stdout_callback = yaml. changing it json fixed the issue

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

Read and use values from csv file

I'm struggling to find a solution for LAB project I'm working on right now. I'd like to use csv file to populate variables in my playbook when configuring Cisco ACI. I'm using read_csv module and the latest Ansible 2.9
Sample CSV:
tenant1;tenant1-vrf;tenant1-app
tenant1;tenant1-vrf2;tenant1-app2
tenant2;;tenant2-vrf2;tenant2-app2
UPDATE - based on Sai's code I'm not far from reaching the objective. This is the full tasks code.
UPDATE2 - eventually I went back to the read_csv module. It works nice even for complex things. Hope it helps someone as an example.
tasks:
- name: Read tenant from CSV file and return a list
read_csv:
path: "{{ filename }}"
delimiter: ;
register: tenantconfig
- name: TASK 1 - BUILD tenant
aci_tenant:
<<: *aci_login
validate_certs: no
use_ssl: yes
tenant: "{{ item.tenant }}"
description: "{{ item.tenant }} creation as per {{ filename }} source file"
state: present
with_items: "{{ tenantconfig.list }}"
- name: TASK 2 - BUILD Routing {{ vrf }} for {{ tenant }} on {{ apic_host }}
aci_vrf:
<<: *aci_login
state: present
validate_certs: no
use_ssl: yes
tenant: "{{ item.tenant }}"
vrf: "{{ item.vrf }}"
description: "{{ item.vrf }}"
with_items: "{{ tenantconfig.list }}"
i have changed answer to dynamically process your input file and assign tenant,vrf fields where ever you want to call.
tasks:
- name: split fields
command: /usr/bin/awk -F';' '!/^#/ && !/^$/ { print $1 }' tenant1.csv
register: tenants_out
#- debug:
# msg: "{{ lookup('csvfile', item + ' file=tenant1.csv delimiter=; col=0') }}"
# with_items: "{{ tenants_out.stdout_lines }}"
- name: TASK 1 - BUILD tenant
aci_tenant:
state: present
tenant: "{{ lookup('csvfile', item + ' file=tenant1.csv delimiter=; col=0') }}"
vrf: "{{ lookup('csvfile', item + ' file=tenant1.csv delimiter=; col=1') }}"
with_items: "{{ tenants_out.stdout_lines }}"
input file lines are spitted using initial task, and you can direct specify required tenent, vrf values using "with_items" looping. this is useful if your input file has multiple lines as well.

Add a new key-value to a json file using Ansible

I'm using Ansible to automate some configuration steps for my application VM, but having difficult to insert a new key-value to an existing json file on the remote host.
Say I have this json file:
{
"foo": "bar"
}
And I want to insert a new key value pair to make the file become:
{
"foo": "bar",
"hello": "world"
}
Since json format is not line based, I'm excluding lineinfile module from my options. Also, I would prefer not to use any external modules. Google keeps giving me examples to show how to read json file, but nothing about change json values and write them back to file. Really appreciate your help please!
since the file is of json format, you could import the file to a variable, append the extra key:value pairs you want, and then write back to the filesystem.
here is a way to do it:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
tasks:
- name: load var from file
include_vars:
file: /tmp/var.json
name: imported_var
- debug:
var: imported_var
- name: append more key/values
set_fact:
imported_var: "{{ imported_var | default([]) | combine({ 'hello': 'world' }) }}"
- debug:
var: imported_var
- name: write var to file
copy:
content: "{{ imported_var | to_nice_json }}"
dest: /tmp/final.json
UPDATE:
as OP updated, the code should work towards remote host, in this case we cant use included_vars or lookups. We could use the slurp module.
NEW code for remote hosts:
---
- hosts: greenhat
# connection: local
gather_facts: false
vars:
tasks:
- name: load var from file
slurp:
src: /tmp/var.json
register: imported_var
- debug:
msg: "{{ imported_var.content|b64decode|from_json }}"
- name: append more key/values
set_fact:
imported_var: "{{ imported_var.content|b64decode|from_json | default([]) | combine({ 'hello': 'world' }) }}"
- debug:
var: imported_var
- name: write var to file
copy:
content: "{{ imported_var | to_nice_json }}"
dest: /tmp/final.json
hope it helps
ilias-sp's solution is great!
In my case, it lacked the case when we may have to create a base json file.
So I had to add this task in the beginning of the play:
- name: Ensure json file exists
copy:
content: "{}"
dest: /tmp/var.json
force: false
For people who are OK with custom ansible modules: https://github.com/ParticleDecay/ansible-jsonpatch works great!
With this you can simply do:
- name: append key/values
json_patch:
src: /tmp/var.json
operations:
- op: add
path: "/hello"
value: "world"
pretty: yes
create: yes
- name: update log
copy:
content: "{{ log | to_nice_json}}"
dest: "{{ log_file }}"
vars:
log: "{{ (lookup('file', log_file) | from_json) + ([{'job': (build_id if build_id != '' else 'dev'), 'keystore': ks, 'timestamp': ansible_date_time.iso8601}]) }}"
log_file: log/log.json
build_id: "{{ lookup('ENV', 'BUILD_ID') }}"
tags: log