Jinja2和template模块的使用
Jinja2简介
Ansible 模板是一种强大的工具,可用于在自动化任务中以动态方式生成配置、文件和其他工件。模板使用 Jinja2 模板语言,这是一种功能强大的模板语言,可用于创建复杂的模板。
Jinja2 模板语言:使用字面量,有下面形式。
- 字符串:使用单引号或者双引号数字:
-
整数、浮点数
-
列表元组:[item1、item2、……]
-
字典:[key1:value1,key2:value2,……]
-
布尔型:true/false
-
算数运算:+ ,-, *, /,/ /,%,**
-
比较操作:==, !=, >, >=, <, <=
-
逻辑运算:and、or、not
-
流表达式:If For When
jinja2的语法结构
playbook中的tasks 必须使用template 模板;
- {{ }}:用来装载表达式,比如:变量、运算表达式、比较表达式;
- {% %}:用来装载控制语句,比如 if 控制结构,for循环控制结构;
- {# #}:用来装载注释,模板文件被渲染后,注释不会包含在最终生成的文件中;
- jinja2 的文件后缀一般为 .j2
注意:
- 在jinja2模板文件里使用for语句和if语句,playbook中使用loop语句和when语句。
控制语句
if表达式和注释
if表达式
{{ ‘A’ if 4>1 else ‘B’}}
如果4>1条件为真,输出A,否则输出B
jinja.yml
- name: jinjia模板配置文件
hosts: all
tasks:
- name: jinjia
template:
src: host.j2
dest: /tmp/hosts
cat host.j2
{{ 'A' if 4>1 else 'B'}}
注释
修改配置文件中ansible_managed的值
{# 这是注释,不会输出到文件中 #}
{{ ansible_managed }}
{{ 'A' if 4<1 else 'B'}}
if条件语句
1.普通if
{% if 条件 %}
...
{% endif %}
2.if…else
语法:
{% if 条件 %}
...
{% else %}
...
{% endif %}
3.if…elif…else
语法:
{% if 条件 %}
...
{% elif 条件N %}
...
{% else %}
...
{% endif %}
if语句测试
---
- name: jinjia模板配置文件
hosts: all
vars:
score: 75
tasks:
- name: jinjia
template:
src: host.j2
dest: /tmp/hosts
[root@eab5aec2df6c palybook]# cat host.j2
{# 这是注释,不会输出到文件中 #}
{{ ansible_managed }}
{% if score >80 %}
优秀
{% elif score>60 %}
及格
{% else %}
不及格
{% endif %}
修改score变量的值
for循环
{% for 迭代变量 in 可迭代对象%}
{{ 迭代变量 }}
{% endfor %}
{% for i in [0,2,4,10,5] %}
{{ i }}
{% endfor %}
{% for key,val in {'name':'shadowwu','age':'29'}.items() %}
{{ key ~ ': '~ val }}
{%endfor%}
python2使用iteritems()函数,python3使用items(),~连接变量与字符串
for循环加if
{% for i in [3,2,5,9,7] if i >= 3 -%}
{{ i~' ' }}
{%- endfor %}
-%}{%-让输出不会换行
for递归操作
{%- set dictionary={ 'name':'shadow','son':{ 'name':'zhangsan','son':{ 'name':'lisi' } } } -%}
{%- for key,val in dictionary.items() recursive -%}
{% if key == 'name' %}
{{ val -}}
{%- endif -%}
{% if key == 'son' -%}
{{ ' son is '~ val.name }}
{% if val.son is defined -%}
{{ loop(val.items() ) -}}
{% endif %}
{% endif %}
{%- endfor -%}
在for循环的末尾,我们添加了recursive 修饰符,当for循环中有recursive时,表示这个循环是一个递归的循环,当我们需要在for循环中进行递归时,只要在需要进行递归的地方调用loop函数即可
注意在表达式的两头添加-消除空格换行,Jinja2默认只替换数据,不消除原来的空格和换行
Jinja2提供了两个配置项:lstrip_blocks
和trim_blocks
,它们的意义分别是:
- (1).
lstrip_blocks
:设置为true时,会将Jinja2语句块前面的本行前缀空白符号移除 - (2).
trim_blocks
:设置为true时,Jinja2语句块后的换行符会被移除掉
对于Ansible的template模块,lstrip_blocks
默认设置为False,trim_blocks
默认设置为true。也就是说,默认情况下,template模块会将语句块后面的换行符移除掉,但是会保留语句块前的本行前缀空白符号。
---
- name: jinjia模板配置文件
hosts: all
vars:
score: 40
tasks:
- name: jinjia
template:
src: host.j2
dest: /tmp/hosts
lstrip_blocks: true
trim_blocks: true
{% set dictionary={ 'name':'wuzhaobo','son':{ 'name':'aaa','son':{ 'name':'bbb' } } } %}
{% for key,val in dictionary.items() recursive %}
{% if key == 'name' %}
{{ val -}}
{% endif %}
{% if key == 'son' %}
{{ ' son is '~ val.name }}
{% if val.son is defined %}
{{ loop(val.items() ) }}
{% endif %}
{% endif %}
{% endfor %}
注意,有实际输出的行,前面的空格是有效的
for循环+playbook变量组
{% for name in group %}
number user:{{ loop.index }} ,{{ name }}
{% endfor %}
---
- name: jinjia模板配置文件
hosts: localhost
vars:
group:
- zhangsan
- lisi
- wangwu
fruit:
- apple
- banana
- orange
tasks:
- name: jinjia
template:
src: host.j2
dest: /tmp/hosts
lstrip_blocks: true
trim_blocks: true
将group改为groups就是默认的清单文件组
ansible启用break 和continue扩展
默认情况下 ansible是无法使用break和continue,只不过需要修改配置文件/etc/ansible/ansible.cfg
文件151行设置jinja2_extension选项,取消注释,并添加loopcontrols 扩展
jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n,jinja2.ext.loopcontrols
{% for i in [5,2,8,4] %}
{% if loop.index > 2 %}
{%break%}
{%endif%}
{{i ~'---'~ loop.index}}
{% endfor %}
loop.index从1开始
---
- name: jinjia模板配置文件
hosts: localhost
vars:
score: 40
tasks:
- name: jinjia
template:
src: host.j2
dest: /tmp/hosts
lstrip_blocks: true
trim_blocks: true
变量过滤器
- 变量过滤器就是优化输出内容格式,方便阅读。
- 格式:{{ 变量 | to_json }}
# {{ ansible_managed }}
{# to_json #}
{% if 1 < 2 %}
{{ ansible_facts['default_ipv4'] | to_json }}
{% endif %}
# {{ ansible_managed }}
{# to_nice_json #}
{% if 1 < 2 %}
{{ ansible_facts['default_ipv4'] | to_nice_json }}
{% endif %}
# {{ ansible_managed }}
{# to_yaml #}
{% if 1 < 2 %}
{{ ansible_facts['default_ipv4'] |to_yaml }}
{% endif %}
# {{ ansible_managed }}
{# to_nice_yaml #}
{% if 1 < 2 %}
{{ ansible_facts['default_ipv4'] | to_nice_yaml }}
{% endif %}
转义操作
普通转义
在模板文件中,使用“{{ }}”,“{{% %}}”,“{{# #}}”来定义模板,一旦遇到要使用这些特殊字符怎么办,在里面用引号括起来,当中字符处理即可,单引号反斜杠进行转义
# {{ ansible_managed }}
{{ '{{\'' ~ ' ' ~ '}}' }}
{{ '{{ test abc }}' }}
raw块转义
如果有较多这样的字符需要保持原样,使用引号转义比较麻烦,可以使用“{% raw %}”来实现块的转义
{% raw %}
# {{ ansible_managed }}
{{ '{{\'' ~ ' ' ~ '}}' }}
{{ '{{ test abc }}' }}
{% endraw %}
宏的使用
jinja2中有类似函数的东西,它叫做”宏”,利用宏,我们可以方便快捷的重复的利用一段内容。
定义时需要使用”{% macro %}“开头,使用”{% endmacro %}”结束,然后在使用的地方进行调用。
简单的无参宏
{% macro testfunc() %}
some thing
{% endmacro %}
传参数的宏
// 定义了两个变量
{% set myname='shadow' %}
{% set myage=18 %}
{% macro testfunc(a,b) %}
我的名字是:{{ a }}
我的年龄是:{{ b }}
{% endmacro %}
//调用宏并传参
{{ testfunc(myname,myage) }}
定义参数的默认值
// 定义了两个变量
{% set myname='shadow' %}
{% set myage=18 %}
{% macro testfunc(a='张三',b=28) %}
我的名字是:{{ a }}
我的年龄是:{{ b }}
{% endmacro %}
//调用宏并传参
{{ testfunc() }}
{{ testfunc(myname,myage) }}
可以看到,调用无参的宏使用了默认的值
宏内部特殊变量
在宏的内部,有三个默认的内置特殊变量,分别是:varargs、kwargs、caller
varargs
我们在调用宏时,多传入几个参数,这些额外的参数会作为一个元组织报错在varargs变量上,我们可以通过获取 varargs变量的值,获取到额外的参数
// 定义了两个变量
{% set myname='shadow' %}
{% set myage=18 %}
{% macro testfunc(a='张三',b=28) %}
我的名字是:{{ a }}
我的年龄是:{{ b }}
{% for i in varargs -%}
{{ i ~' ' }}
{% endfor %}
{% endmacro %}
//调用宏并传参
{{ testfunc(myname,myage,45,36,28) }}
kwargs
kwargs变量和varargs变量很像,但是kwargs变量值是针对“关键字参数”而言,而varargs变量时针对“非关键字参数”而言
varargs变量的结构是一个元组,kwargs变量的结构是一个字典,kwargs必须在参数末尾添加
// 定义了两个变量
{% set myname='shadow' %}
{% set myage=18 %}
{% macro testfunc(a='张三',b=28) %}
{{ kwargs }}
{{ varargs }}
{% for i in varargs -%}
{{ i ~' ' }}
{% endfor %}
{% endmacro %}
//调用宏并传参
{{ testfunc(myname,myage,45,36,wangwu='28',liuxiu=88) }}
caller
caller可以帮助我们将宏中的内容进行替换,当我们要传入大段内容或者复杂的内容时,可以借助caller进行传递。
类似导入代码,使用一下进行定义
{% call 宏 %}
{% endcall %}
在宏中使用{{ caller() }}调用
{% macro 宏() %}
{{ caller() }}
{% endmacro %}
在一个宏中调用另一个宏,有两种方法
{% macro testfunc() %}
test
{{ caller() }}
{% endmacro %}
{% macro testfunc1() %}
{% for i in range(3) -%}
{{ i~ ' ' }}
{%- endfor %}
{% endmacro %}
{% call testfunc() %}
{{ testfunc1() }}
{% endcall %}
{% macro testfunc() %}
test
{{ testfunc1() }}
{% endmacro %}
{% macro testfunc1() %}
{% for i in range(3) -%}
{{ i~ ' ' }}
{%- endfor %}
{% endmacro %}
{{ testfunc() }}
宏的属性
属性 | 含义 |
---|---|
name | 宏的名称 |
arguments | 宏中定义的所有参数名 |
catch_varargs | 宏中如果使用了变量,此值为true |
catch_kwargs | 宏中使用了关键字,此值为true |
caller | 宏中使用了caller变量,此值为true |
{% macro testfunc(a=44,b=66) %}
test
{{ caller() }}
{% endmacro %}
{% macro testfunc1() %}
{% for i in range(3) -%}
{{ i~ ' ' }}
{%- endfor %}
{% endmacro %}
{% call testfunc() %}
{{ testfunc1() }}
{% endcall %}
{{ testfunc.name }}
{{ testfunc.arguments }}
{{ testfunc.catch_varargs }}
{{ testfunc.catch_kwargs }}
{{ testfunc.caller }}
宏的导入
如果宏的定义和宏的使用不在同一个模板文件中,则需要先使用{% from <宏所在文件> import <宏名> %}引入宏的定义再进行使用。
host.j2
/ 定义了两个变量
{% set myname='shadow' %}
{% set myage=18 %}
{% from 'test.j2' import testfunc %}
//调用宏并传参
{{ testfunc(myname,myage) }}
test.j2
{% macro testfunc(a,b) %}
我的名字是:{{ a }}
我的年龄是:{{ b }}
{% endmacro %}
问题及解决
jinja2模板空格换行问题
配置lstrip_blocks和trim_blocks为true时,Jinja2语句块后的换行符会被移除掉
参考
Ansible基础6——文件模块、jinja2模板_ansible修改文件内容 模块-CSDN博客
十二、ansible中的模板 template 模块和 jinja2模板引擎_ansible template-CSDN博客