{{tag>Ansible YAML Jinja}} # Notes Ansible Jinja2 Voir : * https://jinja.palletsprojects.com/en/3.1.x/templates/#list-of-control-structures * https://ansible-arista-howto.readthedocs.io/en/latest/JINJA_YAML_STRUCTURES.html * https://tedboy.github.io/jinja2/templ14.html * https://docs.saltproject.io/en/latest/topics/jinja/index.html * https://blog.stephane-robert.info/post/ansible-template-jinja/ * j2cli * [Mastering loops with Jinja templates in Ansible](https://www.redhat.com/en/blog/ansible-jinja) * https://blog.stephane-robert.info/docs/developper/programmation/python/jinja/ A la place du module **template** il est possible d'utiliser le module **copy** : Ansible M(template) M(copy) ~~~yaml - name: Generate new resolv.conf ansible.builtin.copy: mode: "{{ stat_resolv_conf.stat.mode }}" content: | {% for nameserver in nameservers %} nameserver {{ nameserver }} {% endfor %} dest: resolv.conf.test ~~~ ### Ansible jinja version ~~~ $ ansible --version |grep jinja jinja version = 3.1.6 ~~~ ### Jinja2 en ligne de commande j2cli j2 #### Install ~~~bash apt-get install j2cli ~~~ ou ~~~bash pip3 install jinja2-cli[yaml,toml,xml,hjson,json5]==0.8.2 # --break-system-packages ~~~ #### Exemples Exemple 1 ''nginx.conf.j2'' ~~~nginx server { listen 80; server_name {{ nginx.hostname }}; root {{ nginx.webroot }}; index index.htm; } ~~~ ''nginx.yaml'' ~~~yaml --- nginx: hostname: localhost webroot: "/var/html/projet1" ~~~ ~~~bash # j2 -f json nginx.conf.j2 nginx.json > nginx.conf j2 nginx.conf.j2 nginx.yaml > nginx.conf ~~~ Exemple 2 ''timezone.j2'' ~~~ {{ TIMEZONE }} ~~~ ''ENV'' ~~~bash TIMEZONE=Europe/Paris ~~~ ~~~bash j2 -f env timezone.j2 ENV > timezone ~~~ ### Regex ~~~ {{ requete_conteneur.stdout | regex_replace('\\s', '') }} ~~~ Exclure un élément d'une liste / enlever un élément d'une liste. ~~~yaml - name: DEBUG debug: msg='{{ item }}' with_items: '{{ ansible_interfaces |difference(["lo"]) }}' ~~~ ### Condition IF ''/etc/yum.repos.d/plop.repo'' ~~~ {% if ansible_distribution_version == '7.2' %} [mirorlinux-rh7.2-x86_64] name=Plop RH 7.2 baseurl=http://172.16.12.42/redhat/rh7.2/x86_64/ gpgcheck=0 enabled=1 {% endif %} ~~~ ~~~ {% for user in users if not user.hidden %}
  • {{ user.username|e }}
  • {% endfor %} ~~~ ### Valeur par défaut - variable undef Erreur ~~~ fatal: [vmware-local]: FAILED! => {"failed": true, "msg": "'servers' is undefined"} ~~~ Code Ansible ~~~yaml - name: conf /etc/chrony.conf - add servers lineinfile: dest=/etc/chrony.conf line='server {{ item }} iburst' with_items: - "{{ servers }}" - 172.18.32.3 ~~~ Code Ansible corrigé ~~~yaml - name: conf /etc/chrony.conf - add servers lineinfile: dest=/etc/chrony.conf line='server {{ item }} iburst' with_items: #- "{{ servers |default('') }}" - "{{ servers |default([]) }}" - 172.18.32.3 ~~~ Liste vide avec item undef - with_item undef / empty : ~~~ {{ item |default([]) }}'' ~~~ ### Affichage - Alignement Avec la méthode ''ljust'' ou ''rjust'' ~~~jinja {% for HOST in hosts %} define host { {% for k, v in HOST.items() %} {{ k.ljust(20) }} {{ v }} {% endfor %} } {% endfor %} ~~~ ### Lever une exeption Source https://stackoverflow.com/questions/21778252/how-to-raise-an-exception-in-a-jinja2-macro Avec Ansibe ~~~ {{ ('OK text' if condition_ok) | mandatory('Text of error message') }} {{ ('' if false) | mandatory('ERROR: KIND must be A, B or C') }} ~~~ Ou sinon plus simple : ~~~ {{ 0/0 }} ~~~ ## Linter Voir : * https://pypi.org/project/djlint/ ~~~bash sudo apt-get install python3-pip ~~~ ~~~bash python3 -m venv djlint source bin/activate pip install djlint ~~~ ~~~ $ ./bin/djlint ~/code/template-dockerfile/ --extension=j2 --lint Linting 1/1 files ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 00:00 Dockerfile.j2 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── H025 19:9 Tag seems to be an orphan. H014 132:27 Found extra blank lines. e/pcc/logs Linted 1 file, found 2 errors. ~~~ ~~~bash djlint . --extension=html.j2 --lint djlint . --extension=html.j2 --check djlint . --extension=html.j2 --reformat ~~~ ## Exemples ### Variables - Whitespace Control - Exemple de contrôles de sauts de ligne et d'espaces Voir : https://jinja.palletsprojects.com/en/stable/templates/#variables #### Méthode 1 * Utilisation de ''+'' dans la boucle * Utilisation de ''-'' dans les conditions à l'interieur de la boucle * A la fin de la dernière condition (''endif'') utiliser un ''+'' à la place d'un ''-'' ~~~ {%+ for KEY in CONF.SYS_GROUPS +%} {%- if CONF.SYS_GROUPS[KEY].GID is defined -%} RUN groupadd -g {{ CONF.SYS_GROUPS[KEY].GID }} {{ KEY }} {%- else -%} RUN groupadd {{ KEY }} {%- endif +%} {%+ endfor +%} ~~~ #### Méthode 2 * Mettre des ''-'' systématiquement sauf aux extrémités de la boucle * A chaque fin de ligne utiliser : ~~~ {{ '\n' }} ~~~ ~~~ {% for KEY in CONF.SYS_GROUPS -%} {%- if CONF.SYS_GROUPS[KEY].GID is defined -%} RUN groupadd -g {{ CONF.SYS_GROUPS[KEY].GID }} {{ KEY + '\n' }} {%- else -%} RUN groupadd {{ KEY + '\n' }} {%- endif -%} {%- endfor %} ~~~ ## Ansible Templating all j2 files ~~~yaml - name: Templating all j2 files with config.yaml gather_facts: false hosts: localhost tasks: - name: Include vars of config.yaml no_log: true ansible.builtin.include_vars: file: config.yaml name: CONF - name: Find all j2 templates files register: reg_find_j2tpls ansible.builtin.find: paths: "." patterns: "*.j2" hidden: false file_type: file recurse: true excludes: - .git - name: Templating j2 files ansible.builtin.template: src: "{{ item }}" dest: "{{ dest }}" mode: "0600" loop: "{{ reg_find_j2tpls.files | map(attribute='path') }}" vars: dest: "{{ item | regex_replace('\\.j2$', '') }}" ~~~ ## Pb ### Err template error while templating string: no filter named 'split' ~~~ fatal: [localhost]: FAILED! => {"msg": "template error while templating string: no filter named 'split'. String: {{ fic_resolv.content | b64decode | split('\n') | select('match', '^nameserver\\\\s' ) | replace('\\\\t', ' ') | regex_replace('nameserver\\\\s+') }}"} ~~~ La version d'Ansible est trop ancienne A la place du filtre ''split'' il est possible d'utiliser la méthode ''split'' ou ''splitlines'' ~~~yaml - name: Set facts - get values - resolv.conf ansible.builtin.set_fact: # resolv_lines_domains: "{{ fic_slurp_resolv.content | b64decode | split('\n') | select('match', '^domain\\s' ) | replace('\\t', ' ') | regex_replace('domain\\s+') }}" resolv_lines_domains: "{{ (fic_slurp_resolv.content | b64decode).split('\n') | select('match', '^domain\\s' ) | list | replace('\\t', ' ') | regex_replace('domain\\s+') }}" ~~~