ansible 中的模板 jinja2模板引擎(二)

前一篇文章中我们提到过,在jinja2中,使用"{% %}“对控制语句进行包含,比如"if"控制语句、“for"循环控制语句等 都需要包含在”{% %}“中,那么这篇文章我们就来聊聊”{% %}”。

一、"if"控制语句

if结构
先来聊聊if,与其他语言相同,if用来进行条件判断,在jinja2中,if的语法如下:

{% if 条件 %}
...
...
...
{% endif %}

如你所见,当使用if语法时,需要使用endif作为结束,示例模板如下:

[root@server4 jinja2-2]# vim test1.j2
[root@server4 jinja2-2]# cat test1.j2 
{% if testnum > 3 %}
greater than 3
{% endif %}

使用如下playbook对模板文件进行渲染

注:不要使用ad-hoc命令调用template模块进行渲染,因为使用命令调用template模块时,无论你传入的数据是哪种类型,都会被当做字符串进行处理,所以此处使用playbook渲染模板,以保证数据类型的正确。

[root@server4 jinja2-2]# vim temptest2.yml
[root@server4 jinja2-2]# cat temptest2.yml 
---
- hosts: testB
  remote_user: root
  gather_facts: no
  tasks:
  - template:
      src: /root/jinja2-2/test1.j2
      dest: /opt/test
    vars:
      testnum: 5

最终生成的文件内容如下:

[root@server3 opt]# cat test
greater than 3

使用if进行条件判断,能够让我们的模板变得更加灵活。

if…else…结构

除了"if"结构,当然还有"if…else…"结构,语法如下:

{% if 条件 %}
...
{% else %}
...
{% endif %}

"if…else if…"结构
与其他语言一样,也有"if…else if…"的语法结构,如下:

{% if 条件一 %}
...
{% elif 条件二 %}
...
{% elif 条件N %}
...
{% endif %}

或者结合在一起使用,语法如下

{% if 条件一 %}
...
{% elif 条件N %}
...
{% else %}
...
{% endif %}

上述语法就是"if"控制语句的语法。

二、if表达式

其实,说到"if",不仅仅有"if"控制语句,还有"if"表达式,利用"if"表达式,可以实现类似三元运算的效果,示例模板内容如下:

[root@server4 jinja2-2]# vim test2.j2
[root@server4 jinja2-2]# cat test2.j2 
{{ 'a' if 2>1 else 'b' }}
[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test2.j2 dest=/opt/test"

渲染后的文件内容如下

[root@server3 opt]# cat test
a

如果你使用过其他语言的三目运算,或者你使用过 python 的三元运算,那么你一定已经看明白了上述表达式的含义,上例中的if表达式的含义为,如果2>1这个条件为真,则使用’a’,如果2>1这个条件不成立,则使用’b’,而2必定大于1,所以条件成立,最终使用’a’,在jinja2中,if表达式的语法如下:

 <do something> if <something is true> else <do something else>

"if"表达式和"if"控制语句并不是一个东西,"if"表达式可以与其他的控制语句结合使用

在前文的示例中,我们都是在playbook中定义变量,然后在模板文件中使用变量,其实,我们也可以直接在模板文件中定义变量,示例如下:

[root@server4 jinja2-2]# vim test3.j2
[root@server4 jinja2-2]# cat test3.j2 
{% set teststr='abc' %}
{{ teststr }}

如你所见,在jinja2中,使用set关键字定义变量,执行如下ad-hoc命令渲染模板

[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test3.j2 dest=/opt/test"

最终生成的文件内容如下

[root@server3 opt]# cat test
abc

直接在模板中定义变量可以方便我们的测试。

三、for循环

for循环基本语法
刚才说完了"if",现在来聊聊"for",for循环的基本语法如下:

{% for 迭代变量 in 可迭代对象 %}
{{ 迭代变量 }}
{% endfor %}

没错,如你所见,与"if"很像,"for"需要使用"endfor"作为结束,jinja2的控制语句大多都会遵循这个规则,即"XXX"控制语句需要使用"endXXX"作为结尾,之后不再进行赘述。

先看一个简单的for循环示例,如下

[root@server4 jinja2-2]# vim test4.j2
[root@server4 jinja2-2]# cat test4.j2 
{% for i in [3,1,7,8,2] %}
{{ i }}
{% endfor %}

上例中我们直接在模板文件的for循环中定义了一个列表,执行如下命令,对模板进行渲染

[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test4.j2 dest=/opt/test"

最终生成的文件内容如下:

[root@server3 opt]# cat test
3
1
7
8
2

从生成的内容可以看出,每次循环后都会自动换行,如果不想要换行,则可以使用如下语法

[root@server4 jinja2-2]# vim test4.j2
[root@server4 jinja2-2]# cat test4.j2 
{% for i in [3,1,7,8,2] -%}
{{ i }}
{%- endfor %}

在for的结束控制符"%}“之前添加了减号”-"

在endfor的开始控制符"{%“之后添加到了减号”-"

渲染上述模板,最终的生成效果如下:

[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test4.j2 dest=/opt/test"
[root@server3 opt]# cat test
31782

如上所示,列表中的每一项都没有换行,而是连在了一起显示,如果你觉得这样显示有些"拥挤",则可以稍微改进一下上述模板,如下:

[root@server4 jinja2-2]# cat test4.j2 
{% for i in [3,1,7,8,2] -%}
{{ i }}{{ '  ' }}
{%- endfor %}

如上例所示,我们在循环每一项时,在每一项后面加入了一个空格字符串,所以,最终生成的效果如下:

[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test4.j2 dest=/opt/test"
[root@server3 opt]# cat test
3  1  7  8  2 

其实,还有更加简洁的写法,就是将上述模板内容修改为如下内容:

[root@server4 jinja2-2]# vim test4.j2
[root@server4 jinja2-2]# cat test4.j2 
{% for i in [3,1,7,8,2] -%}
{{ i~' ' }}
{%- endfor %}

如上例所示,我们直接在迭代变量的后面使用了波浪符,并且用波浪符将迭代变量和空格字符串连在一起,渲染上述模板内容,最终生成内容的效果与刚才示例中的效果是相同的,在jinja2中,波浪符""就是字符串连接符,它会把所有的操作数转换为字符串,并且连接它们。
for循环操作字典
"for"除了能够循环操作列表,也能够循环操作字典,示例如下:

[root@server4 jinja2-2]# vim test5.j2
[root@server4 jinja2-2]# cat test5.j2 
{% for key,val in {'name':'redhat','age':18}.iteritems() %}
{{ key ~  ':' ~ val }}
{% endfor %}

如上所示,在循环操作字典时,先使用iteritems函数对字典进行处理,然后使用key和val两个变量作为迭代变量,分别用于存放字典中键值对的"键"和"值",所以,直接输出两个变量的值即可,key和val是我随意起的变量名,你可以自己定义这两个迭代变量的名称,而且,上例中的iteritems函数也可以替换成items函数,但是推荐使用iteritems函数,上例最终生成内容如下:

[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test5.j2 dest=/opt/test"
[root@server3 opt]# cat test
age:18
name:redhat

在使用for循环时,有一些内置的特殊变量可以使用,比如,如果我想要知道当前循环操作为整个循环的第几次操作,则可以借助"loop.index"特殊变量,示例如下:

[root@server4 jinja2-2]# vim test6.j2 
[root@server4 jinja2-2]# cat test6.j2 
{% for i in [3,1,7,8,2] %}
{{ i ~ '----' ~ loop.index }}
{% endfor %}
[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test6.j2 dest=/opt/test"

最终生成文件内容如下:

[root@server3 opt]# cat test
3----1
1----2
7----3
8----4
2----5

除了内置特殊变量"loop.index",还有一些其他的内置变量,它们的作用如下:

变量作用
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()这是一个辅助函数,通过这个函数我们可以在指定的一些值中进行轮询取值,

for循环递归操作

for循环也支持递归操作,递归示例如下:

[root@server4 jinja2-2]# vim test11.j2
[root@server4 jinja2-2]# cat test11.j2 
{% 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 %}
[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test11.j2 dest=/opt/test"

如上例所示,我们定义了一个字典变量,从字典中可以看出,bob的儿子是tom,tom的儿子是jerry,然后我们使用for循环操作了这个字典,如前文所示,我们在操作字典时,使用了iteritems函数,在for循环的末尾,我们添加了recursive 修饰符,当for循环中有recursive时,表示这个循环是一个递归的循环,当我们需要在for循环中进行递归时,只要在需要进行递归的地方调用loop函数即可,没错,如你所见,上例中的"loop( value.iteritems() )"即为调用递归的部分,由于value也是一个字典,所以需要使用iteritems函数进行处理。

渲染上述模板内容,最终效果如下

[root@server3 opt]# cat test
 
         
     
      bob's son is tom
             
     
      tom's son is jerry

             

loop.cycle()辅助函数
刚才在总结与循环有关的内置变量时,还提到了一个辅助函数,它就是"loop.cycle()",它能够让我们在指定的一些值中进行轮询取值,这样说可能不够直观,不如来看一个小示例,如下:

[root@server4 jinja2-2]# vim test12.j2
[root@server4 jinja2-2]# cat test12.j2 
{% set userlist=['Naruto','Kakashi','Sasuke','Sakura','Lee','Gaara','Itachi']  %}
 
{% for u in userlist %}
{{ u ~'----'~ loop.cycle('team1','team2','team3')}}
{%endfor%}
[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test12.j2 dest=/opt/test"

上例中,我们定义了一个用户列表,这个列表里面有一些人,现在,我想要将这些人分组,按照顺序将这些人分别分配到三个组中,直到分完为止,三个组的组名分别为team1、team2、team3,渲染上例的内容,最终生成内容如下:

[root@server3 opt]# cat test
 
Naruto----team1
Kakashi----team2
Sasuke----team3
Sakura----team1
Lee----team2
Gaara----team3
Itachi----team1

从生成的内容可以看出,用户与三个组已经轮询的进行了结合。

四、ansible启用break和continue扩展

刚才我们提到过,默认情况下,模板中的for循环无法使用break和continue,不过jinja2支持一些扩展,如果我们在ansible中启用这些扩展,则可以让模板中的for循环支持break和continue,方法如下:

如果想要开启对应的扩展支持,需要修改ansible的配置文件/etc/ansible/ansible.cfg,默认情况下未启用jinja2的扩展,如果想要启用jinja2扩展,则需要在配置文件132行设置jinja2_extension选项,这个设置项默认情况下是注释的,我的默认设置如下

132 #jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n

把注释符去掉,默认已经有两个扩展了,如果想要支持break和continue,则需要添加一个loopcontrols扩展,最终配置如下

jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n,jinja2.ext.loopcontrols

完成上述配置步骤即可在for循环中使用break和continue控制语句,与其他语言一样,break表示结束整个循环,continue表示结束当次循环,示例如下:

[root@server4 jinja2-2]# vim test13.j2
[root@server4 jinja2-2]# cat test13.j2 
{% for i in [7,1,5,3,9] %}
  {% if loop.index is even %}
    {%continue%}
  {%endif%}
  {{ i ~'----'~ loop.index }}
{% endfor %}
[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test13.j2 dest=/opt/test"
[root@server3 opt]# cat test
    7----1
          5----3
          9----5

上述示例表示偶数次的循环将会跳过,even这个tests在前文中已经总结过,此处不再赘述。

break的示例如下:

[root@server4 jinja2-2]# vim test14.j2
[root@server4 jinja2-2]# cat test14.j2 
{% for i in [7,1,5,3,9] %}
  {% if loop.index > 3 %}
    {%break%}
  {%endif%}
  {{i ~'---'~ loop.index}}
{% endfor %}
[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test14.j2 dest=/opt/test"
[root@server3 opt]# cat test
    7---1
    1---2
    5---3

上例表示3次迭代以后的元素不会被处理

五、ansible使用do扩展修改列表

如果我们想要在jinja2中修改列表中的内容,则需要借助jinja2的另一个扩展,这个扩展的名字就是"do",我们可以发现,刚才修改jinja2_extensions配置的时候,默认就有这个扩展,它的名字是jinja2.ext.do,通过do扩展修改列表的示例如下:

[root@server4 jinja2-2]# vim test15.j2
[root@server4 jinja2-2]# cat test15.j2 
{% set testlist=[3,5] %}
 
{% for i in testlist  %}
  {{i}}
{% endfor %}
 
{%do testlist.append(7)%}
 
{% for i in testlist  %}
  {{i}}
{% endfor %}
[root@server4 jinja2-2]# ansible testB -m template -a "src=/root/jinja2-2/test15.j2 dest=/opt/test"
[root@server3 opt]# cat test
 
  3
  5
 
 
  3
  5
  7

如上例所示,我们先定义了一个列表,然后遍历了这个列表,使用 do在列表的末尾添加了一个元素,数字7,然后又遍历 了它。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值