Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
11 changes: 7 additions & 4 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2835,11 +2835,14 @@ def del_portchannel_member(ctx, portchannel_name, port_name):
@portchannel.group(cls=clicommon.AbbreviationGroup, name='retry-count')
@click.pass_context
def portchannel_retry_count(ctx):
pass
teamdctl_command = ["teamdctl"]
if ctx.obj["namespace"] != DEFAULT_NAMESPACE:
teamdctl_command += ["-n", ctx.obj['namespace'].removeprefix("asic")]
ctx.obj["teamdctl_command"] = teamdctl_command

def check_if_retry_count_is_enabled(ctx, portchannel_name):
try:
proc = subprocess.Popen(["teamdctl", portchannel_name, "state", "item", "get", "runner.enable_retry_count_feature"], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc = subprocess.Popen(ctx.obj["teamdctl_command"] + [portchannel_name, "state", "item", "get", "runner.enable_retry_count_feature"], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = proc.communicate(timeout=10)
if proc.returncode != 0:
ctx.fail("Unable to determine if the retry count feature is enabled or not: {}".format(err.strip()))
Expand Down Expand Up @@ -2870,7 +2873,7 @@ def get_portchannel_retry_count(ctx, portchannel_name):
if not is_retry_count_enabled:
ctx.fail("Retry count feature is not enabled!")

proc = subprocess.Popen(["teamdctl", portchannel_name, "state", "item", "get", "runner.retry_count"], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc = subprocess.Popen(ctx.obj["teamdctl_command"] + [portchannel_name, "state", "item", "get", "runner.retry_count"], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = proc.communicate(timeout=10)
if proc.returncode != 0:
ctx.fail("Unable to get the retry count: {}".format(err.strip()))
Expand Down Expand Up @@ -2906,7 +2909,7 @@ def set_portchannel_retry_count(ctx, portchannel_name, retry_count):
if not is_retry_count_enabled:
ctx.fail("Retry count feature is not enabled!")

proc = subprocess.Popen(["teamdctl", portchannel_name, "state", "item", "set", "runner.retry_count", str(retry_count)], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc = subprocess.Popen(ctx.obj["teamdctl_command"] + [portchannel_name, "state", "item", "set", "runner.retry_count", str(retry_count)], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = proc.communicate(timeout=10)
if proc.returncode != 0:
ctx.fail("Unable to set the retry count: {}".format(err.strip()))
Expand Down
52 changes: 35 additions & 17 deletions scripts/teamd_increase_retry_count.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import subprocess
import json
from pyroute2 import netns
from scapy.config import conf
conf.ipv6_enabled = False
conf.verb = False
Expand All @@ -18,10 +19,10 @@
import argparse
import signal

from sonic_py_common import logger
from swsscommon.swsscommon import DBConnector, Table
from sonic_py_common import logger, multi_asic
from swsscommon.swsscommon import DBConnector, Table, SonicDBConfig, SonicDBKey

log = logger.Logger()
log = None
revertTeamdRetryCountChanges = False
DEFAULT_RETRY_COUNT = 3
EXTENDED_RETRY_COUNT = 5
Expand Down Expand Up @@ -88,9 +89,11 @@ def run(self):
sniff(stop_filter=self.lacpPacketCallback, iface=self.port, filter="ether proto {} and ether src {}".format(LACP_ETHERTYPE, self.targetMacAddress),
store=0, timeout=30, started_callback=self.sendReadyEvent.set)

def getPortChannels():
applDb = DBConnector("APPL_DB", 0)
configDb = DBConnector("CONFIG_DB", 0)
def getPortChannels(namespace=""):
key = SonicDBKey(namespace)
is_tcp_conn = False
applDb = DBConnector("APPL_DB", 0, is_tcp_conn, key)
configDb = DBConnector("CONFIG_DB", 0, is_tcp_conn, key)
portChannelTable = Table(applDb, "LAG_TABLE")
portChannels = portChannelTable.getKeys()
activePortChannels = []
Expand Down Expand Up @@ -159,12 +162,18 @@ def getPortChannels():

return set([portChannelData[x]["portChannel"] for x in portChannelData.keys() if portChannelData[x]["adminUp"]])

def getPortChannelConfig(portChannelName):
(processStdout, _) = getCmdOutput(["teamdctl", portChannelName, "state", "dump"])
def getPortChannelConfig(portChannelName, namespace=""):
teamdctl_command = ["teamdctl"]
if namespace:
teamdctl_command += ["-n", namespace.removeprefix("asic")]
(processStdout, _) = getCmdOutput(teamdctl_command + [portChannelName, "state", "dump"])
return json.loads(processStdout)

def getLldpNeighbors():
(processStdout, _) = getCmdOutput(["lldpctl", "-f", "json"])
def getLldpNeighbors(namespace=""):
container = "lldp"
if namespace:
container += namespace.removeprefix("asic")
(processStdout, _) = getCmdOutput(["docker", "exec", container, "lldpctl", "-f", "json"])
return json.loads(processStdout)

def craftLacpPacket(portChannelConfig, portName, isResetPacket=False, newVersion=True):
Expand Down Expand Up @@ -215,19 +224,23 @@ def getCmdOutput(cmd):
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
return proc.communicate()[0], proc.returncode

def main(probeOnly=False):
def main(probeOnly=False, namespace=""):
if os.geteuid() != 0:
log.log_error("Root privileges required for this operation", also_print_to_console=True)
sys.exit(1)
portChannels = getPortChannels()
if namespace:
netns.setns(namespace)
if multi_asic.is_multi_asic():
SonicDBConfig.initializeGlobalConfig()
portChannels = getPortChannels(namespace)
if not portChannels:
log.log_info("No port channels retrieved; exiting")
return
failedPortChannels = []
if probeOnly:
for portChannel in portChannels:
config = getPortChannelConfig(portChannel)
lldpInfo = getLldpNeighbors()
lldpInfo = getLldpNeighbors(namespace)
portChannelChecked = False
for portName in config["ports"].keys():
if not "runner" in config["ports"][portName] or \
Expand Down Expand Up @@ -288,19 +301,19 @@ def main(probeOnly=False):
global revertTeamdRetryCountChanges
signal.signal(signal.SIGUSR1, abortTeamdChanges)
signal.signal(signal.SIGTERM, abortTeamdChanges)
(_, rc) = getCmdOutput(["config", "portchannel", "retry-count", "get", list(portChannels)[0]])
(_, rc) = getCmdOutput(["config", "portchannel", "-n", namespace, "retry-count", "get", list(portChannels)[0]])
if rc == 0:
# Currently running on SONiC version with teamd retry count feature
for portChannel in portChannels:
getCmdOutput(["config", "portchannel", "retry-count", "set", portChannel, str(EXTENDED_RETRY_COUNT)])
getCmdOutput(["config", "portchannel", "-n", namespace, "retry-count", "set", portChannel, str(EXTENDED_RETRY_COUNT)])
pid = os.fork()
if pid == 0:
# Running in a new process, detached from parent process
while not revertTeamdRetryCountChanges:
time.sleep(15)
if revertTeamdRetryCountChanges:
for portChannel in portChannels:
getCmdOutput(["config", "portchannel", "retry-count", "set", portChannel, str(DEFAULT_RETRY_COUNT)])
getCmdOutput(["config", "portchannel", "-n", namespace, "retry-count", "set", portChannel, str(DEFAULT_RETRY_COUNT)])
else:
lacpPackets = []
revertLacpPackets = []
Expand All @@ -326,5 +339,10 @@ def main(probeOnly=False):
parser = argparse.ArgumentParser(description='Teamd retry count changer.')
parser.add_argument('--probe-only', action='store_true',
help='Probe the peer devices only, to verify that they support the teamd retry count feature')
parser.add_argument('-n', '--namespace', default="", type=str, help='namespace to use')
args = parser.parse_args()
main(args.probe_only)
log_identifier = "teamd_increase_retry_count"
if args.namespace:
log_identifier += f"_{args.namespace}"
log = logger.Logger(log_identifier=log_identifier)
main(args.probe_only, args.namespace)
12 changes: 6 additions & 6 deletions tests/portchannel_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ def __call__(self, *args, **kwargs):
def test_get_portchannel_retry_count_disabled(self, subprocessMock):
runner = CliRunner()
db = Db()
obj = {'db':db.cfgdb}
obj = {'db':db.cfgdb, 'teamdctl_command':['teamdctl']}

subprocessMock.retryCountEnabled = False

Expand All @@ -374,7 +374,7 @@ def test_get_portchannel_retry_count_disabled(self, subprocessMock):
def test_set_portchannel_retry_count_disabled(self, subprocessMock):
runner = CliRunner()
db = Db()
obj = {'db':db.cfgdb}
obj = {'db':db.cfgdb, 'teamdctl_command':['teamdctl']}

subprocessMock.retryCountEnabled = False

Expand All @@ -389,7 +389,7 @@ def test_set_portchannel_retry_count_disabled(self, subprocessMock):
def test_get_portchannel_retry_count_timeout(self, subprocessMock):
runner = CliRunner()
db = Db()
obj = {'db':db.cfgdb}
obj = {'db':db.cfgdb, 'teamdctl_command':['teamdctl']}

subprocessMock.retryCountEnabled = True
subprocessMock.timeout = True
Expand All @@ -406,7 +406,7 @@ def test_get_portchannel_retry_count_timeout(self, subprocessMock):
def test_set_portchannel_retry_count_timeout(self, subprocessMock):
runner = CliRunner()
db = Db()
obj = {'db':db.cfgdb}
obj = {'db':db.cfgdb, 'teamdctl_command':['teamdctl']}

subprocessMock.retryCountEnabled = True
subprocessMock.timeout = True
Expand All @@ -423,7 +423,7 @@ def test_set_portchannel_retry_count_timeout(self, subprocessMock):
def test_get_portchannel_retry_count(self, subprocessMock):
runner = CliRunner()
db = Db()
obj = {'db':db.cfgdb}
obj = {'db':db.cfgdb, 'teamdctl_command':['teamdctl']}

subprocessMock.retryCountEnabled = True

Expand All @@ -439,7 +439,7 @@ def test_get_portchannel_retry_count(self, subprocessMock):
def test_set_portchannel_retry_count(self, subprocessMock):
runner = CliRunner()
db = Db()
obj = {'db':db.cfgdb}
obj = {'db':db.cfgdb, 'teamdctl_command':['teamdctl']}

subprocessMock.retryCountEnabled = True

Expand Down
Loading