Jinja2模版介绍
注:本文demo使用ansible2.7稳定版
在ansible基础-变量的「8.2 模版使用变量」章节中关于模版与变量也有所提及,有兴趣的同学可以去回顾一下。
ansible通过Jinja2模版来实现动态表达式和变量的引用,模版的执行都是在ansible控制端完成的,所以理论上python的jinja2模块在控制端存在就能满足需求。
Jinja2模版都可以怎么使用?(分类)
- playbook文件中引用Jinja2模版实现动态表达式和变量的引用。
- 模版文件(roles/templates/xxx.j2)中引用Jinja2模版实现配置文件内容的拼接。
为什么要使用Jinja2模版?(好处)
- 使用过滤器、插件、测试变量等能够非常简单的实现一些小型的函数运算,使部署代码更加简洁高效。
- 适用性更加广泛,使部署代码更加灵活。
相比较于python原生的Jinja2模版,ansible扩展了很多过滤器和测试变量,同时也添加了一个新的插件「lookups」。
关于jinja2模版的基础语法和使用,因为篇幅原因,这里不再扩展。本文着重介绍下过滤器。
数据格式化过滤器
过滤器「to_json」「to_yaml」,将变量转换为json和yaml格式
{{ some_variable | to_json }}
{{ some_variable | to_yaml }}
过滤器「to_nice_json」「to_nice_yaml」,将变量转换为更加友好的json和yaml格式
{{ some_variable | to_nice_json }}
{{ some_variable | to_nice_yaml }}
也可以自定义缩进的大小
{{ some_variable | to_nice_json(indent=2) }}
{{ some_variable | to_nice_yaml(indent=8) }}
过滤器「from_json」「from_yaml」,从已经格式化好了的变量读取数据:
{{ some_variable | from_json }}
{{ some_variable | from_yaml }}
「from_json」示例,从file.json文件读取json数据:
tasks:
- shell: cat /some/path/to/file.json
register: result
- set_fact:
myvar: "{{ result.stdout | from_json }}"
过滤器「from_yaml_all」,用来解析YAML多文档文件
tasks:
- shell: cat /some/path/to/multidoc-file.yaml
register: result
- debug:
msg: '{{ item }}'
loop: '{{ result.stdout | from_yaml_all | list }}'
YAML多文档文件指一个文件中包含多个yaml数据文档,例如:
---
part_one: one
...
---
part_two: two
...
变量强制定义过滤器
当我们引用一个未被定义的变量时,ansible默认会报错,当然我们可以通过更改ansible.cfg配置项的方式关闭这种机制(即设置[defaults]字段下的error_on_undefined_vars = False)。
在关闭这个机制的情况下,如果我们想让ansible强制检查某个变量是否定义,可以使用「mandatory」过滤器,写法如下:
{{ variable | mandatory }}
此时,如果变量「variable」被定义了,则引用,否则会报错:
fatal: [node1]: FAILED! => {"msg": "Mandatory variable 'aaa' not defined."}
变量默认值过滤器
「default」过滤器可以为未定义变量设置默认值,类似于roles/defaults/main.yaml里定义的变量,优先级最低(变量优先级参考ansible基础-变量)。
示例如下:
{{ some_variable | default(5) }}
另外,如果我们想将变量参数是false、False和空(None)视为未定义,则必须给defaults过滤器第二参数位置加上「true」:
{{ lookup('env', 'MY_USER') | default('admin', true) }}
上面示例中表示:从环境变量中查找「MU_USER」变量,如果变量值为false、False、空(None)、未定义则将其设置为「admin」。否则引用之前被定义的参数。
可删除参数过滤器
过滤器「omit」:在使用模块的时候,有些参数的存在与否可以取决于变量是否被定义:
- name: touch files with an optional mode
file: dest={{ item.path }} state=touch mode={{ item.mode | default(omit) }}
loop:
- path: /tmp/foo
- path: /tmp/bar
- path: /tmp/baz
mode: "0444"
上面示例表示:变量中如果定义了变量「mode」,file模块则使用mode参数,否则就不使用。
执行结果为「/tmp/foo」和「/tmp/bar」文件使用默认权限,「/tmp/baz」文件使用「0444」权限。
列表过滤器
过滤器「min」,获取最小值元素
{{ list1 | min }}
过滤器「max」,获取最大值元素
{{ [3, 4, 2] | max }}
过滤器「flatten」,扁平化列表元素
{{ [3, [4, 2] ] | flatten }}
转换结果为:
[3, 4, 2 ]
过滤器「flatten」,并且指定级别
{{ [3, [4, [2]] ] | flatten(levels=1) }}
只转换一级的列表元素,结果为:
[3, 4, [2] ]
过滤器「unique」,给列表元素去重
{{ list1 | unique }}
过滤器「union」,合并两个列表后去重
{{ list1 | union(list2) }}
过滤器「intersect」,取两个列表相同的元素
{{ list1 | intersect(list2) }}
过滤器「difference」,去掉list1中与list2相同的元素,返回list1中剩余的元素
{{ list1 | difference(list2) }}
过滤器「symmetric_difference」,去掉list1与list2相同的元素,返回list1和list2剩余元素的集合
{{ list1 | symmetric_difference(list2) }}
操作列表过滤器zip和zip_longest
过滤器「zip」,使两个列表元素递归的融合,生成一个「itertools.izip」生成器对象。
通常后面加上「list」过滤器来使用,表示list1[0]元素与list2[0]元素组合,作为新列表的第一个元素;list1[1]元素与list2[1]元素组合,作为新列表的第二个元素 ,以此类推…… 新列表元素个数以list1和list2中元素个数较少者为准。
如果文字描述不懂,看下面示例就懂了:
- name: give me list combo of two lists
debug:
msg: "{{ [1,2,3,4,5] | zip(['a','b','c','d','e','f']) | list }}"
转换结果为:
"msg": [
[
1,
"a"
],
[
2,
"b"
],
[
3,
"c"
],
[
4,
"d"
],
[
5,
"e"
]
]
过滤器「zip_longest」,与「zip」过滤器合并原理相似,「zip_longest」可以对更多的列表进行操作,且新列表元素个数以被操作列表中元素个数最多者为准,此时就需要指定「fillvalue」参数作为补位填充。示例如下:
{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], [100,200,300],fillvalue='X') | list }}
转换结果为:
"msg": [ [ 1, "a", 21, 100 ], [ 2, "b", 22, 200 ], [ 3, "c", 23, 300 ], [ "X", "d", "X", "X" ], [ "X", "e", "X", "X" ], [ "X", "f", "X", "X" ] ]