Skip to content

Commit

Permalink
[teamshow]: refactor teamshow to use state db information (sonic-net#…
Browse files Browse the repository at this point in the history
…1049)

previously, teamshow needs to read data from teamdctl command
output. As teamdmon daemon has been developed to put data into
state db. Now, teamshow can get data from state db.

Also remove teamshow command and incoporate as part of show
command.

Also add extensive unit tests

Signed-off-by: Guohan Lu <lguohan@gmail.com>
  • Loading branch information
lguohan authored Aug 12, 2020
1 parent 91986b7 commit 84b2c5b
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 74 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
'pddf_thermalutil',
'pddf_ledutil',
'show',
'show.interfaces',
'sonic_installer',
'sonic_installer.bootloader',
'tests',
Expand Down Expand Up @@ -102,7 +103,6 @@
'scripts/route_check_test.sh',
'scripts/sfpshow',
'scripts/syseeprom-to-json',
'scripts/teamshow',
'scripts/tempershow',
'scripts/update_json.py',
'scripts/warm-reboot',
Expand Down
12 changes: 4 additions & 8 deletions show/interfaces.py → show/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from tabulate import tabulate

import utilities_common.cli as clicommon
import portchannel

def try_convert_interfacename_from_alias(ctx, db, interfacename):
"""try to convert interface name from alias"""
Expand Down Expand Up @@ -88,14 +89,6 @@ def naming_mode(verbose):

click.echo(clicommon.get_interface_naming_mode())

# 'portchannel' subcommand ("show interfaces portchannel")
@interfaces.command()
@click.option('--verbose', is_flag=True, help="Enable verbose output")
def portchannel(verbose):
"""Show PortChannel information"""
cmd = "sudo teamshow"
clicommon.run_command(cmd, display_cmd=verbose)

@interfaces.command()
@click.argument('interfacename', required=False)
@click.option('--verbose', is_flag=True, help="Enable verbose output")
Expand Down Expand Up @@ -261,6 +254,8 @@ def expected(db, interfacename):

click.echo(tabulate(body, header))

interfaces.add_command(portchannel.portchannel)

#
# transceiver group (show interfaces trasceiver ...)
#
Expand Down Expand Up @@ -389,3 +384,4 @@ def rif(interface, period, verbose):
cmd += " -i {}".format(interface)

clicommon.run_command(cmd, display_cmd=verbose)

90 changes: 45 additions & 45 deletions scripts/teamshow → show/interfaces/portchannel.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#!/usr/bin/python
import click

from tabulate import tabulate
from natsort import natsorted

import utilities_common.cli as clicommon

"""
Script to show LAG and LAG member status in a summary view
Expand All @@ -19,31 +24,23 @@
"""

import json
import os
import swsssdk
import subprocess
import sys
from tabulate import tabulate
from natsort import natsorted

PORT_CHANNEL_APPL_TABLE_PREFIX = "LAG_TABLE:"
PORT_CHANNEL_CFG_TABLE_PREFIX = "PORTCHANNEL|"
PORT_CHANNEL_STATE_TABLE_PREFIX = "LAG_TABLE|"
PORT_CHANNEL_STATUS_FIELD = "oper_status"

PORT_CHANNEL_MEMBER_APPL_TABLE_PREFIX = "LAG_MEMBER_TABLE:"
PORT_CHANNEL_MEMBER_STATE_TABLE_PREFIX = "LAG_MEMBER_TABLE|"
PORT_CHANNEL_MEMBER_STATUS_FIELD = "status"

class Teamshow(object):
def __init__(self):
def __init__(self, db):
self.teams = []
self.teamsraw = {}
self.summary = {}
self.err = None
# setup db connection
self.db = swsssdk.SonicV2Connector(host="127.0.0.1")
self.db.connect(self.db.APPL_DB)
self.db.connect(self.db.CONFIG_DB)
self.db = db.db
self.db2 = db

def get_portchannel_names(self):
"""
Expand Down Expand Up @@ -76,15 +73,15 @@ def get_teamdctl(self):
Get teams raw data from teamdctl.
Command: 'teamdctl <teamdevname> state dump'.
"""

team_keys = self.db.keys(self.db.STATE_DB, PORT_CHANNEL_STATE_TABLE_PREFIX+"*")
if team_keys is None:
return
_teams = [key[len(PORT_CHANNEL_STATE_TABLE_PREFIX):] for key in team_keys]

for team in self.teams:
teamdctl_cmd = 'teamdctl ' + team + ' state dump'
p = subprocess.Popen(teamdctl_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
rc = p.wait()
if rc == 0:
self.teamsraw[self.get_team_id(team)] = output
else:
self.err = err
if team in _teams:
self.teamsraw[self.get_team_id(team)] = self.db.get_all(self.db.STATE_DB, PORT_CHANNEL_STATE_TABLE_PREFIX+team)

def get_teamshow_result(self):
"""
Expand All @@ -98,9 +95,10 @@ def get_teamshow_result(self):
self.summary[team_id] = info
self.summary[team_id]['ports'] = ''
continue
json_info = json.loads(self.teamsraw[team_id])
info['protocol'] = json_info['setup']['runner_name'].upper()
info['protocol'] += '(A)' if json_info['runner']['active'] else '(I)'
state = self.teamsraw[team_id]
info['protocol'] = "LACP"
info['protocol'] += "(A)" if state['runner.active'] == "true" else '(I)'

portchannel_status = self.get_portchannel_status(team)
if portchannel_status is None:
info['protocol'] += '(N/A)'
Expand All @@ -112,14 +110,20 @@ def get_teamshow_result(self):
info['protocol'] += '(N/A)'

info['ports'] = ""
if 'ports' not in json_info:
member_keys = self.db.keys(self.db.STATE_DB, PORT_CHANNEL_MEMBER_STATE_TABLE_PREFIX+team+'|*')
if member_keys is None:
info['ports'] = 'N/A'
else:
for port in json_info['ports']:
ports = [key[len(PORT_CHANNEL_MEMBER_STATE_TABLE_PREFIX+team+'|'):] for key in member_keys]
for port in ports:
status = self.get_portchannel_member_status(team, port)
selected = json_info["ports"][port]["runner"]["selected"]

info["ports"] += port + "("
pstate = self.db.get_all(self.db.STATE_DB, PORT_CHANNEL_MEMBER_STATE_TABLE_PREFIX+team+'|'+port)
selected = True if pstate['runner.aggregator.selected'] == "true" else False
if clicommon.get_interface_naming_mode() == "alias":
alias = clicommon.InterfaceAliasConverter(self.db2).name_to_alias(port)
info["ports"] += alias + "("
else:
info["ports"] += port + "("
info["ports"] += "S" if selected else "D"
if status is None or (status == "enabled" and not selected) or (status == "disabled" and selected):
info["ports"] += "*"
Expand All @@ -140,18 +144,14 @@ def display_summary(self):
output.append([team_id, 'PortChannel'+team_id, self.summary[team_id]['protocol'], self.summary[team_id]['ports']])
print(tabulate(output, header))

def main():
if os.geteuid() != 0:
exit("This utility must be run as root")

try:
team = Teamshow()
team.get_portchannel_names()
team.get_teamdctl()
team.get_teamshow_result()
team.display_summary()
except Exception as e:
sys.exit(e.message)

if __name__ == "__main__":
main()
# 'portchannel' subcommand ("show interfaces portchannel")
@click.command()
@click.option('--verbose', is_flag=True, help="Enable verbose output")
@clicommon.pass_db
def portchannel(db, verbose):
"""Show PortChannel information"""
team = Teamshow(db)
team.get_portchannel_names()
team.get_teamdctl()
team.get_teamshow_result()
team.display_summary()
45 changes: 45 additions & 0 deletions tests/interfaces_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import traceback

from click.testing import CliRunner

Expand Down Expand Up @@ -68,6 +69,30 @@
etp29 ARISTA01T1 Ethernet1 None 10.250.0.51 LeafRouter
"""

show_interfaces_portchannel_output="""\
Flags: A - active, I - inactive, Up - up, Dw - Down, N/A - not available,
S - selected, D - deselected, * - not synced
No. Team Dev Protocol Ports
----- --------------- ----------- --------------
0001 PortChannel0001 LACP(A)(Dw) Ethernet112(D)
0002 PortChannel0002 LACP(A)(Up) Ethernet116(S)
0003 PortChannel0003 LACP(A)(Up) Ethernet120(S)
0004 PortChannel0004 LACP(A)(Up) N/A
1001 PortChannel1001 N/A
"""

show_interfaces_portchannel_in_alias_mode_output="""\
Flags: A - active, I - inactive, Up - up, Dw - Down, N/A - not available,
S - selected, D - deselected, * - not synced
No. Team Dev Protocol Ports
----- --------------- ----------- --------
0001 PortChannel0001 LACP(A)(Dw) etp29(D)
0002 PortChannel0002 LACP(A)(Up) etp30(S)
0003 PortChannel0003 LACP(A)(Up) etp31(S)
0004 PortChannel0004 LACP(A)(Up) N/A
1001 PortChannel1001 N/A
"""

class TestInterfaces(object):
@classmethod
def setup_class(cls):
Expand Down Expand Up @@ -170,6 +195,26 @@ def test_show_interfaces_neighbor_expected_Ethernet0(self):
assert result.exit_code == 0
assert result.output.rstrip() == "No neighbor information available for interface Ethernet0"

def test_show_interfaces_portchannel(self):
runner = CliRunner()
result = runner.invoke(show.cli.commands["interfaces"].commands["portchannel"], [])
print(result.exit_code)
print(result.output)
traceback.print_tb(result.exc_info[2])
assert result.exit_code == 0
assert result.output == show_interfaces_portchannel_output

def test_show_interfaces_portchannel_in_alias_mode(self):
runner = CliRunner()
os.environ['SONIC_CLI_IFACE_MODE'] = "alias"
result = runner.invoke(show.cli.commands["interfaces"].commands["portchannel"], [])
os.environ['SONIC_CLI_IFACE_MODE'] = "default"
print(result.exit_code)
print(result.output)
traceback.print_tb(result.exc_info[2])
assert result.exit_code == 0
assert result.output == show_interfaces_portchannel_in_alias_mode_output

@classmethod
def teardown_class(cls):
print("TEARDOWN")
8 changes: 4 additions & 4 deletions tests/intfutil_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
Ethernet116 89,90,91,92 40G 9100 rs etp30 PortChannel0002 up up N/A off
Ethernet120 101,102,103,104 40G 9100 rs etp31 PortChannel0003 up up N/A off
Ethernet124 97,98,99,100 40G 9100 rs etp32 PortChannel0004 up up N/A off
PortChannel0001 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A
PortChannel0002 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A
PortChannel0003 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A
PortChannel0004 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A
PortChannel0001 N/A 40G 9100 N/A N/A routed down up N/A N/A
PortChannel0002 N/A 40G 9100 N/A N/A routed up up N/A N/A
PortChannel0003 N/A 40G 9100 N/A N/A routed up up N/A N/A
PortChannel0004 N/A 40G 9100 N/A N/A routed up up N/A N/A
PortChannel1001 N/A 40G 9100 N/A N/A routed N/A N/A N/A N/A
"""

Expand Down
32 changes: 32 additions & 0 deletions tests/mock_tables/appl_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,37 @@
"index": "200",
"line_speed": "50000",
"system_speed": "25000"
},
"LAG_MEMBER_TABLE:PortChannel0001:Ethernet112": {
"status": "disabled"
},
"LAG_MEMBER_TABLE:PortChannel0002:Ethernet116": {
"status": "enabled"
},
"LAG_MEMBER_TABLE:PortChannel0003:Ethernet120": {
"status": "enabled"
},
"LAG_MEMBER_TABLE:PortChannel0004:Ethernet124": {
"status": "enabled"
},
"LAG_TABLE:PortChannel0001": {
"admin_status": "up",
"mtu": "9100",
"oper_status": "down"
},
"LAG_TABLE:PortChannel0002": {
"admin_status": "up",
"mtu": "9100",
"oper_status": "up"
},
"LAG_TABLE:PortChannel0003": {
"admin_status": "up",
"mtu": "9100",
"oper_status": "up"
},
"LAG_TABLE:PortChannel0004": {
"admin_status": "up",
"mtu": "9100",
"oper_status": "up"
}
}
Loading

0 comments on commit 84b2c5b

Please sign in to comment.