Ansible - is it possible to register multiple variable? - ansible

I have a python script which is returning / print two lists.
test.py
def getHosts():
Pool1=[x.x.x.x, x.x.x.x]
Pool2=[x.x.x.x, x.x.x.x]
Print pool1,pool2
Return pool1,pool2
getHosts()
My playbook looks like:
-task:
name: get the hosts
command: /test.py
register: result
Now, is it possible to fetch out the pool1 and pool2 seperately from the registered variable result ? If yes, please show me an example.
Any help or suggestions will be highly appreciated.

Produce JSON and feed it to Ansible. It will automatically create an appropriate data structure:
---
- hosts: localhost
gather_facts: no
connection: local
tasks:
- command: 'echo { \"Pool1\": \"[x.x.x.x, x.x.x.x]\", \"Pool2\": \"[x.x.x.x, x.x.x.x]\" }'
register: my_output
- set_fact:
my_variable: "{{ my_output.stdout | from_json }}"
- debug:
msg: "Pool1 is {{ my_variable.Pool1 }} and Pool2 is {{ my_variable.Pool2 }}"
Result:
ok: [localhost] => {
"msg": "Pool1 is [x.x.x.x, x.x.x.x] and Pool2 is [x.x.x.x, x.x.x.x]"
}
Depending on how you later use the variable, you might/might not need to from_json filter (see this).

Related

Ansible hostvars variable undefined when debugging/templating etc

I'm trying to dynamically generate part of an /etc/hosts file with ansible, by gathering facts from all the hosts and looping through the resulting hostvars to grab the IP of the second interface (I need a private IP on this interface, rather than the public IP of the other interface)
I can grab this information from a single host using the following plays:
- name: play 1
hosts: all
- name: play 2
hosts: localhost
connection: local
become: no
tasks:
- debug:
var: hostvars['mysinglehost'].ansible_all_ipv4_addresses[1]
...but what I'd like to do is loop through all the hosts and get this value from every host, eventually writing this information to my hosts file, but I'd settle for just getting some debug information :)
I tried
...
- debug:
var: hostvars[item].ansible_all_ipv4_addresses[1]
with_inventory_hostnames:
- all
...
...which gives the output I expect, yet when I try to output a msg with this debug task:
...
- debug:
msg: "{{ hostvars[item].ansible_all_ipv4_addresses[1] }}"
with_inventory_hostnames:
- all
...
I get the following error (as I do when I attempt to e.g. write to a file using lineinfile):
fatal: [localhost]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'ansible_all_ipv4_addresses'
I'm not sure why this is happening, as it's trying to reference the exact same variable. Is there a way to do this?
I've not tested this, but in principle, perhaps this would work?
- hosts: all
- hosts: localhost
gather_facts: false
tasks:
- set_fact:
my_hosts: |
{
{% for a_host in hostvars | dict2items %}
"{{ a_host.key }}": "{{ a_host.value.ansible_all_ipv4_addresses[1] }}",
{% endfor %}
}
Then, instead of looping over hostvars, you just loop over my_hosts?

Ansible - check variable type

Apparently, according to several hours of searching nobody has encountered this use-case:
Its simple - I would like to execute some ansible logic depending on variable type. Basically equivalent of e.g. instanceof(dict, var_name) but in Ansible:
- name: test
debug:
msg: "{{ instanceof(var_name, dict) | ternary('is a dictionary', 'is something else') }}"
Is there any way this can be done?
Q: "Execute some ansible logic depending on the variable type."
A: The tests including mapping work as expected. For example, the tasks below
- set_fact:
myvar:
key1: value1
- debug:
msg: "{{ (myvar is mapping)|
ternary('is a dictionary', 'is something else') }}"
give
msg: is a dictionary
Q: "Ansible - check variable type"
A: An option would be to discover the data type and dynamically include_tasks. For example, the tasks below
shell> cat tasks-int
- debug:
msg: Processing integer {{ item }}
shell> cat tasks-str
- debug:
msg: Processing string {{ item }}
shell> cat tasks-list
- debug:
msg: Processing list {{ item }}
shell> cat tasks-dict
- debug:
msg: Processing dictionary {{ item }}
with this playbook
- hosts: localhost
vars:
test:
- 123
- '123'
- [a,b,c]
- {key1: value1}
tasks:
- include_tasks: "tasks-{{ item|type_debug }}"
loop: "{{ test }}"
give (abridged)
msg: Processing integer 123
msg: Processing string 123
msg: Processing list ['a', 'b', 'c']
msg: 'Processing dictionary {''key1'': ''value1''}'
If you want to simulate the switch statement create a dictionary
case:
int: tasks-int
str: tasks-str
list: tasks-list
dict: tasks-dict
default: tasks-default
and use it in the include
- include_tasks: "{{ case[item|type_debug]|d(case.default) }}"
loop: "{{ test }}"
Since Ansible version 2.3 there is type_debug:
- name: test
debug:
msg: "{{ (( var_name | type_debug) == 'dict') | ternary('is a dictionary', 'is something else') }}"
Note that the docs state a preference for 'type tests'.
Older question, but you can do this easily with a Python custom filter plugin. Granted it would give you Python specific types, but that may be fine for your use case.
This could work. It just needs to be placed in a folder named filter_plugins alongside your playbook or role.
import sys
if sys.version_info[0] < 3:
raise Exception("Must be using Python 3")
def get_type(var, **kwargs):
return type(var)
class FilterModule(object):
def filters(self):
return {
"get_type": get_type
}
Then in your playbook:
- name: test
debug:
msg: "{{ var_name | get_type }}"

Filter hostvar by special propery

I have host.yml like this
---
all:
hosts:
server-a:
server_dc: "Hetzner"
ansible_host: 192.168.1.1
server-b:
server_dc: "OVH"
ansible_host: 192.168.1.2
And play book debug is:
- name: sample
debug:
var: hostvars
And all hostvars debug success.
How to get same hostvars variable but filtered. Any of that server_dc is equal OVH
I dont want to iterate for template, i just one new filtered variable that contain all other properies.
I need another variable that i debug see this output:
['server-b']
This I believe meets your requirement (removing 'no_log: true', will result in the complete dictionary being printed in your playbook output):
- set_fact:
filtered_hosts: "{{ filtered_hosts | default({}) | combine({item.key: item.value}) }}"
when: "item.value.server_dc == 'OVH'"
with_dict: "{{ hostvars }}"
no_log: true
- debug:
var: filtered_hosts

Return Variable from Included Ansible Playbook

I have seen how to register variables within tasks in an ansible playbook and then use those variables elsewhere in the same playbook, but can you register a variable in an included playbook and then access those variables back in the original playbook?
Here is what I am trying to accomplish:
This is my main playbook:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
roles:
- some_role
sub-playbook.yml:
---
- hosts: localhost
tasks:
- name: Collect info from Jenkins Job
script: whatever.py --url "{{ job_url }}"
register: jenkins_artifacts
I'd like to be able to access the jenkins_artifacts results back in main_playbook if possible. I know you can access it from other hosts in the same playbook like this: "{{ hostvars['localhost']['jenkins_artifacts'].stdout_lines }}"
Is it the same idea for sharing across playbooks?
I'm confused what this question is about. Just use the variable name jenkins_artifacts:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
debug:
var: jenkins_artifacts
This might seem complicated but I love doing this in my Playbooks:
rc defines the name of the variable which contains the return value
ar gives the arguments to the include tasks
master.yml:
- name: verify_os
include_tasks: "verify_os/main.yml"
vars:
verify_os:
rc: "isos_present"
ar:
image: "{{ os.ar.to_os }}"
verify_os/main.yml:
---
- name: check image on device
ios_command:
commands:
- "sh bootflash: | inc {{ verify_os.ar.image }}"
register: image_check
- name: check if available
shell: "printf '{{ image_check.stdout_lines[0][0] }}\n' | grep {{ verify_os.ar.image }} | wc -l"
register: image_available
delegate_to: localhost
- set_fact: { "{{ verify_os.rc }}": "{{ true if image_available.stdout == '1' else false }}" }
...
I can now use the isos_present variable anywhere in the master.yml to access the returned value.

iteration using with_items and register

Looking for help with a problem I've been struggling with for a few hours. I want to iterate over a list, run a command, register the output for each command and then iterate with debug over each unique registers {{ someregister }}.stdout
For example, the following code will spit out "msg": "1" and "msg": "2"
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: result
with_items: "{{ numbers }}"
- debug: msg={{ item.stdout }}
with_items: "{{ result.results }}"
If however, I try and capture the output of a command in a register variable that is named using with_list, I am having trouble accessing the list or the elements within it. For example, altering the code slightly to:
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: "{{ item.name }}"
with_items: "{{ numbers }}"
- debug: var={{ item.name.stdout }}
with_items: "{{ numbers }}"
Gives me:
TASK [debug]
> ******************************************************************* fatal: [localhost]: FAILED! => {"failed": true, "msg": "'unicode
> object' has no attribute 'stdout'"}
Is it not possible to dynamically name the register the output of a command which can then be called later on in the play? I would like each iteration of the command and its subsequent register name to be accessed uniquely, e.g, given the last example I would expect there to be variables registered called "first" and "second" but there aren't.
Taking away the with_items from the debug stanza, and just explicitly defining the var or message using first.stdout returns "undefined".
Ansible version is 2.0.2.0 on Centos 7_2.
Thanks in advance.
OK so I found a post on stackoverflow that helped me better understand what is going on here and how to access the elements in result.results.
The resultant code I ended up with was:
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: echo_out
with_items: "{{ numbers }}"
- debug: msg="item.item={{item.item.name}}, item.stdout={{item.stdout}}"
with_items: "{{ echo_out.results }}"
Which gave me the desired result:
"msg": "item.item=first, item.stdout=1"
"msg": "item.item=second, item.stdout=2"
I am not sure if I understand the question correctly, but maybe this can help:
- debug: msg="{{ item.stdout }}"
with_items: echo_out.results
Please note that Ansible will print each item and the msg both - so you need to look carefully for a line that looks like "msg": "2".

Resources