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

[teamshow]: refactor teamshow to use state db information #1049

Merged
merged 3 commits into from
Aug 12, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,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
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/portchannel.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#!/usr/bin/python
lguohan marked this conversation as resolved.
Show resolved Hide resolved
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"
}
}
88 changes: 88 additions & 0 deletions tests/mock_tables/state_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,93 @@
"DEBUG_COUNTER_CAPABILITIES|SWITCH_EGRESS_DROPS": {
"reasons": "[ACL_ANY,L2_ANY,L3_ANY]",
"count": "2"
},
"LAG_MEMBER_TABLE|PortChannel0001|Ethernet112": {
"runner.actor_lacpdu_info.state": "5",
"runner.state": "disabled",
"runner.partner_lacpdu_info.port": "0",
"runner.actor_lacpdu_info.port": "113",
"runner.selected": "false",
"runner.partner_lacpdu_info.state": "0",
"ifinfo.dev_addr": "52:54:00:f2:e1:23",
"runner.partner_lacpdu_info.system": "00:00:00:00:00:00",
"link_watches.list.link_watch_0.up": "false",
"runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23",
"runner.aggregator.selected": "false",
"runner.aggregator.id": "0",
"link.up": "false",
"ifinfo.ifindex": "98"
},
"LAG_MEMBER_TABLE|PortChannel0002|Ethernet116": {
"runner.actor_lacpdu_info.state": "61",
"runner.state": "current",
"runner.partner_lacpdu_info.port": "1",
"runner.actor_lacpdu_info.port": "117",
"runner.selected": "true",
"runner.partner_lacpdu_info.state": "61",
"ifinfo.dev_addr": "52:54:00:f2:e1:23",
"runner.partner_lacpdu_info.system": "1e:af:77:fc:79:ee",
"link_watches.list.link_watch_0.up": "false",
"runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23",
"runner.aggregator.selected": "true",
"runner.aggregator.id": "97",
"link.up": "true",
"ifinfo.ifindex": "97"
},
"LAG_MEMBER_TABLE|PortChannel0003|Ethernet120": {
"runner.actor_lacpdu_info.state": "61",
"runner.state": "current",
"runner.partner_lacpdu_info.port": "1",
"runner.actor_lacpdu_info.port": "121",
"runner.selected": "true",
"runner.partner_lacpdu_info.state": "61",
"ifinfo.dev_addr": "52:54:00:f2:e1:23",
"runner.partner_lacpdu_info.system": "16:0e:58:6f:3c:dd",
"link_watches.list.link_watch_0.up": "false",
"runner.actor_lacpdu_info.system": "52:54:00:f2:e1:23",
"runner.aggregator.selected": "true",
"runner.aggregator.id": "100",
"link.up": "true",
"ifinfo.ifindex": "100"
},
"LAG_TABLE|PortChannel0001": {
"runner.fallback": "false",
"team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23",
"team_device.ifinfo.ifindex": "71",
"setup.pid": "32",
"state": "ok",
"runner.fast_rate": "false",
"setup.kernel_team_mode_name": "loadbalance",
"runner.active": "true"
},
"LAG_TABLE|PortChannel0002": {
"runner.fallback": "false",
"team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23",
"team_device.ifinfo.ifindex": "72",
"setup.pid": "40",
"state": "ok",
"runner.fast_rate": "false",
"setup.kernel_team_mode_name": "loadbalance",
"runner.active": "true"
},
"LAG_TABLE|PortChannel0003": {
"runner.fallback": "false",
"team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23",
"team_device.ifinfo.ifindex": "73",
"setup.pid": "48",
"state": "ok",
"runner.fast_rate": "false",
"setup.kernel_team_mode_name": "loadbalance",
"runner.active": "true"
},
"LAG_TABLE|PortChannel0004": {
"runner.fallback": "false",
"team_device.ifinfo.dev_addr": "52:54:00:f2:e1:23",
"team_device.ifinfo.ifindex": "74",
"setup.pid": "56",
"state": "ok",
"runner.fast_rate": "false",
"setup.kernel_team_mode_name": "loadbalance",
"runner.active": "true"
}
}
Loading