Skip to content

Commit 811a4c6

Browse files
authored
Changes to make lldp show command for multi-npu platforms. (sonic-net#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
1 parent fab196e commit 811a4c6

File tree

2 files changed

+97
-23
lines changed

2 files changed

+97
-23
lines changed

scripts/lldpshow

+95-21
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,73 @@ import re
2525
import sys
2626
import xml.etree.ElementTree as ET
2727
from tabulate import tabulate
28+
import argparse
29+
import sonic_device_util
30+
from swsssdk import ConfigDBConnector, SonicDBConfig
31+
BACKEND_ASIC_INTERFACE_NAME_PREFIX = 'Ethernet-BP'
32+
33+
LLDP_INTERFACE_LIST_IN_HOST_NAMESPACE = ''
34+
LLDP_INSTANCE_IN_HOST_NAMESPACE = ''
35+
LLDP_DEFAULT_INTERFACE_LIST_IN_ASIC_NAMESPACE = ''
36+
SPACE_TOKEN = ' '
2837

2938
class Lldpshow(object):
3039
def __init__(self):
31-
self.lldpraw = None
40+
self.lldpraw = []
3241
self.lldpsum = {}
42+
self.lldp_interface = []
43+
self.lldp_instance = []
3344
self.err = None
3445
### So far only find Router and Bridge two capabilities in lldpctl, so any other capacility types will be read as Other
3546
### if further capability type is supported like WLAN, can just add the tag definition here
3647
self.ctags = {'Router': 'R', 'Bridge': 'B'}
48+
SonicDBConfig.load_sonic_global_db_config()
49+
50+
# For multi-asic platforms we will get only front-panel interface to display
51+
namespaces = sonic_device_util.get_all_namespaces()
52+
per_asic_configdb = {}
53+
for instance_num, front_asic_namespaces in enumerate(namespaces['front_ns']):
54+
per_asic_configdb[front_asic_namespaces] = ConfigDBConnector(use_unix_socket_path=True, namespace=front_asic_namespaces)
55+
per_asic_configdb[front_asic_namespaces].connect()
56+
# Initalize Interface list to be ''. We will do string append of the interfaces below.
57+
self.lldp_interface.append(LLDP_DEFAULT_INTERFACE_LIST_IN_ASIC_NAMESPACE)
58+
self.lldp_instance.append(instance_num)
59+
keys = per_asic_configdb[front_asic_namespaces].get_keys("PORT")
60+
for key in keys:
61+
if key.startswith(BACKEND_ASIC_INTERFACE_NAME_PREFIX):
62+
continue
63+
self.lldp_interface[instance_num] += key + SPACE_TOKEN
3764

38-
def get_info(self):
65+
# LLDP running in host namespace
66+
self.lldp_instance.append(LLDP_INSTANCE_IN_HOST_NAMESPACE)
67+
self.lldp_interface.append(LLDP_INTERFACE_LIST_IN_HOST_NAMESPACE)
68+
69+
def get_info(self, lldp_detail_info, lldp_port):
3970
"""
40-
use 'lldpctl -f xml' command to gather local lldp detailed information
71+
use 'lldpctl' command to gather local lldp detailed information
4172
"""
42-
lldp_cmd = 'lldpctl -f xml'
43-
p = subprocess.Popen(lldp_cmd, stdout=subprocess.PIPE, shell=True)
44-
(output, err) = p.communicate()
45-
## Wait for end of command. Get return returncode ##
46-
returncode = p.wait()
47-
### if no error, get the lldpctl result
48-
if returncode == 0:
49-
self.lldpraw = output
50-
else:
51-
self.err = err
73+
for lldp_instace_num in range(len(self.lldp_instance)):
74+
lldp_interface_list = lldp_port if lldp_port is not None else self.lldp_interface[lldp_instace_num]
75+
# In detail mode we will pass interface list (only front ports) and get O/P as plain text
76+
# and in table format we will get xml output
77+
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)
78+
p = subprocess.Popen(lldp_cmd, stdout=subprocess.PIPE, shell=True)
79+
(output, err) = p.communicate()
80+
## Wait for end of command. Get return returncode ##
81+
returncode = p.wait()
82+
### if no error, get the lldpctl result
83+
if returncode == 0:
84+
# ignore the output if given port is not present
85+
if lldp_port is not None and lldp_port not in output:
86+
continue
87+
self.lldpraw.append(output)
88+
if lldp_port is not None:
89+
break
90+
else:
91+
self.err = err
92+
93+
if self.err:
94+
self.lldpraw = []
5295

5396
def parse_cap(self, capabs):
5497
"""
@@ -64,15 +107,19 @@ class Lldpshow(object):
64107
capability += 'O'
65108
return capability
66109

67-
def parse_info(self):
110+
def parse_info(self, lldp_detail_info):
68111
"""
69112
Parse the lldp detailed infomation into dict
70113
"""
71-
if self.lldpraw is not None:
72-
neis = ET.fromstring(self.lldpraw)
114+
if lldp_detail_info:
115+
return
116+
for lldpraw in self.lldpraw:
117+
neis = ET.fromstring(lldpraw)
73118
intfs = neis.findall('interface')
74119
for intf in intfs:
75120
l_intf = intf.attrib['name']
121+
if l_intf.startswith(BACKEND_ASIC_INTERFACE_NAME_PREFIX):
122+
continue
76123
self.lldpsum[l_intf] = {}
77124
chassis = intf.find('chassis')
78125
capabs = chassis.findall('capability')
@@ -97,11 +144,17 @@ class Lldpshow(object):
97144
return sorted(summary, key=alphanum_key)
98145

99146

100-
def display_sum(self):
147+
def display_sum(self, lldp_detail_info):
101148
"""
102149
print out summary result of lldp neighbors
103150
"""
104-
if self.lldpraw is not None:
151+
# In detail mode output is plain text
152+
if self.lldpraw and lldp_detail_info:
153+
lldp_output = ''
154+
for lldp_detail_output in self.lldpraw:
155+
lldp_output += lldp_detail_output
156+
print (lldp_output)
157+
elif self.lldpraw:
105158
lldpstatus = []
106159
print ('Capability codes: (R) Router, (B) Bridge, (O) Other')
107160
header = ['LocalPort', 'RemoteDevice', 'RemotePortID', 'Capability', 'RemotePortDescr']
@@ -115,11 +168,32 @@ class Lldpshow(object):
115168
print ('Error:',self.err)
116169

117170
def main():
171+
parser = argparse.ArgumentParser(description='Display the LLDP neighbors',
172+
version='1.0.0',
173+
formatter_class=argparse.RawTextHelpFormatter,
174+
epilog="""
175+
Examples:
176+
lldpshow
177+
lldpshow -d
178+
lldpshow -d -p Ethernet0
179+
lldpshow -p Ethernet0
180+
""")
181+
182+
parser.add_argument('-d', '--detail', action='store_true', help='LLDP neighbors detail information', default=False)
183+
parser.add_argument('-p', '--port', type=str, help='LLDP neighbors detail information for given port', default=None)
184+
args = parser.parse_args()
185+
186+
lldp_detail_info = args.detail
187+
lldp_port = args.port
188+
189+
if lldp_port and not lldp_detail_info:
190+
lldp_detail_info = True
191+
118192
try:
119193
lldp = Lldpshow()
120-
lldp.get_info()
121-
lldp.parse_info()
122-
lldp.display_sum()
194+
lldp.get_info(lldp_detail_info, lldp_port)
195+
lldp.parse_info(lldp_detail_info)
196+
lldp.display_sum(lldp_detail_info)
123197
except Exception as e:
124198
print(e.message, file=sys.stderr)
125199
sys.exit(1)

show/main.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1629,13 +1629,13 @@ def lldp():
16291629
@click.option('--verbose', is_flag=True, help="Enable verbose output")
16301630
def neighbors(interfacename, verbose):
16311631
"""Show LLDP neighbors"""
1632-
cmd = "sudo lldpctl"
1632+
cmd = "sudo lldpshow -d"
16331633

16341634
if interfacename is not None:
16351635
if get_interface_mode() == "alias":
16361636
interfacename = iface_alias_converter.alias_to_name(interfacename)
16371637

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

16401640
run_command(cmd, display_cmd=verbose)
16411641

0 commit comments

Comments
 (0)