Aller au contenu principal

Les templates Jinja avec Ansible

· 6 minutes de lecture
Stéphane ROBERT

logo ansible

Jinja2 est un système de templates puissant qui peut être utilisé directement dans vos playbooks, via les filtres, mais aussi pour générer des fichiers dynamiquement.

En effet, Ansible permet, via le système de template Jinja, de créer des documents dans lesquels une partie du contenu peut être remplacé dynamiquement. Les fichiers résultants peuvent être au format HTML, JSON, XML ou tout ce qui utilise du texte pur comme encodage. On peut ainsi générer des fichiers de configuration, des documents, des fichiers de ligne de commandes, ...

Utilisation des templates Jinja2 dans vos playbooks Ansible

Les modèles Jinja2 sont de simples fichiers textes contenant des variables qui sont remplacées par les valeurs définies lors de l'éxecution du module template d'Ansible. Ces données peuvent être celles définies dans des inventaires Ansible, dans des variables ou bien encore collectées lors de l'éxécution du playbook.

Les fichiers de templates jinja2 utilisent, en plus des variables et expressions, des balises qui permettent de contrôler la logique du document. La syntaxe utilise des délimiteurs qui sont du type suivant:

  • {{ ... }}pour les variables, comme dans les playbooks
  • {% ... %}pour les instructions de contrôle: if, for, raw, block, ...
  • {# ... #} pour les commentaires qui ne seront pas inclus le fichier résultat
  • # ... ## pour les instructions de ligne

Les filtres

Les filtres permettent de manipuler des variables. La syntaxe utilisée est la suivante :

{{ variable | filtre }}

Je vous ai déjà largement documenté les filtres disponibles dans les billets suivant 1 2 3

Par exemple pour définir une valeur par défaut :

switchport access vlan: {{ interface.vlan | default('8') }}

Les conditions

Jinja2 utilise l'instruction conditionnelle de type if avec elif else. Une condition s'écrie comme suit :

{% if eos_ver >= 4.22 -%}
Detected EOS ver {{ eos_ver }}, using new command syntax.
{% else -%}
Detected EOS ver {{ eos_ver }}, using old command syntax.
{% endif %}

Les tests

Les tests peuvent être utilisés pour contrôler le type d'une variable, qu'elle est définie, qu'un nombre est divisible par, qu'il est pair, ...

Pour tester une variable ou une expression, vous ajoutez is plus le nom du test après la variable.

Par exemple, pour savoir si une variable est définie :

{% if variable is defined %}
value of variable: {{ variable }}
{% else %}
variable is not defined
{% endif %}

Je ne vais pas vous en faite le listing, je vous renvoie plutôt sur la référence

Les boucles

Il est possible de boucler sur une liste d'éléments. Par exemple, pour afficher une liste d'utilisateurs fournie dans une variable appelée users :

{% for user in users %}
<li>count: {{loop.index}} {{ user }}</a></li>
{% endfor %}

Vous remarquez peut-être la variable interne loop.index qui le compteur de la boucle. Il existe d'autres qui permettent d'ajouter encore plus de logique à vos boucles.

Par exemple, nous voudrions que seuls les users dont l'index est pair soit imprimé, on fait à l'instruction jinja if :

% for user in users %}
{%- if loop.index is even %}{% continue %}{% endif %}
...
{% endfor %}

Écriture d'un template Ansible

Prenons comme exemple un fichier de configuration ntp simplifié. Voici le template qui généra ce fichier de configuration.

{{ ansible_managed | comment }}

{% if ntp_tinker_panic is sameas true %}
# Always reset the clock, even if the new time is more than 1000s away
# from the current system time. Usefull for VMs that can be paused
# and much later resumed.
tinker panic 0
{% endif %}

# Use public servers from the pool.ntp.org project.
{% for item in ntp_servers %}
pool {{ item }} iburst
{% endfor %}

Dans cet exemple, on retrouve un exemple d'utilisation :

  • Des filtres avec {{ ansible_managed | comment }} qui produit une ligne de commentaire grâce au filtre | comment indiquant que ce fichier a été généré par Ansible.
  • Des conditions avec l'instruction {% if ... %} .. {% endif %}.
  • Des boucles avec l'instruction {% for item ... %} {{ item }} {% endfor %}.

Qui peut être utilisé via ce playbook ansible :

---
- hosts: all
connection: local
gather_facts: no
vars:
ntp_servers:
- "0.rhel.pool.ntp.org"
- "1.rhel.pool.ntp.org"
- "2.rhel.pool.ntp.org"
- "3.rhel.pool.ntp.org"
ntp_tinker_panic: false

tasks:
- name: Generate ntp configuration file.
template:
src: "ntp.conf.j2"
dest: "/tmp/ntp.conf"
mode: 0644

Cet exemple produit un fichier en local /tmp/ntp.conf juste pour illustrer la logique des templates Ansible !

Pour lancer ce playbook:

ansible-playbook -c local -i localhost, playbook.yml

Qui produit le fichier :

#
# Ansible managed
#


# Use public servers from the pool.ntp.org project.
pool 0.rhel.pool.ntp.org iburst
pool 1.rhel.pool.ntp.org iburst
pool 2.rhel.pool.ntp.org iburst
pool 3.rhel.pool.ntp.org iburst

Gérer les espacements et les retours à la ligne

Si vos fichiers produits doivent respecter des règles strictes d'espacement ou de retour, Ansible permet de contrôler l'impression via des paramètres suivants :

  • lstrip_blocks : les espaces et les tabulations au début des variables sont supprimés
  • trim_blocks : si défini à yes les nouvelles lignes après la suppression d'un bloc.

Vous pouvez aussi utiliser des caractères de contrôle manuel pour modifier la gestion d'un espace autour d'un bloc.

{%- if ... %} vire la ligne avant {%+ if ... %} garde la ligne avant {%+ if ... -%} garde la ligne avant mais vire celle après

Par exemple dans notre exemple la variable ntp_tinker_panic est définie à false mais dans le fichier généré, on se retrouve avec une ligne vide. Si vous ne voulez pas de cette ligne, vous pourriez soit :

  • Utiliser cette syntaxe dans votre template :
{% if ntp_tinker_panic is sameas true -%}
# Always reset the clock, even if the new time is more than 1000s away
# from the current system time. Usefull for VMs that can be paused
# and much later resumed.
tinker panic 0
{% endif -%}
  • Utiliser les paramètres dans le playbook :
  tasks:
- name: Generate ntp configuration file.
template:
src: "ntp.conf.j2"
dest: "/tmp/ntp.conf"
mode: 0644
lstrip_blocks: true
trim_blocks: true

D'autres paramètres

Conserver les anciens fichiers

Il est possible de demander à Ansible de conserver les fichiers en les horodatant :

  tasks:
- name: Generate ntp configuration file.
template:
src: "ntp.conf.j2"
dest: "/tmp/ntp.conf"
mode: 0644
lstrip_blocks: true
trim_blocks: true
backup: yes

Forcer l'encodage

Il est possible de forcer l'encodage d'un fichier avec le paramètre :

output_encoding : utf-8 par défaut

Si vous voulez plus de tutorials Ansible je vous renvoie sur le billet de l'introduction à ansible