使用 Jinja2 模板遍历嵌套列表/字典

使用 Jinja2 模板遍历嵌套列表/字典

我正在尝试通过使用 Ansible 生成 /etc/exports 文件来动态配置系统中的多个 NFS 服务器。我希望能够使用 jinja2 模板来做到这一点。根据我的导出列表,我无法找出 jinja2 模板。

我的 nfs 角色中定义了以下变量:

site_nfs_servers: ['ansibletarget1', 'ansibletarget2']

exports:
  - server: "ansibletarget1"
    shares:
      - path: "/my/first/share/path"
        client: "*"
        options: "rw,sync"
      - path: "/my/second/share/path"
        client: "*"
        options: "rw,sync,root_squash"
  - server: "ansibletarget2"
    shares:
      - path: "/another/shared/path/different/server"
        client: "*"
        options: "ro,sync"

然后我使用以下 ansible play 来生成模板:

- name: Generate the exports file.
  template:
    src: exports.j2
    dest: /etc/exports
    owner: root
    group: root
    mode: '0750'

我的模板目前如下所示:

{% for export in exports %}
{% if ansible_hostname in export.server %}
{% for share in shares %}
{{ share.path }} {{ share.client }} {{ share.options }}
{% endfor %}
{% endif %}
{% endfor %}

我认为我离正确的模板结构还差得很远。究竟如何遍历这个列表?

答案1

创建库存

shell> cat hosts
[site_nfs_servers]
ansibletarget1
ansibletarget2

并把分享进入主机变量

shell> cat host_vars/ansibletarget1.yml 
shares:
  - path: "/my/first/share/path"
    client: "*"
    options: "rw,sync"
  - path: "/my/second/share/path"
    client: "*"
    options: "rw,sync,root_squash"
shell> cat host_vars/ansibletarget2.yml 
shares:
  - path: "/another/shared/path/different/server"
    client: "*"
    options: "ro,sync"

创建简化的角色以进行测试

shell> tree roles/my_nfs_role/
roles/my_nfs_role/
├── tasks
│   └── main.yml
└── templates
    └── exports.j2

2 directories, 2 files
shell> cat roles/my_nfs_role/tasks/main.yml 
- template:
    src: exports.j2
    dest: /etc/exports.test
shell> cat roles/my_nfs_role/templates/exports.j2 
{% for share in shares %}
{{ share.path }} {{ share.client }} {{ share.options }}
{% endfor %}

然后,在剧本中使用库存组和角色

shell> cat playbook.yml
- hosts: site_nfs_servers
  roles:
    - my_nfs_role

运行剧本并创建文件

shell> ansible-playbook -i hosts playbook.yml

PLAY [site_nfs_servers] ************************************************

TASK [my_nfs_role : template] ******************************************
changed: [ansibletarget1]
changed: [ansibletarget2]
 ...
shell> ssh admin@ansibletarget1 cat /etc/exports.test
/my/first/share/path * rw,sync
/my/second/share/path * rw,sync,root_squash

shell> ssh admin@ansibletarget2 cat /etc/exports.test
/another/shared/path/different/server * ro,sync

Ansible 设置示例


如果你想将共享内容保存在一个对象中,请将列表放入组变量。为了简化代码,将列表转换为字典。您可以使用社区.general.groupby_as_dict例如

shell> cat group_vars/all.yml
exports:
  - server: "ansibletarget1"
    shares:
      - path: "/my/first/share/path"
        client: "*"
        options: "rw,sync"
      - path: "/my/second/share/path"
        client: "*"
        options: "rw,sync,root_squash"
  - server: "ansibletarget2"
    shares:
      - path: "/another/shared/path/different/server"
        client: "*"
        options: "ro,sync"

exports_dict: "{{ exports|community.general.groupby_as_dict('server') }}"

给出

  exports_dict:
    ansibletarget1:
      server: ansibletarget1
      shares:
      - client: '*'
        options: rw,sync
        path: /my/first/share/path
      - client: '*'
        options: rw,sync,root_squash
        path: /my/second/share/path
    ansibletarget2:
      server: ansibletarget2
      shares:
      - client: '*'
        options: ro,sync
        path: /another/shared/path/different/server

然后修改模板。这应该会创建与之前相同的文件。

shell> cat roles/my_nfs_role/templates/exports.j2 
{% for share in exports_dict[inventory_hostname]['shares'] %}
{{ share.path }} {{ share.client }} {{ share.options }}
{% endfor %}

答案2

export您在第二个循环中缺少对的引用。

{% for export in exports %}
{% if ansible_hostname in export.server %}
{% for share in export.shares %}
{{ share.path }} {{ share.client }} {{ share.options }}
{% endfor %}
{% endif %}
{% endfor %}

然而,最好在主机变量中定义共享,正如 Vladimir 的回答所示。

相关内容