RSS 피드 구독하기

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!

Read full bio
UI_Icon-Red_Hat-Close-A-Black-RGB

채널별 검색

automation icon

오토메이션

기술, 팀, 인프라를 위한 IT 자동화 최신 동향

AI icon

인공지능

고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트

open hybrid cloud icon

오픈 하이브리드 클라우드

하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요

security icon

보안

환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보

edge icon

엣지 컴퓨팅

엣지에서의 운영을 단순화하는 플랫폼 업데이트

Infrastructure icon

인프라

세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보

application development icon

애플리케이션

복잡한 애플리케이션에 대한 솔루션 더 보기

Virtualization icon

가상화

온프레미스와 클라우드 환경에서 워크로드를 유연하게 운영하기 위한 엔터프라이즈 가상화의 미래