【问题标题】:Create a dictionary from an xml in Ansible在 Ansible 中从 xml 创建字典
【发布时间】:2020-10-16 15:06:06
【问题描述】:

因此,我正在使用 Ansible,并且正在从某些设备中检索特定信息。特别是,我有这样的任务:

<rpc-reply message-id="urn:uuid:1914-b84d7ff">
   <lldp-neighbors-information style="brief">
      <lldp-neighbor-information>
         <lldp-local-port-id>A1</lldp-local-port-id>
         <lldp-local-parent-interface-name>-</lldp-local-parent-interface-name>
         <lldp-remote-chassis-id-subtype>A2</lldp-remote-chassis-id-subtype>
         <lldp-remote-chassis-id>A3</lldp-remote-chassis-id>
         <lldp-remote-port-id-subtype>A4</lldp-remote-port-id-subtype>
         <lldp-remote-port-id>A5</lldp-remote-port-id>
         <lldp-remote-system-name>A6</lldp-remote-system-name>
      </lldp-neighbor-information>
      <lldp-neighbor-information>
         <lldp-local-port-id>B1</lldp-local-port-id>
         <lldp-local-parent-interface-name>-</lldp-local-parent-interface-name>
         <lldp-remote-chassis-id-subtype>B2</lldp-remote-chassis-id-subtype>
         <lldp-remote-chassis-id>B3</lldp-remote-chassis-id>
         <lldp-remote-port-id-subtype>B4</lldp-remote-port-id-subtype>
         <lldp-remote-port-id>B5</lldp-remote-port-id>
         <lldp-remote-system-name>B6</lldp-remote-system-name>
      </lldp-neighbor-information>
      <lldp-neighbor-information>
         <lldp-local-port-id>C1</lldp-local-port-id>
         <lldp-local-parent-interface-name>-</lldp-local-parent-interface-name>
         <lldp-remote-chassis-id-subtype>C2</lldp-remote-chassis-id-subtype>
         <lldp-remote-chassis-id>C3</lldp-remote-chassis-id>
         <lldp-remote-port-id-subtype>C4</lldp-remote-port-id-subtype>
         <lldp-remote-port-id>C5</lldp-remote-port-id>
      </lldp-neighbor-information>
   </lldp-neighbors-information>
</rpc-reply>

我的目标是建立一个这样的字典:

{'A6': ['A1', 'A2'], 'B6': ['B1', 'B2']}

我所做的是通过执行以下任务创建一个包含所有密钥的列表:

- name: Retrieve lldp system names
  xml:
    xmlstring: "{{ item.string | regex_replace('\n', '') }}"
    xpath: "{{ item.path }}"
    content: text
  loop:
    - { path: "/rpc-reply/lldp-neighbors-information/lldp-neighbor-information/lldp-remote-system-name", string: "{{xml_reply.xml}}" }
  register: sys_names


- name: Save all sys names in a list
  set_fact:
    sys_names_list: "{{ sys_names.results[0].matches | map('dict2items') | list | json_query('[].value') }}"

然后我可以分别为元素 [A1, B1] 和第三个列表 [A2, B2] 创建第二个列表。所以我可以结合这 3 个列表并创建我的字典。

所以这里有3个问题:

  1. 有没有办法直接从 xml 元素构建 ansible-dictionary 还是我应该继续编写自己的模块?
  2. 由于最后一个元素 C6 将是我的第三个键但不存在,我想跳过它。在我上面的任务中怎么可能?
  3. 如何组合这 3 个列表并创建我的字典,跳过第 3 个元素?否则我的列表将与正确的信息不匹配..

【问题讨论】:

  • 我认为您应该将所有逻辑放入过滤器插件中,以保持您的剧本可读。你正在尝试实现一个复杂的算法,在这种情况下 python 比 ansible/jinja2 更好
  • 有没有办法在 ansible 中解决上述任何问题?

标签: loops dictionary ansible


【解决方案1】:

您可以通过组合将您的 xml 文件转换为结构化 json 的过滤器来执行此操作,以便于数据操作。

在这个例子中,我使用了converter from xml to json

文件filter_plugins/from_xml.py

#!/usr/bin/python
# From https://github.com/nasgoncalves/ansible-xml-to-json-filter/blob/master/filter_plugins/xml_to_json.py

import json
import xml.etree.ElementTree as ET
from collections import defaultdict


class FilterModule(object):

    def etree_to_dict(self, t):
        d = {t.tag: {} if t.attrib else None}
        children = list(t)
        if children:
            dd = defaultdict(list)
            for dc in map(self.etree_to_dict, children):
                for k, v in dc.items():
                    dd[k].append(v)
            d = {t.tag: {k: v[0] if len(v) == 1 else v
                         for k, v in dd.items()}}
        if t.attrib:
            d[t.tag].update(('@' + k, v)
                            for k, v in t.attrib.items())
        if t.text:
            text = t.text.strip()
            if children or t.attrib:
                if text:
                  d[t.tag]['#text'] = text
            else:
                d[t.tag] = text
        return d

    def filters(self):
        return {
            'from_xml': self.from_xml,
            'xml_to_json': self.xml_to_json
        }

    def from_xml(self, data):
        root = ET.ElementTree(ET.fromstring(data)).getroot()
        return self.etree_to_dict(root)

    def xml_to_json(self, data):
        return json.dumps(self.from_xml(data))

文件 playbook.yml,我使用 jinja2 模板过滤键并组合列表中的值,然后将此列表转换为带有过滤器 items2dict 的字典:

---
- hosts: localhost
  gather_facts: no
  connection: local
  tasks:
  - set_fact:
      xml_message: |
        <rpc-reply message-id="urn:uuid:1914-b84d7ff">
          <lldp-neighbors-information style="brief">
              <lldp-neighbor-information>
                <lldp-local-port-id>A1</lldp-local-port-id>
                <lldp-local-parent-interface-name>-</lldp-local-parent-interface-name>
                <lldp-remote-chassis-id-subtype>A2</lldp-remote-chassis-id-subtype>
                <lldp-remote-chassis-id>A3</lldp-remote-chassis-id>
                <lldp-remote-port-id-subtype>A4</lldp-remote-port-id-subtype>
                <lldp-remote-port-id>A5</lldp-remote-port-id>
                <lldp-remote-system-name>A6</lldp-remote-system-name>
              </lldp-neighbor-information>
              <lldp-neighbor-information>
                <lldp-local-port-id>B1</lldp-local-port-id>
                <lldp-local-parent-interface-name>-</lldp-local-parent-interface-name>
                <lldp-remote-chassis-id-subtype>B2</lldp-remote-chassis-id-subtype>
                <lldp-remote-chassis-id>B3</lldp-remote-chassis-id>
                <lldp-remote-port-id-subtype>B4</lldp-remote-port-id-subtype>
                <lldp-remote-port-id>B5</lldp-remote-port-id>
                <lldp-remote-system-name>B6</lldp-remote-system-name>
              </lldp-neighbor-information>
              <lldp-neighbor-information>
                <lldp-local-port-id>C1</lldp-local-port-id>
                <lldp-local-parent-interface-name>-</lldp-local-parent-interface-name>
                <lldp-remote-chassis-id-subtype>C2</lldp-remote-chassis-id-subtype>
                <lldp-remote-chassis-id>C3</lldp-remote-chassis-id>
                <lldp-remote-port-id-subtype>C4</lldp-remote-port-id-subtype>
                <lldp-remote-port-id>C5</lldp-remote-port-id>
              </lldp-neighbor-information>
          </lldp-neighbors-information>
        </rpc-reply>
  - set_fact:
      my_dict: |
        {% set json_message = xml_message | from_xml %}
        {% for item in json_message['rpc-reply']['lldp-neighbors-information']['lldp-neighbor-information'] %}
        {%   if "lldp-remote-system-name" in item %}
        - key: {{ item['lldp-remote-system-name'] }}
          value: [{{ item['lldp-local-port-id'] }}, {{ item['lldp-remote-chassis-id-subtype'] }}]
        {%   endif %}
        {% endfor %}
  - set_fact:
      my_dict: "{{ my_dict | from_yaml | items2dict }}"
  - debug:
      var: my_dict

使用ansible-playbook playbook.yml 执行此剧本会返回:

PLAY [localhost] **********************************

TASK [set_fact] ***********************************
ok: [localhost]

TASK [set_fact] ***********************************
ok: [localhost]

TASK [set_fact] ***********************************
ok: [localhost]

TASK [debug] **************************************
ok: [localhost] => {
    "my_dict": {
        "A6": [
            "A1", 
            "A2"
        ], 
        "B6": [
            "B1", 
            "B2"
        ]
    }
}

【讨论】:

    猜你喜欢
    • 2022-10-14
    • 2014-09-09
    • 2021-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    相关资源
    最近更新 更多