Ansible将两个列表转换为扁平化聚合字典的实现方法
Ansible Jinja2 实现指定IP列表到reg分组映射方案
需求说明
重发说明:无法编辑之前的提问,修正后的核心需求为:将指定IP列表转换为键为reg标识、值为对应IP数组的反转映射字典,最终结果仅允许包含IPs列表内指定的IP,不得纳入列表外条目。
输入结构
初始输入数据格式:
IPs: [ 'ip1', 'ip2', 'ip3' ] DICT: { 'ip1': 'reg1', 'ip2': 'reg2', 'ip3': 'reg2', 'ip4': 'reg3', ... }
前期已通过过滤器提取两个简化处理用的列表:
list1: {{ DICT | dict2items() | selectattr('key', 'in', IPs) | list | map(attribute='value') | list }} list2: {{ DICT | dict2items() | selectattr('key', 'in', IPs) | list | map(attribute='key') | list }}
提取后列表值:
list1: [ 'reg1', 'reg2', 'reg2' ] list2: [ 'ip1', 'ip2', 'ip3']
期望输出
两种格式均符合要求:
- 格式1(单IP为字符串、多IP为数组):
{ 'reg1': 'ip1', 'reg2': [ 'ip2', 'ip3' ] }
- 格式2(所有值统一为数组,更推荐):
{ 'reg1': [ 'ip1' ], 'reg2': [ 'ip2', 'ip3' ] }
已尝试方案的问题
- 直接zip转字典:使用
{{ dict(list1 | zip(list2) | list }}转换时,重复reg键会被覆盖,仅保留首个对应IP,输出为{ 'reg1': 'ip1', 'reg2': 'ip2' },无法自动聚合同reg下的多个IP。 - 分组后直接转字典:使用
{{ dict(dict(list2 | zip(list1)) | dict2items() | groupby(attribute='value') | list ) }}时,输出值为完整的key/value字典项列表,未提取纯IP值,错误输出如下:
{ 'reg2': [{'key': 'ip2', 'value': 'reg2'}, {'key': 'ip3', 'value': 'reg2'}], 'reg1': [{'key': 'ip1', 'value': 'reg1'}] }
验证场景
测试输入
IPs: [ 'ip1', 'ip2' ] DICT: { 'ip1': 'reg1', 'ip2': 'reg2', 'ip3': 'reg2', 'ip4': 'reg3', ... }
期望输出
{ 'reg1': [ 'ip1' ], 'reg2': [ 'ip2' ] }
已有方案缺陷
@Vladimir-Botka 提供的方案逻辑顺序错误,未对IP范围做二次过滤,会直接读取原DICT中同reg下的所有IP,包含不在指定IP列表内的条目,实现代码如下:
arr: "{{ DICT|dict2items|groupby('value') }}" keys: "{{ arr|map('first')|list }}" vals: "{{ arr|map('last')|map('map', attribute='key')|list }}" var1: "{{ dict(keys|zip(vals)) }}" regs: "{{ IPs|map('extract', DICT) }}" var2: "{{ var1|dict2items|selectattr('key','in', regs)|items2dict }}"
该方案在上述验证场景下会错误输出{ 'reg1': [ 'ip1' ], 'reg2': [ 'ip2' , 'ip3' ] },将不在IP列表内的ip3也纳入结果,不符合需求。
正确实现方案
之前方案出错的核心原因是处理顺序颠倒:先对全量DICT按reg分组,再筛选目标reg,会把同reg下不在指定IP列表的条目也带入结果。正确逻辑必须先过滤IP范围,再对筛选后的结果按reg值分组,实现代码如下:
# 输出统一数组格式(推荐,后续遍历、判断逻辑更简单) ip_reg_map: "{{ dict( DICT | dict2items | selectattr('key', 'in', IPs) | groupby('value') | map('zip', ['reg_key', 'ip_list']) | map('map', 'last') | map('zip', [[], ['key']]) | map('map', 'map') | map('map', 'last') ) }}"
如果需要单IP返回字符串、多IP返回数组的格式,在上述结果基础上增加一步转换即可:
ip_reg_map_fmt: "{{ dict( ip_reg_map | dict2items | map(attribute='key') | zip( ip_reg_map | dict2items | map(attribute='value') | map('first' if (item | length == 1) else item) ) ) }}"
结果验证
- 针对第一组输入
IPs: ['ip1','ip2','ip3'],输出为{'reg1': ['ip1'], 'reg2': ['ip2', 'ip3']},符合预期。 - 针对第二组验证输入
IPs: ['ip1','ip2'],输出为{'reg1': ['ip1'], 'reg2': ['ip2']},不会引入不在列表内的ip3,结果正确。
内容的提问来源于stack exchange,提问作者Mister Bullyster
相关产品推荐
相关产品推荐

