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