From 3662ee746590d8b87e87fc79e3135c41ad5e7195 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Thu, 30 Mar 2023 20:25:14 +0200 Subject: [PATCH] Use JSON for fetching LLDP information to avoid character encoding issues --- scripts/lldpshow | 55 +++++++++++----------- tests/lldp_test.py | 113 +++++++++++++++++++++++++++++++-------------- 2 files changed, 104 insertions(+), 64 deletions(-) diff --git a/scripts/lldpshow b/scripts/lldpshow index e09176cf3c..8f6cfb2fe0 100755 --- a/scripts/lldpshow +++ b/scripts/lldpshow @@ -23,7 +23,7 @@ import argparse import re import subprocess import sys -from lxml import etree as ET +import json from sonic_py_common import device_info from swsscommon.swsscommon import ConfigDBConnector @@ -79,15 +79,10 @@ class Lldpshow(object): for lldp_instace_num in range(len(self.lldp_instance)): lldp_interface_list = lldp_port if lldp_port is not None else self.lldp_interface[lldp_instace_num] # In detail mode we will pass interface list (only front ports) and get O/P as plain text - # and in table format we will get xml output - if not lldp_detail_info: - lldp_args = ['-f', 'xml'] - elif lldp_interface_list == '': - lldp_args = [] - else: - lldp_args = [lldp_interface_list] - lldp_cmd = ['sudo', 'docker', 'exec', '-i', 'lldp{}'.format(self.lldp_instance[lldp_instace_num]), 'lldpctl'] + lldp_args - p = subprocess.Popen(lldp_cmd, stdout=subprocess.PIPE, text=True) + # and in table format we will get json output + lldp_cmd = 'sudo docker exec -i lldp{} lldpctl '.format(self.lldp_instance[lldp_instace_num]) + ( + '-f json' if not lldp_detail_info else lldp_interface_list) + p = subprocess.Popen(lldp_cmd, stdout=subprocess.PIPE, shell=True, text=True) (output, err) = p.communicate() ## Wait for end of command. Get return returncode ## returncode = p.wait() @@ -111,8 +106,8 @@ class Lldpshow(object): """ capability = "" for cap in capabs: - if cap.attrib['enabled'] == 'on': - captype = cap.attrib['type'] + if cap['enabled']: + captype = cap['type'] if captype in self.ctags.keys(): capability += self.ctags[captype] else: @@ -126,31 +121,33 @@ class Lldpshow(object): if lldp_detail_info: return for lldpraw in self.lldpraw: - neis = ET.fromstring(lldpraw.encode()) - intfs = neis.findall('interface') + neis = json.loads(lldpraw) + intfs = neis['lldp']['interface'] for intf in intfs: - l_intf = intf.attrib['name'] + l_intf = next(iter(intf.keys())) if l_intf.startswith(BACKEND_ASIC_INTERFACE_NAME_PREFIX): continue - remote_port = intf.find('port') - r_portid = remote_port.find('id').text + intf = intf[l_intf] + + remote_port = intf['port'] + r_portid = remote_port['id']['value'] key = l_intf + "#" + r_portid self.lldpsum[key] = {} self.lldpsum[key]['l_intf'] = l_intf self.lldpsum[key]['r_portid'] = r_portid - chassis = intf.find('chassis') - capabs = chassis.findall('capability') - capab = self.parse_cap(capabs) - rmt_name = chassis.find('name') - if rmt_name is not None: - self.lldpsum[key]['r_name'] = rmt_name.text - else: - self.lldpsum[key]['r_name'] = '' - rmt_desc = remote_port.find('descr') - if rmt_desc is not None: - self.lldpsum[key]['r_portname'] = rmt_desc.text + + chassis = intf['chassis'] + if len(chassis) == 1: + rmt_name = next(iter(chassis.keys())) + chassis = chassis[rmt_name] else: - self.lldpsum[key]['r_portname'] = '' + rmt_name = '' + capabs = chassis['capability'] + if isinstance(capabs, dict): + capabs = [capabs] + capab = self.parse_cap(capabs) + self.lldpsum[key]['r_name'] = rmt_name + self.lldpsum[key]['r_portname'] = remote_port['descr'] self.lldpsum[key]['capability'] = capab def sort_sum(self, summary): diff --git a/tests/lldp_test.py b/tests/lldp_test.py index 89177338e0..48f9ec5555 100644 --- a/tests/lldp_test.py +++ b/tests/lldp_test.py @@ -23,41 +23,84 @@ '--------------------------------------------------\n' 'Total entries displayed: 2') -expected_lldpctl_xml_output = \ -['\n\ - \n\ - \n\ - \n\ - 00:00:00:00:00:01\n\ - dummy\n\ - NA\n\ - 00:00:00:00:00:00\n\ - \n\ - \n\ - \n\ - \n\ - \n\ - \n\ - 00:00:00:00:00:01\n\ - First MAC\n\ - 120\n\ - \n\ - \n\ - \n\ - \n\ - 00:00:00:00:00:02\n\ - dummy\n\ - NA\n\ - 00:00:00:00:00:00\n\ - \n\ - \n\ - \n\ - 00:00:00:00:00:02\n\ - Second MAC\n\ - 120\n\ - \n\ - \n\ - \n'] +expected_lldpctl_xml_output = [''' +{ + "lldp": { + "interface": [ + { + "Ethernet0": { + "via": "LLDP", + "rid": "2", + "age": "7 days, 22:11:33", + "chassis": { + "dummy": { + "id": { + "type": "mac", + "value": "00:00:00:00:00:01" + }, + "descr": "NA", + "mgmt-ip": "192.0.2.1", + "capability": [ + { + "type": "Bridge", + "enabled": true + }, + { + "type": "Router", + "enabled": true + }, + { + "type": "Wlan", + "enabled": false + }, + { + "type": "Station", + "enabled": false + } + ] + } + }, + "port": { + "id": { + "type": "mac", + "value": "00:00:00:00:00:01" + }, + "descr": "First MAC", + "ttl": "120" + } + } + }, + { + "Ethernet0": { + "via": "LLDP", + "rid": "4", + "age": "7 days, 22:11:34", + "chassis": { + "dummy": { + "id": { + "type": "mac", + "value": "00:00:00:00:00:02" + }, + "descr": "NA", + "capability": { + "type": "Router", + "enabled": true + } + } + }, + "port": { + "id": { + "type": "mac", + "value": "00:00:00:00:00:02" + }, + "descr": "Second MAC", + "ttl": "120" + } + } + } + ] + } +}'''] class TestLldp(object): @classmethod