Collecting variables from multiple hosts in a playbook - ansible

I have a playbook that collects a list of information from multiple hosts, and I want to collect those lists together into a single variable.
So for example, my output might look like this:
ok: [ALBSWA052] => {
"myvar": [
"ALBSWA052 exists",
"ALBSWA052 is secondary"
]
}
ok: [ALBSWA051] => {
"myvar": [
"ALBSWA051 exists",
"ALBSWA051 is primary"
]
}
and I'd like to combine them into a list like
"myvar": [
"ALBSWA052 exists",
"ALBSWA052 is secondary"
"ALBSWA051 exists",
"ALBSWA051 is primary"
]
Is there an elegant way to do this? The only way I can think of is to write it into a file and read it back in.

For example
- set_fact:
myvar: "{{ ansible_play_hosts|map('extract', hostvars, 'myvar')|flatten }}"
run_once: true
gives the expected result
myvar:
- ALBSWA051 exists
- ALBSWA051 is secondary
- ALBSWA052 exists
- ALBSWA052 is secondary

Related

Difficulty Parsing Ansible Output

[ansible 2.9.6, Ubuntu 20.04]
This seems pretty straightforward, but I keep getting an error message saying:
fatal: [192.168.254.100]: FAILED! => {"msg": "template error while templating string: expected name or number. String: {{ipv4addrs.'host[0]'}}"}
Here's my ansible playbook:
---
- hosts: nios
connection: local
vars:
nios_provider:
host: 192.x.x.x
username: xxx
password: xxx
wapi_version: "2.11.2"
tasks:
- name: Find client app server records
set_fact:
recs: "{{ lookup('nios', 'record:host', filter={'name~':'sdk' }, provider=nios_provider) }}"
- name: check return
debug:
msg: "{{ recs }}"
- name: get host name
debug:
var: ipv4addrs.'host[0]'
And here's the output:
TASK [check return] **************************************************************************************************************************************************************
ok: [192.x.x.x] => {
"msg": [
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5lYWdsZWFjY2Vzcy5zZGstZDAwMQ:sdk-d001.somename.com/default",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQuY29tLmVhZ2xlYWNjZXNzLnNkay1kMDAxLjEwLjcwLjAuMS4:10.70.0.1/sdk-d001.somename.com/default",
"configure_for_dhcp": false,
"host": "sdk-d001.somename.com",
"ipv4addr": "10.70.0.1"
}
],
"name": "sdk-d001.somename.com",
"view": "default"
},
{
"_ref": "record:host/ZG5zLmhvc3QkLl9kZWZhdWx0LmNvbS5lYWdsZWFjY2Vzcy5zZGstZDAwMg:sdk-d002.somename.com/default",
"ipv4addrs": [
{
"_ref": "record:host_ipv4addr/ZG5zLmhvc3RfYWRkcmVzcyQuX2RlZmF1bHQuY29tLmVhZ2xlYWNjZXNzLnNkay1kMDAyLjEwLjcwLjAuMi4:10.70.0.2/sdk-d002.somename.com/default",
"configure_for_dhcp": false,
"host": "sdk-d002.somename.com",
"ipv4addr": "10.70.0.2"
}
],
"name": "sdk-d002.somename.com",
"view": "default"
}
]
}
TASK [get host name] ***************************************************************************************************************************************************************
fatal: [192.168.254.100]: FAILED! => {"msg": "template error while templating string: expected name or number. String: {{ipv4addrs.'host[0]'}}"}
My objective is to find all of the host names beginning with (for example) "sdk". There may be 1 or several. And then I want to get the full name and ip address captured as variables. I have tried lots of different options: rec.ipv4addrs[0].host, ipv4addrs[0].host, rec.ipv4addrs[0].'host', rec.ipv4addrs[0].['host'] . . . but I cannot find the proper syntax.
This appears to be an array of dict blocks with an ipv4addrs array within it. So the first thing I tried was "ipv4addrs[0]['host']" with no joy.
Thanks in advance for the help.
You're trying to make use of a variable called ipv4addrs, but you
haven't defined any variables with that name. From the previous
debug task you apparently have a variable named recs, which is a
list of dictionaries each of which contains an ipv4addrs attribute.
If you wanted to look up the host attribute for each record in
recs, you could write something like this:
- name: get host name
debug:
var: item.ipv4addrs[0].host
loop: "{{ recs }}"
If you wanted the ipv4addrs.host value from the first record, you
could write:
- name: get host name
debug:
var: recs[0].ipv4addrs[0].host
The error you're seeing stems from the fact that the expression
ipv4addrs.'host[0]' doesn't make any sense; that's not a syntax that
Ansible supports. You can't use a string with dot-notation like that.

ansible JEMSPATH errors while parasing ansible facts

i am trying to filter all the strings which contains "RegButton-" from the below ansible facts and use the output as list of items in the next play.
trying to use json_query filter but it is failing with below error
ansible fact
{
"ansible_facts": {
"srcgrpname": [
"RegButton-48773",
"test_vio",
"RegButton-23395",
"RegButton-520859",
"RegButton-743141",
"RegButton-297578",
"RegButton-186156"
]
},
"changed": false
}
playbook entry
- name: "Filter Regbutton policy Names"
set_fact:
srcgrpname2: "{{ resultid1 | json_query(query) }}"
vars:
query: "ansible_facts.srcgrpname[?contains(#, 'RegButton-') == `true`]"
Error that i am receiving.
{
"msg": "JMESPathError in json_query filter plugin:\nIn function contains(), invalid type for value: RegButton-48773, expected one of: ['array', 'string'], received: \"unknown\"",
"_ansible_no_log": false
}
It's possible to use select and regex. For example the tasks below
- set_fact:
srcgrpname2: "{{ ansible_facts.srcgrpname|
select('regex', '^RegButton-(.*)$')|
list }}"
- debug:
var: srcgrpname2
give
"srcgrpname2": [
"RegButton-48773",
"RegButton-23395",
"RegButton-520859",
"RegButton-743141",
"RegButton-297578",
"RegButton-186156"
]
Notes
It's an open issue with contains.
json_query filter fails when using the functions "contains", "starts_with", others #27299
Allow for subclassed types #158
It's reproducible "RegButton-48773, expected one of: ['array', 'string'], received: unknown"
See json format query with contains

Fetch a value from the Output of a task and debug the message in Ansible Playbook

I need to Fetch the ipv4Address value from the Below JSON Output using set_fact. Finally ending up with some errors. Could you please suggest me some best method.
JSON Output:
"result_dns": {
"ansible_facts": {
"azure_dnsrecordset": [
{
"etag": "440922d5-b234-488a-8cbc-97b77f0fef8f",
"id": "2",
"name": "test2",
"properties": {
"ARecords": [
{
"ipv4Address": "10.30.23.5"
}
],
"TTL": 3600,
"fqdn": "test2.testzone.com."
},
"type": "Microsoft.Network/dnszones/A"
I am using the set_fact as below to retrieve ipv4Address.
- name: name
set_fact:
host_name: "{{ result_dns.ansible_facts.azure_dnsrecordset map(attribute='ipv4Address') | list }}"
I am not able to filter the value by above method. Can you suggest me some best method to filter the Value.
I think you are getting snared by the fact that 'azure_dnsrecordset' contains a list of dictionaries. There is only one, but I guess the presumption is that there could in other conditions be multiple. This will do what you have asked, but once you have more than one record, you might find your requirements are more detailed than in this question:
# Presumes that you have a 'result_dns' variable set in Ansible containing the JSON
- set_fact:
ip_s: "{{ ( ip_s | default([]) ) + [ item.1.ipv4Address ] }}"
with_subelements:
- "{{ result_dns.ansible_facts.azure_dnsrecordset }}"
- properties.ARecords
- debug:
msg: "{{ ip_s }}"
with_subelements, extracts keys from a list of dictionaries.
This is just going to produce a list of IP addresses, which might not be useful in the case you have several record sets returned. If you do something like:
ip_s: "{{ ( ip_s | default([]) ) + [ { 'etag': item.0.etag, 'ip_address': item.1.ipv4Address } ] }}"
that will give you a list of dictionaries containing an 'etag' and 'ip_address' key, giving you a way to identify the recordsets they came from.
Hopefully enough to get you started.

Ansible loop through a set of dicts, register that list and then print the specific output [duplicate]

This question already has answers here:
Using set_facts and with_items together in Ansible
(7 answers)
Closed 4 years ago.
Here is what I am trying to do.
log onto a networking switch using the built in networking modules and send a command.
register that command as a var
print that var or use that var elsewhere inside of a playbook.
This seems simple right? But here are the issues I am facing.
First of all, I am logging into one device (currently) and then issuing a simple command inside of a loop (this becomes 2 commands, and 2 outputs).
I want to put the outputs from both of the commands into a list.
Next I would like to loop through this list and inspect the return of the value from each command (remember this is 2 outputs).
Here is the current play:
- name: Checking for free ports
nxos_command:
provider:
host: "{{inventory_hostname}}"
username: "{{user.stdout}}"
commands: "show run interface {{ item.interface }}"
when: device.ansible_facts.ansible_device_os == 'nxos'
loop: "{{ device_vars[inventory_hostname] }}"
register: ports
Then when I use debug, I get a bunch of data:
- debug:
var: item.stdout
loop: "{{ports.results}}"
register: ports_output
I then set the fact and then debug (print) once more:
- name: Setting var
set_fact:
port_list: "{{item.stdout}}"
loop: "{{ports.results}}"
- debug: var=port_list
The problem I am getting is that even though port_list is a list, ansible is only returning one value of that list. This is the last value/command from the initial play. So I am assuming it is being overwritten somewhere.
Here would be my desired output:
ok: [device1] => {
"port_list": [
"1st output from the device",
"2nd output from the device"
]
}
But all I can get is this:
ok: [device1] => {
"port_list": [
"2nd output from the device"
]
}
Here is are the vars I am declaring inside of my site.yml:
vars:
device_vars:
device1:
- interface: Ethernet1/1
description: "some description
vlan: 1
- interface: Ethernet1/2
description: "some description"
vlan: 1
port_list: []
I think my issue here is I am working with a dict of dicts of lists etc. and it doesn't seem that Ansible is very friendly with this.
I've managed to get the data into this format (omitted):
{
"ports":
{
"results":
[
{
"stdout":
[
"1st output from the device"
]
},
{
"stdout":
[
"2nd output from the device"
]
}
]
}
}
I've spent 3 days on this and can't seem to find a solution.
During the loop set_fact overwrites the variable, therefore you just see the last variable is being set. However you can also use set_fact to append the the previously assigned value and include all of variables as follows:
- name: Setting var
set_fact:
port_list: "{{ port_list|default([]) + [item.stdout] }}"
loop: "{{ports.results}}"
default([]) filter above is to assign an initial value to port_list variable.
Append the elements to the list
port_list: "{{ port_list + [item.stdout] }}"

Parsing Ansible output with Linux tools

I have a silly playbook which just runs a command to get the list of vhost in every host on webserver group. As all vhost are located in /var/www is easy to get the list of webs.
The problem is the way of Ansible returns the info. For example:
ok: [host1] => {
"var": {
"out.stdout_lines": [
"",
"host1.com"
]
}
}
ok: [host2] => {
"var": {
"out.stdout_lines": [
"",
"host2.com"
]
}
}
Do you know an easy way to just get the name of the vhosts? Using grep awk or something like that?
Dirty way: prepend each line in stdout_lines with some marker (e.g. ANSBLMRK_) before printing, so you have a list if "ANSBLMRK_host2.com", then grep and cut.
Good way: set ANSIBLE_STDOUT_CALLBACK=json and pipe it to jq.
Maybe just write a file containing the hosts names in your playbook and then use that later:
tasks:
- name: make output file
file: name=./list_of_hosts state=touch
- name: show my hostname
lineinfile: dest=./list_of_hosts line="{{ item }}"
with_items:
"{{ out.stdout_lines[1] }}"

Resources