這是Ansible系列課程第八節,Ansible playbook中如何使用when進行條件判斷,以滿足在各種條件下的任務場景。
該系列課程前后章節都是有關聯性的,對于初學者建議按順序閱讀。也可以選擇特定的章節了解單個知識點。
上一節介紹了Ansible的調試利器debugger,了解了各種調試工具,那么在后續playbook的學習過程中,就會方便很多。我們也會通過調試工具來驗證playbook的處理邏輯。今天要介紹的是playbook中如何使用when來進行條件判斷,這個和其他編程語言中if、else、when進行條件判斷了邏輯是一樣的,只不過要以YAML的格式進行編寫。
when基本條件
when條件的使用很簡單,只需要在單個任務的后面添加when條件判斷語句。when語句中的變量不需要使用{{}}表達式。when條件語句的處理邏輯是:當playbook或task執行時,ansible會在所有主機上進行測試,只在測試通過的主機上執行該任務。比如:只在啟動了SELinux的主機上配置SELinux以允許mysql運行。
tasks:
- name: Configure SELinux to start mysql on any port
seboolean:
name: mysql_connect_any
state: true
persistent: yes
when: ansible_selinux.status == "enabled"
when條件語句中能夠使用的判斷條件有很多,有變量、facts等,when條件語句可以應用于task,roles或者import等。
基于ansible_facts的條件
ansible_facts是單個主機的屬性,比如IP地址,操作系統,網絡信息。當處理不同主機的差異時可以根據ansible_facts的值進行判斷。比如:
當操作系統是CentOS時,安裝哪個包,怎么安裝
當IP地址為內部IP時,就跳過配置防火墻
當文件系統快滿時,執行清理任務
當然還有很多其他facts,可以通過debug打印出ansible_facts都有哪些值。
---
- hosts: devops
tasks:
- name: show ansible facts
debug:
var: ansible_facts
①、當distribution是CentOS時重啟主機
tasks:
- name: Shut down CentOS systems
command: /sbin/shutdown -t now
when: ansible_facts['distribution'] == "CentOS"
②、如果有多個條件,使用括號進行分組
tasks:
- name: Shut down CentOS 6 and Debian 7 systems
command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
(ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
這里用到了邏輯運算符來組合條件:or。如果有多個條件都為真時(即and),可以使用列表形式。
tasks:
- name: Shut down CentOS 6 and Debian 7 systems
command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
③、如果需要類型轉換,可以使用過濾器,比如將字符串轉變為數字。
tasks:
- shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
基于注冊變量的條件
通常在playbook中,會根據前面任務執行的結果來判斷后面任務的執行與否。比如:只有當依賴包安裝成功后,才能安裝該軟件。這時就可以將安裝依賴包的任務的執行結果注冊(register)為變量,再根據注冊變量的值決定后續是否安裝該軟件。
tasks:
- command: /bin/false
register: result
ignore_errors: True
- command: /bin/something
when: result is succeeded</pre>
注冊變量也是一個對象,包含了任務執行的結果和輸出。可以通過debug將注冊變量輸出,以下是打印出注冊變量ls_result的內容:
---
- hosts: devops
tasks:
- name: register variable
command: ls /mnt
register: ls_result
- name: print register variable
debug:
var: ls_result
ls_result的內容:
了解了注冊變量有哪些內容,可以基于注冊變量的結果進行判斷。比如:當結果不為空時,打印出該目錄下有幾個條目。
---
- hosts: devops
tasks:
- name: register variable
command: ls /mnt
register: ls_result
- name: print register variable
debug:
msg: "this directory includes {{ls_result.stdout_lines|length}} items"
when: ls_result.stdout != ""
執行結果如下。其他字段可以根據實際情況進行判斷。
基于變量的條件
可以基于playboo或inventory中定義的變量進行條件判斷。因為when條件判斷的結果是布爾值(True|False)。因此基于條件判斷的變量值有兩類:
可以轉換成布爾的值,比如yes、on、1、true等。該類型的值需要進行bool過濾器轉換。
其他類型的值,通過表達式計算出布爾值。比如:master == ‘master’
①、根據變量值判斷
---
- hosts: devops
debugger: on_failed
vars:
output: yes
tasks:
- name: print debug msg
debug:
msg: "this is debug msg"
when: output | bool
執行結果:
②、根據變量是否定義判斷
---
- hosts: devops
debugger: on_failed
vars:
output: yes
tasks:
- name: variable is defined
debug:
msg: "variable output is {{output}}"
when: output is defined
- name: variable is not defined
debug:
msg: "variable output is not defined"
when: output is undefined
執行結果:
與循環一起使用
如果將when與循環一起使用時,ansible會為每個循環項都執行單獨的條件判斷,不滿足條件的項就會跳過。
①、打印大于5的數字
---
- hosts: devops
debugger: on_failed
tasks:
- name: print items greater than 5
debug:
msg: "item is {{item}}"
loop: [0,1,3,5,6,7,8,10]
when: item > 5
執行結果:
②、指定默認值default,當該集合未定義時,可以跳過。
---
- hosts: devops
debugger: on_failed
tasks:
- name: print items greater than 5
debug:
msg: "item is {{item}}"
loop: "{{ mylist|default([]) }}"
when: item > 5
執行結果:
③、循環dict字典
---
- hosts: devops
debugger: on_failed
vars:
mydict: {"zhangsan":6,"lisi":8,"wangwu":3}
tasks:
- name: print items greater than 5
debug:
msg: "item is {{item.key}}"
loop: "{{ query('dict', mydict|default({})) }}"
when: item.value > 5
執行結果:
與import一起使用
當when條件語句與import一起使用時,ansible會在import的所有task上進行when條件判斷。這個行為和上面的loop一樣。當不滿足條件時,task會skipped。
when-import.yml
---
- hosts: devops
debugger: on_failed
tasks:
- name: import task with when
import_tasks: defined-x-task.yml
when: x is not defined
defined-x-task.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
- name: Print a variable
ansible.builtin.debug:
var: x
執行結果:
與include一起使用
當when與include語句一起使用時,when判斷條件只應用于include這個task,不會應用到include 文件中的任何task。還是以上面的例子為例,將import改成include,看看效果。
---
- hosts: devops
debugger: on_failed
tasks:
- name: include task with when
include_tasks: defined-x-task.yml
when: x is not defined
執行結果,從執行過程可以看出,when條件語句只應用到include task with when這個task,文件里的task沒有應用when語句。
與roles一起使用
可以通過三種方式將when條件語句應用到roles。
①、通過將when語句添加到roles關鍵字下面,向角色中的所有任務添加相同的一個或多個條件。
when-role.yml
---
- hosts: devops
debugger: on_failed
roles:
- role: defined-x-task
when: x is not defined
defined-x-task角色目錄結構,內容與上面的defined-x-task一樣。
roles
- defined-x-task
- tasks
- main.yml
執行結果:
②、通過將when語句放到import_role的下面,向roles中的所有任務添加相同的一個或多個條件。
---
- hosts: devops
debugger: on_failed
tasks:
- name: import role with when
import_role:
name: defined-x-task
when: x is not defined
執行結果:
③、通過將when放到include-role的下面,只會應用到include_role這單個任務,如果需要應該到include文件中的每個任務,那么每個任務也需要添加when語句。這里將roles改成如下內容:
when-include-role.yml
---
- hosts: devops
debugger: on_failed
tasks:
- name: include role with when
include_role:
name: task-with-when
when: x is not defined
/roles/task-with-when/tasks/main.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
when: x is not defined
- name: Print a variable
ansible.builtin.debug:
var: x
when: x is defined
執行結果:
總結
這一節主要介紹了when條件語句的使用場景。在不同的使用場景下,when的影響范圍以及表達式的寫法都是不一樣的。本文列出的這幾種基本能滿足日常工作的大多數需求。