Ansible Playbook 循環


Standard Loops

為了節省一些打字,重復的任務可以寫成如下:

- name: add several users user: name: "{{ item }}" state: present groups: "wheel" with_items: - testuser1 - testuser2

如果您在變量文件或“vars”部分中定義了YAML列表,則還可以執行以下操作:
with_items: "{{ 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模塊使用with_item來執行較少的包管理器事務。
請注意,使用“with_items”迭代的項目類型不必是簡單的字符串列表。 如果你有一個哈希列表,你可以使用以下的東西來引用子項:
- name: add several users user: name: "{{ item.name }}" state: present groups: "{{ item.groups }}" with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' }

還要注意,當與with_items (或任何其他循環語句)組合 ,對每個項目單獨處理when語句。 請參閱When語句為例。

循環實際上是with_ + lookup()的組合,所以任何查找插件都可以用作循環的源,'items'是查找。

Nested Loops(嵌套循環)

- name: give users access to multiple databases mysql_user: name: "{{ item[0] }}" priv: "{{ item[1] }}.*:ALL" append_privs: yes password: "foo" with_nested: - [ 'alice', 'bob' ] - [ 'clientdb', 'employeedb', 'providerdb' ]

與上述'with_items'一樣,您可以使用以前定義的變量:
- name: here, 'users' contains the above list of employees mysql_user: name: "{{ item[0] }}" priv: "{{ item[1] }}.*:ALL" append_privs: yes password: "foo" with_nested: - "{{ users }}" - [ 'clientdb', 'employeedb', 'providerdb' ]

Looping over Hashes

1.5版新功能

假設你有以下變量:

---
users: alice: name: Alice Appleworth telephone: 123-456-7890 bob: name: Bob Bananarama telephone: 987-654-3210
並且您想打印每個用戶的名字和電話號碼。 您可以使用with_dict循環遍歷哈希的元素,如下所示:
tasks: - name: Print phone records debug: msg: "User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})" with_dict: "{{ users }}"

Looping over Files

with_file遍歷文件列表的內容, 項目將按順序設置為每個文件的內容。 它可以像這樣使用:

---
- hosts: all tasks: # emit a debug message containing the content of each file. - debug: msg: "{{ item }}" with_file: - first_example_file - second_example_file

假設first_example_file包含文本“hello”,而second_example_file包含文本“world”,這將導致:

TASK [debug msg={{ item }}] ******************************************************
ok: [localhost] => (item=hello) => {  "item": "hello",  "msg": "hello" } ok: [localhost] => (item=world) => {  "item": "world",  "msg": "world" }

Looping over Fileglobs

with_fileglob匹配單個目錄中的所有文件,非遞歸地匹配模式。 它調用Python的glob庫 ,可以這樣使用:

---
- hosts: all tasks: # first ensure our target directory exists - name: Ensure target directory exists file: dest: "/etc/fooapp" state: directory # copy each file over that matches the given pattern - name: Copy each file over that matches the given pattern copy: src: "{{ item }}" dest: "/etc/fooapp/" owner: "root" mode: 0600 with_fileglob: - "/playbooks/files/fooapp/*"

Looping over Parallel Sets of Data

假設你有以下變量數據被加載到某處:

---
alpha: [ 'a', 'b', 'c', 'd' ] numbers: [ 1, 2, 3, 4 ]

你想要一套“(a,1)”和“(b,2)”等等。 使用'with_together'得到這個:
tasks: - debug: msg: "{{ item.0 }} and {{ item.1 }}" with_together: - "{{ alpha }}" - "{{ numbers }}"

Looping over Subelements

假設你想做一些像循環遍歷用戶列表,創建它們,並允許他們通過一組SSH密鑰登錄。

怎么可能實現? 假設你有以下定義並通過“vars_files”或“group_vars / all”文件加載:

---
users: - name: alice authorized: - /tmp/alice/onekey.pub - /tmp/alice/twokey.pub mysql: password: mysql-password hosts: - "%" - "127.0.0.1" - "::1" - "localhost" privs: - "*.*:SELECT" - "DB1.*:ALL" - name: bob authorized: - /tmp/bob/id_rsa.pub mysql: password: other-mysql-password hosts: - "db1" privs: - "*.*:SELECT" - "DB2.*:ALL"

可能會發生如此:

- name: Create User user: name: "{{ item.name }}" state: present generate_ssh_key: yes with_items: - "{{ users }}" - name: Set authorized ssh key authorized_key: user: "{{ item.0.name }}" key: "{{ lookup('file', item.1) }}" with_subelements: - "{{ users }}" - authorized

給出mysql主機和privs子項列表,還可以遍歷一個嵌套子項中的列表:
- name: Setup MySQL users mysql_user: name: "{{ item.0.name }}" password: "{{ item.0.mysql.password }}" host: "{{ item.1 }}" priv: "{{ item.0.mysql.privs | join('/') }}" with_subelements: - "{{ users }}" - "{{ mysql.hosts }}"

或者,您可以將第三個元素添加到子元素列表,該列表包含標志的字典。 目前您可以添加'skip_missing'標志。 如果設置為True,查找插件將跳過不包含給定子項的列表項。 沒有這個標志,或者如果該標志設置為False,插件將產生一個錯誤,並抱怨丟失的子項。

authorized_key模式正是它最多出現的地方。

Looping over Integer Sequences

with_sequence生成一個項目序列。 您可以指定起始值,結束值,可選的“stride”值,該值指定增加序列的步數,以及可選的printf樣式格式字符串。

參數應指定為key = value對字符串。

參數string的簡單快捷方式也被接受: [start-]end[/stride][:format]

數值可以十進制,十六進制(0x3f8)或八進制(0600)指定。 不支持負數。 這樣做的工作如下:

---
- hosts: all tasks: # create groups - group: name: "evens" state: present - group: name: "odds" state: present # create some test users - user: name: "{{ item }}" state: present groups: "evens" with_sequence: start=0 end=32 format=testuser%02x # create a series of directories with even numbers for some reason - file: dest: "/var/stuff/{{ item }}" state: directory with_sequence: start=4 end=16 stride=2 # a simpler way to use the sequence plugin # create 4 groups - group: name: "group{{ item }}" state: present with_sequence: count=4

Random Choices

'random_choice'功能可以隨機選擇一些東西。 雖然它不是一個負載平衡器(有模塊的那些),它可以有點被用作一個窮人的負載平衡器在MacGyver像情況:
- debug: msg: "{{ item }}" with_random_choice: - "go through the door" - "drink from the goblet" - "press the red button" - "do nothing"
所提供的字符串之一將隨機選擇。

在更基礎的層面上,它們可以用來增加混亂和興奮,以達到另外可預測的自動化環境。

Do-Until Loops

版本1.4中的新功能。

有時你想重試一個任務,直到滿足一定的條件。 這里有一個例子:

- shell: /usr/bin/foo register: result until: result.stdout.find("all systems go") != -1 retries: 5 delay: 10

上述示例遞歸運行shell模塊,直到模塊的結果在其stdout中具有“所有系統”,或者任務已經重試了5次,延遲了10秒。 “重試”的默認值為3,“延遲”為5。

任務返回最后一個任務運行返回的結果。 單獨重試的結果可以通過-vv選項查看。 注冊的變量還將有一個新的密鑰“attempts”,它將具有任務的重試次數。

Finding First Matched Files

這不是一個循環,但是很接近。 如果要根據與給定條件匹配的第一個文件使用對文件的引用,並且某些文件名由變量名稱決定? 是的,你可以這樣做:

- name: INTERFACES | Create Ansible header for /etc/network/interfaces template: src: "{{ item }}" dest: "/etc/foo.conf" with_first_found: - "{{ ansible_virtualization_type }}_foo.conf" - "default_foo.conf"


此工具還具有允許可配置搜索路徑的長格式版本。 這里有一個例子:
- name: some configuration template template: src: "{{ item }}" dest: "/etc/file.cfg" mode: 0444 owner: "root" group: "root" with_first_found: - files: - "{{ inventory_hostname }}/etc/file.cfg" paths: - ../../../templates.overwrites - ../../../templates - files: - etc/file.cfg paths: - templates

Iterating Over The Results of a Program Execution

有時你可能想執行一個程序,並根據該程序的輸出,逐行循環結果。 盡管應該記住,這可以提供一個整潔的方式來實現,但這總是在控制機器上執行,而不是遠程機器:

- name: Example of looping over a command result shell: "/usr/bin/frobnicate {{ item }}" with_lines: - "/usr/bin/frobnications_per_host --param {{ inventory_hostname }}"

好的,那有點隨意 實際上,如果您正在做一些與庫存相關的內容,您可能只想編寫一個動態庫存源代碼(請參閱動態庫存 ),但這對於快速和臟的實現可能會偶爾有用。

如果您需要遠程執行命令,則不能使用上述方法。 這樣做:

- name: Example of looping over a REMOTE command result shell: "/usr/bin/something" register: command_result - name: Do something with each result shell: "/usr/bin/something_else --param {{ item }}" with_items: - "{{ command_result.stdout_lines }}"

Looping Over A List With An Index

1.3版新功能

如果你想循環一個數組,並且還可以在數組中獲得數組的數字索引,你也可以這樣做。 這是不常用的:

- name: indexed loop demo debug: msg: "at array position {{ item.0 }} there is a value {{ item.1 }}" with_indexed_items: - "{{ some_list }}"

Using ini file with a loop

2.0版新功能

ini插件可以使用regexp來檢索一組鍵。 因此,我們可以循環這個集合。 這是我們將使用的ini文件:

[section1]
value1=section1/value1 value2=section1/value2 [section2] value1=section2/value1 value2=section2/value2

以下是使用with_ini的示例:

- debug: msg: "{{ item }}" with_ini: - value[1-2] - section: section1 - file: "lookup.ini" - re: true

這里是返回的值:

{
      "changed": false, "msg": "All items completed", "results": [ { "invocation": { "module_args": "msg=\"section1/value1\"", "module_name": "debug" }, "item": "section1/value1", "msg": "section1/value1", "verbose_always": true }, { "invocation": { "module_args": "msg=\"section1/value2\"", "module_name": "debug" }, "item": "section1/value2", "msg": "section1/value2", "verbose_always": true } ] }

Flattening A List(展開列表)

在極少數情況下,您可能有幾個列表列表,您只需要迭代所有列表中的每個項目。 假設一個真正瘋狂的假設數據結構:

----
# file: roles/foo/vars/main.yml packages_base: - [ 'foo-package', 'bar-package' ] packages_apps: - [ ['one-package', 'two-package' ]] - [ ['red-package'], ['blue-package']]

您可以看到這些列表中的軟件包的格式是完整的。 我們如何在兩個列表中安裝所有的包:
- name: flattened loop demo yum: name: "{{ item }}" state: present with_flattened: - "{{ packages_base }}" - "{{ packages_apps }}"

Using register with a loop

在使用register循環后,放置在變量中的數據結構將包含一個results屬性,該屬性是模塊中所有響應的列表。

以下是使用with_items registerwith_items

- shell: "echo {{ item }}" with_items: - "one" - "two" register: echo

這與使用沒有循環的register時返回的數據結構不同:

{
    "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 with_items: "{{ echo.results }}"

在迭代過程中,當前項目的結果將被放在變量中:
- shell: echo "{{ item }}" with_items: - one - two register: echo changed_when: echo.stdout != "one"

Looping over the inventory

如果您希望循環查看廣告資源,或只是其中的一部分,則有多種方法。 可以使用常規的with_itemsplay_hostsgroups變量,如下所示:

# show all the hosts in the inventory
- debug: msg: "{{ item }}" with_items: - "{{ groups['all'] }}" # show all the hosts in the current play - debug: msg: "{{ item }}" with_items: - "{{ play_hosts }}"

還有一個特定的查找插件inventory_hostnames可以這樣使用:
# show all the hosts in the inventory
- debug: msg: "{{ item }}" with_inventory_hostnames: - all # show all the hosts matching the pattern, ie all but the group www - debug: msg: "{{ item }}" with_inventory_hostnames: - all:!www

Loop Control

2.1版新功能

在2.0中,您再次可以使用with_循環和任務包括(但不包括playbook)。 這增加了在一次鏡像中循環該任務集的能力。 默認情況下可以設置每個循環的循環變量 ,這會導致這些嵌套循環從“外部”循環覆蓋項的值。 從Ansible 2.1開始, loop_control選項可用於指定要用於循環的變量的名稱:

# main.yml
- include: inner.yml with_items: - 1 - 2 - 3 loop_control: loop_var: outer_item # inner.yml - debug: msg: "outer item={{ outer_item }} inner item={{ item }}" with_items: - a - b - c

新版本2.2。

當使用復雜的數據結構來循環顯示可能會有點太忙,這就是C(label)指令來幫助的:

- name: create servers digital_ocean: name: "{{ item.name }}" state: present with_items: - name: server1 disks: 3gb ram: 15Gb network: nic01: 100Gb nic02: 10Gb ... loop_control: label: "{{item.name}}"

現在,它將只顯示“label”字段而不是每個“item”的整個結構,它默認為“{{item}}”'以照常顯示。

新版本2.2。

循環控制的另一個選項是C(暫停),它允許您控制任務循環中項目執行之間的時間(以秒為單位):

# main.yml
- name: create servers, pause 3s before creating next digital_ocean: name: "{{ item }}" state: present with_items: - server1 - server2 loop_control: pause: 3

Loops and Includes in 2.0

因為loop_control在Ansible 2.0中不可用,當使用帶循環的include時,應該使用set_fact來保存項的“outer”循環值:

# main.yml
- include: inner.yml with_items: - 1 - 2 - 3 # inner.yml - set_fact: outer_item: "{{ item }}" - debug: msg: "outer item={{ outer_item }} inner item={{ item }}" with_items: - a - b - c

Writing Your Own Iterators

雖然通常不需要,如果您希望編寫自己的方法來循環任意數據結構,則可以閱讀開發插件以獲取一些啟動器信息。 以上每個功能都實現為可插入的插件,因此有很多實現可供參考。

 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM