ansible cannot use variable - ansible

I created a role and with a variables file named defaults/main.yml with following content:
level1:
level2_1:
level3_1: "value_3_1"
level2_2:
level3_2: "value_3_2"
level3_3: "{{ level1.level2_1.level3_1 }} {{ level1.level2_2.level3_2 }}"
When I try running inside a task file it throws An unhandled exception occurred while templating.
I have tried changing the level3_3 line without adding parrents but also throws an error.
The only way I found to work is if I remove indentation from level3_3 which will not make it part of the structure.
How can I compose a variable similar to level3_3 inside the structure without throwing an error?

It's not possible by design. See Can't reference a dict key inside the same dict #50280. Either create compounds outside the dictionary or put the repeating values into a variables. For example
val_A: value_3_1
val_B: value_3_2
level1:
level2_1:
level3_1: "{{ val_A }}"
level2_2:
level3_2: "{{ val_B }}"
level3_3: "{{ val_A }} {{ val_B }}"
I'd prefer this structure. It's simpler and less error-prone.

Related

Passing a Go template format string to an Ansible command [duplicate]

In my playbook, a JSON file is included using the include_vars module. The content of the JSON file is as given below:
{
"Component1": {
"parameter1" : "value1",
"parameter2" : "value2"
},
"Component2": {
"parameter1" : "{{ NET_SEG_VLAN }}",
"parameter2": "value2"
}
}
After the JSON file is included in the playbook, I am using uri module to sent an http request as given below:
- name: Configure Component2 variables using REST API
uri:
url: "http://0.0.0.0:5000/vse/api/v1.0/config/working/Component2/configvars/"
method: POST
return_content: yes
HEADER_x-auth-token: "{{ login_resp.json.token }}"
HEADER_Content-Type: "application/json"
body: "{{ Component2 }}"
body_format: json
As it can be seen, the body of the http request is send with the JSON data Component2. However, Jinja2 tries to substitute the {{ NET_SEG_VLAN }} in the JSON file and throws and undefined error. The intention is not to substitute anything inside the JSON file using Jinja2 and send the body as it is in http request.
How to prevent the Jinja2 substitution for the variables included from the JSON file?
You should able to escape the variable even with {{'{{NET_SEG_VLAN}}'}} to tell jinja not to template anything inside that block.
You should be able to escape the variable with {% raw %} and {% endraw %} to tell Jinja not to template anything inside that block.
!unsafe
From documentation at https://docs.ansible.com/ansible/2.10/user_guide/playbooks_advanced_syntax.html#unsafe-or-raw-strings:
When handling values returned by lookup plugins, Ansible uses a data type called unsafe to block templating. Marking data as unsafe prevents malicious users from abusing Jinja2 templates to execute arbitrary code on target machines. The Ansible implementation ensures that unsafe values are never templated. It is more comprehensive than escaping Jinja2 with {% raw %} ... {% endraw %} tags.
You can use the same unsafe data type in variables you define, to prevent templating errors and information disclosure. You can mark values supplied by vars_prompts as unsafe. You can also use unsafe in playbooks. The most common use cases include passwords that allow special characters like { or %, and JSON arguments that look like templates but should not be templated.
I am using it all the time, like this:
# Load JSON content, as a raw string with !unsafe
- tags: ["always"]
set_fact:
dashboard_content: !unsafe "{{ lookup('file', './dash.json') | to_json }}"
# Build dictionnary via template
- tags: ["always"]
set_fact:
cc: "{{ lookup('template', './templates/cm_dashboard.yaml.j2') | from_yaml }}"
## cm_dashboard.yaml.j2 content:
hello: {{ cc_dashboard_content }}
# Now, "cc" is a dict variable, with "hello" field protected!

ansible conditional statements should not include jinja2 templating delimiters

What I can use to replace {{ }} while I still want to use variables?
when: ansible_lvm.lvs.{{ resize_lvname }}.size_g < 10 and
ansible_devices.{{ new_dev }}.size == "70.00 GB
[WARNING]: conditional statements should not include jinja2 templating
delimiters such as {{ }} or {% %}. Found: ansible_lvm.lvs.{{
resize_lvname }}.size_g < 10 and ansible_devices.{{ new_dev }}.size ==
"70.00 GB" and not "[new_dev].value.partitions"
In a when conditional you are already inside an implicit Jinja template context...which means if you want to refer to a variable, you just need the variable name. For example:
when: ansible_lvm.lvs[resize_lvname].size_g < 10 and
ansible_devices[new_dev].size == "70.00 GB
Recall that some.var.key is equivalent to some.var["key"]; we need to use the [...] syntax here because we want to use the value of resize_lvname as key on the ansible_lvm.lvs dictionary. If we were to write ansible_lvm.lvs.resize_lvname, we would be attempting to look up a key with the literal name resize_lvmname (in other words, that would be equivalent to ansible_lvm.lvs["resize_lvname"]).

How to use the second value of a variable in ansbile playbook

I would like to use the 2 values of a group variable in two different places. My current group vars looks like this.
[test1:vars]
fooname=foo1
barname=bar1
I am using jinja template in the playbook as {{ fooname }} and {{ barname }} based on my requirements in the playbook in multiple places. Now instead of two different variables, i would like to use it as one variable as names and i would like to use the values of it in different places.
Expected group variables :
[test1:vars]
names=foo1,bar1
Is there a way that now i can call {{ names }} variable with some function or condition like {{ names is search(foo) }} or {{ names is search(bar) }} inside the playbook like we use in condition so that i can avoid declaring 2 variables instead of one. I will use these variables in different places in my playbook.
I tried using the above one which prints "True" instead i need the value of my variable which has only foo or bar when i search accordingly.
Note : I have close to 400 groups which same pattern with makes the extra variable makes my inventory lengthy. So, i would like to minimize it.
Ansible 2 supports Arrays in the inventory File.
[test1:vars]
names=["foo","bar"]
But there are some limitations.
[example:vars]
# working
var1=["foo","bar"]
var2=[1,2]
var3=[True, False]
# not working
var4=[yes, no] # Boolean need to be True and False
var5=[foo, bar] # Interpreted as one string
var6='["foo","bar"]' # Interpreted as one string as well
Usage example:
- debug:
- msg: "Item: {{ example[0] }}"
- debug:
- msg: "Item: {{ example | first }}"

Ansible: Evaluate value of var inside parentheses

This might be a trivial or a duplicate question, but i seem to have exhausted my search and unable to properly frame a query to search, and so here i am with the question.
How to evaluate a value of a variable inside parentheses.
Below is my vars file
patch_version: 6.4
patch_list:
patch_type1:
6.4:
id: 123
feature: 123
And below is the how i am trying to evaluate
{{ patch_list.patch_type1.{{ patch_version }}.id }}
Unfortunately, there is a number with a decimal point in it, and that's giving me headache.
Any suggestions?
Put the attribute into the brackets. For example
- debug:
msg: "{{ patch_list.patch_type1[patch_version].id }}"
gives
"msg": "123"

How to use arithmetic when setting a variable value in Ansible?

I would like to use a system fact for a host times a number/percentage as a base for a variable. What I am trying to do specifically is use the ansible_memtotal_mb value and multiply it by .80 to get a ramsize to then use in setting a Couchbase value. I have been trying different variations of the line below. I'm not ever sure that it is possible, but any help would be appreciated.
vars:
ramsize: '"{{ ansible_memtotal_mb }}" * .80'
You're really close! I use calculations to set some default java memory sizes, which is similar to what you are doing. Here's an example:
{{ (ansible_memtotal_mb*0.8-700)|int|abs }}
That shows a couple of things- first, it's using jinja math, so do the calculations inside the {{ jinja }}. Second, int and abs do what you'd expect- ensure the result is an unsigned integer.
In your case, the correct code would be:
vars:
ramsize: "{{ ansible_memtotal_mb * 0.8 }}"
One little thing to add.
If you presume the math multiplication has precedence before jinja filter (| sign), you're wrong ;-)
With values like
total_rate: 150
host_ratio: 14 # percentual
"{{ total_rate*host_ratio*0.01|int }}" => 0 because 0.01|int = 0
"{{ (total_rate*host_ratio*0.01)|int) }}" => 21 as one expects
use: {{ ansible_memtotal_mb|int * 0.8 - 700 }}

Resources