Jinja templates in Ansible can be very powerful. They can also be a leading contributor to hair loss. Why? In some ways it comes down to documentation, a mixing of languages (YAML, Python, Jinja2), and variables.
During a recent consulting project with a customer, focused on network automation, we
embarked on a journey to re-evaluate how routers were provisioned. A significant part of this initiative was to dynamically create configuration templates for routers, based on variable input. In developing the j2 (the Jinja2 templating language) logic to do things like calculate bandwidth figures, we ran into some limitations. Mainly the ability to have a variable’s value accessible outside of the loop that is currently being run.
Please keep in mind that it is not possible to set variables inside a block and have them show up outside of it. This also applies to loops. -- http://jinja.pocoo.org/docs/2.9/templates/#assignments
So let's dive right into it. First, I'll describe the issue in detail (feel free to follow along on your own Ansible install).
Given the following playbook structure:
---
├── output.txt
├── varloop.j2
├── varloop.yml
└── vars.yml
Hosts is our inventory file and is simply our localhost that we are running Ansible on. Our var.yml file looks like this:
---
people:
- name: Mike
fav_colour: Blue
- name: Kyle
fav_colour: Yellow
- name: Shea
fav_colour: Blue
- name: Aly
fav_colour: Yellow
- name: Daniyal
fav_colour: Yellow
- name: Tim
fav_colour: Orange
colours:
- name: Blue
things:
- Sky
- Sea
- Jeans
- name: Yellow
things:
- Egg yolk
- Taxi
- Banana
- Lemon
- Sun
- name: Orange
things:
- Pumpkin
- Basketball
- Carrots
- Oranges
As you can see we have 2 dictionary variables defined. One containing a list of people with their favourite colours, and a second one containing a list of colours with things that happen to be of those colours. Now we write a simple playbook that will call on a Jinja template we will write. The playbook is called varloop.yml
:
---
- name: Demonstrating variables in Jinja2 Loops
hosts: localhost
connection: local
vars_files:
- vars.yml
gather_facts: no
tasks:
- name: Create the Jinja2 based template
template: src=./varloop.j2 dest=./output.txt
The playbook simply uses the variable file we specified and calls the template module in a task to build a file called output.txt from a j2 template. Now for the j2 template itself varloop.j2
:
---
{% for colour in colours %}
Colour number {{ loop.index }} is {{ colour.name }}.
{% set colour_count = 0 %}
{% for person in people if person.fav_colour == colour.name %}
{% set colour_count = colour_count + 1 %}
{% endfor %}
Currently {{ colour_count }} people call {{ colour.name }} their favourite.
And the following are examples of things that are {{ colour.name }}:
{% for item in colour.things %}
- {{ item }}
{% endfor %}
{% endfor %}
In this j2 template we are attempting the following:
- looping over the list of colours and attempting to run a nested loop inside, that counts the number of people who's favorite is the color of the current loop iteration.
- listing all of the things that are of the colour of the current loop iteration.
We get an output that is not what we expect. We are NOT able to extract the value of the colour_count variable outside of the inner loop where we are performing the count. See the output here:
---
Colour number 1 is Blue.
Currently 0 people call Blue their favourite.
And the following are examples of things that are Blue:
- Sky
- Sea
- Jeans
Colour number 2 is Yellow.
Currently 0 people call Yellow their favourite.
And the following are examples of things that are Yellow:
- Egg yolk
- Taxi
- Banana
- Lemon
- Sun
Colour number 3 is Orange.
Currently 0 people call Orange their favourite.
And the following are examples of things that are Orange:
- Pumpkin
- Basketball
- Carrots
- Oranges
In our example we see that because we can’t call the variable outside of the inner loop, the counting didn’t work. A quick modification to your /etc/ansible.cfg
file and a small change to your template, and we can get this working. First, add the following line to your ansible.cfg:
---
[defaults]
jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
Then modify your varloop.j2
file like this:
---
{% for colour in colours %}
Colour number {{ loop.index }} is {{ colour.name }}.
{% set colour_count = 0 %}
{% for person in people if person.fav_colour == colour.name %}
{% set colour_count = colour_count + 1 %}
{% do colour.update({'people_count':colour_count}) %}
{% endfor %}
Currently {{ colour.people_count }} people call {{ colour.name }} their favourite.
And the following are examples of things that are {{ colour.name }}:
{% for item in colour.things %}
- {{ item }}
{% endfor %}
{% endfor %}
Notice the “do” block in the updated template. This block allows us to use the update function to update an element in a dictionary variable. (we unlocked this with that extensions line in our ansible.cfg
file) In our case, we are updating the colour of the current loop iteration to include a new key/value pair called people_count. So in each loop iteration of the colours variable, we are able to now add a new element that contains the number of people who deem this their favourite colour.
---
Colour number 1 is Blue.
Currently 2 people call Blue their favourite.
And the following are examples of things that are Blue:
- Sky
- Sea
- Jeans
Colour number 2 is Yellow.
Currently 3 people call Yellow their favourite.
And the following are examples of things that are Yellow:
- Egg yolk
- Taxi
- Banana
- Lemon
- Sun
Colour number 3 is Orange.
Currently 1 people call Orange their favourite.
And the following are examples of things that are Orange:
- Pumpkin
- Basketball
- Carrots
- Oranges
Notice in our output after running Ansible with our updated template file, the numbers are properly counted.
Hope this helps you out in your template writing. Jinja 2 can be a pain, but ultimately a very powerful tool.
This article was originally published on the Arctiq blog and is republished with permission.
저자 소개
Tim has spent the bulk of his career in the solution architecture space, problem-solving and designing solutions to meet very specific needs. Since Arctiq's start in 2016, Tim has applied this background to various technologies and verticals for Arctiq's clients and partners.
Tim’s focus at Arctiq is in the following key areas:
Automation - Ansible, Puppet
Microservices - Openshift, Kubernetes, GKE, Anthos, Docker, CI/CD
Foundations - Red Hat Enterprise Linux, VMware, Cloud (AWS, GCE)
Integrated Security - Satellite, image hardening, custom provisioning
DevOps Consulting - Team and culture improvements, workflow and process improvements
Cheers!
채널별 검색
오토메이션
기술, 팀, 인프라를 위한 IT 자동화 최신 동향
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
오픈 하이브리드 클라우드
하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
가상화
온프레미스와 클라우드 환경에서 워크로드를 유연하게 운영하기 위한 엔터프라이즈 가상화의 미래