forked from sonic-net/sonic-buildimage
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[show] Add support for SONiC Gearbox Manager via new gearboxutil util…
…ity (sonic-net#931) * add and modify command line utilities to support gearbox phy * added build time mock unit tests HLD is located at https://github.com/Azure/SONiC/blob/b817a12fd89520d3fd26bbc5897487928e7f6de7/doc/gearbox/gearbox_mgr_design.md Signed-off-by: syd.logan@broadcom.com
- Loading branch information
Showing
6 changed files
with
394 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
#! /usr/bin/python | ||
|
||
import swsssdk | ||
import sys | ||
from tabulate import tabulate | ||
from natsort import natsorted | ||
|
||
import os | ||
|
||
# mock the redis for unit test purposes # | ||
try: | ||
if os.environ["UTILITIES_UNIT_TESTING"] == "1": | ||
modules_path = os.path.join(os.path.dirname(__file__), "..") | ||
tests_path = os.path.join(modules_path, "sonic-utilities-tests") | ||
sys.path.insert(0, modules_path) | ||
sys.path.insert(0, tests_path) | ||
import mock_tables.dbconnector | ||
client = mock_tables.dbconnector.redis.StrictRedis() | ||
if client.keys() is None: | ||
raise Exception("Invalid mock_table keys") | ||
except KeyError: | ||
pass | ||
|
||
# ========================== Common gearbox-utils logic ========================== | ||
|
||
GEARBOX_TABLE_PHY_PREFIX = "_GEARBOX_TABLE:phy:{}" | ||
GEARBOX_TABLE_INTERFACE_PREFIX = "_GEARBOX_TABLE:interface:{}" | ||
GEARBOX_TABLE_PORT_PREFIX = "_GEARBOX_TABLE:phy:{}:ports:{}" | ||
|
||
PORT_TABLE_ETHERNET_PREFIX = "PORT_TABLE:{}" | ||
|
||
PHY_NAME = "name" | ||
PHY_ID = "phy_id" | ||
PHY_FIRMWARE_MAJOR_VERSION = "firmware_major_version" | ||
PHY_LINE_LANES = "line_lanes" | ||
PHY_SYSTEM_LANES = "system_lanes" | ||
|
||
PORT_OPER_STATUS = "oper_status" | ||
PORT_ADMIN_STATUS = "admin_status" | ||
PORT_SYSTEM_SPEED = "system_speed" | ||
PORT_LINE_SPEED = "line_speed" | ||
|
||
INTF_NAME = "name" | ||
INTF_LANES = "lanes" | ||
INTF_SPEED = "speed" | ||
|
||
def get_appl_key_attr(db, key, attr, lane_count=1): | ||
""" | ||
Get APPL_DB key attribute | ||
""" | ||
|
||
val = db.get(db.APPL_DB, key, attr) | ||
if val is None: | ||
return "N/A" | ||
|
||
if "speed" in attr: | ||
if val == "0": | ||
return "N/A" | ||
|
||
speed = int(val[:-3]) | ||
|
||
if (speed % lane_count == 0): | ||
speed = speed // lane_count | ||
else: | ||
return "N/A" | ||
|
||
val = '{}G'.format(str(speed)) | ||
|
||
return val | ||
|
||
def db_connect_appl(): | ||
appl_db = swsssdk.SonicV2Connector(host='127.0.0.1') | ||
if appl_db is None: | ||
return None | ||
appl_db.connect(appl_db.APPL_DB) | ||
return appl_db | ||
|
||
def db_connect_state(): | ||
""" | ||
Connect to REDIS STATE DB and get optics info | ||
""" | ||
state_db = swsssdk.SonicV2Connector(host='127.0.0.1') | ||
if state_db is None: | ||
return None | ||
state_db.connect(state_db.STATE_DB, False) # Make one attempt only | ||
return state_db | ||
|
||
def appl_db_keys_get(appl_db): | ||
""" | ||
Get APPL_DB Keys | ||
""" | ||
return appl_db.keys(appl_db.APPL_DB, GEARBOX_TABLE_PHY_PREFIX.format("*")) | ||
|
||
def appl_db_interface_keys_get(appl_db): | ||
""" | ||
Get APPL_DB Keys | ||
""" | ||
return appl_db.keys(appl_db.APPL_DB, GEARBOX_TABLE_INTERFACE_PREFIX.format("*")) | ||
|
||
# ========================== phy-status logic ========================== | ||
|
||
phy_header_status = ['PHY Id', 'Name', 'Firmware'] | ||
|
||
class PhyStatus(object): | ||
|
||
def display_phy_status(self, appl_db_keys): | ||
""" | ||
Generate phy status output | ||
""" | ||
table = [] | ||
key = [] | ||
|
||
for key in appl_db_keys: | ||
if 'lanes' in key or 'ports' in key: | ||
continue | ||
list_items = key.split(':') | ||
phy_id = list_items[2] | ||
data_row = ( | ||
phy_id, | ||
get_appl_key_attr(self.appl_db, GEARBOX_TABLE_PHY_PREFIX.format(phy_id), PHY_NAME), | ||
get_appl_key_attr(self.appl_db, GEARBOX_TABLE_PHY_PREFIX.format(phy_id), PHY_FIRMWARE_MAJOR_VERSION)) | ||
table.append(data_row) | ||
|
||
# Sorting and tabulating the result table. | ||
sorted_table = natsorted(table) | ||
print tabulate(sorted_table, phy_header_status, tablefmt="simple", stralign='right') | ||
|
||
def __init__(self): | ||
self.appl_db = db_connect_appl() | ||
if self.appl_db is None: | ||
return | ||
|
||
appl_db_keys = appl_db_keys_get(self.appl_db) | ||
if appl_db_keys is None: | ||
return | ||
|
||
self.display_phy_status(appl_db_keys) | ||
|
||
# ========================== interface-status logic ========================== | ||
|
||
intf_header_status = ['PHY Id', 'Interface', 'MAC Lanes', 'MAC Lane Speed', 'PHY Lanes', 'PHY Lane Speed', 'Line Lanes', 'Line Lane Speed', 'Oper', 'Admin'] | ||
|
||
class InterfaceStatus(object): | ||
|
||
def display_intf_status(self, appl_db_keys): | ||
""" | ||
Generate phy status output | ||
""" | ||
table = [] | ||
key = [] | ||
|
||
for key in appl_db_keys: | ||
list_items = key.split(':') | ||
index = list_items[2] | ||
|
||
name = get_appl_key_attr(self.appl_db, GEARBOX_TABLE_INTERFACE_PREFIX.format(index), INTF_NAME), | ||
name = name[0] | ||
|
||
mac_lanes = get_appl_key_attr(self.appl_db, PORT_TABLE_ETHERNET_PREFIX.format(name), INTF_LANES) | ||
lanes = mac_lanes.split(',') | ||
lane_count = 0 | ||
for lane in lanes: | ||
lane_count += 1 | ||
|
||
phy_id = get_appl_key_attr(self.appl_db, GEARBOX_TABLE_INTERFACE_PREFIX.format(index), PHY_ID) | ||
|
||
data_row = ( | ||
phy_id, | ||
name, | ||
mac_lanes, | ||
get_appl_key_attr(self.appl_db, PORT_TABLE_ETHERNET_PREFIX.format(name), INTF_SPEED, lane_count), | ||
get_appl_key_attr(self.appl_db, GEARBOX_TABLE_INTERFACE_PREFIX.format(index), PHY_SYSTEM_LANES), | ||
get_appl_key_attr(self.appl_db, GEARBOX_TABLE_PORT_PREFIX.format(phy_id, index), PORT_SYSTEM_SPEED), | ||
get_appl_key_attr(self.appl_db, GEARBOX_TABLE_INTERFACE_PREFIX.format(index), PHY_LINE_LANES), | ||
get_appl_key_attr(self.appl_db, GEARBOX_TABLE_PORT_PREFIX.format(phy_id, index), PORT_LINE_SPEED), | ||
get_appl_key_attr(self.appl_db, PORT_TABLE_ETHERNET_PREFIX.format(name), PORT_OPER_STATUS), | ||
get_appl_key_attr(self.appl_db, PORT_TABLE_ETHERNET_PREFIX.format(name), PORT_ADMIN_STATUS)) | ||
|
||
table.append(data_row) | ||
|
||
# Sorting and tabulating the result table. | ||
sorted_table = natsorted(table) | ||
print tabulate(sorted_table, intf_header_status, tablefmt="simple", stralign='right') | ||
|
||
def __init__(self): | ||
self.appl_db = db_connect_appl() | ||
if self.appl_db is None: | ||
return | ||
|
||
appl_db_keys = appl_db_interface_keys_get(self.appl_db) | ||
if appl_db_keys is None: | ||
return | ||
|
||
self.display_intf_status(appl_db_keys) | ||
|
||
def main(args): | ||
""" | ||
phy status | ||
interfaces status | ||
interfaces counters | ||
""" | ||
|
||
if len(args) == 0: | ||
print "No valid arguments provided" | ||
return | ||
|
||
cmd1 = args[0] | ||
if cmd1 != "phys" and cmd1 != "interfaces": | ||
print "No valid command provided" | ||
return | ||
|
||
cmd2 = args[1] | ||
if cmd2 != "status" and cmd2 != "counters": | ||
print "No valid command provided" | ||
return | ||
|
||
if cmd1 == "phys" and cmd2 == "status": | ||
PhyStatus() | ||
elif cmd1 == "interfaces" and cmd2 == "status": | ||
InterfaceStatus() | ||
|
||
sys.exit(0) | ||
|
||
if __name__ == "__main__": | ||
main(sys.argv[1:]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import sys | ||
import os | ||
from click.testing import CliRunner | ||
from unittest import TestCase | ||
|
||
test_path = os.path.dirname(os.path.abspath(__file__)) | ||
modules_path = os.path.dirname(test_path) | ||
scripts_path = os.path.join(modules_path, "scripts") | ||
sys.path.insert(0, test_path) | ||
sys.path.insert(0, modules_path) | ||
|
||
import mock_tables.dbconnector # required by sonic-utilities-tests | ||
|
||
import show.main as show | ||
|
||
class TestGearbox(TestCase): | ||
@classmethod | ||
def setup_class(cls): | ||
print("SETUP") | ||
os.environ["PATH"] += os.pathsep + scripts_path | ||
os.environ["UTILITIES_UNIT_TESTING"] = "1" | ||
|
||
def setUp(self): | ||
self.runner = CliRunner() | ||
|
||
def test_gearbox_phys_status_validation(self): | ||
result = self.runner.invoke(show.cli.commands["gearbox"].commands["phys"].commands["status"], []) | ||
print >> sys.stderr, result.output | ||
expected_output = ( | ||
"PHY Id Name Firmware\n" | ||
"-------- ------- ----------\n" | ||
" 1 sesto-1 v0.2\n" | ||
" 2 sesto-2 v0.3" | ||
) | ||
self.assertEqual(result.output.strip(), expected_output) | ||
|
||
def test_gearbox_interfaces_status_validation(self): | ||
result = self.runner.invoke(show.cli.commands["gearbox"].commands["interfaces"].commands["status"], []) | ||
print >> sys.stderr, result.output | ||
expected_output = ( | ||
"PHY Id Interface MAC Lanes MAC Lane Speed PHY Lanes PHY Lane Speed Line Lanes Line Lane Speed Oper Admin\n" | ||
"-------- ----------- --------------- ---------------- --------------- ---------------- ------------ ----------------- ------ -------\n" | ||
" 1 Ethernet200 200,201,202,203 25G 300,301,302,303 25G 304,305 50G down up" | ||
) | ||
self.assertEqual(result.output.strip(), expected_output) | ||
|
||
@classmethod | ||
def teardown_class(cls): | ||
print("TEARDOWN") | ||
os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) | ||
os.environ["UTILITIES_UNIT_TESTING"] = "0" |
Oops, something went wrong.