Skip to content

Commit b082684

Browse files
authored
[ecn] Add tests for ecnconfig command (#1372)
- What I did Added unit tests for ecnconfig command Updated 'show ecn' command to use run_command wrapper Updated 'config ecn' command to allow configuration of drop probabilities Cleanup trailing spaces in ecnconfig script and add hooks to support unit tests - How to verify it Ran the new ecn_test.py and it passed Signed-off-by: Neetha John <nejo@microsoft.com>
1 parent 23e0920 commit b082684

File tree

8 files changed

+295
-21
lines changed

8 files changed

+295
-21
lines changed

config/main.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -3533,8 +3533,11 @@ def remove_reasons(counter_name, reasons, verbose):
35333533
@click.option('-ymin', metavar='<yellow threshold min>', type=int, help="Set yellow min threshold")
35343534
@click.option('-gmax', metavar='<green threshold max>', type=int, help="Set green max threshold")
35353535
@click.option('-gmin', metavar='<green threshold min>', type=int, help="Set green min threshold")
3536+
@click.option('-rdrop', metavar='<red drop probability>', type=click.IntRange(0, 100), help="Set red drop probability")
3537+
@click.option('-ydrop', metavar='<yellow drop probability>', type=click.IntRange(0, 100), help="Set yellow drop probability")
3538+
@click.option('-gdrop', metavar='<green drop probability>', type=click.IntRange(0, 100), help="Set green drop probability")
35363539
@click.option('-v', '--verbose', is_flag=True, help="Enable verbose output")
3537-
def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, verbose):
3540+
def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbose):
35383541
"""ECN-related configuration tasks"""
35393542
log.log_info("'ecn -profile {}' executing...".format(profile))
35403543
command = "ecnconfig -p %s" % profile
@@ -3544,6 +3547,9 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, verbose):
35443547
if ymin is not None: command += " -ymin %d" % ymin
35453548
if gmax is not None: command += " -gmax %d" % gmax
35463549
if gmin is not None: command += " -gmin %d" % gmin
3550+
if rdrop is not None: command += " -rdrop %d" % rdrop
3551+
if ydrop is not None: command += " -ydrop %d" % ydrop
3552+
if gdrop is not None: command += " -gdrop %d" % gdrop
35473553
if verbose: command += " -vv"
35483554
clicommon.run_command(command, display_cmd=verbose)
35493555

scripts/ecnconfig

+51-15
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,25 @@ ECN status:
4848
queue 3: on
4949
"""
5050
import argparse
51+
import json
5152
import os
5253
import sys
5354

5455
import swsssdk
5556
from tabulate import tabulate
5657

58+
# mock the redis for unit test purposes #
59+
try:
60+
if os.environ["UTILITIES_UNIT_TESTING"] == "2":
61+
modules_path = os.path.join(os.path.dirname(__file__), "..")
62+
tests_path = os.path.join(modules_path, "tests")
63+
sys.path.insert(0, modules_path)
64+
sys.path.insert(0, tests_path)
65+
import mock_tables.dbconnector
66+
67+
except KeyError:
68+
pass
69+
5770
WRED_PROFILE_TABLE_NAME = "WRED_PROFILE"
5871
WRED_CONFIG_FIELDS = {
5972
"gmax": "green_max_threshold",
@@ -78,11 +91,12 @@ lossless_queues = ['3', '4']
7891

7992
class EcnConfig(object):
8093
"""
81-
Process aclstat
94+
Process ecnconfig
8295
"""
83-
def __init__(self, verbose):
96+
def __init__(self, filename, verbose):
8497
self.ports = []
8598
self.queues = []
99+
self.filename = filename
86100
self.verbose = verbose
87101

88102
# Set up db connections
@@ -109,8 +123,8 @@ class EcnConfig(object):
109123

110124
for profile_name, profile_data in wred_profiles.items():
111125
if profile_name == profile:
112-
return profile_data
113-
126+
return profile_data
127+
114128
return None
115129

116130
def validate_profile_data(self, profile_data):
@@ -125,17 +139,17 @@ class EcnConfig(object):
125139
if 'threshold' in field:
126140
if value.isdigit() == False:
127141
print("Invalid %s (%s). %s should be an non-negative integer." % (key, value, key))
128-
result = False
142+
result = False
129143

130144
elif 'probability' in field:
131145
if value.isdigit() == False or int(value) < 0 or int(value) > 100:
132146
print("Invalid %s (%s). %s should be an integer between 0 and 100." % (key, value, key))
133147
result = False
134148

135149
if result == False:
136-
return result
150+
return result
137151

138-
# check if min threshold is no larger than max threshold
152+
# check if min threshold is no larger than max threshold
139153
colors = ['g', 'y', 'r']
140154
for color in colors:
141155
if (WRED_CONFIG_FIELDS[color + 'min'] in profile_data and
@@ -145,11 +159,11 @@ class EcnConfig(object):
145159
max_thresh = int(profile_data[WRED_CONFIG_FIELDS[color + 'max']])
146160

147161
if min_thresh > max_thresh:
148-
print("Invalid %s (%d) and %s (%d). %s should be no smaller than %s" %
162+
print("Invalid %s (%d) and %s (%d). %s should be smaller than %s" %
149163
(color + 'min', min_thresh, color + 'max', max_thresh, color + 'min', color + 'max'))
150-
result = False
164+
result = False
151165

152-
return result
166+
return result
153167

154168
def set_wred_threshold(self, profile, threshold, value):
155169
if os.geteuid() != 0:
@@ -159,15 +173,23 @@ class EcnConfig(object):
159173
if self.verbose:
160174
print("Setting %s value to %s" % (field, value))
161175
self.db.mod_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value})
176+
if self.filename is not None:
177+
prof_table = self.db.get_table(WRED_PROFILE_TABLE_NAME)
178+
with open(self.filename, "w") as fd:
179+
json.dump(prof_table, fd)
162180

163181
def set_wred_prob(self, profile, drop_color, value):
164182
if os.geteuid() != 0:
165183
sys.exit("Root privileges required for this operation")
166-
184+
167185
field = WRED_CONFIG_FIELDS[drop_color]
168186
if self.verbose:
169187
print("Setting %s value to %s%%" % (field, value))
170188
self.db.mod_entry(WRED_PROFILE_TABLE_NAME, profile, {field: value})
189+
if self.filename is not None:
190+
prof_table = self.db.get_table(WRED_PROFILE_TABLE_NAME)
191+
with open(self.filename, "w") as fd:
192+
json.dump(prof_table, fd)
171193

172194
class EcnQ(object):
173195
"""
@@ -253,21 +275,35 @@ def main():
253275

254276
parser.add_argument('command', nargs='?', choices=['on', 'off'], type=str, help='turn on/off ecn', default=None)
255277
parser.add_argument('-q', '--queue', type=str, help='specify queue index list: 3,4', default=None)
278+
parser.add_argument('-f', '--filename', help='file used by mock tests', type=str, default=None)
279+
280+
if os.environ.get("UTILITIES_UNIT_TESTING", "0") == "2":
281+
sys.argv.extend(['-f', '/tmp/ecnconfig'])
256282

257283
args = parser.parse_args()
258284

259285
try:
260286
if args.list or args.profile:
261-
prof_cfg = EcnConfig(args.verbose)
287+
prof_cfg = EcnConfig(args.filename, args.verbose)
262288
if args.list:
263-
if len(sys.argv) > (3 if args.verbose else 2):
289+
arg_len_max = 2
290+
if args.verbose:
291+
arg_len_max += 1
292+
if args.filename:
293+
arg_len_max += 2
294+
if len(sys.argv) > arg_len_max:
264295
raise Exception("Input arguments error. No set options allowed when -l[ist] specified")
265296
prof_cfg.list()
266297
elif args.profile:
267-
if len(sys.argv) < (5 if args.verbose else 4):
298+
arg_len_min = 4
299+
if args.verbose:
300+
arg_len_min += 1
301+
if args.filename:
302+
arg_len_min += 2
303+
if len(sys.argv) < arg_len_min:
268304
raise Exception("Input arguments error. Specify at least one threshold parameter to set")
269305

270-
# get current configuration data
306+
# get current configuration data
271307
wred_profile_data = prof_cfg.get_profile_data(args.profile)
272308
if wred_profile_data is None:
273309
raise Exception("Input arguments error. Invalid WRED profile %s" % (args.profile))

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@
6161
'mock_tables/asic2/*.json',
6262
'filter_fdb_input/*',
6363
'pfcwd_input/*',
64-
'wm_input/*']
64+
'wm_input/*',
65+
'ecn_input/*']
6566
},
6667
scripts=[
6768
'scripts/aclshow',

show/main.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from utilities_common.db import Db
1616

1717
from . import acl
18-
from . import bgp_common
18+
from . import bgp_common
1919
from . import chassis_modules
2020
from . import dropcounters
2121
from . import feature
@@ -1449,11 +1449,11 @@ def policer(policer_name, verbose):
14491449
# 'ecn' command ("show ecn")
14501450
#
14511451
@cli.command('ecn')
1452-
def ecn():
1452+
@click.option('--verbose', is_flag=True, help="Enable verbose output")
1453+
def ecn(verbose):
14531454
"""Show ECN configuration"""
14541455
cmd = "ecnconfig -l"
1455-
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, text=True)
1456-
click.echo(proc.stdout.read())
1456+
run_command(cmd, display_cmd=verbose)
14571457

14581458

14591459
#

tests/ecn_input/__init__.py

Whitespace-only changes.

tests/ecn_input/ecn_test_vectors.py

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
ecn_show_config_output="""\
2+
Profile: AZURE_LOSSLESS
3+
----------------------- -------
4+
red_max_threshold 2097152
5+
wred_green_enable true
6+
ecn ecn_all
7+
green_min_threshold 1048576
8+
red_min_threshold 1048576
9+
wred_yellow_enable true
10+
yellow_min_threshold 1048576
11+
green_max_threshold 2097152
12+
green_drop_probability 5
13+
yellow_max_threshold 2097152
14+
wred_red_enable true
15+
yellow_drop_probability 5
16+
red_drop_probability 5
17+
----------------------- -------
18+
19+
"""
20+
21+
testData = {
22+
'ecn_show_config' : {'cmd' : ['show'],
23+
'args' : [],
24+
'rc' : 0,
25+
'rc_output': ecn_show_config_output
26+
},
27+
'ecn_cfg_gmin' : {'cmd' : ['config'],
28+
'args' : ['-profile', 'AZURE_LOSSLESS', '-gmin', '1048600'],
29+
'rc' : 0,
30+
'cmp_args' : ['AZURE_LOSSLESS,green_min_threshold,1048600']
31+
},
32+
'ecn_cfg_gmax' : {'cmd' : ['config'],
33+
'args' : ['-profile', 'AZURE_LOSSLESS', '-gmax', '2097153'],
34+
'rc' : 0,
35+
'cmp_args' : ['AZURE_LOSSLESS,green_max_threshold,2097153']
36+
},
37+
'ecn_cfg_ymin' : {'cmd' : ['config'],
38+
'args' : ['-profile', 'AZURE_LOSSLESS', '-ymin', '1048600'],
39+
'rc' : 0,
40+
'cmp_args' : ['AZURE_LOSSLESS,yellow_min_threshold,1048600']
41+
},
42+
'ecn_cfg_ymax' : {'cmd' : ['config'],
43+
'args' : ['-profile', 'AZURE_LOSSLESS', '-ymax', '2097153'],
44+
'rc' : 0,
45+
'cmp_args' : ['AZURE_LOSSLESS,yellow_max_threshold,2097153']
46+
},
47+
'ecn_cfg_rmin' : {'cmd' : ['config'],
48+
'args' : ['-profile', 'AZURE_LOSSLESS', '-rmin', '1048600'],
49+
'rc' : 0,
50+
'cmp_args' : ['AZURE_LOSSLESS,red_min_threshold,1048600']
51+
},
52+
'ecn_cfg_rmax' : {'cmd' : ['config'],
53+
'args' : ['-profile', 'AZURE_LOSSLESS', '-rmax', '2097153'],
54+
'rc' : 0,
55+
'cmp_args' : ['AZURE_LOSSLESS,red_max_threshold,2097153']
56+
},
57+
'ecn_cfg_rdrop' : {'cmd' : ['config'],
58+
'args' : ['-profile', 'AZURE_LOSSLESS', '-rdrop', '10'],
59+
'rc' : 0,
60+
'cmp_args' : ['AZURE_LOSSLESS,red_drop_probability,10']
61+
},
62+
'ecn_cfg_ydrop' : {'cmd' : ['config'],
63+
'args' : ['-profile', 'AZURE_LOSSLESS', '-ydrop', '11'],
64+
'rc' : 0,
65+
'cmp_args' : ['AZURE_LOSSLESS,yellow_drop_probability,11']
66+
},
67+
'ecn_cfg_gdrop' : {'cmd' : ['config'],
68+
'args' : ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12'],
69+
'rc' : 0,
70+
'cmp_args' : ['AZURE_LOSSLESS,green_drop_probability,12']
71+
},
72+
'ecn_cfg_multi_set' : {'cmd' : ['config'],
73+
'args' : ['-profile', 'AZURE_LOSSLESS', '-gdrop', '12', '-gmax', '2097153'],
74+
'rc' : 0,
75+
'cmp_args' : ['AZURE_LOSSLESS,green_drop_probability,12',
76+
'AZURE_LOSSLESS,green_max_threshold,2097153'
77+
]
78+
},
79+
'ecn_cfg_gmin_gmax_invalid' : {'cmd' : ['config'],
80+
'args' : ['-profile', 'AZURE_LOSSLESS', '-gmax', '2097153', '-gmin', '2097154'],
81+
'rc' : 1,
82+
'rc_msg' : 'Invalid gmin (2097154) and gmax (2097153). gmin should be smaller than gmax'
83+
},
84+
'ecn_cfg_ymin_ymax_invalid' : {'cmd' : ['config'],
85+
'args' : ['-profile', 'AZURE_LOSSLESS', '-ymax', '2097153', '-ymin', '2097154'],
86+
'rc' : 1,
87+
'rc_msg' : 'Invalid ymin (2097154) and ymax (2097153). ymin should be smaller than ymax'
88+
},
89+
'ecn_cfg_rmin_rmax_invalid' : {'cmd' : ['config'],
90+
'args' : ['-profile', 'AZURE_LOSSLESS', '-rmax', '2097153', '-rmin', '2097154'],
91+
'rc' : 1,
92+
'rc_msg' : 'Invalid rmin (2097154) and rmax (2097153). rmin should be smaller than rmax'
93+
},
94+
'ecn_cfg_rmax_invalid' : {'cmd' : ['config'],
95+
'args' : ['-profile', 'AZURE_LOSSLESS', '-rmax', '-2097153'],
96+
'rc' : 1,
97+
'rc_msg' : 'Invalid rmax (-2097153). rmax should be an non-negative integer'
98+
},
99+
'ecn_cfg_rdrop_invalid' : {'cmd' : ['config'],
100+
'args' : ['-profile', 'AZURE_LOSSLESS', '-rdrop', '105'],
101+
'rc' : 1,
102+
'rc_msg' : 'Invalid value for "-rdrop": 105 is not in the valid range of 0 to 100'
103+
}
104+
105+
}

0 commit comments

Comments
 (0)