环境
- 控制节点:Ubuntu 22.04
- Ansible 2.10.8
- 管理节点:CentOS 8
使用 when
语句做条件判断
创建文件 test1.yml
如下:
---
- hosts: alltasks:- name: task1debug:msg: "hello"when: 1 > 0- name: task2debug:msg: "OK"when: 1 > 2
运行结果如下:
......
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "hello"
}TASK [task2] ***************************************************************************************
skipping: [192.168.1.55]
......
可见,task满足条件,所以运行了;task2不满足条件,所以没有运行。
多个条件之间可以做逻辑运算(与或非),比如:
when: ((1 > 0) and (1 < 2)) or (not (1 == 3))
多个 and
条件,也可以写成list的形式,比如:
when:- 1 < 2- 2 < 3- 3 < 4
基于 ansible_facts
的条件判断
例如:
- name: task3debug:msg: "hello"when: ansible_facts['os_family'] == "RedHat" #"Debian"
也可以先把ansible_facts先存为变量,再对变量做条件判断,比如:
- name: task5block:- name: part1set_fact:version: "{{ ansible_facts['distribution_major_version'] }}"- name: part2debug:msg: "Horse"when: version | int >= 8
注:先用filter把变量转为int类型,再和整数8比较大小。
基于register变量的条件判断
这是一种常见的用法,上一个task把结果记录在变量里,下一个task根据该变量的值做条件判断。
---
- hosts: alltasks:- name: task1shell: cat /tmp/a.txt | wc -lregister: result- name: task2debug:msg: "More than 100 lines!"when: result.stdout | int > 1
- 当文件超过100行时,结果如下:
......
TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "More than 100 lines!"
}
......
当文件不超过100行时,结果如下:
......
TASK [task2] ***************************************************************************************
skipping: [192.168.1.55]
......
对register变量,可作如下判断:
is failed
is succeeded
is skipped
is changed
例如:
---
- hosts: alltasks:- name: task1ansible.builtin.command: /bin/falseregister: resultignore_errors: true- name: task2debug:msg: "Task fails!"when: result is failed- name: task3debug:msg: "Task succeeds!"when: result is succeeded- name: task4debug:msg: "Task skipped!"when: result is skipped- name: task5 debug:msg: "Task changed!"when: result is changed
基于变量的条件判断
注意: when
语句里的变量,不需要加 {{ }}
。
变量可转换成bool类型,例如:
---
- hosts: allvars:- var1: true- var2: false- var3: "yes"- var4: "no"tasks:- name: task1debug:msg: "task1"when: var1- name: task2debug:msg: "task2"when: not var2- name: task3debug:msg: "task3"when: var3 | bool- name: task4debug:msg: "task4"when: not (var4 | bool)
使用没有定义的变量会报错,所以,在使用变量前,可以先判断其是否定义:
is defined
is undifined
例如:
---
- hosts: alltasks:- name: task1debug:msg: "OK"when: var1 is defined and var1
这是一种非常常见的用法。
在循环里使用条件判断
loop
和 when
可以一起用,对每次循环做条件判断。例如,遍历文件每一行,若内容超过2个字符,则打印其内容:
---
- hosts: alltasks:- name: task1shell: cat /tmp/a.txtregister: result- name: task2debug:msg: "{{ item }}"loop: "{{ result.stdout_lines }}"when: item | length > 2
假设 a.txt
内容如下:
aaaaa
b
ccccc
则运行结果如下:
TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => (item=aaaaa) => {"msg": "aaaaa"
}
skipping: [192.168.1.55] => (item=b)
ok: [192.168.1.55] => (item=ccccc) => {"msg": "ccccc"
}
也可以遍历/判断自定义的list或者dict,例如:
---
- hosts: allvars:- mylist1: [ 1, 2, 3 ]- mydict1: {"a": 10, "b": 20, "c": 30}tasks:- name: task1debug:msg: "{{ item }}"loop: "{{ mylist1 }}"when: item > 1- name: task2debug:msg: "{{ item }}"loop: "{{ query('dict', mydict1) }}"when: item.value > 10
运行结果如下:
......
TASK [task1] ***************************************************************************************
skipping: [192.168.1.55] => (item=1)
ok: [192.168.1.55] => (item=2) => {"msg": 2
}
ok: [192.168.1.55] => (item=3) => {"msg": 3
}TASK [task2] ***************************************************************************************
skipping: [192.168.1.55] => (item={'key': 'a', 'value': 10})
ok: [192.168.1.55] => (item={'key': 'b', 'value': 20}) => {"msg": {"key": "b","value": 20}
}
ok: [192.168.1.55] => (item={'key': 'c', 'value': 30}) => {"msg": {"key": "c","value": 30}
}
......
注意,最好也判断一下变量是否定义,比如:
- name: task3debug:msg: "{{ item }}"loop: "{{ mylist2 }}"when: mylist2 is defined and item > 1- name: task4debug:msg: "{{ item }}"loop: "{{ query('dict', mydict2) }}"when: mydict2 is defined and item.value > 10
或者提供一个缺省的空list/dict,例如:
- name: task5debug:msg: "{{ item }}"loop: "{{ mylist2 | default([]) }}"when: item > 1- name: task6debug:msg: "{{ item }}"loop: "{{ query('dict', mydict2 | default({})) }}"when: item.value > 10
注意:task3和task4会skip,而task5和task6会运行。
import和include的条件判断
先看include。例如, main.yml
内容如下:
---
- hosts: alltasks:- name: task1include_tasks: test11.yml#import_tasks: test11.ymlwhen: var1 is not defined#vars:# - var1: 111
test11.yml
内容如下:
---
- name: task1debug:msg: "var1 = {{ var1 }}"when: var1 is defined- name: task2debug:msg: "I am task2"- name: task3set_fact:var1: 123when: var1 is not defined- name: task4debug:msg: "var1 = {{ var1 }}"when: var1 is defined- name: task5debug:msg: "I am task5"
若 main.yml
中没有定义 var1
,则运行结果如下:
TASK [main_task1] **********************************************************************************
included: /root/temp/temp1113_2/test11.yml for 192.168.1.55TASK [task1] ***************************************************************************************
skipping: [192.168.1.55]TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "I am task2"
}TASK [task3] ***************************************************************************************
ok: [192.168.1.55]TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "var1 = 123"
}TASK [task5] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "I am task5"
}
若 main.yml
中定义了 var1
,则运行结果如下:
TASK [main_task1] **********************************************************************************
skipping: [192.168.1.55]
include_tasks
的逻辑比较直观:
- 如果
main.yml
没有定义var1
,则满足条件,运行test11.yml
。 - 如果
main.yml
定义了var1
,则不满足条件,不运行test11.yml
。
现在,把 include_tasks
换成 import_tasks
, test11.yml
内容不变。
若 main.yml
中没有定义 var1
,则运行结果如下:
TASK [task1] ***************************************************************************************
skipping: [192.168.1.55]TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "I am task2"
}TASK [task3] ***************************************************************************************
ok: [192.168.1.55]TASK [task4] ***************************************************************************************
skipping: [192.168.1.55]TASK [task5] ***************************************************************************************
skipping: [192.168.1.55]
若 main.yml
中定义了 var1
,则运行结果如下:
TASK [task1] ***************************************************************************************
skipping: [192.168.1.55]TASK [task2] ***************************************************************************************
skipping: [192.168.1.55]TASK [task3] ***************************************************************************************
skipping: [192.168.1.55]TASK [task4] ***************************************************************************************
skipping: [192.168.1.55]TASK [task5] ***************************************************************************************
skipping: [192.168.1.55]
import_tasks
的运行结果和 include_tasks
大不相同。这是因为, import_tasks
是在预编译期,就把引用的task替换过来了(注意在运行结果里并没有出现 main_task1
的字眼),所以就相当于把 when
的条件判断放到 test11.yml
的每个task里了:
---
- name: task1debug:msg: "var1 = {{ var1 }}"when: var1 is defined and var1 is not defined- name: task2debug:msg: "I am task2"when: var1 is not defined- name: task3set_fact:var1: 123when: var1 is not defined and var1 is not defined- name: task4debug:msg: "var1 = {{ var1 }}"when: var1 is defined and var1 is not defined- name: task5debug:msg: "I am task5"when: var1 is not defined
这样就能理解运行结果了。
注意: main_task1
里的变量定义,并没有放到 test11.yml
的每个task里。
创建文件 main.yml
如下:
---
- hosts: alltasks:- name: main_task1import_tasks: test13.ymlvars:- var1: 111
创建文件 test13.yml
如下:
---
- name: task1debug:msg: "var1 = {{ var1 }}"when: var1 is defined- name: task2set_fact:var1: 123- name: task3debug:msg: "var1 = {{ var1 }}"when: var1 is defined
运行结果如下:
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "var1 = 111"
}TASK [task2] ***************************************************************************************
ok: [192.168.1.55]TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "var1 = 123"
}
可见,task1和task3打印的 var1
变量值并不相同。前者是从 main.yml
而来,后者是从task2而来。
注:关于 import_xxx
和 include_xxx
,参见我另一篇文档。
调试
最简单的方法就是打印出来看一下。比如:
---
- hosts: alltasks:- name: task1set_fact:var1: '123'- name: task2debug:msg: "var1 = {{ var1 }}"when: var1 == 123
运行结果如下:
TASK [task1] ***************************************************************************************
ok: [192.168.1.55]TASK [task2] ***************************************************************************************
skipping: [192.168.1.55]
可以看到,task2被skip了,这是因为 when
语句没有满足条件。
要确认的话,可以把条件判断的结果打印出来:
......- name: task3debug:var: var1 == 123
......
运行结果如下:
......
TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {"var1 == 123": false
}
......
可见条件判断的结果确实是false。
注意:用的是 debug
和 var
打印条件判断的结果。
接下来,可以打印出来变量看一下其值:
......- name: task4debug:var: var1
......
运行结果如下:
......
TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => {"var1": "123"
}
......
可见 var1
的值是 "123"
,是一个字符串(如果是数值则是 123
)。
当然,更直接的办法是查看一下 var1
的类型:
......- name: task5debug:msg: "{{ var1 | type_debug }}"
......
运行结果如下:
......
TASK [task5] ***************************************************************************************
ok: [192.168.1.55] => {"msg": "AnsibleUnicode"
}
......
如果是数值,则类型是 int
。
因为 var1
是字符串,而 123
是整数,所以task2的判断条件不满足。
修改方法为:
原先为: var1 == 123
应改为: var1 == '123'
,或者 var1 | int == 123
参考
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html