在Ansible中解析层级不同节点的Juniper设备XML库存
实现Juniper机箱XML库存的通配匹配与过滤
要实现你需要的功能,摆脱parse_xml过滤器的局限性,推荐结合Ansible的xml模块和XPath表达式来完成,这比直接用parse_xml更灵活,能轻松实现通配符式的层级匹配和条件过滤。
核心思路
- 用XPath匹配所有与
chassis相关的各级节点(包括自身和所有子/孙模块)。 - 过滤掉
serial-number属性值为BUILTIN的条目。
完整Playbook示例
--- - name: Parse Juniper chassis inventory and filter built-in modules hosts: junos_devices gather_facts: no tasks: # 1. 获取Juniper机箱硬件的XML格式输出 - name: Fetch chassis hardware XML data junipernetworks.junos.junos_command: commands: "show chassis hardware | display xml" register: chassis_xml_raw # 2. 用XPath匹配所有chassis相关的各级节点 # XPath逻辑:匹配所有节点名以chassis开头的节点,以及这些节点的所有子节点 - name: Extract all chassis-related modules ansible.builtin.xml: xmlstring: "{{ chassis_xml_raw.stdout[0] }}" # 忽略XML命名空间(Juniper XML通常带命名空间,local-name()避免匹配失败) xpath: "//*[starts-with(local-name(), 'chassis') or ancestor::*[starts-with(local-name(), 'chassis')]]" content: attribute # 提取节点的所有属性 register: chassis_modules_raw # 3. 过滤掉serial-number为BUILTIN的条目 - name: Filter out BUILTIN serial-number entries ansible.builtin.set_fact: filtered_modules: "{{ chassis_modules_raw.matches | rejectattr('serial-number', 'equalto', 'BUILTIN') | list }}" # 验证结果 - name: Display filtered chassis modules ansible.builtin.debug: var: filtered_modules
关键细节说明
XPath表达式:
//*[starts-with(local-name(), 'chassis') or ancestor::*[starts-with(local-name(), 'chassis')]]starts-with(local-name(), 'chassis'):匹配所有节点名以chassis开头的节点(比如chassis、chassis-module等)。ancestor::*[starts-with(local-name(), 'chassis')]:匹配所有父/祖节点是chassis开头的子节点,覆盖各级子模块。- 用
local-name()是因为Juniper的XML输出通常带有命名空间,直接用节点名会匹配失败。
过滤逻辑:使用Ansible内置的
rejectattr过滤器,直接剔除serial-number等于BUILTIN的条目,简洁高效。
替代方案(如果必须用parse_xml)
如果因为某些限制必须使用parse_xml过滤器,需要先将XML转换为字典,再通过递归遍历字典来匹配包含chassis的键并过滤:
- name: Convert XML to dict with parse_xml ansible.builtin.set_fact: chassis_dict: "{{ chassis_xml_raw.stdout[0] | parse_xml }}" # 自定义递归查找逻辑(Jinja2实现) - name: Recursively collect chassis-related entries ansible.builtin.set_fact: raw_modules: "{{ raw_modules | default([]) + ([item] if 'serial-number' in item else []) + (item.values() | select('is_dict') | map('recursive_collect') | flatten) }}" loop: "{{ chassis_dict.values() | flatten }}" - name: Filter out BUILTIN entries ansible.builtin.set_fact: filtered_modules: "{{ raw_modules | rejectattr('serial-number', 'equalto', 'BUILTIN') | list }}"
这种方式需要额外处理递归逻辑,不如XPath方案简洁,仅作为兼容场景的备选。
内容的提问来源于stack exchange,提问作者ankost403
相关产品推荐
相关产品推荐

