映射嵌套结构的属性

Mapping an attribute of a nested structure

我有一个网络接口的嵌套结构。

interfaces:
  - name: bond0
    bonding_xmit_hash_policy: layer3+4
    bridge:
      name: br0
      inet: 12.34.56.78/24
    slaves:
      - name: eth0
        mac: xx:xx:xx:xx:xx:xx
      - name: eth1
        mac: xx:xx:xx:xx:xx:xx
      - name: eth2
        mac: xx:xx:xx:xx:xx:xx
      - name: eth3
        mac: xx:xx:xx:xx:xx:xx

我现在想要生成所有已定义接口的平面列表 names:

理论上这应该很简单,但我未能提取从接口的名称 (eth0 - eth3)

以下是工作部分:

  1. 根级别的接口列表(本例中为bond0

    interfaces | map(attribute='name') | list
  2. 网桥接口列表

    interfaces | selectattr('bridge', 'mapping') | map(attribute='bridge') | map(attribute='name') | list

这是我尝试获取所有从属接口名称的尝试:

interfaces | selectattr('slaves') | map(attribute='slaves') | map(attribute='name') | list

换句话说,首先减少接口列表,只获取那些具有slaves属性的接口。然后使用 map 过滤器获取奴隶。直到这里它才有效,如果我输出结果,它看起来像这样:

[
    {
        "mac": "xx:xx:xx:xx:xx:xx", 
        "name": "eth0"
    }, 
    {
        "mac": "xx:xx:xx:xx:xx:xx", 
        "name": "eth1"
    }, 
    {
        "mac": "xx:xx:xx:xx:xx:xx", 
        "name": "eth2"
    }, 
    {
        "mac": "xx:xx:xx:xx:xx:xx", 
        "name": "eth3"
    }
]

这显然是一个对象列表。这实际上与结构根级别的接口格式相同 (bond0)。但是当我再次尝试获取所有具有 map 的对象的 name 属性时,它会失败,结果是 [Undefined]。 (注意[]。好像是一个未定义的列表,不是简单的undefined)

但这正是 map 过滤器应该做的:

Applies a filter on a sequence of objects or looks up an attribute. This is useful when dealing with lists of objects but you are really only interested in a certain value of it.

The basic usage is mapping on an attribute. Imagine you have a list of users but you are only interested in a list of usernames

出于测试目的,我还尝试查看使用 selectattr:

时会发生什么
interfaces | selectattr('slaves') | map(attribute='slaves') | selectattr('name') | list

这应该没什么区别,因为所有对象都有 name 属性,但 Ansible 失败了:

FAILED! => {"failed": true, "msg": "ERROR! 'list object' has no attribute 'name'"}

不知何故,列表中似乎有一个列表,调试任务没有显示它,因为输出似乎是一个对象列表,但从 Jinja 的角度来看,它似乎正在处理一个列表列表?

有谁知道发生了什么以及如何简单地从该列表中获取接口名称?

现在我用自定义过滤器插件解决了这个问题,但我不明白为什么它不能立即使用。如果这个问题仍未解决并且有人遇到同样的问题,这是我的插件:

class FilterModule(object):
    
    def filters(self):
        return {
            'interfaces_flattened': self.interfaces_flattened
        }
    def interfaces_flattened(*args):
        names = []
        for interface in args[1]:
            names.extend(get_names(interface))
        return names

def get_names(interface):
    names = []
    if "name" in interface:
        names.append(interface["name"])
    if "bridge" in interface:
        names.append(interface["bridge"]["name"])
    if "slaves" in interface:
        for slave in interface["slaves"]:
            names.extend(get_names(slave))
    return names

问:"所有已定义接口名称的平面列表。"

蛮力

    - set_fact:
        names: "{{ names|d([]) + [item.split(':')|last|trim] }}"
      loop: "{{ (interfaces|to_nice_yaml).splitlines() }}"
      when: item is match('^.*\s+name:\s+.*$')

给予

  names:
  - br0
  - bond0
  - eth0
  - eth1
  - eth2
  - eth3