概述

        Ansible 提供loopwith_<lookup>until关键字来多次执行任务。重复轮询步骤直到达到特定结果,达到简化playbook的目的。比如创建多个用户,修改多个文件、目录的权限。

        loop在ansible2.5被引入。大多数情况下建议使loop,但是目前loop还不能完全替代with_*。

loopup、with_*

lookup和with_*的比较

  • with_<lookup>的<lookup>填什么内容?取决于你要是用什么lookup plugins,其实这里的<lookup>是填写插件名称。例如with_items,items就是一个lookup plugin。可以用以下命令查找可用插件和插件相关文档:

                查看可用lookup plugins列表:ansible-doc -t lookup -l

                查看lookup plugins帮助文档:ansible-doc -t lookup {lookup plugin name}

  • loop等同于with_list。是简单循环的最佳选择。
  • loop只接受列表的输入,不支持字符串的输入。所以需要确保使用query作为输入,而不是lookup。

                query是ansible2.5引入的新的jinja2函数,该函数始终返回一个列表。

                默认情况下lookup返回一个字符串,可以使用wantlist=True强制lookup返回列表。

#loop输入列表示例

loop: "{{ query('inventory_hostnames', 'all') }}"

loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
  • 此文档介绍了,可转换为loop的with_*使用方法:Migrating from with_X to loop
  • with_items更改为loop时需要注意。因为with_items执行了隐式单级展平。您可能需要使用flatten过滤去来匹配确切结果。
  • 任何需要在loop中使用lookup的with_*语句,都不应该使用wantlist=True转换为loop关键字(直接使用query)。例如不要像如下这么做:
loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"

标准循环

简单列表遍历

重复的任务可以写成一个简单的字符串列表上的标准循环。比如创建用户的两种写法,前者是不用循环,后者是使用循环:

- name: Add user testuser1
  ansible.builtin.user:
    name: "testuser1"
    state: present
    groups: "wheel"

- name: Add user testuser2
  ansible.builtin.user:
    name: "testuser2"
    state: present
    groups: "wheel"
- name: Add several users
  ansible.builtin.user:
    name: "{{ item }}"
    state: present
    groups: "wheel"
  loop:
     - testuser1
     - testuser2

遍历哈希列表

- name: Add several users
  ansible.builtin.user:
    name: "{{ item.name }}"
    state: present
    groups: "{{ item.groups }}"
  loop:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

遍历字典

- name: Using dict2items
  ansible.builtin.debug:
    msg: "{{ item.key }} - {{ item.value }}"
  loop: "{{ tag_data | dict2items }}"
  vars:
    tag_data:
      Environment: dev
      Application: payment

使用循环注册变量

当在循环时使用register注册变量时,变量的数据结构会和非循环时的不同。循环时变量的数据结构会包含一个results列表。如下:

- name: Register loop output as a variable
  ansible.builtin.shell: "echo {{ item }}"
  loop:
    - "one"
    - "two"
  register: echo
{
    "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"
        }
    ]
}

until-重试任务直到满足条件

- name: Retry a task until a certain condition is met
  ansible.builtin.shell: /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") != -1
  retries: 5
  delay: 10

此任务最多运行 5 次,每次尝试之间有 10 秒的延迟。如果任何尝试的结果在其标准输出中显示“all systems go”,则任务成功。“重试次数”的默认值为 3,“延迟”的默认值为 5。

当您运行任务until并将结果注册为变量时,注册的变量将包含一个名为“attempts”的键,它记录了该任务的重试次数。