循环子元素的子元素

问题描述 投票:0回答:2

我在 Ansible 中使用以下数据结构:

datacenters:
  - name: Datacenter1
    clusters:
      - name: ClusterA
        hosts:
          - 192.168.0.1
          - 192.168.0.2
      - name: ClusterB
        hosts:
          - 192.168.1.1
          - 192.168.1.2
  - name: Datacenter2
    clusters:
      - name: ClusterC
        hosts:
          - 192.168.2.1
          - 192.168.2.2

在任务中,我想迭代每个host,同时访问所有父层的数据。如果只有一层嵌套,可以使用

subelements
过滤器轻松完成:

loop: '{{ datacenters | subelements(''clusters'') }}'

这将使我能够访问如下数据:

'Datacenter: {{ item.0.name }}, Cluster: {{ item.1.name }}'

我希望能够像这样扩展这个概念:

loop: '{{ datacenters | subelements(''clusters'') | subelements(''hosts'') }}'

并且能够像这样访问数据:

'Datacenter: {{ item.0.name }}, Cluster: {{ item.1.name }}, Host: {{ item.2 }}'

但这不起作用,我收到以下错误消息:

Unexpected templating type error occurred on ({{ datacenters | subelements('clusters') | subelements('hosts') }}): the key hosts should point to a dictionary, got ...(the result of the first layer)

我发现这个问题,它解决了类似的问题,但依赖于在所有嵌套级别上具有不同的字典键,但我没有,因为数据中心和集群具有相同的

name
键。

那么,如何在 Ansible 中迭代子元素的子元素?

loops ansible nested nested-lists ansible-2.x
2个回答
3
投票

有点牵强,但以下剧本将实现您的目标:

 ---
 - hosts: localhost
   gather_facts: false
 
   vars:
     datacenters:
     - name: Datacenter1
       clusters:
         - name: ClusterA
           hosts:
             - 192.168.0.1
             - 192.168.0.2
         - name: ClusterB
           hosts:
             - 192.168.1.1
             - 192.168.1.2
     - name: Datacenter2
       clusters:
         - name: ClusterC
           hosts:
             - 192.168.2.1
             - 192.168.2.2
 
     # Get the list of datacenters
     _dcs: "{{ datacenters | map(attribute='name') }}"
     # Get the corresponding list of clusters with subelements on hosts
     _clusters: "{{ datacenters | map(attribute='clusters') | map('subelements', 'hosts') }}"
     # Recreate a list with the sublisted hosts per clusters and create subelements on that final result
     _overall: "{{ dict(_dcs | zip(_clusters)) | dict2items(key_name='datacenter', value_name='clusters') | subelements('clusters') }}"
 
   tasks:
     - name: Loop on the result
       debug:
         msg:
           - "DC: {{ item.0.datacenter }}"
           - "Cluster: {{ item.1.0.name }}"
           - "Host: {{ item.1.1 }}"
       loop: "{{ _overall }}"
       loop_control:
         label: "{{ item.0.datacenter }} - {{ item.1.0.name }}"

这给出:

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

TASK [Loop on the result] *****************************************************************************************************************************************
ok: [localhost] => (item=Datacenter1 - ClusterA) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterA",
        "Host: 192.168.0.1"
    ]
}
ok: [localhost] => (item=Datacenter1 - ClusterA) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterA",
        "Host: 192.168.0.2"
    ]
}
ok: [localhost] => (item=Datacenter1 - ClusterB) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterB",
        "Host: 192.168.1.1"
    ]
}
ok: [localhost] => (item=Datacenter1 - ClusterB) => {
    "msg": [
        "DC: Datacenter1",
        "Cluster: ClusterB",
        "Host: 192.168.1.2"
    ]
}
ok: [localhost] => (item=Datacenter2 - ClusterC) => {
    "msg": [
        "DC: Datacenter2",
        "Cluster: ClusterC",
        "Host: 192.168.2.1"
    ]
}
ok: [localhost] => (item=Datacenter2 - ClusterC) => {
    "msg": [
        "DC: Datacenter2",
        "Cluster: ClusterC",
        "Host: 192.168.2.2"
    ]
}

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

0
投票

不同的方法

虽然 Zeitounator 已经回答了问题,但我仍然想提出一种不同的方法来解决循环问题。如果嵌套层超过两层或三层,则循环遍历多个嵌套层仍然会变得困难。

解决此问题的另一种方法是使用 ansible.builtin.include_tasks 和每个层的不同任务文件来循环整个数据结构。问题中的数据结构示例:

  • configure-environment.yaml
    :

    # Configure datacenters
    - name: Configure datacenters
      ansible.builtin.include_tasks: configure-datacenter.yaml
      loop: '{{ datacenters }}'
      loop_control:
        loop_var: datacenter
        label: '{{ datacenter.name }}'
    
  • configure-datacenter.yaml
    :

    # Configure datacenter
    - name: Configure datacenter
      ansible.builtin.debug:
        msg: Configure datacenter {{ datacenter.name }}.
    
    # Configure clusters
    - name: Configure clusters
      ansible.builtin.include_tasks: configure-cluster.yaml
      loop: '{{ datacenter.clusters }}'
      loop_control:
        loop_var: cluster
        label: '{{ cluster.name }}'
    
  • configure-cluster.yaml
    :

    # Configure cluster
    - name: Configure cluster
      ansible.builtin.debug:
        msg: Configure cluster {{ cluster.name }} of datacenter {{ datacenter.name }}.
    
    # Configure hosts
    - name: Configure hosts
      ansible.builtin.debug:
        msg: Configure host {{ my_host }} of cluster {{ cluster.name }} of datacenter {{ datacenter.name }}.
      loop: '{{ cluster.hosts }}'
      loop_control:
        loop_var: my_host
    
© www.soinside.com 2019 - 2024. All rights reserved.