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