簡介
在有的時候play的結果依賴於變量、fact或者是前一個任務的執行結果,或者有的時候,我們會基於上一個task執行返回的結果而決定如何執行后續的task。這個時候就需要用到條件判斷。
條件語句在Ansible中的使用場景:
- 在目標主機上定義了一個硬限制,比如目標主機的最小內存必須達到多少,才能執行該task
- 捕獲一個命令的輸出,根據命令輸出結果的不同以觸發不同的task
- 根據不同目標主機的facts,以定義不同的task
- 根據目標機的cpu的大小,以調優相關應用性能
- 用於判斷某個服務的配置文件是否發生變更,以確定是否需要重啟服務
when關鍵字
1. when基本使用
在ansible中,使用條件判斷的關鍵字就是when。
如在安裝包的時候,需要指定主機的操作系統類型,或者是當操作系統的硬盤滿了之后,需要清空文件等,可以使用when語句來做判斷 。when關鍵字后面跟着的是python的表達式,在表達式中你能夠使用任何的變量或者fact,當表達式的結果返回的是false,便會跳過本次的任務
下面是一個基本的用法示例:
---
- name: Install vim
hosts: all
tasks:
- name:Install VIM via yum
yum:
name: vim-enhanced
state: installed
when: ansible_os_family =="RedHat"
- name:Install VIM via apt
apt:
name: vim
state: installed
when: ansible_os_family =="Debian"
- name: Unexpected OS family
debug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yes
when: not ansible_os_family =="RedHat" or ansible_os_family =="Debian"
2. 比較運算符
在上面的示例當中,我們使用了"=="的比較運算符,在ansible中,還支持如下比較運算符:
==
:比較兩個對象是否相等,相等則返回真。可用於比較字符串和數字!=
:比較兩個對象是否不等,不等則為真。>
:比較兩個對象的大小,左邊的值大於右邊的值,則為真<
:比較兩個對象的大小,左邊的值小於右邊的值,則為真>=
:比較兩個對象的大小,左邊的值大於等於右邊的值,則為真<=
:比較兩個對象的大小,左邊的值小於等於右邊的值,則為真
下面是一些簡單的示例:
when: ansible_machine == "x86_64"
when: max_memory <= 512
3. 邏輯運算符
在Ansible中,除了比較運算符,還支持邏輯運算符:
- and:邏輯與,當左邊和右邊兩個表達式同時為真,則返回真
- or:邏輯或,當左右和右邊兩個表達式任意一個為真,則返回真
- not:邏輯否,對表達式取反
- ():當一組表達式組合在一起,形成一個更大的表達式,組合內的所有表達式都是邏輯與的關系
示例:
# 邏輯或
when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"
# 邏輯與
when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"
when:
- ansible_distribution_version == "7.5"
- ansible_kernel == "3.10.0-327.el7.x86_64"
# 組合
when: =>
( ansible_distribution == "RedHat" and ansible_distribution_major_version == "7" )
or
( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28")
一個完整的例子:
# 判斷register注冊變量的返回結果
- name: restart httpd if postfix is running
hosts: test
tasks:
- name: get postfix server status
command: /usr/bin/systemctl is-active postfix
ignore_errors: yes
register: result
- name: restart apache httpd based on postfix status
service:
name: httpd
state: restarted
when: result.rc == 0
條件判斷與tests
在shell當中,我們可使用test命令來進行一些常用的判斷操作,如下:
# 判斷/test文件是否存在
test -e /test
# 判斷/testdir是否存在且為一個目錄
test -d /testdir
事實上,在ansible中也有類似的用法,只不過ansible沒有使用linux的test命令,而是jinja2模板的tests。
下面是一個簡單示例:
# 通過條件語句判斷testpath的路徑是否存在
- hosts: test
vars:
testpath: /testdir
tasks:
- debug:
msg: "file exist"
when: testpath is exists
上面的示例中,我們使用了is exists
用於路徑存在時返回真,也可以使用is not exists
用於路徑不存在時返回真。也可以在整個條件表達式的前面使用not以取反:
- hosts: test
vars:
testpath: /testdir1
tasks:
- debug:
msg: "file not exist"
when: not testpath is exists
在ansible中,除了能夠使用exists這種tests之外,還有一些別的tests。接下來我們詳細說一說。
判斷變量
- defined:判斷變量是否已定義,已定義則返回真
- undefined:判斷變量是否未定義,未定義則返回真
- none:判斷變量的值是否為空,如果變量已定義且值為空,則返回真
示例:
- hosts: test
gather_facts: no
vars:
testvar: "test"
testvar1:
tasks:
- debug:
msg: "testvar is defined"
when: testvar is defined
- debug:
msg: "testvar2 is undefined"
when: testvar2 is undefined
- debug:
msg: "testvar1 is none"
when: testvar1 is none
判斷執行結果
- sucess或succeeded:通過任務執行結果返回的信息判斷任務的執行狀態,任務執行成功則返回true
- failure或failed:任務執行失敗則返回true
- change或changed:任務執行狀態為changed則返回true
- skip或skipped:任務被跳過則返回true
示例:
- hosts: test
gather_facts: no
vars:
doshell: true
tasks:
- shell: 'cat /testdir/aaa'
when: doshell
register: result
ignore_errors: true
- debug:
msg: "success"
when: result is success
- debug:
msg: "failed"
when: result is failure
- debug:
msg: "changed"
when: result is change
- debug:
msg: "skip"
when: result is skip
判斷路徑
- file:判斷指定路徑是否為一個文件,是則為真
- directory:判斷指定路徑是否為一個目錄,是則為真
- link:判斷指定路徑是否為一個軟鏈接,是則為真
- mount:判斷指定路徑是否為一個掛載點,是則為真
- exists:判斷指定路徑是否存在,存在則為真
特別注意:關於路徑的所有判斷均是判斷主控端上的路徑,而非被控端上的路徑
示例:
- hosts: test
gather_facts: no
vars:
testpath1: "/testdir/test"
testpath2: "/testdir"
tasks:
- debug:
msg: "file"
when: testpath1 is file
- debug:
msg: "directory"
when: testpath2 is directory
判斷字符串
- lower:判斷字符串中的所有字母是否都是小寫,是則為真
- upper:判斷字符串中的所有字母是否都是大寫,是則為真
- hosts: test
gather_facts: no
vars:
str1: "abc"
str2: "ABC"
tasks:
- debug:
msg: "str1 is all lowercase"
when: str1 is lower
- debug:
msg: "str2 is all uppercase"
when: str2 is upper
判斷整除
- even:判斷數值是否為偶數,是則為真
- odd:判斷數值是否為奇數,是則為真
- divisibleby(num):判斷是否可以整除指定的數值,是則為真
示例:
- hosts: test
gather_facts: no
vars:
num1: 6
num2: 8
num3: 15
tasks:
- debug:
msg: "num1 is an even number"
when: num1 is even
- debug:
msg: "num2 is an odd number"
when: num2 is odd
- debug:
msg: "num3 can be divided exactly by"
when: num3 is divisibleby(3)
其他tests
-
version
可用於對比兩個版本號的大小,或者與指定的版本號進行對比,使用語法為version("版本號","比較操作符")
- hosts: test vars: ver1: 1.2 ver2: 1.3 tasks: - debug: msg: "ver1 is greater than ver2" when: ver1 is version(ver2,">") - debug: msg: "system version {{ ansible_distribution_version }} greater than 7.3" when: ansible_distribution_version is version("7.3","gt")
version中使用的比較運算符說明:
- 大於: >, gt
- 大於等於: >=, ge
- 小於: <, lt
- 小於等於: <=, le
- 等於: =, ==, eq
- 不等於: !=, <>, ne
-
subset
判斷一個list是不是另一個list的子集 -
superset
判斷一個list是不是另一個list的父集"- hosts: test gather_facts: no vars: a: - 2 - 5 b: [1,2,3,4,5] tasks: - debug: msg: "A is a subset of B" when: a is subset(b) - debug: msg: "B is the parent set of A" when: b is superset(a)
-
in
判斷一個字符串是否存在於另一個字符串中,也可用於判斷某個特定的值是否存在於列表中- hosts: test vars: supported_distros: - RedHat - CentOS tasks: - debug: msg: "{{ ansible_distribution }} in supported_distros" when: ansible_distribution in supported_distros
-
string
判斷對象是否為一個字符串,是則為真 -
number
判斷對象是否為一個數字,是則為真
- hosts: test
gather_facts: no
vars:
var1: 1
var2: "1"
var3: a
tasks:
- debug:
msg: "var1 is a number"
when: var1 is number
- debug:
msg: "var2 is a string"
when: var2 is string
- debug:
msg: "var3 is a string"
when: var3 is string
條件判斷與block
block
我們在前面使用when做條件判斷時,如果條件成立則執行對應的任務。但這就面臨一個問題,當我們要使用同一個條件判斷執行多個任務的時候,就意味着我們要在某一個任務下面都寫一下when語句,而且判斷條件完全一樣。這種方式不僅麻煩而且顯得low。Ansible提供了一種更好的方式來解決這個問題,即block。
在ansible中,使用block將多個任務進行組合,當作一個整體。我們可以對這一個整體做條件判斷,當條件成立時,則執行塊中的所有任務:
- hosts: test
tasks:
- debug:
msg: "task1 not in block"
- block:
- debug:
msg: "task2 in block1"
- debug:
msg: "task3 in block1"
when: 2 > 1
下面是一個稍微有用點兒的例子:
- hosts: test
tasks:
- name: set /etc/resolv.conf
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
owner: root
group: root
mode: 0644
- block:
- name: ensure /etc/resolvconf/resolv.conf.d/base file for ubuntu 16.04
template:
src: resolv.conf.j2
dest: /etc/resolvconf/resolv.conf.d/base
- name: config dns for ubuntu 16.04
template:
src: resolv.conf.j2
dest: /etc/resolv.conf
when: ansible_distribution == "Ubuntu" and ansible_distribution_major_version == "16"
使用block注意事項:
- 可以為block定義name(ansible 2.3增加的特性)
- 可以直接對block使用when,但不能直接對block使用loop
rescue
block除了能和when一起使用之外,還能作錯誤處理。這個時候就需要用到rescue關鍵字:
- hosts: test
tasks:
- block:
- shell: 'ls /testdir'
rescue:
- debug:
msg: '/testdir is not exists'
在上面的例子中,當block中的任務執行失敗時,則運行rescue中的任務。如果block中的任務正常執行,則rescue的任務就不會被執行。如果block中有多個任務,則任何一個任務執行失敗,都會執行rescue。block中可以定義多個任務,同樣rescue當中也可以定義多個任務。
always
當block執行失敗時,rescue中的任務才會被執行;而無論block執行成功還是失敗,always中的任務都會被執行:
- hosts: test
tasks:
- block:
- shell: 'ls /testdir'
rescue:
- debug:
msg: '/testdir is not exists'
always:
- debug:
msg: 'This task always executes'
條件判斷與錯誤處理
在上面講block的使用方法的時候,我們說block除了可以將多個任務組合到一起,還有錯誤處理的功能。接下來我們繼續說一說錯誤處理。
fail模塊
在shell中,可能會有這樣的需求:當腳本執行至某個階段時,需要對某個條件進行判斷,如果條件成立,則立即終止腳本的運行。在shell中,可以直接調用"exit"即可執行退出。事實上,在playbook中也有類似的模塊可以做這件事。即fail模塊。
fail模塊用於終止當前playbook的執行,通常與條件語句組合使用,當滿足條件時,終止當前play的運行。
選項只有一個:
- msg:終止前打印出信息
示例:
# 使用fail模塊中斷playbook輸出
- hosts: test
tasks:
- shell: echo "Just a test--error"
register: result
- fail:
msg: "Conditions established,Interrupt running playbook"
when: "'error' in result.stdout"
- debug:
msg: "Inever execute,Because the playbook has stopped"
failed_when
事實上,當fail和when組合使用的時候,還有一個更簡單的寫法,即failed_when
,當滿足某個條件時,ansible主動觸發失敗。
# 如果在command_result存在錯誤輸出,且錯誤輸出中,包含了`FAILED`字串,即返回失敗狀態:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
也可以直接通過fail
模塊和when
條件語句,寫成如下:
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
register: command_result
ignore_errors: True
- name: fail the play if the previous command did not succeed
fail: msg="the command failed"
when: " command_result.stderr and 'FAILED' in command_result.stderr"
ansible一旦執行返回失敗,后續操作就會中止,所以failed_when通常可以用於滿足某種條件時主動中止playbook運行的一種方式。
ansible默認處理錯誤的機制是遇到錯誤就停止執行。但有些時候,有些錯誤是計划之中的。我們希望忽略這些錯誤,以讓playbook繼續往下執行。這個時候就可以使用
ignore_errors
忽略錯誤,從而讓playbook繼續往下執行。
changed_when
當我們控制一些遠程主機執行某些任務時,當任務在遠程主機上成功執行,狀態發生更改時,會返回changed狀態響應,狀態未發生更改時,會返回OK狀態響應,當任務被跳過時,會返回skipped狀態響應。我們可以通過changed_when
來手動更改changed
響應狀態。示例如下:
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2" #只有該條task執行以后,bass_result.rc的值不為2時,才會返回changed狀態
# this will never report 'changed' status
- shell: wall 'beep'
changed_when: False #當changed_when為false時,該條task在執行以后,永遠不會返回changed狀態
在循環語句中使用條件語句
# 只打印大於5的值
tasks:
- command: echo {{ item }}
loop: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
# 確保將mariadb-server安裝到根分區且根分區的可用空間要大於300M
- name: install mariadb-server if enough space on root
yum:
name: mariadb-server
state;拉特st
loop: "{{ ansible_mounts }}"
when: item.mount == "/" and item.size_available > 300000000