Skip to content

Commit 567d47b

Browse files
arista-hpandyamssonicbld
authored andcommitted
Fix multi-asic support to PFC config/show (sonic-net#3521)
* Add multi-asic support to pfc - Add namespace arg for show and config cmds for pfc - Replace test DB with JSON to support verification of multiple namespaces in unit test - Add unit tests for multi-asic behaviour - Added a test vector file for better test organization * Fix linter errors
1 parent 2fd5f15 commit 567d47b

File tree

6 files changed

+554
-73
lines changed

6 files changed

+554
-73
lines changed

config/main.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -6446,8 +6446,9 @@ def pfc(ctx):
64466446
@pfc.command()
64476447
@click.argument('interface_name', metavar='<interface_name>', required=True)
64486448
@click.argument('status', type=click.Choice(['on', 'off']))
6449+
@multi_asic_util.multi_asic_click_option_namespace
64496450
@click.pass_context
6450-
def asymmetric(ctx, interface_name, status):
6451+
def asymmetric(ctx, interface_name, status, namespace):
64516452
"""Set asymmetric PFC configuration."""
64526453
# Get the config_db connector
64536454
config_db = ctx.obj['config_db']
@@ -6457,7 +6458,11 @@ def asymmetric(ctx, interface_name, status):
64576458
if interface_name is None:
64586459
ctx.fail("'interface_name' is None!")
64596460

6460-
clicommon.run_command(['pfc', 'config', 'asymmetric', str(status), str(interface_name)])
6461+
cmd = ['pfc', 'config', 'asymmetric', str(status), str(interface_name)]
6462+
if namespace is not None:
6463+
cmd += ['-n', str(namespace)]
6464+
6465+
clicommon.run_command(cmd)
64616466

64626467
#
64636468
# 'pfc priority' command ('config interface pfc priority ...')
@@ -6467,8 +6472,9 @@ def asymmetric(ctx, interface_name, status):
64676472
@click.argument('interface_name', metavar='<interface_name>', required=True)
64686473
@click.argument('priority', type=click.Choice([str(x) for x in range(8)]))
64696474
@click.argument('status', type=click.Choice(['on', 'off']))
6475+
@multi_asic_util.multi_asic_click_option_namespace
64706476
@click.pass_context
6471-
def priority(ctx, interface_name, priority, status):
6477+
def priority(ctx, interface_name, priority, status, namespace):
64726478
"""Set PFC priority configuration."""
64736479
# Get the config_db connector
64746480
config_db = ctx.obj['config_db']
@@ -6478,7 +6484,11 @@ def priority(ctx, interface_name, priority, status):
64786484
if interface_name is None:
64796485
ctx.fail("'interface_name' is None!")
64806486

6481-
clicommon.run_command(['pfc', 'config', 'priority', str(status), str(interface_name), str(priority)])
6487+
cmd = ['pfc', 'config', 'priority', str(status), str(interface_name), str(priority)]
6488+
if namespace is not None:
6489+
cmd += ['-n', str(namespace)]
6490+
6491+
clicommon.run_command(cmd)
64826492

64836493
#
64846494
# 'buffer' group ('config buffer ...')

pfc/main.py

+78-54
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,63 @@
11
#!/usr/bin/env python3
2+
import os
23
import click
3-
from swsscommon.swsscommon import ConfigDBConnector
4+
import json
5+
from sonic_py_common import multi_asic
46
from tabulate import tabulate
57
from natsort import natsorted
8+
from utilities_common import multi_asic as multi_asic_util
69

10+
# Constants
711
ALL_PRIORITIES = [str(x) for x in range(8)]
812
PRIORITY_STATUS = ['on', 'off']
13+
PORT_TABLE_NAME = "PORT"
14+
PORT_QOS_MAP_TABLE_NAME = "PORT_QOS_MAP"
915

1016

1117
class Pfc(object):
12-
def __init__(self, cfgdb=None):
13-
self.cfgdb = cfgdb
18+
def __init__(self, namespace=None):
19+
self.multi_asic = multi_asic_util.MultiAsic(namespace_option=namespace)
20+
self.config_db = None
1421

22+
# For unit testing
23+
self.updated_port_tables = {}
24+
self.test_filename = '/tmp/pfc_testdata.json'
25+
26+
def dump_config_to_json(self, table_name, namespace):
27+
"""
28+
This function dumps the current config in a JSON file for unit testing.
29+
"""
30+
# Only dump files in unit testing mode
31+
if os.environ["UTILITIES_UNIT_TESTING"] != "2":
32+
return
33+
34+
if namespace not in self.updated_port_tables.keys():
35+
self.updated_port_tables[namespace] = {}
36+
37+
self.updated_port_tables[namespace][table_name] = self.config_db.get_table(table_name)
38+
with open(self.test_filename, "w") as fd:
39+
json.dump(self.updated_port_tables, fd)
40+
41+
@multi_asic_util.run_on_multi_asic
1542
def configPfcAsym(self, interface, pfc_asym):
1643
"""
1744
PFC handler to configure asymmetric PFC.
1845
"""
19-
configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
20-
configdb.connect()
21-
22-
configdb.mod_entry("PORT", interface, {'pfc_asym': pfc_asym})
46+
self.config_db.mod_entry(PORT_TABLE_NAME, interface, {'pfc_asym': pfc_asym})
47+
self.dump_config_to_json(PORT_TABLE_NAME, self.multi_asic.current_namespace)
2348

49+
@multi_asic_util.run_on_multi_asic
2450
def showPfcAsym(self, interface):
2551
"""
2652
PFC handler to display asymmetric PFC information.
2753
"""
54+
namespace_str = f"Namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else ''
2855
header = ('Interface', 'Asymmetric')
2956

30-
configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
31-
configdb.connect()
32-
3357
if interface:
34-
db_keys = configdb.keys(configdb.CONFIG_DB, 'PORT|{0}'.format(interface))
58+
db_keys = self.config_db.keys(self.config_db.CONFIG_DB, 'PORT|{0}'.format(interface))
3559
else:
36-
db_keys = configdb.keys(configdb.CONFIG_DB, 'PORT|*')
60+
db_keys = self.config_db.keys(self.config_db.CONFIG_DB, 'PORT|*')
3761

3862
table = []
3963

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

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

4973
sorted_table = natsorted(table)
5074

51-
click.echo()
75+
click.echo(namespace_str)
5276
click.echo(tabulate(sorted_table, headers=header, tablefmt="simple", missingval=""))
5377
click.echo()
5478

79+
@multi_asic_util.run_on_multi_asic
5580
def configPfcPrio(self, status, interface, priority):
56-
configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
57-
configdb.connect()
58-
59-
if interface not in configdb.get_keys('PORT_QOS_MAP'):
81+
if interface not in self.config_db.get_keys(PORT_QOS_MAP_TABLE_NAME):
6082
click.echo('Cannot find interface {0}'.format(interface))
6183
return
6284

6385
"""Current lossless priorities on the interface"""
64-
entry = configdb.get_entry('PORT_QOS_MAP', interface)
86+
entry = self.config_db.get_entry('PORT_QOS_MAP', interface)
6587
enable_prio = entry.get('pfc_enable').split(',')
6688

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

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

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

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

84107
enable_prio.sort()
85-
configdb.mod_entry("PORT_QOS_MAP", interface, {'pfc_enable': ','.join(enable_prio)})
108+
self.config_db.mod_entry(PORT_QOS_MAP_TABLE_NAME, interface, {'pfc_enable': ','.join(enable_prio)})
109+
self.dump_config_to_json(PORT_QOS_MAP_TABLE_NAME, self.multi_asic.current_namespace)
86110

87-
"""Show the latest PFC configuration"""
88-
self.showPfcPrio(interface)
89-
111+
@multi_asic_util.run_on_multi_asic
90112
def showPfcPrio(self, interface):
91113
"""
92114
PFC handler to display PFC enabled priority information.
93115
"""
94116
header = ('Interface', 'Lossless priorities')
95117
table = []
96118

97-
configdb = ConfigDBConnector() if self.cfgdb is None else self.cfgdb
98-
configdb.connect()
99-
100119
"""Get all the interfaces with QoS map information"""
101-
intfs = configdb.get_keys('PORT_QOS_MAP')
120+
intfs = self.config_db.get_keys('PORT_QOS_MAP')
102121

103122
"""The user specifies an interface but we cannot find it"""
123+
namespace_str = f"Namespace {self.multi_asic.current_namespace}" if multi_asic.is_multi_asic() else ''
104124
if interface and interface not in intfs:
105-
click.echo('Cannot find interface {0}'.format(interface))
125+
if multi_asic.is_multi_asic():
126+
click.echo('Cannot find interface {0} for {1}'.format(interface, namespace_str))
127+
else:
128+
click.echo('Cannot find interface {0}'.format(interface))
106129
return
107130

108131
if interface:
109132
intfs = [interface]
110133

111134
for intf in intfs:
112-
entry = configdb.get_entry('PORT_QOS_MAP', intf)
135+
entry = self.config_db.get_entry('PORT_QOS_MAP', intf)
113136
table.append([intf, entry.get('pfc_enable', 'N/A')])
114137

115138
sorted_table = natsorted(table)
116-
click.echo()
139+
click.echo(namespace_str)
117140
click.echo(tabulate(sorted_table, headers=header, tablefmt="simple", missingval=""))
118141
click.echo()
119-
142+
143+
120144
@click.group()
121-
@click.pass_context
122-
def cli(ctx):
145+
def cli():
123146
"""PFC Command Line"""
124-
# Use the cfgdb object if given as input.
125-
cfgdb = None if ctx.obj is None else ctx.obj.cfgdb
126147

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

129149
@cli.group()
130-
@click.pass_context
131-
def config(ctx):
150+
def config():
132151
"""Config PFC"""
133152
pass
134153

154+
135155
@cli.group()
136-
@click.pass_context
137-
def show(ctx):
156+
def show():
138157
"""Show PFC information"""
139158
pass
140159

160+
141161
@click.command()
142162
@click.argument('status', type=click.Choice(PRIORITY_STATUS))
143163
@click.argument('interface', type=click.STRING)
144-
@click.pass_context
145-
def configAsym(ctx, status, interface):
164+
@multi_asic_util.multi_asic_click_option_namespace
165+
def configAsym(status, interface, namespace):
146166
"""Configure asymmetric PFC on a given port."""
147-
ctx.obj['pfc'].configPfcAsym(interface, status)
167+
Pfc(namespace).configPfcAsym(interface, status)
168+
148169

149170
@click.command()
150171
@click.argument('status', type=click.Choice(PRIORITY_STATUS))
151172
@click.argument('interface', type=click.STRING)
152173
@click.argument('priority', type=click.Choice(ALL_PRIORITIES))
153-
@click.pass_context
154-
def configPrio(ctx, status, interface, priority):
174+
@multi_asic_util.multi_asic_click_option_namespace
175+
def configPrio(status, interface, priority, namespace):
155176
"""Configure PFC on a given priority."""
156-
ctx.obj['pfc'].configPfcPrio(status, interface, priority)
177+
Pfc(namespace).configPfcPrio(status, interface, priority)
178+
157179

158180
@click.command()
159181
@click.argument('interface', type=click.STRING, required=False)
160-
@click.pass_context
161-
def showAsym(ctx, interface):
182+
@multi_asic_util.multi_asic_click_option_namespace
183+
def showAsym(interface, namespace):
162184
"""Show asymmetric PFC information"""
163-
ctx.obj['pfc'].showPfcAsym(interface)
185+
Pfc(namespace).showPfcAsym(interface)
186+
164187

165188
@click.command()
166189
@click.argument('interface', type=click.STRING, required=False)
167-
@click.pass_context
168-
def showPrio(ctx, interface):
190+
@multi_asic_util.multi_asic_click_option_namespace
191+
def showPrio(interface, namespace):
169192
"""Show PFC priority information"""
170-
ctx.obj['pfc'].showPfcPrio(interface)
193+
Pfc(namespace).showPfcPrio(interface)
194+
171195

172196
config.add_command(configAsym, "asymmetric")
173197
config.add_command(configPrio, "priority")

show/main.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -645,27 +645,33 @@ def counters(namespace, display, verbose):
645645

646646
@pfc.command()
647647
@click.argument('interface', type=click.STRING, required=False)
648-
def priority(interface):
648+
@multi_asic_util.multi_asic_click_option_namespace
649+
def priority(interface, namespace):
649650
"""Show pfc priority"""
650651
cmd = ['pfc', 'show', 'priority']
651652
if interface is not None and clicommon.get_interface_naming_mode() == "alias":
652653
interface = iface_alias_converter.alias_to_name(interface)
653654

654655
if interface is not None:
655656
cmd += [str(interface)]
657+
if namespace is not None:
658+
cmd += ['-n', str(namespace)]
656659

657660
run_command(cmd)
658661

659662
@pfc.command()
660663
@click.argument('interface', type=click.STRING, required=False)
661-
def asymmetric(interface):
664+
@multi_asic_util.multi_asic_click_option_namespace
665+
def asymmetric(interface, namespace):
662666
"""Show asymmetric pfc"""
663667
cmd = ['pfc', 'show', 'asymmetric']
664668
if interface is not None and clicommon.get_interface_naming_mode() == "alias":
665669
interface = iface_alias_converter.alias_to_name(interface)
666670

667671
if interface is not None:
668672
cmd += [str(interface)]
673+
if namespace is not None:
674+
cmd += ['-n', str(namespace)]
669675

670676
run_command(cmd)
671677

0 commit comments

Comments
 (0)