ansible学习之jinja2

1.template简单使用

如果装多台redis,每台redis绑定自身ip.可以通过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

在模板中修改bind 127..0.0.1 为 bind {{ansible_host}}

template模块包含如下
owner参数: 指定最终生成的文件拷贝到远程主机后的属主。
group参数: 指定最终生成的文件拷贝到远程主机后的属组。
mode参数: 指定最终生成的文件拷贝到远程主机后的权限,如果你想将权限设置为"rw-r--r--",则可以使用mode=0644表示,如果你想要在user对应的权限位上添加执行权限,则可以使用mode=u+x表示。
除了上述参数,还有如下参数也很常用
force参数: 当远程主机的目标路径中已经存在同名文件,并且与最终生成的文件内容不同时,是否强制覆盖,可选值有yes和no,默认值为yes,表示覆盖,如果设置为no,则不会执行覆盖拷贝操作,远程主机中的文件保持不变。
backup参数: 当远程主机的目标路径中已经存在同名文件,并且与最终生成的文件内容不同时,是否对远程主机的文件进行备份,可选值有yes和no,当设置为yes时,会先备份远程主机中的文件,然后再将最终生成的文件拷贝到远程主机。

2.jinja2的语法

{{      }}  :用来装载表达式,比如变量、运算表达式、比较表达式等。
{%   %}   :用来装载控制语句,比如 if 控制结构,for循环控制结构。
{#    #}   :用来装载注释,模板文件被渲染后,注释不会包含在最终生成的文件中。

 例子:
 [root@ansible ansible]# cat test.j2
test jinja2 variable
test {{ testvar1 }} test 
执行:
ansible 192.168.10.20 -m template  -e "testvar1=testtest" -a "src=test.j2 dest=/opt/test"
验证:
[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
test jinja2 variable
test testtest 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

逻辑运算的示例
jinja2 test
{{ (2 > 1) or (1 > 2) }}
{{ (2 > 1) and (1 > 2) }}
 
{{ not true }}
{{ not True }}
{{ not false }}
{{ not False }}

算数运算的相关示例如下:

# cat test.j2
jinja2 test
{{ 3 + 2 }}
{{ 3 - 4 }}
{{ 3 * 5 }}
{{ 2 ** 3 }}
{{ 7 / 5 }}
{{ 7 // 5 }}
{{ 17 % 5 }}

成员运算的相关示例如下:

模板文件内容
# cat test.j2
jinja2 test
{{ 1 in [1,2,3,4] }}
{{ 1 not in [1,2,3,4] }}
 
生成文件内容
# cat test
jinja2 test
True
False

除了变量和各种常用的运算符,过滤器也可以直接在"{{  }}"中使用
# cat test.j2
jinja2 test
{{ 'abc' | upper }}

jinja2的tests自然也能够在"{{  }}"中使用
模板文件内容
# cat test.j2
jinja2 test
{{ testvar1 is defined }}
{{ testvar1 is undefined }}
{{ '/opt' is exists }}
{{ '/opt' is file }}
{{ '/opt' is directory }}

"lookup"也可以在"{{}}"使用
# cat /testdir/ansible/test.j2
jinja2 test

{{ lookup('file','/testdir/testfile') }}

{{ lookup('env','PATH') }}

{# 为注释信息#}

test 2 tes2
{# zhushi #}
{#

hihhhj
#} 

[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test2"
192.168.10.20 | CHANGED | rc=0 >>

test 2 tes2

 

3. {%if%}在jinja2中的使用


"if"控制语句的用法 {% if 条件 %} ... {% endif %} 例子: [root@ansible ansible]# cat jinja2_test.yaml --- - hosts: 192.168.10.20 remote_user: root gather_facts: no tasks: - template: src: /etc/ansible/test2.j2 dest: /opt/test2 vars: testnum: 5 cat test2.j2 {% if testnum > 3 %} greater than 3 {% endif %} [root@ansible ansible]# ansible-playbook jinja2_test.yaml PLAY [192.168.10.20] ***************************************************************************************************************************************************************************************** TASK [template] ********************************************************************************************************************************************************************************************** changed: [192.168.10.20] PLAY RECAP *************************************************************************************************************************************************************************************************** 192.168.10.20 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test2" 192.168.10.20 | CHANGED | rc=0 >> greater than 3 复杂的if结构 {% if 条件 %} ... {% else %} ... {% endif %} 或者 {% if 条件一 %} ... {% elif 条件N %} ... {% else %} ... {% endif %} 或者 {% if 条件一 %} ... {% elif 条件二 %} ... {% elif 条件N %} ... {% endif %} "if"表达式  <do something> if <something is true> else <do something else> {{ 'a' if 2>1 else 'b' }}

4.模板文件中使用变量

  cat test.j2
jinja2 test
{% set teststr='jinja2var' %}
{{ teststr }}

[root@ansible ansible]# ansible 192.168.10.20 -m template -a "src=test2.j2 dest=/opt/test"
查看结果
[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
jinja2 test
jinja2var

5.for在jinja2中的使用

for循环的基本语法
{% for 迭代变量 in 可迭代对象 %}
{{ 迭代变量 }}
{% endfor %}

例子:
[root@ansible ansible]# cat test2.j2 
jinja2 test
{% for i in [3,1,4,6] %}
{{ i }}
{% endfor %}

[root@ansible ansible]# ansible 192.168.10.20 -m template -a "src=test2.j2 dest=/opt/test"
验证
[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
jinja2 test
3
1
4
6
从生成的内容可以看出,每次循环后都会自动换行,如果不想要换行,则可以使用如下语法

jinja2 test
{% for i in [3,1,4,6] -%}
{{ i }}
{%-endfor %}

[root@ansible ansible]# ansible 192.168.10.20 -m template -a "src=test2.j2 dest=/opt/test"
[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
jinja2 test
3146

在for的结束控制符"%}"之前添加了减号"-"
在endfor的开始控制符"{%"之后添加到了减号"-"

在每一项后面加一个空格
jinja2 test
{% for i in [3,1,4,6] -%}
{{ i }} {{ ' ' }}
{%-endfor %}

结果
[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
jinja2 test
3  1  4  6  

或者这样写
# cat test.j2
jinja2 test
{% for i in [3,1,7,8,2] -%}
{{ i~' ' }}
{%- endfor %}

循环操作字典
jinja2 test
{% for key,val in {'name':'bob','age':'18'}.iteritems() %}
{{ key ~':'~ val}}
{% endfor %}

 ansible 192.168.10.20 -m template -a "src=test2.j2 dest=/opt/test"

[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
jinja2 test
age:18
name:bob

借助loop.index特殊变量可以获取第几次循环
[root@ansible ansible]# ansible 192.168.10.20 -m template -a "src=test3.j2 dest=/opt/test"
192.168.10.20 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "f2f44e9e31f2f693928de2f50c289aead20eeb95", 
    "dest": "/opt/test", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "e7a4f21bf3a8240e90cb795bfebad711", 
    "mode": "0644", 
    "owner": "root", 
    "size": 29, 
    "src": "/root/.ansible/tmp/ansible-tmp-1600329612.94-90721-265848675337608/source", 
    "state": "file", 
    "uid": 0
}
[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
1----1
3----2
5----3
7----4

结果
[root@ansible ansible]# cat test3.j2 
{% for i in [1,3,5,7] %}
{{ i ~'----'~ loop.index }}
{% endfor %}

range函数可以指定起始数字、结束数字、步长等,默认的起始数字为0
{% for i in range(1,4,2) %}
  {{i}}
{% endfor %}

for嵌套if语句
{% for i in [7,1,5,3,9] if i>10 %}
{{ i }}
{%else%}
no one is greater than 10
{% 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 %}

想要支持break和continue,则需要添加一个loopcontrols扩展

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

例子:
{% for i in [7,1,5,3,9] %}
  {% if loop.index > 3 %}
    {%break%}
  {%endif%}
  {{i ~'---'~ loop.index}}
{% endfor %}
上例表示3次迭代以后的元素不会被处理

6 引号在jinja2中的使用

如果jinja2文件里使用 类似{{}}等 可以使用引号
[root@ansible ansible]# cat test4.j2
{{ '{{' }}
{{ '{% test %}' }}

结果:
[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
{{
{% test %}

可以使用"{% raw %}"块实现上述功能
{% raw %}
{% endraw %}

例子:
[root@ansible ansible]# cat test5.j2 
{% raw %}
{{ test }}
{% for %}
{% if %}
{% endraw %}

结果:

[root@ansible ansible]# ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>

{{ test }}
{% for %}
{% if %}

7 可以指定在jinja2文件里的引用符号

当我们调用templdate模块时,执行如下命令,注意,如下命令表示使用"(("代替"{{",使用"))"代替"}}"。  ansible test70 -m template -a "src=test.j2 dest=/opt/test variable_start_string='((' variable_end_string='))'" 可以使用block_end_string参数指定一个符号,这个符号用于替换"{% %}"中的"%}"

8 在jinja2中可以定义宏(函数)并引用它

9 在jinja2文件中可以使用include

# 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

如果不想让背包含的文件使用外部变量的话,可以用"without context"显式的设置"include"
# 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

如果导入的jinja2文件不存在 也让任务能正常执行的话,可以使用"ignore missing"标记皆可

# cat test.j2
test...................
test...................
{% include 'test1.j2' with context %}

test...................
{% include 'test2.j2' ignore missing with context %}

10 在jinja2文件中使用"{% import %}"

import的作用是在一个文件中导入其他文件中的宏

# 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"变量中,所以当我们需要调用"function_lib.j2"文件中的testfunc宏时,直接使用了如下代码即可。

{{ funclib.testfunc(1,2,3) }}

# 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') }}

如上例所示,我们使用了如下语法导入了'function_lib.j2'文件中的两个宏
{% from 'function_lib.j2' import testfunc as tf, testfunc1 as tf1  %}
上述语法表示:
从'function_lib.j2'文件中将testfunc宏导入为tf宏
从'function_lib.j2'文件中将testfunc1宏导入为tf1宏

11 在jinja2中使用继承

下面例子展示了继承的简单使用

[root@ansible ansible]# cat jinja2_jincheng.j2
something in fu

{% block test %}
this is test1.j2
{% endblock test %}

this is in fu

[root@ansible ansible]# cat jinja2_jincheng1.j2 
thisis test2
{% extends 'jinja2_jincheng.j2' %}

{% block test %}
nihao nihao jinja2_jincheng1.j2
{% endblock test %}
thisis test2

执行
ansible 192.168.10.20 -m template -a "src=jinja2_jincheng1.j2 dest=/opt/test"

结果
[root@ansible ansible]#  ansible 192.168.10.20 -m shell -a "cat /opt/test"
192.168.10.20 | CHANGED | rc=0 >>
thisis test2
something in fu

nihao nihao jinja2_jincheng1.j2

this is in fu 

如果你并不想完全覆盖父模板中的块,而是想要在父模板某个块的基础之上进行扩展,那么则可以子模板中使用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 %}

如上例所示,test1.j2继承自test.j2文件,同时,test1.j2中指明要修改test块,如你所见,子模板的test块中包含"{{ super() }}",这表示父模板中test块中的内容会替换到"{{ super() }}"对应的位置,换句话说就是,我们可以通过"{{ super() }}"来获取父级块中的内容,上例test1.j2的渲染结果如下:

# cat test1
something in test.j2...
 
aaaaaaaaaaaaaa
something in block test
something else in block test
 
11111111111111
 
something in test.j2...

更多关于继承的例子及说明参考如下链接

http://www.zsythink.net/archives/3051
  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin
avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: