Cannot iterate through a dictionary, how to check if it`s empty? - ansible

I am trying to upgrade a server for a web application, I have to iterate through a key value dictionary, but I am given the following error
FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'iteritems'"}
I tried
{
"version":"{{ version.actual_version_number }}",
"integrations": {
{% for id, port in integration_details.items() %}
{% if integration_details is defined and integration_details is not empty %}
{{ id }} : {{ port }}
{% endif %}
{%- if not loop.last -%},{% endif %}
{% endfor %}
}
}
If anyone could help with this problem would be really appreciated!

Assuming that your data structure is a dict use if integration_details is defined and integration_details.keys()|length > 0 this will check if there are any subkeys present. If its a list use if integration_details is defined and integration_details|length > 0
But seeing the error you posted you might have a for loop with iteritems()somewhere, which is not in the code you posted.
This may be because you use python3 but want to iterate your dict with iteritems() instead of items() or your variable is not a dict but a different type.

Related

Jinja2 Namespaces odd result

Im trying to set a global variable within a jinja2 template so i can set variables inside and outside of a loop. I've used this before and its worked but in this case it doesnt and i'm struggling to find out why.
This is my j2 template.
{% set found = namespace() %}
{% set vrf = namespace() %}
{% for group_item in site_master_list -%}
{% for host_item in site.list -%}
{% if group_item.ip_addr != host_item.ip_addr %}
none match {{ group_item.ip_addr }} none match {{ host_item.ip_addr }}
{% set found.tracker = false %}
does it have vrf {{ host_item.vrf }}
{% if host_item.vrf != "" %}
{% set vrf = namespace(id=host_item.vrf) %}
vrf {{ vrf }}
vrf.id {{ vrf.id }}
{% endif %}
{% endif %}
{% endfor %}
bottom vrf {{ vrf }}
{%- endfor %}
This is the output
bottom vrf <Namespace {}>none match 10.180.193.235 none match 10.112.208.11
does it have vrf management
vrf <Namespace {'id': 'management'}>
vrf.id management
bottom vrf <Namespace {}>
As you can see i'm setting host_item.vrf to the namespace vrf so i can use vrf.id outside of the loop further down the template.
Where i've got bottom vrf {{ vrf }} you can see in the output the empty namespace where it shows
bottom vrf <namespace {}>
Returning vrf.id within the loop returns management the correct value and all is good.
Anyone got any suggestions why i cant return vrf.id outside of the inner loop? I was returning vrf.id but Ansible returns the below which is correct because 'id' doesn't exist at that stage in the template.
''jinja2.utils.Namespace object'' has no attribute ''id'''
Thanks
I've fixed this by replacing
{% set vrf = namespace(id=host_item.vrf) %}
With
{% set vrf.value = host_item.vrf %}
Not sure why this worked previously but the latter works.

How can one generate a dict in Jinja/Ansible as a var?

I need to dynamically generate a dict and assign it to an Ansible var so that the same data is available to every host and does not need to be regenerated on each one. This will be looped over by a task and template. It is possible to build lists by nesting Jinja in a var definition like so:
var_list: >
[
{% set data = [] %}
{% for host in groups['all'] %}
{{ data.append(host) }}
{% endfor %}
{{ data }}
]
However, I am not able to do so with dicts:
var_dict: >
{
{% set data = dict() %}
{% for host in groups['all'] %}
{{ data | combine({host: []}) }}
{% endfor %}
{{ data }}
}
This results in a giant, garbled string: {u'host.subdom.dom.tld': []} {}...
What I am expecting is a var set to a true dict object that can have its components referenced individually (like {{ var_dict.host1[0] }}), with a structure similar to the following JSON:
{
"host1": [],
"host2": []
}
Is there a way to cause this to expand into a proper dict when assigned to the variable? Or is there perhaps another way to generate a dict from a loop without that awful set_fact hackery?
Either of your templates is broken (the first one produces nested lists, the second one prints data in loop with this expression {{ data | combine({host: []}) }}, the value of data remains empty until the end).
Jinja2 is a templating engine, you don't really need to create data structures to print them later. You can form your output directly.
For the list:
var_list: >
[ {% for host in groups['all'] %}'{{ host }}',{% endfor %} ]
For the dictionary:
var_dict: >
{ {% for host in groups['all'] %}'{{ host }}': [],{% endfor %} }
As you expect the values to be interpreted by Ansible, you don't need to pay attention to the trailing coma, otherwise there is loop.last.

Jinja2 Templating: Conditionally forming a set from Ansible Variables

I am trying to generate some config using Jinja2 templating and Ansible variables. The framework under which I am currently working does not allow me to perform the following operation in Ansible and thus I was hoping to achieve the same results in Jinja2.
My Ansible variables are as follows:
---
Top:
inner:
type: type1
other_random_variable:
- random: 1
inner2:
type: type2
inner3:
type: type1
The above structure works well when I am iterating over a loop and forming a configuration file as follows:
{% if Top is defined %}
{% for inner_vars in Top %}
# perform substitution here
{% endfor %}
{% endif %}
What I would like to do is to form a set of types such that I can generate another configuration for each unique type.
Is there any way for me to iterate through Top and add an item to a set?
I think I have a solution that could work:
{% set types = [] %}
{% if Top is defined%}
{% for inner_var in Top %}
{% if types.append(Top[inner_var].type) %}{% endif %}
{% endfor %}
{% endif %}
{{ types|unique }}

Display session parameters with twig

I would like to display all session variables with twig but I dont know exactly how:
{% for variable in app.session %}
session[ {{variable clef}}] = {{variable value}}
{% endfor %}
thank you for your help
You can use this, it will work for other associative arrays as well:
{% for key, value in app.session %}
session['{{ key }}'] = {{ value }}
{% endfor %}
It's also explained in the documentation: Iterating over Keys and Values.
You could also use twig's debugging function, dump. This will work even if the value of a key is an object or array.
{{ dump(app.session.all) }}

Liquid templates - accessing members by name

I'm using Jekyll to create a new blog. It uses Liquid underneath.
Jekyll defines certain "variables": site, content, page, post and paginator. These "variables" have several "members". For instance, post.date will return the date of a post, while post.url will return its url.
My question is: can I access a variable's member using another variable as the member name?
See the following example:
{% if my_condition %}
{% assign name = 'date' %}
{% else %}
{% assign name = 'url' %}
{% endif %}
I have a variable called name which is either 'date' or 'url'.
How can I make the liquid equivalent of post[name] in ruby?
The only way I've found is using a for loop to iterate over all the pairs (key-value) of post. Beware! It is quite horrible:
{% for property in post %}
{% if property[0] == name %}
{{ property[1] }}
{% endif %}
{% endfor %}
Argh! I hope there is a better way.
Thanks.
I don't know what I was thinking.
post[name] is a perfectly valid liquid construction. So the for-if code above can be replaced by this:
{{ post[name] }}
I thought that I tried this, but apparently I didn't. D'oh!
Liquid admits even fancier constructs; the following one is syntactically correct, and will return the expected value if post, element, categories, etc are correctly defined:
{{ post[element.id].categories[1].name }}
I am greatly surprised with Liquid. Will definitively continue investigating.
Actually, this did not work for me. I tried a bunch of different combinations and what finally worked was
<!-- object myObj.test has the string value "this is a test" -->
{% assign x = 'test' %}
{{ myObj.[x] }}
Output:
this is a test

Resources