ansible Jinja2 流程控制
Jinja2 在 Ansible 中的模板渲染功能非常强大,支持条件判断 (if
)、循环 (for
)、过滤器 (filters
)、宏 (macros
)、变量 (set
)、以及包含 (include
) 等流程控制机制。以下是更详细的介绍,并配有示例和参数解析。
1. 条件判断 (if
语句)
Jinja2 支持 if
语句进行逻辑判断,根据变量的值动态控制模板内容。
基本语法
{% if condition %} 条件为 True 时执行的代码 {% elif other_condition %} 另一个条件为 True 时执行的代码 {% else %} 所有条件都不满足时执行的代码 {% endif %}
示例
1.1 判断某个变量的值
{% if ansible_os_family == "Debian" %} apt update && apt upgrade -y {% elif ansible_os_family == "RedHat" %} yum update -y {% else %} echo "Unsupported OS" {% endif %}
ansible_os_family
是一个 Ansible 变量,它的值可以是"Debian"
、"RedHat"
等。- 这里根据不同的操作系统执行不同的命令。
1.2 检查变量是否已定义
{% if my_variable is defined %} {{ my_variable }} {% else %} "my_variable 未定义" {% endif %}
is defined
判断变量是否存在,防止 UndefinedError
。
示例
[root@master-1 nginx]# cat vars.yaml nginx_version: "1.22.1" nginx_src_dir: "/tmp/nginx/" nginx_install_dir: "/usr/local/nginx" #server_name: localhost server_name: 127.0.0.1 #listen_port: 80
server { {% if listen_port is defined %} listen {{ listen_port }}; {% else %} listen 80; # 给一个默认值 {% endif %} {% if server_name is defined %} server_name {{ server_name }}; # 顶格写4个就可以跟listen对齐 {% elif server_name2 is defined %} server_name {{ server_name2 }}; {% endif %} location / { root html; index index.html index.htm; }
1.3 检查变量是否为空
{% if my_list %} List is not empty {% else %} List is empty {% endif %}
在 Jinja2 中,空字符串 (""
)、空列表 ([]
)、空字典 ({}
)、None
都会被视为 False
。
2. 循环 (for
语句)
Jinja2 的 for
语句用于遍历列表、字典等数据结构。
基本语法
{% for item in some_list %} {{ item }} {% endfor %}
示例
2.1 遍历列表
{% for user in users %} 用户名: {{ user.name }}, 角色: {{ user.role }} {% endfor %}
users
可能是:
users: - name: Alice role: Admin - name: Bob role: User
输出:
用户名: Alice, 角色: Admin
用户名: Bob, 角色: User
2.2 遍历字典
{% for key, value in my_dict.items() %} Key: {{ key }}, Value: {{ value }} {% endfor %}
my_dict
可能是:
my_dict: hostname: server1 ip: 192.168.1.100
输出:
Key: hostname, Value: server1 Key: ip, Value: 192.168.1.100
2.3 使用 loop
变量
Jinja2 提供 loop
变量来获取循环状态:
{% for user in users %} {{ loop.index }}. {{ user.name }} - {{ user.role }} {% endfor %}
loop.index
:从1
开始的索引loop.index0
:从0
开始的索引loop.first
:是否为第一个元素 (True
/False
)loop.last
:是否为最后一个元素 (True
/False
)
2.4 反向遍历
{% for num in numbers | reverse %} {{ num }} {% endfor %}
3. 过滤器 (Filters)
Jinja2 提供了 过滤器 来对变量进行操作。常见的过滤器如下:
3.1 常用过滤器
{{ some_variable | filter_name }}
过滤器 | 作用 | 示例 |
---|---|---|
upper |
转大写 | "hello" → "HELLO" |
lower |
转小写 | "HELLO" → "hello" |
replace(old, new) |
替换字符串 | "Hello World" → "Hello Jinja" |
default(value) |
默认值 | `undefined_variable |
length |
获取长度 | [1, 2, 3] → 3 |
join(separator) |
连接列表 | ['a', 'b', 'c'] → "a, b, c" |
sort |
排序 | [3, 1, 2] → [1, 2, 3] |
示例
{{ "hello world" | upper }} {# 结果:HELLO WORLD #} {{ my_list | join(", ") }} {# 结果:"item1, item2, item3" #}
4. 宏 (Macros)
Jinja2 允许定义 宏(类似于函数),可以复用代码。
{% macro macro_name(param1, param2) %}
宏的内容
{% endmacro %}
示例
{% macro user_info(user) %} 用户名: {{ user.name }}, 角色: {{ user.role }} {% endmacro %} # 传参,传入一个字典 {{ user_info({'name': 'Alice', 'role': 'Admin'}) }}
5. 变量赋值 (set
)
在 Jinja2 模板中,可以使用 set
关键字定义变量。
{% set variable_name = value %}
6. 继承 (include
和 extends
)
Jinja2 允许在模板中 包含其他模板,实现模板复用。
6.1 include
语法
{% include "header.j2" %}
- 这将在当前位置插入
header.j2
的内容。
6.2 extends
继承
在 Ansible 的 Jinja2 模板系统中,extends
允许模板继承另一个模板的结构,使得多个模板可以共享一个基础模板 (base template
),实现 代码复用 和 模块化设计。
1. extends
的基本原理
extends
用于继承一个 基础模板 (base template
)。- 继承的模板 必须 定义
block
,子模板 (child template
) 可以覆盖这些block
以填充内容。 - 这种结构适用于 HTML 文件、配置文件、YAML/JSON 文件等,可减少代码重复,提高可维护性。
2. extends
语法
2.1 base.j2
(基础模板)
{% block header %} 默认头部 {% endblock %} {% block content %} 默认内容 {% endblock %} {% block footer %} 默认页脚 {% endblock %}
- 这里定义了三个
block
:header
(头部)content
(内容)footer
(页脚)
- 这些
block
允许子模板覆盖它们。
2.2 child.j2
(子模板,继承 base.j2
)
{% extends "base.j2" %} {% block header %} 自定义头部 {% endblock %} {% block content %} 这里是子模板的主要内容。 {% endblock %}
child.j2
继承了 base.j2
:
header
覆盖为"自定义头部"
。content
也进行了自定义。footer
未覆盖,因此仍然使用base.j2
中的默认值 ("默认页脚"
)。
2.3 渲染后的结果
自定义头部
这里是子模板的主要内容。
默认页脚
header
被子模板覆盖,显示"自定义头部"
。content
也被子模板覆盖。footer
没有被子模板修改,因此使用基础模板的默认值"默认页脚"
。
3. extends
的使用规则
1.extends
语句必须是模板的第一行:
{% extends "base.j2" %}
不能有空行或其他内容,否则会报错。
2. block
只能出现在 extends
继承的模板中:
{% block content %}这里是内容{% endblock %}
错误示例(没有继承 extends
):
{% block content %}报错:Jinja2 会认为block
没有匹配的extends
。
-
未覆盖的
block
将使用base
模板中的默认值: - 如果子模板 不覆盖某个
block
,那么它仍然会显示base.j2
里定义的默认内容。
4. extends
的应用场景
4.1 生成 Nginx 配置文件
基础模板:nginx_base.j2
server { listen 80; server_name {% block server_name %}default.server{% endblock %}; location / { {% block location %} root /var/www/html; {% endblock %} } }
子模板:nginx_custom.j2
{% extends "nginx_base.j2" %} {% block server_name %} example.com {% endblock %} {% block location %} root /var/www/example; {% endblock %}
渲染结果
server { listen 80; server_name example.com; location / { root /var/www/example; } }
server_name
和 location
被子模板覆盖,其他内容保持不变。
4.2 生成 Ansible 配置文件
基础模板:ansible_base.j2
[defaults] inventory = {% block inventory %}/etc/ansible/hosts{% endblock %} timeout = {% block timeout %}10{% endblock %}
子模板:ansible_custom.j2
{% extends "ansible_base.j2" %} {% block inventory %} /home/user/custom_inventory {% endblock %}
渲染结果
[defaults] inventory = /home/user/custom_inventory timeout = 10
inventory
被覆盖,timeout
使用默认值。
5. extends
vs include
的区别
特性 | extends | include |
---|---|---|
继承关系 | 子模板继承父模板 | 只是简单插入另一个文件 |
block 支持 |
✅ 支持 block 结构 |
❌ 不能使用 block |
适用于 | 页面结构、配置结构 | 静态文件、可复用片段 |
include
只是 直接插入 文件内容,不涉及 block
继承。如果只是插入小代码片段,include
更合适。
总结
Jinja2 功能 | 作用 | 示例 |
---|---|---|
if |
逻辑判断 | {% if var %} ... {% endif %} |
for |
循环 | {% for item in list %} ... {% endfor %} |
filters |
数据处理 | `{{ text |
macro |
代码复用 | {% macro func() %} ... {% endmacro %} |
set |
变量赋值 | {% set var = value %} |
include |
引入模板 | {% include "file.j2" %} |
本文来自博客园,作者:不会跳舞的胖子,转载请注明原文链接:https://www.cnblogs.com/rtnb/p/18725919