Skip to content

Commit 206aabe

Browse files
authored
CLI support for Layer 2 MAC/FDB show (sonic-net#106)
* CLI support to show MAC/FDB entries learnt in Hardware * Updated to use common library API to get bridge port mapping * Rearranged imports * Addressed review comments, check-logic modified * Show command hookup added
1 parent 70422b5 commit 206aabe

File tree

3 files changed

+160
-1
lines changed

3 files changed

+160
-1
lines changed

scripts/fdbshow

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/python
2+
"""
3+
Script to show MAC/FDB entries learnt in Hardware
4+
5+
usage: fdbshow [-p PORT] [-v VLAN]
6+
optional arguments:
7+
-p, --port FDB learned on specific port: Ethernet0
8+
-v, --vlan FDB learned on specific Vlan: 1000
9+
10+
Example of the output:
11+
admin@str~$ fdbshow
12+
No. Vlan MacAddress Port
13+
----- ------ ----------------- ----------
14+
1 1000 7C:FE:90:80:9F:05 Ethernet20
15+
2 1000 7C:FE:90:80:9F:10 Ethernet40
16+
3 1000 7C:FE:90:80:9F:01 Ethernet4
17+
4 1000 7C:FE:90:80:9F:02 Ethernet8
18+
Total number of entries 4
19+
admin@str:~$ fdbshow -p Ethernet4
20+
No. Vlan MacAddress Port
21+
----- ------ ----------------- ---------
22+
1 1000 7C:FE:90:80:9F:01 Ethernet4
23+
Total number of entries 1
24+
admin@str:~$ fdbshow -v 1001
25+
1001 is not in list
26+
27+
"""
28+
import argparse
29+
import json
30+
import sys
31+
32+
from natsort import natsorted
33+
from swsssdk import SonicV2Connector, port_util
34+
from tabulate import tabulate
35+
36+
class FdbShow(object):
37+
38+
HEADER = ['No.', 'Vlan', 'MacAddress', 'Port']
39+
FDB_COUNT = 0
40+
41+
def __init__(self):
42+
super(FdbShow,self).__init__()
43+
self.db = SonicV2Connector(host="127.0.0.1")
44+
self.if_name_map, \
45+
self.if_oid_map = port_util.get_interface_oid_map(self.db)
46+
self.if_br_oid_map = port_util.get_bridge_port_map(self.db)
47+
self.fetch_fdb_data()
48+
return
49+
50+
def fetch_fdb_data(self):
51+
"""
52+
Fetch FDB entries from ASIC DB.
53+
FDB entries are sorted on "VlanID" and stored as a list of tuples
54+
"""
55+
self.db.connect(self.db.ASIC_DB)
56+
self.bridge_mac_list = []
57+
58+
fdb_str = self.db.keys('ASIC_DB', "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:*")
59+
if not fdb_str:
60+
return
61+
62+
if self.if_br_oid_map is None:
63+
return
64+
65+
oid_pfx = len("oid:0x")
66+
for s in fdb_str:
67+
fdb_entry = s.decode()
68+
fdb = json.loads(fdb_entry .split(":", 2)[-1])
69+
if not fdb:
70+
continue
71+
72+
ent = self.db.get_all('ASIC_DB', s, blocking=True)
73+
br_port_id = ent[b"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"][oid_pfx:]
74+
port_id = self.if_br_oid_map[br_port_id]
75+
if_name = self.if_oid_map[port_id]
76+
77+
self.bridge_mac_list.append((int(fdb["vlan"]),) + (fdb["mac"],) + (if_name,))
78+
79+
self.bridge_mac_list.sort(key = lambda x: x[0])
80+
return
81+
82+
83+
def get_iter_index(self, key_value=0, pos=0):
84+
"""
85+
Get the starting index of matched entry
86+
"""
87+
if pos != 0:
88+
self.bridge_mac_list = natsorted(self.bridge_mac_list, key = lambda x: x[pos])
89+
90+
if key_value == 0:
91+
return 0
92+
93+
keys = [r[pos] for r in self.bridge_mac_list]
94+
return keys.index(key_value)
95+
96+
97+
def display(self, vlan, port):
98+
"""
99+
Display the FDB entries for specified vlan/port.
100+
@todo: - PortChannel support
101+
"""
102+
output = []
103+
104+
if vlan is not None:
105+
vlan = int(vlan)
106+
s_index = self.get_iter_index(vlan)
107+
self.bridge_mac_list = [fdb for fdb in self.bridge_mac_list[s_index:]
108+
if fdb[0] == vlan]
109+
if port is not None:
110+
s_index = self.get_iter_index(port, 2)
111+
self.bridge_mac_list = [fdb for fdb in self.bridge_mac_list[s_index:]
112+
if fdb[2] == port]
113+
114+
for fdb in self.bridge_mac_list:
115+
self.FDB_COUNT += 1
116+
output.append([self.FDB_COUNT, fdb[0], fdb[1], fdb[2]])
117+
118+
print tabulate(output, self.HEADER)
119+
print "Total number of entries {0} ".format(self.FDB_COUNT)
120+
121+
122+
def main():
123+
124+
parser = argparse.ArgumentParser(description='Display ASIC FDB entries',
125+
formatter_class=argparse.RawTextHelpFormatter)
126+
parser.add_argument('-p', '--port', type=str, help='FDB learned on specific port: Ethernet0', default=None)
127+
parser.add_argument('-v', '--vlan', type=str, help='FDB learned on specific Vlan: 1001', default=None)
128+
args = parser.parse_args()
129+
130+
try:
131+
fdb = FdbShow()
132+
fdb.display(args.vlan, args.port)
133+
except Exception as e:
134+
print e.message
135+
sys.exit(1)
136+
137+
if __name__ == "__main__":
138+
main()

setup.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'scripts/dropcheck',
2323
'scripts/fast-reboot',
2424
'scripts/fast-reboot-dump.py',
25+
'scripts/fdbshow',
2526
'scripts/generate_dump',
2627
'scripts/lldpshow',
2728
'scripts/portstat',
@@ -45,7 +46,8 @@
4546
'click',
4647
'click-default-group',
4748
'natsort',
48-
'tabulate'
49+
'tabulate',
50+
'swsssdk'
4951
],
5052
classifiers=[
5153
'Development Status :: 3 - Alpha',

show/main.py

+19
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,25 @@ def sfp(interfacename):
226226

227227
run_command(cmd)
228228

229+
#
230+
# 'mac' command ("show mac ...")
231+
#
232+
233+
@cli.command()
234+
@click.option('-v', '--vlan')
235+
@click.option('-p', '--port')
236+
def mac(vlan, port):
237+
"""Show MAC (FDB) entries"""
238+
239+
command = "fdbshow"
240+
241+
if vlan is not None:
242+
command += " -v {}".format(vlan)
243+
244+
if port is not None:
245+
command += " -p {}".format(port)
246+
247+
run_command(command)
229248

230249
#
231250
# 'ip' group ("show ip ...")

0 commit comments

Comments
 (0)