Skip to content
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

[show] Add support for SONiC Gearbox Manager via new gearboxutil utility #931

Merged
merged 8 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
225 changes: 225 additions & 0 deletions scripts/gearboxutil
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 # required by sonic-utilities-tests
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:

jleveque marked this conversation as resolved.
Show resolved Hide resolved
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):

jleveque marked this conversation as resolved.
Show resolved Hide resolved
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):

jleveque marked this conversation as resolved.
Show resolved Hide resolved
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:])
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
'scripts/fdbclear',
'scripts/fdbshow',
'scripts/filter_fdb_entries.py',
'scripts/gearboxutil',
'scripts/generate_dump',
'scripts/intfutil',
'scripts/intfstat',
Expand Down
37 changes: 37 additions & 0 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2839,6 +2839,43 @@ def pool(verbose):
cmd = "sudo natconfig -p"
run_command(cmd, display_cmd=verbose)

# Define GEARBOX commands only if GEARBOX is configured
app_db = SonicV2Connector(host='127.0.0.1')
app_db.connect(app_db.APPL_DB)
if app_db.keys(app_db.APPL_DB, '_GEARBOX_TABLE:phy:*'):

@cli.group(cls=AliasedGroup)
def gearbox():
"""Show gearbox info"""
pass

# 'phys' subcommand ("show gearbox phys")
@gearbox.group(cls=AliasedGroup)
def phys():
"""Show external PHY information"""
pass

# 'status' subcommand ("show gearbox phys status")
@phys.command()
@click.pass_context
def status(ctx):
"""Show gearbox phys status"""
run_command("gearboxutil phys status")
return

# 'interfaces' subcommand ("show gearbox interfaces")
@gearbox.group(cls=AliasedGroup)
def interfaces():
"""Show gearbox interfaces information"""
pass

# 'status' subcommand ("show gearbox interfaces status")
@interfaces.command()
@click.pass_context
def status(ctx):
"""Show gearbox interfaces status"""
run_command("gearboxutil interfaces status")
return

# 'bindings' subcommand ("show nat config bindings")
@config.command()
Expand Down
51 changes: 51 additions & 0 deletions sonic-utilities-tests/gearbox_test.py
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"
29 changes: 28 additions & 1 deletion sonic-utilities-tests/mock_tables/appl_db.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"PORT_TABLE:Ethernet0": {
"index": "0",
"lanes": "0",
"alias": "Ethernet0",
"description": "ARISTA01T2:Ethernet1",
Expand All @@ -11,16 +12,42 @@
"admin_status": "up"
},
"PORT_TABLE:Ethernet200": {
"index": "200",
"lanes": "200,201,202,203",
"alias": "Ethernet200",
"description": "Ethernet200",
"speed": "100000",
"oper_status": "down",
"fec": "rs",
"mtu": "9100",
"pfc_asym": "off"
"pfc_asym": "off",
"admin_status": "up"
},
"INTF_TABLE:Ethernet0.10": {
"admin_status": "up"
},
"_GEARBOX_TABLE:phy:1": {
"name": "sesto-1",
"phy_id": "1",
"phy_oid": "0x21000000000002",
"firmware_major_version": "v0.2"
},
"_GEARBOX_TABLE:phy:2": {
"name": "sesto-2",
"phy_id": "2",
"phy_oid": "0x21000000000003",
"firmware_major_version": "v0.3"
},
"_GEARBOX_TABLE:interface:200": {
"name": "Ethernet200",
"index": "200",
"line_lanes": "304,305",
"phy_id": "1",
"system_lanes": "300,301,302,303"
},
"_GEARBOX_TABLE:phy:1:ports:200": {
"index": "200",
"line_speed": "50000",
"system_speed": "25000"
}
}
1 change: 1 addition & 0 deletions sonic-utilities-tests/mock_tables/asic_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
"SAI_SWITCH_ATTR_SRC_MAC_ADDRESS": "DE:AD:BE:EF:CA:FE"
}
}

jleveque marked this conversation as resolved.
Show resolved Hide resolved