如何在 Ansible 中遍历深度为 3 的字典元素列表

How to loop over a list of dict elements deep 3 in Ansible

我的 var 文件中有以下变量:

repo_type:
  hosted:
    data:
      - name: hosted_repo1
        online: true
        storage:
          blobstarage: default
          write_policy: allow_once
      - name: hosted_repo2
        online: true
        storage:
          blobstarage: default
          write_policy: allow_once
  proxy:
    data:
      - name: proxy_repo1
        online: true
        storage:
          blobstarage: default
          write_policy: allow_once
      - name: proxy_repo2
        online: true
        storage:
          blobstarage: default
          write_policy: allow_once
  group:
    data:
      - name: group_repo1
        online: true
        storage:
          blobstarage: default
          write_policy: allow_once
      - name: group_repo2
        online: true
        storage:
          blobstarage: default
          write_policy: allow_once

我想配置一个任务来循环(托管、代理和组)和主体数据字典。

这是任务:

- name: Create pypi hosted Repos
  uri:
    url: "{{ nexus_api_scheme }}://{{ nexus_api_hostname }}:{{ nexus_api_port }}\
      {{ nexus_api_context_path }}{{ nexus_rest_api_endpoint }}/repositories/pypi/{{ item.key}}"
    user: "{{ nexus_api_user }}"
    password: "{{ nexus_default_admin_password }}"
    headers:
      accept: "application/json"
      Content-Type: "application/json"
    body_format: json
    method: POST
    force_basic_auth: yes
    validate_certs: "{{ nexus_api_validate_certs }}"
    body: "{{  item  }}"
    status_code: 201
  no_log: no
  with_dict: "{{ repo_type}}"

我尝试了 with_itemswith_dictwith_nested,但没有任何帮助。

The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'data'

如有任何帮助,我们将不胜感激!

如果您的目标是将 data 键的内容作为平面列表循环,您可以这样做:

- debug:
    msg: "repo {{ item.name }} write_policy {{ item.storage.write_policy }}"
  loop_control:
    label: "{{ item.name }}"
  loop: "{{ repo_type | json_query('*.data[]') }}"

它使用 JMESPath 表达式从每个对象中获取 data 键 顶级字典,然后展平生成的嵌套列表。在 换句话说,它将您的原始结构转换为:

- name: hosted_repo1
  online: true
  storage:
    blobstarage: default
    write_policy: allow_once
- name: hosted_repo2
  online: true
  storage:
    blobstarage: default
    write_policy: allow_once
- name: proxy_repo1
  online: true
  storage:
    blobstarage: default
    write_policy: allow_once
- name: proxy_repo2
  online: true
  storage:
    blobstarage: default
    write_policy: allow_once
- name: group_repo1
  online: true
  storage:
    blobstarage: default
    write_policy: allow_once
- name: group_repo2
  online: true
  storage:
    blobstarage: default
    write_policy: allow_once

当 运行 使用您的示例数据时,这会产生输出:

TASK [debug] *********************************************************************************************************
ok: [localhost] => (item=hosted_repo1) => {
    "msg": "repo hosted_repo1 write_policy allow_once"
}
ok: [localhost] => (item=hosted_repo2) => {
    "msg": "repo hosted_repo2 write_policy allow_once"
}
ok: [localhost] => (item=proxy_repo1) => {
    "msg": "repo proxy_repo1 write_policy allow_once"
}
ok: [localhost] => (item=proxy_repo2) => {
    "msg": "repo proxy_repo2 write_policy allow_once"
}
ok: [localhost] => (item=group_repo1) => {
    "msg": "repo group_repo1 write_policy allow_once"
}
ok: [localhost] => (item=group_repo2) => {
    "msg": "repo group_repo2 write_policy allow_once"
}

如果您正在尝试做其他事情,请更新您的问题,以便 它清楚地显示了您对每次迭代的期望值 循环。

正如@larsk 所报告的那样,您实际上没有设法清楚地解释您是如何尝试遍历数据以及您的 api 调用实际期望的内容。

但这次你很幸运,因为我在 Nexus 上搞砸了很多(我认为 I actually recognize those variable names and overall task layout

Nexus 存储库 POST /v1/repositories/pypi/[hosted|proxy|group] API 端点期待对您在 data 中的每个存储库调用一次。要实现您的要求,您需要遍历 repo_type 中的键到 select 适当的端点,然后再次遍历 data 中的每个元素以发送 repo 定义以创建。

这实际上可以在您的循环中组合 dict2itemssubelements 过滤器,如下面的剧本(未直接测试)。

改造的基本情况如下:

  1. 使用 dict2items 将您的字典转换为 key/value 列表,例如(缩短示例)
    - key: hosted
      value:
        data:
          - <repo definition 1>
          - <repo definition 2>
    [...]
    
  2. 使用 subelements 过滤器将每个顶部元素与 value.data 中的每个元素组合,例如:
    - # <- This is the entry for first repo i.e. `item` in your loop
      - key: hosted # <- this is the start of the corresponding top element i.e. `item.0` in your loop
        value:
          data:
            - <repo definition 1>
            - <repo definition 2>
      - <repo definition 1> # <- this is the sub-element i.e. `item.1` in your loop 
    - # <- this is the entry for second repo
      - key: hosted
        value:
          data:
            - <repo definition 1>
            - <repo definition 2>
      - <repo definition 2>
    [...]
    

根据您的一条评论和我的经验,我在我的示例中添加了使用 to_json 过滤器的回购定义的显式 json 序列化。

将它们放在一起得到:

- name: Create pypi hosted Repos
  uri:
    url: "{{ nexus_api_scheme }}://{{ nexus_api_hostname }}:{{ nexus_api_port }}\
      {{ nexus_api_context_path }}{{ nexus_rest_api_endpoint }}/repositories/pypi/{{ item.0.key }}"
    user: "{{ nexus_api_user }}"
    password: "{{ nexus_default_admin_password }}"
    headers:
      accept: "application/json"
      Content-Type: "application/json"
    body_format: json
    method: POST
    force_basic_auth: yes
    validate_certs: "{{ nexus_api_validate_certs }}"
    body: "{{ item.1 | to_json }}"
    status_code: 201
  no_log: no
  loop: "{{ repo_type | dict2items | subelements('value.data') }}"