![使用 Jinja2 模板遍历嵌套列表/字典](https://linux22.com/image/774642/%E4%BD%BF%E7%94%A8%20Jinja2%20%E6%A8%A1%E6%9D%BF%E9%81%8D%E5%8E%86%E5%B5%8C%E5%A5%97%E5%88%97%E8%A1%A8%2F%E5%AD%97%E5%85%B8.png)
我正在尝试通过使用 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
如果你想将共享内容保存在一个对象中,请将列表放入组变量。为了简化代码,将列表转换为字典。您可以使用社区.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 的回答所示。