Ansible14:Playbook條件語句

簡介

在有的時候play的結果依賴於變量、fact或者是前一個任務的執行結果,或者有的時候,咱們會基於上一個task執行返回的結果而決定如何執行後續的task。這個時候就須要用到條件判斷。linux

條件語句在Ansible中的使用場景:shell

  • 在目標主機上定義了一個硬限制,好比目標主機的最小內存必須達到多少,才能執行該task
  • 捕獲一個命令的輸出,根據命令輸出結果的不一樣以觸發不一樣的task
  • 根據不一樣目標主機的facts,以定義不一樣的task
  • 根據目標機的cpu的大小,以調優相關應用性能
  • 用於判斷某個服務的配置文件是否發生變動,以肯定是否須要重啓服務

when關鍵字

1. when基本使用

在ansible中,使用條件判斷的關鍵字就是when。apache

如在安裝包的時候,須要指定主機的操做系統類型,或者是當操做系統的硬盤滿了以後,須要清空文件等,可使用when語句來作判斷 。when關鍵字後面跟着的是python的表達式,在表達式中你可以使用任何的變量或者fact,當表達式的結果返回的是false,便會跳過本次的任務ubuntu

下面是一個基本的用法示例:vim

---
- 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中,還支持以下比較運算符:ide

  • ==:比較兩個對象是否相等,相等則返回真。可用於比較字符串和數字
  • !=:比較兩個對象是否不等,不等則爲真。
  • >:比較兩個對象的大小,左邊的值大於右邊的值,則爲真
  • <:比較兩個對象的大小,左邊的值小於右邊的值,則爲真
  • >=:比較兩個對象的大小,左邊的值大於等於右邊的值,則爲真
  • <=:比較兩個對象的大小,左邊的值小於等於右邊的值,則爲真

下面是一些簡單的示例:oop

when: ansible_machine == "x86_64" 

when: max_memory <= 512

3. 邏輯運算符

在Ansible中,除了比較運算符,還支持邏輯運算符:post

  • 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

  1. 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
  2. subset
    判斷一個list是否是另外一個list的子集
  3. 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)
  4. in
    判斷一個字符串是否存在於另外一個字符串中,也可用於判斷某個特定的值是否存在於列表中

    - hosts: test
      vars:
        supported_distros:
          - RedHat
          - CentOS
      tasks:
        - debug:
            msg: "{{ ansible_distribution }} in supported_distros"
          when: ansible_distribution in supported_distros
  5. string
    判斷對象是否爲一個字符串,是則爲真
  6. 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注意事項:

  1. 能夠爲block定義name(ansible 2.3增長的特性)
  2. 能夠直接對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
相關文章
相關標籤/搜索