Ansible test for variable use another is not defined - ansible

I use the ternary operator to return a variable if it is defined, otherwise another one:
{{ (variable1 is defined) | ternary(variable1, variable2) }}
It's a bit clumsy. Is there a better way to do this?

Try this one
- hosts: nodes
gather_facts: false
vars:
var1: value1
tasks:
- name: Show 1
debug: msg="{{ var1 | default('AAAAAA') }}"
- name: Show 2
debug: msg="{{ var2 | default('BBBBBB') }}"

Related

using ansible variable in task conditonal

When I use an Ansible variable in the failed_when conditional, Ansible complains and fails by telling me that I cannot use Jinja2 expressions with failed_when.
Below snippet does not work:
vars:
var1: "var"
tasks:
- name: "sth"
raw: "anothersth"
register: status
failed_when: var1 in status.stdout
I want to use a regex in the above example and use variable var1 within a regex. This also fails.
I want to fail the task if var1 is repeated in the beginning and at the end of the stdout_lines. I was thinking something like this:
failed_when: regex("^{{var1}}/.*/{{var1}}$") in status.stdout
I wonder whether there is a way to achieve what I intend to do?
===============================================================
Exact play and the description:
Let me explain what I am trying to do:
run a command with "raw" module and register the stdout_lines.
iterate over a list of items, where list is a fact of current ansible host
use failed_when to search a pattern in the registered stdout and fail the task if it is not found
peers_underlay is a list defined in the host file:
peers_underlay:
- ip: "172.16.1.2"
- ip: "172.16.1.6"
This is the play:
- hosts: leaf-2, spine-1
gather_facts: no
tags: [ verify ]
vars:
bgp_underlay: "show ip bgp summary"
tasks:
- name: "underlay bgp test"
raw: "{{ bgp_underlay }}"
register: underlay_status
with_items: "{{ peers_underlay }}"
failed_when: not regex(".*/{{ item['ip'] }}/.*/Estab/.*") in underlay_status.stdout_lines
Q: "Fail the task if var1 is repeated in the beginning and at the end."
A: It's possible to test strings. For example, use regex
- hosts: localhost
vars:
my_stdout1: 'ABC dlkjfsldkfjsldkfj ABC'
my_stdout2: 'alkdjflaksdjflakdjflaksjdflaksdjflasj'
var1: 'ABC'
my_regex: '^{{ var1 }}(.*){{ var1 }}$'
tasks:
- debug:
msg: my_regex match my_stdout1
when: my_stdout1 is regex(my_regex)
- debug:
msg: my_regex match my_stdout2
when: my_stdout2 is regex(my_regex)
gives
"msg": "my_regex match my_stdout1"
The particular task sequence would be
vars:
var1: "var"
my_regex: '^{{ var1 }}(.*){{ var1 }}$'
tasks:
- name: "sth"
raw: "anothersth"
register: status
failed_when: status.stdout is regex(my_regex)

VARIABLE IS NOT DEFINED when trying to register output in playbook

I'm trying to register a variable with the output to a query of a F5 pool and I'm getting this error:
"<type 'list'>": "VARIABLE IS NOT DEFINED!",
What is that I'm doing wrong?
Any help appreciated.
Thanks!
---
- name: GRAB F5 FACTS
hosts: f5
connection: local
gather_facts: no
tasks:
- name: Collect BIG-IP facts
bigip_device_facts:
gather_subset: ltm-pools
provider: "{{ prov }}"
register: bigip_device_facts
- name: FACTS OUTPUT
debug:
var: "{{ item.members | rejectattr('state', 'match', '^present$') | map(attribute='name') | list }}"
register: jkout
with_items: "{{ bigip_device_facts.ltm_pools }}"
when: item.full_path == "/Common/mypool"
- name: Set a variable
debug:
msg: "jkvar={{ jkout }}"
You are using the debug: module with the option var: and this expects a variable, not a jinja2 template.
So either change it to:
debug:
var: item.members
or
debug:
msg: "{{ item.members }}"
Like said by #dgw, the problem is with the var option of debug module.
https://docs.ansible.com/ansible/latest/modules/debug_module.html#parameters
This playbooks works:
- name: test rejectattr
hosts: localhost
gather_facts: no
vars:
members:
- { name: "one", state: "present" }
- { name: "two", state: "absent" }
- { name: "three", state: "present" }
tasks:
- name: FACTS OUTPUT
debug:
msg: "{{ members | rejectattr('state', 'match', '^present$') | map(attribute='name') | list }}"
Thanks for your responses. I'll investigate it further.
Apart from that, I think I've been able to solve it another way.
- name: FACTS OUTPUT
set_fact:
listado: "{{ item.members | rejectattr('state', 'match', '^present$') | map(attribute='name') | list }}"
with_items: "{{ bigip_device_facts.ltm_pools }}"
when: item.full_path == "/Common/mypool"
- debug: msg={{ listado }}
register: jkout
- name: Set a variable
debug:
msg: "jkvar={{ jkout }}"
Is that a right way to do it?
Thanks!!

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

how to use Ansible variables correctly?

- name: Create Dirs for rs disk
file:
name: "{{ /rs{{ items }}"
state: directory
with_sequence: start=1 end={{ disk_group[inventory_hostname]['rs'] | length }}
when: disk_group[inventory_hostname]['rs'] is defined
tasks
When "when" clause is false, how to avoid errors that vairable is undefined in "with_sequence".
In fact, use ignore_errors can do that, but I do think it's not a good idea.
You might want to try this example of include_tasks
> cat task.yml
- debug: var=item
with_sequence: start=1 end={{ rs|length }}
> cat playbook.yml
- hosts: localhost
vars:
# rs: test
tasks:
- include_tasks: task.yml
when: rs is defined

How to fallback to a default value when ansible lookup fails?

I was a little bit surprised to discover that his piece of code fails with an IOError exception instead of defaulting to omitting the value.
#!/usr/bin/env ansible-playbook -i localhost,
---
- hosts: localhost
tasks:
- debug: msg="{{ lookup('ini', 'foo section=DEFAULT file=missing-file.conf') | default(omit) }}"
How can I load a value without raising an exception?
Please note that the lookup module supports a default value parameter but this one is useless to me because it works only when it can open the file.
I need a default value that works even when the it fails to open the file.
As far as I know Jinja2 unfortunately doesn't support any try/catch mechanism.
So you either patch ini lookup plugin / file issue to Ansible team, or use this ugly workaround:
---
- hosts: localhost
gather_facts: no
tasks:
- debug: msg="{{ lookup('first_found', dict(files=['test-ini.conf'], skip=true)) | ternary(lookup('ini', 'foo section=DEFAULT file=test-ini.conf'), omit) }}"
In this example first_found lookup return file name if file exists or empty list otherwise. If file exists, ternary filter calls ini lookup, otherwise omit placeholder is returned.
In case people like me stumble upon this question in 2022,
Ansible now supports rescue blocks, which is similar to try-catch-finally in programming languages.
Examples can be found in the official documentation Error handling with blocks.
You can use block/rescue as follows:
- hosts: localhost
tasks:
- block:
- debug: msg="{{ lookup('ini', 'foo section=DEFAULT file=missing-file.conf') }}"
rescue:
- debug: msg="omit"
You can also convert your input file with a from_yaml filter before using the default filter
- name: "load a yaml file or a default value"
set_fact:
myvar: "{{ lookup('file', 'myfile.yml', errors='ignore') | from_yaml | default(mydefaultObject, true) }}"
To avoid the error when the path doesn't exist, use a condition to check for the path before attempting the lookup:
---
- hosts: localhost
tasks:
- debug: msg="{{ lookup('ini', 'foo section=DEFAULT file=missing-file.conf') }}"
when: missing-file.conf | exists
You can use this with set_fact as well, then omit the undefined var when using it if required:
- hosts: localhost
tasks:
- set_fact:
foo: "{{ lookup('ini', 'foo section=DEFAULT file=missing-file.conf') }}"
when: missing-file.conf | exists
- debug:
var: foo # undefined
msg: "{{ foo | default(omit) }}" # omitted
Note that lookups and Jinja2 tests run on the controller. If you need to check the path on the host, use the stat and either slurp or fetch modules:
- stat:
file: missing-remote-file-with-text-i-want
register: file
- slurp:
src: missing-remote-file-with-text-i-want
register: slurp
when: file.stat.exists
- set_fact:
foo: "{{ slurp.content | b64decode }}"
when: file.stat.exists
- fetch:
src: missing-file.conf
dest: /tmp/fetched
fail_on_missing: False
- set_fact:
bar: "{{ lookup('ini', 'foo section=DEFAULT file=/tmp/fetched/' + inventory_hostname + '/missing-file.conf') }}"
when: ('/tmp/fetched/' + inventory_hostname + '/missing-file.conf') | exists
Second note, in Ansible v2.5 the grammar for using the path tests was changed, the format is now:
- set_fact:
foo: "{{ lookup('ini', 'foo section=DEFAULT file=missing-file.conf') }}"
when: missing-file.conf is exists

Resources