在ansible的路径中访问具有未知键的嵌套字典

Access a nested dict with unknown keys in the path in asnible

我遇到了一个问题,我还没有找到解决方案。 我从设备中收集了一些库存数据并得到了这样的数据:

{
    "inventory_items": {
        "main": {
            "chassis": {
                "ASR1001-X": {
                    "descr": "Cisco ASR1001-X Chassis",
                    "name": "Chassis",
                    "pid": "ASR1001-X",
                    "sn": "123456789",
                    "vid": "V02  "
                }
            },
            "non-chassis": {
                "ASR1001-X": {
                    "descr": "Cisco ASR1001-X Route Processor",
                    "name": "module R0",
                    "pid": "ASR1001-X",
                    "sn": "123456789",
                    "vid": "V02  "
                },
                "GLC-SX-MMD": {
                    "descr": "GE SX",
                    "name": "subslot 0/0 transceiver 3",
                    "pid": "GLC-SX-MMD",
                    "sn": "123456789",
                    "vid": "V01  "
                },
                "SFP-10G-LR": {
                    "descr": "SFP+ 10GBASE-LR",
                    "name": "subslot 0/0 transceiver 1",
                    "pid": "SFP-10G-LR",
                    "sn": "123456789",
                    "vid": "V02  "
                },
                "SP7041-E-R": {
                    "descr": "GE T",
                    "name": "subslot 0/0 transceiver 7",
                    "pid": "SP7041-E-R",
                    "sn": "123456789",
                    "vid": "V02  "
                },
                "SPA-1X10GE-WL-V2": {
                    "descr": "1-port 10 Gigabit Ethernet Shared Port Adapter XFP based",
                    "name": "SPA subslot 0/1",
                    "pid": "SPA-1X10GE-WL-V2",
                    "sn": "123456789",
                    "vid": "V02  "
                },
                "XFP-10G-MM-SR": {
                    "descr": "10GBASE-SR/SW",
                    "name": "subslot 0/1 transceiver 0",
                    "pid": "XFP-10G-MM-SR",
                    "sn": "123456789",
                    "vid": "V03  "
                }
            }
        },
        "slot": {
            "Power Supply Module 0": {
                "other": {
                    "Power Supply Module 0": {
                        "descr": "Cisco ASR1001-X AC Power Supply",
                        "name": "Power Supply Module 0",
                        "pid": "ASR1001-X-PWR-AC",
                        "sn": "123456789",
                        "vid": "V01  "
                    }
                }
            },
            "Power Supply Module 1": {
                "other": {
                    "Power Supply Module 1": {
                        "descr": "Cisco ASR1001-X AC Power Supply",
                        "name": "Power Supply Module 1",
                        "pid": "ASR1001-X-PWR-AC",
                        "sn": "123456789",
                        "vid": "V01  "
                    }
                }
            }
        }
    }
}

我想遍历这些项目,但我对路径并不感兴趣,也无法预测。问题是在给定的示例中访问第一项将是

inventory_items['main']['chassis']

但是第二项是

inventory_items['main']['non-chassis']

其他项目是

inventory_items['slot']

如果每个项目的路径不同,我不知道如何遍历这些项目。我只对每个项目的字典感兴趣,而不是路径。但我不知道如何访问它们。 有没有办法只查找每个项目的 keys/value,但将它们作为每个项目的一个字典?

我想要得到的是一个可以像这样用“with_items”循环的列表

{
    "inventory_items": {
                "ASR1001-X": {
                    "descr": "Cisco ASR1001-X Chassis",
                    "name": "Chassis",
                    "pid": "ASR1001-X",
                    "sn": "123456789",
                    "vid": "V02  "
                },
                "ASR1001-X": {
                    "descr": "Cisco ASR1001-X Route Processor",
                    "name": "module R0",
                    "pid": "ASR1001-X",
                    "sn": "123456789",
                    "vid": "V02  "
                },
                "GLC-SX-MMD": {
                    "descr": "GE SX",
                    "name": "subslot 0/0 transceiver 3",
                    "pid": "GLC-SX-MMD",
                    "sn": "123456789",
                    "vid": "V01  "
                },
                "GLC-TE": {
                    "descr": "GE T",
                    "name": "subslot 0/0 transceiver 7",
                    "pid": "GLC-TE",
                    "sn": "123456789",
                    "vid": "V01  "
                },
                "Power Supply Module 0": {
                        "descr": "Cisco ASR1001-X AC Power Supply",
                        "name": "Power Supply Module 0",
                        "pid": "ASR1001-X-PWR-AC",
                        "sn": "123456789",
                        "vid": "V01  "
                 },
                 "Power Supply Module 1": {
                        "descr": "Cisco ASR1001-X AC Power Supply",
                        "name": "Power Supply Module 1",
                        "pid": "ASR1001-X-PWR-AC",
                        "sn": "123456789",
                        "vid": "V01  "
                 }
      }
}

如果键不是唯一的,则创建一个列表。比如有两个key同名ASR1001-X

    - set_fact:
        iitems: "{{ iitems|d([]) + _keys|zip(_vals)|map('combine') }}"
      loop: "{{ inventory_items.main.keys()|list }}"
      vars:
        _list: "{{ inventory_items.main[item]|dict2items }}"
        _keys: "{{ _list|map(attribute='key')|
                         map('community.general.dict_kv', 'key') }}"
        _vals: "{{ _list|map(attribute='value') }}"
    - set_fact:
        iitems: "{{ iitems|d([]) + _keys|zip(_vals)|map('combine') }}"
      loop: "{{ inventory_items.slot.keys()|list }}"
      vars:
        _list: "{{ inventory_items.slot[item].other|dict2items }}"
        _keys: "{{ _list|map(attribute='key')|
                         map('community.general.dict_kv', 'key') }}"
        _vals: "{{ _list|map(attribute='value') }}"

给予

  iitems|sort(attribute='key'):
  - descr: Cisco ASR1001-X Chassis
    key: ASR1001-X
    name: Chassis
    pid: ASR1001-X
    sn: '123456789'
    vid: 'V02  '
  - descr: Cisco ASR1001-X Route Processor
    key: ASR1001-X
    name: module R0
    pid: ASR1001-X
    sn: '123456789'
    vid: 'V02  '
  - descr: GE SX
    key: GLC-SX-MMD
    name: subslot 0/0 transceiver 3
    pid: GLC-SX-MMD
    sn: '123456789'
    vid: 'V01  '
  - descr: Cisco ASR1001-X AC Power Supply
    key: Power Supply Module 0
    name: Power Supply Module 0
    pid: ASR1001-X-PWR-AC
    sn: '123456789'
    vid: 'V01  '
  - descr: Cisco ASR1001-X AC Power Supply
    key: Power Supply Module 1
    name: Power Supply Module 1
    pid: ASR1001-X-PWR-AC
    sn: '123456789'
    vid: 'V01  '
  - descr: SFP+ 10GBASE-LR
    key: SFP-10G-LR
    name: subslot 0/0 transceiver 1
    pid: SFP-10G-LR
    sn: '123456789'
    vid: 'V02  '
  - descr: GE T
    key: SP7041-E-R
    name: subslot 0/0 transceiver 7
    pid: SP7041-E-R
    sn: '123456789'
    vid: 'V02  '
  - descr: 1-port 10 Gigabit Ethernet Shared Port Adapter XFP based
    key: SPA-1X10GE-WL-V2
    name: SPA subslot 0/1
    pid: SPA-1X10GE-WL-V2
    sn: '123456789'
    vid: 'V02  '
  - descr: 10GBASE-SR/SW
    key: XFP-10G-MM-SR
    name: subslot 0/1 transceiver 0
    pid: XFP-10G-MM-SR
    sn: '123456789'
    vid: 'V03  '

也可以创建字典,但相同的键会相互覆盖。例如,请注意下面的结果字典中只包含一个 ASR1001-X

    - set_fact:
        iitems: "{{ iitems|d({})|combine(inventory_items.main[item]) }}"
      loop: "{{ inventory_items.main.keys()|list }}"
    - set_fact:
        iitems: "{{ iitems|d({})|combine(inventory_items.slot[item].other) }}"
      loop: "{{ inventory_items.slot.keys()|list }}"

给予

  iitems:
    ASR1001-X:
      descr: Cisco ASR1001-X Route Processor
      name: module R0
      pid: ASR1001-X
      sn: '123456789'
      vid: 'V02  '
    GLC-SX-MMD:
      descr: GE SX
      name: subslot 0/0 transceiver 3
      pid: GLC-SX-MMD
      sn: '123456789'
      vid: 'V01  '
    Power Supply Module 0:
      descr: Cisco ASR1001-X AC Power Supply
      name: Power Supply Module 0
      pid: ASR1001-X-PWR-AC
      sn: '123456789'
      vid: 'V01  '
    Power Supply Module 1:
      descr: Cisco ASR1001-X AC Power Supply
      name: Power Supply Module 1
      pid: ASR1001-X-PWR-AC
      sn: '123456789'
      vid: 'V01  '
    SFP-10G-LR:
      descr: SFP+ 10GBASE-LR
      name: subslot 0/0 transceiver 1
      pid: SFP-10G-LR
      sn: '123456789'
      vid: 'V02  '
    SP7041-E-R:
      descr: GE T
      name: subslot 0/0 transceiver 7
      pid: SP7041-E-R
      sn: '123456789'
      vid: 'V02  '
    SPA-1X10GE-WL-V2:
      descr: 1-port 10 Gigabit Ethernet Shared Port Adapter XFP based
      name: SPA subslot 0/1
      pid: SPA-1X10GE-WL-V2
      sn: '123456789'
      vid: 'V02  '
    XFP-10G-MM-SR:
      descr: 10GBASE-SR/SW
      name: subslot 0/1 transceiver 0
      pid: XFP-10G-MM-SR
      sn: '123456789'
      vid: 'V03  '

备注

所以you dont know exactly the structure of dictionary,要使用通用程序,我建议您使用自定义过滤器。

在您的 playbook 文件夹中创建一个文件夹 filter_plugins 并创建一个文件 myfilter.py,其中包含一个名为自定义过滤器.

myfilter.py: 它搜索第一个不是字典的键,我捕获它的父键

#!/usr/bin/python
class FilterModule(object):
    parent = ""
    result=[]
    def filters(self):
        return {
            'customfilter': self.customfilter
        }
    
    def loopping(self, obj):
        for k in obj:            
            if type(obj[k]) is dict:
                self.parent= k
                self.loopping(obj[k])
            else:
                self.result.append({self.parent: obj})
                break
        return
 
    def customfilter(self, obj):
        for k in obj:
            self.loopping(obj[k])  
        return self.result

使用它的剧本:

- name: "make this working"
  hosts: localhost

  vars:
    inventory_items:
      main:
        chassis:
          ASR1001-X:
            descr: Cisco ASR1001-X Chassis
            name: Chassis
            pid: ASR1001-X
            sn: '123456789'
            vid: 'V02  '
           :
           :
        Power Supply Module 1:
          other:
            Power Supply Module 1:
              descr: Cisco ASR1001-X AC Power Supply
              name: Power Supply Module 1
              pid: ASR1001-X-PWR-AC
              sn: '123456789'
              vid: 'V01  '


  tasks:
    - name: Debug
      set_fact:
        inventory_items: "{{ inventory_items | customfilter }}"
    
    - name: display
      debug:
        msg: "{{ inventory_items }}"

除了列表格式外,结果与您想要的相同