Ansible系列(8):如何使用when進行條件判斷

這是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的內容:

image-20210924100836952.png

了解了注冊變量有哪些內容,可以基于注冊變量的結果進行判斷。比如:當結果不為空時,打印出該目錄下有幾個條目。

---
- 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 != ""

執行結果如下。其他字段可以根據實際情況進行判斷。

image-20210924102235863.png

基于變量的條件

可以基于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

執行結果:

image-20210924104321144.png

②、根據變量是否定義判斷

---
- 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

執行結果:


image-20210924105233861.png

與循環一起使用

如果將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

執行結果:


image-20210924110517390.png

②、指定默認值default,當該集合未定義時,可以跳過。

---
- hosts: devops
 debugger: on_failed
 tasks:
 - name: print items greater than 5
 debug:
 msg: "item is {{item}}"
 loop: "{{ mylist|default([]) }}"
 when: item > 5

執行結果:

image-20210924111900168.png

③、循環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

執行結果:

image-20210924112359470.png

與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

執行結果:


image-20210924114409120.png

與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語句。

image-20210924130448933.png

與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 

執行結果:

image-20210924131832671.png

②、通過將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

執行結果:

image-20210924132432103.png

③、通過將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

執行結果:

image-20210924133156317.png

總結

這一節主要介紹了when條件語句的使用場景。在不同的使用場景下,when的影響范圍以及表達式的寫法都是不一樣的。本文列出的這幾種基本能滿足日常工作的大多數需求。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容