Skip to content

Use namespace as global variable in Ansible Jinja templates

A simple task, or so I thought: in a Jinja template keep track of the number of items in a loop. And then use that count afterwards.

Disclaimer: the number of items is not equal the number of times the loop runs, so I can't use the loop variables.

Turns out that Jinja has other opinions, and variables inside a loop are all local. When a variable is changed inside the loop, the scoop of the variable stays local, and once the loop ends the original value of the variable is restored. That's even true for variables which are created outside the loop.

 

To demonstrate the problem, here is a simple Playbook, just a small "template":

- hosts: all
  gather_facts: True
  any_errors_fatal: True
  force_handlers: True

  tasks:
    - name: Template
      local_action:
        module: template
        src: "/tmp/template.in"
        dest: "/tmp/template.out"

The Playbook is executed:

ansible-playbook -i 127.0.0.1, /tmp/template.yml

 

The template is setting a variable, running through a loop and increasing the variable:

{% set counter = 1 %}

Counter before loop: {{ counter }}

{% for i in range(6) %}
{% set counter = counter + 1 %}
Counter in loop ({{ i }}): {{ counter }}

{% endfor %}

Counter after loop: {{ counter }}

One would expect that the variable starts with "1", the loop runs 5 times (the end value of "6" for the range is never reached), and will increase the counter to "6". Instead this happens:

Counter before loop: 1

Counter in loop (1): 2

Counter in loop (2): 2

Counter in loop (3): 2

Counter in loop (4): 2

Counter in loop (5): 2

Counter after loop: 1

Every run of the loop gets a "fresh" copy of the variable value, and the total is never increased beyond "2".

 

Now one can argue that not too much work should be done in templates, but at some point the work has to be done. A counter is rarely pre-calculated. To solve this kind of problems, Jinja introduced Namespaces in version 2.10. By using a namespace, the variable in the loop is never localized, but instead the variable in the namespace is used. The same simple loop from above with a namespace:

{% set ns = namespace() %}
{% set ns.counter = 1 %}

Counter before loop: {{ ns.counter }}

{% for i in range(1, 6) %}
{% set ns.counter = ns.counter + 1 %}
Counter in loop ({{ i }}): {{ ns.counter }}

{% endfor %}

Counter after loop: {{ ns.counter }}

Results in the following output:

Counter before loop: 1

Counter in loop (1): 2

Counter in loop (2): 3

Counter in loop (3): 4

Counter in loop (4): 5

Counter in loop (5): 6

Counter after loop: 6

Exactly what I'm looking for!

  • Twitter
  • Bookmark Use namespace as global variable in Ansible Jinja templates at del.icio.us
  • Facebook
  • Google Bookmarks
  • FriendFeed
  • Digg Use namespace as global variable in Ansible Jinja templates
  • Bloglines Use namespace as global variable in Ansible Jinja templates
  • Technorati Use namespace as global variable in Ansible Jinja templates
  • Fark this: Use namespace as global variable in Ansible Jinja templates
  • Bookmark Use namespace as global variable in Ansible Jinja templates at YahooMyWeb
  • Bookmark Use namespace as global variable in Ansible Jinja templates at Furl.net
  • Bookmark Use namespace as global variable in Ansible Jinja templates at reddit.com
  • Bookmark Use namespace as global variable in Ansible Jinja templates at blinklist.com
  • Bookmark Use namespace as global variable in Ansible Jinja templates at Spurl.net
  • Bookmark Use namespace as global variable in Ansible Jinja templates at Simpy.com
  • Bookmark Use namespace as global variable in Ansible Jinja templates at blogmarks
  • Bookmark Use namespace as global variable in Ansible Jinja templates with wists
  • wong it!
  • Bookmark using any bookmark manager!
  • Stumble It!
  • Identi.ca

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

No comments

Add Comment

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
To leave a comment you must approve it via e-mail, which will be sent to your address after submission.
Form options