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

浙公网安备 33010602011771号