为了节省一些打字,重复的任务能够写成以下:html
- 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'是查找。 python
- 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' ]
1.5版新功能mysql
假设你有如下变量: git
---
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 }}"
with_file
遍历文件列表的内容, 项目将按顺序设置为每一个文件的内容。 它能够像这样使用:sql
--- - hosts: all tasks: # emit a debug message containing the content of each file. - debug: msg: "{{ item }}" with_file: - first_example_file - second_example_file
假设包含文本“hello”,而包含文本“world”,这将致使:
first_example_filesecond_example_file
TASK [debug msg={{ item }}] ******************************************************
ok: [localhost] => (item=hello) => { "item": "hello", "msg": "hello" } ok: [localhost] => (item=world) => { "item": "world", "msg": "world" }
with_fileglob
匹配单个目录中的全部文件,非递归地匹配模式。 它调用Python的glob库 ,能够这样使用:shell
---
- 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/*"
假设你有如下变量数据被加载到某处:数组
--- 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 }}"
假设你想作一些像循环遍历用户列表,建立它们,并容许他们经过一组SSH密钥登陆。数据结构
怎么可能实现? 假设你有如下定义并经过“vars_files”或“group_vars / all”文件加载: app
--- 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模式正是它最多出现的地方。 dom
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_choice'功能能够随机选择一些东西。 虽然它不是一个负载平衡器(有模块的那些),它能够有点被用做一个穷人的负载平衡器在MacGyver像状况:
- debug: msg: "{{ item }}" with_random_choice: - "go through the door" - "drink from the goblet" - "press the red button" - "do nothing"
所提供的字符串之一将随机选择。
在更基础的层面上,它们能够用来增长混乱和兴奋,以达到另外可预测的自动化环境。
版本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”,它将具备任务的重试次数。
这不是一个循环,可是很接近。 若是要根据与给定条件匹配的第一个文件使用对文件的引用,而且某些文件名由变量名称决定? 是的,你能够这样作:
- 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
有时你可能想执行一个程序,并根据该程序的输出,逐行循环结果。 尽管应该记住,这能够提供一个整洁的方式来实现,但这老是在控制机器上执行,而不是远程机器:
- 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 }}"
1.3版新功能
若是你想循环一个数组,而且还能够在数组中得到数组的数字索引,你也能够这样作。 这是不经常使用的:
- name: indexed loop demo debug: msg: "at array position {{ item.0 }} there is a value {{ item.1 }}" with_indexed_items: - "{{ some_list }}"
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 } ] }
在极少数状况下,您可能有几个列表列表,您只须要迭代全部列表中的每一个项目。 假设一个真正疯狂的假设数据结构:
---- # 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 }}"
在使用register
循环后,放置在变量中的数据结构将包含一个results
属性,该属性是模块中全部响应的列表。
如下是使用with_items
register
的with_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"
若是您但愿循环查看广告资源,或只是其中的一部分,则有多种方法。 可使用常规的with_items
与play_hosts
或groups
变量,以下所示:
# 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
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
由于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
虽然一般不须要,若是您但愿编写本身的方法来循环任意数据结构,则能够阅读开发插件以获取一些启动器信息。 以上每一个功能都实现为可插入的插件,所以有不少实现可供参考。