Skip to content

Commit 28a7098

Browse files
committed
Added neighbor_miss trap and copp cli tests
Added test for the following - If the platform supports neighbor_miss trap, verify Vlan subnet traffic is policed under neighbor_miss copp - Verify 'show copp configuration' CLI output - Verify trap installation status of configured traps Signed-off-by: Ravi Minnikanti <[email protected]>
1 parent 8995e65 commit 28a7098

File tree

4 files changed

+243
-11
lines changed

4 files changed

+243
-11
lines changed

ansible/roles/test/files/ptftests/py3/copp_tests.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def __init__(self):
8686
self.platform = test_params.get('platform', None)
8787
self.topo_type = test_params.get('topo_type', None)
8888
self.ip_version = test_params.get('ip_version', None)
89+
self.neighbor_miss_trap_supported = test_params.get('neighbor_miss_trap_supported', False)
8990

9091
def log(self, message, debug=False):
9192
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -737,6 +738,12 @@ class VlanSubnetTest(PolicyTest):
737738
def __init__(self):
738739
PolicyTest.__init__(self)
739740

741+
# Verify with different PPS if neighbor miss trap is supported by the platform
742+
if self.neighbor_miss_trap_supported:
743+
self.PPS_LIMIT = 200
744+
self.PPS_LIMIT_MIN = self.PPS_LIMIT * 0.9
745+
self.PPS_LIMIT_MAX = self.PPS_LIMIT * 1.3
746+
740747
def runTest(self):
741748
self.log("VlanSubnetTest")
742749
self.run_suite()
@@ -773,6 +780,12 @@ class VlanSubnetIPinIPTest(PolicyTest):
773780
def __init__(self):
774781
PolicyTest.__init__(self)
775782

783+
# Verify with different PPS if neighbor miss trap is supported by the platform
784+
if self.neighbor_miss_trap_supported:
785+
self.PPS_LIMIT = 200
786+
self.PPS_LIMIT_MIN = self.PPS_LIMIT * 0.9
787+
self.PPS_LIMIT_MAX = self.PPS_LIMIT * 1.3
788+
776789
def runTest(self):
777790
self.log("VlanSubnetIpinIPTest")
778791
self.run_suite()

tests/copp/copp_utils.py

Lines changed: 121 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import logging
99
import json
1010
import ipaddress
11+
import ast
12+
import random
1113

1214
from tests.common.config_reload import config_reload
1315

@@ -39,7 +41,7 @@
3941
_TEMP_CONFIG_DB = "/home/admin/config_db_copp_backup.json"
4042

4143

42-
def limit_policer(dut, pps_limit, nn_target_namespace):
44+
def limit_policer(dut, pps_limit, nn_target_namespace, neighbor_miss_trap_supported):
4345
"""
4446
Updates the COPP configuration in the SWSS container to respect a given rate limit.
4547
@@ -64,12 +66,13 @@ def limit_policer(dut, pps_limit, nn_target_namespace):
6466
config_format = "config_db"
6567

6668
dut.script(
67-
cmd="{} {} {} {} {} {}".format(_UPDATE_COPP_SCRIPT,
68-
pps_limit,
69-
_BASE_COPP_CONFIG,
70-
_TEMP_COPP_CONFIG,
71-
config_format,
72-
dut.facts["asic_type"])
69+
cmd="{} {} {} {} {} {} {}".format(_UPDATE_COPP_SCRIPT,
70+
pps_limit,
71+
_BASE_COPP_CONFIG,
72+
_TEMP_COPP_CONFIG,
73+
config_format,
74+
dut.facts["asic_type"],
75+
neighbor_miss_trap_supported)
7376
)
7477

7578
if config_format == "app_db":
@@ -474,3 +477,114 @@ def get_lo_ipv4(duthost):
474477
break
475478

476479
return loopback_ip
480+
481+
482+
def get_copp_trap_capabilities(duthost):
483+
"""
484+
Fetches supported trap IDs from COPP_TRAP_CAPABILITY_TABLE in STATE_DB and returns them as a list.
485+
Args:
486+
duthost (SonicHost): The target device.
487+
Returns:
488+
list: A list of supported trap IDs.
489+
"""
490+
491+
trap_ids = duthost.shell("sonic-db-cli STATE_DB HGET 'COPP_TRAP_CAPABILITY_TABLE|traps' trap_ids")['stdout']
492+
return trap_ids.split(",")
493+
494+
495+
def parse_show_copp_configuration(duthost):
496+
"""
497+
Parses the output of the `show copp configuration` command into a structured dictionary.
498+
Args:
499+
duthost (SonicHost): The target device.
500+
Returns:
501+
dict: A dictionary mapping trap IDs to their configuration details.
502+
"""
503+
504+
copp_config_output = duthost.shell("show copp configuration")["stdout"]
505+
copp_config_lines = copp_config_output.splitlines()
506+
507+
# Parse the command output into a structured format
508+
copp_config_data = {}
509+
for line in copp_config_lines[1:]: # Skip the header line
510+
fields = line.split()
511+
if len(fields) >= 8:
512+
trap_id = fields[0]
513+
copp_config_data[trap_id] = {
514+
"trap_group": fields[1],
515+
"trap_action": fields[2],
516+
"cbs": fields[3],
517+
"cir": fields[4],
518+
"meter_type": fields[5],
519+
"mode": fields[6],
520+
"hw_status": fields[7]
521+
}
522+
523+
return copp_config_data
524+
525+
526+
def is_trap_installed(duthost, trap_id):
527+
"""
528+
Checks if a specific trap is installed by parsing the output of `show copp configuration`.
529+
Args:
530+
dut (SonicHost): The target device
531+
trap_id: The trap ID to check.
532+
Returns:
533+
bool: True if the trap is installed, False otherwise.
534+
"""
535+
536+
output = parse_show_copp_configuration(duthost)
537+
assert trap_id in output, f"Trap {trap_id} not found in the configuration"
538+
assert "hw_status" in output[trap_id], f"hw_status not found for trap {trap_id}"
539+
540+
return output[trap_id]["hw_status"] == "installed"
541+
542+
543+
def get_trap_hw_status(duthost):
544+
"""
545+
Retrieves the hw_status for traps from the STATE_DB.
546+
Args:
547+
dut (SonicHost): The target device
548+
Returns:
549+
dict: A dictionary mapping trap IDs to their hw_status.
550+
"""
551+
552+
state_db_data = duthost.shell("sonic-db-cli STATE_DB KEYS 'COPP_TRAP_TABLE|*'")["stdout"]
553+
state_db_data = state_db_data.splitlines()
554+
hw_status = {}
555+
556+
for key in state_db_data:
557+
trap_id = key.split("|")[-1]
558+
trap_data = duthost.shell(f"sonic-db-cli STATE_DB HGETALL '{key}'")["stdout"]
559+
trap_data_dict = ast.literal_eval(trap_data)
560+
hw_status[trap_id] = trap_data_dict.get("hw_status", "not-installed")
561+
562+
return hw_status
563+
564+
565+
def get_random_copp_trap_config(duthost):
566+
"""
567+
Retrieves a random CoPP trap config from /etc/sonic/copp_cfg.json on the DUT.
568+
Returns the trap ID, its group, and related config details from COPP_TRAP and COPP_GROUP sections
569+
Args:
570+
duthost (SonicHost): The target device.
571+
Returns:
572+
tuple: A tuple containing the following elements:
573+
- str: The first trap ID associated with the selected trap.
574+
- str: The trap group associated with the selected trap.
575+
- dict: The configuration details of the selected trap group from the `COPP_GROUP` section.
576+
"""
577+
578+
copp_cfg = json.loads(duthost.shell("cat /etc/sonic/copp_cfg.json")["stdout"])
579+
580+
# Get all traps from COPP_TRAP
581+
copp_trap_cfg = copp_cfg.get("COPP_TRAP", {})
582+
traps = list(copp_trap_cfg.keys())
583+
assert traps, "No traps found in copp_cfg.json"
584+
585+
# Randomly select one trap
586+
selected_trap = random.choice(traps)
587+
trap_data = copp_cfg["COPP_TRAP"][selected_trap]
588+
trap_ids = trap_data.get("trap_ids", "").split(",")
589+
trap_group = trap_data.get("trap_group", "")
590+
return trap_ids[0], trap_group, copp_cfg["COPP_GROUP"][trap_group]

tests/copp/scripts/update_copp_config.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ def generate_limited_pps_config(pps_limit, input_config_file, output_config_file
9292
else:
9393
continue
9494
else:
95+
# queue1_group3 is used by neighbor_miss trap.
96+
# test_copp.py tests the neighbor_miss trap with default CBS/CIR
97+
# values on platforms that support it.
98+
# The default value is 200 PPS for queue1_group3
99+
if tg == "queue1_group3":
100+
if neighbor_miss_trap_supported:
101+
continue
95102
if "cir" in group_config:
96103
group_config["cir"] = pps_limit
97104
if "cbs" in group_config:
@@ -112,5 +119,9 @@ def generate_limited_pps_config(pps_limit, input_config_file, output_config_file
112119
asic_type = ""
113120
else:
114121
asic_type = ARGS[4]
122+
if len(ARGS) < 6:
123+
neighbor_miss_trap_supported = False
124+
else:
125+
neighbor_miss_trap_supported = ARGS[5].lower() == "true"
115126

116127
generate_limited_pps_config(ARGS[0], ARGS[1], ARGS[2], config_format, asic_type)

0 commit comments

Comments
 (0)