diff --git a/config/main.py b/config/main.py index 45224de7b8..debb1c4355 100644 --- a/config/main.py +++ b/config/main.py @@ -3125,10 +3125,11 @@ def pfcwd(): @pfcwd.command() @click.option('--action', '-a', type=click.Choice(['drop', 'forward', 'alert'])) @click.option('--restoration-time', '-r', type=click.IntRange(100, 60000)) +@click.option('--pfc-stat-history', is_flag=True, help="Enable historical statistics tracking") @click.option('--verbose', is_flag=True, help="Enable verbose output") @click.argument('ports', nargs=-1) @click.argument('detection-time', type=click.IntRange(100, 5000)) -def start(action, restoration_time, ports, detection_time, verbose): +def start(action, restoration_time, pfc_stat_history, ports, detection_time, verbose): """ Start PFC watchdog on port(s). To config all ports, use all as input. @@ -3150,6 +3151,9 @@ def start(action, restoration_time, ports, detection_time, verbose): if restoration_time: cmd += ['--restoration-time', str(restoration_time)] + if pfc_stat_history: + cmd += ['--pfc-stat-history'] + clicommon.run_command(cmd, display_cmd=verbose) @pfcwd.command() @@ -3191,6 +3195,21 @@ def big_red_switch(big_red_switch, verbose): clicommon.run_command(cmd, display_cmd=verbose) + +@pfcwd.command('pfc_stat_history') +@click.option('--verbose', is_flag=True, help="Enable verbose output") +@click.argument('pfc_stat_history', type=click.Choice(['enable', 'disable'])) +@click.argument('ports', nargs=-1) +def pfc_stat_history(ports, pfc_stat_history, verbose): + """ Enable/disable PFC Historical Statistics mode on ports""" + + cmd = ['pfcwd', 'pfc_stat_history', pfc_stat_history] + ports = set(ports) - set(['ports']) + cmd += list(ports) + + clicommon.run_command(cmd, display_cmd=verbose) + + @pfcwd.command('start_default') @click.option('--verbose', is_flag=True, help="Enable verbose output") def start_default(verbose): diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 34ed0bccf4..797a15502f 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -8663,6 +8663,7 @@ This command starts PFC Watchdog ``` config pfcwd start --action drop all 400 --restoration-time 400 config pfcwd start --action forward Ethernet0 Ethernet8 400 + config pfcwd start --action drop all 400 --restoration-time 400 --pfc-stat-history ``` **config pfcwd stop** @@ -8701,6 +8702,18 @@ This command enables or disables PFCWD's "BIG RED SWITCH"(BRS). After enabling B config pfcwd big_red_switch enable ``` +**config pfcwd pfc_stat_history \ \** + +This command enables or disables PFCWD's PFC Historical Statistics estimation. After enabling, PFC Watchdog will be configured to estimate pause transitions, total pause time, and the pause time and timstamp of the most recent pause activity on those ports. + +NOTE: The estimation will only be performed on ports the PFCWD has been started on, alternatively use the --pfc-stat-history flag with the `start` command to simultaneously enable history on those ports. + +- Usage: + ``` + config pfcwd pfc_stat_history enable all + config pfcwd pfc_stat_history disable Ethernet0 Ethernet8 + ``` + **config pfcwd start_default** This command starts PFC Watchdog with the default settings. @@ -8716,6 +8729,7 @@ Default values are the following: - restoration time - 200ms - polling interval - 200ms - action - 'drop' + - pfc stat history - disable Additionally if number of ports in the system exceeds 32, all times will be multiplied by roughly /32. diff --git a/pfcwd/main.py b/pfcwd/main.py index ac6075385b..f640ff1d5f 100644 --- a/pfcwd/main.py +++ b/pfcwd/main.py @@ -38,6 +38,7 @@ DEFAULT_POLL_INTERVAL = 200 DEFAULT_PORT_NUM = 32 DEFAULT_ACTION = 'drop' +DEFAULT_PFC_HISTORY_STATUS = "disable" STATS_DESCRIPTION = [ ('STORM DETECTED/RESTORED', 'PFC_WD_QUEUE_STATS_DEADLOCK_DETECTED', 'PFC_WD_QUEUE_STATS_DEADLOCK_RESTORED'), @@ -50,7 +51,8 @@ CONFIG_DESCRIPTION = [ ('ACTION', 'action', 'drop'), ('DETECTION TIME', 'detection_time', 'N/A'), - ('RESTORATION TIME', 'restoration_time', 'infinite') + ('RESTORATION TIME', 'restoration_time', 'infinite'), + ('HISTORY', 'pfc_stat_history', 'disable') ] STATS_HEADER = ('QUEUE', 'STATUS',) + list(zip(*STATS_DESCRIPTION))[0] @@ -248,34 +250,47 @@ def config(self, ports): tablefmt='simple' )) - def start(self, action, restoration_time, ports, detection_time): + def start(self, action, restoration_time, ports, detection_time, pfc_stat_history): invalid_ports = self.get_invalid_ports(ports) if len(invalid_ports): click.echo("Failed to run command, invalid options:") for opt in invalid_ports: click.echo(opt) sys.exit(1) - self.start_cmd(action, restoration_time, ports, detection_time) + self.start_cmd(action, restoration_time, ports, detection_time, pfc_stat_history) + def pfc_stat_history(self, pfc_stat_history, ports): + invalid_ports = self.get_invalid_ports(ports) + if len(invalid_ports): + click.echo("Failed to run command, invalid options:") + for opt in invalid_ports: + click.echo(opt) + sys.exit(1) + self.pfc_stat_history_cmd(pfc_stat_history, ports) - def verify_pfc_enable_status_per_port(self, port, pfcwd_info): + def verify_pfc_enable_status_per_port(self, port, pfcwd_info, overwrite=True): pfc_status = self.config_db.get_entry(PORT_QOS_MAP, port).get('pfc_enable') if pfc_status is None: log.log_warning("SKIPPED: PFC is not enabled on port: {}".format(port), also_print_to_console=True) return - self.config_db.mod_entry( - CONFIG_DB_PFC_WD_TABLE_NAME, port, None - ) - self.config_db.mod_entry( - CONFIG_DB_PFC_WD_TABLE_NAME, port, pfcwd_info - ) + if overwrite: + # don't clear existing pfc history setting unless set explicitely + cur_pfc_history = self.config_db.get_entry( + CONFIG_DB_PFC_WD_TABLE_NAME, port + ).get("pfc_stat_history", DEFAULT_PFC_HISTORY_STATUS) - @multi_asic_util.run_on_multi_asic - def start_cmd(self, action, restoration_time, ports, detection_time): - if os.geteuid() != 0: - sys.exit("Root privileges are required for this operation") + pfcwd_info.setdefault("pfc_stat_history", cur_pfc_history) + self.config_db.set_entry( + CONFIG_DB_PFC_WD_TABLE_NAME, port, pfcwd_info + ) + else: + self.config_db.mod_entry( + CONFIG_DB_PFC_WD_TABLE_NAME, port, pfcwd_info + ) + + def configure_ports(self, ports, pfcwd_info, overwrite=True): all_ports = get_all_ports( self.db, self.multi_asic.current_namespace, self.multi_asic.display_option @@ -284,6 +299,20 @@ def start_cmd(self, action, restoration_time, ports, detection_time): if len(ports) == 0: ports = all_ports + for port in ports: + if port == "all": + for p in all_ports: + self.verify_pfc_enable_status_per_port(p, pfcwd_info, overwrite) + else: + if port not in all_ports: + continue + self.verify_pfc_enable_status_per_port(port, pfcwd_info, overwrite) + + @multi_asic_util.run_on_multi_asic + def start_cmd(self, action, restoration_time, ports, detection_time, pfc_stat_history): + if os.geteuid() != 0: + sys.exit("Root privileges are required for this operation") + pfcwd_info = { 'detection_time': detection_time, } @@ -297,15 +326,10 @@ def start_cmd(self, action, restoration_time, ports, detection_time): "restoration time not defined; default to 2 times " "detection time: {} ms".format(2 * detection_time) ) + if pfc_stat_history: + pfcwd_info["pfc_stat_history"] = "enable" - for port in ports: - if port == "all": - for p in all_ports: - self.verify_pfc_enable_status_per_port(p, pfcwd_info) - else: - if port not in all_ports: - continue - self.verify_pfc_enable_status_per_port(port, pfcwd_info) + self.configure_ports(ports, pfcwd_info, overwrite=True) @multi_asic_util.run_on_multi_asic def interval(self, poll_interval): @@ -391,11 +415,12 @@ def start_default(self): pfcwd_info = { 'detection_time': DEFAULT_DETECTION_TIME * multiply, 'restoration_time': DEFAULT_RESTORATION_TIME * multiply, - 'action': DEFAULT_ACTION + 'action': DEFAULT_ACTION, + 'pfc_stat_history': DEFAULT_PFC_HISTORY_STATUS } for port in active_ports: - self.verify_pfc_enable_status_per_port(port, pfcwd_info) + self.verify_pfc_enable_status_per_port(port, pfcwd_info, overwrite=True) pfcwd_info = {} pfcwd_info['POLL_INTERVAL'] = DEFAULT_POLL_INTERVAL * multiply @@ -423,6 +448,15 @@ def big_red_switch(self, big_red_switch): pfcwd_info ) + @multi_asic_util.run_on_multi_asic + def pfc_stat_history_cmd(self, pfc_stat_history, ports): + if os.geteuid() != 0: + sys.exit("Root privileges are required for this operation") + + pfcwd_info = { + 'pfc_stat_history': pfc_stat_history + } + self.configure_ports(ports, pfcwd_info, overwrite=False) # Show stats class Show(object): @@ -459,20 +493,21 @@ class Start(object): '--action', '-a', type=click.Choice(['drop', 'forward', 'alert']) ) @click.option('--restoration-time', '-r', type=click.IntRange(100, 60000)) + @click.option('--pfc-stat-history', is_flag=True) @click.argument('ports', nargs=-1) @click.argument('detection-time', type=click.IntRange(100, 5000)) @clicommon.pass_db - def start(db, action, restoration_time, ports, detection_time): + def start(db, action, restoration_time, ports, detection_time, pfc_stat_history): """ Start PFC watchdog on port(s). To config all ports, use all as input. Example: - sudo pfcwd start --action drop all 400 --restoration-time 400 + sudo pfcwd start --action drop all 400 --restoration-time 400 --pfc-stat-history enable """ PfcwdCli(db).start( - action, restoration_time, ports, detection_time + action, restoration_time, ports, detection_time, pfc_stat_history ) @@ -525,7 +560,19 @@ def big_red_switch(db, big_red_switch): PfcwdCli(db).big_red_switch(big_red_switch) +# Enable/disable PFC WD PFC_STAT_HISTORY mode +class PfcStatHistory(object): + @cli.command('pfc_stat_history') + @click.argument('pfc_stat_history', type=click.Choice(['enable', 'disable'])) + @click.argument('ports', nargs=-1) + @clicommon.pass_db + def pfc_stat_history(db, pfc_stat_history, ports): + """ Enable/disable PFC Historical Statistics mode on ports""" + PfcwdCli(db).pfc_stat_history(pfc_stat_history, ports) + + def get_pfcwd_clis(): + cli.add_command(PfcStatHistory().pfc_stat_history) cli.add_command(BigRedSwitch().big_red_switch) cli.add_command(CounterPoll().counter_poll) cli.add_command(StartDefault().start_default) diff --git a/tests/mock_tables/asic0/config_db.json b/tests/mock_tables/asic0/config_db.json index 296a2d8ea2..d7cd302bef 100644 --- a/tests/mock_tables/asic0/config_db.json +++ b/tests/mock_tables/asic0/config_db.json @@ -99,22 +99,26 @@ "PFC_WD|Ethernet0": { "action": "drop", "detection_time": "200", - "restoration_time": "200" + "restoration_time": "200", + "pfc_stat_history": "disable" }, "PFC_WD|Ethernet4": { "action": "drop", "detection_time": "200", - "restoration_time": "200" + "restoration_time": "200", + "pfc_stat_history": "disable" }, "PFC_WD|Ethernet-BP0": { "action": "drop", "detection_time": "200", - "restoration_time": "200" + "restoration_time": "200", + "pfc_stat_history": "disable" }, "PFC_WD|Ethernet-BP4": { "action": "drop", "detection_time": "200", - "restoration_time": "200" + "restoration_time": "200", + "pfc_stat_history": "disable" }, "PFC_WD|GLOBAL": { "BIG_RED_SWITCH": "enable", diff --git a/tests/mock_tables/asic1/config_db.json b/tests/mock_tables/asic1/config_db.json index 5c1d9f344c..2169f581f2 100644 --- a/tests/mock_tables/asic1/config_db.json +++ b/tests/mock_tables/asic1/config_db.json @@ -66,12 +66,14 @@ "PFC_WD|Ethernet-BP256": { "action": "drop", "detection_time": "200", - "restoration_time": "200" + "restoration_time": "200", + "pfc_stat_history": "disable" }, "PFC_WD|Ethernet-BP260": { "action": "drop", "detection_time": "200", - "restoration_time": "200" + "restoration_time": "200", + "pfc_stat_history": "disable" }, "PFC_WD|GLOBAL": { "BIG_RED_SWITCH": "enable", diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index d1f1c46f3d..23c5f23aa9 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1835,17 +1835,20 @@ "PFC_WD|Ethernet0": { "action": "drop", "detection_time": "600", - "restoration_time": "600" + "restoration_time": "600", + "pfc_stat_history": "disable" }, "PFC_WD|Ethernet4": { "action": "drop", "detection_time": "600", - "restoration_time": "600" + "restoration_time": "600", + "pfc_stat_history": "disable" }, "PFC_WD|Ethernet8": { "action": "drop", "detection_time": "600", - "restoration_time": "600" + "restoration_time": "600", + "pfc_stat_history": "disable" }, "PFC_WD|GLOBAL": { "POLL_INTERVAL": "600" diff --git a/tests/pfcwd_input/pfcwd_test_vectors.py b/tests/pfcwd_input/pfcwd_test_vectors.py index 127bfaac2b..bb103450d4 100644 --- a/tests/pfcwd_input/pfcwd_test_vectors.py +++ b/tests/pfcwd_input/pfcwd_test_vectors.py @@ -1,80 +1,98 @@ pfcwd_show_config_output="""\ Changed polling interval to 600ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 drop 600 600 -Ethernet4 drop 600 600 -Ethernet8 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 drop 600 600 disable +Ethernet4 drop 600 600 disable +Ethernet8 drop 600 600 disable """ pfcwd_show_start_config_output_pass = """\ Changed polling interval to 600ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 forward 102 101 -Ethernet4 drop 600 600 -Ethernet8 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 forward 102 101 disable +Ethernet4 drop 600 600 disable +Ethernet8 drop 600 600 disable +""" + +pfcwd_show_enable_history_config_output_pass = """\ +Changed polling interval to 600ms + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 drop 600 600 enable +Ethernet4 drop 600 600 disable +Ethernet8 drop 600 600 disable """ pfcwd_show_start_action_forward_output = """\ Changed polling interval to 600ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 forward 302 301 -Ethernet4 forward 302 301 -Ethernet8 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 forward 302 301 disable +Ethernet4 forward 302 301 disable +Ethernet8 drop 600 600 disable """ pfcwd_show_start_action_alert_output = """\ Changed polling interval to 600ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 alert 502 501 -Ethernet4 alert 502 501 -Ethernet8 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 alert 502 501 disable +Ethernet4 alert 502 501 disable +Ethernet8 drop 600 600 disable """ pfcwd_show_start_action_drop_output = """\ Changed polling interval to 600ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 drop 602 601 -Ethernet4 drop 602 601 -Ethernet8 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 drop 602 601 disable +Ethernet4 drop 602 601 disable +Ethernet8 drop 600 600 disable """ pfcwd_show_start_default = """\ Changed polling interval to 200ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 drop 200 200 -Ethernet4 drop 200 200 -Ethernet8 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 drop 200 200 disable +Ethernet4 drop 200 200 disable +Ethernet8 drop 600 600 disable """ -pfcwd_show_start_config_output_fail = """\ +pfcwd_show_start_history_output = """\ +Changed polling interval to 600ms + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 drop 600 601 enable +Ethernet4 drop 600 601 enable +Ethernet8 drop 600 600 disable +""" + +show_pfc_config_invalid_options_fail = """\ Failed to run command, invalid options: Ethernet1000 """ pfcwd_show_config_single_port_output="""\ Changed polling interval to 600ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 drop 600 600 disable """ pfcwd_show_config_multi_port_output="""\ Changed polling interval to 600ms - PORT ACTION DETECTION TIME RESTORATION TIME ---------- -------- ---------------- ------------------ -Ethernet0 drop 600 600 -Ethernet4 drop 600 600 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +--------- -------- ---------------- ------------------ --------- +Ethernet0 drop 600 600 disable +Ethernet4 drop 600 600 disable """ pfcwd_show_config_invalid_port_output="""\ - PORT ACTION DETECTION TIME RESTORATION TIME ------- -------- ---------------- ------------------ + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +------ -------- ---------------- ------------------ --------- """ pfcwd_show_stats_output="""\ @@ -265,14 +283,14 @@ BIG_RED_SWITCH status is enable on asic0 Changed polling interval to 199ms on asic1 BIG_RED_SWITCH status is enable on asic1 - PORT ACTION DETECTION TIME RESTORATION TIME --------------- -------- ---------------- ------------------ - Ethernet0 drop 200 200 - Ethernet4 drop 200 200 - Ethernet-BP0 drop 200 200 - Ethernet-BP4 drop 200 200 -Ethernet-BP256 drop 200 200 -Ethernet-BP260 drop 200 200 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 drop 200 200 disable + Ethernet4 drop 200 200 disable + Ethernet-BP0 drop 200 200 disable + Ethernet-BP4 drop 200 200 disable +Ethernet-BP256 drop 200 200 disable +Ethernet-BP260 drop 200 200 disable """ show_pfc_config_start_pass = """\ @@ -280,14 +298,29 @@ BIG_RED_SWITCH status is enable on asic0 Changed polling interval to 199ms on asic1 BIG_RED_SWITCH status is enable on asic1 - PORT ACTION DETECTION TIME RESTORATION TIME --------------- -------- ---------------- ------------------ - Ethernet0 forward 102 101 - Ethernet4 drop 200 200 - Ethernet-BP0 drop 200 200 - Ethernet-BP4 forward 102 101 -Ethernet-BP256 drop 200 200 -Ethernet-BP260 drop 200 200 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 forward 102 101 disable + Ethernet4 drop 200 200 disable + Ethernet-BP0 drop 200 200 disable + Ethernet-BP4 forward 102 101 disable +Ethernet-BP256 drop 200 200 disable +Ethernet-BP260 drop 200 200 disable +""" + +show_pfc_config_enable_history_pass = """\ +Changed polling interval to 199ms on asic0 +BIG_RED_SWITCH status is enable on asic0 +Changed polling interval to 199ms on asic1 +BIG_RED_SWITCH status is enable on asic1 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 drop 200 200 enable + Ethernet4 drop 200 200 disable + Ethernet-BP0 drop 200 200 disable + Ethernet-BP4 drop 200 200 enable +Ethernet-BP256 drop 200 200 disable +Ethernet-BP260 drop 200 200 disable """ show_pfc_config_start_action_drop_masic = """\ @@ -295,14 +328,14 @@ BIG_RED_SWITCH status is enable on asic0 Changed polling interval to 199ms on asic1 BIG_RED_SWITCH status is enable on asic1 - PORT ACTION DETECTION TIME RESTORATION TIME --------------- -------- ---------------- ------------------ - Ethernet0 drop 302 301 - Ethernet4 drop 302 301 - Ethernet-BP0 drop 302 301 - Ethernet-BP4 drop 302 301 -Ethernet-BP256 drop 302 301 -Ethernet-BP260 drop 200 200 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 drop 302 301 disable + Ethernet4 drop 302 301 disable + Ethernet-BP0 drop 302 301 disable + Ethernet-BP4 drop 302 301 disable +Ethernet-BP256 drop 302 301 disable +Ethernet-BP260 drop 200 200 disable """ show_pfc_config_start_action_alert_masic = """\ @@ -310,14 +343,14 @@ BIG_RED_SWITCH status is enable on asic0 Changed polling interval to 199ms on asic1 BIG_RED_SWITCH status is enable on asic1 - PORT ACTION DETECTION TIME RESTORATION TIME --------------- -------- ---------------- ------------------ - Ethernet0 alert 402 401 - Ethernet4 alert 402 401 - Ethernet-BP0 alert 402 401 - Ethernet-BP4 alert 402 401 -Ethernet-BP256 alert 402 401 -Ethernet-BP260 drop 200 200 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 alert 402 401 disable + Ethernet4 alert 402 401 disable + Ethernet-BP0 alert 402 401 disable + Ethernet-BP4 alert 402 401 disable +Ethernet-BP256 alert 402 401 disable +Ethernet-BP260 drop 200 200 disable """ show_pfc_config_start_action_forward_masic = """\ @@ -325,17 +358,32 @@ BIG_RED_SWITCH status is enable on asic0 Changed polling interval to 199ms on asic1 BIG_RED_SWITCH status is enable on asic1 - PORT ACTION DETECTION TIME RESTORATION TIME --------------- -------- ---------------- ------------------ - Ethernet0 forward 702 701 - Ethernet4 forward 702 701 - Ethernet-BP0 forward 702 701 - Ethernet-BP4 forward 702 701 -Ethernet-BP256 forward 702 701 -Ethernet-BP260 drop 200 200 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 forward 702 701 disable + Ethernet4 forward 702 701 disable + Ethernet-BP0 forward 702 701 disable + Ethernet-BP4 forward 702 701 disable +Ethernet-BP256 forward 702 701 disable +Ethernet-BP260 drop 200 200 disable +""" + +pfcwd_show_start_history_output_masic = """\ +Changed polling interval to 199ms on asic0 +BIG_RED_SWITCH status is enable on asic0 +Changed polling interval to 199ms on asic1 +BIG_RED_SWITCH status is enable on asic1 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 drop 600 601 enable + Ethernet4 drop 600 601 enable + Ethernet-BP0 drop 600 601 enable + Ethernet-BP4 drop 600 601 enable +Ethernet-BP256 drop 600 601 enable +Ethernet-BP260 drop 200 200 disable """ -show_pfc_config_start_fail = """\ +show_pfc_config_invalid_options_fail_masic = """\ Failed to run command, invalid options: Ethernet-500 """ @@ -354,9 +402,9 @@ BIG_RED_SWITCH status is enable on asic0 Changed polling interval to 199ms on asic1 BIG_RED_SWITCH status is enable on asic1 - PORT ACTION DETECTION TIME RESTORATION TIME --------------- -------- ---------------- ------------------ - Ethernet0 drop 200 200 - Ethernet-BP0 drop 200 200 -Ethernet-BP256 drop 200 200 + PORT ACTION DETECTION TIME RESTORATION TIME HISTORY +-------------- -------- ---------------- ------------------ --------- + Ethernet0 drop 200 200 disable + Ethernet-BP0 drop 200 200 disable +Ethernet-BP256 drop 200 200 disable """ diff --git a/tests/pfcwd_test.py b/tests/pfcwd_test.py index 2735cd09df..9d6ff71aaa 100644 --- a/tests/pfcwd_test.py +++ b/tests/pfcwd_test.py @@ -7,7 +7,7 @@ from utilities_common.db import Db -from .pfcwd_input.pfcwd_test_vectors import * +from .pfcwd_input import pfcwd_test_vectors as test_vectors test_path = os.path.dirname(os.path.abspath(__file__)) modules_path = os.path.dirname(test_path) @@ -24,28 +24,28 @@ def setup_class(cls): print("SETUP") def test_pfcwd_show_config(self): - self.executor(testData['pfcwd_show_config']) + self.executor(test_vectors.testData['pfcwd_show_config']) def test_pfcwd_show_config_single_port(self): - self.executor(testData['pfcwd_show_config_single_port']) + self.executor(test_vectors.testData['pfcwd_show_config_single_port']) def test_pfcwd_show_config_multi_port(self): - self.executor(testData['pfcwd_show_config_multi_port']) + self.executor(test_vectors.testData['pfcwd_show_config_multi_port']) def test_pfcwd_show_config_invalid_port(self): - self.executor(testData['pfcwd_show_config_invalid_port']) + self.executor(test_vectors.testData['pfcwd_show_config_invalid_port']) def test_pfcwd_show_stats(self): - self.executor(testData['pfcwd_show_stats']) + self.executor(test_vectors.testData['pfcwd_show_stats']) def test_pfcwd_show_stats_single_queue(self): - self.executor(testData['pfcwd_show_stats_single_queue']) + self.executor(test_vectors.testData['pfcwd_show_stats_single_queue']) def test_pfcwd_show_stats_multi_queue(self): - self.executor(testData['pfcwd_show_stats_multi_queue']) + self.executor(test_vectors.testData['pfcwd_show_stats_multi_queue']) def test_pfcwd_show_stats_invalid_queue(self): - self.executor(testData['pfcwd_show_stats_invalid_queue']) + self.executor(test_vectors.testData['pfcwd_show_stats_invalid_queue']) def executor(self, testcase): import pfcwd.main as pfcwd @@ -93,7 +93,7 @@ def test_pfcwd_start_ports_valid(self, mock_os): obj=db ) print(result.output) - assert result.output == pfcwd_show_config_output + assert result.output == test_vectors.pfcwd_show_config_output mock_os.geteuid.return_value = 0 result = runner.invoke( @@ -114,7 +114,43 @@ def test_pfcwd_start_ports_valid(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == pfcwd_show_start_config_output_pass + assert result.output == test_vectors.pfcwd_show_start_config_output_pass + + @patch('pfcwd.main.os') + def test_pfcwd_enable_history_ports_valid(self, mock_os): + # pfcwd pfc_stat_history enable Ethernet0 + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + + # initially history is disabled + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.output == test_vectors.pfcwd_show_config_output + + mock_os.geteuid.return_value = 0 + result = runner.invoke( + pfcwd.cli.commands["pfc_stat_history"], + [ + "enable", + "Ethernet0" + ], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + + # now valid port is enabled + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == test_vectors.pfcwd_show_enable_history_config_output_pass @patch('pfcwd.main.os') def test_pfcwd_start_actions(self, mock_os): @@ -129,7 +165,7 @@ def test_pfcwd_start_actions(self, mock_os): obj=db ) print(result.output) - assert result.output == pfcwd_show_config_output + assert result.output == test_vectors.pfcwd_show_config_output # always skip Ethernet8 because 'pfc_enable' not configured for this port mock_os.geteuid.return_value = 0 @@ -151,7 +187,7 @@ def test_pfcwd_start_actions(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == pfcwd_show_start_action_forward_output + assert result.output == test_vectors.pfcwd_show_start_action_forward_output result = runner.invoke( pfcwd.cli.commands["start"], @@ -171,7 +207,7 @@ def test_pfcwd_start_actions(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == pfcwd_show_start_action_alert_output + assert result.output == test_vectors.pfcwd_show_start_action_alert_output result = runner.invoke( pfcwd.cli.commands["start"], @@ -191,7 +227,7 @@ def test_pfcwd_start_actions(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == pfcwd_show_start_action_drop_output + assert result.output == test_vectors.pfcwd_show_start_action_drop_output result = runner.invoke( pfcwd.cli.commands["start_default"], @@ -208,8 +244,44 @@ def test_pfcwd_start_actions(self, mock_os): print(result.output) assert result.exit_code == 0 - assert result.output == pfcwd_show_start_default + assert result.output == test_vectors.pfcwd_show_start_default + + @patch('pfcwd.main.os') + def test_pfcwd_start_history(self, mock_os): + # pfcwd start all 600 --restoration-time 601 --pfc-stat-history + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + + # initially history disabled on all ports + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.output == test_vectors.pfcwd_show_config_output + + mock_os.geteuid.return_value = 0 + # start wd with history flag + result = runner.invoke( + pfcwd.cli.commands["start"], + [ + "all", "600", "--restoration-time", "601", + "--pfc-stat-history" + ], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + # get config after the change + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == test_vectors.pfcwd_show_start_history_output @patch('pfcwd.main.os') def test_pfcwd_pfc_not_enabled(self, mock_os): @@ -223,22 +295,61 @@ def test_pfcwd_pfc_not_enabled(self, mock_os): obj=db ) print(result.output) - assert result.output == pfcwd_show_config_output + assert result.output == test_vectors.pfcwd_show_config_output mock_os.geteuid.return_value = 0 result = runner.invoke( pfcwd.cli.commands["start"], [ - "--action", "drop", "--restoration-time", "601", + "--action", "drop", "--restoration-time", "601", "--pfc-stat-history", "Ethernet8", "602" ], obj=db ) print(result.output) assert result.exit_code == 0 - assert pfc_is_not_enabled == result.output + assert test_vectors.pfc_is_not_enabled == result.output + + @patch('pfcwd.main.os') + def test_pfcwd_enable_history_pfc_not_enabled(self, mock_os): + # pfcwd pfc_stat_history enable Ethernet8 + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + + # get initial config + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.output == test_vectors.pfcwd_show_config_output + + # attempt to enable history on Ethernet without pfc + mock_os.geteuid.return_value = 0 + result = runner.invoke( + pfcwd.cli.commands["pfc_stat_history"], + [ + "enable", + "Ethernet8" + ], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + assert test_vectors.pfc_is_not_enabled == result.output + + # verify no change + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + # same as original config + assert result.output == test_vectors.pfcwd_show_config_output def test_pfcwd_start_ports_invalid(self): # pfcwd start --action drop --restoration-time 200 Ethernet0 200 @@ -249,14 +360,51 @@ def test_pfcwd_start_ports_invalid(self): result = runner.invoke( pfcwd.cli.commands["start"], [ - "--action", "forward", "--restoration-time", "101", + "--action", "forward", "--restoration-time", "101", "--pfc-stat-history", "Ethernet1000", "102" ], obj=db ) print(result.output) assert result.exit_code == 1 - assert result.output == pfcwd_show_start_config_output_fail + assert result.output == test_vectors.show_pfc_config_invalid_options_fail + + def test_pfcwd_enable_history_ports_invalid(self): + # pfcwd pfc_stat_history enable Ethernet1000 + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + + # get initial config + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.output == test_vectors.pfcwd_show_config_output + + # attempt to enable history on invalid port + result = runner.invoke( + pfcwd.cli.commands["pfc_stat_history"], + [ + "enable", + "Ethernet1000" + ], + obj=db + ) + print(result.output) + assert result.exit_code == 1 + assert result.output == test_vectors.show_pfc_config_invalid_options_fail + + # config unchanged + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + # same as original config + assert result.output == test_vectors.pfcwd_show_config_output @classmethod def teardown_class(cls): @@ -334,6 +482,17 @@ def test_pfcwd_big_red_switch_nonroot(self): assert result.exit_code == 1 assert result.output == 'Root privileges are required for this operation\n' + @patch('pfcwd.main.os.geteuid', MagicMock(return_value=8)) + def test_pfcwd_pfc_stat_history_nonroot(self): + import pfcwd.main as pfcwd + runner = CliRunner() + result = runner.invoke( + pfcwd.cli.commands['pfc_stat_history'], ['enable', 'all'], + ) + print(result.output) + assert result.exit_code == 1 + assert result.output == 'Root privileges are required for this operation\n' + def test_pfcwd_stats_all(self): import pfcwd.main as pfcwd print(pfcwd.__file__) @@ -343,7 +502,7 @@ def test_pfcwd_stats_all(self): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfcwd_stats_all + assert result.output == test_vectors.show_pfcwd_stats_all def test_pfcwd_stats_with_queues(self): import pfcwd.main as pfcwd @@ -357,7 +516,7 @@ def test_pfcwd_stats_with_queues(self): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfcwd_stats_with_queues + assert result.output == test_vectors.show_pfcwd_stats_with_queues def test_pfcwd_config_all(self): import pfcwd.main as pfcwd @@ -367,7 +526,7 @@ def test_pfcwd_config_all(self): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfc_config_all + assert result.output == test_vectors.show_pfc_config_all def test_pfcwd_config_with_ports(self): import pfcwd.main as pfcwd @@ -378,7 +537,7 @@ def test_pfcwd_config_with_ports(self): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfcwd_config_with_ports + assert result.output == test_vectors.show_pfcwd_config_with_ports @patch('pfcwd.main.os') def test_pfcwd_start_ports_masic_valid(self, mock_os): @@ -392,7 +551,7 @@ def test_pfcwd_start_ports_masic_valid(self, mock_os): obj=db ) print(result.output) - assert result.output == show_pfc_config_all + assert result.output == test_vectors.show_pfc_config_all mock_os.geteuid.return_value = 0 result = runner.invoke( @@ -413,7 +572,42 @@ def test_pfcwd_start_ports_masic_valid(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfc_config_start_pass + assert result.output == test_vectors.show_pfc_config_start_pass + + @patch('pfcwd.main.os') + def test_pfcwd_enable_history_ports_masic_valid(self, mock_os): + # pfcwd pfc_stat_history enable Ethernet0, Ethernet-BP4 + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + # get initial config + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.output == test_vectors.show_pfc_config_all + + mock_os.geteuid.return_value = 0 + result = runner.invoke( + pfcwd.cli.commands["pfc_stat_history"], + [ + "enable", + "Ethernet0", "Ethernet-BP4" + ], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + + # get config after the change + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == test_vectors.show_pfc_config_enable_history_pass @patch('pfcwd.main.os') def test_pfcwd_start_actions_masic(self, mock_os): @@ -427,7 +621,7 @@ def test_pfcwd_start_actions_masic(self, mock_os): obj=db ) print(result.output) - assert result.output == show_pfc_config_all + assert result.output == test_vectors.show_pfc_config_all # always skip Ethernet-BP260 because 'pfc_enable' not configured for this port mock_os.geteuid.return_value = 0 @@ -449,7 +643,7 @@ def test_pfcwd_start_actions_masic(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfc_config_start_action_drop_masic + assert result.output == test_vectors.show_pfc_config_start_action_drop_masic result = runner.invoke( pfcwd.cli.commands["start"], @@ -469,7 +663,7 @@ def test_pfcwd_start_actions_masic(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfc_config_start_action_alert_masic + assert result.output == test_vectors.show_pfc_config_start_action_alert_masic result = runner.invoke( pfcwd.cli.commands["start"], @@ -489,7 +683,44 @@ def test_pfcwd_start_actions_masic(self, mock_os): ) print(result.output) assert result.exit_code == 0 - assert result.output == show_pfc_config_start_action_forward_masic + assert result.output == test_vectors.show_pfc_config_start_action_forward_masic + + @patch('pfcwd.main.os') + def test_pfcwd_start_history_masic(self, mock_os): + # pfcwd start all 600 --restoration-time 601 --pfc-stat-history + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + + # initially history disabled on all ports + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.output == test_vectors.show_pfc_config_all + + mock_os.geteuid.return_value = 0 + # start wd with history flag + result = runner.invoke( + pfcwd.cli.commands["start"], + [ + "all", "600", "--restoration-time", "601", + "--pfc-stat-history" + ], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + + # get config after the change + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + assert result.output == test_vectors.pfcwd_show_start_history_output_masic def test_pfcwd_start_ports_masic_invalid(self): # --action drop --restoration-time 200 Ethernet0 Ethernet500 200 @@ -500,14 +731,14 @@ def test_pfcwd_start_ports_masic_invalid(self): result = runner.invoke( pfcwd.cli.commands["start"], [ - "--action", "forward", "--restoration-time", "101", + "--action", "forward", "--restoration-time", "101", "--pfc-stat-history", "Ethernet0", "Ethernet-500", "102" ], obj=db ) print(result.output) assert result.exit_code == 1 - assert result.output == show_pfc_config_start_fail + assert result.output == test_vectors.show_pfc_config_invalid_options_fail_masic # get config after the command, config shouldn't change result = runner.invoke( @@ -517,7 +748,35 @@ def test_pfcwd_start_ports_masic_invalid(self): print(result.output) assert result.exit_code == 0 # same as original config - assert result.output == show_pfc_config_all + assert result.output == test_vectors.show_pfc_config_all + + def test_pfcwd_enable_history_ports_masic_invalid(self): + # pfcwd pfc_stat_history enable Ethernet0 Ethernet-500 + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + + result = runner.invoke( + pfcwd.cli.commands["pfc_stat_history"], + [ + "enable", + "Ethernet0", "Ethernet-500", + ], + obj=db + ) + print(result.output) + assert result.exit_code == 1 + assert result.output == test_vectors.show_pfc_config_invalid_options_fail_masic + + # get config after the command, config shouldn't change + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + # same as original config + assert result.output == test_vectors.show_pfc_config_all @patch('pfcwd.main.os') def test_pfcwd_pfc_not_enabled_masic(self, mock_os): @@ -529,15 +788,55 @@ def test_pfcwd_pfc_not_enabled_masic(self, mock_os): result = runner.invoke( pfcwd.cli.commands["start"], [ - "--action", "drop", "--restoration-time", "601", + "--action", "drop", "--restoration-time", "601", "--pfc-stat-history", "Ethernet-BP260", "602" ], obj=db ) assert result.exit_code == 0 - assert pfc_is_not_enabled_masic == result.output + assert test_vectors.pfc_is_not_enabled_masic == result.output + + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + + print(result.output) + assert result.exit_code == 0 + # same as original config + assert result.output == test_vectors.show_pfc_config_all + + @patch('pfcwd.main.os') + def test_pfcwd_enable_history_pfc_not_enabled_masic(self, mock_os): + # pfcwd pfc_stat_history enable Ethernet-BP260 + import pfcwd.main as pfcwd + runner = CliRunner() + db = Db() + + # get initial config + result = runner.invoke( + pfcwd.cli.commands["show"].commands["config"], + obj=db + ) + print(result.output) + assert result.output == test_vectors.show_pfc_config_all + + # attempt to enable history on Ethernet without pfc + mock_os.geteuid.return_value = 0 + result = runner.invoke( + pfcwd.cli.commands["pfc_stat_history"], + [ + "enable", + "Ethernet-BP260" + ], + obj=db + ) + print(result.output) + assert result.exit_code == 0 + assert test_vectors.pfc_is_not_enabled_masic == result.output + # verify no change result = runner.invoke( pfcwd.cli.commands["show"].commands["config"], obj=db @@ -546,7 +845,7 @@ def test_pfcwd_pfc_not_enabled_masic(self, mock_os): print(result.output) assert result.exit_code == 0 # same as original config - assert result.output == show_pfc_config_all + assert result.output == test_vectors.show_pfc_config_all @classmethod def teardown_class(cls):