Master Loops con plantillas Jinja en Ansible

Los modelos Jinja en Ansible pueden ser muy poderosos. También pueden ser uno de los principales contribuyentes a la caída del cabello. ¿Por qué? En cierto modo, todo se reduce a la documentación, una mezcla de lenguajes (YAML, Python, Jinja2) y variables.

Durante un proyecto de consultoría reciente con un cliente, centrado en la automatización de redes,

se embarcó en un viaje para reevaluar cómo se aprovisionaban los enrutadores. Una parte importante de esta iniciativa fue crear dinámicamente plantillas de configuración para enrutadores, basadas en entradas variables. Al desarrollar la lógica j2 (el lenguaje de plantilla Jinja2) para hacer cosas como calcular números de ancho de banda, nos encontramos con algunas limitaciones. Principalmente la capacidad de tener el valor de una variable accesible fuera del bucle en ejecución.

Así que vamos a sumergirnos en él. Primero, describiré el problema en detalle (no dude en seguir su propia instalación de Ansible).

Dada la siguiente estructura del libro de jugadas:

---
├── output.txt
├── varloop.j2
├── varloop.yml
└── vars.yml

Hosts es nuestro archivo de inventario y es simplemente nuestro host local donde ejecutamos Ansible. Nuestro archivo var.yml se ve así:

---
  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

Como puede ver, hemos definido 2 variables de diccionario. Uno que contiene una lista de personas con sus colores favoritos y un segundo que contiene una lista de colores con cosas que resultan ser esos colores. Ahora estamos escribiendo un libro de jugadas simple que recurrirá a un modelo de Jinja que vamos a escribir. El cuadernillo se llama 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

El libro de jugadas simplemente usa el archivo de variables que especificamos y llama al módulo modelo en una tarea para crear un archivo llamado salida.txt a partir de un modelo j2. Ahora para el modelo j2 en sí 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 %}

En esta plantilla j2, intentamos lo siguiente:

  • recorriendo la lista de colores e intentando ejecutar un bucle anidado dentro de ella, esto cuenta el número de personas cuyo favorito es el color de la iteración del bucle actual.
  • enumerando todas las cosas que son del color de la iteración del bucle actual.

Obtenemos una salida que no es la que esperábamos. NO podemos extraer el valor de la variable fuera del ciclo interno donde hacemos el conteo. Vea la salida aquí:

---
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

En nuestro ejemplo, vemos que debido a que no podemos llamar a la variable fuera del ciclo interno, el conteo no funcionó. Un cambio rápido en tu /etc/ansible.cfg archivo y un pequeño cambio en su modelo, y podemos hacer que eso funcione. Primero, agregue la siguiente línea a su ansible.cfg:

---
[defaults]
jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n

Entonces cambia tu varloop.j2 archivo como este:

---
{% 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 %}

Observe la "hacer" bloque en el modelo actualizado. Este bloque nos permite utilizar el poner al día función para actualizar un elemento en una variable de diccionario. (lo desbloqueamos con esta línea de extensiones en nuestro ansible.cfg file) En nuestro caso, estamos actualizando el color de la iteración de bucle actual para incluir un nuevo par clave/valor llamado número_de_personas. Por lo tanto, en cada iteración de bucle de la colores variable, ahora podemos agregar un nuevo elemento que contiene el número de personas que consideran este color como su favorito.

---
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

Observe en nuestra salida después de ejecutar Ansible con nuestro archivo de modelo actualizado, los números se cuentan correctamente.

Espero que esto te ayude a escribir tu plantilla. Jinja 2 puede ser un dolor, pero en última instancia, una herramienta muy poderosa.

Artículos de interés

Subir