diff --git a/counterpoll/main.py b/counterpoll/main.py index ad15c8c248..d7b66d5743 100644 --- a/counterpoll/main.py +++ b/counterpoll/main.py @@ -3,17 +3,28 @@ from flow_counter_util.route import exit_if_route_flow_counter_not_support from swsscommon.swsscommon import ConfigDBConnector from tabulate import tabulate +from sonic_py_common import device_info BUFFER_POOL_WATERMARK = "BUFFER_POOL_WATERMARK" PORT_BUFFER_DROP = "PORT_BUFFER_DROP" PG_DROP = "PG_DROP" ACL = "ACL" +ENI = "ENI" DISABLE = "disable" ENABLE = "enable" DEFLT_60_SEC= "default (60000)" DEFLT_10_SEC= "default (10000)" DEFLT_1_SEC = "default (1000)" +def is_dpu(db): + """ Check if the device is DPU """ + platform_info = device_info.get_platform_info(db) + if platform_info.get('switch_type', '') == 'dpu': + return True + else: + return False + + @click.group() def cli(): """ SONiC Static Counter Poll configurations """ @@ -126,6 +137,7 @@ def disable(): port_info['FLEX_COUNTER_STATUS'] = DISABLE configdb.mod_entry("FLEX_COUNTER_TABLE", PORT_BUFFER_DROP, port_info) + # Ingress PG drop packet stat @cli.group() @click.pass_context @@ -382,6 +394,52 @@ def disable(ctx): fc_info['FLEX_COUNTER_STATUS'] = 'disable' ctx.obj.mod_entry("FLEX_COUNTER_TABLE", "FLOW_CNT_ROUTE", fc_info) +# ENI counter commands +@cli.group() +@click.pass_context +def eni(ctx): + """ ENI counter commands """ + ctx.obj = ConfigDBConnector() + ctx.obj.connect() + +@eni.command() +@click.argument('poll_interval', type=click.IntRange(1000, 30000)) +@click.pass_context +def interval(ctx, poll_interval): + """ Set eni counter query interval """ + if not is_dpu(ctx.obj): + click.echo("ENI counters are not supported on non DPU platforms") + exit(1) + + eni_info = {} + if poll_interval is not None: + eni_info['POLL_INTERVAL'] = poll_interval + ctx.obj.mod_entry("FLEX_COUNTER_TABLE", ENI, eni_info) + +@eni.command() +@click.pass_context +def enable(ctx): + """ Enable eni counter query """ + if not is_dpu(ctx.obj): + click.echo("ENI counters are not supported on non DPU platforms") + exit(1) + + eni_info = {} + eni_info['FLEX_COUNTER_STATUS'] = 'enable' + ctx.obj.mod_entry("FLEX_COUNTER_TABLE", ENI, eni_info) + +@eni.command() +@click.pass_context +def disable(ctx): + """ Disable eni counter query """ + if not is_dpu(ctx.obj): + click.echo("ENI counters are not supported on non DPU platforms") + exit(1) + + eni_info = {} + eni_info['FLEX_COUNTER_STATUS'] = 'disable' + ctx.obj.mod_entry("FLEX_COUNTER_TABLE", ENI, eni_info) + @cli.command() def show(): """ Show the counter configuration """ @@ -399,6 +457,7 @@ def show(): tunnel_info = configdb.get_entry('FLEX_COUNTER_TABLE', 'TUNNEL') trap_info = configdb.get_entry('FLEX_COUNTER_TABLE', 'FLOW_CNT_TRAP') route_info = configdb.get_entry('FLEX_COUNTER_TABLE', 'FLOW_CNT_ROUTE') + eni_info = configdb.get_entry('FLEX_COUNTER_TABLE', ENI) header = ("Type", "Interval (in ms)", "Status") data = [] @@ -428,6 +487,9 @@ def show(): data.append(["FLOW_CNT_ROUTE_STAT", route_info.get("POLL_INTERVAL", DEFLT_10_SEC), route_info.get("FLEX_COUNTER_STATUS", DISABLE)]) + if is_dpu(config_db) and eni_info: + data.append(["ENI_STAT", eni_info.get("POLL_INTERVAL", DEFLT_1_SEC), eni_info.get("FLEX_COUNTER_STATUS", DISABLE)]) + click.echo(tabulate(data, headers=header, tablefmt="simple", missingval="")) def _update_config_db_flex_counter_table(status, filename): diff --git a/tests/counterpoll_test.py b/tests/counterpoll_test.py index 4a4da07ee9..ee7122cbc3 100644 --- a/tests/counterpoll_test.py +++ b/tests/counterpoll_test.py @@ -2,6 +2,7 @@ import json import os import pytest +import mock import sys from click.testing import CliRunner from shutil import copyfile @@ -31,6 +32,21 @@ FLOW_CNT_ROUTE_STAT 10000 enable """ +expected_counterpoll_show_dpu = """Type Interval (in ms) Status +-------------------- ------------------ -------- +QUEUE_STAT 10000 enable +PORT_STAT 1000 enable +PORT_BUFFER_DROP 60000 enable +QUEUE_WATERMARK_STAT default (60000) enable +PG_WATERMARK_STAT default (60000) enable +PG_DROP_STAT 10000 enable +ACL 5000 enable +TUNNEL_STAT 3000 enable +FLOW_CNT_TRAP_STAT 10000 enable +FLOW_CNT_ROUTE_STAT 10000 enable +ENI_STAT 1000 enable +""" + class TestCounterpoll(object): @classmethod def setup_class(cls): @@ -44,6 +60,14 @@ def test_show(self): print(result.output) assert result.output == expected_counterpoll_show + @mock.patch('counterpoll.main.device_info.get_platform_info') + def test_show_dpu(self, mock_get_platform_info): + mock_get_platform_info.return_value = {'switch_type': 'dpu'} + runner = CliRunner() + result = runner.invoke(counterpoll.cli.commands["show"], []) + print(result.output) + assert result.output == expected_counterpoll_show_dpu + def test_port_buffer_drop_interval(self): runner = CliRunner() result = runner.invoke(counterpoll.cli.commands["port-buffer-drop"].commands["interval"], ["30000"]) @@ -221,6 +245,43 @@ def test_update_route_counter_interval(self): assert result.exit_code == 2 assert expected in result.output + @pytest.mark.parametrize("status", ["disable", "enable"]) + def test_update_eni_status(self, status): + runner = CliRunner() + db = Db() + + result = runner.invoke(counterpoll.cli.commands["eni"].commands[status], [], obj=db.cfgdb) + print(result.exit_code, result.output) + assert result.exit_code == 1 + assert result.output == "ENI counters are not supported on non DPU platforms\n" + + @pytest.mark.parametrize("status", ["disable", "enable"]) + @mock.patch('counterpoll.main.device_info.get_platform_info') + def test_update_eni_status_dpu(self, mock_get_platform_info, status): + mock_get_platform_info.return_value = {'switch_type': 'dpu'} + runner = CliRunner() + db = Db() + + result = runner.invoke(counterpoll.cli.commands["eni"].commands[status], [], obj=db.cfgdb) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + table = db.cfgdb.get_table('FLEX_COUNTER_TABLE') + assert status == table["ENI"]["FLEX_COUNTER_STATUS"] + + @mock.patch('counterpoll.main.device_info.get_platform_info') + def test_update_eni_interval(self, mock_get_platform_info): + mock_get_platform_info.return_value = {'switch_type': 'dpu'} + runner = CliRunner() + db = Db() + test_interval = "2000" + + result = runner.invoke(counterpoll.cli.commands["eni"].commands["interval"], [test_interval], obj=db.cfgdb) + print(result.exit_code, result.output) + assert result.exit_code == 0 + + table = db.cfgdb.get_table('FLEX_COUNTER_TABLE') + assert test_interval == table["ENI"]["POLL_INTERVAL"] @classmethod def teardown_class(cls): diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index af37538447..fbe3b43e94 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1783,6 +1783,10 @@ "POLL_INTERVAL": "10000", "FLEX_COUNTER_STATUS": "enable" }, + "FLEX_COUNTER_TABLE|ENI": { + "POLL_INTERVAL": "1000", + "FLEX_COUNTER_STATUS": "enable" + }, "PFC_WD|Ethernet0": { "action": "drop", "detection_time": "600",