Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 86 additions & 21 deletions scripts/lldpshow
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,65 @@ 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'

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()

def get_info(self):
# For multi-npu platforms we will get only front-panel interface to display
namespaces = sonic_device_util.get_all_namespaces()
per_npu_configdb = {}
for instance_num, front_asic_namespaces in enumerate(namespaces['front_ns']):
per_npu_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces)
per_npu_configdb[front_asic_namespaces].connect()
self.lldp_interface.append('')
self.lldp_instance.append(instance_num)
keys = per_npu_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 + ' '

# LLDP running in host namespace
self.lldp_instance.append('')
self.lldp_interface.append('')

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)
else:
self.err = err

if self.err:
self.lldpraw = []

def parse_cap(self, capabs):
"""
Expand All @@ -64,15 +99,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 +136,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 +160,31 @@ 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
""")

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:
parser.error('-d is required when -p is set.')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this? Can we set -d automatically when -p?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavel-shirshov yes that is fine too. Updated accordingly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavel-shirshov can you please review/approve the PR. Above comment has been addressed.


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 @@ -1626,13 +1626,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