环境
- 管理节点:Ubuntu 22.04
- 控制节点:CentOS 8
- Ansible:2.15.6
概述
Jinja test是指计算一个表达式并返回 true
或 false
。
同所有的template一样,test是运行在控制节点上,而不是受控节点上。
语法
<expression> is <test name>
比如: result is failed
字符串
我们经常需要用正则表达式来匹配字符串。可用如下test:
match
:从起始位置匹配search
:从任意位置匹配regex
:类似search
,可添加match_type
参数
比如:
---
- hosts: all
vars:
var1: "abcdefghijklmn"
tasks:
- name: task1
debug:
msg: "OK"
when: var1 is match("abc") # OK
- name: task2
debug:
msg: "OK"
when: var1 is match ("^abc") # OK
- name: task3
debug:
msg: "OK"
when: var1 is match ("abc.*mn$") # OK
- name: task4
debug:
msg: "OK"
when: var1 is match ("mn") # Skip
- name: task5
debug:
msg: "OK"
when: var1 is search ("mn") # OK
- name: task6
debug:
msg: "OK"
when: var1 is regex ("mn") # OK
上述task中,只有task4不满足条件。
此外, match
、 search
、 regex
方法都可以添加 ignorecase
和 multiline
参数。
ignorecase
:忽略大小写,默认值为Falsemultiline
:多行,默认值为False
比如:
---
- hosts: all
vars:
var1: "abcdefg\nhijklmn"
tasks:
- name: task1
debug:
msg: "OK"
when: var1 is match("ABC") # Skip
- name: task2
debug:
msg: "OK"
when: var1 is match("ABC", ignorecase=True) # OK
- name: task3
debug:
msg: "OK"
when: var1 is search("^hi") # Skip
- name: task4
debug:
msg: "OK"
when: var1 is search("^hi", multiline=True) # OK
本例中, ^hi
匹配了第二行的开头,若 multiline
设置为True,则匹配成功。
Vault
vault_encrypted
:测试是否为加密的vault值
比如:
---
- hosts: all
vars:
var1: "abcdefg"
var2: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
61323931353866666336306139373937316366366138656131323863373866376666353364373761
3539633234313836346435323766306164626134376564330a373530313635343535343133316133
36643666306434616266376434363239346433643238336464643566386135356334303736353136
6565633133366366360a326566323363363936613664616364623437336130623133343530333739
3039
tasks:
- name: task1
debug:
msg: "OK"
when: var1 is vault_encrypted # Skip
- name: task2
debug:
msg: "OK"
when: var2 is vault_encrypted # OK
真假性
truthy
:真falsy
:假
此外,二者都可以添加 convert_bool
参数,将“yes”/“no”、“on”/“off”等转换为布尔值。
比如:
---
- hosts: all
vars:
var1: "abcdefg"
var2: ""
var3: "yes"
var4: "no"
var5: "on"
var6: "off"
tasks:
- name: task1
debug:
msg: "OK"
when: var1 is truthy # OK
- name: task2
debug:
msg: "OK"
when: var2 is truthy # Skip
- name: task3
debug:
msg: "OK"
when: var3 is truthy(convert_bool=True) # OK
- name: task4
debug:
msg: "OK"
when: var4 is truthy(convert_bool=True) # Skip
- name: task5
debug:
msg: "OK"
when: var5 is truthy(convert_bool=True) # OK
- name: task6
debug:
msg: "OK"
when: var6 is truthy(convert_bool=True) # Skip
- name: task7
debug:
msg: "OK"
when: var1 is falsy # Skip
- name: task8
debug:
msg: "OK"
when: var2 is falsy # OK
- name: task9
debug:
msg: "OK"
when: var3 is falsy(convert_bool=True) # Skip
- name: task10
debug:
msg: "OK"
when: var4 is falsy(convert_bool=True) # OK
- name: task11
debug:
msg: "OK"
when: var5 is falsy(convert_bool=True) # Skip
- name: task12
debug:
msg: "OK"
when: var6 is falsy(convert_bool=True) # OK
版本比较
version
:版本比较
可指定如下比较:
<
lt
:小于<=
le
:小于等于>
gt
:大于>=
ge
:大于等于==
=
eq
:等于!=
<>
ne
:不等于
比如:
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ ansible_facts['distribution_version'] }}" # 8.1
- name: task2
debug:
msg: "OK"
when: ansible_facts['distribution_version'] is version('8.1', '>') # Skip
- name: task3
debug:
msg: "OK"
when: ansible_facts['distribution_version'] is version('8.1', '>=') # OK
version
方法还可以添加第三个参数,表示比较类型。
strict
:True/False,是否严格比较version_type
loose
strict
semver
/semantic
pep440
注意: strict
和 version_type
互斥,最多只能指定一个。
此处我没仔细研究,等需要用的时候再说吧。
子集和超集
subset
:子集superset
:超集
比如:
---
- hosts: all
vars:
var1: [1, 2, 3, 4, 5]
var2: [2, 3, 4]
tasks:
- name: task1
debug:
msg: "OK"
when: var2 is subset(var1) # OK
- name: task2
debug:
msg: "OK"
when: var1 is subset(var2) # Skip
- name: task3
debug:
msg: "OK"
when: var2 is superset(var1) # Skip
- name: task4
debug:
msg: "OK"
when: var1 is superset(var2) # OK
- name: task5
debug:
msg: "OK"
when: var1 is subset(var1) # OK
- name: task6
debug:
msg: "OK"
when: var1 is superset(var1) # OK
list里的元素
contains
:是否包含特定的元素,若包含则返回元素
比如:
---
- hosts: all
vars:
var1:
- name: Tom
age: 20
sport:
- football
- basketball
- name: Jerry
age: 18
sport:
- swim
- tennis
- football
tasks:
- name: task1
debug:
msg: "{{ var1 | selectattr('name', 'contains', 'Tom') }}" # 第一个元素
- name: task2
debug:
msg: "{{ var1 | selectattr('sport', 'contains', 'football') }}" # 第一个和第二个元素
- name: task3
debug:
msg: "{{ var1 | selectattr('sport', 'contains', 'football') | first }}" # 第一个元素
- name: task4
debug:
msg: "{{ (var1 | selectattr('sport', 'contains', 'football') | first).age }}" # 第一个元素的age属性值
运行结果如下:
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": [
{
"age": 20,
"name": "Tom",
"sport": [
"football",
"basketball"
]
}
]
}
TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": [
{
"age": 20,
"name": "Tom",
"sport": [
"football",
"basketball"
]
},
{
"age": 18,
"name": "Jerry",
"sport": [
"swim",
"tennis",
"football"
]
}
]
}
TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": {
"age": 20,
"name": "Tom",
"sport": [
"football",
"basketball"
]
}
}
TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": "20"
}
本例中,是使用 selectattr
方法,通过元素的属性值来匹配元素。 contains
可作用于如下filter:
select
:选择元素reject
:排除元素selectattr
:根据属性值选择元素rejectattr
:根据属性值排除元素
比如:
---
- hosts: all
vars:
var1: ["aaa", "bbb", "ccc"]
tasks:
- name: task1
debug:
msg: "{{ var1 | select('contains', 'aaa') }}"
- name: task2
debug:
msg: "{{ var1 | select('contains', 'Tom') }}"
- name: task3
debug:
msg: "{{ var1 | reject('contains', 'aaa') }}"
- name: task4
debug:
msg: "{{ var1 | reject('contains', 'Tom') }}"
运行结果如下:
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": [
"aaa"
]
}
TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": []
}
TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": [
"bbb",
"ccc"
]
}
TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => {
"msg": [
"aaa",
"bbb",
"ccc"
]
}
注意: select
等方法还可以有很多其它有趣的参数,比如:
---
- hosts: all
vars:
var1: [0, 1, 2, 3, 4, 4, 3, 2, 1]
tasks:
- name: task1
debug:
msg: "{{ var1 | select('odd') }}" # 1, 3, 3, 1
- name: task2
debug:
msg: "{{ var1 | select('even') }}" # 0, 2, 4, 4, 2
- name: task3
debug:
msg: "{{ var1 | select('lessthan', 3) }}" # 0, 1, 2, 2, 1
- name: task4
debug:
msg: "{{ var1 | select('equalto', 1) }}" # 1, 1
select
等方法的返回值也可以当做布尔值,比如:
---
- hosts: all
vars:
var1: ["aaa", "bbb", "ccc"]
tasks:
- name: task1
debug:
msg: "OK"
when: var1 | select('contains', 'aaa') # OK
- name: task2
debug:
msg: "OK"
when: var1 | select('contains', 'Tom') # Skip
- name: task3
debug:
msg: "OK"
when: var1 | reject('contains', 'aaa') # OK
- name: task4
debug:
msg: "OK"
when: var1 | reject('contains', 'Tom') # OK
list的“所有元素都是true”和“至少一个元素是true”
all
:所有元素都是Trueany
:至少一个元素是True
比如:
---
- hosts: all
vars:
var1: [True, "{{ 1==1 }}", "!{{ 1==2 }}"]
var2: [True, False]
var3: [False, False, False]
tasks:
- name: task1
debug:
msg: "{{ var1 is all }}" # true
- name: task2
debug:
msg: "{{ var1 is any }}" # true
- name: task3
debug:
msg: "{{ var2 is all }}" # false
- name: task4
debug:
msg: "{{ var2 is any }}" # true
- name: task5
debug:
msg: "{{ var3 is all }}" # false
- name: task6
debug:
msg: "{{ var3 is any }}" # false
路径
directory
:是否为目录file
:是否为文件link
:是否为链接exists
: 路径是否存在abs
:是否为绝对路径same_file
:是否为相同路径的文件mount
:是否为mount
比如:
---
- hosts: all
vars:
var1: "/usr/bin/ps"
tasks:
- name: task1
debug:
msg: "{{ var1 is directory }}" # false
- name: task2
debug:
msg: "{{ var1 is file }}" # true
- name: task3
debug:
msg: "{{ var1 is exists }}" # true
- name: task4
debug:
msg: "{{ var1 is abs }}" # true
human_readable
注:这貌似是转换,并不是test。
human_readable
:数值转换为可读格式human_to_bytes
:可读格式转换为数值
比如:
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ 1 | human_readable }}" # 1.00 Bytes
- name: task2
debug:
msg: "{{ 1 | human_readable(isbits = True) }}" # 1.00 bits
- name: task3
debug:
msg: "{{ 1024 | human_readable }}" # 1.00 KB
- name: task4
debug:
msg: "{{ (1024 * 1024) | human_readable }}" # 1.00 MB
- name: task5
debug:
msg: "{{ (1024 * 1024 * 1024) | human_readable }}" # 1.00 GB
- name: task5
debug:
msg: "{{ (1024 * 1024 * 1024) | human_readable(unit='M') }}" # 1024.00 MB
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{'1'|human_to_bytes}}" # 1
- name: task2
debug:
msg: "{{'1KB'|human_to_bytes}}" # 1024
- name: task3
debug:
msg: "{{'1MB'|human_to_bytes}}" # 1048576
- name: task4
debug:
msg: "{{'1GB'|human_to_bytes}}" # 1073741824
- name: task5
debug:
msg: "{{'1Kb'|human_to_bytes(isbits=True)}}" # 1024
task结果
success
、succeeded
:成功failed
:失败changed
:变化skipped
:略过
比如:
---
- hosts: all
tasks:
- name: task1
debug:
msg: "OK"
register: result1
- name: task2
debug:
msg: "{{ result1 is success }}, {{ result1 is failed }}, {{ result1 is changed }}, {{ result1 is skipped }}"
# True, False, False, False
- name: task3
debug:
msg: "OK"
when: 1 > 2
register: result2
- name: task4
debug:
msg: "{{ result2 is success }}, {{ result2 is failed }}, {{ result2 is changed }}, {{ result2 is skipped }}"
# True, False, False, True
- name: task5
debug:
msg: "{{ var1 }}"
ignore_errors: true
register: result3
- name: task6
debug:
msg: "{{ result3 is success }}, {{ result3 is failed }}, {{ result3 is changed }}, {{ result3 is skipped }}"
# False, True, False, False
- name: task7
shell: "echo OK"
register: result4
- name: task8
debug:
msg: "{{ result4 is success }}, {{ result4 is failed }}, {{ result4 is changed }}, {{ result4 is skipped }}"
# True, False, True, False
类型
你可能想通过 type_debug
filter获取类型,然后跟字符串比较,比如:
---
- hosts: all
tasks:
- name: task1
debug:
msg: "{{ (1 | type_debug) == 'int' }}" # true
但是还有更好的方法,就是使用test来做类型比较。
string
:字符串iterable
:可迭代sequence
:顺序mapping
:映射(dictionary)
比如:
---
- hosts: all
vars:
var1: "aaa"
var2: 123
var3: ["aaa", "bbb", "ccc"]
var4: {name: Tom, age: 20}
tasks:
- name: task1
debug:
msg: "{{ var1 is string }}, {{ var1 is iterable }}, {{ var1 is sequence }}, {{ var1 is mapping }}" # True, True, True, False
- name: task2
debug:
msg: "{{ var2 is string }}, {{ var2 is iterable }}, {{ var2 is sequence }}, {{ var2 is mapping }}" # False, False, False, False
- name: task3
debug:
msg: "{{ var3 is string }}, {{ var3 is iterable }}, {{ var3 is sequence }}, {{ var3 is mapping }}" # False, True, True, False
- name: task4
debug:
msg: "{{ var4 is string }}, {{ var4 is iterable }}, {{ var4 is sequence }}, {{ var4 is mapping }}" # True, True, False
数值类型:
number
:数值integer
:整数float
:浮点数
比如:
---
- hosts: all
vars:
var1: 123
var2: 45.6
tasks:
- name: task1
debug:
msg: "{{ var1 is number }}, {{ var1 is integer }}, {{ var1 is float }}"
# True, True, False
- name: task2
debug:
msg: "{{ var2 is number }}, {{ var2 is integer }}, {{ var2 is float }}"
# True, False, True
类型转换,比如:
---
- hosts: all
vars:
var1: "123"
var2: 123
var3: 45.6
tasks:
- name: task1
debug:
msg: "{{ (var1 | int) is integer }}" # true
- name: task2
debug:
msg: "{{ (var1 | float) is float }}" # true
- name: task3
debug:
msg: "{{ (var2 | float) is float }}" # true
- name: task4
debug:
msg: "{{ (var3 | int) is integer }}" # true
- name: task5
debug:
msg: "{{ (var2 | string) is string }}" # true
- name: task6
debug:
msg: "{{ (var3 | string) is string }}" # true
注:把 45.6
转换为 int
类型,结果是45
。
布尔类型:
yes
true
True
TRUE
no
No
NO
false
False
FALSE
比如:
---
- hosts: all
tasks:
- name: task1
loop:
- yes
- Yes
- YES
- true
- True
- TRUE
- no
- No
- NO
- false
- False
- FALSE
debug:
msg: "{{ item is boolean }}"
本例中,所有item都是布尔类型。
注意:要么全部小写,要么全部大写,要么首字母大写,后面都小写。其它写法比如 FaLSE
不是布尔类型。
注:官网上说 yes
是唯一大小写敏感的:
Note also that
yes
is the only case-sensitive variant of these values.
但我试了一下,貌似跟其它几个值也没什么不同。
注:在我的测试中发现, yes
和 no
赋值给变量时,变量会被当做 bool
类型,但是literal的 yes
和 no
并不是 bool
类型:
---
- hosts: all
vars:
var1: yes
tasks:
- name: task1
debug:
msg: "{{ var1 | type_debug }}" # bool
- name: task2
debug:
msg: "{{ var1 is boolean }}" # true
- name: task3
debug:
msg: "{{ no is boolean }}" # false
- name: task4
debug:
msg: "{{ no | type_debug }}" # AnsibleUndefined
- name: task5
debug:
msg: "{{ true is boolean }}" # true
参考
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tests.html
https://jinja.palletsprojects.com/en/latest/templates/#jinja-filters.select