首页 > 解决方案 > 合并多个字典列表

问题描述

我有一个简单的角色,将模板推入/etc/sysconfig/network-scripts/route-<interface>.

我们的一些路线是相同的,所以我想在角色变量和组/主机变量的帮助下分解路线。

我尝试应用此解决方案,但它需要一个 dict 并且我有一个 dict 列表,而且我目前对 jinja 过滤器还不是很满意。

这是我的数据结构:

management_default_gateway: '192.168.128.254'
management_interface: eth1
routes:
  - interface: "{{management_interface}}"
    route_array:
      - gateway: "{{management_default_gateway}}"
        network: 10.0.0.1/24
      - gateway: "{{management_default_gateway}}"
        network: 10.0.0.5/24

它可以工作,即使有其他接口,因为模板循环路由列表(见下文),并按预期为每个接口创建一个文件。

roles/routing/tasks/main.yml

- name: Template
  template:
    src: route.j2
    dest: "{{'-'.join((route_script_file,item.interface))}}"
    owner: root
    group: root
    mode: 0640
  notify: networking restart
  with_list: "{{routes}}"

roles/routing/templates/route.j2

{% for i in item.route_array %}
{{i.network ~ ' via ' ~ i.gateway ~ '\n'}}
{%- endfor %}

我想在角色 vars 文件夹(或group_vars/all)上有“标准”路由,并在组或主机级别添加路由。

所以我试着去: roles/routing/vars/main.yml:

default_routes:
  - interface: "{{management_interface}}"
    route_array:
      - gateway: "{{management_default_gateway}}"
        network: 10.0.0.1/24
      - gateway: "{{management_default_gateway}}"
        network: 10.0.0.5/24

routes_merged: "{{ default_routes + specific_routes }}"

inventories/prod/group_vars/XXX.yml

specific_routes:
  - interface: "{{management_interface}}"
    route_array:
      - gateway: "{{management_default_gateway}}"
        network: 10.8.0.1/24

然后我用调试变量得到了这个结果:

ok: [hostname] => {
    "routes": [
        {
            "interface": "eth1",
            "route_array": [
                {
                    "gateway": "192.168.128.254",
                    "network": "10.0.0.1/24"
                },
                {
                    "gateway": "192.168.128.254",
                    "network": "10.0.0.5/24"
                }
            ]
        },
        {
            "interface": "eth1",
            "route_array": [
                {
                    "gateway": "192.168.128.254",
                    "network": "10.8.0.1/24"
                }
            ]
        }
    ]
}

我想要:

ok: [hostname] => {
    "routes": [
        {
            "interface": "eth1",
            "route_array": [
                {
                    "gateway": "192.168.128.254",
                    "network": "10.0.0.1/24"
                },
                {
                    "gateway": "192.168.128.254",
                    "network": "10.0.0.5/24"
                },
                {
                    "gateway": "192.168.128.254",
                    "network": "10.8.0.1/24"
                }
            ]
        }
    ]
}

我想通过接口合并route_array,以便只有一个由接口生成的模板文件。

标签: ansibleyamljinja2

解决方案


groupby的循环完成了这项工作

- set_fact:
    routes: "{{ default_routes + specific_routes }}"
- set_fact:
    routes2: "{{ routes2|default([]) +
                [{'interface': item.1|map(attribute='interface')|list|first,
                  'route_array': item.1|map(attribute='route_array')|list|flatten}] }}"
  loop: "{{ routes| groupby('interface') }}"
- debug:
    var: routes2

"routes2": [
    {
        "interface": "eth1", 
        "route_array": [
            {
                "gateway": "192.168.128.254", 
                "network": "10.0.0.1/24"
            }, 
            {
                "gateway": "192.168.128.254", 
                "network": "10.0.0.5/24"
            }, 
            {
                "gateway": "192.168.128.254", 
                "network": "10.8.0.1/24"
            }
        ]
    }
]

推荐阅读