I'm using Ansible with Jinja2 templates, and this is a scenario that I can't find a solution for in Ansible's documentation or googling around for Jinja2 examples. Here's the logic that I want to achieve in Ansible:
if {{ existing_ansible_var }} == "string1"
new_ansible_var = "a"
else if {{ existing_ansible_var }} == "string2"
new_ansible_var = "b"
<...>
else
new_ansible_var = ""
I could probably do this by combining several techniques, the variable assignment from here: Set variable in jinja, the conditional comparison here: http://jinja.pocoo.org/docs/dev/templates/#if-expression, and the defaulting filter here: https://docs.ansible.com/playbooks_filters.html#defaulting-undefined-variables ,
...but I feel like that's overkill. Is there a simpler way to do this?
If you just want to output a value in your template depending on the value of existing_ansible_var you simply could use a dict and feed it with existing_ansible_var.
{{ {"string1": "a", "string2": "b"}[existing_ansible_var] | default("") }}
You can define a new variable the same way:
{% set new_ansible_var = {"string1": "a", "string2": "b"}[existing_ansible_var] | default("") -%}
In case existing_ansible_var might not necessarily be defined, you need to catch this with a default() which does not exist in your dict:
{"string1": "a", "string2": "b"}[existing_ansible_var | default("this key does not exist in the dict")] | default("")
You as well can define it in the playbook and later then use new_ansible_var in the template:
vars:
myDict:
string1: a
string2: b
new_ansible_var: '{{myDict[existing_ansible_var | default("this key does not exist in the dict")] | default("") }}'
Something like this would work, but it's ugly. And as #podarok mentioned in his answer, it's likely unnecessary depending on exactly what you're attempting to do:
- name: set default
set_fact: new_ansible_var= ""
- name: set to 'a'
set_fact: new_ansible_var= "a"
when: "{{ existing_ansible_var }} == string1"
- name: set to 'b'
set_fact: new_ansible_var= "b"
when: "{{ existing_ansible_var }} == string2"
etc.
you don't need to set var, because I'm guessing that you trying to set var for some condition later.
Just make condition there like
- name: Later task
shell: "command is here"
when: {{ existing_ansible_var }} == "string1"
and get a profit
Related
I tried to register an output to a variable, but i couldnt filter the way i want it.
output:
oc get hpa -o json |jq -r '.items[].spec'
{
"maxReplicas": 3,
"minReplicas": 1,
"scaleTargetRef": {
"apiVersion": "apps.openshift.io/v1",
"kind": "DeploymentConfig",
"name": "hello-openshift"
},
"targetCPUUtilizationPercentage": 70
}
{
"maxReplicas": 4,
"minReplicas": 2,
"scaleTargetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "testrhel"
},
"targetCPUUtilizationPercentage": 79
}
Register the output to variable
- name: check for existing
shell: oc get hpa -o json |jq -r '.items[].spec'
register: existing
I would like to loop the output.name and compare it to another variable.
- name: set_fact
exist: {% if item.name == newvar and item.kind == newvar2 %}yes{%else%}no{%endif%}
loop:
- "{{ existing }}"
- name: task
shell: do something
when: exist == yes
Thanks in advance.
edit:
currently i am using below to get my comparison for the variables.
- name: Get existing hpa output
shell: oc get hpa -o json -n {{ namespace }} |jq -r '.'
register: tempvar
- name: set hpa variable to fact
set_fact:
existing_deploy: "{{ tempvar.stdout}}"
- name: Comparing existing hpa to new config
set_fact:
hpa_exist: "{% if deploy_type == item.spec.scaleTargetRef.kind|lower and deploy_name == item.spec.scaleTargetRef.name|lower %}yes{% else %}no{% endif %}"
with_items:
- "{{ existing_deploy['items'] }}"
but the variable got overwrite when i trying to use when condition
- name: task a
include_tasks: a.yml
when: hpa_exist
- name: task b
include_tasks: b.yml
when: not hpa_exist
deploymentconfig/hello-openshift condition always fail even when it is true. leading to execute task b, which is not supposed to
Check out the documentation of the shell module.
The output of the shell on stdout will be in <var>.stdout (so in existing.stdout in your case.)
Once you got that, you obviously have json as text, but you want to parse it. To do that, use the from_json filter as shown in this answer.
Summa summarum your task should look like this:
- name: set_fact
set_fact:
exist: {% if item['scaleTargetRef']['name'] == newvar and item['scaleTargetRef']['kind'] == newvar2 %}yes{% else %}no{% endif %}
loop: "{{ existing.stdout | from_json}}"
But your output needs to be a valid list, so basically, it needs to look like this:
[{
"maxReplicas": 3,
"minReplicas": 1,
"scaleTargetRef": {
"apiVersion": "apps.openshift.io/v1",
"kind": "DeploymentConfig",
"name": "hello-openshift"
},
"targetCPUUtilizationPercentage": 70
},
{
"maxReplicas": 4,
"minReplicas": 2,
"scaleTargetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "testrhel"
},
"targetCPUUtilizationPercentage": 79
}]
But you might actually have a logic error, because you are looping over the list and overwriting the variable exist on every turn. So you will end up with one variable exist in the end and that will hold the value of the last iteration.
Check out how to register variables with a loop if you need the output of every iteration.
If you want to do something for each item that meets the condition, you can do this:
- name: check for existing
shell: oc get hpa -o json | jq -r '.items[].spec'
register: existing
- name: include tasks a
include_tasks: a.yml
when:
- deploy_type == item['scaleTargetRef']['kind'] | lower
- deploy_name == item['scaleTargetRef']['name'] | lower
loop: "{{ existing.stdout | from_json }}"
- name: include tasks b
include_tasks: b.yml
when: (deploy_type != item['scaleTargetRef']['kind'] | lower) or
(deploy_name != item['scaleTargetRef']['name'] | lower)
loop: "{{ existing.stdout | from_json }}"
You do not need any of the set_fact stuff in that case.
Attempting to make a decision in a template based on the last character of a variable (third level domain hostname) , but the epiphany alludes me. Make a config stanza if value else, do the other.
I set a fact in play:
- name: Set third level domain name to a variable
set_fact:
my_3rd_levelname: "{{ ansible_nodename.split('.')[0] }}"
- name: Ascertain if which server we're on
set_fact:
my_one_or_two: "{{ my_3rd_levelname[-1]|int }}"
...which appears to echo out with debug, save the casting as an int...see below.
TASK [role-test : Echo out my_one_or_two] *******************************************************************************************************************
ok: [w.x.y.42] => {
"my_one_or_two": "2"
}
Then in the template.j2...
{# If my_one_or_two is even list server1 first. If not, second. #}
{% if lookup('vars,',my_one_or_two) + my_one_or_two|int is 1 %}
[some config file stanza here]
{% else %}
[some other config file stanza instead]
I've poked and hoped until I can stand it no longer and am reaching out. I've tried just using the raw variable, e.g., {% if my_one_or_two|int == 1 %} along with many other attempts, but I'm stuck. I can't seem to overcome this error:
AnsibleError: template error while templating string: expected token 'name', got 'integer'. String: [the contents of my template]
Any input would be greatly appreciated at this juncture.
Thanks
Okay...leaving this here in case someone else doesn't realize you can use any Python method that the object supports. Here's what I did. Remember the server names end in 1 or 2 and its a String.
Created a varible in /roles/[rolename]/vars...
my_simple_hostname: "{{ ansible_nodename.split('.')[0] }}"
Then used the 'endswith' method to evaluate it....
% if my_simple_hostname.endswith('1') == true %}
[content if true]
{% else %}
[content when false]
{% endif %}
I need to pass a variable in a json_query filter.
This example, with a fixed string, is working correctly (string=tutu) :
- set_fact:
my_value_exist: "{{ my_json.json | json_query('contains(component.name,`tutu`)')}}"
But i need to pass a variable , instead of tutu
- set_fact:
my_value_exist: "{{ my_json.json | json_query('contains(component.name,`{{my_var}}`)')}}"
{{my_var}} is a string retreived in a previous step
Do you have the correct syntax, so that the variable {{my_var}} could be passed correctly in parameter ?
Thanks for your help.
Regards,
Use helper variable for a task:
- set_fact:
my_value_exist: "{{ my_json.json | json_query(qry) }}"
vars:
qry: 'contains(component.name,`{{my_var}}`'
If you would like to avoid using a helper var you can use the second var directly by wrapping it in escaped double quotes ( \" ) between plus characters ( + ) like this:
- set_fact:
my_value_exist: "{{ my_json.json | json_query('contains(component.name,`\" + my_var + \"`)') }}"
I know that this is a old question but it might help someone since this is the top result on the subject on google.
I have a variable that is an array [{'foo':1},{'bar':2}].
I want to combine it with the following hash: {'baz':3} using a set fact (?) such as my output registered variable is:
[{'foo':1, 'baz':3},{'bar':2, 'baz':3}]
I've looked into the combine filter, but it only works when I already have an hash to work with. In my case I have an array.
Is there a way to achieve that using ansible?
Actually, I have found a way. map can be used with any filters, and arguments have to be passed after a comma
- name: test
set_fact:
_test: "{{ [{'foo':1}, {'bar':2}] | map('combine', {'baz':3}) | list }}"
produces:
ok: [localhost] => {
"_test": [
{
"baz": 3,
"foo": 1
},
{
"bar": 2,
"baz": 3
}
]
}
Jinja2 doesn't have list comprehension, but I think you can use set and for loop to achieve it:
{% set outputarray = [] -%}
{% for d in inputarray -%}
{% set r = d|combine({'baz': 3}) -%}
{{ ouputarray.append(r) and '' }}
{%- endfor %}
Based on extra vars parameter I Need to write variable value in ansible playbook
ansible-playbook playbook.yml -e "param1=value1 param2=value2 param3=value3"
If only param1 passed
myvariable: 'param1'
If only param1,param2 passed
myvariable: 'param1,param2'
If param1,param2,param3 are passed then variable value will be
myvariable: 'param1,param2,param3'
When I try to create variable dynamically through template then my playbook always takes previous variable value. But inside dest=roles/myrole/vars/main.yml its writing correct value.
What I make a try here
- hosts: local
user: roop
gather_facts: yes
connection: local
tasks:
- template: src=roles/myrole/templates/myvar.j2 dest=roles/myrole/vars/main.yml
- debug: var=myvariable
roles:
- { role: myrole }
So inside myrole directory I have created template and vars
- roles
- myrole
- vars/main.yml
- templates/myvar.j2
templates/myvar.j2
{% if param1 is defined and param2 is defined and param3 is defined %}
myvariable: 'param1,param2,param3'
{% elif param1 is defined and param2 is defined %}
myvariable: 'param1,param2'
{% elif param1 is defined %}
myvariable: 'param1'
{% else %}
myvariable: 'default-param'
{% endif %}
As I know if only two condition then I can do this using inline expression like below
{{ 'param1,param2' if param1 is defined and param2 is defined else 'default-param' }}
<do something> if <something is true> else <do something else>
Is it possible if - elif - else in inline expression like above. Or any other way to assign value dynamically in ansible playbook?
I am sure there is a smarter way for doing what you want but this should work:
- name : Test var
hosts : all
gather_facts : no
vars:
myvariable : false
tasks:
- name: param1
set_fact:
myvariable: "{{param1}}"
when: param1 is defined
- name: param2
set_fact:
myvariable: "{{ param2 if not myvariable else myvariable + ',' + param2 }}"
when: param2 is defined
- name: param3
set_fact:
myvariable: "{{ param3 if not myvariable else myvariable + ',' + param3 }}"
when: param3 is defined
- name: default
set_fact:
myvariable: "default"
when: not myvariable
- debug:
var=myvariable
Hope that helps. I am not sure if you can construct variables dynamically and do this in an iterator. But you could also write a small python code or any other language and plug it into ansible
my_var: the variable declared
VAR: the variable, whose value is to be checked
param_1, param_2: values of the variable VAR
value_1, value_2, value_3: the values to be assigned to my_var according to the values of my_var
my_var: "{{ 'value_1' if VAR == 'param_1' else 'value_2' if VAR == 'param_2' else 'value_3' }}"
I would first suggest that you step back and look at organizing your plays to not require such complexity, but if you really really do, use the following:
vars:
myvariable: "{{[param1|default(''), param2|default(''), param3|default('')]|join(',')}}"