From 066a2695b1cb23d5151dd2567fd84ada284e93ca Mon Sep 17 00:00:00 2001 From: bingwang Date: Sun, 2 Mar 2025 08:27:59 +0000 Subject: [PATCH 1/3] Initial commit --- .../acl/templates/create_stress_acl_table.j2 | 12 + tests/acl/templates/stress_table_type.json | 13 + tests/acl/test_stress_acl.py | 482 ++++++++++++------ tests/conftest.py | 2 +- 4 files changed, 360 insertions(+), 149 deletions(-) create mode 100644 tests/acl/templates/create_stress_acl_table.j2 create mode 100644 tests/acl/templates/stress_table_type.json diff --git a/tests/acl/templates/create_stress_acl_table.j2 b/tests/acl/templates/create_stress_acl_table.j2 new file mode 100644 index 00000000000..745b4df8f1e --- /dev/null +++ b/tests/acl/templates/create_stress_acl_table.j2 @@ -0,0 +1,12 @@ +[ + { + "op": "add", + "path": "/ACL_TABLE/STRESS_ACL_TABLE", + "value": { + "policy_desc": "STRESS_ACL_TABLE", + "type": "STRESS_ACL_TABLE_TYPE", + "stage": "INGRESS", + "ports": {{ bind_ports }} + } + } +] diff --git a/tests/acl/templates/stress_table_type.json b/tests/acl/templates/stress_table_type.json new file mode 100644 index 00000000000..df68634fc6d --- /dev/null +++ b/tests/acl/templates/stress_table_type.json @@ -0,0 +1,13 @@ +[ + { + "op": "add", + "path": "/ACL_TABLE_TYPE", + "value": { + "STRESS_ACL_TABLE_TYPE" : { + "MATCHES": ["DST_IP","DST_IPV6","ETHER_TYPE","IN_PORTS","L4_DST_PORT","L4_DST_PORT_RANGE","IP_PROTOCOL","IP_TYPE"], + "ACTIONS": ["PACKET_ACTION","COUNTER"], + "BIND_POINTS": ["PORT"] + } + } + } +] diff --git a/tests/acl/test_stress_acl.py b/tests/acl/test_stress_acl.py index 192c0506da4..258c7d641a7 100644 --- a/tests/acl/test_stress_acl.py +++ b/tests/acl/test_stress_acl.py @@ -2,41 +2,93 @@ import random import pytest import json +import time +import netaddr import ptf.testutils as testutils from ptf import mask, packet from collections import defaultdict from tests.common.dualtor.mux_simulator_control import toggle_all_simulator_ports_to_rand_selected_tor # noqa F401 from tests.common.utilities import wait_until +from tests.common.helpers.assertions import pytest_assert pytestmark = [ - pytest.mark.topology("t0", "t1", "m0", "mx"), - pytest.mark.device_type('vs') + pytest.mark.topology("t0") ] logger = logging.getLogger(__name__) -LOOP_TIMES_LEVEL_MAP = { - 'debug': 10, - 'basic': 50, - 'confident': 200 -} - -# Template json file used to test scale rules -STRESS_ACL_TABLE_TEMPLATE = "acl/templates/acltb_test_stress_acl_table.j2" -STRESS_ACL_RULE_TEMPLATE = "acl/templates/acltb_test_stress_acl_rules.j2" -STRESS_ACL_READD_RULE_TEMPLATE = "acl/templates/acltb_test_stress_acl_readd_rules.j2" -DEL_STRESS_ACL_TABLE_TEMPLATE = "acl/templates/del_acltb_test_stress_acl_table.j2" -STRESS_ACL_TABLE_JSON_FILE = "/tmp/acltb_test_stress_acl_table.json" -STRESS_ACL_RULE_JSON_FILE = "/tmp/acltb_test_stress_acl_rules.json" -DEL_STRESS_ACL_TABLE_JSON_FILE = "/tmp/del_acltb_test_stress_acl_table.json" - -LOG_EXPECT_ACL_TABLE_CREATE_RE = ".*Created ACL table.*" -LOG_EXPECT_ACL_RULE_FAILED_RE = ".*Failed to create ACL rule.*" - -ACL_RULE_NUMS = 10 +STRESS_ACL_TABLE_TYPE_NAME = "STRESS_ACL_TABLE_TYPE" +STRESS_ACL_TABLE_NAME = "STRESS_ACL_TABLE" + +STRESS_ACL_TABLE_TYPE_SRC = "acl/templates/stress_table_type.json" +STRESS_ACL_TABLE_TYPE_DST = "/tmp/stress_table_type.json" + +STRESS_ACL_TABLE_CREATE_JSON_SRC = "acl/templates/create_stress_acl_table.j2" +STRESS_ACL_TABLE_CREATE_JSON_DST = "/tmp/create_stress_acl_table.json" + +STRESS_ACL_RULE_JSON_FILE = "/tmp/stress_acl_rules.json" +STRESS_ACL_RULE_V4_REMOVE_JSON_FILE = "/tmp/stress_acl_rules_v4_remove.json" +STRESS_ACL_RULE_V6_REMOVE_JSON_FILE = "/tmp/stress_acl_rules_v6_remove.json" +STRESS_ACL_RULE_V4_ADD_JSON_FILE = "/tmp/stress_acl_rules_v4_add.json" +STRESS_ACL_RULE_V6_ADD_JSON_FILE = "/tmp/stress_acl_rules_v6_add.json" +STRESS_ACL_RULE_V4_UPDATE_JSON_FILE = "/tmp/stress_acl_rules_v4_update.json" +STRESS_ACL_RULE_V6_UPDATE_JSON_FILE = "/tmp/stress_acl_rules_v6_update.json" + +STRESS_ACL_RULE_GROUPS = { + # IPv4 rules that never changed + "RULE_IPV4_GROUP_1": [], + # IPv4 rules that will be updated + "RULE_IPV4_GROUP_2": [], + # IPv4 rules that will be used to overwrite rules in group 2 + "RULE_IPV4_GROUP_3": [], + # IPv6 rules that never changed + "RULE_IPV6_GROUP_1": [], + # IPv6 rules that will be updated + "RULE_IPV6_GROUP_2": [], + # IPv6 rules that will be used to overwrite rules in group 2 + "RULE_IPV6_GROUP_3": [] +} -@pytest.fixture(scope="module", autouse=True) +def prepare_stress_acl_rules(): + """ + A helper function to generate 700 stress acl rules + """ + global STRESS_ACL_RULE_GROUPS + count = 1 + # 250 IPv4 rules in group 1 + RULE_TEMPLATE = "123.1.1.{}" + for i in range(1, 251): + STRESS_ACL_RULE_GROUPS["RULE_IPV4_GROUP_1"].append((count, RULE_TEMPLATE.format(i))) + count += 1 + # 100 IPv4 rules in group 2 + RULE_TEMPLATE = "123.1.2.{}" + for i in range(1, 101): + STRESS_ACL_RULE_GROUPS["RULE_IPV4_GROUP_2"].append((count, RULE_TEMPLATE.format(i))) + count += 1 + # 100 IPv4 rules in group 3 + RULE_TEMPLATE = "123.1.3.{}" + for i in range(1, 101): + STRESS_ACL_RULE_GROUPS["RULE_IPV4_GROUP_3"].append( + (STRESS_ACL_RULE_GROUPS["RULE_IPV4_GROUP_2"][i - 1][0], RULE_TEMPLATE.format(i))) + # 250 IPv6 rules in group 1 + RULE_TEMPLATE = "2001:db8:1:1::{}" + for i in range(1, 251): + STRESS_ACL_RULE_GROUPS["RULE_IPV6_GROUP_1"].append((count, RULE_TEMPLATE.format(i))) + count += 1 + # 100 IPv6 rules in group 2 + RULE_TEMPLATE = "2001:db8:1:2::{}" + for i in range(1, 101): + STRESS_ACL_RULE_GROUPS["RULE_IPV6_GROUP_2"].append((count, RULE_TEMPLATE.format(i))) + count += 1 + # 100 IPv6 rules in group 3 + RULE_TEMPLATE = "2001:db8:1:3::{}" + for i in range(1, 101): + STRESS_ACL_RULE_GROUPS["RULE_IPV6_GROUP_3"].append( + (STRESS_ACL_RULE_GROUPS["RULE_IPV6_GROUP_2"][i - 1][0], RULE_TEMPLATE.format(i))) + + +@pytest.fixture(scope="module", autouse=False) def remove_dataacl_table(duthosts, rand_selected_dut): """ Remove DATAACL to free TCAM resources. @@ -73,72 +125,186 @@ def remove_dataacl_table(duthosts, rand_selected_dut): rand_selected_dut.shell(cmd_create_table) -@pytest.fixture(scope='module') -def prepare_test_file(rand_selected_dut): - # Define a custom table type CUSTOM_TYPE by loading a json configuration - rand_selected_dut.copy(src=STRESS_ACL_TABLE_TEMPLATE, dest=STRESS_ACL_TABLE_JSON_FILE, mode="0755") - rand_selected_dut.shell("sonic-cfggen -j {} -w".format(STRESS_ACL_TABLE_JSON_FILE)) - # Copy acl rules - rand_selected_dut.copy(src=STRESS_ACL_RULE_TEMPLATE, dest=STRESS_ACL_RULE_JSON_FILE, mode="0755") - - yield - - rand_selected_dut.copy(src=DEL_STRESS_ACL_TABLE_TEMPLATE, dest=DEL_STRESS_ACL_TABLE_JSON_FILE) - rand_selected_dut.shell("configlet -d -j {}".format(DEL_STRESS_ACL_TABLE_JSON_FILE)) - rand_selected_dut.shell("rm -f {}".format(DEL_STRESS_ACL_TABLE_JSON_FILE)) - +@pytest.fixture(scope="module") +def setup_info(rand_selected_dut, tbinfo): + """ + A fixture to get test setup info + """ + setup_info = {} -@pytest.fixture(scope='module') -def prepare_test_port(rand_selected_dut, tbinfo): mg_facts = rand_selected_dut.get_extended_minigraph_facts(tbinfo) + is_dualtor = False + + # Get router MAC + vlan_name = list(mg_facts['minigraph_vlans'].keys())[0] + if "dualtor" in tbinfo["topo"]["name"]: + # Use VLAN MAC as router MAC on dual-tor testbed + setup_info['router_mac'] = rand_selected_dut.get_dut_iface_mac(vlan_name) + setup_info['is_dualtor'] = True + else: + setup_info['router_mac'] = rand_selected_dut.facts['router_mac'] + setup_info['is_dualtor'] = False + + # Get the list of upstream/downstream ports + downstream_ports = [] + upstream_ports = [] + downstream_port_ids = [] + upstream_port_ids = [] - ports = list(mg_facts['minigraph_portchannels']) - if not ports: - ports = mg_facts["minigraph_acls"]["DataAcl"] - - dut_port = ports[0] if ports else None - - if not dut_port: - pytest.skip('No portchannels nor dataacl ports found') - if "Ethernet" in dut_port: - dut_eth_port = dut_port - elif "PortChannel" in dut_port: - dut_eth_port = mg_facts["minigraph_portchannels"][dut_port]["members"][0] - ptf_src_port = mg_facts["minigraph_ptf_indices"][dut_eth_port] + # Put all VLAN members into downstream_ports + downstream_ports = list(mg_facts["minigraph_vlans"][vlan_name]["members"]) + downstream_port_ids = [mg_facts['minigraph_ptf_indices'][port_name] for port_name in downstream_ports] + # Put all portchannel members into dst_ports + for _, v in mg_facts['minigraph_portchannels'].items(): + for member in v['members']: + upstream_port_ids.append(mg_facts['minigraph_ptf_indices'][member]) + upstream_ports.append(member) + + setup_info['downstream_ports'] = downstream_ports + setup_info['upstream_ports'] = upstream_ports + setup_info['downstream_port_ids'] = downstream_port_ids + setup_info['upstream_port_ids'] = upstream_port_ids + + yield setup_info + + +@pytest.fixture(scope="module") +def setup_stress_acl_table(rand_selected_dut, setup_info): + """ + Create a custom ACL table (combined V4 and V6) for testing + """ + # Step 1: Define a custom ACL table type STRESS_ACL_TABLE_TYPE by loading a json configuration + rand_selected_dut.copy(src=STRESS_ACL_TABLE_TYPE_SRC, dest=STRESS_ACL_TABLE_TYPE_DST, mode="0755") + rand_selected_dut.shell("config apply-patch {}".format(STRESS_ACL_TABLE_TYPE_DST)) + time.sleep(5) + # Step 2: Create a custom ACL table of type STRESS_ACL_TABLE_TYPE. The table is bound to all Vlan ports + extra_vars = { + 'bind_ports': setup_info['downstream_ports'] + } + rand_selected_dut.host.options['variable_manager'].extra_vars.update(extra_vars) + rand_selected_dut.template(src=STRESS_ACL_TABLE_CREATE_JSON_SRC, dest=STRESS_ACL_TABLE_CREATE_JSON_DST) + rand_selected_dut.shell("sed -i \"s/'/\\\"/g\" " + STRESS_ACL_TABLE_CREATE_JSON_DST) + rand_selected_dut.shell("config apply-patch {}".format(STRESS_ACL_TABLE_CREATE_JSON_DST)) + time.sleep(5) + # Check if the table is created successfully + acl_table_status = rand_selected_dut.show_and_parse('show acl table {}'.format(STRESS_ACL_TABLE_NAME)) + pytest_assert(acl_table_status[0]['status'].lower() == 'active', "Failed to create ACL table") - topo = tbinfo["topo"]["type"] - # Get the list of upstream ports - upstream_ports = defaultdict(list) - upstream_port_ids = [] - for interface, neighbor in list(mg_facts["minigraph_neighbors"].items()): - port_id = mg_facts["minigraph_ptf_indices"][interface] - if (topo == "t1" and "T2" in neighbor["name"]) or (topo == "t0" and "T1" in neighbor["name"]) or \ - (topo == "m0" and "M1" in neighbor["name"]) or (topo == "mx" and "M0" in neighbor["name"]): - upstream_ports[neighbor['namespace']].append(interface) - upstream_port_ids.append(port_id) + yield - return ptf_src_port, upstream_port_ids, dut_port + # Remove ACL table STRESS_ACL_TABLE + rand_selected_dut.shell("config acl remove table {}".format(STRESS_ACL_TABLE_NAME)) + # Remove custom ACL table type STRESS_ACL_TABLE_TYPE + rand_selected_dut.shell("sonic-db-cli CONFIG_DB del \"ACL_TABLE_TYPE|{}\"".format(STRESS_ACL_TABLE_TYPE_NAME)) -def verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, - ptf_dst_ports, acl_rule_list, del_rule_id, verity_status): +def prepare_acl_rule_update_files(duthost, group_names, file_name, oper="add", default_drop_rule=False): + """ + Copy json files for update test to DUT + """ + patch = [] + for group in group_names: + for id, ip in STRESS_ACL_RULE_GROUPS[group]: + if oper == "add": + if netaddr.IPAddress(ip).version == 4: + key = "DST_IP" + ip_mask = ip + "/32" + else: + key = "DST_IPV6" + ip_mask = ip + "/128" + rule = { + "op": "add", + "path": "/ACL_RULE", + "value": { + "{}|RULE_{}".format(STRESS_ACL_TABLE_NAME, id): { + "PRIORITY": 900 - id, + "PACKET_ACTION": "FORWARD", + key: ip_mask + } + } + } + else: + rule = { + "op": "remove", + "path": "/ACL_RULE/{}|RULE_{}".format(STRESS_ACL_TABLE_NAME, id) + } + patch.append(rule) + if default_drop_rule: + # Add a default rule to drop all other traffic + patch.append({ + "op": "add", + "path": "/ACL_RULE", + "value": { + "{}|RULE_DROP".format(STRESS_ACL_TABLE_NAME): { + "PRIORITY": 1, + "PACKET_ACTION": "DROP" + } + } + }) + # Dump json to file + TMP_FILE = "/tmp/tmp_acl_rules.json" + with open(TMP_FILE, "w") as f: + json.dump(patch, f) + # Copy json file to DUT + duthost.copy(src=TMP_FILE, dest=file_name, mode="0755") + duthost.shell("sed -i \"s/'/\\\"/g\" " + file_name) + +def apply_acl_rule_patch(duthost, file_name, group_names=(), oper="add"): + """ + Apply patch to DUT + """ + duthost.shell("config apply-patch {}".format(file_name)) + rule_list = [] + for group_name in group_names: + for id, _ in STRESS_ACL_RULE_GROUPS[group_name]: + rule_list.append("RULE_{}".format(id)) + if oper == "add": + # For add operation, check and confirm all rules are active + def _check_acl_rule_status(): + count = 0 + acl_rule_status = duthost.show_and_parse('show acl rule {}'.format(STRESS_ACL_TABLE_NAME)) + for rule in acl_rule_status: + if rule['rule'] in rule_list and rule['status'].lower() == 'active': + count += 1 + return count == len(rule_list) + wait_until(60, 5, 10, _check_acl_rule_status, "Not all ACL rules are active") + else: + # For remove operation, delay for 30 seconds to let the rules removed + time.sleep(30) + + +@pytest.fixture(scope="module") +def setup_stress_acl_rules(rand_selected_dut, setup_stress_acl_table): + """ + Fixture to create stress acl rules + """ + prepare_stress_acl_rules() + group_names = ["RULE_IPV4_GROUP_1", "RULE_IPV4_GROUP_2", "RULE_IPV6_GROUP_1", "RULE_IPV6_GROUP_2"] + prepare_acl_rule_update_files(rand_selected_dut, + group_names, + file_name=STRESS_ACL_RULE_JSON_FILE, + default_drop_rule=True) + # Copy other json files to DUT + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV4_GROUP_2"], STRESS_ACL_RULE_V4_REMOVE_JSON_FILE, oper="remove"), + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV6_GROUP_2"], STRESS_ACL_RULE_V6_REMOVE_JSON_FILE, oper="remove"), + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV4_GROUP_3"], STRESS_ACL_RULE_V4_ADD_JSON_FILE, oper="add"), + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV6_GROUP_3"], STRESS_ACL_RULE_V6_ADD_JSON_FILE, oper="add") + + yield + # Remove all ACL rules + rand_selected_dut.shell("acl-loader delete {}".format(STRESS_ACL_TABLE_NAME)) - for acl_id in acl_rule_list: - ip_addr1 = acl_id % 256 - ip_addr2 = int(acl_id / 256) - src_ip_addr = "20.0.{}.{}".format(ip_addr2, ip_addr1) - dst_ip_addr = "10.0.0.1" - pkt = testutils.simple_ip_packet( - eth_dst=rand_selected_dut.facts['router_mac'], +def verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, router_mac, ip, fwd=True): + """ + Build testing packet to veryfy ACL rule + """ + if netaddr.IPAddress(ip).version == 4: + pkt = testutils.simple_udp_packet( + eth_dst=router_mac, eth_src=ptfadapter.dataplane.get_mac(0, ptf_src_port), - ip_src=src_ip_addr, - ip_dst=dst_ip_addr, - ip_proto=47, - ip_tos=0x84, - ip_id=0, - ip_ihl=5, - ip_ttl=121 + ip_src="192.168.0.100", + ip_dst=ip ) pkt_copy = pkt.copy() @@ -147,74 +313,94 @@ def verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, exp_pkt.set_do_not_care_scapy(packet.Ether, 'dst') exp_pkt.set_do_not_care_scapy(packet.Ether, 'src') exp_pkt.set_do_not_care_scapy(packet.IP, "chksum") - - ptfadapter.dataplane.flush() - testutils.send(test=ptfadapter, port_id=ptf_src_port, pkt=pkt) - if verity_status == "forward" or acl_id == del_rule_id: - testutils.verify_packet_any_port(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) - elif verity_status == "drop" and acl_id != del_rule_id: - testutils.verify_no_packet_any(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) - - -def acl_rule_loaded(rand_selected_dut, acl_rule_list): - acl_rule_infos = rand_selected_dut.show_and_parse("show acl rule") - acl_id_list = [] - for acl_info in acl_rule_infos: - acl_id = int(acl_info['rule'][len('RULE_'):]) - acl_id_list.append(acl_id) - if sorted(acl_id_list) != sorted(acl_rule_list): - return False - return True - - -def test_acl_add_del_stress(rand_selected_dut, tbinfo, ptfadapter, prepare_test_file, - prepare_test_port, get_function_completeness_level, - toggle_all_simulator_ports_to_rand_selected_tor): # noqa F811 - - ptf_src_port, ptf_dst_ports, dut_port = prepare_test_port - - cmd_create_table = "config acl add table STRESS_ACL L3 -s ingress -p {}".format(dut_port) - cmd_remove_table = "config acl remove table STRESS_ACL" - cmd_add_rules = "sonic-cfggen -j {} -w".format(STRESS_ACL_RULE_JSON_FILE) - cmd_rm_all_rules = "acl-loader delete STRESS_ACL" - - normalized_level = get_function_completeness_level - if normalized_level is None: - normalized_level = 'debug' - loop_times = LOOP_TIMES_LEVEL_MAP[normalized_level] - wait_timeout = 15 - - rand_selected_dut.shell(cmd_create_table) - acl_rule_list = list(range(1, ACL_RULE_NUMS + 1)) - verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, acl_rule_list, 0, "forward") - try: - loops = 0 - while loops <= loop_times: - logger.info("loops: {}".format(loops)) - if loops == 0: - rand_selected_dut.shell(cmd_add_rules) + else: + pkt = testutils.simple_udpv6_packet( + eth_dst=router_mac, + eth_src=ptfadapter.dataplane.get_mac(0, ptf_src_port), + ipv6_src="fc02:1000::100", + ipv6_dst=ip + ) + pkt_copy = pkt.copy() + pkt_copy.ttl = pkt_copy.ttl - 1 + exp_pkt = mask.Mask(pkt_copy) + exp_pkt.set_do_not_care_scapy(packet.Ether, 'dst') + exp_pkt.set_do_not_care_scapy(packet.Ether, 'src') + exp_pkt.set_do_not_care_scapy(packet.IPv6ExtHdrHopByHop, "chksum") + + RETRY = 3 + while RETRY > 0: + ptfadapter.dataplane.flush() + testutils.send(test=ptfadapter, port_id=ptf_src_port, pkt=pkt) + if fwd: + try: + testutils.verify_packet_any_port(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) + except Exception as e: + if RETRY == 0: + raise e + else: + logger.info("Retrying...") + RETRY -= 1 + else: + break else: - readd_id = loops + ACL_RULE_NUMS - ip_addr1 = readd_id % 256 - ip_addr2 = int(readd_id / 256) - rand_selected_dut.shell('sonic-db-cli CONFIG_DB hset "ACL_RULE|STRESS_ACL| RULE_{}" \ - "SRC_IP" "20.0.{}.{}/32" "PACKET_ACTION" "DROP" "PRIORITY" "{}"' - .format(readd_id, ip_addr2, ip_addr1, readd_id)) - acl_rule_list.append(readd_id) - - wait_until(wait_timeout, 2, 0, acl_rule_loaded, rand_selected_dut, acl_rule_list) - verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, acl_rule_list, 0, "drop") - - del_rule_id = random.choice(acl_rule_list) - rand_selected_dut.shell('sonic-db-cli CONFIG_DB del "ACL_RULE|STRESS_ACL| RULE_{}"'.format(del_rule_id)) - acl_rule_list.remove(del_rule_id) - - wait_until(wait_timeout, 2, 0, acl_rule_loaded, rand_selected_dut, acl_rule_list) - verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, - acl_rule_list, del_rule_id, "drop") - - loops += 1 - finally: - rand_selected_dut.shell(cmd_rm_all_rules) - rand_selected_dut.shell(cmd_remove_table) - logger.info("End") + testutils.verify_no_packet_any(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) + break + + +def verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, group_name, fwd=True): + """ + Verify ACL rules in a group + """ + ptf_src_port, ptf_dst_ports, router_mac = setup_info['downstream_port_ids'][0], setup_info['upstream_port_ids'], \ + setup_info['router_mac'] + for id, ip in STRESS_ACL_RULE_GROUPS[group_name]: + verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, router_mac, ip, fwd=fwd) + + +def test_stress_acl_with_custom_acl_table(rand_selected_dut, tbinfo, ptfadapter, setup_info, setup_stress_acl_rules, + toggle_all_simulator_ports_to_rand_selected_tor): # noqa F811 + """ + Test stress acl with custom acl table + """ + LOOP = 10 + group_names = ["RULE_IPV4_GROUP_1", "RULE_IPV4_GROUP_2", "RULE_IPV6_GROUP_1", "RULE_IPV6_GROUP_2"] + + for i in range(LOOP): + logger.info("Stress ACL test loop: {}".format(i)) + logger.info("Creating ACL rules") + # Apply patch + apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_JSON_FILE, group_names, "add") + # Verify all ACL rules + logger.info("Verifying all ACL rules") + for group in group_names: + verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, group, fwd=True) + + # Update 1: Remove all the rules in "RULE_IPV4_GROUP_2" + logger.info("Removing IPv4 ACL rules") + apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V4_REMOVE_JSON_FILE, ["RULE_IPV4_GROUP_2"], "remove") + logger.info("Verifying IPv4 ACL rules are removed") + verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV4_GROUP_2", fwd=False) + + # Update 2: Add a new set of IPv4 rules in "RULE_IPV4_GROUP_3" + logger.info("Adding new IPv4 ACL rules") + apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V4_ADD_JSON_FILE, ["RULE_IPV4_GROUP_3"], "add") + logger.info("Verifying new IPv4 ACL rules") + verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV4_GROUP_3", fwd=True) + + # Update 3: Remove all the rules in "RULE_IPV6_GROUP_2" + logger.info("Removing IPv6 ACL rules") + apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V6_REMOVE_JSON_FILE, ["RULE_IPV6_GROUP_2"], "remove") + logger.info("Verifying IPv6 ACL rules are removed") + verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV6_GROUP_2", fwd=False) + + #Update 4: Add a new set of IPv6 rules in "RULE_IPV6_GROUP_3" + logger.info("Adding new IPv6 ACL rules") + apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V6_ADD_JSON_FILE, ["RULE_IPV6_GROUP_3"], "add") + logger.info("Verifying new IPv6 ACL rules") + verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV6_GROUP_3", fwd=True) + + # Update 5: Remove all ACL rules + logger.info("Removing all ACL rules") + rand_selected_dut.shell("acl-loader delete {}".format(STRESS_ACL_TABLE_NAME)) + + logger.info("Stress ACL test loop {} done".format(i)) diff --git a/tests/conftest.py b/tests/conftest.py index b740b454752..3f45ba92168 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2429,7 +2429,7 @@ def compare_running_config(pre_running_config, cur_running_config): return False -@pytest.fixture(scope="module", autouse=True) +@pytest.fixture(scope="module", autouse=False) def core_dump_and_config_check(duthosts, tbinfo, request, # make sure the tear down of sanity_check happened after core_dump_and_config_check sanity_check): From 6b7a5da89b4942d91f22d08ce5f1ddc9543c4562 Mon Sep 17 00:00:00 2001 From: bingwang Date: Tue, 4 Mar 2025 21:58:48 +0000 Subject: [PATCH 2/3] Use sonic-db-cli instead of GCU --- tests/acl/test_stress_acl.py | 184 +++++++++++++++++++++++++---------- 1 file changed, 131 insertions(+), 53 deletions(-) diff --git a/tests/acl/test_stress_acl.py b/tests/acl/test_stress_acl.py index 258c7d641a7..db85dea11c0 100644 --- a/tests/acl/test_stress_acl.py +++ b/tests/acl/test_stress_acl.py @@ -1,12 +1,10 @@ import logging -import random import pytest import json import time import netaddr import ptf.testutils as testutils from ptf import mask, packet -from collections import defaultdict from tests.common.dualtor.mux_simulator_control import toggle_all_simulator_ports_to_rand_selected_tor # noqa F401 from tests.common.utilities import wait_until from tests.common.helpers.assertions import pytest_assert @@ -36,7 +34,7 @@ STRESS_ACL_RULE_V6_UPDATE_JSON_FILE = "/tmp/stress_acl_rules_v6_update.json" STRESS_ACL_RULE_GROUPS = { - # IPv4 rules that never changed + # IPv4 rules that never changed "RULE_IPV4_GROUP_1": [], # IPv4 rules that will be updated "RULE_IPV4_GROUP_2": [], @@ -50,6 +48,7 @@ "RULE_IPV6_GROUP_3": [] } + def prepare_stress_acl_rules(): """ A helper function to generate 700 stress acl rules @@ -133,7 +132,6 @@ def setup_info(rand_selected_dut, tbinfo): setup_info = {} mg_facts = rand_selected_dut.get_extended_minigraph_facts(tbinfo) - is_dualtor = False # Get router MAC vlan_name = list(mg_facts['minigraph_vlans'].keys())[0] @@ -166,7 +164,7 @@ def setup_info(rand_selected_dut, tbinfo): setup_info['upstream_port_ids'] = upstream_port_ids yield setup_info - + @pytest.fixture(scope="module") def setup_stress_acl_table(rand_selected_dut, setup_info): @@ -203,6 +201,13 @@ def prepare_acl_rule_update_files(duthost, group_names, file_name, oper="add", d Copy json files for update test to DUT """ patch = [] + + if oper == "add": + patch.append({}) + patch[0]["path"] = "/ACL_RULE" + patch[0]["value"] = {} + patch[0]["op"] = "add" + for group in group_names: for id, ip in STRESS_ACL_RULE_GROUPS[group]: if oper == "add": @@ -213,34 +218,33 @@ def prepare_acl_rule_update_files(duthost, group_names, file_name, oper="add", d key = "DST_IPV6" ip_mask = ip + "/128" rule = { - "op": "add", - "path": "/ACL_RULE", - "value": { "{}|RULE_{}".format(STRESS_ACL_TABLE_NAME, id): { "PRIORITY": 900 - id, "PACKET_ACTION": "FORWARD", key: ip_mask } - } - } + } + patch[0]["value"].update(rule) else: rule = { "op": "remove", "path": "/ACL_RULE/{}|RULE_{}".format(STRESS_ACL_TABLE_NAME, id) } - patch.append(rule) + patch.append(rule) if default_drop_rule: # Add a default rule to drop all other traffic - patch.append({ - "op": "add", - "path": "/ACL_RULE", - "value": { - "{}|RULE_DROP".format(STRESS_ACL_TABLE_NAME): { + patch[0]["value"].update({ + "{}|RULE_DROP_2".format(STRESS_ACL_TABLE_NAME): { + "PRIORITY": 2, + "IP_TYPE": "IPV6ANY", + "PACKET_ACTION": "DROP" + }, + "{}|RULE_DROP_1".format(STRESS_ACL_TABLE_NAME): { "PRIORITY": 1, + "ETHER_TYPE": 0x0800, "PACKET_ACTION": "DROP" } - } - }) + }) # Dump json to file TMP_FILE = "/tmp/tmp_acl_rules.json" with open(TMP_FILE, "w") as f: @@ -249,11 +253,12 @@ def prepare_acl_rule_update_files(duthost, group_names, file_name, oper="add", d duthost.copy(src=TMP_FILE, dest=file_name, mode="0755") duthost.shell("sed -i \"s/'/\\\"/g\" " + file_name) + def apply_acl_rule_patch(duthost, file_name, group_names=(), oper="add"): """ Apply patch to DUT """ - duthost.shell("config apply-patch {}".format(file_name)) + duthost.shell("config apply-patch {}".format(file_name), module_ignore_errors=True) rule_list = [] for group_name in group_names: for id, _ in STRESS_ACL_RULE_GROUPS[group_name]: @@ -273,6 +278,17 @@ def _check_acl_rule_status(): time.sleep(30) +@pytest.fixture(scope="module") +def setup_stress_acl_rules_cli(rand_selected_dut, setup_stress_acl_table): + """ + Fixture to create stress acl rules with redis-db cli + """ + prepare_stress_acl_rules() + yield + # Remove all ACL rules + rand_selected_dut.shell("acl-loader delete {}".format(STRESS_ACL_TABLE_NAME)) + + @pytest.fixture(scope="module") def setup_stress_acl_rules(rand_selected_dut, setup_stress_acl_table): """ @@ -285,16 +301,77 @@ def setup_stress_acl_rules(rand_selected_dut, setup_stress_acl_table): file_name=STRESS_ACL_RULE_JSON_FILE, default_drop_rule=True) # Copy other json files to DUT - prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV4_GROUP_2"], STRESS_ACL_RULE_V4_REMOVE_JSON_FILE, oper="remove"), - prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV6_GROUP_2"], STRESS_ACL_RULE_V6_REMOVE_JSON_FILE, oper="remove"), - prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV4_GROUP_3"], STRESS_ACL_RULE_V4_ADD_JSON_FILE, oper="add"), - prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV6_GROUP_3"], STRESS_ACL_RULE_V6_ADD_JSON_FILE, oper="add") - + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV4_GROUP_2"], + STRESS_ACL_RULE_V4_REMOVE_JSON_FILE, oper="remove"), + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV6_GROUP_2"], + STRESS_ACL_RULE_V6_REMOVE_JSON_FILE, oper="remove"), + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV4_GROUP_3"], + STRESS_ACL_RULE_V4_ADD_JSON_FILE, oper="add"), + prepare_acl_rule_update_files(rand_selected_dut, ["RULE_IPV6_GROUP_3"], + STRESS_ACL_RULE_V6_ADD_JSON_FILE, oper="add") + yield # Remove all ACL rules rand_selected_dut.shell("acl-loader delete {}".format(STRESS_ACL_TABLE_NAME)) +def add_acl_rules(duthost, group_name, fwd=True, default_drop_rule=False): + """ + Add ACL rules + """ + cmds = [] + rule_list = [] + if fwd: + action = "FORWARD" + else: + action = "DROP" + for id, ip in STRESS_ACL_RULE_GROUPS[group_name]: + if netaddr.IPAddress(ip).version == 4: + key = "DST_IP" + ip_mask = ip + "/32" + else: + key = "DST_IPV6" + ip_mask = ip + "/128" + cmds.append( + "sonic-db-cli CONFIG_DB hmset \'ACL_RULE|{}|RULE_{}\' {} {} PRIORITY {} PACKET_ACTION {}".format( + STRESS_ACL_TABLE_NAME, id, key, ip_mask, 900 - id, action)) + rule_list.append("RULE_{}".format(id)) + + if default_drop_rule: + cmds.append( + "sonic-db-cli CONFIG_DB hmset \'ACL_RULE|{}|RULE_DROP_2\' IP_TYPE IPV6ANY PRIORITY 2 PACKET_ACTION DROP" + .format(STRESS_ACL_TABLE_NAME)) + cmds.append( + "sonic-db-cli CONFIG_DB hmset \'ACL_RULE|{}|RULE_DROP_1\' ETHER_TYPE 0x0800 PRIORITY 1 PACKET_ACTION DROP" + .format(STRESS_ACL_TABLE_NAME)) + rule_list.extend(["RULE_DROP_1", "RULE_DROP_2"]) + + duthost.shell_cmds(cmds=cmds) + + # Verify all rules are active + def _check_acl_rule_status(): + count = 0 + acl_rule_status = duthost.show_and_parse('show acl rule {}'.format(STRESS_ACL_TABLE_NAME)) + for rule in acl_rule_status: + if rule['rule'] in rule_list and rule['status'].lower() == 'active': + count += 1 + return count == len(rule_list) + + pytest_assert(wait_until(0.5*len(rule_list), 5, 10, _check_acl_rule_status), "Not all ACL rules are active") + + +def remove_acl_rules(duthost, group_name): + """ + Remove ACL rules + """ + cmds = [] + for id, _ in STRESS_ACL_RULE_GROUPS[group_name]: + cmds.append("sonic-db-cli CONFIG_DB del \'ACL_RULE|{}|RULE_{}\'".format(STRESS_ACL_TABLE_NAME, id)) + duthost.shell_cmds(cmds=cmds) + # There is no way to verify rules are removed from ASIC, so we just wait for a few seconds + time.sleep(0.5 * len(cmds)) + + def verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, router_mac, ip, fwd=True): """ Build testing packet to veryfy ACL rule @@ -308,7 +385,7 @@ def verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, ) pkt_copy = pkt.copy() - pkt_copy.ttl = pkt_copy.ttl - 1 + pkt_copy['IP'].ttl -= 1 exp_pkt = mask.Mask(pkt_copy) exp_pkt.set_do_not_care_scapy(packet.Ether, 'dst') exp_pkt.set_do_not_care_scapy(packet.Ether, 'src') @@ -321,30 +398,30 @@ def verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, ipv6_dst=ip ) pkt_copy = pkt.copy() - pkt_copy.ttl = pkt_copy.ttl - 1 + pkt_copy['IPv6'].hlim -= 1 exp_pkt = mask.Mask(pkt_copy) exp_pkt.set_do_not_care_scapy(packet.Ether, 'dst') exp_pkt.set_do_not_care_scapy(packet.Ether, 'src') - exp_pkt.set_do_not_care_scapy(packet.IPv6ExtHdrHopByHop, "chksum") - - RETRY = 3 - while RETRY > 0: - ptfadapter.dataplane.flush() - testutils.send(test=ptfadapter, port_id=ptf_src_port, pkt=pkt) - if fwd: - try: - testutils.verify_packet_any_port(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) - except Exception as e: - if RETRY == 0: - raise e - else: - logger.info("Retrying...") - RETRY -= 1 + exp_pkt.set_do_not_care_scapy(packet.UDP, "chksum") + + RETRY = 3 + while RETRY > 0: + ptfadapter.dataplane.flush() + testutils.send(test=ptfadapter, port_id=ptf_src_port, pkt=pkt) + if fwd: + try: + testutils.verify_packet_any_port(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) + except Exception as e: + if RETRY == 0: + raise e else: - break + logger.info("Retrying...") + RETRY -= 1 else: - testutils.verify_no_packet_any(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) break + else: + testutils.verify_no_packet_any(test=ptfadapter, pkt=exp_pkt, ports=ptf_dst_ports) + break def verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, group_name, fwd=True): @@ -352,50 +429,51 @@ def verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, group_name Verify ACL rules in a group """ ptf_src_port, ptf_dst_ports, router_mac = setup_info['downstream_port_ids'][0], setup_info['upstream_port_ids'], \ - setup_info['router_mac'] + setup_info['router_mac'] for id, ip in STRESS_ACL_RULE_GROUPS[group_name]: verify_acl_rules(rand_selected_dut, ptfadapter, ptf_src_port, ptf_dst_ports, router_mac, ip, fwd=fwd) -def test_stress_acl_with_custom_acl_table(rand_selected_dut, tbinfo, ptfadapter, setup_info, setup_stress_acl_rules, +def test_stress_acl_with_custom_acl_table(rand_selected_dut, tbinfo, ptfadapter, setup_info, setup_stress_acl_rules_cli, toggle_all_simulator_ports_to_rand_selected_tor): # noqa F811 """ Test stress acl with custom acl table """ - LOOP = 10 + LOOP = 1000 group_names = ["RULE_IPV4_GROUP_1", "RULE_IPV4_GROUP_2", "RULE_IPV6_GROUP_1", "RULE_IPV6_GROUP_2"] for i in range(LOOP): logger.info("Stress ACL test loop: {}".format(i)) logger.info("Creating ACL rules") # Apply patch - apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_JSON_FILE, group_names, "add") + for group in group_names: + add_acl_rules(rand_selected_dut, group, fwd=True, default_drop_rule=True) # Verify all ACL rules logger.info("Verifying all ACL rules") for group in group_names: verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, group, fwd=True) - + # Update 1: Remove all the rules in "RULE_IPV4_GROUP_2" logger.info("Removing IPv4 ACL rules") - apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V4_REMOVE_JSON_FILE, ["RULE_IPV4_GROUP_2"], "remove") + remove_acl_rules(rand_selected_dut, "RULE_IPV4_GROUP_2") logger.info("Verifying IPv4 ACL rules are removed") verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV4_GROUP_2", fwd=False) # Update 2: Add a new set of IPv4 rules in "RULE_IPV4_GROUP_3" logger.info("Adding new IPv4 ACL rules") - apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V4_ADD_JSON_FILE, ["RULE_IPV4_GROUP_3"], "add") + add_acl_rules(rand_selected_dut, "RULE_IPV4_GROUP_3", fwd=True) logger.info("Verifying new IPv4 ACL rules") verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV4_GROUP_3", fwd=True) # Update 3: Remove all the rules in "RULE_IPV6_GROUP_2" logger.info("Removing IPv6 ACL rules") - apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V6_REMOVE_JSON_FILE, ["RULE_IPV6_GROUP_2"], "remove") + remove_acl_rules(rand_selected_dut, "RULE_IPV6_GROUP_2") logger.info("Verifying IPv6 ACL rules are removed") verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV6_GROUP_2", fwd=False) - #Update 4: Add a new set of IPv6 rules in "RULE_IPV6_GROUP_3" + # Update 4: Add a new set of IPv6 rules in "RULE_IPV6_GROUP_3" logger.info("Adding new IPv6 ACL rules") - apply_acl_rule_patch(rand_selected_dut, STRESS_ACL_RULE_V6_ADD_JSON_FILE, ["RULE_IPV6_GROUP_3"], "add") + add_acl_rules(rand_selected_dut, "RULE_IPV6_GROUP_3", fwd=True) logger.info("Verifying new IPv6 ACL rules") verify_acl_rules_group(rand_selected_dut, ptfadapter, setup_info, "RULE_IPV6_GROUP_3", fwd=True) From 344c5eeec2a0f7146a454cc1ab10148e79436dda Mon Sep 17 00:00:00 2001 From: bingwang Date: Tue, 4 Mar 2025 22:13:12 +0000 Subject: [PATCH 3/3] Revert unnecessary change --- tests/acl/test_stress_acl.py | 2 +- tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/acl/test_stress_acl.py b/tests/acl/test_stress_acl.py index db85dea11c0..dd8f6771abf 100644 --- a/tests/acl/test_stress_acl.py +++ b/tests/acl/test_stress_acl.py @@ -87,7 +87,7 @@ def prepare_stress_acl_rules(): (STRESS_ACL_RULE_GROUPS["RULE_IPV6_GROUP_2"][i - 1][0], RULE_TEMPLATE.format(i))) -@pytest.fixture(scope="module", autouse=False) +@pytest.fixture(scope="module", autouse=True) def remove_dataacl_table(duthosts, rand_selected_dut): """ Remove DATAACL to free TCAM resources. diff --git a/tests/conftest.py b/tests/conftest.py index 3f45ba92168..b740b454752 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2429,7 +2429,7 @@ def compare_running_config(pre_running_config, cur_running_config): return False -@pytest.fixture(scope="module", autouse=False) +@pytest.fixture(scope="module", autouse=True) def core_dump_and_config_check(duthosts, tbinfo, request, # make sure the tear down of sanity_check happened after core_dump_and_config_check sanity_check):