1. hello jinja2模板
cat temptest.yml
---
- hosts: test70
remote_user: root
gather_facts: no
tasks:
- yum:
name: redis
state: present
- template:
src: /testdir/ansible/redis.conf
dest: /etc/redis.conf
模板功能可以灵活的生成配置文件,只需先选择一个文件作为模板文件,然后修改模板 文件中需要灵活生成的部分,使用变量进行替换(对应的变量必须提前定义好或者能够在运行时获取),模板文件中不需要灵活生成的部分保持不变即可,当需要为 各个目标主机生成配置文件时,只需调用template模块,template模块会在ansible控制机中对模板文件进行渲染,最终生成各个主机对应 的配置文件,然后拷贝到远程主机的指定位置中。
除了在playbook中能够使用template模块,在ad-hoc命令中也可以直接调用template模块,在ad-hoc命令中使用template模块比较方便测试模板文件的最终生成效果
ansible test70 -m template -a "src=/testdir/ansible/redis.conf dest=/opt/redis.conf"
owner参数: 指定最终生成的文件拷贝到远程主机后的属主。
group参数: 指定最终生成的文件拷贝到远程主机后的属组。
mode参数: 指定最终生成的文件拷贝到远程主机后的权限,如果想将权限设置为"rw-r–r--",则可以使用mode=0644表示,如果想要在user对应的权限位上添加执行权限,则可以使用mode=u+x表示。
force参数: 当远程主机的目标路径中已经存在同名文件,并且与最终生成的文件内容不同时,是否强制覆盖,可选值有yes和no,默认值为yes,表示复盖,如果设置为no,则不会执行覆盖拷贝操作,远程主机中的文件保持不变。
backup参数: 当远程主机的目标路径中已经存在同名文件,并且与最终生成的文件内容不同时,是否对远程主机的文件进行备份,可选值有yes和no,当设置为yes时,会先备份远程主机中的文件,然后再将最终生成的文件拷贝到远程主机。
2. jinja2的语法
2.1 基本表达式
{{ }} :用来装载表达式,比如变量、运算表达式、比较表达式等。
{% %} :用来装载控制语句,比如 if 控制结构,for循环控制结构。
{# #} :用来装载注释,模板文件被渲染后,注释不会包含在最终生成的文件中。
cat test.j2
test jinja2 variable
test {{ testvar1 }} test
ansible test70 -m template -e "testvar1=teststr" -a "src=test.j2 dest=/opt/test"
除了变量,"{{ }}"中还可以包含一些表达式
cat test.j2
jinja2 test
{{ 1 == 1 }}
{{ 2 != 2 }}
{{ 2 > 1 }}
{{ 2 >= 1 }}
{{ 2 < 1 }}
{{ 2 <= 1 }}
生成文件内容如下:
cat test
jinja2 test
True
False
True
True
False
False
逻辑运算的相关示例
cat test.j2
jinja2 test
{{ (2 > 1) or (1 > 2) }}
{{ (2 > 1) and (1 > 2) }}
{{ not true }}
{{ not True }}
{{ not false }}
{{ not False }}
生成文件内容
cat test
jinja2 test
True
False
False
False
True
True
算数运算的相关示例
模板文件内容
cat test.j2
jinja2 test
{{ 3 + 2 }}
{{ 3 - 4 }}
{{ 3 * 5 }}
{{ 2 ** 3 }}
{{ 7 / 5 }}
{{ 7 // 5 }}
{{ 17 % 5 }}
生成文件内容
cat test
jinja2 test
5
-1
15
8
1.4
1
2
成员运算的相关示例
cat test.j2
jinja2 test
{{ 1 in [1,2,3,4] }}
{{ 1 not in [1,2,3,4] }}
生成文件内容
cat test
jinja2 test
True
False
jinja2本身就是基于python的模板引擎,所以,python的基础数据类型都可以包含在"{{ }}"中
cat test.j2
jinja2 test
### str
{{ 'testString' }}
{{ "testString" }}
### num
{{ 15 }}
{{ 18.8 }}
### list
{{ ['Aa','Bb','Cc','Dd'] }}
{{ ['Aa','Bb','Cc','Dd'].1 }}
{{ ['Aa','Bb','Cc','Dd'][1] }}
### tuple
{{ ('Aa','Bb','Cc','Dd') }}
{{ ('Aa','Bb','Cc','Dd').0 }}
{{ ('Aa','Bb','Cc','Dd')[0] }}
### dic
{{ {'name':'bob','age':18} }}
{{ {'name':'bob','age':18}.name }}
{{ {'name':'bob','age':18}['name'] }}
### Boolean
{{ True }}
{{ true }}
{{ False }}
{{ false }}
生成文件内容如下:
cat test
jinja2 test
### str
testString
testString
### num
15
18.8
### list
['Aa', 'Bb', 'Cc', 'Dd']
Bb
Bb
### tuple
('Aa', 'Bb', 'Cc', 'Dd')
Aa
Aa
### dic
{'age': 18, 'name': 'bob'}
bob
bob
### Boolean
True
True
False
False
字符串、数值、列表、元组、字典、布尔值等数据类型调用
jinja2 test
{{ teststr }}
{{ testnum }}
{{ testlist[1] }}
{{ testlist1[1] }}
{{ testdic['name'] }}
cat temptest.yml
---
- hosts: test70
remote_user: root
gather_facts: no
vars:
teststr: 'tstr'
testnum: 18
testlist: ['aA','bB','cC']
testlist1:
- AA
- BB
- CC
testdic:
name: bob
age: 18
tasks:
- template:
src: /testdir/ansible/test.j2
dest: /opt/test
运行上例playbook以后,最终生成的文件如下
cat test
jinja2 test
tstr
18
bB
BB
bob
过滤器也可以直接在"{{ }}"中使用
模板文件内容
cat test.j2
jinja2 test
{{ 'abc' | upper }}
生成文件内容
cat test
jinja2 test
ABC
模板文件内容
cat test.j2
jinja2 test
{{ testvar1 is defined }}
{{ testvar1 is undefined }}
{{ '/opt' is exists }}
{{ '/opt' is file }}
{{ '/opt' is directory }}
执行命令时传入变量
ansible test70 -m template -e "testvar1=1 testvar2=2" -a "src=test.j2 dest=/opt/test"
生成文件内容
cat test
jinja2 test
True
False
True
False
True
模板文件内容如下
cat /testdir/ansible/test.j2
jinja2 test
{{ lookup('file','/testdir/testfile') }}
{{ lookup('env','PATH') }}
test jinja2
ansible主机中的testfile内容如下
cat /testdir/testfile
testfile in ansible
These are for testing purposes only
生成文件内容如下
cat test
jinja2 test
testfile in ansible
These are for testing purposes only
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
test jinja2
注释信息
模板文件内容如下:
cat test.j2
jinja2 test
{#这是一行注释信息#}
jinja2 test
{#
这是多行注释信息,
模板被渲染以后,
最终的文件中不会包含这些信息
#}
jinja2 test
生成文件内容如下:
cat test
jinja2 test
jinja2 test
jinja2 test
2.2 条件语句
{% if 条件 %}
...
...
...
{% endif %}
cat test.j2
jinja2 test
{% if testnum > 3 %}
greater than 3
{% endif %}
不要使用ad-hoc命令调用template模块进行渲染,因为使用命令调用template模块时,无论传入的数据是哪种类型,都会被当做字符串进行处理,所以此处使用playbook渲染模板,以保证数据类型的正确。
cat temptest.yml
---
- hosts: test70
remote_user: root
gather_facts: no
tasks:
- template:
src: /testdir/ansible/test.j2
dest: /opt/test
vars:
testnum: 5
最终生成的文件内容
cat /opt/test
jinja2 test
greater than 3
{% if 条件 %}
...
{% else %}
...
{% endif %}
{% if 条件一 %}
...
{% elif 条件二 %}
...
{% elif 条件N %}
...
{% endif %}
{% if 条件一 %}
...
{% elif 条件N %}
...
{% else %}
...
{% endif %}
if表达式,利用if表达式,可以实现类似三元运算的效果
cat test.j2
jinja2 test
{{ 'a' if 2>1 else 'b' }}
结果
cat /opt/test
jinja2 test
a
<do something> if <something is true> else <do something else
在模板文件中定义变量
cat test.j2
jinja2 test
{% set teststr='abc' %}
{{ teststr }}
ansible test70 -m template -a "src=test.j2 dest=/opt/test"
最终生成的文件内容
cat /opt/test
jinja2 test
abc
2.3 循环语句
{% for 迭代变量 in 可迭代对象 %}
{{ 迭代变量 }}
{% endfor %}
cat test.j2
jinja2 test
{% for i in [3,1,7,8,2] %}
{{ i }}
{% endfor %}
ansible test70 -m template -a "src=test.j2 dest=/opt/test"
最终生成的文件内容
cat /opt/test
jinja2 test
3
1
7
8
2
每次循环后都会自动换行,如果不想要换行
cat test.j2
jinja2 test
{% for i in [3,1,7,8,2] -%}
{{ i }}
{%- endfor %}
在for的结束控制符"%}“之前添加了减号”-"
在endfor的开始控制符"{%“之后添加到了减号”-"
渲染上述模板,最终的生成效果
cat test
jinja2 test
31782
列表中的每一项都没有换行,而是连在了一起显示,如果觉得这样显示有些"拥挤",则可以稍微改进一下
jinja2 test
{% for i in [3,1,7,8,2] -%}
{{ i }}{{ ' ' }}
{%- endfor %}
最终生成的效果
cat test
jinja2 test
3 1 7 8 2
继续改进
cat test.j2
jinja2 test
{% for i in [3,1,7,8,2] -%}
{{ i~' ' }}
{%- endfor %}
jinja2中,波浪符"~"就是字符串连接符,它会把所有的操作数转换为字符串,并且连接它们
for除了能够循环操作列表,也能够循环操作字典
iteritems函数也可以替换成items函数,但是推荐使用iteritems函数
cat test.j2
jinja2 test
{% for key,val in {'name':'bob','age':18}.iteritems() %}
{{ key ~ ':' ~ val }}
{% endfor %}
最终生成内容
cat test
jinja2 test
age:18
name:bob
在使用for循环时,有一些内置的特殊变量可以使用,比如,如果我想要知道当前循环操作为整个循环的第几次操作,则可以借助"loop.index"特殊变量
cat test.j2
jinja2 test
{% for i in [3,1,7,8,2] %}
{{ i ~ '----' ~ loop.index }}
{% endfor %}
最终生成文件
cat test
jinja2 test
3----1
1----2
7----3
8----4
2----5
其他的内置变量
loop.index 当前循环操作为整个循环的第几次循环,序号从1开始
loop.index0 当前循环操作为整个循环的第几次循环,序号从0开始
loop.revindex 当前循环操作距离整个循环结束还有几次,序号到1结束
loop.revindex0 当前循环操作距离整个循环结束还有几次,序号到0结束
loop.first 当操作可迭代对象中的第一个元素时,此变量的值为true
loop.last 当操作可迭代对象中的最后一个元素时,此变量的值为true
loop.length 可迭代对象的长度
loop.depth 当使用递归的循环时,当前迭代所在的递归中的层级,层级序号从1开始
loop.depth0 当使用递归的循环时,当前迭代所在的递归中的层级,层级序号从0开始
loop.cycle() 这是一个辅助函数,通过这个函数可以在指定的一些值中进行轮询取值
对一段内容循环的生成指定的次数,则可以借助range函数完成,比如,循环3次,range函数可以指定起始数字、结束数字、步长等,默认的起始数字为0
{% for i in range(3) %}
something
...
{% endfor %}
{% for i in range(1,4,2) %}
{{i}}
{% endfor %}
默认情况下,模板中的for循环不能像其他语言中的 for循环那样使用break或者continue跳出循环,但是可以在"for"循环中添加"if"过滤条件,以便符合条件时,循环才执行真正的操作
{% for i in [7,1,5,3,9] if i > 3 %}
{{ i }}
{% endfor %}
{% for i in [7,1,5,3,9] %}
{% if i>3 %}
{{ i }}
{%endif%}
{% endfor %}
{% for i in [7,1,5,3,9] if i>3 %}
{{ i ~'----'~ loop.index }}
{% endfor %}
{% for i in [7,1,5,3,9] %}
{% if i>3 %}
{{ i ~'----'~ loop.index}}
{% endif %}
{% endfor %}
最终生成的内容
cat test
7----1
5----2
9----3
7----1
5----3
9----5
for循环中使用了if内联表达式时,还可以与else控制语句结合使用
{% for i in [7,1,5,3,9] if i>10 %}
{{ i }}
{%else%}
no one is greater than 10
{% endfor %}
只有userlist列表为空时,才会渲染else块后的内容。
{% for u in userlist %}
{{ u.name }}
{%else%}
no one
{% endfor %}
for循环也支持递归操作
定义了一个字典变量,从字典中可以看出,bob的儿子是tom,tom的儿子是jerry,然后使用for循环操作了 这个字典,如前文所示,在操作字典时,使用了iteritems函数,在for循环的末尾,添加了recursive 修饰符,当for循环中有recursive时,表示这个循环是一个递归的循环,当需要在for循环中进行递归时,只要在需要进行递归的地方调用 loop函数即可,上例中的loop( value.iteritems() )即为调用递归的部分,由于value也是一个字典,所以需要使用iteritems函数进行处理
{% set dictionary={ 'name':'bob','son':{ 'name':'tom','son':{ 'name':'jerry' } } } %}
{% for key,value in dictionary.iteritems() recursive %}
{% if key == 'name' %}
{% set fathername=value %}
{% endif %}
{% if key == 'son' %}
{{ fathername ~"'s son is "~ value.name}}
{{ loop( value.iteritems() ) }}
{% endif %}
{% endfor %}
渲染最终效果
bob's son is tom
tom's son is jerry
辅助函数loop.cycle()
{% set userlist=['Naruto','Kakashi','Sasuke','Sakura','Lee','Gaara','Itachi'] %}
{% for u in userlist %}
{{ u ~'----'~ loop.cycle('team1','team2','team3')}}
{%endfor%}
最终生成内容
Naruto----team1
Kakashi----team2
Sasuke----team3
Sakura----team1
Lee----team2
Gaara----team3
Itachi----team1
默认情况下,模板中的for循环无法使用break和continue,不过jinja2支持一些扩展,如果在ansible中启用这些扩展,则可以让模板中的for循环支持break和continue,方法如下:
如果想要开启对应的扩展支持,需要修改ansible的配置文件/etc/ansible/ansible.cfg,默认情况下未启用jinja2 的扩展,如果想要启用jinja2扩展,则需要设置jinja2_extension选项,这个设置项默认情况下是注释的。
jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n,jinja2.ext.loopcontrols
完成上述配置步骤即可在for循环中使用break和continue控制语句,与其他语言一样,break表示结束整个循环,continue表示结束当次循环
{% for i in [7,1,5,3,9] %}
{% if loop.index is even %}
{%continue%}
{%endif%}
{{ i ~'----'~ loop.index }}
{% endfor %}
{% for i in [7,1,5,3,9] %}
{% if loop.index > 3 %}
{%break%}
{%endif%}
{{i ~'---'~ loop.index}}
{% endfor %}
如果想要在jinja2中修改列表中的内容,则需要借助jinja2的另一个扩展,这个扩展的名字就是"do", 默认就有这个扩展,它的名字是jinja2.ext.do。
{% set testlist=[3,5] %}
{% for i in testlist %}
{{i}}
{% endfor %}
{%do testlist.append(7)%}
{% for i in testlist %}
{{i}}
{% endfor %}
2.4 转义
cat test.j2
{{ '{{' }}
{{ '}}' }}
{{ '{{ test string }}' }}
{{ '{% test string %}' }}
{{ '{# test string #}' }}
转义如果有较大的段落时,可以借助"{% raw %}"块,来实现刚才的需求
cat test.j2
{% raw %}
{{ test }}
{% test %}
{# test #}
{% if %}
{% for %}
{% endraw %}
最终生成内容
cat test
{{ test }}
{% test %}
{# test #}
{% if %}
{% for %}
默认情况下,变量和表达式被包含在"{{ }}“中,控制语句被包含在”{% %}",其实,也可以在调用模板引擎时,手动的指定一些符号,这些符号可以替换默认的"{{ }}“和”{% %}",当在ansible中调用templdate模块时,可以使用variable_start_string参数指定一个符号,这个符号用于替 换"{{ }}“中的”{{",同时,可以使用variable_end_string参数指定一个符号,这个符号用于替换"{{ }}“中的”}}" 。
cat test.j2
{% set test='abc' %}
(( test ))
{{ test }}
{{ test1 }}
{{ 'test' }}
{{ 'test1' }}
ansible test70 -m template -a "src=test.j2 dest=/opt/test variable_start_string='((' variable_end_string='))'"
最终生成的文件
cat test
abc
{{ test }}
{{ test1 }}
{{ 'test' }}
{{ 'test1' }}
同理,可以使用block_start_string参数指定一个符号,这个符号用于替换"{% %}“中的”{% "
可以使用block_end_string参数指定一个符号,这个符号用于替换"{% %}“中的”%}"
2.5 宏
利用宏,可以方便快捷的重复的利用一段内容,并且把这段内容当做一个独立的逻辑单元,与其他语言中的函数一样,jinja2的宏也可以传入参数。
定义宏时需要使用"{% macro %}“开头,使用”{% endmacro %}“结束,上例中,定义了一个名为testfunc的宏,与大多数语言中的函数一样,宏的括号中可以传入参数,testfunc中的内容没有什么意义,只是用于示例宏的用法,”{% macro %}“和”{% endmacro %}"之间的内容只是用来定义宏,如果想要真正的使用宏,还需要引用它,最后一行就是在调用testfunc宏
cat test.j2
{% macro testfunc() %}
test string
{% endmacro %}
{{ testfunc() }}
传参的宏
cat test.j2
{% set testvar1='teststr1' %}
{% set testvar2=2 %}
{% macro testfunc(tv1,tv2) %}
test string
{{tv1}}
{{tv2}}
{% endmacro %}
{{ testfunc(testvar1,testvar2) }}
有一个很明显的问题,就是如果宏在定义的时候有对应的参数,在 调用宏时就必须传入对应的参数,否则就会报错,其实,还可以在定义宏时,为对应的参数指定一个默认值,当在调用宏时没有显式的指定对应的参数时,宏就 使用参数的默认值。没有默认值的参数必须在有默认值的参数之前,否则会出现错误。
{% macro testfunc(tv1=111) %}
test string
{{tv1}}
{% endmacro %}
{{ testfunc( ) }}
{{ testfunc(666) }}
渲染结果
test string
111
test string
666
在传入参数时,也可以显式的指明参数的名称。显式的指定参数名可以帮助灵活的传入参数。
{% macro testfunc(tv1,tv2=2,tv3=3) %}
test string
{{tv1}}
{{tv2}}
{{tv3}}
{% endmacro %}
{{ testfunc( 111,tv3='ccc' ) }}
渲染后内容
test string
111
2
ccc
在宏的内部,有三个默认的内置特殊变量可供使用,它们分别是varargs、kwargs、caller,在调用宏时,多传入几个额外的参数,这些额外的参数会作为一个元组保存在varargs变量上,可以通过获取varargs变量的值获取到额外传入的参数。
cat test.j2
{% macro testfunc(testarg1=1,testarg2=2) %}
test string
{{testarg1}}
{{testarg2}}
{{varargs}}
{% endmacro %}
{{ testfunc('a','b','c','d','e') }}
在调用宏时,传入了5个参数,而在宏里面,输出了内置变量varargs,最终渲染后的效果。
test string
a
b
('c', 'd', 'e')
如果宏没有定义任何参数,却传入了一些参数,那么这些所有传入的参数都是“多余”出的参数,也可以使用varargs变量处理这些参数
{% macro testfunc() %}
test string
{%for i in varargs%}
{{i}}
{%endfor%}
{{ '--------' }}
{% endmacro %}
{{ testfunc() }}
{{ testfunc(1,2,3) }}
最终渲染效果
test string
--------
test string
1
2
3
--------
kwargs变量与varargs变量的作用很像,但是kwargs变量只是针对’关键字参数’而言的,而varargs变量是针对’非关键字参数’而言的
{% macro testfunc(tv1='tv1') %}
test string
{{varargs}}
{{kwargs}}
{% endmacro %}
{{ testfunc('a',2,'test',testkeyvar='abc') }}
在定义宏时,定义了一个参数tv1,并且设置了默认值,在宏中,输出了varargs变量和kwargs变量,在调用宏时,多传入了3个参数,最后一个参数是一个带有参数名的关键字参数。多余的非关键字参数都会保存在varargs变量中,varargs变量的结构是一个元组,而多余的关键字参数都会保存在kwargs变量中,kwargs变量的结构是一个字典,kwargs变量实现的效果与Python的关键字参数效果类似。
test string
(2, 'test')
{'testkeyvar': 'abc'}
与其说是caller变量,不如称其为caller函数或者caller方法,caller可以帮助将宏中的内容进行替换。定义了一个testfunc宏,在testfunc宏中,"{{caller()}}“部分可以被"其他内容"替换,但是 此刻,还没有调用testfunc宏,如果想要替换testfunc宏中的”{{caller()}}"部分,则需要在调用testfunc宏时,使 用"call语句块"进行调用。
{% macro testfunc() %}
test string
{{caller()}}
{% endmacro %}
{% macro testfunc() %}
test string
{{caller()}}
{% endmacro %}
{%call testfunc()%}
something~~~~~
something else~~~~~
{%endcall%}
使用了"{%call%}“语句块调用了testfunc宏,”{%call%}“和”{%endcall%}“之间的内容将会替换testfunc宏中的”{{caller()}}"部分,上例模板最终渲染效果
test string
something~~~~~
something else~~~~~
caller其实还能够帮助在一个宏中调用另一个宏。定义了两个宏,testfunc和testfunc1,将testfunc1传递到了testfunc中。
{% macro testfunc() %}
test string
{{caller()}}
{% endmacro %}
{% macro testfunc1() %}
{% for i in range(3) %}
{{i}}
{% endfor %}
{% endmacro %}
{%call testfunc()%}
{{testfunc1()}}
{%endcall%}
caller()也可以接收参数,只要在call块中提前定义好,在caller中传入参数
{% macro testfunc() %}
test string
{{caller('somethingElse~~')}}
{% endmacro %}
{%call(testvar) testfunc()%}
something~~~~
{{testvar}}
{%endcall%}
call块中定义了testvar参数,call块中的内容使用了testvar参数,在testfunc宏中的调用caller时,传入了一个字符串作为参数,上例最终的渲染的效果。
test string
something~~~~
somethingElse~~
宏属性
name属性:宏的名称。
arguments属性:宏中定义的所有参数的参数名,这些参数名组成了一个元组存放在arguments中。
defaults属性:宏中定义的参数如果有默认值,这些默认值组成了一个元组存放在defaults中。
catch_varargs属性:如果宏中使用了varargs变量,此属性的值为true。
catch_kwargs属性: 如果宏中使用了kwargs变量,此属性的值为true。
caller属性:如果宏中使用了caller变量,此属性值为true。
cat test.j2
{% macro testfunc(tv1,tv2,tv3=3,tv4=4) %}
test string
{{tv1}}
{{tv2}}
{{tv3}}
{{tv4}}
{% endmacro %}
{{testfunc.name}}
{{testfunc.arguments}}
{{testfunc.defaults}}
{{testfunc.catch_varargs}}
{{testfunc.catch_kwargs}}
{{testfunc.caller}}
{{'################################'}}
{% macro testfunc1(tv1='a',tv2='b') %}
test string
{{tv1}}
{{tv2}}
{{varargs}}
{{kwargs}}
{% endmacro %}
{{testfunc1.catch_varargs}}
{{testfunc1.catch_kwargs}}
{{testfunc1.caller}}
{{'################################'}}
{% macro testfunc2() %}
test string
{{caller()}}
{% endmacro %}
{{testfunc2.caller}}
上例模板内容渲染后的结果
testfunc
('tv1', 'tv2', 'tv3', 'tv4')
(3, 4)
False
False
False
################################
True
True
False
################################
True
2.6 包含
cat test.j2
test...................
test...................
{% include 'test1.j2' %}
test...................
# cat test1.j2
test1.j2 start
{% for i in range(3) %}
{{i}}
{% endfor %}
test1.j2 end
最终结果
test...................
test...................
test1.j2 start
0
1
2
test1.j2 end
test...................
cat test.j2
{% set varintest='var in test.j2' %}
test...................
test...................
{% include 'test1.j2' %}
test...................
cat test1.j2
test1.j2 start
{{ varintest }}
test1.j2 end
最终结果
test...................
test...................
test1.j2 start
var in test.j2
test1.j2 end
test...................
不想让被包含文件能够使用到外部文件中定义的变量,则可以使用"without context"显式的设置"include",当"include"中存在"without context"时,表示不导入对应的上下文
cat test.j2
{% set varintest='var in test.j2' %}
test...................
test...................
{% include 'test1.j2' without context %}
test...................
cat test1.j2
test1.j2 start
{{ varintest }}
test1.j2 end
会报错
可以显式的指定"with context",表示导入上下文
cat test.j2
test...................
test...................
{% include 'test1.j2' with context %}
test...................
默认情况下,即使不使用"with context","include"也会导入对应的上下文,所以,如下两种写法是等效的。
{% include 'test1.j2' %}
{% include 'test1.j2' with context %}
默认情况下,如果指定包含的文件不存在,则会报错
cat test.j2
test...................
test...................
{% include 'test1.j2' with context %}
test...................
{% include 'test2.j2' with context %}
在test.j2中指定包含了两个文件,test1.j2和test2.j2,但是,并没有编写test2.j2,所以,当渲染test.j2模板时,会报错
在指定包含的文件不存在时,自动忽略包含对应的文件,使用ignore missing标记
cat test.j2
test...................
test...................
{% include 'test1.j2' with context %}
test...................
{% include 'test2.j2' ignore missing with context %}
2.7 导入
无论是定义宏,还是调用宏,都是在同一个模板文件中完成的,通过import,可以实现在A文件中定义宏,在B文件中使用宏。
cat function_lib.j2
{% macro testfunc() %}
test function
{% for i in varargs %}
{{ i }}
{% endfor %}
{% endmacro %}
{% macro testfunc1(tv1=1) %}
{{tv1}}
{% endmacro %}
cat test.j2
{% import 'function_lib.j2' as funclib %}
something in test.j2
{{ funclib.testfunc(1,2,3) }}
something in test.j2
{{ funclib.testfunc1('aaaa') }}
将function_lib.j2文件中的宏导入到 funclib变量中
{% import ‘function_lib.j2’ as funclib %}
由于已经将"function_lib.j2"文件中的宏导入到了"funclib"变量中,所以当需要调用"function_lib.j2"文件中的testfunc宏时,直接使用{{ funclib.testfunc(1,2,3) }}
使用了 {% from ‘function_lib.j2’ import testfunc as tf, testfunc1 as tf1 %}导入了’function_lib.j2’文件中的两个宏
从’function_lib.j2’文件中将testfunc宏导入为tf宏
从’function_lib.j2’文件中将testfunc1宏导入为tf1宏
导入后,直接调用tf宏和tf1宏,即为调用’function_lib.j2’文件中对应的宏
cat function_lib.j2
{% macro testfunc() %}
test function
{% for i in varargs %}
{{ i }}
{% endfor %}
{% endmacro %}
{% macro testfunc1(tv1=111) %}
test function1
{{tv1}}
{% endmacro %}
cat test1.j2
{% from 'function_lib.j2' import testfunc as tf, testfunc1 as tf1 %}
something in test1.j2
{{ tf(1,2) }}
something in test1.j2
{{ tf1('a') }}
只导入testfunc1
{% from ‘function_lib.j2’ import testfunc1 as tf1 %}
两种import方法的不同
方法一:
{% import ‘function_lib.j2’ as funclib %}
一次性导入’function_lib.j2’ 文件中的所有宏,调用宏时使用对应的变量进行调用。
方法二:
{% from ‘function_lib.j2’ import testfunc1 as tf1 %}
导入’function_lib.j2’ 文件中指定的宏,调用宏时使用对应的新名称进行调用。
import和include不同,include默认会导入上下文环境,而import默认则不会,所以,如果想要让宏被import以后能够使用到对应的上下文环境,则需要显式的配置"with context"
cat function_lib.j2
{% macro testfunc1(tv1=111) %}
test function1
{{tv1}}
{{outvartest}}
{% endmacro %}
cat test.j2
{% set outvartest='00000000' %}
{% import 'function_lib.j2' as funclib with context%}
something in test.j2
{{ funclib.testfunc1() }}
宏中如果包含for循环并且for循环中使用了range()函数,那么在"import"宏时则必须显式的指定"with context",否则在ansible中渲染对应模板时,会出现报错
宏如果以一个或多个下划线开头,则表示这个宏为私有宏,这个宏不能被导入到其他文件中使用
cat func.j2
{% macro _test() %}
something in test macro
{% endmacro %}
{{_test()}}
上述宏不能被导入到其他文件,只能在当前文件中被调用。
2.8 继承
继承可以更加灵活的生成模板文件。
定义父模板
cat test.j2
something in test.j2...
something in test.j2...
{% block test %}
Some of the options that might be replaced
{% endblock %}
something in test.j2...
something in test.j2...
渲染后的结果
something in test.j2...
something in test.j2...
Some of the options that might be replaced
something in test.j2...
something in test.j2...
定义子模板
cat test1.j2
{% extends 'test.j2' %}
{% block test %}
aaaaaaaaaaaaaa
11111111111111
{% endblock %}
得到的结果
子模板中的test块中的内容覆盖了父模板中的test块的内容
something in test.j2...
something in test.j2...
aaaaaaaaaaaaaa
11111111111111
something in test.j2...
something in test.j2...
在父模板的块中不写任何内容,而是靠子模板去填充对应的内容
cat test.j2
something in test.j2...
something in test.j2...
{% block test %}
{% endblock %}
something in test.j2...
something in test.j2...
cat test1.j2
{% extends 'test.j2' %}
{% block test %}
aaaaaaaaaaaaaa
11111111111111
{% endblock %}
块中嵌套另一个块
something in test.j2...
{% block test %}
something in block test
{% block t1 %}
something in block t1
{% endblock %}
something in block test
{% endblock %}
无论test块还是t1块,都使用" {% endblock %}"作为结尾,虽然能够正常 解析,但是可读性比较差,可以在endblock中也加入对应的块名称以提高可读性。
something in test.j2...
{% block test %}
something in block test
{% block t1 %}
something in block t1
{% endblock t1 %}
something in block test
{% endblock test %}
something in test.j2...
在一个模板中多次的引用同一个块,则可以使用self特殊变量来引用模板自身的某个块
cat test.j2
something in test.j2...
{% block test %}
something in block test
something else in block test
{% endblock test %}
{{ self.test() }}
something in test.j2...
不完全覆盖父模板中的块,在父模板某个块的基础之上进行扩展,可以在子模板中使用super块来完成。
cat test.j2
something in test.j2...
{% block test %}
something in block test
something else in block test
{% endblock test %}
something in test.j2...
cat test1.j2
{% extends 'test.j2' %}
{% block test%}
aaaaaaaaaaaaaa
{{ super() }}
11111111111111
{% endblock test %}
在super块的末尾加入空白控制符"减号"就可以将自动换行去掉
{{ super() -}}
使用for循环去迭代一个块,但是在块中无法获取到for的循环变量。
cat test.j2
something in test.j2...
{%set testvar=123%}
{% block test %}
something in block test ---- {{testvar}}
{% endblock %}
{% for i in range(3) -%}
{% block test1 %}
something in block test1 ---- {{i}}
{% endblock %}
{%- endfor %}
something in test.j2...
test块和test1块,test块未使用for循环,test1块使用for循环进行处理,渲染上述模板,会报错"msg": “AnsibleUndefinedVariable: ‘i’ is undefined”
因为当test1块被for循环处理时,无法在块中获取到for的循环变量造成的,如果想要在上述情况中获取到for的循环变量,则可以在块中使用scoped修饰符。
cat test.j2
something in test.j2...
{%set testvar=123%}
{% block test %}
something in block test ---- {{testvar}}
{% endblock %}
{% for i in range(3) -%}
{% block test1 scoped %}
something in block test1 ---- {{i}}
{% endblock %}
{%- endfor %}
something in test.j2...
渲染后结果
something in test.j2...
something in block test ---- 123
something in block test1 ---- 0
something in block test1 ---- 1
something in block test1 ---- 2
something in test.j2...
在继承模板时,如果父模板在当前目录的子目录中,则可以使用如下方法继承对应的父模板。test1.j2为子模板,test.j2为父模板,父模板在子模板所在目录的子目录中,此时,可以使用’parent/test.j2’引用test.j2模板。
# tree
.
├── parent
│ └── test.j2
└── test1.j2
# cat test1.j2
{% extends 'parent/test.j2' %}
{% block test%}
{{ super() -}}
11111111111111
{% endblock test %}
————Blueicex 2020/3/30 16:23 [email protected]