ansible 官方文檔翻譯 playbook loop部分
ansible源文檔位址
有時候你想多次重複一個任務。 在計算機程式設計中,這叫做循環。 常見的 Ansible 循環包括使用檔案子產品更改幾個檔案和 / 或目錄的所有權,使用使用者子產品建立多個使用者,并重複一個輪詢步驟,直到達到某個結果。 為建立循環提供了兩個關鍵字:
loop
和
with_<lookup>
。
注意
- 我們增加在Ansible2.5版本中中加了
。它還沒有完全取代
loop
, 但我們推薦在大多數場景下使用它。
with_<lookup>
- 我們并舍棄
用法--在可預見的未來,這種文法仍然有效。
with_<lookup>
- 我們正在尋求改進
文法,關注這個頁面和changelog 來擷取更新。
loop
目錄
- 對比 loop and with_*
- 标準loop
- 周遊一個簡單的清單
- 周遊哈希清單
- 周遊字典
- 用循環注冊變量
- 複雜的loops
- 周遊嵌套清單
- 嘗試一個任務知道滿足條件為止
- 主機清單的循環
- 使用query或lookup確定清單輸入
- 為loops添加控制
- 用label限制loop輸出
- loop中的暫停
- 通過index_var跟蹤進度
- 通過loop_var定義内部和外部變量名
- 擴充loop變量
- 通路loop_var
- 從 with_X 遷移到 loop
- with_list
- with_items
- with_indexed_items
- with_flattened
- with_together
- with_dict
- with_sequence
- with_subelements
- with_nested/with_cartesian
- with_random_choice
對比 loop
and with_*
loop
with_*
-
關鍵字依賴Lookup Plugins插件--盡管with_
也是一個lookup插件。item
-
關鍵字等價于loop
,是使用簡單周遊時的最佳選擇。with_list
-
關鍵字不再接收一個字元串作為輸入,檢視 Ensuring list input for loop: query vs. lookuploop
- 通常來說,任何包含在 從with_X遷移到loop中的
用法都可以替換成with_*
。loop
- 需要注意的是,在将
替換成with_items
時,由于loop
執行單層隐式扁平化周遊,在使用with_items
作為輸出時,你需要結合loop
一起使用。舉例說明,下面兩種方式的輸出結果相同:flatten(1)
with_items:
- 1
- [2,3]
- 4
你需要像這樣使用:
loop: "{{ [1, [2,3] ,4] | flatten(1) }}"
- 任何依賴
插件的lookup
語句不應該被轉換成with_*
關鍵字。舉例說明,不建議使用下面的場景:loop
loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"
保持這樣的格式會更簡潔。
with_fileglob: '*.txt'
标準loop
周遊一個簡單的清單
重複的任務可以通過簡單的字元串清單寫成标準的
loop
形式。你可以在任務裡使用清單:
- name: add several users
user:
name: "{{ item }}"
state: present
groups: "wheel"
loop:
- testuser1
- testuser2
你可以使用一個變量檔案或者在你的劇本中使用
var
定義清單,然後在任務裡通過清單名稱引用即可:
loop: "{{ somelist }}"
上面例子中的任何一個都相當于:
- name: add user testuser1
user:
name: "testuser1"
state: present
groups: "wheel"
- name: add user testuser2
user:
name: "testuser2"
state: present
groups: "wheel"
您可以直接将清單傳遞給某些插件作為參數。 大多數打包子產品如 yum 包管理器和apt包管理器都具有這種功能。 如果可用,将清單傳遞給參數要比在任務上循環要好。 例如:
- name: optimal yum
yum:
name: "{{ list_of_packages }}"
state: present
- name: non-optimal yum, slower and may cause issues with interdependencies
yum:
name: "{{ item }}"
state: present
loop: "{{ list_of_packages }}"
檢視 子產品文檔 ,看看是否可以将清單傳遞給任何特定子產品的參數。
周遊哈希清單
如果你有一個哈希清單,你可以在循環中引用子鍵。例如:
- name: add several users
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
當将條件句與循環結合時,
When
語句将為每個項分别處理。 有關示例,請參見 When 語句。
周遊字典
若要周遊字典, 請使用
dict2items
字典過濾器:
- name: create a tag dictionary of non-empty tags
set_fact:
tags_dict: "{{ (tags_dict|default({}))|combine({item.key: item.value}) }}"
loop: "{{ tags|dict2items }}"
vars:
tags:
Environment: dev
Application: payment
Another: "{{ doesnotexist|default() }}"
when: item.value != ""
在這裡,我們不想設定空标記,是以我們建立了一個隻包含非空标記的字典。
用循環注冊變量
你可以将
loop
的輸出注冊為變量,例如:
- shell: "echo {{ item }}"
loop:
- "one"
- "two"
register: echo
當使用循環注冊時,放置在變量中的資料結構将包含一個 results 屬性,該屬性是來自子產品的所有響應的清單。 這與直接注冊而不循環時傳回的資料結構不同:
{
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": "echo \"one\" ",
"delta": "0:00:00.003110",
"end": "2013-12-19 12:00:05.187153",
"invocation": {
"module_args": "echo \"one\"",
"module_name": "shell"
},
"item": "one",
"rc": 0,
"start": "2013-12-19 12:00:05.184043",
"stderr": "",
"stdout": "one"
},
{
"changed": true,
"cmd": "echo \"two\" ",
"delta": "0:00:00.002920",
"end": "2013-12-19 12:00:05.245502",
"invocation": {
"module_args": "echo \"two\"",
"module_name": "shell"
},
"item": "two",
"rc": 0,
"start": "2013-12-19 12:00:05.242582",
"stderr": "",
"stdout": "two"
}
]
}
對注冊變量進行後續循環以檢查結果如下所示:
- name: Fail if return code is not 0
fail:
msg: "The command ({{ item.cmd }}) did not have a 0 return code"
when: item.rc != 0
loop: "{{ echo.results }}"
在疊代過程中,目前項的結果将被放置在變量中:
- shell: echo "{{ item }}"
loop:
- one
- two
register: echo
changed_when: echo.stdout != "one"
複雜的loops
周遊嵌套清單
你可以使用 Jinja2表達式來周遊複雜的清單,例如,循環可以組合嵌套的清單:
- name: give users access to multiple databases
mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
loop: "{{ ['alice', 'bob'] | product(['clientdb', 'employeedb', 'providerdb'])|list }}"
嘗試一個任務知道滿足條件為止
在 1.4版本中引入
你可以使用 until 關鍵字重試一個任務,直到滿足某個條件為止:
- shell: /usr/bin/foo
register: result
until: result.stdout.find("all systems go") != -1
retries: 5
delay: 10
此任務最多運作5次,每次嘗試之間延遲10秒。 如果任何嘗試的結果在其标準輸出中具有“ all systems go” ,則任務成功。 “ retries”的預設值為3,“ delay”為5。
若要檢視單個重試的結果,請使用
-vv
運作此劇本。
當您使用 until 運作任務并将結果注冊為變量時,注冊的變量将包含一個名為“ attempts”的鍵,該鍵記錄任務的重試次數。
注意
如果要重試任務,則必須設定 until 參數。 如果沒有定義 until,則強制重試參數的值為1。
主機清單的循環
為了周遊你的主機清單,或者僅僅是主機清單的一個子集,你可以使用一個正常的循環來周遊可以播放的批處理或者分組變量:
# show all the hosts in the inventory
- debug:
msg: "{{ item }}"
loop: "{{ groups['all'] }}"
# show all the hosts in the current play
- debug:
msg: "{{ item }}"
loop: "{{ ansible_play_batch }}"
還有一個特定的查找插件主機清單中主機名,可以這樣使用:
# show all the hosts in the inventory
- debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all') }}"
# show all the hosts matching the pattern, ie all but the group www
- debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all:!www') }}"
關于更多資訊可以在模式中找到目标主機群組。
使用 query
或 lookup
確定清單輸入
query
lookup
Loop 關鍵字需要一個清單作為輸入,但是 lookup 關鍵字預設傳回一個逗号分隔值字元串。 2.5引入了一個新的 Jinja2函數,命名為調用查找插件,它總是傳回一個清單,當使用 loop 關鍵字時,它提供了一個更簡單的界面和更可預測的查找插件輸出。
您可以使用
wantlist=true
強制查找傳回一個要循環的清單,或者您可以改用 query。
這些例子做了同樣的事情:
loop: "{{ query('inventory_hostnames', 'all') }}"
loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
為loops添加控制
在 2.1版本中引入
loop_control 關鍵字可以讓您以有用的方式管理循環。
用label限制loop輸出
在 2.2版本中引入
當循環周遊複雜的資料結構時,任務的控制台輸出可能是巨大的。 要限制顯示的輸出,使用循環控制的 label 指令:
- name: create servers
digital_ocean:
name: "{{ item.name }}"
state: present
loop:
- name: server1
disks: 3gb
ram: 15Gb
network:
nic01: 100Gb
nic02: 10Gb
...
loop_control:
label: "{{ item.name }}"
此任務的輸出将僅顯示每個項的 name 字段,而不是多行
{{ item }}
變量的全部内容。
注意
這是為了使控制台輸出更具可讀性,而不是保護敏感資料。 如果循環中有敏感資料,請在任務上設定
以防止洩露。
no_log: yes
loop中的暫停
在 2.2版本中引入
若要控制任務循環中每個項目執行之間的時間(以秒為機關) ,請使用帶循環控制的 pause 指令
loop_control
:
# main.yml
- name: create servers, pause 3s before creating next
digital_ocean:
name: "{{ item }}"
state: present
loop:
- server1
- server2
loop_control:
pause: 3
通過 index_var
跟蹤進度
index_var
在 2.5版本中引入
若要跟蹤您在循環中的位置,請使用
index_var
指令和
loop_control
。 這個指令指定一個包含目前循環索引的變量名:
- name: count our fruit
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
通過 loop_var
定義内部和外部變量名
loop_var
在 2.1版本中引入
可以使用 include 任務嵌套兩個循環任務。 但是,預設情況下,Ansible 為每個循環設定循環變量項。 這意味着内部的嵌套循環将覆寫外部循環中 item 的值。 您可以使用
loop_var
loop_control
為每個循環指定變量的名稱:
# main.yml
- include_tasks: inner.yml
loop:
- 1
- 2
- 3
loop_control:
loop_var: outer_item
# inner.yml
- debug:
msg: "outer item={{ outer_item }} inner item={{ item }}"
loop:
- a
- b
- c
注意
如果 Ansible 檢測到目前循環正在使用一個已經定義的變量,它将引發一個錯誤以使任務失敗。
擴充loop變量
在 2.8 版本中引入
從 ansible 2.8開始,你可以使用擴充選項來獲得擴充循環資訊來進行循環控制。 此選項将公開以下資訊。
Variable | Description |
---|---|
| 循環中所有項的清單 |
| 循環的目前疊代的索引。(索引從1開始) |
| 循環的目前疊代的索引。(索引從0開始) |
| 倒序循環的目前疊代的索引。(索引到1結束) |
| 倒序循環的目前疊代的索引。(索引到0結束) |
| 如果第一次疊代則為True,否則是False |
| 如果最後一次疊代則為True,否則是False |
| 循環中的項數 |
| 循環上一次疊代中的項。在第一次疊代中這個變量未定義 |
| 循環下一次疊代中的項。在最後一次疊代中這個變量未定義 |
loop_control:
extended: yes
通路loop_var
在 2.8 版本中引入
從 ansible2.8你可以得到提供的值的名稱循環控制。循環變量使用安塞循環變量
對于角色作者,編寫允許循環的角色,而不是口述所需的循環變量值,您可以通過以下方式收集該值:
"{{ lookup('vars', ansible_loop_var) }}"
從 with_X 遷移到 loop
随着 Ansible 2.5的釋出,推薦的循環執行方式是使用新的 loop 關鍵字,而不是使用
with_x
樣式的循環。
在許多情況下,循環文法更好地使用過濾器,而不是更複雜地使用
query
或者
lookup
。
下面的示例将展示如何将許多常見的樣式循環轉換為循環和過濾器。
with_list
with_list
用
loop
替換。
- name: with_list
debug:
msg: "{{ item }}"
with_list:
- one
- two
- name: with_list -> loop
debug:
msg: "{{ item }}"
loop:
- one
- two
with_items
with_items
用
loop
和
flatten
過濾器替換。
- name: with_items
debug:
msg: "{{ item }}"
with_items: "{{ items }}"
- name: with_items -> loop
debug:
msg: "{{ item }}"
loop: "{{ items|flatten(levels=1) }}"
with_indexed_items
with_indexed_items
用
loop
,
flatten
過濾器 和
loop_control.index_var
替換。
- name: with_indexed_items
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_indexed_items: "{{ items }}"
- name: with_indexed_items -> loop
debug:
msg: "{{ index }} - {{ item }}"
loop: "{{ items|flatten(levels=1) }}"
loop_control:
index_var: index
with_flattened
with_flattened
用
loop
和
flatten
過濾器替換。
- name: with_flattened
debug:
msg: "{{ item }}"
with_flattened: "{{ items }}"
- name: with_flattened -> loop
debug:
msg: "{{ item }}"
loop: "{{ items|flatten }}"
with_together
with_together
用
loop
和
zip
過濾器替換。
- name: with_together
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_together:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_together -> loop
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|zip(list_two)|list }}"
with_dict
with_dict
用
loop
和
dictsort
或者
dict2items
過濾器替換。
- name: with_dict
debug:
msg: "{{ item.key }} - {{ item.value }}"
with_dict: "{{ dictionary }}"
- name: with_dict -> loop (option 1)
debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ dictionary|dict2items }}"
- name: with_dict -> loop (option 2)
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ dictionary|dictsort }}"
with_sequence
with_sequence
用
loop
和
range
函數,
format
過濾器替換。
- name: with_sequence
debug:
msg: "{{ item }}"
with_sequence: start=0 end=4 stride=2 format=testuser%02x
- name: with_sequence -> loop
debug:
msg: "{{ 'testuser%02x' | format(item) }}"
# range is exclusive of the end point
loop: "{{ range(0, 4 + 1, 2)|list }}"
with_subelements
用循環和子元素過濾器代替子元素過濾器。
- name: with_subelements
debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
- name: with_subelements -> loop
debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
loop: "{{ users|subelements('mysql.hosts') }}"
with_nested/with_cartesian
with_nested
和
with_cartesian
用
loop
和
product
過濾器替換。
- name: with_nested
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_nested:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_nested -> loop
debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|product(list_two)|list }}"
with_random_choice
with_random_choice
用
random
過濾器替換,不在需要
loop
。
- name: with_random_choice
debug:
msg: "{{ item }}"
with_random_choice: "{{ my_list }}"
- name: with_random_choice -> loop (No loop is needed here)
debug:
msg: "{{ my_list|random }}"
tags: random