Skip to content

Commit

Permalink
Changes to make lldp show command for multi-npu platforms. (sonic-net…
Browse files Browse the repository at this point in the history
…#914)

* Changes to make lldp show command for multi-npu platforms.
We will display only front-panel port information.

* Address Review Comments.

* Added Comment

* Fix LGTM error

* Address Review Comments
  • Loading branch information
abdosi authored May 29, 2020
1 parent fab196e commit 811a4c6
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 23 deletions.
116 changes: 95 additions & 21 deletions scripts/lldpshow
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,73 @@ import re
import sys
import xml.etree.ElementTree as ET
from tabulate import tabulate
import argparse
import sonic_device_util
from swsssdk import ConfigDBConnector, SonicDBConfig
BACKEND_ASIC_INTERFACE_NAME_PREFIX = 'Ethernet-BP'

LLDP_INTERFACE_LIST_IN_HOST_NAMESPACE = ''
LLDP_INSTANCE_IN_HOST_NAMESPACE = ''
LLDP_DEFAULT_INTERFACE_LIST_IN_ASIC_NAMESPACE = ''
SPACE_TOKEN = ' '

class Lldpshow(object):
def __init__(self):
self.lldpraw = None
self.lldpraw = []
self.lldpsum = {}
self.lldp_interface = []
self.lldp_instance = []
self.err = None
### So far only find Router and Bridge two capabilities in lldpctl, so any other capacility types will be read as Other
### if further capability type is supported like WLAN, can just add the tag definition here
self.ctags = {'Router': 'R', 'Bridge': 'B'}
SonicDBConfig.load_sonic_global_db_config()

# For multi-asic platforms we will get only front-panel interface to display
namespaces = sonic_device_util.get_all_namespaces()
per_asic_configdb = {}
for instance_num, front_asic_namespaces in enumerate(namespaces['front_ns']):
per_asic_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces)
per_asic_configdb[front_asic_namespaces].connect()
# Initalize Interface list to be ''. We will do string append of the interfaces below.
self.lldp_interface.append(LLDP_DEFAULT_INTERFACE_LIST_IN_ASIC_NAMESPACE)
self.lldp_instance.append(instance_num)
keys = per_asic_configdb[front_asic_namespaces].get_keys("PORT")
for key in keys:
if key.startswith(BACKEND_ASIC_INTERFACE_NAME_PREFIX):
continue
self.lldp_interface[instance_num] += key + SPACE_TOKEN

def get_info(self):
# LLDP running in host namespace
self.lldp_instance.append(LLDP_INSTANCE_IN_HOST_NAMESPACE)
self.lldp_interface.append(LLDP_INTERFACE_LIST_IN_HOST_NAMESPACE)

def get_info(self, lldp_detail_info, lldp_port):
"""
use 'lldpctl -f xml' command to gather local lldp detailed information
use 'lldpctl' command to gather local lldp detailed information
"""
lldp_cmd = 'lldpctl -f xml'
p = subprocess.Popen(lldp_cmd, stdout=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
## Wait for end of command. Get return returncode ##
returncode = p.wait()
### if no error, get the lldpctl result
if returncode == 0:
self.lldpraw = output
else:
self.err = err
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
lldp_cmd = 'sudo docker exec -it lldp{} lldpctl '.format(self.lldp_instance[lldp_instace_num]) + ('-f xml' if not lldp_detail_info else lldp_interface_list)
p = subprocess.Popen(lldp_cmd, stdout=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
## Wait for end of command. Get return returncode ##
returncode = p.wait()
### if no error, get the lldpctl result
if returncode == 0:
# ignore the output if given port is not present
if lldp_port is not None and lldp_port not in output:
continue
self.lldpraw.append(output)
if lldp_port is not None:
break
else:
self.err = err

if self.err:
self.lldpraw = []

def parse_cap(self, capabs):
"""
Expand All @@ -64,15 +107,19 @@ class Lldpshow(object):
capability += 'O'
return capability

def parse_info(self):
def parse_info(self, lldp_detail_info):
"""
Parse the lldp detailed infomation into dict
"""
if self.lldpraw is not None:
neis = ET.fromstring(self.lldpraw)
if lldp_detail_info:
return
for lldpraw in self.lldpraw:
neis = ET.fromstring(lldpraw)
intfs = neis.findall('interface')
for intf in intfs:
l_intf = intf.attrib['name']
if l_intf.startswith(BACKEND_ASIC_INTERFACE_NAME_PREFIX):
continue
self.lldpsum[l_intf] = {}
chassis = intf.find('chassis')
capabs = chassis.findall('capability')
Expand All @@ -97,11 +144,17 @@ class Lldpshow(object):
return sorted(summary, key=alphanum_key)


def display_sum(self):
def display_sum(self, lldp_detail_info):
"""
print out summary result of lldp neighbors
"""
if self.lldpraw is not None:
# In detail mode output is plain text
if self.lldpraw and lldp_detail_info:
lldp_output = ''
for lldp_detail_output in self.lldpraw:
lldp_output += lldp_detail_output
print (lldp_output)
elif self.lldpraw:
lldpstatus = []
print ('Capability codes: (R) Router, (B) Bridge, (O) Other')
header = ['LocalPort', 'RemoteDevice', 'RemotePortID', 'Capability', 'RemotePortDescr']
Expand All @@ -115,11 +168,32 @@ class Lldpshow(object):
print ('Error:',self.err)

def main():
parser = argparse.ArgumentParser(description='Display the LLDP neighbors',
version='1.0.0',
formatter_class=argparse.RawTextHelpFormatter,
epilog="""
Examples:
lldpshow
lldpshow -d
lldpshow -d -p Ethernet0
lldpshow -p Ethernet0
""")

parser.add_argument('-d', '--detail', action='store_true', help='LLDP neighbors detail information', default=False)
parser.add_argument('-p', '--port', type=str, help='LLDP neighbors detail information for given port', default=None)
args = parser.parse_args()

lldp_detail_info = args.detail
lldp_port = args.port

if lldp_port and not lldp_detail_info:
lldp_detail_info = True

try:
lldp = Lldpshow()
lldp.get_info()
lldp.parse_info()
lldp.display_sum()
lldp.get_info(lldp_detail_info, lldp_port)
lldp.parse_info(lldp_detail_info)
lldp.display_sum(lldp_detail_info)
except Exception as e:
print(e.message, file=sys.stderr)
sys.exit(1)
Expand Down
4 changes: 2 additions & 2 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1629,13 +1629,13 @@ def lldp():
@click.option('--verbose', is_flag=True, help="Enable verbose output")
def neighbors(interfacename, verbose):
"""Show LLDP neighbors"""
cmd = "sudo lldpctl"
cmd = "sudo lldpshow -d"

if interfacename is not None:
if get_interface_mode() == "alias":
interfacename = iface_alias_converter.alias_to_name(interfacename)

cmd += " {}".format(interfacename)
cmd += " -p {}".format(interfacename)

run_command(cmd, display_cmd=verbose)

Expand Down

0 comments on commit 811a4c6

Please sign in to comment.