用字典迭代字典列表

时间:2020-12-20 11:20:45

标签: ansible

我无法掌握 Ansible 中的数据操作。设置如下:

- hosts: emirr01
  gather_facts: no
  vars:
    setup:
      - emirr01: { label: "label1" }
      - emirr02: { label: "label2" }
      - emirr03: { label: "label3" }
    lookup: [ "emirr01", "emirr02"]
    use_labels: [ ]
  tasks:
    - debug: msg="setup={{ setup }}"
    - debug: msg="lookup={{ lookup }}"

    - debug: msg="item0={{ item.0 }} item1={{ item.1 }}"
      when: inventory_hostname == item.1
      with_nested:
        - "{{ setup }}"
        - "{{ lookup }}"

    - set_fact:
        use_labels: "{{ use_labels + [ item.1.label ] }}"
      when: item.0 == item.1.label
      with_nested:
        - "{{ setup }}"
        - "{{ lookup }}"

    - debug: msg="use_labels={{ use_labels }}"

我需要的是为 use_labels 列表中找到的每个主机设置一个事实 setup,它是 lookup 列表中定义的标签列表。我期望的是这样的列表:

[ "label1", "label2" ]

我的问题是无法“到达”我在 item.1 中获得的列表中的标签,这是(示例):

item1=[{'emirr02': {'label': 'label2'}}

这里是错误:

$ ansible-playbook test.yml -l emirr01

PLAY [emirr01] ****************************************************************************************************************************************************************************************************************************************************************************************************************************

TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] =>
  msg: 'setup=[{''emirr01'': {''label'': ''label1''}}, {''emirr02'': {''label'': ''label2''}}, {''emirr03'': {''label'': ''label3''}}]'

TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] =>
  msg: lookup=['emirr01', 'emirr02']

TASK [debug] ******************************************************************************************************************************************************************************************************************************************************************************************************************************
ok: [emirr01] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr01']) =>
  msg: 'item0={''emirr01'': {''label'': ''label1''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr02'])
ok: [emirr01] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr01']) =>
  msg: 'item0={''emirr02'': {''label'': ''label2''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr02'])
ok: [emirr01] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr01']) =>
  msg: 'item0={''emirr03'': {''label'': ''label3''}} item1=emirr01'
skipping: [emirr01] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr02'])

TASK [set_fact] ***************************************************************************************************************************************************************************************************************************************************************************************************************************
fatal: [emirr01]: FAILED! =>
  msg: |-
    The conditional check 'item.0 == item.1.label' failed. The error was: error while evaluating conditional (item.0 == item.1.label): 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'label'

    The error appears to be in '/home/ansible/test.yml': line 21, column 7, but may
    be elsewhere in the file depending on the exact syntax problem.

    The offending line appears to be:


        - set_fact:
          ^ here

这一切都归结为从列表中的字典中“挖掘”一个值。有人知道如何到达item.1.label吗?这有点令人沮丧,因为这在 Python 或其他编程语言中是微不足道的,但在 Ansible 中……或者我的错误是为此目的使用了错误类型的 with_* 循环?

2 个答案:

答案 0 :(得分:2)

问:"期待这样的列表 [label1, label2]"

    setup:
      - emirr01: { label: "label1" }
      - emirr02: { label: "label2" }
      - emirr03: { label: "label3" }
    lookup: [ "emirr01", "emirr02"]

A:这是此用例错误数据结构的典型示例。不能在 mapping 过滤器 extract 中使用字典列表。此外,字典中的键不必是唯一的,因为它们隐藏在列表的项目中。解决方法很简单,用字典中的数据

    setup:
      emirr01: { label: "label1" }
      emirr02: { label: "label2" }
      emirr03: { label: "label3" }
    - set_fact:
        use_labels: "{{ lookup|map('extract', setup, 'label')|list }}"

给予

  use_labels:
  - label1
  - label2

其中一个选项是首先从列表中创建一个字典。例如

    - set_fact:
        setup2: "{{ setup2|default({})|combine(item) }}"
      loop: "{{ setup }}"

那么可以使用字典setup2得到相同的结果


可以直接过滤列表。例如,下面的任务也给出了相同的结果

    - set_fact:
        use_labels: "{{ setup|map('dict2items')|map('first')|
                        selectattr('key', 'in', lookup )|
                        map(attribute='value.label')|
                        list }}"

答案 1 :(得分:1)

您对 with_nested 的问题实际上来自于那些“双列表”循环的工作方式。

当您使用像 with_nested 这样的循环时,item.0 是第一个列表的项目,而 item.1 是第二个列表的项目。

因为 lookup 是一个列表,而不是像

这样的字典列表
lookup:
  - label: "emirr01"
  - label: "emirr02"

您可以立即使用 item.1 的值。

所以解决问题的方法是这样使用它:

- set_fact:
    use_labels: "{{ use_labels + [ item.0[item.1].label ] }}"
  when: item.0[item.1].label is defined
  with_nested:
    - "{{ setup }}"
    - "{{ lookup }}"

这是一个演示此操作的手册:

- hosts: localhost
  gather_facts: no
      
  tasks:  
    - set_fact:
        use_labels: "{{ use_labels | default([]) + [ item.0[item.1].label ] }}"
      when: item.0[item.1].label is defined
      with_nested:
        - "{{ setup }}"
        - "{{ lookup }}"
      vars:
        setup:
          - emirr01: 
              label: "label1"
          - emirr02: 
              label: "label2"
          - emirr03: 
              label: "label3"
        lookup: 
          - "emirr01"
          - "emirr02"

    - debug:
        msg: "{{ use_labels }}"

Wich 给出:

PLAY [localhost] ***************************************************************************************************

TASK [set_fact] ****************************************************************************************************
ok: [localhost] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr01'])
skipping: [localhost] => (item=[{'emirr01': {'label': 'label1'}}, 'emirr02']) 
skipping: [localhost] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr01']) 
ok: [localhost] => (item=[{'emirr02': {'label': 'label2'}}, 'emirr02'])
skipping: [localhost] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr01']) 
skipping: [localhost] => (item=[{'emirr03': {'label': 'label3'}}, 'emirr02']) 

TASK [debug] *******************************************************************************************************
ok: [localhost] => {
    "msg": [
        "label1",
        "label2"
    ]
}

PLAY RECAP *********************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0