Bootstrap

Ansible的when语句做条件判断

环境

  • 控制节点:Ubuntu 22.04
  • Ansible 2.10.8
  • 管理节点:CentOS 8

使用 when 语句做条件判断

创建文件 test1.yml 如下:

---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "hello"
      when: 1 > 0

    - name: task2
      debug:
        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: task3
      debug:
        msg: "hello"
      when: ansible_facts['os_family'] == "RedHat" #"Debian"

也可以先把ansible_facts先存为变量,再对变量做条件判断,比如:

    - name: task5
      block:
        - name: part1
          set_fact:
            version: "{{ ansible_facts['distribution_major_version'] }}"
        - name: part2
          debug:
            msg: "Horse"
          when: version | int >= 8

注:先用filter把变量转为int类型,再和整数8比较大小。

基于register变量的条件判断

这是一种常见的用法,上一个task把结果记录在变量里,下一个task根据该变量的值做条件判断。

---
- hosts: all
  tasks:
    - name: task1
      shell: cat /tmp/a.txt | wc -l
      register: result
    - name: task2
      debug:
        msg: "More than 100 lines!"
      when: result.stdout | int > 100
  • 当文件超过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: all
  tasks:
  - name: task1
    ansible.builtin.command: /bin/false
    register: result
    ignore_errors: true

  - name: task2
    debug:
      msg: "Task fails!"
    when: result is failed

  - name: task3
    debug:
      msg: "Task succeeds!"
    when: result is succeeded

  - name: task4
    debug:
      msg: "Task skipped!"
    when: result is skipped

  - name: task5 
    debug:
      msg: "Task changed!"
    when: result is changed

基于变量的条件判断

注意: when 语句里的变量,不需要加 {{ }}

变量可转换成bool类型,例如:

---
- hosts: all
  vars:
    - var1: true
    - var2: false
    - var3: "yes"
    - var4: "no"
  tasks:
    - name: task1
      debug:
        msg: "task1"
      when: var1
    - name: task2
      debug:
        msg: "task2"
      when: not var2
    - name: task3
      debug:
        msg: "task3"
      when: var3 | bool
    - name: task4
      debug:
        msg: "task4"
      when: not (var4 | bool)

使用没有定义的变量会报错,所以,在使用变量前,可以先判断其是否定义:

  • is defined
  • is undifined

例如:

---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "OK"
      when: var1 is defined and var1

这是一种非常常见的用法。

在循环里使用条件判断

loopwhen 可以一起用,对每次循环做条件判断。例如,遍历文件每一行,若内容超过2个字符,则打印其内容:

---
- hosts: all
  tasks:
    - name: task1
      shell: cat /tmp/a.txt
      register: result
    - name: task2
      debug:
        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: all
  vars:
    - mylist1: [ 1, 2, 3 ]
    - mydict1: {"a": 10, "b": 20, "c": 30}
  tasks:
    - name: task1
      debug:
        msg: "{{ item }}"
      loop: "{{ mylist1 }}"
      when: item > 1
    - name: task2
      debug:
        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: task3
      debug:
        msg: "{{ item }}"
      loop: "{{ mylist2 }}"
      when: mylist2 is defined and item > 1

    - name: task4
      debug:
        msg: "{{ item }}"
      loop: "{{ query('dict', mydict2) }}"
      when: mydict2 is defined and item.value > 10

或者提供一个缺省的空list/dict,例如:

    - name: task5
      debug:
        msg: "{{ item }}"
      loop: "{{ mylist2 | default([]) }}"
      when: item > 1

    - name: task6
      debug:
        msg: "{{ item }}"
      loop: "{{ query('dict', mydict2 | default({})) }}"
      when: item.value > 10

注意:task3和task4会skip,而task5和task6会运行。

import和include的条件判断

先看include。例如, main.yml 内容如下:

---
- hosts: all
  tasks:
    - name: task1
      include_tasks: test11.yml
      #import_tasks: test11.yml
      when: var1 is not defined
      #vars:
      #  - var1: 111

test11.yml 内容如下:

---
- name: task1
  debug:
    msg: "var1 = {{ var1 }}"
  when: var1 is defined

- name: task2
  debug:
    msg: "I am  task2"

- name: task3
  set_fact:
    var1: 123
  when: var1 is not defined

- name: task4
  debug:
    msg: "var1 = {{ var1 }}"
  when: var1 is defined

- name: task5
  debug:
    msg: "I am task5"

main.yml 中没有定义 var1 ,则运行结果如下:

TASK [main_task1] **********************************************************************************
included: /root/temp/temp1113_2/test11.yml for 192.168.1.55

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] ***************************************************************************************
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_taskstest11.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: task1
  debug:
    msg: "var1 = {{ var1 }}"
  when: var1 is defined and var1 is not defined

- name: task2
  debug:
    msg: "I am  task2"
  when: var1 is not defined

- name: task3
  set_fact:
    var1: 123
  when: var1 is not defined and var1 is not defined

- name: task4
  debug:
    msg: "var1 = {{ var1 }}"
  when: var1 is defined and var1 is not defined

- name: task5
  debug:
    msg: "I am task5"
  when: var1 is not defined

这样就能理解运行结果了。

注意: main_task1 里的变量定义,并没有放到 test11.yml 的每个task里。

创建文件 main.yml 如下:

---
- hosts: all
  tasks:
    - name: main_task1
      import_tasks: test13.yml
      vars:
        - var1: 111

创建文件 test13.yml 如下:

---
- name: task1
  debug:
    msg: "var1 = {{ var1 }}"
  when: var1 is defined

- name: task2
  set_fact:
    var1: 123

- name: task3
  debug:
    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_xxxinclude_xxx ,参见我另一篇文档。

调试

最简单的方法就是打印出来看一下。比如:

---
- hosts: all
  tasks:
    - name: task1
      set_fact:
        var1: '123'

    - name: task2
      debug:
        msg: "var1 = {{ var1 }}"
      when: var1 == 123

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55]

TASK [task2] ***************************************************************************************
skipping: [192.168.1.55]

可以看到,task2被skip了,这是因为 when 语句没有满足条件。

要确认的话,可以把条件判断的结果打印出来:

......
    - name: task3
      debug:
        var: var1 == 123
......

运行结果如下:

......
TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
    "var1 == 123": false
}
......

可见条件判断的结果确实是false。

注意:用的是 debugvar 打印条件判断的结果。

接下来,可以打印出来变量看一下其值:

......
    - name: task4
      debug:
        var: var1
......

运行结果如下:

......
TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => {
    "var1": "123"
}
......

可见 var1 的值是 "123" ,是一个字符串(如果是数值则是 123 )。

当然,更直接的办法是查看一下 var1 的类型:

......
    - name: task5
      debug:
        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
;