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 5 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
52 changes: 52 additions & 0 deletions doc/Command-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
* [ECN](#ecn)
* [ECN show commands](#ecn-show-commands)
* [ECN config commands](#ecn-config-commands)
* [Gearbox](#gearbox)
* [Gearbox show commands](#gearbox-show-commands)
* [Interfaces](#interfaces)
* [Interface Show Commands](#interface-show-commands)
* [Interface Config Commands](#interface-config-commands)
Expand Down Expand Up @@ -2257,6 +2259,56 @@ The list of the WRED profile fields that are configurable is listed in the below

Go Back To [Beginning of the document](#) or [Beginning of this section](#ecn)

## Gearbox

This section explains all the Gearbox PHY show commands that are supported in SONiC.

### Gearbox show commands
This sub-section contains the show commands that are supported for gearbox phy.

**show gearbox interfaces status**

This command displays information about the gearbox phy interface lanes, speeds and status. Data is displayed for both MAC side and line side of the gearbox phy

- Usage:
```
show gearbox interfaces status
```

- Example:

```
home/admin# show gearbox interfaces status
PHY Id Interface MAC Lanes MAC Lane Speed PHY Lanes PHY Lane Speed Line Lanes Line Lane Speed Oper Admin
-------- ----------- ----------- ---------------- ----------- ---------------- ------------ ----------------- ------ -------
1 Ethernet0 25,26,27,28 10G 200,201 20G 206 40G up up
1 Ethernet4 29,30,31,32 10G 202,203 20G 207 40G up up
1 Ethernet8 33,34,35,36 10G 204,205 20G 208 40G up up

```

**show gearbox phys status**

This command displays basic information about the gearbox phys configured on the switch.

- Usage:
```
show gearbox phy status
jleveque marked this conversation as resolved.
Show resolved Hide resolved
```

- Example:

```
/home/admin# show gearbox phys status
PHY Id Name Firmware
-------- ------- ----------
1 sesto-1 v0.1

```

Go Back To [Beginning of the document](#) or [Beginning of this section](#gearbox)


## Update Device Hostname Configuration Commands

This sub-section of commands is used to change device hostname without traffic being impacted.
Expand Down
228 changes: 228 additions & 0 deletions scripts/gearboxutil
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#! /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:

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"
Loading