Sổ tay kỹ thuật

Jinja2 cho template cấu hình mạng

cheatsheet#jinja2#ansible#python#config· cập nhật 2026-11-06

Tra nhanh cú pháp Jinja2 (vòng lặp, điều kiện, filter, whitespace control, macro) khi viết template config IOS XR/Junos cho Ansible hoặc Python.

Sửa trang này
Mục lục

Jinja2 dùng chung cho Ansible (template, src: của *_config), NAPALM load_template và script Python. Filter có dấu (A) chỉ có trong Ansible, Python thuần không có.

Cú pháp cơ bản

Mục đích Cú pháp
In biến {{ hostname }}, {{ intf.name }} hoặc {{ intf['name'] }}
Lệnh điều khiển {% if %}, {% for %}, {% set %}, {% macro %}
Comment (không ra output) {# ghi chú #}
Giá trị mặc định {{ mtu | default(9192) }}
Mặc định cả khi rỗng "" {{ desc | default('unused', true) }}
Gán biến {% set asn = 65001 %}
Nối chuỗi {{ 'Bundle-Ether' ~ lag_id }}
Escape {{ thật {{ '{{' }} hoặc {% raw %}...{% endraw %}

Điều kiện và vòng lặp

Mục đích Cú pháp
if / elif / else {% if role == 'pe' %}...{% elif role == 'p' %}...{% else %}...{% endif %}
Có định nghĩa không {% if peer.password is defined %}
Thuộc danh sách {% if 'mpls' in features %}
Lặp list {% for i in interfaces %}...{% endfor %}
Lặp dict {% for name, i in interfaces.items() %}
Lặp có lọc {% for p in peers if p.enabled %}
Khi list rỗng {% for ... %}...{% else %}! none{% endfor %}
Chỉ số, phần tử đầu/cuối loop.index (từ 1), loop.index0, loop.first, loop.last, loop.length
Đếm/cộng dồn trong vòng lặp {% set ns = namespace(n=0) %} rồi {% set ns.n = ns.n + 1 %}

{% set x = ... %} bên trong for không giữ giá trị ra ngoài vòng lặp. Cần cộng dồn thì dùng namespace như bảng trên.

Filter hay dùng

Mục đích Cú pháp
Sắp xếp theo thuộc tính interfaces | sort(attribute='name')
Lấy 1 thuộc tính của list dict peers | map(attribute='ip') | list
Lọc theo thuộc tính peers | selectattr('enabled') | list, selectattr('asn', 'equalto', 65001)
Loại bỏ rejectattr('shutdown')
Ghép chuỗi vlans | join(',')
Đếm peers | length
Không trùng vlans | unique | list
Thay chuỗi 'ge-0/0/1' | replace('/', '_')
Hoa/thường upper, lower, title
Định dạng số '%04d' | format(5) → 0005
Gom nhóm peers | groupby('group') → list (key, items)
Đổi kiểu int, string, float
Thụt lề khối block | indent(2)
Địa chỉ từ prefix (A) '192.0.2.1/31' | ansible.utils.ipaddr('address') → 192.0.2.1
Netmask (A) '192.0.2.0/24' | ansible.utils.ipaddr('netmask')
Regex (A) regex_replace('^Gi', 'GigabitEthernet'), regex_search('\\d+')
Test regex (A) intf is match('Bundle-Ether'), is search(...)
Gộp dict (A) defaults | combine(host_vars)
Xuất YAML/JSON (A) to_nice_yaml, to_nice_json

Whitespace control

Mục đích Cú pháp
Xoá khoảng trắng/newline bên trái {%- ... %}, {{- ... }}
Xoá bên phải {% ... -%}, {{ ... -}}
Bỏ newline sau thẻ block trim_blocks=True (Ansible template bật mặc định)
Bỏ khoảng trắng trước thẻ block lstrip_blocks=True (Ansible mặc định tắt)
Bật trong 1 file template Ansible dòng đầu: #jinja2: lstrip_blocks: True

IOS XR và Junos dạng text quan tâm thụt lề để hiển thị, nhưng *_config so sánh theo dòng: thừa/thiếu khoảng trắng là lần nào cũng changed. Render thử và so với show running-config trước khi chạy.

Macro

templates/xr_interfaces.j2
{% macro iface(name, desc, ip=none, mtu=9192) %}
interface {{ name }}
 description {{ desc }}
 mtu {{ mtu }}
{% if ip %}
 ipv4 address {{ ip }}
{% else %}
 shutdown
{% endif %}
!
{% endmacro %}
{% for i in interfaces | sort(attribute='name') %}
{{ iface(i.name, i.desc | default('unused'), i.ip | default(none)) -}}
{% endfor %}
templates/junos_bgp.set.j2
{% for p in bgp_peers if p.enabled %}
set protocols bgp group {{ p.group }} neighbor {{ p.ip }} peer-as {{ p.asn }}
set protocols bgp group {{ p.group }} neighbor {{ p.ip }} description "{{ p.desc | default('peer-' ~ p.asn) }}"
{% endfor %}

Dùng macro từ file khác: {% from 'macros.j2' import iface %}. Chia template: {% include 'snmp.j2' %}.

Render thử

Mục đích Lệnh
Render bằng Python, báo lỗi biến thiếu xem code block dưới
Render bằng Ansible, không cần thiết bị task ansible.builtin.template với delegate_to: localhost rồi xem file
Sinh lệnh từ resource module state: rendered (xem Đẩy cấu hình IOS XR bằng Ansible)
render.py
import sys, yaml
from jinja2 import Environment, FileSystemLoader, StrictUndefined
 
env = Environment(loader=FileSystemLoader("templates"), trim_blocks=True,
                  lstrip_blocks=True, undefined=StrictUndefined)  # thiếu biến là lỗi, không in rỗng
data = yaml.safe_load(open(sys.argv[2]))
print(env.get_template(sys.argv[1]).render(**data))
# python3 render.py xr_interfaces.j2 host_vars/pe1-hni.yml