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

Fix multi-asic support to PFC config/show #3521

Merged
merged 2 commits into from
Sep 5, 2024
Merged
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
18 changes: 14 additions & 4 deletions config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6483,8 +6483,9 @@ def pfc(ctx):
@pfc.command()
@click.argument('interface_name', metavar='<interface_name>', required=True)
@click.argument('status', type=click.Choice(['on', 'off']))
@multi_asic_util.multi_asic_click_option_namespace
@click.pass_context
def asymmetric(ctx, interface_name, status):
def asymmetric(ctx, interface_name, status, namespace):
"""Set asymmetric PFC configuration."""
# Get the config_db connector
config_db = ctx.obj['config_db']
Expand All @@ -6494,7 +6495,11 @@ def asymmetric(ctx, interface_name, status):
if interface_name is None:
ctx.fail("'interface_name' is None!")

clicommon.run_command(['pfc', 'config', 'asymmetric', str(status), str(interface_name)])
cmd = ['pfc', 'config', 'asymmetric', str(status), str(interface_name)]
if namespace is not None:
cmd += ['-n', str(namespace)]

clicommon.run_command(cmd)

#
# 'pfc priority' command ('config interface pfc priority ...')
Expand All @@ -6504,8 +6509,9 @@ def asymmetric(ctx, interface_name, status):
@click.argument('interface_name', metavar='<interface_name>', required=True)
@click.argument('priority', type=click.Choice([str(x) for x in range(8)]))
@click.argument('status', type=click.Choice(['on', 'off']))
@multi_asic_util.multi_asic_click_option_namespace
@click.pass_context
def priority(ctx, interface_name, priority, status):
def priority(ctx, interface_name, priority, status, namespace):
"""Set PFC priority configuration."""
# Get the config_db connector
config_db = ctx.obj['config_db']
Expand All @@ -6515,7 +6521,11 @@ def priority(ctx, interface_name, priority, status):
if interface_name is None:
ctx.fail("'interface_name' is None!")

clicommon.run_command(['pfc', 'config', 'priority', str(status), str(interface_name), str(priority)])
cmd = ['pfc', 'config', 'priority', str(status), str(interface_name), str(priority)]
if namespace is not None:
cmd += ['-n', str(namespace)]

clicommon.run_command(cmd)

#
# 'buffer' group ('config buffer ...')
Expand Down
132 changes: 78 additions & 54 deletions pfc/main.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,63 @@
#!/usr/bin/env python3
import os
import click
from swsscommon.swsscommon import ConfigDBConnector
import json
from sonic_py_common import multi_asic
from tabulate import tabulate
from natsort import natsorted
from utilities_common import multi_asic as multi_asic_util

# Constants
ALL_PRIORITIES = [str(x) for x in range(8)]
PRIORITY_STATUS = ['on', 'off']
PORT_TABLE_NAME = "PORT"
PORT_QOS_MAP_TABLE_NAME = "PORT_QOS_MAP"


class Pfc(object):
def __init__(self, cfgdb=None):
self.cfgdb = cfgdb
def __init__(self, namespace=None):
self.multi_asic = multi_asic_util.MultiAsic(namespace_option=namespace)
self.config_db = None

# For unit testing
self.updated_port_tables = {}
self.test_filename = '/tmp/pfc_testdata.json'

def dump_config_to_json(self, table_name, namespace):
"""
This function dumps the current config in a JSON file for unit testing.
"""
# Only dump files in unit testing mode
if os.environ["UTILITIES_UNIT_TESTING"] != "2":
return

if namespace not in self.updated_port_tables.keys():
self.updated_port_tables[namespace] = {}

self.updated_port_tables[namespace][table_name] = self.config_db.get_table(table_name)
with open(self.test_filename, "w") as fd:
json.dump(self.updated_port_tables, fd)

@multi_asic_util.run_on_multi_asic
def configPfcAsym(self, interface, pfc_asym):
"""
PFC handler to configure asymmetric PFC.
"""
configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
configdb.connect()

configdb.mod_entry("PORT", interface, {'pfc_asym': pfc_asym})
self.config_db.mod_entry(PORT_TABLE_NAME, interface, {'pfc_asym': pfc_asym})
self.dump_config_to_json(PORT_TABLE_NAME, self.multi_asic.current_namespace)

@multi_asic_util.run_on_multi_asic
def showPfcAsym(self, interface):
"""
PFC handler to display asymmetric PFC information.
"""
namespace_str = f"Namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else ''
header = ('Interface', 'Asymmetric')

configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
configdb.connect()

if interface:
db_keys = configdb.keys(configdb.CONFIG_DB, 'PORT|{0}'.format(interface))
db_keys = self.config_db.keys(self.config_db.CONFIG_DB, 'PORT|{0}'.format(interface))
else:
db_keys = configdb.keys(configdb.CONFIG_DB, 'PORT|*')
db_keys = self.config_db.keys(self.config_db.CONFIG_DB, 'PORT|*')

table = []

Expand All @@ -43,36 +67,35 @@ def showPfcAsym(self, interface):
key = i.split('|')[-1]

if key and key.startswith('Ethernet'):
entry = configdb.get_entry('PORT', key)
entry = self.config_db.get_entry(PORT_TABLE_NAME, key)
table.append([key, entry.get('pfc_asym', 'N/A')])

sorted_table = natsorted(table)

click.echo()
click.echo(namespace_str)
click.echo(tabulate(sorted_table, headers=header, tablefmt="simple", missingval=""))
click.echo()

@multi_asic_util.run_on_multi_asic
def configPfcPrio(self, status, interface, priority):
configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
configdb.connect()

if interface not in configdb.get_keys('PORT_QOS_MAP'):
if interface not in self.config_db.get_keys(PORT_QOS_MAP_TABLE_NAME):
click.echo('Cannot find interface {0}'.format(interface))
return

"""Current lossless priorities on the interface"""
entry = configdb.get_entry('PORT_QOS_MAP', interface)
entry = self.config_db.get_entry('PORT_QOS_MAP', interface)
enable_prio = entry.get('pfc_enable').split(',')

"""Avoid '' in enable_prio"""
enable_prio = [x.strip() for x in enable_prio if x.strip()]

namespace_str = f" for namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else ''
if status == 'on' and priority in enable_prio:
click.echo('Priority {0} has already been enabled on {1}'.format(priority, interface))
click.echo('Priority {0} has already been enabled on {1}{2}'.format(priority, interface, namespace_str))
return

if status == 'off' and priority not in enable_prio:
click.echo('Priority {0} is not enabled on {1}'.format(priority, interface))
click.echo('Priority {0} is not enabled on {1}{2}'.format(priority, interface, namespace_str))
return

if status == 'on':
Expand All @@ -82,92 +105,93 @@ def configPfcPrio(self, status, interface, priority):
enable_prio.remove(priority)

enable_prio.sort()
configdb.mod_entry("PORT_QOS_MAP", interface, {'pfc_enable': ','.join(enable_prio)})
self.config_db.mod_entry(PORT_QOS_MAP_TABLE_NAME, interface, {'pfc_enable': ','.join(enable_prio)})
self.dump_config_to_json(PORT_QOS_MAP_TABLE_NAME, self.multi_asic.current_namespace)

"""Show the latest PFC configuration"""
self.showPfcPrio(interface)

@multi_asic_util.run_on_multi_asic
def showPfcPrio(self, interface):
"""
PFC handler to display PFC enabled priority information.
"""
header = ('Interface', 'Lossless priorities')
table = []

configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
configdb.connect()

"""Get all the interfaces with QoS map information"""
intfs = configdb.get_keys('PORT_QOS_MAP')
intfs = self.config_db.get_keys('PORT_QOS_MAP')

"""The user specifies an interface but we cannot find it"""
namespace_str = f"Namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else ''
if interface and interface not in intfs:
click.echo('Cannot find interface {0}'.format(interface))
if multi_asic.is_multi_asic():
click.echo('Cannot find interface {0} for {1}'.format(interface, namespace_str))
else:
click.echo('Cannot find interface {0}'.format(interface))
return

if interface:
intfs = [interface]

for intf in intfs:
entry = configdb.get_entry('PORT_QOS_MAP', intf)
entry = self.config_db.get_entry('PORT_QOS_MAP', intf)
table.append([intf, entry.get('pfc_enable', 'N/A')])

sorted_table = natsorted(table)
click.echo()
click.echo(namespace_str)
click.echo(tabulate(sorted_table, headers=header, tablefmt="simple", missingval=""))
click.echo()



@click.group()
@click.pass_context
def cli(ctx):
def cli():
"""PFC Command Line"""
# Use the cfgdb object if given as input.
cfgdb = None if ctx.obj is None else ctx.obj.cfgdb

ctx.obj = {'pfc': Pfc(cfgdb)}

@cli.group()
@click.pass_context
def config(ctx):
def config():
"""Config PFC"""
pass


@cli.group()
@click.pass_context
def show(ctx):
def show():
"""Show PFC information"""
pass


@click.command()
@click.argument('status', type=click.Choice(PRIORITY_STATUS))
@click.argument('interface', type=click.STRING)
@click.pass_context
def configAsym(ctx, status, interface):
@multi_asic_util.multi_asic_click_option_namespace
def configAsym(status, interface, namespace):
"""Configure asymmetric PFC on a given port."""
ctx.obj['pfc'].configPfcAsym(interface, status)
Pfc(namespace).configPfcAsym(interface, status)


@click.command()
@click.argument('status', type=click.Choice(PRIORITY_STATUS))
@click.argument('interface', type=click.STRING)
@click.argument('priority', type=click.Choice(ALL_PRIORITIES))
@click.pass_context
def configPrio(ctx, status, interface, priority):
@multi_asic_util.multi_asic_click_option_namespace
def configPrio(status, interface, priority, namespace):
"""Configure PFC on a given priority."""
ctx.obj['pfc'].configPfcPrio(status, interface, priority)
Pfc(namespace).configPfcPrio(status, interface, priority)


@click.command()
@click.argument('interface', type=click.STRING, required=False)
@click.pass_context
def showAsym(ctx, interface):
@multi_asic_util.multi_asic_click_option_namespace
def showAsym(interface, namespace):
"""Show asymmetric PFC information"""
ctx.obj['pfc'].showPfcAsym(interface)
Pfc(namespace).showPfcAsym(interface)


@click.command()
@click.argument('interface', type=click.STRING, required=False)
@click.pass_context
def showPrio(ctx, interface):
@multi_asic_util.multi_asic_click_option_namespace
def showPrio(interface, namespace):
"""Show PFC priority information"""
ctx.obj['pfc'].showPfcPrio(interface)
Pfc(namespace).showPfcPrio(interface)


config.add_command(configAsym, "asymmetric")
config.add_command(configPrio, "priority")
Expand Down
10 changes: 8 additions & 2 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,27 +648,33 @@ def counters(namespace, display, verbose):

@pfc.command()
@click.argument('interface', type=click.STRING, required=False)
def priority(interface):
@multi_asic_util.multi_asic_click_option_namespace
def priority(interface, namespace):
"""Show pfc priority"""
cmd = ['pfc', 'show', 'priority']
if interface is not None and clicommon.get_interface_naming_mode() == "alias":
interface = iface_alias_converter.alias_to_name(interface)

if interface is not None:
cmd += [str(interface)]
if namespace is not None:
cmd += ['-n', str(namespace)]

run_command(cmd)

@pfc.command()
@click.argument('interface', type=click.STRING, required=False)
def asymmetric(interface):
@multi_asic_util.multi_asic_click_option_namespace
def asymmetric(interface, namespace):
"""Show asymmetric pfc"""
cmd = ['pfc', 'show', 'asymmetric']
if interface is not None and clicommon.get_interface_naming_mode() == "alias":
interface = iface_alias_converter.alias_to_name(interface)

if interface is not None:
cmd += [str(interface)]
if namespace is not None:
cmd += ['-n', str(namespace)]

run_command(cmd)

Expand Down
Loading
Loading