• ansible playbook loop 翻译


    ansible源文档地址
    有时候你想多次重复一个任务。 在计算机编程中,这叫做循环。 常见的 Ansible 循环包括使用文件模块更改几个文件和 / 或目录的所有权,使用用户模块创建多个用户,并重复一个轮询步骤,直到达到某个结果。 为创建循环提供了两个关键字: loopwith_<lookup>

    注意

    • 我们增加在Ansible2.5版本中中加了loop。它还没有完全取代with_<lookup>, 但我们推荐在大多数场景下使用它。
    • 我们并舍弃with_<lookup>用法--在可预见的未来,这种语法仍然有效。
    • 我们正在寻求改进loop语法,关注这个页面和changelog 来获取更新。

    对比 loop and with_*

    • with_关键字依赖Lookup Plugins插件--尽管item也是一个lookup插件。
    • loop关键字等价于with_list,是使用简单遍历时的最佳选择。
    • loop关键字不再接收一个字符串作为输入,查看 Ensuring list input for loop: query vs. lookup
    • 通常来说,任何包含在 从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') }}"
    

    关于更多信息可以在模式中找到目标主机和组

    使用querylookup确保列表输入

    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跟踪进度

    在 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定义内部和外部变量名

    在 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
    ansible_loop.allitems 循环中所有项的列表
    ansible_loop.index 循环的当前迭代的索引。(索引从1开始)
    ansible_loop.index0 循环的当前迭代的索引。(索引从0开始)
    ansible_loop.revindex 倒序循环的当前迭代的索引。(索引到1结束)
    ansible_loop.revindex0 倒序循环的当前迭代的索引。(索引到0结束)
    ansible_loop.first 如果第一次迭代则为True,否则是False
    ansible_loop.last 如果最后一次迭代则为True,否则是False
    ansible_loop.length 循环中的项数
    ansible_loop.previtem 循环上一次迭代中的项。在第一次迭代中这个变量未定义
    ansible_loop.nextitem 循环下一次迭代中的项。在最后一次迭代中这个变量未定义
    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_listloop替换。

    - name: with_list
      debug:
        msg: "{{ item }}"
      with_list:
        - one
        - two
    
    - name: with_list -> loop
      debug:
        msg: "{{ item }}"
      loop:
        - one
        - two
    

    with_items

    with_itemsloopflatten 过滤器替换。

    - 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_itemsloop, 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_flattenedloopflatten 过滤器替换。

    - name: with_flattened
      debug:
        msg: "{{ item }}"
      with_flattened: "{{ items }}"
    
    - name: with_flattened -> loop
      debug:
        msg: "{{ item }}"
      loop: "{{ items|flatten }}"
    

    with_together

    with_togetherloopzip过滤器替换。

    - 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_dictloopdictsort 或者 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_sequencelooprange 函数, 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_nestedwith_cartesianloopproduct 过滤器替换。

    - 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_choicerandom 过滤器替换,不在需要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
    
  • 相关阅读:
    SQL Server 删除重复数据只保留一条
    英语冠词有哪些?
    英语基本语法
    统一身份认证服务(客户端用户身份验证)
    解决MVC中使用BundleConfig.RegisterBundles引用Css及js文件发布后丢失的问题
    统一身份认证服务 源码(客户端)
    MVC 如何设定默认默认路由为指定的Area下的某个action(笔记)
    MongoDB安装笔记
    消息队列第二篇:MessageQueue实战(课程订单)
    消息队列第一篇:MessageQueue介绍
  • 原文地址:https://www.cnblogs.com/hiyang/p/12775210.html
Copyright © 2020-2023  润新知