【问题标题】:Nested loops using list and dict in ansible在ansible中使用list和dict的嵌套循环
【发布时间】:2020-03-09 04:58:51
【问题描述】:

我在 ansible 中有 2 种类型的输入数据。 首先是字符串列表:

- '1.1.1.1'
- '2.2.2.2'

其次是字典列表。

- {'name': 'obj1', 'addr': '1.1.1.1'}
- {'name': 'objx', 'addr': 'x.x.x.x'}

我事先不知道第一个列表的项目是否在第二个列表的地址字段中。因此我必须执行评估,为此我必须使用嵌套循环。我需要遍历第一个列表并遍历第二个列表,然后执行条件检查第一个列表的项目是否等于第二个列表的 item.addr。但是我不知道如何在 ansible 条件下区分第一个列表的项目和第二个列表的项目。

在 python 中,我可以通过使用以下表达式来实现类似的效果:

for add in my_list: 
    for obj in my_list2: 
        if add == obj['addr']: 
            new_list.append([obj])

在ansible中应该是这样的:

- set_fact:
    new_list: "{{ new_list }} + [ {'name': '{{ item_second_list.name }}', 'address': '{{ item_second_list.addr }}'} ]"
  when: item_first_list == item_second_list.addr
  with_list: first_list
  with_list: second_list

【问题讨论】:

    标签: python loops ansible conditional-statements


    【解决方案1】:

    您可以尝试使用 with_nested。

    下面是一个例子..(不是工作代码,仅供参考)

      vars:
         first_list:
         - '1.1.1.1'
         - '2.2.2.2'
        second_list:
         - {'name': 'obj1', 'addr': '1.1.1.1'}
         - {'name': 'objx', 'addr': '2.3.4.5'}
    
    - set_fact:
        new_list: " {{new_list|default([])}} + {{ item.1}} "
      when: item.0 == item.1.addr
      with_nested:
        - "{{first_list}}"
        - "{{second_list}}"
    

    参考:https://docs.ansible.com/ansible/latest/plugins/lookup/nested.html

    输出:

    ok: [localhost] => (item=[u'1.1.1.1', {u'name': u'obj1', u'addr': u'1.1.1.1'}]) => {
        "ansible_facts": {
            "new_list": " [] + {u'name': u'obj1', u'addr': u'1.1.1.1'} "
        },
    

    【讨论】:

    • 我认为这行不通:不能保证两个列表的长度相同。
    • 在链接中提供的文档示例中,列表的长度不同
    • 测试和粘贴的输出
    【解决方案2】:

    在 YAML 字典中有两个相同的键是错误的。换句话说,您不能像这样使用with_list 两次:它应该 生成错误,否则,除了最后一个错误之外的所有内容都将被忽略。在大多数情况下,Ansible 不直接支持嵌套循环。

    您似乎想要生成包含在字典列表中的地址列表。也就是说,给定:

    list1:
      - 1.1.1.1
      - 2.2.2.2
      - 3.3.3.3
    
    list2:
      - {'name': 'obj1', 'addr': '1.1.1.1'}
      - {'name': 'objx', 'addr': '2.2.2.2'}
    

    您想生成一个新列表:

    list3:
      - 1.1.1.1
      - 2.2.2.2
    

    我们可以在没有嵌套列表的情况下做到这一点,方法是询问 list2 中的所有地址,然后只选择 in list1 中的地址:

    ---
    - hosts: localhost
      gather_facts: false
      vars:
        list1:
          - 1.1.1.1
          - 2.2.2.2
          - 3.3.3.3
    
        list2:
          - name: obj1
            addr: 1.1.1.1
          - name: obj2
            addr: 2.2.2.2
    
      tasks:
        - set_fact:
            list3: "{{ (list3|default([])) + [item.addr] }}"
          when: item.addr in list1
          loop: "{{ list2 }}"
    
        - debug:
            var: list3
    

    这将输出:

    [...]
    
    TASK [debug] *************************************************************************
    ok: [localhost] => {
        "list3": [
            "1.1.1.1",
            "2.2.2.2"
        ]
    }
    
    

    【讨论】:

    • 这个解决方案非常适合我。我想反之亦然,但这种结构是有道理的。我的意思是检查列表中是否存在元素比循环遍历 2 个列表要容易得多。谢谢!
    【解决方案3】:

    下面的戏

      vars:
        my_list:
          - '1.1.1.1'
          - '2.2.2.2'
        my_dict:
          - {'name': 'obj1', 'addr': '1.1.1.1'}
          - {'name': 'objx', 'addr': 'x.x.x.x'}
    
      tasks:
        - set_fact:
            sel_list: "{{ sel_list|default([]) +
                          my_dict|
                          selectattr('addr', 'in', my_list)|
                          list }}"
        - debug:
            var: sel_list
    

    给予

    "sel_list": [
        {
            "addr": "1.1.1.1", 
            "name": "obj1"
        }
    ]
    

    【讨论】:

    • 这个方法也有效。这是一个有点复杂的过滤器表达式。谢谢分享。
    • 是的。复杂性立即清晰my_dict|selectattr('addr', 'in', my_list)
    猜你喜欢
    • 1970-01-01
    • 2020-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    相关资源
    最近更新 更多