
前言
很多時候,某些場景下playbook的結果依賴于變量、fact或者是前一個任務的執(zhí)行結果,或者有的時候,我們會基于上一個task執(zhí)行返回的結果而決定如何執(zhí)行后續(xù)的task。這個時候就需要用到條件判斷。
條件語句在Ansible中的使用場景:
- 在目標主機上定義了一個硬限制,比如:目標主機的發(fā)行版本必須是RedHat,才能執(zhí)行該task。
- 捕獲一個命令的輸出,根據命令輸出結果的不同以觸發(fā)不同的task。
- 根據不同目標主機的facts,以定義不同的task。
- 根據目標機的cpu或者memory的大小,對相關應用性能進行調優(yōu)。
- 用于判斷某個服務的配置文件是否發(fā)生變更,以確定是否需要重啟服務等。
下面就介紹一些常用的條件判斷。
when 關鍵字
1、when 關鍵字使用
在ansible中,when是條件判斷的最常用關鍵字。如在安裝包的時候,需要指定主機的操作系統(tǒng)類型,可以使用when語句來做判斷。when關鍵字后面跟著的是python的表達式,在表達式中你能夠使用任何的變量或者fact,當表達式的結果返回的是false,便會跳過本次的任務。
示例:
---
- name: install wget package
hosts: all
tasks:
- name: Install wget
yum:
name: wget
state: installed
when: ansible_os_family == "RedHat"
2、when 配合比較運算符
以上示例,我們使用了"=="的比較運算符,在ansible中,還支持如下比較運算符:
- ==:比較兩個對象是否相等,相等則返回真??捎糜诒容^字符串和數字
- !=:比較兩個對象是否不等,不等則為真。
:比較兩個對象的大小,左邊的值大于右邊的值,則為真
- <:比較兩個對象的大小,左邊的值小于右邊的值,則為真
=:比較兩個對象的大小,左邊的值大于等于右邊的值,則為真
- <=:比較兩個對象的大小,左邊的值小于等于右邊的值,則為真
如:
when: ansible_disibution == "CentOS"
when: ansible_machine == "x86_64"
when: max_memory <= 512
3、邏輯運算符
- and:邏輯與,當左邊和右邊兩個表達式同時為真,則返回真。
- or:邏輯或,當左右和右邊兩個表達式任意一個為真,則返回真。
- not:邏輯否,對表達式取反。
- ():當一組表達式組合在一起,形成一個更大的表達式,組合內的所有表達式都是邏輯與的關系。
# 邏輯與
when: ansible_disibution == "CentOS" and ansible_disibution_major_vsion == "7"
# 邏輯或
when: ansible_disibution == "RedHat" or ansible_disibution == "Fedora"
when:
- ansible_disibution_vsion == "7.9"
- ansible_kernel == "3.10.0-327.el7.x86_64"
# 組合使用
when: =>
( ansible_disibution == "RedHat" and ansible_disibution_major_vsion == "7" )
or
( ansible_disibution == "Fedora" and ansible_disibution_major_vsion == "28")
示例:
- name: uninstall and stop forewalld
hosts: dbsrvs
tasks:
- name: uninstall firewalld
yum: pkg=firwalld state=absent
when: ansible_disibution == "CentOS" and ansible_disibution_major_vsion == "7"
tags: uninstall_firewalld
- name: stop and disabled iptables
shell: systemctl stop firewalld.service && systemctl disable firewalld && systemctl stop iptables && systemctl disable iptables
when: ansible_disibution == "CentOS" and ansible_disibution_major_vsion == "7"
tags: stop_firewalld
###
- name: restart httpd if postfix is running
hosts: dbsrvs
tasks:
- name: get postfix serv 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 配合條件判斷
通過條件語句判斷tpath的路徑是否存在。
- hosts: dbsrvs
vars:
tpath: /ayunwSky
tasks:
- debug:
msg: "file exist"
when: tpath is exists
參數解釋:
- is exists: 用于路徑存在時返回真。
- is not exists: 用于路徑不存在時返回真。
- hosts: dbsrvs
vars:
tpath: /ayunwSky
tasks:
- debug:
msg: "file not exist"
when: not tpath is exists
除了 exists 方式以外,還有其他的判斷方式,如下:
判斷變量
- defined:判斷變量是否已定義,已定義則返回真。
- undefined:判斷變量是否未定義,未定義則返回真。
- none:判斷變量的值是否為空,如果變量已定義且值為空,則返回真。
- hosts: dbsrvs
gather_facts: no
vars:
tvar: "test"
tvar1:
tasks:
- debug:
msg: "tvar is defined"
when: tvar is defined
- debug:
msg: "tvar2 is undefined"
when: tvar2 is undefined
- debug:
msg: "tvar1 is none"
when: tvar1 is none
判斷執(zhí)行結果
- sucess或succeeded:通過任務執(zhí)行結果返回的信息判斷任務的執(zhí)行狀態(tài),任務執(zhí)行成功則返回true。
- failure或failed:任務執(zhí)行失敗則返回true。
- change或changed:任務執(zhí)行狀態(tài)為changed則返回true。
- skip或skipped:任務被跳過則返回true。
- hosts: dbsrvs
gather_facts: no
vars:
doshell: true
tasks:
- shell: 'cat /ayunwSky/allenjol'
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: dbsrvs
gather_facts: no
vars:
tpath1: "/ayunwSky/allenjol"
tpath2: "/ayunwSky"
tasks:
- debug:
msg: "file"
when: tpath1 is file
- debug:
msg: "directory"
when: tpath2 is directory
判斷字符串
- lower:判斷字符串中的所有字母是否都是小寫,是則為真。
- upper:判斷字符串中的所有字母是否都是大寫,是則為真。
- hosts: dbsrvs
gather_facts: no
vars:
s1: "ayunw"
s2: "AYUNW"
tasks:
- debug:
msg: "s1 is all lowercase"
when: s1 is lower
- debug:
msg: "s2 is all uppercase"
when: s2 is upper
判斷整除
- even:判斷數值是否為偶數,是則為真。
- odd:判斷數值是否為奇數,是則為真。
- divisibleby(n):判斷是否可以整除指定的數值,是則為真。
- hosts: dbsrvs
gather_facts: no
vars:
n1: 5
n2: 10
n3: 20
tasks:
- debug:
msg: "n1 is an even nber"
when: n1 is even
- debug:
msg: "n2 is an odd nber"
when: n2 is odd
- debug:
msg: "n3 can be divided exactly
when: n3 is divisibleby(3)
其他 tests 方法
- version:對比兩個版本號的大小,或者與指定的版本號進行對比,使用語法為vsion("版本號","比較操作符")。
version中使用的比較運算符說明:
- 大于:>, gt
- 大于等于:>=, ge
- 小于:<, lt
- 小于等于:<=, le
- 等于:=, ==, eq
- 不等于:!=, <>, ne
- hosts: dbsrvs
vars:
v1: 1.2
v2: 1.3
tasks:
- debug:
msg: "v1 is greater than v2"
when: v1 is vsion(v2,">")
- debug:
msg: "system vsion ` ansible_distribution_vsion ` greater than 7.3"
when: ansible_distribution_vsion is vsion("7.3","gt")
- superset: 判斷一個list是不是另一個list的父集。
- hosts: dbsrvs
gather_facts: no
vars:
a:
- 3
- 7
b: [1,3,4,5,7,9]
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: dbsrvs
vars:
supported_distros:
- RedHat
- CentOS
tasks:
- debug:
msg: "` ansible_distribution ` in supported_distros"
when: ansible_distribution in supported_distros
- number: 判斷對象是否為一個數字,是則為真。
- hosts: dbsrvs
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做條件判斷時,如果條件成立則執(zhí)行對應的任務。但這就存在一個問題:當我們要使用同一個條件判斷執(zhí)行多個任務的時候,就意味著我們要在某一個任務下面都寫一下when語句,而且判斷條件完全一樣。這種方式非常麻煩。Ansible提供了一種更好的方式來解決這個問題,即block。
在ansible中,使用block將多個任務進行組合,當作一個整體。我們可以對這一個整體做條件判斷,當條件成立時,則執(zhí)行塊中的所有任務:
使用block注意事項:
- 可以為block定義name。
- 可以直接對block使用when,但不能直接對block使用loop。
- hosts: dbsrvs
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"
rescue
block除了能和when一起使用之外,還能作錯誤處理。這個時候就需要用到rescue關鍵字:
- hosts: dbsrvs
tasks:
- block:
- shell: 'ls /ayunwSky'
rescue:
- debug:
msg: '/ayunwSky is not exists'
當block中的任務執(zhí)行失敗時,則運行rescue中的任務。如果block中的任務正常執(zhí)行,則rescue的任務就不會被執(zhí)行。如果block中有多個任務,則任何一個任務執(zhí)行失敗,都會執(zhí)行rescue。block中可以定義多個任務,同樣rescue當中也可以定義多個任務。
always
當block執(zhí)行失敗時,rescue中的任務才會被執(zhí)行;而無論block執(zhí)行成功還是失敗,always中的任務都會被執(zhí)行:
- hosts: dbsrvs
tasks:
- block:
- shell: 'ls /ayunwSky'
rescue:
- debug:
msg: '/ayunwSky is not exists'
always:
- debug:
msg: 'This task always executes'
條件判斷與錯誤處理
fail模塊
在shell中,可能會有這樣的需求:當腳本執(zhí)行至某個階段時,需要對某個條件進行判斷,如果條件成立,則立即終止腳本的運行。在shell中,可以直接調用"exit"即可執(zhí)行退出。事實上,在playbook中也有類似的模塊可以做這件事。即fail模塊。
fail模塊用于終止當前playbook的執(zhí)行,通常與條件語句組合使用,當滿足條件時,終止當前play的運行。
fail模塊只有一個參數,即 msg:終止前打印出信息。
# 使用fail模塊中斷playbook輸出
- hosts: dbsrvs
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主動觸發(fā)失敗。
如果在command_result存在錯誤輸出,且錯誤輸出中,包含了FAILED字串,即返回失敗狀態(tài):
- 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一旦執(zhí)行返回失敗,后續(xù)操作就會中止,所以failed_when通??梢杂糜跐M足某種條件時主動中止playbook運行的一種方式。
- ansible默認處理錯誤的機制是遇到錯誤就停止執(zhí)行。但有些時候,有些錯誤是計劃之中的。我們希望忽略這些錯誤,以讓playbook繼續(xù)往下執(zhí)行。此時可以使用ignore_errors忽略錯誤,從而讓playbook繼續(xù)往下執(zhí)行。
changed_when
當我們控制一些遠程主機執(zhí)行某些任務時,當任務在遠程主機上成功執(zhí)行,狀態(tài)發(fā)生更改時,會返回changed狀態(tài)響應,狀態(tài)未發(fā)生更改時,會返回OK狀態(tài)響應,當任務被跳過時,會返回skipped狀態(tài)響應。我們可以通過changed_when來手動更改changed響應狀態(tài)。
- shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
# 該條task執(zhí)行以后,bass_result.rc的值不為2時,才會返回changed狀態(tài)
changed_when: "bass_result.rc != 2"
# this will never report 'changed' status
- shell: wall 'beep'
# 當changed_when為false時,該條task在執(zhí)行以后,永遠不會返回changed狀態(tài)
changed_when: False
循環(huán)語句中使用條件語句
- 只打印大于 10 的值。
tasks:
- command: echo ` item `
loop: [ 0, 2, 4, 6, 8, 10, 100, 130, 150 ]
when: item > 10
- 確保將mariadb-server安裝到根分區(qū)且根分區(qū)的可用空間要大于200M。
- name: install nginx if enough space on root
yum:
name: nginx
state;latest
loop: "` ansible_mounts `"
when: item.mount == "/" and item.size_available > 200000000
以上就是大部分的判斷方法。