條件選擇

常常來說,一個play的結果經常取決於一個變數的值,事件(從遠端系統得到事件), 或者之前任務的結果.在有些情況下,這些變數的值也會取決於其他變數. 進而,可以建立多餘的組基於這些主機是否符合某些條件來操控主機, Ansible 提供了很多不同選項,來控制執行流. 讓我們詳細看看這些都是啥.

When 語句

有時候使用者有可能需要某一個主機越過某一個特定的步驟.這個過程就可以簡單的像在某一個特定版本的系統上 少裝了一個包一樣或者像在一個滿了的檔案系統上執行清理操作一樣. 這些操作在Ansible上,若使用`when`語句都異常簡單.When語句包含Jinja2表示式(參見:doc:playbooks_variables). 實際上真的很簡單:

tasks:
  - name: "shutdown Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_os_family == "Debian"

一系列的Jinja2 “過濾器” 也可以在when語句中使用, 但有些是Ansible中獨有的. 比如我們想忽略某一錯誤,通過執行成功與否來做決定,我們可以像這樣:

tasks:
  - command: /bin/false
    register: result
    ignore_errors: True
  - command: /bin/something
    when: result|failed
  - command: /bin/something_else
    when: result|success
  - command: /bin/still/something_else
    when: result|skipped

我知道,在這裡討論’register’語句命令,有點過於超前,我們將會在這議長靠後一點的地方接觸這個.

友情提示,如果想檢視哪些事件在某個特定系統中時允許的,可以執行以下命令:

ansible hostname.example.com -m setup

提示: 有些時候你得到一個返回參數的值是一個字元串,並且你還想使用數學操作來比較它,那麼你可以執行一下操作:

tasks:
  - shell: echo "only on Red Hat 6, derivatives, and later"
    when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6

Note

以上事例需要目標主機上安裝lsb_release軟體包,來返回ansible_lsb.major_release 事件.

在playbooks 和 inventory中定義的變數都可以使用. 下面一個例子,就是基於布爾值來決定一個任務是否被執行:

vars:
  epic: true

一個條件選擇執行也許看起來像這樣:

tasks:
    - shell: echo "This certainly is epic!"
      when: epic

或者像這樣:

tasks:
    - shell: echo "This certainly isn't epic!"
      when: not epic

如果一個變數不存在,你可以使用Jinja2的`defined`命令跳過或略過.例如:

tasks:
    - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
      when: foo is defined

    - fail: msg="Bailing out. this play requires 'bar'"
      when: bar is not defined

這個機制在選擇引入變數檔案時有時候特別有用,詳情如下.

note當同時使用`when`he`with_items` (詳見:doc:playbooks_loops), note`when`語句對於不同項目將會單獨處理.這個源於原初設計:

tasks:
    - command: echo {{ item }}
      with_items: [ 0, 2, 4, 6, 8, 10 ]
      when: item > 5

載入客戶事件

載入客戶自己的事件,事實上也很簡單,將在:doc:developing_modules 詳細介紹.只要呼叫客戶自己的事件,進而把所有的模組放在任務列表頂端, 變數的返回值今後就可以訪問了:

tasks:
    - name: gather site specific fact data
      action: site_facts
    - command: /usr/bin/thingy
      when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'

在roles 和 includes 上面應用’when’語句

note,如果你的很多工都共享同樣的條件語句的話,可以在選擇語句後面新增inlcudes語句,參見下面事例. 這個特性並不適用於playbook的inclues,只有task 的 includes適用.所有的task都會被檢驗, 選擇會應用到所有的task上面:

- include: tasks/sometasks.yml
  when: "'reticulating splines' in output"

或者應用於role:

- hosts: webservers
  roles:
     - { role: debian_stock_config, when: ansible_os_family == 'Debian' }

在系統中使用這個方法但是並不能匹配某些標準時,你會發現在Ansible中,有很多預設’skipped’的結果. 詳情參見:doc:modules 文件中的 ‘group_by’ 模組, 你會找到更加賞心悅目的方法來解決這個問題.

條件匯入

Note

這是一個很高階但是卻被經常使用的話題.當然你也可以跳過這一節.

基於某個特定標準,又是你也許在一個playbook中你想以不同的方式做同一件事. 在不同平臺或作業系統上使用痛一個playbook就是一個很好的例子.

舉個例子,名字叫做Apache的包,在CentOS 和 Debian系統中也許不同, 但是這個問題可以一些簡單的語法就可以被Ansible Playbook解決:

---
- hosts: all
  remote_user: root
  vars_files:
    - "vars/common.yml"
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure apache is running
    service: name={{ apache }} state=running

Note

‘ansible_os_family’ 已經被匯入到為vars_files定義的檔名列表中了.

提醒一下,很多的不同的YAML檔案只是包含鍵和值:

---
# for vars/CentOS.yml
apache: httpd
somethingelse: 42

這個具體事怎麼工作的呢? 如果作業系統是’CentOS’, Ansible匯入的第一個檔案將是’vars/CentOS.yml’,緊接著 是’/var/os_defaults.yml’,如果這個檔案不存在.而且在列表中沒有找到,就會報錯. 在Debian,最先檢視的將是’vars/Debian.yml’而不是’vars/CentOS.yml’, 如果沒找到,則尋找預設檔案’vars/os_defaults.yml’ 很簡單.如果使用這個條件性匯入特性,你需要在執行playbook之前安裝facter 或者 ohai.當然如果你喜歡, 你也可以把這個事情推給Ansible來做:

# for facter
ansible -m yum -a "pkg=facter state=present"
ansible -m yum -a "pkg=ruby-json state=present"

# for ohai
ansible -m yum -a "pkg=ohai state=present"

Ansible 中的設定方式———— 從任務中把參數分開,這樣可避免程式碼中有太多醜陋巢狀if等複雜語句. 這樣可以使得配置條目更加的流暢的賞心悅目———— 特別是因為這樣可以儘量減少決定點

基於變數選擇檔案和模版

Note

這是一個經常用到的高階話題.也可以跳過這章.

有時候,你想要複製一個配置檔案,或者一個基於參數的模版. 下面的結構選載選第一個宿主給予的變數檔案,這些可以比把很多if選擇放在模版裡要簡單的多. 下面的例子展示怎樣根據不同的系統,例如CentOS,Debian製作一個配置檔案的模版:

- name: template a file
   template: src={{ item }} dest=/etc/myapp/foo.conf
   with_first_found:
     - files:
        - {{ ansible_distribution }}.conf
        - default.conf
       paths:
        - search_location_one/somedir/
        - /opt/other_location/somedir/

註冊變數

經常在playbook中,儲存某個命令的結果在變數中,以備日後訪問是很有用的. 這樣使用命令模組可以在許多方面除去寫站(site)特異事件,據哥例子 你可以檢測某一個特定程式是否存在

這個 ‘register’ 關鍵詞決定了把結果儲存在哪個變數中.結果參數可以用在模版中,動作條目,或者 when 語句. 像這樣(這是一個淺顯的例子):

- name: test play
  hosts: all

  tasks:

      - shell: cat /etc/motd
        register: motd_contents

      - shell: echo "motd contains the word hi"
        when: motd_contents.stdout.find('hi') != -1

就像上面展示的那樣,這個註冊後的參數的內容為字元串’stdout’是可以訪問. 這個註冊了以後的結果,如果像上面展示的,可以轉化為一個list(或者已經是一個list),就可以在任務中的”with_items”中使用. “stdout_lines”在物件中已經可以訪問了,當然如果你喜歡也可以呼叫 “home_dirs.stdout.split()” , 也可以用其它欄位切割:

- name: registered variable usage as a with_items list
  hosts: all

  tasks:

      - name: retrieve the list of home directories
        command: ls /home
        register: home_dirs

      - name: add home dirs to the backup spooler
        file: path=/mnt/bkspool/{{ item }} src=/home/{{ item }} state=link
        with_items: home_dirs.stdout_lines
        # same as with_items: home_dirs.stdout.split()

See also

Playbooks
An introduction to playbooks
Playbook Roles and Include Statements
Playbook organization by roles
最佳實踐
Best practices in playbooks
條件選擇
Conditional statements in playbooks
Variables
All about variables
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel