-
Notifications
You must be signed in to change notification settings - Fork 818
CLI support for Layer 2 MAC/FDB show #106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
83a6a85
0626d7d
76b493d
7e32eee
9ec32a2
25f704d
3bf4b8a
592e17b
67b52a2
96356c5
8edf4e7
508fb78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| #!/usr/bin/python | ||
| """ | ||
| Script to show MAC/FDB entries learnt in Hardware | ||
|
|
||
| usage: fdbshow [-p PORT] [-v VLAN] | ||
| optional arguments: | ||
| -p, --port FDB learned on specific port: Ethernet0 | ||
| -v, --vlan FDB learned on specific Vlan: 1000 | ||
|
|
||
| Example of the output: | ||
| admin@str~$ fdbshow | ||
| No. Vlan MacAddress Port | ||
| ----- ------ ----------------- ---------- | ||
| 1 1000 7C:FE:90:80:9F:05 Ethernet20 | ||
| 2 1000 7C:FE:90:80:9F:10 Ethernet40 | ||
| 3 1000 7C:FE:90:80:9F:01 Ethernet4 | ||
| 4 1000 7C:FE:90:80:9F:02 Ethernet8 | ||
| Total number of entries 4 | ||
| admin@str:~$ fdbshow -p Ethernet4 | ||
| No. Vlan MacAddress Port | ||
| ----- ------ ----------------- --------- | ||
| 1 1000 7C:FE:90:80:9F:01 Ethernet4 | ||
| Total number of entries 1 | ||
| admin@str:~$ fdbshow -v 1001 | ||
| 1001 is not in list | ||
|
|
||
| """ | ||
|
|
||
| import swsssdk | ||
| import argparse | ||
| import json | ||
| import sys | ||
|
|
||
| from tabulate import tabulate | ||
| from natsort import natsorted | ||
|
|
||
| class FdbShow(object): | ||
|
|
||
| HEADER = ['No.', 'Vlan', 'MacAddress', 'Port'] | ||
| FDB_COUNT = 0 | ||
|
|
||
| def __init__(self): | ||
| super(FdbShow,self).__init__() | ||
| self.db = swsssdk.SonicV2Connector(host="127.0.0.1") | ||
|
||
| self.get_interface_oid_map() | ||
| self.get_bridge_port_oid_map() | ||
| self.fetch_fdb_data() | ||
| return | ||
|
|
||
|
|
||
| def get_interface_oid_map(self): | ||
| """ | ||
| Get the Interface names from Counters DB | ||
| """ | ||
| self.db.connect('COUNTERS_DB') | ||
| self.if_name_map = self.db.get_all('COUNTERS_DB', 'COUNTERS_PORT_NAME_MAP', blocking=True) | ||
| self.if_oid_map = {sai_oid: if_name for if_name, sai_oid in self.if_name_map.items()} | ||
|
|
||
|
|
||
| def get_bridge_port_oid_map(self): | ||
| """ | ||
| Get the Bridge port names from ASIC DB | ||
| """ | ||
| self.db.connect(self.db.ASIC_DB) | ||
| self.if_br_oid_map = {} | ||
| br_port_str = self.db.keys('ASIC_DB', "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:*") | ||
| if not br_port_str: | ||
| return | ||
|
|
||
| offset = len("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:") | ||
| oid_pfx = len("oid:0x") | ||
| for s in br_port_str: | ||
| br_port_id = s[(offset + oid_pfx):] | ||
| ent = self.db.get_all('ASIC_DB', s, blocking=True) | ||
| port_id = ent[b"SAI_BRIDGE_PORT_ATTR_PORT_ID"][oid_pfx:] | ||
| self.if_br_oid_map[br_port_id] = port_id | ||
|
|
||
|
|
||
| def fetch_fdb_data(self): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I notice code duplication with sonic-snmpagent. Could you reuse code?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I checked this in detail to see if we can reuse some functionality but there is tight coupling with MIB and SNMP functionality. Since this is a standalone functionality, thought of keeping it separate.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Modified to use common library API |
||
| """ | ||
| Fetch FDB entries from ASIC DB. | ||
| FDB entries are sorted on "VlanID" and stored as a list of tuples | ||
| """ | ||
| self.db.connect(self.db.ASIC_DB) | ||
| self.bridge_mac_list = [] | ||
|
|
||
| fdb_str = self.db.keys('ASIC_DB', "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:*") | ||
| if not fdb_str: | ||
| return | ||
|
|
||
| oid_pfx = len("oid:0x") | ||
| for s in fdb_str: | ||
| fdb_entry = s.decode() | ||
| try: | ||
| fdb = json.loads(fdb_entry .split(":", 2)[-1]) | ||
| except ValueError as e: | ||
| print e.message | ||
| continue | ||
|
|
||
| ent = self.db.get_all('ASIC_DB', s, blocking=True) | ||
| br_port_id = ent[b"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"][oid_pfx:] | ||
| try: | ||
|
||
| port_id = self.if_br_oid_map[br_port_id] | ||
| if_name = self.if_oid_map[port_id] | ||
| except KeyError as e: | ||
| continue | ||
|
|
||
| self.bridge_mac_list.append((int(fdb["vlan"]),) + (fdb["mac"],) + (if_name,)) | ||
|
|
||
| self.bridge_mac_list.sort(key = lambda x: x[0]) | ||
| return | ||
|
|
||
|
|
||
| def get_iter_index(self, key_value=0, pos=0): | ||
| """ | ||
| Get the starting index of matched entry | ||
| """ | ||
| if pos == 0: | ||
| self.bridge_mac_list.sort(key = lambda x: x[pos]) | ||
| keys = [r[pos] for r in self.bridge_mac_list] | ||
| try: | ||
| return keys.index(key_value) | ||
| except IndexError as e: | ||
| print e.message | ||
|
|
||
|
|
||
| def display_specific(self, port, vlan): | ||
| """ | ||
| Display the FDB entries for specified vlan/port. | ||
| """ | ||
| output = [] | ||
|
|
||
| if vlan is not None and port is None: | ||
| vlan = int(vlan) | ||
| s_index = self.get_iter_index(vlan) | ||
| fdb_list = [fdb for fdb in self.bridge_mac_list[s_index:] if fdb[0] == vlan] | ||
|
|
||
| elif vlan is None and port is not None: | ||
| self.bridge_mac_list = natsorted(self.bridge_mac_list, key = lambda x: x[2]) | ||
| s_index = self.get_iter_index(port, 2) | ||
| fdb_list = [fdb for fdb in self.bridge_mac_list[s_index:] if fdb[2] == port] | ||
|
|
||
| else: | ||
| vlan = int(vlan) | ||
| s_index = self.get_iter_index(vlan) | ||
| fdb_list = [fdb for fdb in self.bridge_mac_list[s_index:] | ||
| if fdb[0] == vlan and fdb[2] == port] | ||
|
||
|
|
||
| for fdb in fdb_list: | ||
| self.FDB_COUNT += 1 | ||
| output.append([self.FDB_COUNT, fdb[0], fdb[1], fdb[2]]) | ||
|
|
||
| print tabulate(output, self.HEADER) | ||
| print "Total number of entries {0} ".format(self.FDB_COUNT) | ||
|
|
||
|
|
||
| def display_summary(self): | ||
|
||
| """ | ||
| Display the FDB summary. | ||
| @todo: - PortChannel support | ||
| """ | ||
| output = [] | ||
| self.bridge_mac_list.sort(key = lambda x: x[0]) | ||
| for fdb in self.bridge_mac_list: | ||
| self.FDB_COUNT += 1 | ||
| output.append([self.FDB_COUNT, fdb[0], fdb[1], fdb[2]]) | ||
|
|
||
| print tabulate(output, self.HEADER) | ||
| print "Total number of entries {0} ".format(self.FDB_COUNT) | ||
|
|
||
|
|
||
| def main(): | ||
|
|
||
| parser = argparse.ArgumentParser(description='Display ASIC FDB entries', | ||
| formatter_class=argparse.RawTextHelpFormatter) | ||
| parser.add_argument('-p', '--port', type=str, help='FDB learned on specific port: Ethernet0', default=None) | ||
| parser.add_argument('-v', '--vlan', type=str, help='FDB learned on specific Vlan: 1001', default=None) | ||
| args = parser.parse_args() | ||
|
|
||
| try: | ||
| fdb = FdbShow() | ||
| if args.port is None and args.vlan is None: | ||
| fdb.display_summary() | ||
| else: | ||
| fdb.display_specific(args.port, args.vlan) | ||
|
|
||
| except Exception as e: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No need to catch all exceptions.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we discussed, I removed this and checked. But observed that in case of invalid inputs, in addition to displaying the error message, it also gives the trace back which is not expected. For e.g: admin@str-s6000-acs-7:~$ ./fdbshow -v 1001 Whereas, with the exception is would be: admin@str-s6000-acs-7:~$ ./fdbshow -v 1001
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you are arguing about this case, the solution should be catch specific exception, instead of catch all.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer to keep it same as other scripts |
||
| print e.message | ||
| sys.exit(1) | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please reorder so that
import ...statements are ordered alphabetically followed byfrom ... import ...statements ordered alphabetically. This makes it easier to locate used packages/modules.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done