我在 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 中迭代子元素的子元素?
有点牵强,但以下剧本将实现您的目标:
---
- 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
虽然 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