如何在Ansible中遍历子元素的子元素并保留所有父层数据?
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
直接嵌套subelements会报错,因为第一次subelements('clusters')返回的是(数据中心对象, 集群对象)的元组,第二次subelements('hosts')会错误地尝试从元组中找hosts键,自然失败。
下面是几种可行的解决方案:
方法一:嵌套loop(最直观)
用两层遍历嵌套,外层处理数据中心→集群,内层处理集群→主机,每个层级的变量都能直接访问:
- name: 遍历所有主机并输出父级信息 debug: msg: "Datacenter: {{ dc.name }}, Cluster: {{ cluster.name }}, Host: {{ host }}" loop: "{{ cluster.hosts }}" loop_control: loop_var: host vars: cluster: "{{ item.1 }}" dc: "{{ item.0 }}" loop: "{{ datacenters | subelements('clusters') }}" loop_control: loop_var: item
或者用with_subelements的更清晰写法(Ansible 2.5+支持):
- name: 遍历所有主机并输出父级信息 debug: msg: "Datacenter: {{ dc.name }}, Cluster: {{ cluster.name }}, Host: {{ host }}" loop: "{{ cluster.hosts }}" loop_control: loop_var: host with_subelements: - "{{ datacenters }}" - clusters loop_control: loop_var: cluster parent_var: dc
方法二:subelements结合map重构结构
先把第一次subelements的元组转换成字典,让第二次subelements能正确识别主机列表:
- name: 遍历所有主机并输出父级信息 debug: msg: "Datacenter: {{ item.0.dc.name }}, Cluster: {{ item.0.cluster.name }}, Host: {{ item.1 }}" loop: "{{ datacenters | subelements('clusters') | map('community.general.dict', key=['dc', 'cluster']) | subelements('cluster.hosts') }}"
这里map('community.general.dict', key=['dc', 'cluster'])把(数据中心对象, 集群对象)的元组转成{dc: 数据中心对象, cluster: 集群对象}的字典,第二次subelements就能从cluster.hosts里拿到主机列表。
方法三:用json_query扁平化数据
直接用json_query把所有主机和父级信息提取成扁平化列表,再遍历:
- name: 遍历所有主机并输出父级信息 debug: msg: "Datacenter: {{ item.dc }}, Cluster: {{ item.cluster }}, Host: {{ item.host }}" loop: "{{ datacenters | json_query('[].{dc: name, clusters: clusters[].{cluster: name, hosts: hosts[]}}') | json_query('[].clusters[] | {dc: @.dc, cluster: @.cluster, host: @.hosts}') }}"
两次json_query先提取数据中心和集群的关联信息,再把每个主机展开成独立条目,每个条目包含dc、cluster、host三个字段。
方法四:预生成扁平化列表
先通过set_fact把所有主机的完整信息提前生成一个列表,后续直接遍历这个列表:
- name: 生成包含父级信息的主机列表 set_fact: all_hosts_with_parent: "{{ all_hosts_with_parent | default([]) + [{'dc': dc.name, 'cluster': cluster.name, 'host': host}] }}" loop: "{{ cluster.hosts }}" loop_control: loop_var: host vars: cluster: "{{ item.1 }}" dc: "{{ item.0 }}" loop: "{{ datacenters | subelements('clusters') }}" - name: 遍历预生成的主机列表 debug: msg: "Datacenter: {{ item.dc }}, Cluster: {{ item.cluster }}, Host: {{ item.host }}" loop: "{{ all_hosts_with_parent }}"
内容的提问来源于stack exchange,提问作者stackprotector
相关产品推荐
相关产品推荐

