环境
- 控制节点:Ubuntu 22.04
- Ansible 2.10.8
- 管理节点:CentOS 8
filter
使用filter可以对数据做操作,比如把JSON数据转换为YAML数据,从URL中解析出hostname,提取字符串的SHA1哈希值,做数学运算,等等。可以使用Ansible特有的filter,也可以使用jinja2移植的标准filter。
因为templating发生在Ansible的控制节点,而不是目标节点,所以filter运行在控制节点,并在本地转换数据。
处理未定义变量
提供缺省值
创建文件 test1.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "Hello {{ var1 }}"
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test1.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
fatal: [192.168.1.55]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'var1' is undefined\n\nThe error appears to be in '/root/temp/temp1105_2/test1.yml': line 4, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: task1\n ^ here\n"}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
这是因为变量 var1
未定义。
为避免出错,可以使用jinja2的 default
filter:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "Hello {{ var1 | default('ABC') }}, {{var2 | default(\"XYZ\")}}, {{ var3 | default(123) }}"
其中:
var1
default值是单引号引起来的字符串var2
default值是双引号引起来的字符串,所以用\
来转义var3
default值是数值,不需要用引号
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test1.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "Hello ABC, XYZ, 123"
}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
注:对于role,也可以在其 defaults/main.yml
文件里给该role的变量定义缺省值。
如果原值evaluate为false或者空字符串,则使用default时,需要将其第二个参数设置为true(默认值是false):
yaml
......
msg: "aaa {{ '' | default('empty string', true) }} bbb"
......
运行结果如下:
powershell
......
"msg": "aaa empty string bbb"
......
注:如果不加第二个参数,则不会替换为缺省值。
变量为空值
创建文件 test2.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "aaa {{ item.var1 }} bbb {{ item.var2 | default(omit) }}"
loop:
- var1: "Tom"
var2: 20
- var1: "Jerry"
其中,item1指定了 var1
和 var2
,而item2只指定了 var1
。
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test2.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item={'var1': 'Tom', 'var2': 20}) => {
"msg": "aaa Tom bbb 20"
}
ok: [192.168.1.55] => (item={'var1': 'Jerry'}) => {
"msg": "aaa Jerry bbb __omit_place_holder__ae2509bf44427378895b86e1e65511fbaaf58727"
}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可见,指定了 default(omit)
,则变量默认值为空(若打印其值,则输出一个占位符)。
注:对于链式filter,官网上说,要用 "{``{ foo | default(None) | <some_filter> or omit }}"
的形式,比如:
yaml
msg: " {{ var1 | default(None) | lower or omit }}"
运行结果如下:
powershell
"msg": " none"
但我试了一下,如果不这么做,比如:
yaml
msg: " {{ var1 | default(omit) | lower }}"
也不报错,运行结果如下:
powershell
"msg": " __omit_place_holder__2212ea218f1e07f0070e5184cf11ebf71e3e5e9e"
变量必须有值
如果配置了 DEFAULT_UNDEFINED_VAR_BEHAVIOR
为 false
,则变量允许为空值。此时,如果想要强制变量不得为空值,则可以使用 mandatory
filter,比如:
yaml
{{ var1 | mandatory }}
根据不同值true/false/null做判断
注: ternary
是 三元
的意思。
创建文件 test6.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ (var1 == 'a') | ternary('111', '222') }}"
vars:
- var1: 'a'
本例中,由于 var1 == 'a'
是true,所以会打印 111
:
powershell
➜ temp1105_2 ansible-playbook test6.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "111"
}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
创建文件 test7.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ var1 | ternary('111', '222', omit) }}"
vars:
- var1: 'abc'
- name: task2
debug:
msg: "{{ var2 | ternary('111', '222', omit) }}"
vars:
- var2: false
- name: task3
debug:
msg: "{{ var3 | ternary('111', '222', omit) }}"
vars:
- var3: null
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test7.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "111"
}
TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "222"
}
TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "Hello world!"
}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
var1
有值且不等于false
,表示truevar2
其值为false
(注意没有引号,否则是字符串),表示falsevar3
其值为null
,注意打印出了Hello world!
管理数据类型
检测数据类型
创建文件 test8.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ 'abc' | type_debug }}, {{ 123 | type_debug }}, {{ 0.5 | type_debug }}, {{ true | type_debug }}"
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test8.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "str, int, float, bool"
}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可见:
'abc'
类型为str
123
类型为int
0.5
类型为float
true
类型为bool
强制类型转换
创建文件 test9.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ var1 | int | type_debug }}, {{ var2 | float | type_debug }}, {{ var3 |bool | type_debug }}, {{ var4 | string | type_debug }}"
vars:
- var1: '123'
- var2: '0.5'
- var3: 'true'
- var4: 456
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test9.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "int, float, bool, str"
}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
JSON和YAML
to_json
:转换为JSON格式to_nice_json
:转换为可读的JSON格式(加上换行,缩进等)to_yaml
:转换为YAML格式to_nice_yaml
:转换为可读的YAML格式(加上换行,缩进等)
创建文件 test12.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
template:
src: /tmp/src1
dest: /tmp/dest1
- name: task2
template:
src: /tmp/src2
dest: /tmp/dest2
- name: task3
template:
src: /tmp/src3
dest: /tmp/dest3
- name: task4
template:
src: /tmp/src4
dest: /tmp/dest4
/tmp/src1
如下:
yaml
{{ ansible_facts['default_ipv4'] | to_json }}
/tmp/src2
如下:
yaml
{{ ansible_facts['default_ipv4'] | to_nice_json }}
/tmp/src3
如下:
yaml
{{ ansible_facts['default_ipv4'] | to_yaml }}
/tmp/src4
如下:
yaml
{{ ansible_facts['default_ipv4'] | to_nice_yaml }}
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test12.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
changed: [192.168.1.55]
TASK [task2] ***************************************************************************************
changed: [192.168.1.55]
TASK [task3] ***************************************************************************************
changed: [192.168.1.55]
TASK [task4] ***************************************************************************************
changed: [192.168.1.55]
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
运行结束后,到目标机器上查看。
/tmp/dest1
如下:
javascript
{"gateway": "192.168.1.1", "interface": "ens33", "address": "192.168.1.55", "broadcast": "192.168.1.255", "netmask": "255.255.255.0", "network": "192.168.1.0", "macaddress": "00:0c:29:12:a6:b7", "mtu": 1500, "type": "ether", "alias": "ens33"}
/tmp/dest2
如下:
javascript
{
"address": "192.168.1.55",
"alias": "ens33",
"broadcast": "192.168.1.255",
"gateway": "192.168.1.1",
"interface": "ens33",
"macaddress": "00:0c:29:12:a6:b7",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "192.168.1.0",
"type": "ether"
}
/tmp/dest3
如下:
yaml
{address: 192.168.1.55, alias: ens33, broadcast: 192.168.1.255, gateway: 192.168.1.1,
interface: ens33, macaddress: '00:0c:29:12:a6:b7', mtu: 1500, netmask: 255.255.255.0,
network: 192.168.1.0, type: ether}
/tmp/dest4
如下:
yaml
address: 192.168.1.55
alias: ens33
broadcast: 192.168.1.255
gateway: 192.168.1.1
interface: ens33
macaddress: 00:0c:29:12:a6:b7
mtu: 1500
netmask: 255.255.255.0
network: 192.168.1.0
type: ether
注: to_yaml
和 to_nice_yaml
,默认每行包含80个字符(当超过80字符后,遇到空格就会换行),可以指定 width
选项,比如:
yaml
{{ ansible_facts['default_ipv4'] | to_yaml(width=500) }}
结果如下:
yaml
{address: 192.168.1.55, alias: ens33, broadcast: 192.168.1.255, gateway: 192.168.1.1, interface: ens33, macaddress: '00:0c:29:12:a6:b7', mtu: 1500, netmask: 255.255.255.0, network: 192.168.1.0, type: ether}
可以指定 indent
选项(默认值貌似是4),比如:
yaml
{{ ansible_facts['default_ipv4'] | to_nice_json(indent=8) }}
结果如下:
javascript
{
"address": "192.168.1.55",
"alias": "ens33",
"broadcast": "192.168.1.255",
"gateway": "192.168.1.1",
"interface": "ens33",
"macaddress": "00:0c:29:12:a6:b7",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "192.168.1.0",
"type": "ether"
}
可见缩进值变为8了。
from_json
:读取已知的JSON数据from_yaml
:读取已知的YAML数据
在目标机器上创建 json1.json
如下:
javascript
{
"Name": "Tom",
"Age": 20,
"Sports":
["Football", "Swimming"]
}
在目标机器上创建 yaml1.yml
如下:
yaml
Name: Tom
Age: 20
Sports:
- Football
- Swimming
创建文件 test14.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
shell: cat /tmp/json1.json
register: result1
- name: task2
debug:
msg: "{{ result1.stdout | type_debug }}"
- name: task3
debug:
msg: "{{ result1.stdout | from_json | type_debug }}"
- name: task4
shell: cat /tmp/yaml1.yml
register: result2
- name: task5
debug:
msg: "{{ result2.stdout | type_debug }}"
- name: task6
debug:
msg: "{{ result2.stdout | from_yaml | type_debug }}"
运行结果如下:
powershell
➜ temp1105_2 ansible-playbook test14.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
changed: [192.168.1.55]
TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "AnsibleUnsafeText"
}
TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "dict"
}
TASK [task4] ***************************************************************************************
changed: [192.168.1.55]
TASK [task5] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "AnsibleUnsafeText"
}
TASK [task6] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "dict"
}
PLAY RECAP *****************************************************************************************
192.168.1.55 : ok=7 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可见,如果不加 from_json
或 from_yaml
,则读取的是字符串( AnsibleUnsafeText
),加上from_json
或 from_yaml
,则读取为 dict
。
随机数
创建文件 test15.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ 60 | random }}"
运行结果如下:
powershell
"msg": "4"
多次运行,结果也不同。
注意:取值范围是0到60,左闭右开。
如果想指定起始值,可以用 start
选项:
yaml
msg: "{{ 60 | random(start=55) }}"
则取值范围是55到60,左闭右开。
如果要求取值是特定值的整倍数,可以用 step
选项:
yaml
msg: "{{ 60 | random(step=20) }}"
则取值只能是0或20或40。
如果想在多个目标机器上各自产生不同的随机数,但要求每个机器上的随机数多次运行结果不变(幂等性),则可以添加 seed
选项,使用该种子来产生随机数,注意seed的值不要变:
yaml
msg: "{{ 60 | random(seed=inventory_hostname) }}"
powershell
"msg": "50"
多次运行,产生的随机数保持不变。
数学运算
log
pow
root
abs
round
创建文件 test16.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ 100 | log(10) }}, {{ 2 | pow(10) }}, {{ 216 | root(3) }}, {{ -1.5 | abs }}, {{ 12.34 | round }}"
运行结果如下:
powershell
"msg": "2.0, 1024.0, 5.999999999999999, 1.5, 12.0"
注释
comment
将内容注释。默认的注释符是 #
,可以指定不同语言来做注释。
创建文件 test17.yml
如下:
yaml
---
- hosts: all
tasks:
- name: task1
template:
src: /tmp/src1
dest: /tmp/dest1
- name: task2
template:
src: /tmp/src2
dest: /tmp/dest2
- name: task3
template:
src: /tmp/src3
dest: /tmp/dest3
- name: task4
template:
src: /tmp/src4
dest: /tmp/dest4
- name: task5
template:
src: /tmp/src5
dest: /tmp/dest5
创建文件 /tmp/src1
如下:
yaml
{{ 'aaa' | comment }}
创建文件 /tmp/src2
如下:
yaml
{{ 'bbb' | comment('c') }}
创建文件 /tmp/src3
如下:
yaml
{{ 'ccc' | comment('cblock') }}
创建文件 /tmp/src4
如下:
yaml
{{ 'ddd' | comment('erlang') }}
创建文件 /tmp/src5
如下:
yaml
{{ 'eee' | comment('xml') }}
运行结束后,到目标机器上查看:
/tmp/dest1
如下:
powershell
#
# aaa
#
/tmp/dest2
如下:
c
//
// bbb
//
/tmp/dest3
如下:
c
/*
*
* ccc
*
*/
/tmp/dest4
如下:
erlang
%
% ddd
%
/tmp/dest5
如下:
xml
<!--
-
- eee
-
-->
字符串操作
quote
:给值加上引号
yaml
shell: echo {{ var1 | quote }} > /tmp/a.txt
vars:
var1: "a\nb\nc"
运行结束后,目标机器 /tmp/a.txt
如下:
powershell
a
b
c
如果没有 quote
,则会报错。
join
:连接字符串
yaml
debug:
msg: "{{ ['a', 'b', 'c'] | join(' ') }}"
运行结果如下:
powershell
"msg": "a b c"
split
:切割字符串
yaml
debug:
msg: "{{ 'a,b,c' | split(',') }}"
注:需要升级到2.11,我用的是2.10,会报错。
b64encode
:Base64 encode
yaml
debug:
msg: "{{ 'abcdefg' | b64encode }}"
运行结果如下:
powershell
"msg": "YWJjZGVmZw=="
b64decode
:Base64 decode
yaml
debug:
msg: "{{ 'YWJjZGVmZw==' | b64decode }}"
运行结果如下:
powershell
"msg": "abcdefg"
UUID
to_uuid
:创建UUID
yaml
debug:
msg: "{{ 'abcdefg' | to_uuid }}"
运行结果如下:
powershell
"msg": "aeb26d4d-43ec-587e-b3a8-67b6ca88a4df"
参考
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_filters.html
https://jinja.palletsprojects.com/en/latest/templates